/brz/remove-bazaar

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

« back to all changes in this revision

Viewing changes to bzrlib/tests/workingtree_implementations/test_parents.py

merge bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
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
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
"""Tests of the parent related functions of WorkingTrees."""
18
18
 
 
19
from errno import EEXIST
19
20
import os
20
21
 
21
22
from bzrlib import (
22
23
    errors,
 
24
    osutils,
23
25
    revision as _mod_revision,
24
26
    symbol_versioning,
25
 
    )
 
27
    tests,
 
28
    )
 
29
from bzrlib.inventory import (
 
30
    Inventory,
 
31
    InventoryFile,
 
32
    InventoryDirectory,
 
33
    InventoryLink,
 
34
    )
 
35
from bzrlib.revision import Revision
26
36
from bzrlib.tests.workingtree_implementations import TestCaseWithWorkingTree
27
37
from bzrlib.uncommit import uncommit
28
38
 
41
51
                             _mod_revision.ensure_null(tree.last_revision()))
42
52
        else:
43
53
            self.assertEqual(expected[0], tree.last_revision())
44
 
        self.assertEqual(expected[1:],
45
 
            self.applyDeprecated(symbol_versioning.zero_eleven,
46
 
                tree.pending_merges))
47
54
 
48
55
 
49
56
class TestGetParents(TestParents):
153
160
        self.assertConsistentParents(
154
161
            [first_revision, second_revision, third_revision], t)
155
162
 
 
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)
 
174
 
 
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)
 
190
 
 
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
 
204
        # kept
 
205
        t.set_parent_ids([rev2, rev3, rev1])
 
206
        self.assertConsistentParents([rev2, rev3], t)
 
207
 
 
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),
 
221
                            (rev3, rev_tree3)])
 
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
 
225
        # kept
 
226
        t.set_parent_trees([(rev2, rev_tree2), (rev1, rev_tree1),
 
227
                            (rev3, rev_tree3)])
 
228
        self.assertConsistentParents([rev2, rev3], t)
 
229
 
 
230
    def test_unicode_symlink(self):
 
231
        # this tests bug #272444
 
232
        self.requireFeature(tests.SymlinkFeature)
 
233
        self.requireFeature(tests.UnicodeFilenameFeature)
 
234
 
 
235
        tree = self.make_branch_and_tree('tree1')
 
236
 
 
237
        # The link points to a file whose name is an omega
 
238
        # U+03A9 GREEK CAPITAL LETTER OMEGA
 
239
        # UTF-8: ce a9  UTF-16BE: 03a9  Decimal: Ω
 
240
        target = u'\u03a9'
 
241
        link_name = u'\N{Euro Sign}link'
 
242
        os.symlink(target, 'tree1/' + link_name)
 
243
        tree.add([link_name],['link-id'])
 
244
 
 
245
        revision1 = tree.commit('added a link to a Unicode target')
 
246
        revision2 = tree.commit('this revision will be discarded')
 
247
        tree.set_parent_ids([revision1])
 
248
        tree.lock_read()
 
249
        self.addCleanup(tree.unlock)
 
250
        # Check that the symlink target is safely round-tripped in the trees.
 
251
        self.assertEqual(target, tree.get_symlink_target('link-id'))
 
252
        basis = tree.basis_tree()
 
253
        self.assertEqual(target, basis.get_symlink_target('link-id'))
 
254
 
156
255
 
157
256
class TestAddParent(TestParents):
158
257
 
163
262
        uncommit(tree.branch, tree=tree)
164
263
        tree.add_parent_tree_id(first_revision)
165
264
        self.assertConsistentParents([first_revision], tree)
166
 
        
 
265
 
167
266
    def test_add_first_parent_id_ghost_rejects(self):
168
267
        """Test adding the first parent id - as a ghost"""
169
268
        tree = self.make_branch_and_tree('.')
170
269
        self.assertRaises(errors.GhostRevisionUnusableHere,
171
270
            tree.add_parent_tree_id, 'first-revision')
172
 
        
 
271
 
173
272
    def test_add_first_parent_id_ghost_force(self):
174
273
        """Test adding the first parent id - as a ghost"""
175
274
        tree = self.make_branch_and_tree('.')
