/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
5557.1.7 by John Arbash Meinel
Merge in the bzr.dev 5582
1
# Copyright (C) 2007-2011 Canonical Ltd
2490.2.5 by Aaron Bentley
Use GraphWalker.unique_ancestor to determine merge base
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2490.2.5 by Aaron Bentley
Use GraphWalker.unique_ancestor to determine merge base
16
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
17
from .. import (
2490.2.28 by Aaron Bentley
Fix handling of null revision
18
    errors,
2653.2.1 by Aaron Bentley
Implement Graph.is_ancestor
19
    graph as _mod_graph,
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
20
    tests,
2490.2.28 by Aaron Bentley
Fix handling of null revision
21
    )
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
22
from ..revision import NULL_REVISION
23
from . import TestCaseWithMemoryTransport
2490.2.1 by Aaron Bentley
Start work on GraphWalker
24
2490.2.25 by Aaron Bentley
Update from review
25
26
# Ancestry 1:
27
#
28
#  NULL_REVISION
29
#       |
30
#     rev1
31
#      /\
32
#  rev2a rev2b
33
#     |    |
34
#   rev3  /
35
#     |  /
36
#   rev4
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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']}
2490.2.25 by Aaron Bentley
Update from review
39
40
41
# Ancestry 2:
42
#
43
#  NULL_REVISION
44
#    /    \
45
# rev1a  rev1b
46
#   |
47
# rev2a
48
#   |
49
# rev3a
50
#   |
51
# rev4a
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
52
ancestry_2 = {b'rev1a': [NULL_REVISION], b'rev2a': [b'rev1a'],
53
              b'rev1b': [NULL_REVISION], b'rev3a': [b'rev2a'], b'rev4a': [b'rev3a']}
2490.2.2 by Aaron Bentley
add minimal-common-ancestor calculation
54
2490.2.25 by Aaron Bentley
Update from review
55
56
# Criss cross ancestry
57
#
58
#     NULL_REVISION
59
#         |
60
#        rev1
61
#        /  \
62
#    rev2a  rev2b
63
#       |\  /|
64
#       |  X |
65
#       |/  \|
66
#    rev3a  rev3b
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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']}
2490.2.3 by Aaron Bentley
Implement new merge base picker
69
2490.2.25 by Aaron Bentley
Update from review
70
71
# Criss-cross 2
72
#
73
#  NULL_REVISION
74
#    /   \
75
# rev1a  rev1b
76
#   |\   /|
77
#   | \ / |
78
#   |  X  |
79
#   | / \ |
80
#   |/   \|
81
# rev2a  rev2b
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
82
criss_cross2 = {b'rev1a': [NULL_REVISION], b'rev1b': [NULL_REVISION],
83
                b'rev2a': [b'rev1a', b'rev1b'], b'rev2b': [b'rev1b', b'rev1a']}
2490.2.4 by Aaron Bentley
More tests for unique common ancestor
84
2490.2.25 by Aaron Bentley
Update from review
85
86
# Mainline:
87
#
88
#  NULL_REVISION
89
#       |
90
#      rev1
91
#      /  \
92
#      | rev2b
93
#      |  /
94
#     rev2a
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
95
mainline = {b'rev1': [NULL_REVISION], b'rev2a': [b'rev1', b'rev2b'],
96
            b'rev2b': [b'rev1']}
2490.2.6 by Aaron Bentley
Use new common-ancestor code everywhere
97
2490.2.25 by Aaron Bentley
Update from review
98
99
# feature branch:
100
#
101
#  NULL_REVISION
102
#       |
103
#      rev1
104
#       |
105
#     rev2b
106
#       |
107
#     rev3b
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
108
feature_branch = {b'rev1': [NULL_REVISION],
109
                  b'rev2b': [b'rev1'], b'rev3b': [b'rev2b']}
2490.2.6 by Aaron Bentley
Use new common-ancestor code everywhere
110
2490.2.25 by Aaron Bentley
Update from review
111
112
# History shortcut
113
#  NULL_REVISION
114
#       |
115
#     rev1------
116
#     /  \      \
117
#  rev2a rev2b rev2c
118
#    |  /   \   /
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
119
#  rev3a    rev3b
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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']}
2490.2.9 by Aaron Bentley
Fix minimal common ancestor algorithm for non-minimal perhipheral ancestors
123
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
124
# Extended history shortcut
125
#  NULL_REVISION
126
#       |
127
#       a
128
#       |\
129
#       b |
130
#       | |
131
#       c |
132
#       | |
133
#       d |
134
#       |\|
135
#       e f
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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'],
7143.15.2 by Jelmer Vernooij
Run autopep8.
142
                             }
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
143
144
# Double shortcut
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
145
# Both sides will see b'A' first, even though it is actually a decendent of a
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
146
# different common revision.
147
#
148
#  NULL_REVISION
149
#       |
150
#       a
151
#      /|\
152
#     / b \
153
#    /  |  \
154
#   |   c   |
155
#   |  / \  |
156
#   | d   e |
157
#   |/     \|
158
#   f       g
159
7143.15.2 by Jelmer Vernooij
Run autopep8.
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']}
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
163
164
# Complex shortcut
165
# This has a failure mode in that a shortcut will find some nodes in common,
3377.3.37 by John Arbash Meinel
Ian's first review comments.
166
# but the common searcher won't have time to find that one branch is actually
3377.3.1 by John Arbash Meinel
Bring in some of the changes from graph_update and graph_optimization
167
# in common. The extra nodes at the beginning are because we want to avoid
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
168
# walking off the graph. Specifically, node G should be considered common, but
169
# is likely to be seen by M long before the common searcher finds it.
170
#
171
# NULL_REVISION
172
#     |
173
#     a
174
#     |
175
#     b
176
#     |
177
#     c
178
#     |
179
#     d
180
#     |\
181
#     e f
182
#     | |\
3377.3.1 by John Arbash Meinel
Bring in some of the changes from graph_update and graph_optimization
183
#     | g h
184
#     |/| |
185
#     i j |
186
#     | | |
187
#     | k |
188
#     | | |
189
#     | l |
190
#     |/|/
191
#     m n
7143.15.2 by Jelmer Vernooij
Run autopep8.
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']}
3377.3.1 by John Arbash Meinel
Bring in some of the changes from graph_update and graph_optimization
196
197
# NULL_REVISION
198
#     |
199
#     a
200
#     |
201
#     b
202
#     |
203
#     c
204
#     |
205
#     d
206
#     |\
3377.3.13 by John Arbash Meinel
Change _search_for_extra_common slightly.
207
#     e |
208
#     | |
209
#     f |
210
#     | |
211
#     g h
3377.3.1 by John Arbash Meinel
Bring in some of the changes from graph_update and graph_optimization
212
#     | |\
3377.3.13 by John Arbash Meinel
Change _search_for_extra_common slightly.
213
#     i | j
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
214
#     |\| |
215
#     | k |
216
#     | | |
217
#     | l |
3377.3.13 by John Arbash Meinel
Change _search_for_extra_common slightly.
218
#     | | |
219
#     | m |
220
#     | | |
221
#     | n |
222
#     | | |
223
#     | o |
224
#     | | |
225
#     | p |
226
#     | | |
227
#     | q |
228
#     | | |
229
#     | r |
230
#     | | |
231
#     | s |
232
#     | | |
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
233
#     |/|/
3377.3.13 by John Arbash Meinel
Change _search_for_extra_common slightly.
234
#     t u
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
235
complex_shortcut2 = {b'a': [NULL_REVISION], b'b': [b'a'], b'c': [b'b'], b'd': [b'c'],
7143.15.2 by Jelmer Vernooij
Run autopep8.
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
                     }
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
241
3377.3.36 by John Arbash Meinel
Small updates, try to write a test for the race condition.
242
# Graph where different walkers will race to find the common and uncommon
243
# nodes.
244
#
245
# NULL_REVISION
246
#     |
247
#     a
248
#     |
249
#     b
250
#     |
251
#     c
252
#     |
253
#     d
254
#     |\
255
#     e k
256
#     | |
257
#     f-+-p
258
#     | | |
259
#     | l |
260
#     | | |
261
#     | m |
262
#     | |\|
263
#     g n q
264
#     |\| |
265
#     h o |
266
#     |/| |
267
#     i r |
268
#     | | |
269
#     | s |
270
#     | | |
271
#     | t |
272
#     | | |
273
#     | u |
274
#     | | |
275
#     | v |
276
#     | | |
277
#     | w |
278
#     | | |
279
#     | x |
280
#     | |\|
281
#     | y z
282
#     |/
283
#     j
284
#
3377.4.2 by John Arbash Meinel
Merge in the bzr.dev changes
285
# x is found to be common right away, but is the start of a long series of
3377.3.36 by John Arbash Meinel
Small updates, try to write a test for the race condition.
286
# common commits.
287
# o is actually common, but the i-j shortcut makes it look like it is actually
3377.4.2 by John Arbash Meinel
Merge in the bzr.dev changes
288
# unique to j at first, you have to traverse all of x->o to find it.
289
# q,m gives the walker from j a common point to stop searching, as does p,f.
3377.3.36 by John Arbash Meinel
Small updates, try to write a test for the race condition.
290
# k-n exists so that the second pass still has nodes that are worth searching,
291
# rather than instantly cancelling the extra walker.
292
7143.15.2 by Jelmer Vernooij
Run autopep8.
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']}
3377.3.36 by John Arbash Meinel
Small updates, try to write a test for the race condition.
298
3377.3.35 by John Arbash Meinel
Add a test that exercises the multiple interesting unique code
299
300
# A graph with multiple nodes unique to one side.
301
#
302
# NULL_REVISION
303
#     |
304
#     a
305
#     |
306
#     b
307
#     |
308
#     c
309
#     |
310
#     d
311
#     |\
312
#     e f
313
#     |\ \
314
#     g h i
315
#     |\ \ \
316
#     j k l m
317
#     | |/ x|
318
#     | n o p
319
#     | |/  |
320
#     | q   |
321
#     | |   |
322
#     | r   |
323
#     | |   |
324
#     | s   |
325
#     | |   |
326
#     | t   |
327
#     | |   |
328
#     | u   |
329
#     | |   |
330
#     | v   |
331
#     | |   |
332
#     | w   |
333
#     | |   |
334
#     | x   |
335
#     |/ \ /
336
#     y   z
337
#
338
7143.15.2 by Jelmer Vernooij
Run autopep8.
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']}
3377.3.35 by John Arbash Meinel
Add a test that exercises the multiple interesting unique code
345
346
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
347
# Shortcut with extra root
348
# We have a long history shortcut, and an extra root, which is why we can't
349
# stop searchers based on seeing NULL_REVISION
350
#  NULL_REVISION
351
#       |   |
352
#       a   |
353
#       |\  |
354
#       b | |
355
#       | | |
356
#       c | |
357
#       | | |
358
#       d | g
359
#       |\|/
360
#       e f
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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],
7143.15.2 by Jelmer Vernooij
Run autopep8.
368
                       }
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
369
2653.2.1 by Aaron Bentley
Implement Graph.is_ancestor
370
#  NULL_REVISION
371
#       |
372
#       f
373
#       |
374
#       e
375
#      / \
376
#     b   d
377
#     | \ |
378
#     a   c
379
7143.15.2 by Jelmer Vernooij
Run autopep8.
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]}
2653.2.1 by Aaron Bentley
Implement Graph.is_ancestor
382
383
3228.4.2 by John Arbash Meinel
Add a Graph.iter_ancestry()
384
# A graph that contains a ghost
385
#  NULL_REVISION
386
#       |
387
#       f
388
#       |
389
#       e   g
390
#      / \ /
391
#     b   d
392
#     | \ |
393
#     a   c
394
7143.15.2 by Jelmer Vernooij
Run autopep8.
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: ()}
3228.4.2 by John Arbash Meinel
Add a Graph.iter_ancestry()
397
3445.1.3 by John Arbash Meinel
Search from all of the known revisions.
398
# A graph that shows we can shortcut finding revnos when reaching them from the
399
# side.
400
#  NULL_REVISION
401
#       |
402
#       a
403
#       |
404
#       b
405
#       |
406
#       c
407
#       |
408
#       d
409
#       |
410
#       e
411
#      / \
412
#     f   g
413
#     |
414
#     h
415
#     |
416
#     i
417
7143.15.2 by Jelmer Vernooij
Run autopep8.
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']}
3228.4.2 by John Arbash Meinel
Add a Graph.iter_ancestry()
420
421
2653.2.1 by Aaron Bentley
Implement Graph.is_ancestor
422
class InstrumentedParentsProvider(object):
423
424
    def __init__(self, parents_provider):
