85
86
(8, [0, 1, 4, 5, 6])]),
86
87
[0, 1, 2, 3, 4, 5, 6, 7, 8])
89
def test_tsort_unincluded_parent(self):
90
"""Sort nodes, but don't include some parents in the output"""
91
self.assertSortAndIterate([(0, [1]),
89
96
class MergeSortTests(TestCase):
91
98
def assertSortAndIterate(self, graph, branch_tip, result_list,
92
mainline_revisions=None):
99
generate_revno, mainline_revisions=None):
93
100
"""Check that merge based sorting and iter_topo_order on graph works."""
101
value = merge_sort(graph, branch_tip,
102
mainline_revisions=mainline_revisions,
103
generate_revno=generate_revno)
104
if result_list != value:
106
self.assertEqualDiff(pprint.pformat(result_list),
107
pprint.pformat(value))
94
108
self.assertEquals(result_list,
95
merge_sort(graph, branch_tip, mainline_revisions=mainline_revisions))
109
merge_sort(graph, branch_tip, mainline_revisions=mainline_revisions,
110
generate_revno=generate_revno))
96
111
self.assertEqual(result_list,
100
mainline_revisions=mainline_revisions).iter_topo_order()))
115
mainline_revisions=mainline_revisions,
116
generate_revno=generate_revno,
117
).iter_topo_order()))
102
119
def test_merge_sort_empty(self):
103
120
# sorting of an emptygraph does not error
104
self.assertSortAndIterate({}, None, [])
121
self.assertSortAndIterate({}, None, [], False)
122
self.assertSortAndIterate({}, None, [], True)
123
self.assertSortAndIterate({}, NULL_REVISION, [], False)
124
self.assertSortAndIterate({}, NULL_REVISION, [], True)
106
126
def test_merge_sort_not_empty_no_tip(self):
107
127
# merge sorting of a branch starting with None should result
108
128
# in an empty list: no revisions are dragged in.
109
self.assertSortAndIterate({0: []}.items(), None, [])
129
self.assertSortAndIterate({0: []}.items(), None, [], False)
130
self.assertSortAndIterate({0: []}.items(), None, [], True)
111
132
def test_merge_sort_one_revision(self):
112
133
# sorting with one revision as the tip returns the correct fields:
113
134
# sequence - 0, revision id, merge depth - 0, end_of_merge
114
135
self.assertSortAndIterate({'id': []}.items(),
116
[(0, 'id', 0, True)])
137
[(0, 'id', 0, True)],
139
self.assertSortAndIterate({'id': []}.items(),
141
[(0, 'id', 0, (1,), True)],
118
144
def test_sequence_numbers_increase_no_merges(self):
119
145
# emit a few revisions with no merges to check the sequence
139
177
[(0, 'C', 0, False),
140
178
(1, 'B', 1, True),
141
179
(2, 'A', 0, True),
183
self.assertSortAndIterate(
186
'C': ['A', 'B']}.items(),
188
[(0, 'C', 0, (2,), False),
189
(1, 'B', 1, (1,1,1), True),
190
(2, 'A', 0, (1,), True),
195
def test_merge_sort_race(self):
211
self.assertSortAndIterate(graph, 'F',
212
[(0, 'F', 0, (3,), False),
213
(1, 'D', 1, (2,2,1), False),
214
(2, 'C', 1, (2,1,1), True), # XXX: Shouldn't it be merge_depth=2?
215
(3, 'B', 0, (2,), False),
216
(4, 'A', 0, (1,), True),
234
self.assertSortAndIterate(graph, 'F',
235
[(0, 'F', 0, (3,), False),
236
(1, 'D', 1, (2,1,2), False),
237
(2, 'C', 2, (2,2,1), True),
238
(3, 'X', 1, (2,1,1), True),
239
(4, 'B', 0, (2,), False),
240
(5, 'A', 0, (1,), True),
145
243
def test_merge_depth_with_nested_merges(self):
146
244
# the merge depth marker should reflect the depth of the revision
147
245
# in terms of merges out from the mainline
173
271
(5, 'F', 2, True),
174
272
(6, 'G', 1, True),
175
273
(7, 'H', 0, True),
277
self.assertSortAndIterate(
288
[(0, 'A', 0, (3,), False),
289
(1, 'B', 1, (1,3,2), False),
290
(2, 'C', 1, (1,3,1), True),
291
(3, 'D', 0, (2,), False),
292
(4, 'E', 1, (1,1,2), False),
293
(5, 'F', 2, (1,2,1), True),
294
(6, 'G', 1, (1,1,1), True),
295
(7, 'H', 0, (1,), True),
300
def test_dotted_revnos_with_simple_merges(self):
305
# D E F 3, 1.1.2, 1.2.1
307
# G H I 4, 1.2.2, 1.3.1
312
self.assertSortAndIterate(
327
[(0, 'L', 0, (6,), False),
328
(1, 'K', 1, (1,3,2), False),
329
(2, 'I', 1, (1,3,1), True),
330
(3, 'J', 0, (5,), False),
331
(4, 'H', 1, (1,2,2), False),
332
(5, 'F', 1, (1,2,1), True),
333
(6, 'G', 0, (4,), False),
334
(7, 'E', 1, (1,1,2), False),
335
(8, 'C', 1, (1,1,1), True),
336
(9, 'D', 0, (3,), False),
337
(10, 'B', 0, (2,), False),
338
(11, 'A', 0, (1,), True),
342
# Adding a shortcut from the first revision should not change any of
343
# the existing numbers
344
self.assertSortAndIterate(
361
[(0, 'N', 0, (7,), False),
362
(1, 'M', 1, (1,4,1), True),
363
(2, 'L', 0, (6,), False),
364
(3, 'K', 1, (1,3,2), False),
365
(4, 'I', 1, (1,3,1), True),
366
(5, 'J', 0, (5,), False),
367
(6, 'H', 1, (1,2,2), False),
368
(7, 'F', 1, (1,2,1), True),
369
(8, 'G', 0, (4,), False),
370
(9, 'E', 1, (1,1,2), False),
371
(10, 'C', 1, (1,1,1), True),
372
(11, 'D', 0, (3,), False),
373
(12, 'B', 0, (2,), False),
374
(13, 'A', 0, (1,), True),
179
379
def test_end_of_merge_not_last_revision_in_branch(self):
180
380
# within a branch only the last revision gets an
181
381
# end of merge marker.
222
433
(5, 'F', 2, True),
223
434
(6, 'G', 1, True),
224
435
(7, 'H', 0, True),
439
self.assertSortAndIterate(
440
{'A': ['H', 'B', 'E'],
450
[(0, 'A', 0, (2,), False),
451
(1, 'B', 1, (1,3,2), False),
452
(2, 'C', 2, (1,4,1), True),
453
(3, 'D', 1, (1,3,1), True),
454
(4, 'E', 1, (1,1,2), False),
455
(5, 'F', 2, (1,2,1), True),
456
(6, 'G', 1, (1,1,1), True),
457
(7, 'H', 0, (1,), True),
228
462
def test_mainline_revs_partial(self):
277
528
[(0, 'A', 0, True),
279
mainline_revisions=[None, 'A']
531
mainline_revisions=[None, 'A']
533
self.assertSortAndIterate(
537
[(0, 'A', 0, (1,), True),
540
mainline_revisions=[None, 'A']
543
def test_mainline_revs_with_ghost(self):
544
# We have a mainline, but the end of it is actually a ghost
545
# The graph that is passed to tsort has had ghosts filtered out, but
546
# the mainline history has not.
547
self.assertSortAndIterate(
551
[(0, 'C', 0, (2,), False),
552
(1, 'B', 0, (1,), True),
554
True, mainline_revisions=['A', 'B', 'C'])
556
def test_parallel_root_sequence_numbers_increase_with_merges(self):
557
"""When there are parallel roots, check their revnos."""
558
self.assertSortAndIterate(
561
'C': ['A', 'B']}.items(),
563
[(0, 'C', 0, (2,), False),
564
(1, 'B', 1, (0,1,1), True),
565
(2, 'A', 0, (1,), True),
570
def test_revnos_are_globally_assigned(self):
571
"""revnos are assigned according to the revision they derive from."""
572
# in this test we setup a number of branches that all derive from
573
# the first revision, and then merge them one at a time, which
574
# should give the revisions as they merge numbers still deriving from
575
# the revision were based on.
576
# merge 3: J: ['G', 'I']
580
# merge 2: G: ['D', 'F']
584
# merge 1: D: ['A', 'C']
589
self.assertSortAndIterate(
602
[(0, 'J', 0, (4,), False),
603
(1, 'I', 1, (1,3,2), False),
604
(2, 'H', 1, (1,3,1), True),
605
(3, 'G', 0, (3,), False),
606
(4, 'F', 1, (1,2,2), False),
607
(5, 'E', 1, (1,2,1), True),
608
(6, 'D', 0, (2,), False),
609
(7, 'C', 1, (1,1,2), False),
610
(8, 'B', 1, (1,1,1), True),
611
(9, 'A', 0, (1,), True),
616
def test_roots_and_sub_branches_versus_ghosts(self):
617
"""Extra roots and their mini branches use the same numbering.
619
All of them use the 0-node numbering.
632
self.assertSortAndIterate(
653
[( 0, 'R', 0, (6,), False),
654
( 1, 'Q', 1, (0,4,5), False),
655
( 2, 'P', 2, (0,6,1), True),
656
( 3, 'O', 1, (0,4,4), False),
657
( 4, 'N', 1, (0,4,3), False),
658
( 5, 'M', 2, (0,5,1), True),
659
( 6, 'L', 1, (0,4,2), False),
660
( 7, 'K', 1, (0,4,1), True),
661
( 8, 'J', 0, (5,), False),
662
( 9, 'I', 1, (0,3,1), True),
663
(10, 'H', 0, (4,), False),
664
(11, 'G', 1, (0,1,3), False),
665
(12, 'F', 2, (0,2,1), True),
666
(13, 'E', 1, (0,1,2), False),
667
(14, 'D', 1, (0,1,1), True),
668
(15, 'C', 0, (3,), False),
669
(16, 'B', 0, (2,), False),
670
(17, 'A', 0, (1,), True),