182
281
        tree.add_parent_tree_id('first-revision', allow_leftmost_as_ghost=True)
183
282
        tree.add_parent_tree_id('second')
184
283
        self.assertConsistentParents(['first-revision', 'second'], tree)
185
 
        
 
284
 
186
285
    def test_add_second_parent_id(self):
187
286
        """Test adding the second parent id"""
188
287
        tree = self.make_branch_and_tree('.')
191
290
        second_revision = tree.commit('second post')
192
291
        tree.add_parent_tree_id(first_revision)
193
292
        self.assertConsistentParents([second_revision, first_revision], tree)
194
 
        
 
293
 
195
294
    def test_add_second_parent_id_ghost(self):
196
295
        """Test adding the second parent id - as a ghost"""
197
296
        tree = self.make_branch_and_tree('.')
198
297
        first_revision = tree.commit('first post')
199
298
        tree.add_parent_tree_id('second')
200
299
        self.assertConsistentParents([first_revision, 'second'], tree)
201
 
        
 
300
 
202
301
    def test_add_first_parent_tree(self):
203
302
        """Test adding the first parent id"""
204
303
        tree = self.make_branch_and_tree('.')
207
306
        tree.add_parent_tree((first_revision,
208
307
            tree.branch.repository.revision_tree(first_revision)))
209
308
        self.assertConsistentParents([first_revision], tree)
210
 
        
 
309
 
211
310
    def test_add_first_parent_tree_ghost_rejects(self):
212
311
        """Test adding the first parent id - as a ghost"""
213
312
        tree = self.make_branch_and_tree('.')
214
313
        self.assertRaises(errors.GhostRevisionUnusableHere,
215
314
            tree.add_parent_tree, ('first-revision', None))
216
 
        
 
315
 
217
316
    def test_add_first_parent_tree_ghost_force(self):
218
317
        """Test adding the first parent id - as a ghost"""
219
318
        tree = self.make_branch_and_tree('.')
220
319
        tree.add_parent_tree(('first-revision', None),
221
320
            allow_leftmost_as_ghost=True)
222
321
        self.assertConsistentParents(['first-revision'], tree)
223
 
        
 
322
 
224
323
    def test_add_second_parent_tree(self):
225
324
        """Test adding the second parent id"""
226
325
        tree = self.make_branch_and_tree('.')
230
329
        tree.add_parent_tree((first_revision,
231
330
            tree.branch.repository.revision_tree(first_revision)))
232
331
        self.assertConsistentParents([second_revision, first_revision], tree)
233
 
        
 
332
 
234
333
    def test_add_second_parent_tree_ghost(self):
235
334
        """Test adding the second parent id - as a ghost"""
236
335
        tree = self.make_branch_and_tree('.')
237
336
        first_revision = tree.commit('first post')
238
337
        tree.add_parent_tree(('second', None))
239
338
        self.assertConsistentParents([first_revision, 'second'], tree)
 
339
 
 
340
 
 
341
class UpdateToOneParentViaDeltaTests(TestCaseWithWorkingTree):
 
342
    """Tests for the update_basis_by_delta call.
 
343
 
 
344
    This is intuitively defined as 'apply an inventory delta to the basis and
 
345
    discard other parents', but for trees that have an inventory that is not
 
346
    managed as a tree-by-id, the implementation requires roughly duplicated
 
347
    tests with those for apply_inventory_delta on the main tree.
 
348
    """
 
349
 
 
350
    def assertDeltaApplicationResultsInExpectedBasis(self, tree, revid, delta,
 
351
        expected_inventory):
 
352
        tree.lock_write()
 
353
        try:
 
354
            tree.update_basis_by_delta(revid, delta)
 
355
        finally:
 
356
            tree.unlock()
 
357
        # check the last revision was adjusted to rev_id
 
358
        self.assertEqual(revid, tree.last_revision())
 
359
        # check the parents are what we expect
 
360
        self.assertEqual([revid], tree.get_parent_ids())
 
361
        # check that the basis tree has the inventory we expect from applying
 
362
        # the delta.
 
363
        result_basis = tree.basis_tree()
 
364
        result_basis.lock_read()
 
365
        try:
 
366
            self.assertEqual(expected_inventory, result_basis.inventory)
 