425
        self.calls = []
426
        self._real_parents_provider = parents_provider
6015.24.3 by John Arbash Meinel
Implement get_cached_parent_map
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
2653.2.1 by Aaron Bentley
Implement Graph.is_ancestor
432
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
433
    def get_parent_map(self, nodes):
434
        self.calls.extend(nodes)
435
        return self._real_parents_provider.get_parent_map(nodes)
436
6015.24.3 by John Arbash Meinel
Implement get_cached_parent_map
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
2490.2.25 by Aaron Bentley
Update from review
462
3445.1.2 by John Arbash Meinel
Handle when a known revision is an ancestor.
463
class TestGraphBase(tests.TestCase):
464
465
    def make_graph(self, ancestors):
466
        return _mod_graph.Graph(_mod_graph.DictParentsProvider(ancestors))
467
468
    def make_breaking_graph(self, ancestors, break_on):
469
        """Make a Graph that raises an exception if we hit a node."""
470
        g = self.make_graph(ancestors)
471
        orig_parent_map = g.get_parent_map
7143.15.2 by Jelmer Vernooij
Run autopep8.
472
3445.1.2 by John Arbash Meinel
Handle when a known revision is an ancestor.
473
        def get_parent_map(keys):
474
            bad_keys = set(keys).intersection(break_on)
475
            if bad_keys:
476
                self.fail('key(s) %s was accessed' % (sorted(bad_keys),))
477
            return orig_parent_map(keys)
478
        g.get_parent_map = get_parent_map
479
        return g
480
481
2490.2.31 by Aaron Bentley
Fix iter_topo_order to permit un-included parents
482
class TestGraph(TestCaseWithMemoryTransport):
2490.2.1 by Aaron Bentley
Start work on GraphWalker
483
2490.2.21 by Aaron Bentley
Rename graph to deprecated_graph
484
    def make_graph(self, ancestors):
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
485
        return _mod_graph.Graph(_mod_graph.DictParentsProvider(ancestors))
2490.2.3 by Aaron Bentley
Implement new merge base picker
486
2490.2.6 by Aaron Bentley
Use new common-ancestor code everywhere
487
    def prepare_memory_tree(self, location):
488
        tree = self.make_branch_and_memory_tree(location)
2490.2.1 by Aaron Bentley
Start work on GraphWalker
489
        tree.lock_write()
490
        tree.add('.')
2490.2.6 by Aaron Bentley
Use new common-ancestor code everywhere
491
        return tree
492
493
    def build_ancestry(self, tree, ancestors):
2490.2.25 by Aaron Bentley
Update from review
494
        """Create an ancestry as specified by a graph dict
495
496
        :param tree: A tree to use
497
        :param ancestors: a dict of {node: [node_parent, ...]}
498
        """
2490.2.1 by Aaron Bentley
Start work on GraphWalker
499
        pending = [NULL_REVISION]
500
        descendants = {}
6656.1.1 by Martin
Apply 2to3 dict fixer and clean up resulting mess using view helpers
501
        for descendant, parents in ancestors.items():
2490.2.1 by Aaron Bentley
Start work on GraphWalker
502
            for parent in parents:
503
                descendants.setdefault(parent, []).append(descendant)
504
        while len(pending) > 0:
505
            cur_node = pending.pop()
506
            for descendant in descendants.get(cur_node, []):
2490.2.3 by Aaron Bentley
Implement new merge base picker
507
                if tree.branch.repository.has_revision(descendant):
508
                    continue
2490.2.1 by Aaron Bentley
Start work on GraphWalker
509
                parents = [p for p in ancestors[descendant] if p is not
510
                           NULL_REVISION]
511
                if len([p for p in parents if not
7143.15.2 by Jelmer Vernooij
Run autopep8.
512
                        tree.branch.repository.has_revision(p)]) > 0:
2490.2.1 by Aaron Bentley
Start work on GraphWalker
513
                    continue
514
                tree.set_parent_ids(parents)
2490.2.3 by Aaron Bentley
Implement new merge base picker
515
                if len(parents) > 0:
516
                    left_parent = parents[0]
517
                else:
518
                    left_parent = NULL_REVISION
2490.2.1 by Aaron Bentley
Start work on GraphWalker
519
                tree.branch.set_last_revision_info(
2490.2.3 by Aaron Bentley
Implement new merge base picker
520
                    len(tree.branch._lefthand_history(left_parent)),
521
                    left_parent)
2490.2.1 by Aaron Bentley
Start work on GraphWalker
522
                tree.commit(descendant, rev_id=descendant)
523
                pending.append(descendant)
2490.2.2 by Aaron Bentley
add minimal-common-ancestor calculation
524
2490.2.13 by Aaron Bentley
Update distinct -> lowest, refactor, add ParentsProvider concept
525
    def test_lca(self):
2490.2.25 by Aaron Bentley
Update from review
526
        """Test finding least common ancestor.
2490.2.3 by Aaron Bentley
Implement new merge base picker
527
528
        ancestry_1 should always have a single common ancestor
529
        """
2490.2.21 by Aaron Bentley
Rename graph to deprecated_graph
530
        graph = self.make_graph(ancestry_1)
2490.2.28 by Aaron Bentley
Fix handling of null revision
531
        self.assertRaises(errors.InvalidRevisionId, graph.find_lca, None)
6619.3.12 by Jelmer Vernooij
Use 2to3 set_literal fixer.
532
        self.assertEqual({NULL_REVISION},
2490.2.21 by Aaron Bentley
Rename graph to deprecated_graph
533
                         graph.find_lca(NULL_REVISION, NULL_REVISION))
6619.3.12 by Jelmer Vernooij
Use 2to3 set_literal fixer.
534
        self.assertEqual({NULL_REVISION},
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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'))
2490.2.3 by Aaron Bentley
Implement new merge base picker
538
2520.4.104 by Aaron Bentley
Avoid infinite loop when there is no unique lca
539
    def test_no_unique_lca(self):
540
        """Test error when one revision is not in the graph"""
541
        graph = self.make_graph(ancestry_1)
542
        self.assertRaises(errors.NoCommonAncestor, graph.find_unique_lca,
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
543
                          b'rev1', b'1rev')
2520.4.104 by Aaron Bentley
Avoid infinite loop when there is no unique lca
544
2490.2.13 by Aaron Bentley
Update distinct -> lowest, refactor, add ParentsProvider concept
545
    def test_lca_criss_cross(self):
2490.2.25 by Aaron Bentley
Update from review
546
        """Test least-common-ancestor after a criss-cross merge."""
2490.2.21 by Aaron Bentley
Rename graph to deprecated_graph
547
        graph = self.make_graph(criss_cross)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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'))
2490.2.3 by Aaron Bentley
Implement new merge base picker
552
2490.2.13 by Aaron Bentley
Update distinct -> lowest, refactor, add ParentsProvider concept
553
    def test_lca_shortcut(self):
2490.2.25 by Aaron Bentley
Update from review
554
        """Test least-common ancestor on this history shortcut"""
2490.2.21 by Aaron Bentley
Rename graph to deprecated_graph
555
        graph = self.make_graph(history_shortcut)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
556
        self.assertEqual({b'rev2b'}, graph.find_lca(b'rev3a', b'rev3b'))
2490.2.9 by Aaron Bentley
Fix minimal common ancestor algorithm for non-minimal perhipheral ancestors
557
4332.3.4 by Robert Collins
Add a graph API for getting multiple distances to NULL at once.
558
    def test_lefthand_distance_smoke(self):
559
        """A simple does it work test for graph.lefthand_distance(keys)."""
560
        graph = self.make_graph(history_shortcut)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
561
        distance_graph = graph.find_lefthand_distances([b'rev3b', b'rev2a'])
562
        self.assertEqual({b'rev2a': 2, b'rev3b': 3}, distance_graph)
4332.3.4 by Robert Collins
Add a graph API for getting multiple distances to NULL at once.
563
4332.3.6 by Robert Collins
Teach graph.find_lefthand_distances about ghosts.
564
    def test_lefthand_distance_ghosts(self):
565
        """A simple does it work test for graph.lefthand_distance(keys)."""
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
566
        nodes = {b'nonghost': [NULL_REVISION], b'toghost': [b'ghost']}
4332.3.6 by Robert Collins
Teach graph.find_lefthand_distances about ghosts.
567
        graph = self.make_graph(nodes)
7143.15.2 by Jelmer Vernooij
Run autopep8.
568
        distance_graph = graph.find_lefthand_distances(
569
            [b'nonghost', b'toghost'])
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
570
        self.assertEqual({b'nonghost': 1, b'toghost': -1}, distance_graph)
4332.3.6 by Robert Collins
Teach graph.find_lefthand_distances about ghosts.
571
2490.2.13 by Aaron Bentley
Update distinct -> lowest, refactor, add ParentsProvider concept
572
    def test_recursive_unique_lca(self):
2490.2.25 by Aaron Bentley
Update from review
573
        """Test finding a unique least common ancestor.
2490.2.3 by Aaron Bentley
Implement new merge base picker
574
575
        ancestry_1 should always have a single common ancestor
576
        """
2490.2.21 by Aaron Bentley
Rename graph to deprecated_graph
577
        graph = self.make_graph(ancestry_1)
578
        self.assertEqual(NULL_REVISION,
579
                         graph.find_unique_lca(NULL_REVISION, NULL_REVISION))
580
        self.assertEqual(NULL_REVISION,
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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',
7143.15.2 by Jelmer Vernooij
Run autopep8.
586
                                               count_steps=True))
2490.2.3 by Aaron Bentley
Implement new merge base picker
587
3377.3.1 by John Arbash Meinel
Bring in some of the changes from graph_update and graph_optimization
588
    def assertRemoveDescendants(self, expected, graph, revisions):
589
        parents = graph.get_parent_map(revisions)
590
        self.assertEqual(expected,
591
                         graph._remove_simple_descendants(revisions, parents))
592
593
    def test__remove_simple_descendants(self):
594
        graph = self.make_graph(ancestry_1)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
595
        self.assertRemoveDescendants({b'rev1'}, graph,
7143.15.2 by Jelmer Vernooij
Run autopep8.
596
                                     {b'rev1', b'rev2a', b'rev2b', b'rev3', b'rev4'})
3377.3.1 by John Arbash Meinel
Bring in some of the changes from graph_update and graph_optimization
597
598
    def test__remove_simple_descendants_disjoint(self):
599
        graph = self.make_graph(ancestry_1)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
600
        self.assertRemoveDescendants({b'rev1', b'rev3'}, graph,
7143.15.2 by Jelmer Vernooij
Run autopep8.
601
                                     {b'rev1', b'rev3'})
3377.3.1 by John Arbash Meinel
Bring in some of the changes from graph_update and graph_optimization
602
603
    def test__remove_simple_descendants_chain(self):
604
        graph = self.make_graph(ancestry_1)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
605
        self.assertRemoveDescendants({b'rev1'}, graph,
7143.15.2 by Jelmer Vernooij
Run autopep8.
606
                                     {b'rev1', b'rev2a', b'rev3'})
3377.3.1 by John Arbash Meinel
Bring in some of the changes from graph_update and graph_optimization
607
608
    def test__remove_simple_descendants_siblings(self):
609
        graph = self.make_graph(ancestry_1)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
610
        self.assertRemoveDescendants({b'rev2a', b'rev2b'}, graph,
7143.15.2 by Jelmer Vernooij
Run autopep8.
611
                                     {b'rev2a', b'rev2b', b'rev3'})
