/brz/remove-bazaar

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

« back to all changes in this revision

Viewing changes to breezy/tests/test_transform.py

  • Committer: Jelmer Vernooij
  • Date: 2020-11-18 02:15:43 UTC
  • mfrom: (7490.40.117 work)
  • mto: This revision was merged to the branch mainline in revision 7526.
  • Revision ID: jelmer@jelmer.uk-20201118021543-0flrmtqv7ibat6yv
Merge lp:brz/3.1.

Show diffs side-by-side

added added

removed removed

Lines of Context:
70
70
    SymlinkFeature,
71
71
    )
72
72
from ..transform import (
73
 
    build_tree,
74
73
    create_from_tree,
75
74
    _FileMover,
76
75
    FinalPaths,
77
76
    resolve_conflicts,
78
 
    resolve_checkout,
79
77
    ROOT_PARENT,
80
78
    ImmortalLimbo,
81
79
    MalformedTransform,
293
291
        self.assertEqual(this.wt.id2path(b'i'), pathjoin('b/i1.OTHER'))
294
292
 
295
293
 
296
 
class TestBuildTree(tests.TestCaseWithTransport):
297
 
 
298
 
    def test_build_tree_with_symlinks(self):
299
 
        self.requireFeature(SymlinkFeature)
300
 
        os.mkdir('a')
301
 
        a = ControlDir.create_standalone_workingtree('a')
302
 
        os.mkdir('a/foo')
303
 
        with open('a/foo/bar', 'wb') as f:
304
 
            f.write(b'contents')
305
 
        os.symlink('a/foo/bar', 'a/foo/baz')
306
 
        a.add(['foo', 'foo/bar', 'foo/baz'])
307
 
        a.commit('initial commit')
308
 
        b = ControlDir.create_standalone_workingtree('b')
309
 
        basis = a.basis_tree()
310
 
        basis.lock_read()
311
 
        self.addCleanup(basis.unlock)
312
 
        build_tree(basis, b)
313
 
        self.assertIs(os.path.isdir('b/foo'), True)
314
 
        with open('b/foo/bar', 'rb') as f:
315
 
            self.assertEqual(f.read(), b"contents")
316
 
        self.assertEqual(os.readlink('b/foo/baz'), 'a/foo/bar')
317
 
 
318
 
    def test_build_with_references(self):
319
 
        tree = self.make_branch_and_tree('source',
320
 
                                         format='development-subtree')
321
 
        subtree = self.make_branch_and_tree('source/subtree',
322
 
                                            format='development-subtree')
323
 
        tree.add_reference(subtree)
324
 
        tree.commit('a revision')
325
 
        tree.branch.create_checkout('target')
326
 
        self.assertPathExists('target')
327
 
        self.assertPathExists('target/subtree')
328
 
 
329
 
    def test_file_conflict_handling(self):
330
 
        """Ensure that when building trees, conflict handling is done"""
331
 
        source = self.make_branch_and_tree('source')
332
 
        target = self.make_branch_and_tree('target')
333
 
        self.build_tree(['source/file', 'target/file'])
334
 
        source.add('file', b'new-file')
335
 
        source.commit('added file')
336
 
        build_tree(source.basis_tree(), target)
337
 
        self.assertEqual(
338
 
            [DuplicateEntry('Moved existing file to', 'file.moved',
339
 
                            'file', None, 'new-file')],
340
 
            target.conflicts())
341
 
        target2 = self.make_branch_and_tree('target2')
342
 
        with open('target2/file', 'wb') as target_file, \
343
 
                open('source/file', 'rb') as source_file:
344
 
            target_file.write(source_file.read())
345
 
        build_tree(source.basis_tree(), target2)
346
 
        self.assertEqual([], target2.conflicts())
347
 
 
348
 
    def test_symlink_conflict_handling(self):
349
 
        """Ensure that when building trees, conflict handling is done"""
350
 
        self.requireFeature(SymlinkFeature)
351
 
        source = self.make_branch_and_tree('source')
352
 
        os.symlink('foo', 'source/symlink')
353
 
        source.add('symlink', b'new-symlink')
354
 
        source.commit('added file')
355
 
        target = self.make_branch_and_tree('target')
356
 
        os.symlink('bar', 'target/symlink')
357
 
        build_tree(source.basis_tree(), target)
358
 
        self.assertEqual(
359
 
            [DuplicateEntry('Moved existing file to', 'symlink.moved',
360
 
                            'symlink', None, 'new-symlink')],
361
 
            target.conflicts())
362
 
        target = self.make_branch_and_tree('target2')
363
 
        os.symlink('foo', 'target2/symlink')
364
 
        build_tree(source.basis_tree(), target)
365
 
        self.assertEqual([], target.conflicts())
366
 
 
367
 
    def test_directory_conflict_handling(self):
368
 
        """Ensure that when building trees, conflict handling is done"""
369
 
        source = self.make_branch_and_tree('source')
370
 
        target = self.make_branch_and_tree('target')
371
 
        self.build_tree(['source/dir1/', 'source/dir1/file', 'target/dir1/'])
372
 
        source.add(['dir1', 'dir1/file'], [b'new-dir1', b'new-file'])
373
 
        source.commit('added file')
374
 
        build_tree(source.basis_tree(), target)
375
 
        self.assertEqual([], target.conflicts())
376
 
        self.assertPathExists('target/dir1/file')
377
 
 
378
 
        # Ensure contents are merged
379
 
        target = self.make_branch_and_tree('target2')
380
 
        self.build_tree(['target2/dir1/', 'target2/dir1/file2'])
381
 
        build_tree(source.basis_tree(), target)
382
 
        self.assertEqual([], target.conflicts())
383
 
        self.assertPathExists('target2/dir1/file2')
384
 
        self.assertPathExists('target2/dir1/file')
385
 
 
386
 
        # Ensure new contents are suppressed for existing branches
387
 
        target = self.make_branch_and_tree('target3')
388
 
        self.make_branch('target3/dir1')
389
 
        self.build_tree(['target3/dir1/file2'])
390
 
        build_tree(source.basis_tree(), target)
391
 
        self.assertPathDoesNotExist('target3/dir1/file')
392
 
        self.assertPathExists('target3/dir1/file2')
393
 
        self.assertPathExists('target3/dir1.diverted/file')
394
 
        self.assertEqual(
395
 
            [DuplicateEntry('Diverted to', 'dir1.diverted',
396
 
                            'dir1', 'new-dir1', None)],
397
 
            target.conflicts())
398
 
 
399
 
        target = self.make_branch_and_tree('target4')
400
 
        self.build_tree(['target4/dir1/'])
401
 
        self.make_branch('target4/dir1/file')
402
 
        build_tree(source.basis_tree(), target)
403
 
        self.assertPathExists('target4/dir1/file')
404
 
        self.assertEqual('directory', file_kind('target4/dir1/file'))
405
 
        self.assertPathExists('target4/dir1/file.diverted')
406
 
        self.assertEqual(
407
 
            [DuplicateEntry('Diverted to', 'dir1/file.diverted',
408
 
                            'dir1/file', 'new-file', None)],
409
 
            target.conflicts())
410
 
 
411
 
    def test_mixed_conflict_handling(self):
412
 
        """Ensure that when building trees, conflict handling is done"""
413
 
        source = self.make_branch_and_tree('source')
414
 
        target = self.make_branch_and_tree('target')
415
 
        self.build_tree(['source/name', 'target/name/'])
416
 
        source.add('name', b'new-name')
417
 
        source.commit('added file')
418
 
        build_tree(source.basis_tree(), target)
419
 
        self.assertEqual(
420
 
            [DuplicateEntry('Moved existing file to',
421
 
                            'name.moved', 'name', None, 'new-name')],
422
 
            target.conflicts())
423
 
 
424
 
    def test_raises_in_populated(self):
425
 
        source = self.make_branch_and_tree('source')
426
 
        self.build_tree(['source/name'])
427
 
        source.add('name')
428
 
        source.commit('added name')
429
 
        target = self.make_branch_and_tree('target')
430
 
        self.build_tree(['target/name'])
431
 
        target.add('name')
432
 
        self.assertRaises(errors.WorkingTreeAlreadyPopulated,
433
 
                          build_tree, source.basis_tree(), target)
434
 
 
435
 
    def test_build_tree_rename_count(self):
436
 
        source = self.make_branch_and_tree('source')
437
 
        self.build_tree(['source/file1', 'source/dir1/'])
438
 
        source.add(['file1', 'dir1'])
439
 
        source.commit('add1')
440
 
        target1 = self.make_branch_and_tree('target1')
441
 
        transform_result = build_tree(source.basis_tree(), target1)
442
 
        self.assertEqual(2, transform_result.rename_count)
443
 
 
444
 
        self.build_tree(['source/dir1/file2'])
445
 
        source.add(['dir1/file2'])
446
 
        source.commit('add3')
447
 
        target2 = self.make_branch_and_tree('target2')
448
 
        transform_result = build_tree(source.basis_tree(), target2)
449
 
        # children of non-root directories should not be renamed
450
 
        self.assertEqual(2, transform_result.rename_count)
451
 
 
452
 
    def create_ab_tree(self):
453
 
        """Create a committed test tree with two files"""
454
 
        source = self.make_branch_and_tree('source')
455
 
        self.build_tree_contents([('source/file1', b'A')])
456
 
        self.build_tree_contents([('source/file2', b'B')])
457
 
        source.add(['file1', 'file2'], [b'file1-id', b'file2-id'])
458
 
        source.commit('commit files')
459
 
        source.lock_write()
460
 
        self.addCleanup(source.unlock)
461
 
        return source
462
 
 
463
 
    def test_build_tree_accelerator_tree(self):
464
 
        source = self.create_ab_tree()
465
 
        self.build_tree_contents([('source/file2', b'C')])
466
 
        calls = []
467
 
        real_source_get_file = source.get_file
468
 
 
469
 
        def get_file(path):
470
 
            calls.append(path)
471
 
            return real_source_get_file(path)
472
 
        source.get_file = get_file
473
 
        target = self.make_branch_and_tree('target')
474
 
        revision_tree = source.basis_tree()
475
 
        revision_tree.lock_read()
476
 
        self.addCleanup(revision_tree.unlock)
477
 
        build_tree(revision_tree, target, source)
478
 
        self.assertEqual(['file1'], calls)
479
 
        target.lock_read()
480
 
        self.addCleanup(target.unlock)
481
 
        self.assertEqual([], list(target.iter_changes(revision_tree)))
482
 
 
483
 
    def test_build_tree_accelerator_tree_observes_sha1(self):
484
 
        source = self.create_ab_tree()
485
 
        sha1 = osutils.sha_string(b'A')
486
 
        target = self.make_branch_and_tree('target')
487
 
        target.lock_write()
488
 
        self.addCleanup(target.unlock)
489
 
        state = target.current_dirstate()
490
 
        state._cutoff_time = time.time() + 60
491
 
        build_tree(source.basis_tree(), target, source)
492
 
        entry = state._get_entry(0, path_utf8=b'file1')
493
 
        self.assertEqual(sha1, entry[1][0][1])
494
 
 
495
 
    def test_build_tree_accelerator_tree_missing_file(self):
496
 
        source = self.create_ab_tree()
497
 
        os.unlink('source/file1')
498
 
        source.remove(['file2'])
499
 
        target = self.make_branch_and_tree('target')
500
 
        revision_tree = source.basis_tree()
501
 
        revision_tree.lock_read()
502
 
        self.addCleanup(revision_tree.unlock)
503
 
        build_tree(revision_tree, target, source)
504
 
        target.lock_read()
505
 
        self.addCleanup(target.unlock)
506
 
        self.assertEqual([], list(target.iter_changes(revision_tree)))
507
 
 
508
 
    def test_build_tree_accelerator_wrong_kind(self):
509
 
        self.requireFeature(SymlinkFeature)
510
 
        source = self.make_branch_and_tree('source')
511
 
        self.build_tree_contents([('source/file1', b'')])
512
 
        self.build_tree_contents([('source/file2', b'')])
513
 
        source.add(['file1', 'file2'], [b'file1-id', b'file2-id'])
514
 
        source.commit('commit files')
515
 
        os.unlink('source/file2')
516
 
        self.build_tree_contents([('source/file2/', b'C')])
517
 
        os.unlink('source/file1')
518
 
        os.symlink('file2', 'source/file1')
519
 
        calls = []
520
 
        real_source_get_file = source.get_file
521
 
 
522
 
        def get_file(path):
523
 
            calls.append(path)
524
 
            return real_source_get_file(path)
525
 
        source.get_file = get_file
526
 
        target = self.make_branch_and_tree('target')
527
 
        revision_tree = source.basis_tree()
528
 
        revision_tree.lock_read()
529
 
        self.addCleanup(revision_tree.unlock)
530
 
        build_tree(revision_tree, target, source)
531
 
        self.assertEqual([], calls)
532
 
        target.lock_read()
533
 
        self.addCleanup(target.unlock)
534
 
        self.assertEqual([], list(target.iter_changes(revision_tree)))
535
 
 
536
 
    def test_build_tree_hardlink(self):
537
 
        self.requireFeature(HardlinkFeature)
538
 
        source = self.create_ab_tree()
539
 
        target = self.make_branch_and_tree('target')
540
 
        revision_tree = source.basis_tree()
541
 
        revision_tree.lock_read()
542
 
        self.addCleanup(revision_tree.unlock)
543
 
        build_tree(revision_tree, target, source, hardlink=True)
544
 
        target.lock_read()
545
 
        self.addCleanup(target.unlock)
546
 
        self.assertEqual([], list(target.iter_changes(revision_tree)))
547
 
        source_stat = os.stat('source/file1')
548
 
        target_stat = os.stat('target/file1')
549
 
        self.assertEqual(source_stat, target_stat)
550
 
 
551
 
        # Explicitly disallowing hardlinks should prevent them.
552
 
        target2 = self.make_branch_and_tree('target2')
553
 
        build_tree(revision_tree, target2, source, hardlink=False)
554
 
        target2.lock_read()
555
 
        self.addCleanup(target2.unlock)
556
 
        self.assertEqual([], list(target2.iter_changes(revision_tree)))
557
 
        source_stat = os.stat('source/file1')
558
 
        target2_stat = os.stat('target2/file1')
559
 
        self.assertNotEqual(source_stat, target2_stat)
560
 
 
561
 
    def test_build_tree_accelerator_tree_moved(self):
562
 
        source = self.make_branch_and_tree('source')
563
 
        self.build_tree_contents([('source/file1', b'A')])
564
 
        source.add(['file1'], [b'file1-id'])
565
 
        source.commit('commit files')
566
 
        source.rename_one('file1', 'file2')
567
 
        source.lock_read()
568
 
        self.addCleanup(source.unlock)
569
 
        target = self.make_branch_and_tree('target')
570
 
        revision_tree = source.basis_tree()
571
 
        revision_tree.lock_read()
572
 
        self.addCleanup(revision_tree.unlock)
573
 
        build_tree(revision_tree, target, source)
574
 
        target.lock_read()
575
 
        self.addCleanup(target.unlock)
576
 
        self.assertEqual([], list(target.iter_changes(revision_tree)))
577
 
 
578
 
    def test_build_tree_hardlinks_preserve_execute(self):
579
 
        self.requireFeature(HardlinkFeature)
580
 
        source = self.create_ab_tree()
581
 
        tt = source.transform()
582
 
        trans_id = tt.trans_id_tree_path('file1')
583
 
        tt.set_executability(True, trans_id)
584
 
        tt.apply()
585
 
        self.assertTrue(source.is_executable('file1'))
586
 
        target = self.make_branch_and_tree('target')
587
 
        revision_tree = source.basis_tree()
588
 
        revision_tree.lock_read()
589
 
        self.addCleanup(revision_tree.unlock)
590
 
        build_tree(revision_tree, target, source, hardlink=True)
591
 
        target.lock_read()
592
 
        self.addCleanup(target.unlock)
593
 
        self.assertEqual([], list(target.iter_changes(revision_tree)))
594
 
        self.assertTrue(source.is_executable('file1'))
595
 
 
596
 
    def install_rot13_content_filter(self, pattern):
597
 
        # We could use
598
 
        # self.addCleanup(filters._reset_registry, filters._reset_registry())
599
 
        # below, but that looks a bit... hard to read even if it's exactly
600
 
        # the same thing.
601
 
        original_registry = filters._reset_registry()
602
 
 
603
 
        def restore_registry():
604
 
            filters._reset_registry(original_registry)
605
 
        self.addCleanup(restore_registry)
606
 
 
607
 
        def rot13(chunks, context=None):
608
 
            return [
609
 
                codecs.encode(chunk.decode('ascii'), 'rot13').encode('ascii')
610
 
                for chunk in chunks]
611
 
        rot13filter = filters.ContentFilter(rot13, rot13)
612
 
        filters.filter_stacks_registry.register(
613
 
            'rot13', {'yes': [rot13filter]}.get)
614
 
        os.mkdir(self.test_home_dir + '/.bazaar')
615
 
        rules_filename = self.test_home_dir + '/.bazaar/rules'
616
 
        with open(rules_filename, 'wb') as f:
617
 
            f.write(b'[name %s]\nrot13=yes\n' % (pattern,))
618
 
 
619
 
        def uninstall_rules():
620
 
            os.remove(rules_filename)
621
 
            rules.reset_rules()
622
 
        self.addCleanup(uninstall_rules)
623
 
        rules.reset_rules()
624
 
 
625
 
    def test_build_tree_content_filtered_files_are_not_hardlinked(self):
626
 
        """build_tree will not hardlink files that have content filtering rules
627
 
        applied to them (but will still hardlink other files from the same tree
628
 
        if it can).
629
 
        """
630
 
        self.requireFeature(HardlinkFeature)
631
 
        self.install_rot13_content_filter(b'file1')
632
 
        source = self.create_ab_tree()
633
 
        target = self.make_branch_and_tree('target')
634
 
        revision_tree = source.basis_tree()
635
 
        revision_tree.lock_read()
636
 
        self.addCleanup(revision_tree.unlock)
637
 
        build_tree(revision_tree, target, source, hardlink=True)
638
 
        target.lock_read()
639
 
        self.addCleanup(target.unlock)
640
 
        self.assertEqual([], list(target.iter_changes(revision_tree)))
641
 
        source_stat = os.stat('source/file1')
642
 
        target_stat = os.stat('target/file1')
643
 
        self.assertNotEqual(source_stat, target_stat)
644
 
        source_stat = os.stat('source/file2')
645
 
        target_stat = os.stat('target/file2')
646
 
        self.assertEqualStat(source_stat, target_stat)
647
 
 
648
 
    def test_case_insensitive_build_tree_inventory(self):
649
 
        if (features.CaseInsensitiveFilesystemFeature.available()
650
 
                or features.CaseInsCasePresFilenameFeature.available()):
651
 
            raise tests.UnavailableFeature('Fully case sensitive filesystem')
652
 
        source = self.make_branch_and_tree('source')
653
 
        self.build_tree(['source/file', 'source/FILE'])
654
 
        source.add(['file', 'FILE'], [b'lower-id', b'upper-id'])
655
 
        source.commit('added files')
656
 
        # Don't try this at home, kids!
657
 
        # Force the tree to report that it is case insensitive
658
 
        target = self.make_branch_and_tree('target')
659
 
        target.case_sensitive = False
660
 
        build_tree(source.basis_tree(), target, source, delta_from_tree=True)
661
 
        self.assertEqual('file.moved', target.id2path(b'lower-id'))
662
 
        self.assertEqual('FILE', target.id2path(b'upper-id'))
663
 
 
664
 
    def test_build_tree_observes_sha(self):
665
 
        source = self.make_branch_and_tree('source')
666
 
        self.build_tree(['source/file1', 'source/dir/', 'source/dir/file2'])
667
 
        source.add(['file1', 'dir', 'dir/file2'],
668
 
                   [b'file1-id', b'dir-id', b'file2-id'])
669
 
        source.commit('new files')
670
 
        target = self.make_branch_and_tree('target')
671
 
        target.lock_write()
672
 
        self.addCleanup(target.unlock)
673
 
        # We make use of the fact that DirState caches its cutoff time. So we
674
 
        # set the 'safe' time to one minute in the future.
675
 
        state = target.current_dirstate()
676
 
        state._cutoff_time = time.time() + 60
677
 
        build_tree(source.basis_tree(), target)
678
 
        entry1_sha = osutils.sha_file_by_name('source/file1')
679
 
        entry2_sha = osutils.sha_file_by_name('source/dir/file2')
680
 
        # entry[1] is the state information, entry[1][0] is the state of the
681
 
        # working tree, entry[1][0][1] is the sha value for the current working
682
 
        # tree
683
 
        entry1 = state._get_entry(0, path_utf8=b'file1')
684
 
        self.assertEqual(entry1_sha, entry1[1][0][1])
685
 
        # The 'size' field must also be set.
686
 
        self.assertEqual(25, entry1[1][0][2])
687
 
        entry1_state = entry1[1][0]
688
 
        entry2 = state._get_entry(0, path_utf8=b'dir/file2')
689
 
        self.assertEqual(entry2_sha, entry2[1][0][1])
690
 
        self.assertEqual(29, entry2[1][0][2])
691
 
        entry2_state = entry2[1][0]
692
 
        # Now, make sure that we don't have to re-read the content. The
693
 
        # packed_stat should match exactly.
694
 
        self.assertEqual(entry1_sha, target.get_file_sha1('file1'))
695
 
        self.assertEqual(entry2_sha, target.get_file_sha1('dir/file2'))
696
 
        self.assertEqual(entry1_state, entry1[1][0])
697
 
        self.assertEqual(entry2_state, entry2[1][0])
698
 
 
699
 
 
700
294
class TestCommitTransform(tests.TestCaseWithTransport):
701
295
 
702
296
    def get_branch(self):