367
        finally:
 
368
            result_basis.unlock()
 
369
 
 
370
    def make_inv_delta(self, old, new):
 
371
        """Make an inventory delta from two inventories."""
 
372
        old_ids = set(old._byid.iterkeys())
 
373
        new_ids = set(new._byid.iterkeys())
 
374
        adds = new_ids - old_ids
 
375
        deletes = old_ids - new_ids
 
376
        common = old_ids.intersection(new_ids)
 
377
        delta = []
 
378
        for file_id in deletes:
 
379
            delta.append((old.id2path(file_id), None, file_id, None))
 
380
        for file_id in adds:
 
381
            delta.append((None, new.id2path(file_id), file_id, new[file_id]))
 
382
        for file_id in common:
 
383
            if old[file_id] != new[file_id]:
 
384
                delta.append((old.id2path(file_id), new.id2path(file_id),
 
385
                    file_id, new[file_id]))
 
386
        return delta
 
387
 
 
388
    def fake_up_revision(self, tree, revid, shape):
 
389
        tree.lock_write()
 
390
        try:
 
391
            tree.branch.repository.start_write_group()
 
392
            try:
 
393
                if shape.root.revision is None:
 
394
                    shape.root.revision = revid
 
395
                sha1 = tree.branch.repository.add_inventory(revid, shape, [])
 
396
                rev = Revision(timestamp=0,
 
397
                               timezone=None,
 
398
                               committer="Foo Bar <foo@example.com>",
 
399
                               message="Message",
 
400
                               inventory_sha1=sha1,
 
401
                               revision_id=revid)
 
402
                tree.branch.repository.add_revision(revid, rev)
 
403
            except:
 
404
                tree.branch.repository.abort_write_group()
 
405
                raise
 
406
            else:
 
407
                tree.branch.repository.commit_write_group()
 
408
        finally:
 
409
            tree.unlock()
 
410
 
 
411
    def add_entry(self, inv, rev_id, entry):
 
412
        entry.revision = rev_id
 
413
        inv.add(entry)
 
414
 
 
415
    def add_dir(self, inv, rev_id, file_id, parent_id, name):
 
416
        new_dir = InventoryDirectory(file_id, name, parent_id)
 
417
        self.add_entry(inv, rev_id, new_dir)
 
418
 
 
419
    def add_file(self, inv, rev_id, file_id, parent_id, name, sha, size):
 
420
        new_file = InventoryFile(file_id, name, parent_id)
 
421
        new_file.text_sha1 = sha
 
422
        new_file.text_size = size
 
423
        self.add_entry(inv, rev_id, new_file)
 
424
 
 
425
    def add_link(self, inv, rev_id, file_id, parent_id, name, target):
 
426
        new_link = InventoryLink(file_id, name, parent_id)
 
427
        new_link.symlink_target = target
 
428
        self.add_entry(inv, rev_id, new_link)
 
429
 
 
430
    def add_new_root(self, new_shape, old_revid, new_revid):
 
431
        if self.bzrdir_format.repository_format.rich_root_data:
 
432
            self.add_dir(new_shape, old_revid, 'root-id', None, '')
 
433
        else:
 
434
            self.add_dir(new_shape, new_revid, 'root-id', None, '')
 
435
 
 
436
    def assertTransitionFromBasisToShape(self, basis_shape, basis_revid,
 
437
        new_shape, new_revid, extra_parent=None):
 
438
        # set the inventory revision ids.
 
439
        basis_shape.revision_id = basis_revid
 
440
        new_shape.revision_id = new_revid
 
441
        delta = self.make_inv_delta(basis_shape, new_shape)
 
442
        tree = self.make_branch_and_tree('tree')
 
443
        # the shapes need to be in the tree's repository to be able to set them
 
444
        # as a parent, but the file content is not needed.
 
445
        if basis_revid is not None:
 
446
            self.fake_up_revision(tree, basis_revid, basis_shape)
 
447
            parents = [basis_revid]
 
448
            if extra_parent is not None:
 
449
                parents.append(extra_parent)
 
450
            tree.set_parent_ids(parents)
 
451
        self.fake_up_revision(tree, new_revid, new_shape)
 
452
        # give tree an inventory of new_shape
 