3377.3.1 by John Arbash Meinel
Bring in some of the changes from graph_update and graph_optimization
612
2490.2.13 by Aaron Bentley
Update distinct -> lowest, refactor, add ParentsProvider concept
613
    def test_unique_lca_criss_cross(self):
614
        """Ensure we don't pick non-unique lcas in a criss-cross"""
2490.2.21 by Aaron Bentley
Rename graph to deprecated_graph
615
        graph = self.make_graph(criss_cross)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
616
        self.assertEqual(b'rev1', graph.find_unique_lca(b'rev3a', b'rev3b'))
7143.15.2 by Jelmer Vernooij
Run autopep8.
617
        lca, steps = graph.find_unique_lca(
618
            b'rev3a', b'rev3b', count_steps=True)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
619
        self.assertEqual(b'rev1', lca)
1551.19.10 by Aaron Bentley
Merge now warns when it encounters a criss-cross
620
        self.assertEqual(2, steps)
2490.2.4 by Aaron Bentley
More tests for unique common ancestor
621
2490.2.13 by Aaron Bentley
Update distinct -> lowest, refactor, add ParentsProvider concept
622
    def test_unique_lca_null_revision(self):
2490.2.4 by Aaron Bentley
More tests for unique common ancestor
623
        """Ensure we pick NULL_REVISION when necessary"""
2490.2.21 by Aaron Bentley
Rename graph to deprecated_graph
624
        graph = self.make_graph(criss_cross2)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
625
        self.assertEqual(b'rev1b', graph.find_unique_lca(b'rev2a', b'rev1b'))
2490.2.4 by Aaron Bentley
More tests for unique common ancestor
626
        self.assertEqual(NULL_REVISION,
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
627
                         graph.find_unique_lca(b'rev2a', b'rev2b'))
2490.2.4 by Aaron Bentley
More tests for unique common ancestor
628
2490.2.13 by Aaron Bentley
Update distinct -> lowest, refactor, add ParentsProvider concept
629
    def test_unique_lca_null_revision2(self):
2490.2.4 by Aaron Bentley
More tests for unique common ancestor
630
        """Ensure we pick NULL_REVISION when necessary"""
2490.2.21 by Aaron Bentley
Rename graph to deprecated_graph
631
        graph = self.make_graph(ancestry_2)
2490.2.4 by Aaron Bentley
More tests for unique common ancestor
632
        self.assertEqual(NULL_REVISION,
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
633
                         graph.find_unique_lca(b'rev4a', b'rev1b'))
2490.2.6 by Aaron Bentley
Use new common-ancestor code everywhere
634
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
635
    def test_lca_double_shortcut(self):
636
        graph = self.make_graph(double_shortcut)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
637
        self.assertEqual(b'c', graph.find_unique_lca(b'f', b'g'))
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
638
2490.2.6 by Aaron Bentley
Use new common-ancestor code everywhere
639
    def test_common_ancestor_two_repos(self):
2490.2.13 by Aaron Bentley
Update distinct -> lowest, refactor, add ParentsProvider concept
640
        """Ensure we do unique_lca using data from two repos"""
2490.2.6 by Aaron Bentley
Use new common-ancestor code everywhere
641
        mainline_tree = self.prepare_memory_tree('mainline')
642
        self.build_ancestry(mainline_tree, mainline)
3010.1.6 by Robert Collins
Locking in test_graph.
643
        self.addCleanup(mainline_tree.unlock)
2490.2.6 by Aaron Bentley
Use new common-ancestor code everywhere
644
645
        # This is cheating, because the revisions in the graph are actually
646
        # different revisions, despite having the same revision-id.
647
        feature_tree = self.prepare_memory_tree('feature')
648
        self.build_ancestry(feature_tree, feature_branch)
3010.1.6 by Robert Collins
Locking in test_graph.
649
        self.addCleanup(feature_tree.unlock)
650
2490.2.21 by Aaron Bentley
Rename graph to deprecated_graph
651
        graph = mainline_tree.branch.repository.get_graph(
2490.2.6 by Aaron Bentley
Use new common-ancestor code everywhere
652
            feature_tree.branch.repository)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
653
        self.assertEqual(b'rev2b', graph.find_unique_lca(b'rev2a', b'rev3b'))
2490.2.23 by Aaron Bentley
Adapt find_borders to produce a graph difference
654
655
    def test_graph_difference(self):
656
        graph = self.make_graph(ancestry_1)
7143.15.2 by Jelmer Vernooij
Run autopep8.
657
        self.assertEqual(
658
            (set(), set()), graph.find_difference(b'rev1', b'rev1'))
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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'))
2490.2.23 by Aaron Bentley
Adapt find_borders to produce a graph difference
667
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
668
    def test_graph_difference_separate_ancestry(self):
669
        graph = self.make_graph(ancestry_2)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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'))
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
675
2490.2.23 by Aaron Bentley
Adapt find_borders to produce a graph difference
676
    def test_graph_difference_criss_cross(self):
677
        graph = self.make_graph(criss_cross)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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'))
2490.2.25 by Aaron Bentley
Update from review
682
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
683
    def test_graph_difference_extended_history(self):
684
        graph = self.make_graph(extended_history_shortcut)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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'))
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
689
690
    def test_graph_difference_double_shortcut(self):
691
        graph = self.make_graph(double_shortcut)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
692
        self.assertEqual(({b'd', b'f'}, {b'e', b'g'}),
693
                         graph.find_difference(b'f', b'g'))
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
694
695
    def test_graph_difference_complex_shortcut(self):
696
        graph = self.make_graph(complex_shortcut)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
697
        self.assertEqual(({b'm', b'i', b'e'}, {b'n', b'h'}),
698
                         graph.find_difference(b'm', b'n'))
3377.3.1 by John Arbash Meinel
Bring in some of the changes from graph_update and graph_optimization
699
700
    def test_graph_difference_complex_shortcut2(self):
701
        graph = self.make_graph(complex_shortcut2)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
702
        self.assertEqual(({b't'}, {b'j', b'u'}),
703
                         graph.find_difference(b't', b'u'))
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
704
705
    def test_graph_difference_shortcut_extra_root(self):
706
        graph = self.make_graph(shortcut_extra_root)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
707
        self.assertEqual(({b'e'}, {b'f', b'g'}),
708
                         graph.find_difference(b'e', b'f'))
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
709
2490.2.31 by Aaron Bentley
Fix iter_topo_order to permit un-included parents
710
    def test_iter_topo_order(self):
2490.2.30 by Aaron Bentley
Add functionality for tsorting graphs
711
        graph = self.make_graph(ancestry_1)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
712
        args = [b'rev2a', b'rev3', b'rev1']
2490.2.31 by Aaron Bentley
Fix iter_topo_order to permit un-included parents
713
        topo_args = list(graph.iter_topo_order(args))
2490.2.30 by Aaron Bentley
Add functionality for tsorting graphs
714
        self.assertEqual(set(args), set(topo_args))
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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'))
2653.2.1 by Aaron Bentley
Implement Graph.is_ancestor
717
718
    def test_is_ancestor(self):
719
        graph = self.make_graph(ancestry_1)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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'))
2653.2.1 by Aaron Bentley
Implement Graph.is_ancestor
729
        instrumented_provider = InstrumentedParentsProvider(graph)
730
        instrumented_graph = _mod_graph.Graph(instrumented_provider)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
731
        instrumented_graph.is_ancestor(b'rev2a', b'rev2b')
732
        self.assertTrue(b'null:' not in instrumented_provider.calls)
2653.2.1 by Aaron Bentley
Implement Graph.is_ancestor
733
3921.3.5 by Marius Kruger
extract graph.is_between from builtins.cmd_tags.run, and test it
734
    def test_is_between(self):
735
        graph = self.make_graph(ancestry_1)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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'))
3921.3.5 by Marius Kruger
extract graph.is_between from builtins.cmd_tags.run, and test it
744
2653.2.1 by Aaron Bentley
Implement Graph.is_ancestor
745
    def test_is_ancestor_boundary(self):
746
        """Ensure that we avoid searching the whole graph.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
747
2653.2.1 by Aaron Bentley
Implement Graph.is_ancestor
748
        This requires searching through b as a common ancestor, so we
749
        can identify that e is common.
750
        """
751
        graph = self.make_graph(boundary)
752
        instrumented_provider = InstrumentedParentsProvider(graph)
753
        graph = _mod_graph.Graph(instrumented_provider)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
754
        self.assertFalse(graph.is_ancestor(b'a', b'c'))
755
        self.assertTrue(b'null:' not in instrumented_provider.calls)
1551.15.78 by Aaron Bentley
Fix KeyError in filter_candidate_lca
756
3228.4.2 by John Arbash Meinel
Add a Graph.iter_ancestry()
757
    def test_iter_ancestry(self):
3228.4.10 by John Arbash Meinel
Respond to abentley's review comments.
758
        nodes = boundary.copy()
759
        nodes[NULL_REVISION] = ()
760
        graph = self.make_graph(nodes)
761
        expected = nodes.copy()
7143.15.2 by Jelmer Vernooij
Run autopep8.
762
        expected.pop(b'a')  # b'a' is not in the ancestry of b'c', all the
763
        # other nodes are
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
764
        self.assertEqual(expected, dict(graph.iter_ancestry([b'c'])))
765
        self.assertEqual(nodes, dict(graph.iter_ancestry([b'a', b'c'])))
3228.4.2 by John Arbash Meinel
Add a Graph.iter_ancestry()
766
767
    def test_iter_ancestry_with_ghost(self):
768
        graph = self.make_graph(with_ghost)
769
        expected = with_ghost.copy()
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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'])))
3228.4.2 by John Arbash Meinel
Add a Graph.iter_ancestry()
775
1551.15.78 by Aaron Bentley
Fix KeyError in filter_candidate_lca
776
    def test_filter_candidate_lca(self):
777
        """Test filter_candidate_lca for a corner case
778
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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.
1551.15.83 by Aaron Bentley
Update test documentation
782
783
        To compensate for different dict orderings on other Python
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
784
        implementations, we mirror b'd' and b'e' with b'b' and b'a'.
1551.15.78 by Aaron Bentley
Fix KeyError in filter_candidate_lca
785
        """
786
        # This test is sensitive to the iteration order of dicts.  It will
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
787
        # pass incorrectly if b'e' and b'a' sort before b'c'
1551.15.84 by Aaron Bentley
Add ancestry graph for test case
788
        #
789
        # NULL_REVISION
790
        #     / \
791
        #    a   e
792
        #    |   |
793
        #    b   d
794
        #     \ /
795
        #      c
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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']))
2776.1.4 by Robert Collins
Trivial review feedback changes.
799
800
    def test_heads_null(self):
801
        graph = self.make_graph(ancestry_1)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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:')))
2776.1.4 by Robert Collins
Trivial review feedback changes.
807
808
    def test_heads_one(self):
3052.5.5 by John Arbash Meinel
Special case Graph.heads() for NULL_REVISION rather than is_ancestor.
809
        # A single node will always be a head
2776.1.4 by Robert Collins
Trivial review feedback changes.
810
        graph = self.make_graph(ancestry_1)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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']))
2776.1.4 by Robert Collins
Trivial review feedback changes.
817
818
    def test_heads_single(self):
819
        graph = self.make_graph(ancestry_1)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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']))
2776.1.4 by Robert Collins
Trivial review feedback changes.
828
829
    def test_heads_two_heads(self):
830
        graph = self.make_graph(ancestry_1)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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']))
2776.1.4 by Robert Collins
Trivial review feedback changes.
835
836
    def test_heads_criss_cross(self):
837
        graph = self.make_graph(criss_cross)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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']))
2776.1.4 by Robert Collins
Trivial review feedback changes.
864
865
    def test_heads_shortcut(self):
866
        graph = self.make_graph(history_shortcut)
867
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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']))
2921.3.1 by Robert Collins
* Graph ``heads()`` queries have been bugfixed to no longer access all
878
879
    def _run_heads_break_deeper(self, graph_dict, search):
