1
# Copyright (C) 2006 Canonical Ltd
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.
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.
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
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
"""Tests of the parent related functions of WorkingTrees."""
19
from errno import EEXIST
25
revision as _mod_revision,
28
from bzrlib.inventory import (
34
from bzrlib.revision import Revision
35
from bzrlib.tests import SymlinkFeature, TestNotApplicable
36
from bzrlib.tests.workingtree_implementations import TestCaseWithWorkingTree
37
from bzrlib.uncommit import uncommit
40
class TestParents(TestCaseWithWorkingTree):
42
def assertConsistentParents(self, expected, tree):
43
"""Check that the parents found are as expected.
45
This test helper also checks that they are consistent with
46
the pre-get_parent_ids() api - which is now deprecated.
48
self.assertEqual(expected, tree.get_parent_ids())
50
self.assertEqual(_mod_revision.NULL_REVISION,
51
_mod_revision.ensure_null(tree.last_revision()))
53
self.assertEqual(expected[0], tree.last_revision())
56
class TestGetParents(TestParents):
58
def test_get_parents(self):
59
t = self.make_branch_and_tree('.')
60
self.assertEqual([], t.get_parent_ids())
63
class TestSetParents(TestParents):
65
def test_set_no_parents(self):
66
t = self.make_branch_and_tree('.')
67
t.set_parent_trees([])
68
self.assertEqual([], t.get_parent_ids())
69
# now give it a real parent, and then set it to no parents again.
70
t.commit('first post')
71
t.set_parent_trees([])
72
self.assertConsistentParents([], t)
74
def test_set_null_parent(self):
75
t = self.make_branch_and_tree('.')
76
self.assertRaises(errors.ReservedId, t.set_parent_ids, ['null:'],
77
allow_leftmost_as_ghost=True)
78
self.assertRaises(errors.ReservedId, t.set_parent_trees,
79
[('null:', None)], allow_leftmost_as_ghost=True)
81
def test_set_one_ghost_parent_rejects(self):
82
t = self.make_branch_and_tree('.')
83
self.assertRaises(errors.GhostRevisionUnusableHere,
84
t.set_parent_trees, [('missing-revision-id', None)])
86
def test_set_one_ghost_parent_force(self):
87
t = self.make_branch_and_tree('.')
88
t.set_parent_trees([('missing-revision-id', None)],
89
allow_leftmost_as_ghost=True)
90
self.assertConsistentParents(['missing-revision-id'], t)
92
def test_set_two_parents_one_ghost(self):
93
t = self.make_branch_and_tree('.')
94
revision_in_repo = t.commit('first post')
95
# remove the tree's history
96
uncommit(t.branch, tree=t)
97
rev_tree = t.branch.repository.revision_tree(revision_in_repo)
98
t.set_parent_trees([(revision_in_repo, rev_tree),
99
('another-missing', None)])
100
self.assertConsistentParents([revision_in_repo, 'another-missing'], t)
102
def test_set_three_parents(self):
103
t = self.make_branch_and_tree('.')
104
first_revision = t.commit('first post')
105
uncommit(t.branch, tree=t)
106
second_revision = t.commit('second post')
107
uncommit(t.branch, tree=t)
108
third_revision = t.commit('third post')
109
uncommit(t.branch, tree=t)
110
rev_tree1 = t.branch.repository.revision_tree(first_revision)
111
rev_tree2 = t.branch.repository.revision_tree(second_revision)
112
rev_tree3 = t.branch.repository.revision_tree(third_revision)
113
t.set_parent_trees([(first_revision, rev_tree1),
114
(second_revision, rev_tree2),
115
(third_revision, rev_tree3)])
116
self.assertConsistentParents(
117
[first_revision, second_revision, third_revision], t)
119
def test_set_no_parents_ids(self):
120
t = self.make_branch_and_tree('.')
122
self.assertEqual([], t.get_parent_ids())
123
# now give it a real parent, and then set it to no parents again.
124
t.commit('first post')
126
self.assertConsistentParents([], t)
128
def test_set_one_ghost_parent_ids_rejects(self):
129
t = self.make_branch_and_tree('.')
130
self.assertRaises(errors.GhostRevisionUnusableHere,
131
t.set_parent_ids, ['missing-revision-id'])
133
def test_set_one_ghost_parent_ids_force(self):
134
t = self.make_branch_and_tree('.')
135
t.set_parent_ids(['missing-revision-id'],
136
allow_leftmost_as_ghost=True)
137
self.assertConsistentParents(['missing-revision-id'], t)
139
def test_set_two_parents_one_ghost_ids(self):
140
t = self.make_branch_and_tree('.')
141
revision_in_repo = t.commit('first post')
142
# remove the tree's history
143
uncommit(t.branch, tree=t)
144
rev_tree = t.branch.repository.revision_tree(revision_in_repo)
145
t.set_parent_ids([revision_in_repo, 'another-missing'])
146
self.assertConsistentParents([revision_in_repo, 'another-missing'], t)
148
def test_set_three_parents_ids(self):
149
t = self.make_branch_and_tree('.')
150
first_revision = t.commit('first post')
151
uncommit(t.branch, tree=t)
152
second_revision = t.commit('second post')
153
uncommit(t.branch, tree=t)
154
third_revision = t.commit('third post')
155
uncommit(t.branch, tree=t)
156
rev_tree1 = t.branch.repository.revision_tree(first_revision)
157
rev_tree2 = t.branch.repository.revision_tree(second_revision)
158
rev_tree3 = t.branch.repository.revision_tree(third_revision)
159
t.set_parent_ids([first_revision, second_revision, third_revision])
160
self.assertConsistentParents(
161
[first_revision, second_revision, third_revision], t)
163
def test_set_duplicate_parent_ids(self):
164
t = self.make_branch_and_tree('.')
165
rev1 = t.commit('first post')
166
uncommit(t.branch, tree=t)
167
rev2 = t.commit('second post')
168
uncommit(t.branch, tree=t)
169
rev3 = t.commit('third post')
170
uncommit(t.branch, tree=t)
171
t.set_parent_ids([rev1, rev2, rev2, rev3])
172
# We strip the duplicate, but preserve the ordering
173
self.assertConsistentParents([rev1, rev2, rev3], t)
175
def test_set_duplicate_parent_trees(self):
176
t = self.make_branch_and_tree('.')
177
rev1 = t.commit('first post')
178
uncommit(t.branch, tree=t)
179
rev2 = t.commit('second post')
180
uncommit(t.branch, tree=t)
181
rev3 = t.commit('third post')
182
uncommit(t.branch, tree=t)
183
rev_tree1 = t.branch.repository.revision_tree(rev1)
184
rev_tree2 = t.branch.repository.revision_tree(rev2)
185
rev_tree3 = t.branch.repository.revision_tree(rev3)
186
t.set_parent_trees([(rev1, rev_tree1), (rev2, rev_tree2),
187
(rev2, rev_tree2), (rev3, rev_tree3)])
188
# We strip the duplicate, but preserve the ordering
189
self.assertConsistentParents([rev1, rev2, rev3], t)
191
def test_set_parent_ids_in_ancestry(self):
192
t = self.make_branch_and_tree('.')
193
rev1 = t.commit('first post')
194
rev2 = t.commit('second post')
195
rev3 = t.commit('third post')
196
# Reset the tree, back to rev1
197
t.set_parent_ids([rev1])
198
t.branch.set_last_revision_info(1, rev1)
199
self.assertConsistentParents([rev1], t)
200
t.set_parent_ids([rev1, rev2, rev3])
201
# rev2 is in the ancestry of rev3, so it will be filtered out
202
self.assertConsistentParents([rev1, rev3], t)
203
# Order should be preserved, and the first revision should always be
205
t.set_parent_ids([rev2, rev3, rev1])
206
self.assertConsistentParents([rev2, rev3], t)
208
def test_set_parent_trees_in_ancestry(self):
209
t = self.make_branch_and_tree('.')
210
rev1 = t.commit('first post')
211
rev2 = t.commit('second post')
212
rev3 = t.commit('third post')
213
# Reset the tree, back to rev1
214
t.set_parent_ids([rev1])
215
t.branch.set_last_revision_info(1, rev1)
216
self.assertConsistentParents([rev1], t)
217
rev_tree1 = t.branch.repository.revision_tree(rev1)
218
rev_tree2 = t.branch.repository.revision_tree(rev2)
219
rev_tree3 = t.branch.repository.revision_tree(rev3)
220
t.set_parent_trees([(rev1, rev_tree1), (rev2, rev_tree2),
222
# rev2 is in the ancestry of rev3, so it will be filtered out
223
self.assertConsistentParents([rev1, rev3], t)
224
# Order should be preserved, and the first revision should always be
226
t.set_parent_trees([(rev2, rev_tree2), (rev1, rev_tree1),
228
self.assertConsistentParents([rev2, rev3], t)
231
class TestAddParent(TestParents):
233
def test_add_first_parent_id(self):
234
"""Test adding the first parent id"""
235
tree = self.make_branch_and_tree('.')
236
first_revision = tree.commit('first post')
237
uncommit(tree.branch, tree=tree)
238
tree.add_parent_tree_id(first_revision)
239
self.assertConsistentParents([first_revision], tree)
241
def test_add_first_parent_id_ghost_rejects(self):
242
"""Test adding the first parent id - as a ghost"""
243
tree = self.make_branch_and_tree('.')
244
self.assertRaises(errors.GhostRevisionUnusableHere,
245
tree.add_parent_tree_id, 'first-revision')
247
def test_add_first_parent_id_ghost_force(self):
248
"""Test adding the first parent id - as a ghost"""
249
tree = self.make_branch_and_tree('.')
250
tree.add_parent_tree_id('first-revision', allow_leftmost_as_ghost=True)
251
self.assertConsistentParents(['first-revision'], tree)
253
def test_add_second_parent_id_with_ghost_first(self):
254
"""Test adding the second parent when the first is a ghost."""
255
tree = self.make_branch_and_tree('.')
256
tree.add_parent_tree_id('first-revision', allow_leftmost_as_ghost=True)
257
tree.add_parent_tree_id('second')
258
self.assertConsistentParents(['first-revision', 'second'], tree)
260
def test_add_second_parent_id(self):
261
"""Test adding the second parent id"""
262
tree = self.make_branch_and_tree('.')
263
first_revision = tree.commit('first post')
264
uncommit(tree.branch, tree=tree)
265
second_revision = tree.commit('second post')
266
tree.add_parent_tree_id(first_revision)
267
self.assertConsistentParents([second_revision, first_revision], tree)
269
def test_add_second_parent_id_ghost(self):
270
"""Test adding the second parent id - as a ghost"""
271
tree = self.make_branch_and_tree('.')
272
first_revision = tree.commit('first post')
273
tree.add_parent_tree_id('second')
274
self.assertConsistentParents([first_revision, 'second'], tree)
276
def test_add_first_parent_tree(self):
277
"""Test adding the first parent id"""
278
tree = self.make_branch_and_tree('.')
279
first_revision = tree.commit('first post')
280
uncommit(tree.branch, tree=tree)
281
tree.add_parent_tree((first_revision,
282
tree.branch.repository.revision_tree(first_revision)))
283
self.assertConsistentParents([first_revision], tree)
285
def test_add_first_parent_tree_ghost_rejects(self):
286
"""Test adding the first parent id - as a ghost"""
287
tree = self.make_branch_and_tree('.')
288
self.assertRaises(errors.GhostRevisionUnusableHere,
289
tree.add_parent_tree, ('first-revision', None))
291
def test_add_first_parent_tree_ghost_force(self):
292
"""Test adding the first parent id - as a ghost"""
293
tree = self.make_branch_and_tree('.')
294
tree.add_parent_tree(('first-revision', None),
295
allow_leftmost_as_ghost=True)
296
self.assertConsistentParents(['first-revision'], tree)
298
def test_add_second_parent_tree(self):
299
"""Test adding the second parent id"""
300
tree = self.make_branch_and_tree('.')
301
first_revision = tree.commit('first post')
302
uncommit(tree.branch, tree=tree)
303
second_revision = tree.commit('second post')
304
tree.add_parent_tree((first_revision,
305
tree.branch.repository.revision_tree(first_revision)))
306
self.assertConsistentParents([second_revision, first_revision], tree)
308
def test_add_second_parent_tree_ghost(self):
309
"""Test adding the second parent id - as a ghost"""
310
tree = self.make_branch_and_tree('.')
311
first_revision = tree.commit('first post')
312
tree.add_parent_tree(('second', None))
313
self.assertConsistentParents([first_revision, 'second'], tree)
316
class UpdateToOneParentViaDeltaTests(TestCaseWithWorkingTree):
317
"""Tests for the update_basis_by_delta call.
319
This is intuitively defined as 'apply an inventory delta to the basis and
320
discard other parents', but for trees that have an inventory that is not
321
managed as a tree-by-id, the implementation requires roughly duplicated
322
tests with those for apply_inventory_delta on the main tree.
325
def assertDeltaApplicationResultsInExpectedBasis(self, tree, revid, delta,
329
tree.update_basis_by_delta(revid, delta)
332
# check the last revision was adjusted to rev_id
333
self.assertEqual(revid, tree.last_revision())
334
# check the parents are what we expect
335
self.assertEqual([revid], tree.get_parent_ids())
336
# check that the basis tree has the inventory we expect from applying
338
result_basis = tree.basis_tree()
339
result_basis.lock_read()
340
self.addCleanup(result_basis.unlock)
341
self.assertEqual(expected_inventory, result_basis.inventory)
343
def make_inv_delta(self, old, new):
344
"""Make an inventory delta from two inventories."""
345
old_ids = set(old._byid.iterkeys())
346
new_ids = set(new._byid.iterkeys())
347
adds = new_ids - old_ids
348
deletes = old_ids - new_ids
349
common = old_ids.intersection(new_ids)
351
for file_id in deletes:
352
delta.append((old.id2path(file_id), None, file_id, None))
354
delta.append((None, new.id2path(file_id), file_id, new[file_id]))
355
for file_id in common:
356
if old[file_id] != new[file_id]:
357
delta.append((old.id2path(file_id), new.id2path(file_id),
358
file_id, new[file_id]))
361
def fake_up_revision(self, tree, revid, shape):
364
tree.branch.repository.start_write_group()
366
if shape.root.revision is None:
367
shape.root.revision = revid
368
sha1 = tree.branch.repository.add_inventory(revid, shape, [])
369
rev = Revision(timestamp=0,
371
committer="Foo Bar <foo@example.com>",
375
tree.branch.repository.add_revision(revid, rev)
377
tree.branch.repository.abort_write_group()
380
tree.branch.repository.commit_write_group()
384
def add_entry(self, inv, rev_id, entry):
385
entry.revision = rev_id
388
def add_dir(self, inv, rev_id, file_id, parent_id, name):
389
new_dir = InventoryDirectory(file_id, name, parent_id)
390
self.add_entry(inv, rev_id, new_dir)
392
def add_file(self, inv, rev_id, file_id, parent_id, name, sha, size):
393
new_file = InventoryFile(file_id, name, parent_id)
394
new_file.text_sha1 = sha
395
new_file.text_size = size
396
self.add_entry(inv, rev_id, new_file)
398
def add_link(self, inv, rev_id, file_id, parent_id, name, target):
399
new_link = InventoryLink(file_id, name, parent_id)
400
new_link.symlink_target = target
401
self.add_entry(inv, rev_id, new_link)
403
def add_new_root(self, new_shape, old_revid, new_revid):
404
if self.bzrdir_format.repository_format.rich_root_data:
405
self.add_dir(new_shape, old_revid, 'root-id', None, '')
407
self.add_dir(new_shape, new_revid, 'root-id', None, '')
409
def assertTransitionFromBasisToShape(self, basis_shape, basis_revid,
410
new_shape, new_revid, extra_parent=None):
411
# set the inventory revision ids.
412
basis_shape.revision_id = basis_revid
413
new_shape.revision_id = new_revid
414
delta = self.make_inv_delta(basis_shape, new_shape)
415
tree = self.make_branch_and_tree('tree')
416
# the shapes need to be in the tree's repository to be able to set them
417
# as a parent, but the file content is not needed.
418
if basis_revid is not None:
419
self.fake_up_revision(tree, basis_revid, basis_shape)
420
parents = [basis_revid]
421
if extra_parent is not None:
422
parents.append(extra_parent)
423
tree.set_parent_ids(parents)
424
self.fake_up_revision(tree, new_revid, new_shape)
425
# give tree an inventory of new_shape
426
tree._write_inventory(new_shape)
427
self.assertDeltaApplicationResultsInExpectedBasis(tree, new_revid,
429
# The tree should be internally consistent; while this is a moderately
430
# large hammer, this is a particularly sensitive area of code, so the
431
# extra assurance is well worth it.
433
osutils.rmtree('tree')
435
def test_no_parents_just_root(self):
436
"""Test doing an empty commit - no parent, set a root only."""
437
basis_shape = Inventory(root_id=None) # empty tree
438
new_shape = Inventory() # tree with a root
439
self.assertTransitionFromBasisToShape(basis_shape, None, new_shape,
442
def test_no_parents_full_tree(self):
443
"""Test doing a regular initial commit with files and dirs."""
444
basis_shape = Inventory(root_id=None) # empty tree
446
new_shape = Inventory(root_id=None)
447
self.add_dir(new_shape, revid, 'root-id', None, '')
448
self.add_link(new_shape, revid, 'link-id', 'root-id', 'link', 'target')
449
self.add_file(new_shape, revid, 'file-id', 'root-id', 'file', '1' * 32,
451
self.add_dir(new_shape, revid, 'dir-id', 'root-id', 'dir')
452
self.add_file(new_shape, revid, 'subfile-id', 'dir-id', 'subfile',
454
self.assertTransitionFromBasisToShape(basis_shape, None, new_shape,
457
def test_file_content_change(self):
458
old_revid = 'old-parent'
459
basis_shape = Inventory(root_id=None)
460
self.add_dir(basis_shape, old_revid, 'root-id', None, '')
461
self.add_file(basis_shape, old_revid, 'file-id', 'root-id', 'file',
463
new_revid = 'new-parent'
464
new_shape = Inventory(root_id=None)
465
self.add_new_root(new_shape, old_revid, new_revid)
466
self.add_file(new_shape, new_revid, 'file-id', 'root-id', 'file',
468
self.assertTransitionFromBasisToShape(basis_shape, old_revid,
469
new_shape, new_revid)
471
def test_link_content_change(self):
472
old_revid = 'old-parent'
473
basis_shape = Inventory(root_id=None)
474
self.add_dir(basis_shape, old_revid, 'root-id', None, '')
475
self.add_link(basis_shape, old_revid, 'link-id', 'root-id', 'link',
477
new_revid = 'new-parent'
478
new_shape = Inventory(root_id=None)
479
self.add_new_root(new_shape, old_revid, new_revid)
480
self.add_link(new_shape, new_revid, 'link-id', 'root-id', 'link',
482
self.assertTransitionFromBasisToShape(basis_shape, old_revid,
483
new_shape, new_revid)
485
def test_kind_changes(self):
486
def do_file(inv, revid):
487
self.add_file(inv, revid, 'path-id', 'root-id', 'path', '1' * 32,
489
def do_link(inv, revid):
490
self.add_link(inv, revid, 'path-id', 'root-id', 'path', 'target')
491
def do_dir(inv, revid):
492
self.add_dir(inv, revid, 'path-id', 'root-id', 'path')
493
for old_factory in (do_file, do_link, do_dir):
494
for new_factory in (do_file, do_link, do_dir):
495
if old_factory == new_factory:
497
old_revid = 'old-parent'
498
basis_shape = Inventory(root_id=None)
499
self.add_dir(basis_shape, old_revid, 'root-id', None, '')
500
old_factory(basis_shape, old_revid)
501
new_revid = 'new-parent'
502
new_shape = Inventory(root_id=None)
503
self.add_new_root(new_shape, old_revid, new_revid)
504
new_factory(new_shape, new_revid)
505
self.assertTransitionFromBasisToShape(basis_shape, old_revid,
506
new_shape, new_revid)
508
def test_content_from_second_parent_is_dropped(self):
509
left_revid = 'left-parent'
510
basis_shape = Inventory(root_id=None)
511
self.add_dir(basis_shape, left_revid, 'root-id', None, '')
512
self.add_link(basis_shape, left_revid, 'link-id', 'root-id', 'link',
514
# the right shape has content - file, link, subdir with a child,
515
# that should all be discarded by the call.
516
right_revid = 'right-parent'
517
right_shape = Inventory(root_id=None)
518
self.add_dir(right_shape, left_revid, 'root-id', None, '')
519
self.add_link(right_shape, right_revid, 'link-id', 'root-id', 'link',
521
self.add_dir(right_shape, right_revid, 'subdir-id', 'root-id', 'dir')
522
self.add_file(right_shape, right_revid, 'file-id', 'subdir-id', 'file',
524
new_revid = 'new-parent'
525
new_shape = Inventory(root_id=None)
526
self.add_new_root(new_shape, left_revid, new_revid)
527
self.add_link(new_shape, new_revid, 'link-id', 'root-id', 'link',
529
self.assertTransitionFromBasisToShape(basis_shape, left_revid,
530
new_shape, new_revid, right_revid)
532
def test_parent_id_changed(self):
533
# test that when the only change to an entry is its parent id changing
534
# that it is handled correctly (that is it keeps the same path)
535
old_revid = 'old-parent'
536
basis_shape = Inventory(root_id=None)
537
self.add_dir(basis_shape, old_revid, 'root-id', None, '')
538
self.add_dir(basis_shape, old_revid, 'orig-parent-id', 'root-id', 'dir')
539
self.add_dir(basis_shape, old_revid, 'dir-id', 'orig-parent-id', 'dir')
540
new_revid = 'new-parent'
541
new_shape = Inventory(root_id=None)
542
self.add_new_root(new_shape, old_revid, new_revid)
543
self.add_dir(new_shape, new_revid, 'new-parent-id', 'root-id', 'dir')
544
self.add_dir(new_shape, new_revid, 'dir-id', 'new-parent-id', 'dir')
545
self.assertTransitionFromBasisToShape(basis_shape, old_revid,
546
new_shape, new_revid)
548
def test_name_changed(self):
549
# test that when the only change to an entry is its name changing that
550
# it is handled correctly (that is it keeps the same parent id)
551
old_revid = 'old-parent'
552
basis_shape = Inventory(root_id=None)
553
self.add_dir(basis_shape, old_revid, 'root-id', None, '')
554
self.add_dir(basis_shape, old_revid, 'parent-id', 'root-id', 'origdir')
555
self.add_dir(basis_shape, old_revid, 'dir-id', 'parent-id', 'olddir')
556
new_revid = 'new-parent'
557
new_shape = Inventory(root_id=None)
558
self.add_new_root(new_shape, old_revid, new_revid)
559
self.add_dir(new_shape, new_revid, 'parent-id', 'root-id', 'newdir')
560
self.add_dir(new_shape, new_revid, 'dir-id', 'parent-id', 'newdir')
561
self.assertTransitionFromBasisToShape(basis_shape, old_revid,
562
new_shape, new_revid)
564
def test_parent_child_swap(self):
565
# test a A->A/B and A/B->A path swap.
566
old_revid = 'old-parent'
567
basis_shape = Inventory(root_id=None)
568
self.add_dir(basis_shape, old_revid, 'root-id', None, '')
569
self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
570
self.add_dir(basis_shape, old_revid, 'dir-id-B', 'dir-id-A', 'B')
571
self.add_link(basis_shape, old_revid, 'link-id-C', 'dir-id-B', 'C', 'C')
572
new_revid = 'new-parent'
573
new_shape = Inventory(root_id=None)
574
self.add_new_root(new_shape, old_revid, new_revid)
575
self.add_dir(new_shape, new_revid, 'dir-id-B', 'root-id', 'A')
576
self.add_dir(new_shape, new_revid, 'dir-id-A', 'dir-id-B', 'B')
577
self.add_link(new_shape, new_revid, 'link-id-C', 'dir-id-A', 'C', 'C')
578
self.assertTransitionFromBasisToShape(basis_shape, old_revid,
579
new_shape, new_revid)
581
def test_parent_deleted_child_renamed(self):
582
# test a A->None and A/B->A.
583
old_revid = 'old-parent'
584
basis_shape = Inventory(root_id=None)
585
self.add_dir(basis_shape, old_revid, 'root-id', None, '')
586
self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
587
self.add_dir(basis_shape, old_revid, 'dir-id-B', 'dir-id-A', 'B')
588
self.add_link(basis_shape, old_revid, 'link-id-C', 'dir-id-B', 'C', 'C')
589
new_revid = 'new-parent'
590
new_shape = Inventory(root_id=None)
591
self.add_new_root(new_shape, old_revid, new_revid)
592
self.add_dir(new_shape, new_revid, 'dir-id-B', 'root-id', 'A')
593
self.add_link(new_shape, old_revid, 'link-id-C', 'dir-id-B', 'C', 'C')
594
self.assertTransitionFromBasisToShape(basis_shape, old_revid,
595
new_shape, new_revid)
597
def test_dir_to_root(self):
599
old_revid = 'old-parent'
600
basis_shape = Inventory(root_id=None)
601
self.add_dir(basis_shape, old_revid, 'root-id', None, '')
602
self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
603
self.add_link(basis_shape, old_revid, 'link-id-B', 'dir-id-A', 'B', 'B')
604
new_revid = 'new-parent'
605
new_shape = Inventory(root_id=None)
606
self.add_dir(new_shape, new_revid, 'dir-id-A', None, '')
607
self.add_link(new_shape, old_revid, 'link-id-B', 'dir-id-A', 'B', 'B')
608
self.assertTransitionFromBasisToShape(basis_shape, old_revid,
609
new_shape, new_revid)
611
def test_path_swap(self):
612
# test a A->B and B->A path swap.
613
old_revid = 'old-parent'
614
basis_shape = Inventory(root_id=None)
615
self.add_dir(basis_shape, old_revid, 'root-id', None, '')
616
self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
617
self.add_dir(basis_shape, old_revid, 'dir-id-B', 'root-id', 'B')
618
self.add_link(basis_shape, old_revid, 'link-id-C', 'root-id', 'C', 'C')
619
self.add_link(basis_shape, old_revid, 'link-id-D', 'root-id', 'D', 'D')
620
self.add_file(basis_shape, old_revid, 'file-id-E', 'root-id', 'E',
622
self.add_file(basis_shape, old_revid, 'file-id-F', 'root-id', 'F',
624
new_revid = 'new-parent'
625
new_shape = Inventory(root_id=None)
626
self.add_new_root(new_shape, old_revid, new_revid)
627
self.add_dir(new_shape, new_revid, 'dir-id-A', 'root-id', 'B')
628
self.add_dir(new_shape, new_revid, 'dir-id-B', 'root-id', 'A')
629
self.add_link(new_shape, new_revid, 'link-id-C', 'root-id', 'D', 'C')
630
self.add_link(new_shape, new_revid, 'link-id-D', 'root-id', 'C', 'D')
631
self.add_file(new_shape, new_revid, 'file-id-E', 'root-id', 'F',
633
self.add_file(new_shape, new_revid, 'file-id-F', 'root-id', 'E',
635
self.assertTransitionFromBasisToShape(basis_shape, old_revid,
636
new_shape, new_revid)
639
# test adding paths and dirs, including adding to a newly added dir.
640
old_revid = 'old-parent'
641
basis_shape = Inventory(root_id=None)
642
# with a root, so its a commit after the first.
643
self.add_dir(basis_shape, old_revid, 'root-id', None, '')
644
new_revid = 'new-parent'
645
new_shape = Inventory(root_id=None)
646
self.add_new_root(new_shape, old_revid, new_revid)
647
self.add_dir(new_shape, new_revid, 'dir-id-A', 'root-id', 'A')
648
self.add_link(new_shape, new_revid, 'link-id-B', 'root-id', 'B', 'C')
649
self.add_file(new_shape, new_revid, 'file-id-C', 'root-id', 'C',
651
self.add_file(new_shape, new_revid, 'file-id-D', 'dir-id-A', 'D',
653
self.assertTransitionFromBasisToShape(basis_shape, old_revid,
654
new_shape, new_revid)
656
def test_removes(self):
657
# test removing paths, including paths that are within other also
659
old_revid = 'old-parent'
660
basis_shape = Inventory(root_id=None)
661
self.add_dir(basis_shape, old_revid, 'root-id', None, '')
662
self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
663
self.add_link(basis_shape, old_revid, 'link-id-B', 'root-id', 'B', 'C')
664
self.add_file(basis_shape, old_revid, 'file-id-C', 'root-id', 'C',
666
self.add_file(basis_shape, old_revid, 'file-id-D', 'dir-id-A', 'D',
668
new_revid = 'new-parent'
669
new_shape = Inventory(root_id=None)
670
self.add_new_root(new_shape, old_revid, new_revid)
671
self.assertTransitionFromBasisToShape(basis_shape, old_revid,
672
new_shape, new_revid)
674
def test_move_to_added_dir(self):
675
old_revid = 'old-parent'
676
basis_shape = Inventory(root_id=None)
677
self.add_dir(basis_shape, old_revid, 'root-id', None, '')
678
self.add_link(basis_shape, old_revid, 'link-id-B', 'root-id', 'B', 'C')
679
new_revid = 'new-parent'
680
new_shape = Inventory(root_id=None)
681
self.add_new_root(new_shape, old_revid, new_revid)
682
self.add_dir(new_shape, new_revid, 'dir-id-A', 'root-id', 'A')
683
self.add_link(new_shape, new_revid, 'link-id-B', 'dir-id-A', 'B', 'C')
684
self.assertTransitionFromBasisToShape(basis_shape, old_revid,
685
new_shape, new_revid)
687
def test_move_from_removed_dir(self):
688
old_revid = 'old-parent'
689
basis_shape = Inventory(root_id=None)
690
self.add_dir(basis_shape, old_revid, 'root-id', None, '')
691
self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
692
self.add_link(basis_shape, old_revid, 'link-id-B', 'dir-id-A', 'B', 'C')
693
new_revid = 'new-parent'
694
new_shape = Inventory(root_id=None)
695
self.add_new_root(new_shape, old_revid, new_revid)
696
self.add_link(new_shape, new_revid, 'link-id-B', 'root-id', 'B', 'C')
697
self.assertTransitionFromBasisToShape(basis_shape, old_revid,
698
new_shape, new_revid)
700
def test_move_moves_children_recursively(self):
701
old_revid = 'old-parent'
702
basis_shape = Inventory(root_id=None)
703
self.add_dir(basis_shape, old_revid, 'root-id', None, '')
704
self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
705
self.add_dir(basis_shape, old_revid, 'dir-id-B', 'dir-id-A', 'B')
706
self.add_link(basis_shape, old_revid, 'link-id-C', 'dir-id-B', 'C', 'D')
707
new_revid = 'new-parent'
708
new_shape = Inventory(root_id=None)
709
self.add_new_root(new_shape, old_revid, new_revid)
711
self.add_dir(new_shape, new_revid, 'dir-id-A', 'root-id', 'B')
713
self.add_dir(new_shape, old_revid, 'dir-id-B', 'dir-id-A', 'B')
714
self.add_link(new_shape, old_revid, 'link-id-C', 'dir-id-B', 'C', 'D')
715
self.assertTransitionFromBasisToShape(basis_shape, old_revid,
716
new_shape, new_revid)