453
        tree._write_inventory(new_shape)
 
454
        self.assertDeltaApplicationResultsInExpectedBasis(tree, new_revid,
 
455
            delta, new_shape)
 
456
        # The tree should be internally consistent; while this is a moderately
 
457
        # large hammer, this is a particularly sensitive area of code, so the
 
458
        # extra assurance is well worth it.
 
459
        tree._validate()
 
460
        osutils.rmtree('tree')
 
461
 
 
462
    def test_no_parents_just_root(self):
 
463
        """Test doing an empty commit - no parent, set a root only."""
 
464
        basis_shape = Inventory(root_id=None) # empty tree
 
465
        new_shape = Inventory() # tree with a root
 
466
        self.assertTransitionFromBasisToShape(basis_shape, None, new_shape,
 
467
            'new_parent')
 
468
 
 
469
    def test_no_parents_full_tree(self):
 
470
        """Test doing a regular initial commit with files and dirs."""
 
471
        basis_shape = Inventory(root_id=None) # empty tree
 
472
        revid = 'new-parent'
 
473
        new_shape = Inventory(root_id=None)
 
474
        self.add_dir(new_shape, revid, 'root-id', None, '')
 
475
        self.add_link(new_shape, revid, 'link-id', 'root-id', 'link', 'target')
 
476
        self.add_file(new_shape, revid, 'file-id', 'root-id', 'file', '1' * 32,
 
477
            12)
 
478
        self.add_dir(new_shape, revid, 'dir-id', 'root-id', 'dir')
 
479
        self.add_file(new_shape, revid, 'subfile-id', 'dir-id', 'subfile',
 
480
            '2' * 32, 24)
 
481
        self.assertTransitionFromBasisToShape(basis_shape, None, new_shape,
 
482
            revid)
 
483
 
 
484
    def test_file_content_change(self):
 
485
        old_revid = 'old-parent'
 
486
        basis_shape = Inventory(root_id=None)
 
487
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
 
488
        self.add_file(basis_shape, old_revid, 'file-id', 'root-id', 'file',
 
489
            '1' * 32, 12)
 
490
        new_revid = 'new-parent'
 
491
        new_shape = Inventory(root_id=None)
 
492
        self.add_new_root(new_shape, old_revid, new_revid)
 
493
        self.add_file(new_shape, new_revid, 'file-id', 'root-id', 'file',
 
494
            '2' * 32, 24)
 
495
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
 
496
            new_shape, new_revid)
 
497
 
 
498
    def test_link_content_change(self):
 
499
        old_revid = 'old-parent'
 
500
        basis_shape = Inventory(root_id=None)
 
501
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
 
502
        self.add_link(basis_shape, old_revid, 'link-id', 'root-id', 'link',
 
503
            'old-target')
 
504
        new_revid = 'new-parent'
 
505
        new_shape = Inventory(root_id=None)
 
506
        self.add_new_root(new_shape, old_revid, new_revid)
 
507
        self.add_link(new_shape, new_revid, 'link-id', 'root-id', 'link',
 
508
            'new-target')
 
509
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
 
510
            new_shape, new_revid)
 
511
 
 
512
    def test_kind_changes(self):
 
513
        def do_file(inv, revid):
 
514
            self.add_file(inv, revid, 'path-id', 'root-id', 'path', '1' * 32,
 
515
                12)
 
516
        def do_link(inv, revid):
 
517
            self.add_link(inv, revid, 'path-id', 'root-id', 'path', 'target')
 
518
        def do_dir(inv, revid):
 
519
            self.add_dir(inv, revid, 'path-id', 'root-id', 'path')
 
520
        for old_factory in (do_file, do_link, do_dir):
 
521
            for new_factory in (do_file, do_link, do_dir):
 
522
                if old_factory == new_factory:
 
523
                    continue
 
524
                old_revid = 'old-parent'
 
525
                basis_shape = Inventory(root_id=None)
 
526
                self.add_dir(basis_shape, old_revid, 'root-id', None, '')
 
527
                old_factory(basis_shape, old_revid)
 
528
                new_revid = 'new-parent'
 
529
                new_shape = Inventory(root_id=None)
 