880
        """Run heads on a graph-as-a-dict.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
881
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
882
        If the search asks for the parents of b'deeper' the test will fail.
2921.3.1 by Robert Collins
* Graph ``heads()`` queries have been bugfixed to no longer access all
883
        """
884
        class stub(object):
885
            pass
7143.15.2 by Jelmer Vernooij
Run autopep8.
886
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
887
        def get_parent_map(keys):
888
            result = {}
889
            for key in keys:
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
890
                if key == b'deeper':
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
891
                    self.fail('key deeper was accessed')
892
                result[key] = graph_dict[key]
893
            return result
2921.3.1 by Robert Collins
* Graph ``heads()`` queries have been bugfixed to no longer access all
894
        an_obj = stub()
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
895
        an_obj.get_parent_map = get_parent_map
2921.3.1 by Robert Collins
* Graph ``heads()`` queries have been bugfixed to no longer access all
896
        graph = _mod_graph.Graph(an_obj)
897
        return graph.heads(search)
898
899
    def test_heads_limits_search(self):
900
        # test that a heads query does not search all of history
901
        graph_dict = {
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
902
            b'left': [b'common'],
903
            b'right': [b'common'],
904
            b'common': [b'deeper'],
2921.3.1 by Robert Collins
* Graph ``heads()`` queries have been bugfixed to no longer access all
905
        }
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
906
        self.assertEqual({b'left', b'right'},
7143.15.2 by Jelmer Vernooij
Run autopep8.
907
                         self._run_heads_break_deeper(graph_dict, [b'left', b'right']))
2921.3.1 by Robert Collins
* Graph ``heads()`` queries have been bugfixed to no longer access all
908
909
    def test_heads_limits_search_assymetric(self):
910
        # test that a heads query does not search all of history
911
        graph_dict = {
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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'],
2921.3.1 by Robert Collins
* Graph ``heads()`` queries have been bugfixed to no longer access all
917
        }
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
918
        self.assertEqual({b'left', b'right'},
7143.15.2 by Jelmer Vernooij
Run autopep8.
919
                         self._run_heads_break_deeper(graph_dict, [b'left', b'right']))
2921.3.1 by Robert Collins
* Graph ``heads()`` queries have been bugfixed to no longer access all
920
921
    def test_heads_limits_search_common_search_must_continue(self):
922
        # test that common nodes are still queried, preventing
923
        # all-the-way-to-origin behaviour in the following graph:
924
        graph_dict = {
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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'],
2921.3.1 by Robert Collins
* Graph ``heads()`` queries have been bugfixed to no longer access all
930
        }
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
931
        self.assertEqual({b'h1', b'h2'},
7143.15.2 by Jelmer Vernooij
Run autopep8.
932
                         self._run_heads_break_deeper(graph_dict, [b'h1', b'h2']))
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
933
3177.3.1 by Robert Collins
* New method ``next_with_ghosts`` on the Graph breadth-first-search objects
934
    def test_breadth_first_search_start_ghosts(self):
3184.1.1 by Robert Collins
Add basic get_recipe to the graph breadth first searcher.
935
        graph = self.make_graph({})
3177.3.1 by Robert Collins
* New method ``next_with_ghosts`` on the Graph breadth-first-search objects
936
        # with_ghosts reports the ghosts
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
937
        search = graph._make_breadth_first_searcher([b'a-ghost'])
938
        self.assertEqual((set(), {b'a-ghost'}), search.next_with_ghosts())
3177.3.1 by Robert Collins
* New method ``next_with_ghosts`` on the Graph breadth-first-search objects
939
        self.assertRaises(StopIteration, search.next_with_ghosts)
940
        # next includes them
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
941
        search = graph._make_breadth_first_searcher([b'a-ghost'])
942
        self.assertEqual({b'a-ghost'}, next(search))
6634.2.1 by Martin
Apply 2to3 next fixer and make compatible
943
        self.assertRaises(StopIteration, next, search)
3177.3.1 by Robert Collins
* New method ``next_with_ghosts`` on the Graph breadth-first-search objects
944
945
    def test_breadth_first_search_deep_ghosts(self):
3184.1.1 by Robert Collins
Add basic get_recipe to the graph breadth first searcher.
946
        graph = self.make_graph({
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
947
            b'head': [b'present'],
948
            b'present': [b'child', b'ghost'],
949
            b'child': [],
3184.1.1 by Robert Collins
Add basic get_recipe to the graph breadth first searcher.
950
            })
3177.3.1 by Robert Collins
* New method ``next_with_ghosts`` on the Graph breadth-first-search objects
951
        # with_ghosts reports the ghosts
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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'}),
7143.15.2 by Jelmer Vernooij
Run autopep8.
956
                         search.next_with_ghosts())
3177.3.1 by Robert Collins
* New method ``next_with_ghosts`` on the Graph breadth-first-search objects
957
        self.assertRaises(StopIteration, search.next_with_ghosts)
958
        # next includes them
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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'},
7143.15.2 by Jelmer Vernooij
Run autopep8.
963
                         next(search))
6634.2.1 by Martin
Apply 2to3 next fixer and make compatible
964
        self.assertRaises(StopIteration, next, search)
3177.3.1 by Robert Collins
* New method ``next_with_ghosts`` on the Graph breadth-first-search objects
965
966
    def test_breadth_first_search_change_next_to_next_with_ghosts(self):
3177.3.3 by Robert Collins
Review feedback.
967
        # To make the API robust, we allow calling both next() and
968
        # next_with_ghosts() on the same searcher.
3184.1.1 by Robert Collins
Add basic get_recipe to the graph breadth first searcher.
969
        graph = self.make_graph({
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
970
            b'head': [b'present'],
971
            b'present': [b'child', b'ghost'],
972
            b'child': [],
3184.1.1 by Robert Collins
Add basic get_recipe to the graph breadth first searcher.
973
            })
3177.3.3 by Robert Collins
Review feedback.
974
        # start with next_with_ghosts
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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'}),
7143.15.2 by Jelmer Vernooij
Run autopep8.
979
                         search.next_with_ghosts())
6634.2.1 by Martin
Apply 2to3 next fixer and make compatible
980
        self.assertRaises(StopIteration, next, search)
3177.3.3 by Robert Collins
Review feedback.
981
        # start with next
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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'},
7143.15.2 by Jelmer Vernooij
Run autopep8.
986
                         next(search))
3177.3.1 by Robert Collins
* New method ``next_with_ghosts`` on the Graph breadth-first-search objects
987
        self.assertRaises(StopIteration, search.next_with_ghosts)
988
3177.3.2 by Robert Collins
Update graph searchers stop_searching_any and start_searching for next_with_ghosts.
989
    def test_breadth_first_change_search(self):
3177.3.3 by Robert Collins
Review feedback.
990
        # Changing the search should work with both next and next_with_ghosts.
3184.1.1 by Robert Collins
Add basic get_recipe to the graph breadth first searcher.
991
        graph = self.make_graph({
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
992
            b'head': [b'present'],
993
            b'present': [b'stopped'],
994
            b'other': [b'other_2'],
995
            b'other_2': [],
3184.1.1 by Robert Collins
Add basic get_recipe to the graph breadth first searcher.
996
            })
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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'},
7143.15.2 by Jelmer Vernooij
Run autopep8.
1001
                         search.stop_searching_any([b'present']))
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1002
        self.assertEqual(({b'other'}, {b'other_ghost'}),
7143.15.2 by Jelmer Vernooij
Run autopep8.
1003
                         search.start_searching([b'other', b'other_ghost']))
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1004
        self.assertEqual(({b'other_2'}, set()), search.next_with_ghosts())
3177.3.2 by Robert Collins
Update graph searchers stop_searching_any and start_searching for next_with_ghosts.
1005
        self.assertRaises(StopIteration, search.next_with_ghosts)
1006
        # next includes them
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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'},
7143.15.2 by Jelmer Vernooij
Run autopep8.
1011
                         search.stop_searching_any([b'present']))
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1012
        search.start_searching([b'other', b'other_ghost'])
1013
        self.assertEqual({b'other_2'}, next(search))
6634.2.1 by Martin
Apply 2to3 next fixer and make compatible
1014
        self.assertRaises(StopIteration, next, search)
3177.3.2 by Robert Collins
Update graph searchers stop_searching_any and start_searching for next_with_ghosts.
1015
3184.1.6 by Robert Collins
Create a SearchResult object which can be used as a replacement for sets.
1016
    def assertSeenAndResult(self, instructions, search, next):
1017
        """Check the results of .seen and get_result() for a seach.
3184.1.1 by Robert Collins
Add basic get_recipe to the graph breadth first searcher.
1018
3184.1.6 by Robert Collins
Create a SearchResult object which can be used as a replacement for sets.
1019
        :param instructions: A list of tuples:
1020
            (seen, recipe, included_keys, starts, stops).
1021
            seen, recipe and included_keys are results to check on the search
1022
            and the searches get_result(). starts and stops are parameters to
1023
            pass to start_searching and stop_searching_any during each
1024
            iteration, if they are not None.
3184.1.1 by Robert Collins
Add basic get_recipe to the graph breadth first searcher.
1025
        :param search: The search to use.
1026
        :param next: A callable to advance the search.
1027
        """
3184.1.6 by Robert Collins
Create a SearchResult object which can be used as a replacement for sets.
1028
        for seen, recipe, included_keys, starts, stops in instructions:
4152.1.2 by Robert Collins
Add streaming from a stacked branch when the sort order is compatible with doing so.
1029
            # Adjust for recipe contract changes that don't vary for all the
1030
            # current tests.
1031
            recipe = ('search',) + recipe
3184.1.1 by Robert Collins
Add basic get_recipe to the graph breadth first searcher.
1032
            next()
3184.1.2 by Robert Collins
Add tests for starting and stopping searches in combination with get_recipe.
1033
            if starts is not None:
1034
                search.start_searching(starts)
1035
            if stops is not None:
1036
                search.stop_searching_any(stops)
6341.1.5 by Jelmer Vernooij
Fix get_state().
1037
            state = search.get_state()
1038
            self.assertEqual(set(included_keys), state[2])
3184.1.1 by Robert Collins
Add basic get_recipe to the graph breadth first searcher.
1039
            self.assertEqual(seen, search.seen)
1040
3184.1.6 by Robert Collins
Create a SearchResult object which can be used as a replacement for sets.
1041
    def test_breadth_first_get_result_excludes_current_pending(self):
3184.1.1 by Robert Collins
Add basic get_recipe to the graph breadth first searcher.
1042
        graph = self.make_graph({
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1043
            b'head': [b'child'],
1044
            b'child': [NULL_REVISION],
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
1045
            NULL_REVISION: [],
3184.1.1 by Robert Collins
Add basic get_recipe to the graph breadth first searcher.
1046
            })
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1047
        search = graph._make_breadth_first_searcher([b'head'])
3184.1.1 by Robert Collins
Add basic get_recipe to the graph breadth first searcher.
1048
        # At the start, nothing has been seen, to its all excluded:
6341.1.5 by Jelmer Vernooij
Fix get_state().
1049
        state = search.get_state()
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1050
        self.assertEqual(({b'head'}, {b'head'}, set()),
7143.15.2 by Jelmer Vernooij
Run autopep8.
1051
                         state)
3184.1.1 by Robert Collins
Add basic get_recipe to the graph breadth first searcher.
1052
        self.assertEqual(set(), search.seen)
1053
        # using next:
1054
        expected = [
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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),
3184.1.1 by Robert Collins
Add basic get_recipe to the graph breadth first searcher.
1061
            ]
6634.2.1 by Martin
Apply 2to3 next fixer and make compatible
1062
        self.assertSeenAndResult(expected, search, search.__next__)
3184.1.1 by Robert Collins
Add basic get_recipe to the graph breadth first searcher.
1063
        # using next_with_ghosts:
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1064
        search = graph._make_breadth_first_searcher([b'head'])