530
                self.add_new_root(new_shape, old_revid, new_revid)
 
531
                new_factory(new_shape, new_revid)
 
532
                self.assertTransitionFromBasisToShape(basis_shape, old_revid,
 
533
                    new_shape, new_revid)
 
534
 
 
535
    def test_content_from_second_parent_is_dropped(self):
 
536
        left_revid = 'left-parent'
 
537
        basis_shape = Inventory(root_id=None)
 
538
        self.add_dir(basis_shape, left_revid, 'root-id', None, '')
 
539
        self.add_link(basis_shape, left_revid, 'link-id', 'root-id', 'link',
 
540
            'left-target')
 
541
        # the right shape has content - file, link, subdir with a child,
 
542
        # that should all be discarded by the call.
 
543
        right_revid = 'right-parent'
 
544
        right_shape = Inventory(root_id=None)
 
545
        self.add_dir(right_shape, left_revid, 'root-id', None, '')
 
546
        self.add_link(right_shape, right_revid, 'link-id', 'root-id', 'link',
 
547
            'some-target')
 
548
        self.add_dir(right_shape, right_revid, 'subdir-id', 'root-id', 'dir')
 
549
        self.add_file(right_shape, right_revid, 'file-id', 'subdir-id', 'file',
 
550
            '2' * 32, 24)
 
551
        new_revid = 'new-parent'
 
552
        new_shape = Inventory(root_id=None)
 
553
        self.add_new_root(new_shape, left_revid, new_revid)
 
554
        self.add_link(new_shape, new_revid, 'link-id', 'root-id', 'link',
 
555
            'new-target')
 
556
        self.assertTransitionFromBasisToShape(basis_shape, left_revid,
 
557
            new_shape, new_revid, right_revid)
 
558
 
 
559
    def test_parent_id_changed(self):
 
560
        # test that when the only change to an entry is its parent id changing
 
561
        # that it is handled correctly (that is it keeps the same path)
 
562
        old_revid = 'old-parent'
 
563
        basis_shape = Inventory(root_id=None)
 
564
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
 
565
        self.add_dir(basis_shape, old_revid, 'orig-parent-id', 'root-id', 'dir')
 
566
        self.add_dir(basis_shape, old_revid, 'dir-id', 'orig-parent-id', 'dir')
 
567
        new_revid = 'new-parent'
 
568
        new_shape = Inventory(root_id=None)
 
569
        self.add_new_root(new_shape, old_revid, new_revid)
 
570
        self.add_dir(new_shape, new_revid, 'new-parent-id', 'root-id', 'dir')
 
571
        self.add_dir(new_shape, new_revid, 'dir-id', 'new-parent-id', 'dir')
 
572
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
 
573
            new_shape, new_revid)
 
574
 
 
575
    def test_name_changed(self):
 
576
        # test that when the only change to an entry is its name changing that
 
577
        # it is handled correctly (that is it keeps the same parent id)
 
578
        old_revid = 'old-parent'
 
579
        basis_shape = Inventory(root_id=None)
 
580
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
 
581
        self.add_dir(basis_shape, old_revid, 'parent-id', 'root-id', 'origdir')
 
582
        self.add_dir(basis_shape, old_revid, 'dir-id', 'parent-id', 'olddir')
 
583
        new_revid = 'new-parent'
 
584
        new_shape = Inventory(root_id=None)
 
585
        self.add_new_root(new_shape, old_revid, new_revid)
 
586
        self.add_dir(new_shape, new_revid, 'parent-id', 'root-id', 'newdir')
 
587
        self.add_dir(new_shape, new_revid, 'dir-id', 'parent-id', 'newdir')
 
588
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
 
589
            new_shape, new_revid)
 
590
 
 
591
    def test_parent_child_swap(self):
 
592
        # test a A->A/B and A/B->A path swap.
 
593
        old_revid = 'old-parent'
 
594
        basis_shape = Inventory(root_id=None)
 
595
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
 
596
        self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
 
597
        self.add_dir(basis_shape, old_revid, 'dir-id-B', 'dir-id-A', 'B')
 
598
        self.add_link(basis_shape, old_revid, 'link-id-C', 'dir-id-B', 'C', 'C')
 
599
        new_revid = 'new-parent'
 
600
        new_shape = Inventory(root_id=None)
 
601
        self.add_new_root(new_shape, old_revid, new_revid)
 
602
        self.add_dir(new_shape, new_revid, 'dir-id-B', 'root-id', 'A')
 
603
        self.add_dir(new_shape, new_revid, 'dir-id-A', 'dir-id-B', 'B')
 
604
        self.add_link(new_shape, new_revid, 'link-id-C', 'dir-id-A', 'C', 'C')
 
605
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
 
606
            new_shape, new_revid)
 
607
 
 
608
    def test_parent_deleted_child_renamed(self):
 
609
        # test a A->None and A/B->A.
 
610
        old_revid = 'old-parent'
 
611
        basis_shape = Inventory(root_id=None)
 
612
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
 
613
        self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
 
614
        self.add_dir(basis_shape, old_revid, 'dir-id-B', 'dir-id-A', 'B')
 
615
        self.add_link(basis_shape, old_revid, 'link-id-C', 'dir-id-B', 'C', 'C')
 
616
        new_revid = 'new-parent'
 
617
        new_shape = Inventory(root_id=None)
 
618
        self.add_new_root(new_shape, old_revid, new_revid)
 
619
        self.add_dir(new_shape, new_revid, 'dir-id-B', 'root-id', 'A')
 
620
        self.add_link(new_shape, old_revid, 'link-id-C', 'dir-id-B', 'C', 'C')
 
621
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
 
622
            new_shape, new_revid)
 
623
 
 
624
    def test_dir_to_root(self):
 
625
        # test a A->''.
 
626
        old_revid = 'old-parent'
 
627
        basis_shape = Inventory(root_id=None)
 
628
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
 
629
        self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
 
630
        self.add_link(basis_shape, old_revid, 'link-id-B', 'dir-id-A', 'B', 'B')
 
631
        new_revid = 'new-parent'
 
632
        new_shape = Inventory(root_id=None)
 
633
        self.add_dir(new_shape, new_revid, 'dir-id-A', None, '')
 
634
        self.add_link(new_shape, old_revid, 'link-id-B', 'dir-id-A', 'B', 'B')
 
635
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
 
636
            new_shape, new_revid)
 
637
 
 
638
    def test_path_swap(self):
 
639
        # test a A->B and B->A path swap.
 
640
        old_revid = 'old-parent'
 
641
        basis_shape = Inventory(root_id=None)
 
642
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
 
643
        self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
 
644
        self.add_dir(basis_shape, old_revid, 'dir-id-B', 'root-id', 'B')
 
645
        self.add_link(basis_shape, old_revid, 'link-id-C', 'root-id', 'C', 'C')
 
646
        self.add_link(basis_shape, old_revid, 'link-id-D', 'root-id', 'D', 'D')
 
647
        self.add_file(basis_shape, old_revid, 'file-id-E', 'root-id', 'E',
 
648
            '1' * 32, 12)
 
649
        self.add_file(basis_shape, old_revid, 'file-id-F', 'root-id', 'F',
 
650
            '2' * 32, 24)
 
651
        new_revid = 'new-parent'
 
652
        new_shape = Inventory(root_id=None)
 
653
        self.add_new_root(new_shape, old_revid, new_revid)
 
654
        self.add_dir(new_shape, new_revid, 'dir-id-A', 'root-id', 'B')
 
655
        self.add_dir(new_shape, new_revid, 'dir-id-B', 'root-id', 'A')
 
656
        self.add_link(new_shape, new_revid, 'link-id-C', 'root-id', 'D', 'C')
 
657
        self.add_link(new_shape, new_revid, 'link-id-D', 'root-id', 'C', 'D')
 
658
        self.add_file(new_shape, new_revid, 'file-id-E', 'root-id', 'F',
 
659
            '1' * 32, 12)
 
660
        self.add_file(new_shape, new_revid, 'file-id-F', 'root-id', 'E',
 
661
            '2' * 32, 24)
 
662
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
 
663
            new_shape, new_revid)
 
664
 
 
665
    def test_adds(self):
 
666
        # test adding paths and dirs, including adding to a newly added dir.
 
667
        old_revid = 'old-parent'
 
668
        basis_shape = Inventory(root_id=None)
 