3184.1.6 by Robert Collins
Create a SearchResult object which can be used as a replacement for sets.
1065
        self.assertSeenAndResult(expected, search, search.next_with_ghosts)
3184.1.1 by Robert Collins
Add basic get_recipe to the graph breadth first searcher.
1066
3184.1.6 by Robert Collins
Create a SearchResult object which can be used as a replacement for sets.
1067
    def test_breadth_first_get_result_starts_stops(self):
3184.1.2 by Robert Collins
Add tests for starting and stopping searches in combination with get_recipe.
1068
        graph = self.make_graph({
7143.15.2 by Jelmer Vernooij
Run autopep8.
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: []
3184.1.2 by Robert Collins
Add tests for starting and stopping searches in combination with get_recipe.
1075
            })
1076
        search = graph._make_breadth_first_searcher([])
1077
        # Starting with nothing and adding a search works:
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1078
        search.start_searching([b'head'])
3184.1.5 by Robert Collins
Record the number of found revisions for cross checking.
1079
        # head has been seen:
6341.1.5 by Jelmer Vernooij
Fix get_state().
1080
        state = search.get_state()
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1081
        self.assertEqual(({b'head'}, {b'child'}, {b'head'}),
7143.15.2 by Jelmer Vernooij
Run autopep8.
1082
                         state)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1083
        self.assertEqual({b'head'}, search.seen)
3184.1.2 by Robert Collins
Add tests for starting and stopping searches in combination with get_recipe.
1084
        # using next:
1085
        expected = [
1086
            # stop at child, and start a new search at otherhead:
1087
            # - otherhead counts as seen immediately when start_searching is
1088
            # called.
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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),
3184.1.5 by Robert Collins
Record the number of found revisions for cross checking.
1095
            # stop searching excluded now
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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']),
3184.1.2 by Robert Collins
Add tests for starting and stopping searches in combination with get_recipe.
1099
            ]
6634.2.1 by Martin
Apply 2to3 next fixer and make compatible
1100
        self.assertSeenAndResult(expected, search, search.__next__)
3184.1.2 by Robert Collins
Add tests for starting and stopping searches in combination with get_recipe.
1101
        # using next_with_ghosts:
1102
        search = graph._make_breadth_first_searcher([])
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1103
        search.start_searching([b'head'])
3184.1.6 by Robert Collins
Create a SearchResult object which can be used as a replacement for sets.
1104
        self.assertSeenAndResult(expected, search, search.next_with_ghosts)
3184.1.2 by Robert Collins
Add tests for starting and stopping searches in combination with get_recipe.
1105
3184.2.1 by Robert Collins
Handle stopping ghosts in searches properly.
1106
    def test_breadth_first_stop_searching_not_queried(self):
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1107
        # A client should be able to say b'stop node X' even if X has not been
3184.2.1 by Robert Collins
Handle stopping ghosts in searches properly.
1108
        # returned to the client.
1109
        graph = self.make_graph({
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1110
            b'head': [b'child', b'ghost1'],
1111
            b'child': [NULL_REVISION],
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
1112
            NULL_REVISION: [],
3184.2.1 by Robert Collins
Handle stopping ghosts in searches properly.
1113
            })
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1114
        search = graph._make_breadth_first_searcher([b'head'])
3184.2.1 by Robert Collins
Handle stopping ghosts in searches properly.
1115
        expected = [
1116
            # NULL_REVISION and ghost1 have not been returned
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1117
            ({b'head'},
1118
             ({b'head'}, {b'child', NULL_REVISION, b'ghost1'}, 1),
1119
             [b'head'], None, [NULL_REVISION, b'ghost1']),
3184.2.1 by Robert Collins
Handle stopping ghosts in searches properly.
1120
            # ghost1 has been returned, NULL_REVISION is to be returned in the
1121
            # next iteration.
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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']),
3184.2.1 by Robert Collins
Handle stopping ghosts in searches properly.
1125
            ]
6634.2.1 by Martin
Apply 2to3 next fixer and make compatible
1126
        self.assertSeenAndResult(expected, search, search.__next__)
3184.2.1 by Robert Collins
Handle stopping ghosts in searches properly.
1127
        # using next_with_ghosts:
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1128
        search = graph._make_breadth_first_searcher([b'head'])
3184.2.1 by Robert Collins
Handle stopping ghosts in searches properly.
1129
        self.assertSeenAndResult(expected, search, search.next_with_ghosts)
1130
3808.1.1 by Andrew Bennetts
Possible fix for bug in new _walk_to_common_revisions.
1131
    def test_breadth_first_stop_searching_late(self):
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1132
        # A client should be able to say b'stop node X' and have it excluded
3808.1.1 by Andrew Bennetts
Possible fix for bug in new _walk_to_common_revisions.
1133
        # from the result even if X was seen in an older iteration of the
1134
        # search.
1135
        graph = self.make_graph({
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1136
            b'head': [b'middle'],
1137
            b'middle': [b'child'],
1138
            b'child': [NULL_REVISION],
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
1139
            NULL_REVISION: [],
3808.1.1 by Andrew Bennetts
Possible fix for bug in new _walk_to_common_revisions.
1140
            })
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1141
        search = graph._make_breadth_first_searcher([b'head'])
3808.1.1 by Andrew Bennetts
Possible fix for bug in new _walk_to_common_revisions.
1142
        expected = [
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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
3808.1.1 by Andrew Bennetts
Possible fix for bug in new _walk_to_common_revisions.
1148
            # searching it until *after* advancing the searcher.
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1149
            ({b'head', b'middle', b'child'},
1150
             ({b'head'}, {b'middle', b'child'}, 1),
1151
             [b'head'], None, [b'middle', b'child']),
3808.1.3 by Andrew Bennetts
Add another test, this one currently failing. Perhaps the current behaviour should be considered ok, rather than a failure?
1152
            ]
6634.2.1 by Martin
Apply 2to3 next fixer and make compatible
1153
        self.assertSeenAndResult(expected, search, search.__next__)
3808.1.3 by Andrew Bennetts
Add another test, this one currently failing. Perhaps the current behaviour should be considered ok, rather than a failure?
1154
        # using next_with_ghosts:
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1155
        search = graph._make_breadth_first_searcher([b'head'])
3808.1.3 by Andrew Bennetts
Add another test, this one currently failing. Perhaps the current behaviour should be considered ok, rather than a failure?
1156
        self.assertSeenAndResult(expected, search, search.next_with_ghosts)
1157
3184.1.6 by Robert Collins
Create a SearchResult object which can be used as a replacement for sets.
1158
    def test_breadth_first_get_result_ghosts_are_excluded(self):
3184.1.3 by Robert Collins
Automatically exclude ghosts.
1159
        graph = self.make_graph({
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1160
            b'head': [b'child', b'ghost'],
1161
            b'child': [NULL_REVISION],
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
1162
            NULL_REVISION: [],
3184.1.3 by Robert Collins
Automatically exclude ghosts.
1163
            })
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1164
        search = graph._make_breadth_first_searcher([b'head'])
3184.1.3 by Robert Collins
Automatically exclude ghosts.
1165
        # using next:
1166
        expected = [
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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),
3184.1.3 by Robert Collins
Automatically exclude ghosts.
1173
            ]
6634.2.1 by Martin
Apply 2to3 next fixer and make compatible
1174
        self.assertSeenAndResult(expected, search, search.__next__)
3184.1.3 by Robert Collins
Automatically exclude ghosts.
1175
        # using next_with_ghosts:
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1176
        search = graph._make_breadth_first_searcher([b'head'])
3184.1.6 by Robert Collins
Create a SearchResult object which can be used as a replacement for sets.
1177
        self.assertSeenAndResult(expected, search, search.next_with_ghosts)
3184.1.3 by Robert Collins
Automatically exclude ghosts.
1178
3184.1.6 by Robert Collins
Create a SearchResult object which can be used as a replacement for sets.
1179
    def test_breadth_first_get_result_starting_a_ghost_ghost_is_excluded(self):
3184.1.4 by Robert Collins
Correctly exclude ghosts when ghosts are started on an existing search.
1180
        graph = self.make_graph({
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1181
            b'head': [b'child'],
1182
            b'child': [NULL_REVISION],
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
1183
            NULL_REVISION: [],
3184.1.4 by Robert Collins
Correctly exclude ghosts when ghosts are started on an existing search.
1184
            })
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1185
        search = graph._make_breadth_first_searcher([b'head'])
3184.1.4 by Robert Collins
Correctly exclude ghosts when ghosts are started on an existing search.
1186
        # using next:
1187
        expected = [
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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),
3184.1.5 by Robert Collins
Record the number of found revisions for cross checking.
1194
            ]
6634.2.1 by Martin
Apply 2to3 next fixer and make compatible
1195
        self.assertSeenAndResult(expected, search, search.__next__)
3184.1.5 by Robert Collins
Record the number of found revisions for cross checking.
1196
        # using next_with_ghosts:
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1197
        search = graph._make_breadth_first_searcher([b'head'])
3184.1.6 by Robert Collins
Create a SearchResult object which can be used as a replacement for sets.
1198
        self.assertSeenAndResult(expected, search, search.next_with_ghosts)
3184.1.5 by Robert Collins
Record the number of found revisions for cross checking.
1199
1200
    def test_breadth_first_revision_count_includes_NULL_REVISION(self):
1201
        graph = self.make_graph({
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1202
            b'head': [NULL_REVISION],
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
1203
            NULL_REVISION: [],
3184.1.5 by Robert Collins
Record the number of found revisions for cross checking.
1204
            })
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1205
        search = graph._make_breadth_first_searcher([b'head'])
3184.1.5 by Robert Collins
Record the number of found revisions for cross checking.
1206
        # using next:
1207
        expected = [
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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),
3184.1.5 by Robert Collins
Record the number of found revisions for cross checking.
1214
            ]
6634.2.1 by Martin
Apply 2to3 next fixer and make compatible
1215
        self.assertSeenAndResult(expected, search, search.__next__)
3184.1.5 by Robert Collins
Record the number of found revisions for cross checking.
1216
        # using next_with_ghosts:
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1217
        search = graph._make_breadth_first_searcher([b'head'])
3184.1.6 by Robert Collins
Create a SearchResult object which can be used as a replacement for sets.
1218
        self.assertSeenAndResult(expected, search, search.next_with_ghosts)
3184.1.5 by Robert Collins
Record the number of found revisions for cross checking.
1219
3184.1.6 by Robert Collins
Create a SearchResult object which can be used as a replacement for sets.
1220
    def test_breadth_first_search_get_result_after_StopIteration(self):
3184.1.5 by Robert Collins
Record the number of found revisions for cross checking.
1221
        # StopIteration should not invalid anything..
1222
        graph = self.make_graph({
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1223
            b'head': [NULL_REVISION],
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
1224
            NULL_REVISION: [],
3184.1.5 by Robert Collins
Record the number of found revisions for cross checking.
1225
            })
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1226
        search = graph._make_breadth_first_searcher([b'head'])
3184.1.5 by Robert Collins
Record the number of found revisions for cross checking.
1227
        # using next:
1228
        expected = [
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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),
3184.1.5 by Robert Collins
Record the number of found revisions for cross checking.
1235
            ]
6634.2.1 by Martin
Apply 2to3 next fixer and make compatible
1236
        self.assertSeenAndResult(expected, search, search.__next__)
1237
        self.assertRaises(StopIteration, next, search)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1238
        self.assertEqual({b'head', b'ghost', NULL_REVISION}, search.seen)
6341.1.5 by Jelmer Vernooij
Fix get_state().
1239
        state = search.get_state()
1240
        self.assertEqual(
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1241
            ({b'ghost', b'head'}, {b'ghost'},
1242
                {b'head', NULL_REVISION}),
6341.1.5 by Jelmer Vernooij
Fix get_state().
1243
            state)
3184.1.5 by Robert Collins
Record the number of found revisions for cross checking.
1244
        # using next_with_ghosts:
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1245
        search = graph._make_breadth_first_searcher([b'head'])
3184.1.6 by Robert Collins
Create a SearchResult object which can be used as a replacement for sets.
1246
        self.assertSeenAndResult(expected, search, search.next_with_ghosts)
6634.2.1 by Martin
Apply 2to3 next fixer and make compatible
1247
        self.assertRaises(StopIteration, next, search)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1248
        self.assertEqual({b'head', b'ghost', NULL_REVISION}, search.seen)
6341.1.5 by Jelmer Vernooij
Fix get_state().
1249
        state = search.get_state()
1250
        self.assertEqual(
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1251
            ({b'ghost', b'head'}, {b'ghost'},
1252
                {b'head', NULL_REVISION}),
6341.1.5 by Jelmer Vernooij
Fix get_state().
1253
            state)
3184.1.4 by Robert Collins
Correctly exclude ghosts when ghosts are started on an existing search.
1254
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
1255
3445.1.2 by John Arbash Meinel
Handle when a known revision is an ancestor.
1256
class TestFindUniqueAncestors(TestGraphBase):
3377.3.24 by John Arbash Meinel
For some reason find_unique_ancestors is much slower than it should be.
1257
3377.3.21 by John Arbash Meinel
Simple brute-force implementation of find_unique_ancestors
1258
    def assertFindUniqueAncestors(self, graph, expected, node, common):
1259
        actual = graph.find_unique_ancestors(node, common)
1260
        self.assertEqual(expected, sorted(actual))
1261
1262
    def test_empty_set(self):
1263
        graph = self.make_graph(ancestry_1)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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'])
3377.3.21 by John Arbash Meinel
Simple brute-force implementation of find_unique_ancestors
1267
1268
    def test_single_node(self):
1269
        graph = self.make_graph(ancestry_1)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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'])
3377.3.21 by John Arbash Meinel
Simple brute-force implementation of find_unique_ancestors
1273
3377.3.24 by John Arbash Meinel
For some reason find_unique_ancestors is much slower than it should be.
1274
    def test_minimal_ancestry(self):
1275
        graph = self.make_breaking_graph(extended_history_shortcut,
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1276
                                         [NULL_REVISION, b'a', b'b'])
1277
        self.assertFindUniqueAncestors(graph, [b'e'], b'e', [b'd'])
3377.3.24 by John Arbash Meinel
For some reason find_unique_ancestors is much slower than it should be.
1278
3377.3.25 by John Arbash Meinel
A few more minimal ancestry checks
1279
        graph = self.make_breaking_graph(extended_history_shortcut,
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1280
                                         [b'b'])
1281
        self.assertFindUniqueAncestors(graph, [b'f'], b'f', [b'a', b'd'])
3377.3.25 by John Arbash Meinel
A few more minimal ancestry checks
1282
1283
        graph = self.make_breaking_graph(complex_shortcut,
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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'])
3377.3.26 by John Arbash Meinel
Found a graph leak.
1289
3377.3.21 by John Arbash Meinel
Simple brute-force implementation of find_unique_ancestors
1290
    def test_in_ancestry(self):
1291
        graph = self.make_graph(ancestry_1)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1292
        self.assertFindUniqueAncestors(graph, [], b'rev1', [b'rev3'])
1293
        self.assertFindUniqueAncestors(graph, [], b'rev2b', [b'rev4'])
3377.3.21 by John Arbash Meinel
Simple brute-force implementation of find_unique_ancestors
1294
1295
    def test_multiple_revisions(self):
1296
        graph = self.make_graph(ancestry_1)
1297
        self.assertFindUniqueAncestors(graph,
7143.15.2 by Jelmer Vernooij
Run autopep8.
1298
                                       [b'rev4'], b'rev4', [b'rev3', b'rev2b'])
3377.3.21 by John Arbash Meinel
Simple brute-force implementation of find_unique_ancestors
1299
        self.assertFindUniqueAncestors(graph,
7143.15.2 by Jelmer Vernooij
Run autopep8.
1300
                                       [b'rev2a', b'rev3', b'rev4'], b'rev4', [b'rev2b'])
3377.3.21 by John Arbash Meinel
Simple brute-force implementation of find_unique_ancestors
1301
3377.3.22 by John Arbash Meinel
include some tests using the complex graphs
1302
    def test_complex_shortcut(self):
1303
        graph = self.make_graph(complex_shortcut)
1304
        self.assertFindUniqueAncestors(graph,
7143.15.2 by Jelmer Vernooij
Run autopep8.
1305
                                       [b'h', b'n'], b'n', [b'm'])
3377.3.22 by John Arbash Meinel
include some tests using the complex graphs
1306
        self.assertFindUniqueAncestors(graph,
7143.15.2 by Jelmer Vernooij
Run autopep8.
1307
                                       [b'e', b'i', b'm'], b'm', [b'n'])
3377.3.22 by John Arbash Meinel
include some tests using the complex graphs
1308
1309
    def test_complex_shortcut2(self):
1310
        graph = self.make_graph(complex_shortcut2)
1311
        self.assertFindUniqueAncestors(graph,
7143.15.2 by Jelmer Vernooij
Run autopep8.
1312
                                       [b'j', b'u'], b'u', [b't'])
3377.3.22 by John Arbash Meinel
include some tests using the complex graphs
1313
        self.assertFindUniqueAncestors(graph,
7143.15.2 by Jelmer Vernooij
Run autopep8.
1314
                                       [b't'], b't', [b'u'])
3377.3.22 by John Arbash Meinel
include some tests using the complex graphs
1315
3377.3.35 by John Arbash Meinel
Add a test that exercises the multiple interesting unique code
1316
    def test_multiple_interesting_unique(self):
1317
        graph = self.make_graph(multiple_interesting_unique)
1318
        self.assertFindUniqueAncestors(graph,
7143.15.2 by Jelmer Vernooij
Run autopep8.
1319
                                       [b'j', b'y'], b'y', [b'z'])
3377.3.35 by John Arbash Meinel
Add a test that exercises the multiple interesting unique code
1320
        self.assertFindUniqueAncestors(graph,
7143.15.2 by Jelmer Vernooij
Run autopep8.
1321
                                       [b'p', b'z'], b'z', [b'y'])
3377.3.35 by John Arbash Meinel
Add a test that exercises the multiple interesting unique code
1322
3377.3.36 by John Arbash Meinel
Small updates, try to write a test for the race condition.
1323
    def test_racing_shortcuts(self):
1324
        graph = self.make_graph(racing_shortcuts)
1325
        self.assertFindUniqueAncestors(graph,
7143.15.2 by Jelmer Vernooij
Run autopep8.
1326
                                       [b'p', b'q', b'z'], b'z', [b'y'])
3377.3.36 by John Arbash Meinel
Small updates, try to write a test for the race condition.
1327
        self.assertFindUniqueAncestors(graph,
7143.15.2 by Jelmer Vernooij
Run autopep8.
1328
                                       [b'h', b'i', b'j', b'y'], b'j', [b'z'])
3377.3.36 by John Arbash Meinel
Small updates, try to write a test for the race condition.
1329
3377.3.21 by John Arbash Meinel
Simple brute-force implementation of find_unique_ancestors
1330
3445.1.4 by John Arbash Meinel
Change the function to be called 'find_distance_to_null'
1331
class TestGraphFindDistanceToNull(TestGraphBase):
3445.1.1 by John Arbash Meinel
Start working on a new Graph api to make finding revision numbers faster.
1332
    """Test an api that should be able to compute a revno"""
1333
3445.1.4 by John Arbash Meinel
Change the function to be called 'find_distance_to_null'
1334
    def assertFindDistance(self, revno, graph, target_id, known_ids):
1335
        """Assert the output of Graph.find_distance_to_null()"""
1336
        actual = graph.find_distance_to_null(target_id, known_ids)
3445.1.1 by John Arbash Meinel
Start working on a new Graph api to make finding revision numbers faster.
1337
        self.assertEqual(revno, actual)
1338
1339
    def test_nothing_known(self):
1340
        graph = self.make_graph(ancestry_1)
3445.1.4 by John Arbash Meinel
Change the function to be called 'find_distance_to_null'
1341
        self.assertFindDistance(0, graph, NULL_REVISION, [])
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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', [])
3445.1.1 by John Arbash Meinel
Start working on a new Graph api to make finding revision numbers faster.
1347
1348
    def test_rev_is_ghost(self):
1349
        graph = self.make_graph(ancestry_1)
1350
        e = self.assertRaises(errors.GhostRevisionsHaveNoRevno,
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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)
3445.1.1 by John Arbash Meinel
Start working on a new Graph api to make finding revision numbers faster.
1354
1355
    def test_ancestor_is_ghost(self):
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1356
        graph = self.make_graph({b'rev': [b'parent']})
3445.1.1 by John Arbash Meinel
Start working on a new Graph api to make finding revision numbers faster.
1357
        e = self.assertRaises(errors.GhostRevisionsHaveNoRevno,
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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)
3445.1.1 by John Arbash Meinel
Start working on a new Graph api to make finding revision numbers faster.
1361
3445.1.2 by John Arbash Meinel
Handle when a known revision is an ancestor.
1362
    def test_known_in_ancestry(self):
1363
        graph = self.make_graph(ancestry_1)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1364
        self.assertFindDistance(2, graph, b'rev2a', [(b'rev1', 1)])
1365
        self.assertFindDistance(3, graph, b'rev3', [(b'rev2a', 2)])
3445.1.2 by John Arbash Meinel
Handle when a known revision is an ancestor.
1366
1367
    def test_known_in_ancestry_limits(self):
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1368
        graph = self.make_breaking_graph(ancestry_1, [b'rev1'])
1369
        self.assertFindDistance(4, graph, b'rev4', [(b'rev3', 3)])
3445.1.2 by John Arbash Meinel
Handle when a known revision is an ancestor.
1370
3445.1.3 by John Arbash Meinel
Search from all of the known revisions.
1371
    def test_target_is_ancestor(self):
1372
        graph = self.make_graph(ancestry_1)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1373
        self.assertFindDistance(2, graph, b'rev2a', [(b'rev3', 3)])
3445.1.3 by John Arbash Meinel
Search from all of the known revisions.
1374
1375
    def test_target_is_ancestor_limits(self):
1376
        """We shouldn't search all history if we run into ourselves"""
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1377
        graph = self.make_breaking_graph(ancestry_1, [b'rev1'])
1378
        self.assertFindDistance(3, graph, b'rev3', [(b'rev4', 4)])
3445.1.3 by John Arbash Meinel
Search from all of the known revisions.
1379
1380
    def test_target_parallel_to_known_limits(self):
3445.1.8 by John Arbash Meinel
Clarity tweaks recommended by Ian
1381
        # Even though the known revision isn't part of the other ancestry, they
3445.1.3 by John Arbash Meinel
Search from all of the known revisions.
1382
        # eventually converge
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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)])
3445.1.3 by John Arbash Meinel
Search from all of the known revisions.
1388
3445.1.1 by John Arbash Meinel
Start working on a new Graph api to make finding revision numbers faster.
1389
3514.2.8 by John Arbash Meinel
The insertion ordering into the weave has an impact on conflicts.
1390
class TestFindMergeOrder(TestGraphBase):
1391
1392
    def assertMergeOrder(self, expected, graph, tip, base_revisions):
1393
        self.assertEqual(expected, graph.find_merge_order(tip, base_revisions))
1394
1395
    def test_parents(self):
1396
        graph = self.make_graph(ancestry_1)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1397
        self.assertMergeOrder([b'rev3', b'rev2b'], graph, b'rev4',
7143.15.2 by Jelmer Vernooij
Run autopep8.
1398
                              [b'rev3', b'rev2b'])
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1399
        self.assertMergeOrder([b'rev3', b'rev2b'], graph, b'rev4',
7143.15.2 by Jelmer Vernooij
Run autopep8.
1400
                              [b'rev2b', b'rev3'])
3514.2.8 by John Arbash Meinel
The insertion ordering into the weave has an impact on conflicts.
1401
1402
    def test_ancestors(self):