669
        # with a root, so its a commit after the first.
 
670
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
 
671
        new_revid = 'new-parent'
 
672
        new_shape = Inventory(root_id=None)
 
673
        self.add_new_root(new_shape, old_revid, new_revid)
 
674
        self.add_dir(new_shape, new_revid, 'dir-id-A', 'root-id', 'A')
 
675
        self.add_link(new_shape, new_revid, 'link-id-B', 'root-id', 'B', 'C')
 
676
        self.add_file(new_shape, new_revid, 'file-id-C', 'root-id', 'C',
 
677
            '1' * 32, 12)
 
678
        self.add_file(new_shape, new_revid, 'file-id-D', 'dir-id-A', 'D',
 
679
            '2' * 32, 24)
 
680
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
 
681
            new_shape, new_revid)
 
682
 
 
683
    def test_removes(self):
 
684
        # test removing paths, including paths that are within other also
 
685
        # removed paths.
 
686
        old_revid = 'old-parent'
 
687
        basis_shape = Inventory(root_id=None)
 
688
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
 
689
        self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
 
690
        self.add_link(basis_shape, old_revid, 'link-id-B', 'root-id', 'B', 'C')
 
691
        self.add_file(basis_shape, old_revid, 'file-id-C', 'root-id', 'C',
 
692
            '1' * 32, 12)
 
693
        self.add_file(basis_shape, old_revid, 'file-id-D', 'dir-id-A', 'D',
 
694
            '2' * 32, 24)
 
695
        new_revid = 'new-parent'
 
696
        new_shape = Inventory(root_id=None)
 
697
        self.add_new_root(new_shape, old_revid, new_revid)
 
698
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
 
699
            new_shape, new_revid)
 
700
 
 
701
    def test_move_to_added_dir(self):
 
702
        old_revid = 'old-parent'
 
703
        basis_shape = Inventory(root_id=None)
 
704
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
 
705
        self.add_link(basis_shape, old_revid, 'link-id-B', 'root-id', 'B', 'C')
 
706
        new_revid = 'new-parent'
 
707
        new_shape = Inventory(root_id=None)
 
708
        self.add_new_root(new_shape, old_revid, new_revid)
 
709
        self.add_dir(new_shape, new_revid, 'dir-id-A', 'root-id', 'A')
 
710
        self.add_link(new_shape, new_revid, 'link-id-B', 'dir-id-A', 'B', 'C')
 
711
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
 
712
            new_shape, new_revid)
 
713
 
 
714
    def test_move_from_removed_dir(self):
 
715
        old_revid = 'old-parent'
 
716
        basis_shape = Inventory(root_id=None)
 
717
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
 
718
        self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
 
719
        self.add_link(basis_shape, old_revid, 'link-id-B', 'dir-id-A', 'B', 'C')
 
720
        new_revid = 'new-parent'
 
721
        new_shape = Inventory(root_id=None)
 
722
        self.add_new_root(new_shape, old_revid, new_revid)
 
723
        self.add_link(new_shape, new_revid, 'link-id-B', 'root-id', 'B', 'C')
 
724
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
 
725
            new_shape, new_revid)
 
726
 
 
727
    def test_move_moves_children_recursively(self):
 
728
        old_revid = 'old-parent'
 
729
        basis_shape = Inventory(root_id=None)
 
730
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
 
731
        self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
 
732
        self.add_dir(basis_shape, old_revid, 'dir-id-B', 'dir-id-A', 'B')
 
733
        self.add_link(basis_shape, old_revid, 'link-id-C', 'dir-id-B', 'C', 'D')
 
734
        new_revid = 'new-parent'
 
735
        new_shape = Inventory(root_id=None)
 
736
        self.add_new_root(new_shape, old_revid, new_revid)
 
737
        # the moved path:
 
738
        self.add_dir(new_shape, new_revid, 'dir-id-A', 'root-id', 'B')
 
739
        # unmoved children.
 
740
        self.add_dir(new_shape, old_revid, 'dir-id-B', 'dir-id-A', 'B')
 
741
        self.add_link(new_shape, old_revid, 'link-id-C', 'dir-id-B', 'C', 'D')
 
742
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
 
743
            new_shape, new_revid)