1403
        graph = self.make_graph(ancestry_1)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1404
        self.assertMergeOrder([b'rev1', b'rev2b'], graph, b'rev4',
7143.15.2 by Jelmer Vernooij
Run autopep8.
1405
                              [b'rev1', b'rev2b'])
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1406
        self.assertMergeOrder([b'rev1', b'rev2b'], graph, b'rev4',
7143.15.2 by Jelmer Vernooij
Run autopep8.
1407
                              [b'rev2b', b'rev1'])
3514.2.8 by John Arbash Meinel
The insertion ordering into the weave has an impact on conflicts.
1408
1409
    def test_shortcut_one_ancestor(self):
1410
        # When we have enough info, we can stop searching
7143.15.2 by Jelmer Vernooij
Run autopep8.
1411
        graph = self.make_breaking_graph(
1412
            ancestry_1, [b'rev3', b'rev2b', b'rev4'])
3514.2.8 by John Arbash Meinel
The insertion ordering into the weave has an impact on conflicts.
1413
        # Single ancestors shortcut right away
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1414
        self.assertMergeOrder([b'rev3'], graph, b'rev4', [b'rev3'])
3514.2.8 by John Arbash Meinel
The insertion ordering into the weave has an impact on conflicts.
1415
1416
    def test_shortcut_after_one_ancestor(self):
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1417
        graph = self.make_breaking_graph(ancestry_1, [b'rev2a', b'rev2b'])
7143.15.2 by Jelmer Vernooij
Run autopep8.
1418
        self.assertMergeOrder([b'rev3', b'rev1'], graph,
1419
                              b'rev4', [b'rev1', b'rev3'])
3514.2.8 by John Arbash Meinel
The insertion ordering into the weave has an impact on conflicts.
1420
1421
5365.6.1 by Aaron Bentley
Implement find_descendants.
1422
class TestFindDescendants(TestGraphBase):
1423
1424
    def test_find_descendants_rev1_rev3(self):
1425
        graph = self.make_graph(ancestry_1)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1426
        descendants = graph.find_descendants(b'rev1', b'rev3')
1427
        self.assertEqual({b'rev1', b'rev2a', b'rev3'}, descendants)
5365.6.1 by Aaron Bentley
Implement find_descendants.
1428
1429
    def test_find_descendants_rev1_rev4(self):
1430
        graph = self.make_graph(ancestry_1)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1431
        descendants = graph.find_descendants(b'rev1', b'rev4')
1432
        self.assertEqual({b'rev1', b'rev2a', b'rev2b', b'rev3', b'rev4'},
5365.6.1 by Aaron Bentley
Implement find_descendants.
1433
                         descendants)
1434
1435
    def test_find_descendants_rev2a_rev4(self):
1436
        graph = self.make_graph(ancestry_1)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1437
        descendants = graph.find_descendants(b'rev2a', b'rev4')
1438
        self.assertEqual({b'rev2a', b'rev3', b'rev4'}, descendants)
5365.6.1 by Aaron Bentley
Implement find_descendants.
1439
7143.15.2 by Jelmer Vernooij
Run autopep8.
1440
5365.6.3 by Aaron Bentley
Implement find_lefthand_merger.
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):
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1448
        self.check_merger(b'rev4', ancestry_1, b'rev2b', b'rev4')
5365.6.3 by Aaron Bentley
Implement find_lefthand_merger.
1449
1450
    def test_find_lefthand_merger_rev2a(self):
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1451
        self.check_merger(b'rev2a', ancestry_1, b'rev2a', b'rev4')
5365.6.3 by Aaron Bentley
Implement find_lefthand_merger.
1452
1453
    def test_find_lefthand_merger_rev4(self):
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1454
        self.check_merger(None, ancestry_1, b'rev4', b'rev2a')
5365.6.3 by Aaron Bentley
Implement find_lefthand_merger.
1455
1456
    def test_find_lefthand_merger_f(self):
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1457
        self.check_merger(b'i', complex_shortcut, b'f', b'm')
5365.6.3 by Aaron Bentley
Implement find_lefthand_merger.
1458
1459
    def test_find_lefthand_merger_g(self):
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1460
        self.check_merger(b'i', complex_shortcut, b'g', b'm')
5365.6.3 by Aaron Bentley
Implement find_lefthand_merger.
1461
1462
    def test_find_lefthand_merger_h(self):
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1463
        self.check_merger(b'n', complex_shortcut, b'h', b'n')
5365.6.3 by Aaron Bentley
Implement find_lefthand_merger.
1464
5365.6.1 by Aaron Bentley
Implement find_descendants.
1465
1466
class TestGetChildMap(TestGraphBase):
1467
1468
    def test_get_child_map(self):
1469
        graph = self.make_graph(ancestry_1)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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']},
7143.15.2 by Jelmer Vernooij
Run autopep8.
1475
                         child_map)
5365.6.1 by Aaron Bentley
Implement find_descendants.
1476
1477
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
1478
class TestCachingParentsProvider(tests.TestCase):
4190.1.1 by Robert Collins
Negatively cache misses during read-locks in RemoteRepository.
1479
    """These tests run with:
1480
1481
    self.inst_pp, a recording parents provider with a graph of a->b, and b is a
1482
    ghost.
1483
    self.caching_pp, a CachingParentsProvider layered on inst_pp.
1484
    """
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
1485
1486
    def setUp(self):
1487
        super(TestCachingParentsProvider, self).setUp()
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1488
        dict_pp = _mod_graph.DictParentsProvider({b'a': (b'b',)})
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
1489
        self.inst_pp = InstrumentedParentsProvider(dict_pp)
1490
        self.caching_pp = _mod_graph.CachingParentsProvider(self.inst_pp)
1491
3099.3.3 by John Arbash Meinel
Deprecate get_parents() in favor of get_parent_map()
1492
    def test_get_parent_map(self):
1493
        """Requesting the same revision should be returned from cache"""
3835.1.16 by Aaron Bentley
Updates from review
1494
        self.assertEqual({}, self.caching_pp._cache)
7143.15.2 by Jelmer Vernooij
Run autopep8.
1495
        self.assertEqual(
1496
            {b'a': (b'b',)}, self.caching_pp.get_parent_map([b'a']))
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1497
        self.assertEqual([b'a'], self.inst_pp.calls)
7143.15.2 by Jelmer Vernooij
Run autopep8.
1498
        self.assertEqual(
1499
            {b'a': (b'b',)}, self.caching_pp.get_parent_map([b'a']))
3099.3.3 by John Arbash Meinel
Deprecate get_parents() in favor of get_parent_map()
1500
        # No new call, as it should have been returned from the cache
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1501
        self.assertEqual([b'a'], self.inst_pp.calls)
7143.15.2 by Jelmer Vernooij
Run autopep8.
1502
        self.assertEqual({b'a': (b'b',)}, self.caching_pp._cache)
3099.3.3 by John Arbash Meinel
Deprecate get_parents() in favor of get_parent_map()
1503
1504
    def test_get_parent_map_not_present(self):
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
1505
        """The cache should also track when a revision doesn't exist"""
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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']))
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
1509
        # No new calls
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1510
        self.assertEqual([b'b'], self.inst_pp.calls)
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
1511
3099.3.3 by John Arbash Meinel
Deprecate get_parents() in favor of get_parent_map()
1512
    def test_get_parent_map_mixed(self):
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
1513
        """Anything that can be returned from cache, should be"""
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1514
        self.assertEqual({}, self.caching_pp.get_parent_map([b'b']))
1515
        self.assertEqual([b'b'], self.inst_pp.calls)
7143.15.2 by Jelmer Vernooij
Run autopep8.
1516
        self.assertEqual({b'a': (b'b',)},
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1517
                         self.caching_pp.get_parent_map([b'a', b'b']))
1518
        self.assertEqual([b'b', b'a'], self.inst_pp.calls)
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
1519
3099.3.3 by John Arbash Meinel
Deprecate get_parents() in favor of get_parent_map()
1520
    def test_get_parent_map_repeated(self):
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
1521
        """Asking for the same parent 2x will only forward 1 request."""
7143.15.2 by Jelmer Vernooij
Run autopep8.
1522
        self.assertEqual({b'a': (b'b',)},
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1523
                         self.caching_pp.get_parent_map([b'b', b'a', b'b']))
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
1524
        # Use sorted because we don't care about the order, just that each is
1525
        # only present 1 time.
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1526
        self.assertEqual([b'a', b'b'], sorted(self.inst_pp.calls))
3514.2.14 by John Arbash Meinel
Bring in the code to collapse linear portions of the graph.
1527
4190.1.4 by Robert Collins
Cache ghosts when we can get them from a RemoteRepository in get_parent_map.
1528
    def test_note_missing_key(self):
1529
        """After noting that a key is missing it is cached."""
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1530
        self.caching_pp.note_missing_key(b'b')
1531
        self.assertEqual({}, self.caching_pp.get_parent_map([b'b']))
4190.1.4 by Robert Collins
Cache ghosts when we can get them from a RemoteRepository in get_parent_map.
1532
        self.assertEqual([], self.inst_pp.calls)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1533
        self.assertEqual({b'b'}, self.caching_pp.missing_keys)
4190.1.4 by Robert Collins
Cache ghosts when we can get them from a RemoteRepository in get_parent_map.
1534
6015.24.3 by John Arbash Meinel
Implement get_cached_parent_map
1535
    def test_get_cached_parent_map(self):
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1536
        self.assertEqual({}, self.caching_pp.get_cached_parent_map([b'a']))
6015.24.3 by John Arbash Meinel
Implement get_cached_parent_map
1537
        self.assertEqual([], self.inst_pp.calls)
7143.15.2 by Jelmer Vernooij
Run autopep8.
1538
        self.assertEqual(
1539
            {b'a': (b'b',)}, self.caching_pp.get_parent_map([b'a']))
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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']))
6015.24.3 by John Arbash Meinel
Implement get_cached_parent_map
1543
3514.2.14 by John Arbash Meinel
Bring in the code to collapse linear portions of the graph.
1544
3835.1.12 by Aaron Bentley
Unify CachingExtraParentsProvider and CachingParentsProvider.
1545
class TestCachingParentsProviderExtras(tests.TestCaseWithTransport):
3835.1.16 by Aaron Bentley
Updates from review
1546
    """Test the behaviour when parents are provided that were not requested."""
3835.1.10 by Aaron Bentley
Move CachingExtraParentsProvider to Graph
1547
1548
    def setUp(self):
3835.1.12 by Aaron Bentley
Unify CachingExtraParentsProvider and CachingParentsProvider.
1549
        super(TestCachingParentsProviderExtras, self).setUp()
7143.15.2 by Jelmer Vernooij
Run autopep8.
1550
3835.1.11 by Aaron Bentley
Rename FakeParentsProvider to ExtraParentsProvider
1551
        class ExtraParentsProvider(object):
3835.1.10 by Aaron Bentley
Move CachingExtraParentsProvider to Graph
1552
1553
            def get_parent_map(self, keys):
7143.15.2 by Jelmer Vernooij
Run autopep8.
1554
                return {b'rev1': [], b'rev2': [b'rev1', ]}
3835.1.10 by Aaron Bentley
Move CachingExtraParentsProvider to Graph
1555
3835.1.11 by Aaron Bentley
Rename FakeParentsProvider to ExtraParentsProvider
1556
        self.inst_pp = InstrumentedParentsProvider(ExtraParentsProvider())
3835.1.12 by Aaron Bentley
Unify CachingExtraParentsProvider and CachingParentsProvider.
1557
        self.caching_pp = _mod_graph.CachingParentsProvider(
1558
            get_parent_map=self.inst_pp.get_parent_map)
3835.1.10 by Aaron Bentley
Move CachingExtraParentsProvider to Graph
1559
1560
    def test_uncached(self):
3835.1.12 by Aaron Bentley
Unify CachingExtraParentsProvider and CachingParentsProvider.
1561
        self.caching_pp.disable_cache()
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1562
        self.assertEqual({b'rev1': []},
1563
                         self.caching_pp.get_parent_map([b'rev1']))
1564
        self.assertEqual([b'rev1'], self.inst_pp.calls)
3835.1.16 by Aaron Bentley
Updates from review
1565
        self.assertIs(None, self.caching_pp._cache)
1566
1567
    def test_cache_initially_empty(self):
1568
        self.assertEqual({}, self.caching_pp._cache)
3835.1.10 by Aaron Bentley
Move CachingExtraParentsProvider to Graph
1569
1570
    def test_cached(self):
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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']},
3835.1.16 by Aaron Bentley
Updates from review
1575
                         self.caching_pp._cache)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1576
        self.assertEqual({b'rev1': []},
7143.15.2 by Jelmer Vernooij
Run autopep8.
1577
                         self.caching_pp.get_parent_map([b'rev1']))
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1578
        self.assertEqual([b'rev1'], self.inst_pp.calls)
3835.1.16 by Aaron Bentley
Updates from review
1579
1580
    def test_disable_cache_clears_cache(self):
1581
        # Put something in the cache
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1582
        self.caching_pp.get_parent_map([b'rev1'])
3835.1.16 by Aaron Bentley
Updates from review
1583
        self.assertEqual(2, len(self.caching_pp._cache))
3835.1.10 by Aaron Bentley
Move CachingExtraParentsProvider to Graph
1584
        self.caching_pp.disable_cache()
3835.1.16 by Aaron Bentley
Updates from review
1585
        self.assertIs(None, self.caching_pp._cache)
3835.1.10 by Aaron Bentley
Move CachingExtraParentsProvider to Graph
1586
3835.1.19 by Aaron Bentley
Raise exception when caching is enabled twice.
1587
    def test_enable_cache_raises(self):
3835.1.20 by Aaron Bentley
Change custom error to an AssertionError.
1588
        e = self.assertRaises(AssertionError, self.caching_pp.enable_cache)
3835.1.19 by Aaron Bentley
Raise exception when caching is enabled twice.
1589
        self.assertEqual('Cache enabled when already enabled.', str(e))
1590
3835.1.15 by Aaron Bentley
Allow miss caching to be disabled.
1591
    def test_cache_misses(self):
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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)
3835.1.15 by Aaron Bentley
Allow miss caching to be disabled.
1595
1596
    def test_no_cache_misses(self):
3835.1.19 by Aaron Bentley
Raise exception when caching is enabled twice.
1597
        self.caching_pp.disable_cache()
3835.1.15 by Aaron Bentley
Allow miss caching to be disabled.
1598
        self.caching_pp.enable_cache(cache_misses=False)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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)
3835.1.15 by Aaron Bentley
Allow miss caching to be disabled.
1602
3835.1.10 by Aaron Bentley
Move CachingExtraParentsProvider to Graph
1603
    def test_cache_extras(self):
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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)
3835.1.10 by Aaron Bentley
Move CachingExtraParentsProvider to Graph
1608
6015.24.3 by John Arbash Meinel
Implement get_cached_parent_map
1609
    def test_extras_using_cached(self):
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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)
6015.24.3 by John Arbash Meinel
Implement get_cached_parent_map
1615
1616
3514.2.14 by John Arbash Meinel
Bring in the code to collapse linear portions of the graph.
1617
class TestCollapseLinearRegions(tests.TestCase):
1618
1619
    def assertCollapsed(self, collapsed, original):
1620
        self.assertEqual(collapsed,
1621
                         _mod_graph.collapse_linear_regions(original))
1622
1623
    def test_collapse_nothing(self):
7143.15.2 by Jelmer Vernooij
Run autopep8.
1624
        d = {1: [2, 3], 2: [], 3: []}
3514.2.14 by John Arbash Meinel
Bring in the code to collapse linear portions of the graph.
1625
        self.assertCollapsed(d, d)
7143.15.2 by Jelmer Vernooij
Run autopep8.
1626
        d = {1: [2], 2: [3, 4], 3: [5], 4: [5], 5: []}
3514.2.14 by John Arbash Meinel
Bring in the code to collapse linear portions of the graph.
1627
        self.assertCollapsed(d, d)
1628
1629
    def test_collapse_chain(self):
1630
        # Any time we have a linear chain, we should be able to collapse
7143.15.2 by Jelmer Vernooij
Run autopep8.
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)
3514.2.14 by John Arbash Meinel
Bring in the code to collapse linear portions of the graph.
1637
1638
    def test_collapse_with_multiple_children(self):
1639
        #    7
1640
        #    |
1641
        #    6
1642
        #   / \
1643
        #  4   5
1644
        #  |   |
3514.2.16 by John Arbash Meinel
Review feedback from Ian.
1645
        #  2   3
3514.2.14 by John Arbash Meinel
Bring in the code to collapse linear portions of the graph.
1646
        #   \ /
1647
        #    1
1648
        #
1649
        # 4 and 5 cannot be removed because 6 has 2 children
3514.2.16 by John Arbash Meinel
Review feedback from Ian.
1650
        # 2 and 3 cannot be removed because 1 has 2 parents
7143.15.2 by Jelmer Vernooij
Run autopep8.
1651
        d = {1: [2, 3], 2: [4], 4: [6], 3: [5], 5: [6], 6: [7], 7: []}
3514.2.14 by John Arbash Meinel
Bring in the code to collapse linear portions of the graph.
1652
        self.assertCollapsed(d, d)
4070.9.14 by Andrew Bennetts
Tweaks requested by Robert's review.
1653
1654
4819.2.3 by John Arbash Meinel
Add a GraphThunkIdsToKeys as a tested class.
1655
class TestGraphThunkIdsToKeys(tests.TestCase):
1656
1657
    def test_heads(self):
1658
        # A
1659
        # |\
1660
        # B C
1661
        # |/
1662
        # D
7143.15.2 by Jelmer Vernooij
Run autopep8.
1663
        d = {(b'D',): [(b'B',), (b'C',)], (b'C',): [(b'A',)],
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1664
             (b'B',): [(b'A',)], (b'A',): []}
4819.2.3 by John Arbash Meinel
Add a GraphThunkIdsToKeys as a tested class.
1665
        g = _mod_graph.Graph(_mod_graph.DictParentsProvider(d))
1666
        graph_thunk = _mod_graph.GraphThunkIdsToKeys(g)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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'])))
4819.2.3 by John Arbash Meinel
Add a GraphThunkIdsToKeys as a tested class.
1671
5559.3.1 by Jelmer Vernooij
Add GraphThunkIdsToKeys.add_node.
1672
    def test_add_node(self):
7143.15.2 by Jelmer Vernooij
Run autopep8.
1673
        d = {(b'C',): [(b'A',)], (b'B',): [(b'A',)], (b'A',): []}
5559.3.1 by Jelmer Vernooij
Add GraphThunkIdsToKeys.add_node.
1674
        g = _mod_graph.KnownGraph(d)
1675
        graph_thunk = _mod_graph.GraphThunkIdsToKeys(g)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1676
        graph_thunk.add_node(b"D", [b"A", b"C"])
1677
        self.assertEqual([b'B', b'D'],
7143.15.2 by Jelmer Vernooij
Run autopep8.
1678
                         sorted(graph_thunk.heads([b'D', b'B', b'A'])))
5559.3.1 by Jelmer Vernooij
Add GraphThunkIdsToKeys.add_node.
1679
5988.1.1 by Jelmer Vernooij
Fix GraphThunkIdsToKeys.merge_sort
1680
    def test_merge_sort(self):
7143.15.2 by Jelmer Vernooij
Run autopep8.
1681
        d = {(b'C',): [(b'A',)], (b'B',): [(b'A',)], (b'A',): []}
5988.1.1 by Jelmer Vernooij
Fix GraphThunkIdsToKeys.merge_sort
1682
        g = _mod_graph.KnownGraph(d)
1683
        graph_thunk = _mod_graph.GraphThunkIdsToKeys(g)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1684
        graph_thunk.add_node(b"D", [b"A", b"C"])
1685
        self.assertEqual([(b'C', 0, (2,), False), (b'A', 0, (1,), True)],
7143.15.2 by Jelmer Vernooij
Run autopep8.
1686
                         [(n.key, n.merge_depth, n.revno, n.end_of_merge)
1687
                          for n in graph_thunk.merge_sort(b'C')])
5988.1.1 by Jelmer Vernooij
Fix GraphThunkIdsToKeys.merge_sort
1688
4819.2.3 by John Arbash Meinel
Add a GraphThunkIdsToKeys as a tested class.
1689
6015.24.3 by John Arbash Meinel
Implement get_cached_parent_map
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):
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1703
        parents1 = _mod_graph.DictParentsProvider({b'rev2': [b'rev3']})
1704
        parents2 = _mod_graph.DictParentsProvider({b'rev1': [b'rev4']})
6015.24.3 by John Arbash Meinel
Implement get_cached_parent_map
1705
        stacked = _mod_graph.StackedParentsProvider([parents1, parents2])
7143.15.2 by Jelmer Vernooij
Run autopep8.
1706
        self.assertEqual({b'rev1': [b'rev4'], b'rev2': [b'rev3']},
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1707
                         stacked.get_parent_map([b'rev1', b'rev2']))
7143.15.2 by Jelmer Vernooij
Run autopep8.
1708
        self.assertEqual({b'rev2': [b'rev3'], b'rev1': [b'rev4']},
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1709
                         stacked.get_parent_map([b'rev2', b'rev1']))
7143.15.2 by Jelmer Vernooij
Run autopep8.
1710
        self.assertEqual({b'rev2': [b'rev3']},
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1711
                         stacked.get_parent_map([b'rev2', b'rev2']))
7143.15.2 by Jelmer Vernooij
Run autopep8.
1712
        self.assertEqual({b'rev1': [b'rev4']},
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1713
                         stacked.get_parent_map([b'rev1', b'rev1']))
6015.24.3 by John Arbash Meinel
Implement get_cached_parent_map
1714
1715
    def test_stacked_parents_provider_overlapping(self):
1716
        # rev2 is availible in both providers.
1717
        # 1
1718
        # |
1719
        # 2
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1720
        parents1 = _mod_graph.DictParentsProvider({b'rev2': [b'rev1']})
1721
        parents2 = _mod_graph.DictParentsProvider({b'rev2': [b'rev1']})
6015.24.3 by John Arbash Meinel
Implement get_cached_parent_map
1722
        stacked = _mod_graph.StackedParentsProvider([parents1, parents2])
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1723
        self.assertEqual({b'rev2': [b'rev1']},
1724
                         stacked.get_parent_map([b'rev2']))
6015.24.3 by John Arbash Meinel
Implement get_cached_parent_map
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
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1729
        pp1 = self.get_shared_provider(b'pp1', {b'rev2': (b'rev1',)},
6015.24.3 by John Arbash Meinel
Implement get_cached_parent_map
1730
                                       has_cached=False)
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1731
        pp2 = self.get_shared_provider(b'pp2', {b'rev2': (b'rev1',)},
6015.24.3 by John Arbash Meinel
Implement get_cached_parent_map
1732
                                       has_cached=True)
1733
        stacked = _mod_graph.StackedParentsProvider([pp1, pp2])
7143.15.2 by Jelmer Vernooij
Run autopep8.
1734
        self.assertEqual({b'rev2': (b'rev1',)},
1735
                         stacked.get_parent_map([b'rev2']))
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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)
6015.24.3 by John Arbash Meinel
Implement get_cached_parent_map
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.
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
1743
        pp1 = self.get_shared_provider(b'pp1', {b'a': ()}, has_cached=True)
7143.15.2 by Jelmer Vernooij
Run autopep8.
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)
6015.24.3 by John Arbash Meinel
Implement get_cached_parent_map
1748
        stacked = _mod_graph.StackedParentsProvider([pp1, pp2, pp3])
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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']),
6015.24.3 by John Arbash Meinel
Implement get_cached_parent_map
1752
                          # No call to pp2, because it doesn't have cached
6974.2.1 by Jelmer Vernooij
Port breezy.graph to Python3.
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']),
7143.15.2 by Jelmer Vernooij
Run autopep8.
1757
                          ], self.calls)