~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_transform.py

  • Committer: John Arbash Meinel
  • Date: 2007-02-08 23:10:37 UTC
  • mto: This revision was merged to the branch mainline in revision 2294.
  • Revision ID: john@arbash-meinel.com-20070208231037-xzuzlh339rmgfhk6
Add a get_cached_utf8, which will ensure it is really utf8, and cache the strings

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
# Copyright (C) 2006 Canonical Ltd
2
 
 
 
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
7
 
 
 
7
#
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
11
# GNU General Public License for more details.
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
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
import os
 
18
import stat
 
19
import sys
18
20
 
 
21
from bzrlib import (
 
22
    errors,
 
23
    generate_ids,
 
24
    tests,
 
25
    urlutils,
 
26
    )
19
27
from bzrlib.bzrdir import BzrDir
20
28
from bzrlib.conflicts import (DuplicateEntry, DuplicateID, MissingParent,
21
 
                              UnversionedParent, ParentLoop)
 
29
                              UnversionedParent, ParentLoop, DeletingParent,)
22
30
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
23
 
                           ReusingTransform, CantMoveRoot, NotVersionedError,
24
 
                           ExistingLimbo, ImmortalLimbo, LockError)
 
31
                           ReusingTransform, CantMoveRoot, 
 
32
                           PathsNotVersionedError, ExistingLimbo,
 
33
                           ImmortalLimbo, LockError)
25
34
from bzrlib.osutils import file_kind, has_symlinks, pathjoin
26
35
from bzrlib.merge import Merge3Merger
27
36
from bzrlib.tests import TestCaseInTempDir, TestSkipped, TestCase
28
37
from bzrlib.transform import (TreeTransform, ROOT_PARENT, FinalPaths, 
29
38
                              resolve_conflicts, cook_conflicts, 
30
39
                              find_interesting, build_tree, get_backup_name)
31
 
import bzrlib.urlutils as urlutils
 
40
 
32
41
 
33
42
class TestTreeTransform(TestCaseInTempDir):
34
43
 
40
49
    def get_transform(self):
41
50
        transform = TreeTransform(self.wt)
42
51
        #self.addCleanup(transform.finalize)
43
 
        return transform, transform.trans_id_tree_file_id(self.wt.get_root_id())
 
52
        return transform, transform.root
44
53
 
45
54
    def test_existing_limbo(self):
46
55
        limbo_name = urlutils.local_path_from_url(
190
199
        transform3.delete_contents(oz_id)
191
200
        self.assertEqual(transform3.find_conflicts(), 
192
201
                         [('missing parent', oz_id)])
193
 
        root_id = transform3.trans_id_tree_file_id('TREE_ROOT')
 
202
        root_id = transform3.root
194
203
        tip_id = transform3.trans_id_tree_file_id('tip-id')
195
204
        transform3.adjust_path('tip', root_id, tip_id)
196
205
        transform3.apply()
222
231
    def test_name_invariants(self):
223
232
        create_tree, root = self.get_transform()
224
233
        # prepare tree
225
 
        root = create_tree.trans_id_tree_file_id('TREE_ROOT')
 
234
        root = create_tree.root
226
235
        create_tree.new_file('name1', root, 'hello1', 'name1')
227
236
        create_tree.new_file('name2', root, 'hello2', 'name2')
228
237
        ddir = create_tree.new_directory('dying_directory', root, 'ddir')
232
241
        create_tree.apply()
233
242
 
234
243
        mangle_tree,root = self.get_transform()
235
 
        root = mangle_tree.trans_id_tree_file_id('TREE_ROOT')
 
244
        root = mangle_tree.root
236
245
        #swap names
237
246
        name1 = mangle_tree.trans_id_tree_file_id('name1')
238
247
        name2 = mangle_tree.trans_id_tree_file_id('name2')
317
326
    def test_move_dangling_ie(self):
318
327
        create_tree, root = self.get_transform()
319
328
        # prepare tree
320
 
        root = create_tree.trans_id_tree_file_id('TREE_ROOT')
 
329
        root = create_tree.root
321
330
        create_tree.new_file('name1', root, 'hello1', 'name1')
322
331
        create_tree.apply()
323
332
        delete_contents, root = self.get_transform()
333
342
    def test_replace_dangling_ie(self):
334
343
        create_tree, root = self.get_transform()
335
344
        # prepare tree
336
 
        root = create_tree.trans_id_tree_file_id('TREE_ROOT')
 
345
        root = create_tree.root
337
346
        create_tree.new_file('name1', root, 'hello1', 'name1')
338
347
        create_tree.apply()
339
348
        delete_contents = TreeTransform(self.wt)
386
395
                                         'dorothy-id')
387
396
        old_dorothy = conflicts.trans_id_tree_file_id('dorothy-id')
388
397
        oz = conflicts.trans_id_tree_file_id('oz-id')
389
 
        # set up missing, unversioned parent
 
398
        # set up DeletedParent parent conflict
390
399
        conflicts.delete_versioned(oz)
391
400
        emerald = conflicts.trans_id_tree_file_id('emerald-id')
 
401
        # set up MissingParent conflict
 
402
        munchkincity = conflicts.trans_id_file_id('munchkincity-id')
 
403
        conflicts.adjust_path('munchkincity', root, munchkincity)
 
404
        conflicts.new_directory('auntem', munchkincity, 'auntem-id')
392
405
        # set up parent loop
393
406
        conflicts.adjust_path('emeraldcity', emerald, emerald)
394
407
        return conflicts, emerald, oz, old_dorothy, new_dorothy
415
428
                                   'dorothy.moved', 'dorothy', None,
416
429
                                   'dorothy-id')
417
430
        self.assertEqual(cooked_conflicts[1], duplicate_id)
418
 
        missing_parent = MissingParent('Not deleting', 'oz', 'oz-id')
 
431
        missing_parent = MissingParent('Created directory', 'munchkincity',
 
432
                                       'munchkincity-id')
 
433
        deleted_parent = DeletingParent('Not deleting', 'oz', 'oz-id')
419
434
        self.assertEqual(cooked_conflicts[2], missing_parent)
420
 
        unversioned_parent = UnversionedParent('Versioned directory', 'oz',
 
435
        unversioned_parent = UnversionedParent('Versioned directory',
 
436
                                               'munchkincity',
 
437
                                               'munchkincity-id')
 
438
        unversioned_parent2 = UnversionedParent('Versioned directory', 'oz',
421
439
                                               'oz-id')
422
440
        self.assertEqual(cooked_conflicts[3], unversioned_parent)
423
441
        parent_loop = ParentLoop('Cancelled move', 'oz/emeraldcity', 
424
442
                                 'oz/emeraldcity', 'emerald-id', 'emerald-id')
425
 
        self.assertEqual(cooked_conflicts[4], parent_loop)
426
 
        self.assertEqual(len(cooked_conflicts), 5)
 
443
        self.assertEqual(cooked_conflicts[4], deleted_parent)
 
444
        self.assertEqual(cooked_conflicts[5], unversioned_parent2)
 
445
        self.assertEqual(cooked_conflicts[6], parent_loop)
 
446
        self.assertEqual(len(cooked_conflicts), 7)
427
447
        tt.finalize()
428
448
 
429
449
    def test_string_conflicts(self):
439
459
        self.assertEqual(conflicts_s[1], 'Conflict adding id to dorothy.  '
440
460
                                         'Unversioned existing file '
441
461
                                         'dorothy.moved.')
442
 
        self.assertEqual(conflicts_s[2], 'Conflict adding files to oz.  '
443
 
                                         'Not deleting.')
444
 
        self.assertEqual(conflicts_s[3], 'Conflict adding versioned files to '
445
 
                                         'oz.  Versioned directory.')
446
 
        self.assertEqual(conflicts_s[4], 'Conflict moving oz/emeraldcity into'
 
462
        self.assertEqual(conflicts_s[2], 'Conflict adding files to'
 
463
                                         ' munchkincity.  Created directory.')
 
464
        self.assertEqual(conflicts_s[3], 'Conflict because munchkincity is not'
 
465
                                         ' versioned, but has versioned'
 
466
                                         ' children.  Versioned directory.')
 
467
        self.assertEqualDiff(conflicts_s[4], "Conflict: can't delete oz because it"
 
468
                                         " is not empty.  Not deleting.")
 
469
        self.assertEqual(conflicts_s[5], 'Conflict because oz is not'
 
470
                                         ' versioned, but has versioned'
 
471
                                         ' children.  Versioned directory.')
 
472
        self.assertEqual(conflicts_s[6], 'Conflict moving oz/emeraldcity into'
447
473
                                         ' oz/emeraldcity.  Cancelled move.')
448
474
 
449
475
    def test_moving_versioned_directories(self):
492
518
        create.apply()
493
519
        self.assertEqual(find_interesting(wt, wt, ['vfile']),
494
520
                         set(['myfile-id']))
495
 
        self.assertRaises(NotVersionedError, find_interesting, wt, wt,
 
521
        self.assertRaises(PathsNotVersionedError, find_interesting, wt, wt,
496
522
                          ['uvfile'])
497
523
 
498
524
    def test_set_executability_order(self):
516
542
        self.assertTrue(wt.is_executable('soc'))
517
543
        self.assertTrue(wt.is_executable('sac'))
518
544
 
 
545
    def test_preserve_mode(self):
 
546
        """File mode is preserved when replacing content"""
 
547
        if sys.platform == 'win32':
 
548
            raise TestSkipped('chmod has no effect on win32')
 
549
        transform, root = self.get_transform()
 
550
        transform.new_file('file1', root, 'contents', 'file1-id', True)
 
551
        transform.apply()
 
552
        self.assertTrue(self.wt.is_executable('file1-id'))
 
553
        transform, root = self.get_transform()
 
554
        file1_id = transform.trans_id_tree_file_id('file1-id')
 
555
        transform.delete_contents(file1_id)
 
556
        transform.create_file('contents2', file1_id)
 
557
        transform.apply()
 
558
        self.assertTrue(self.wt.is_executable('file1-id'))
 
559
 
 
560
    def test__set_mode_stats_correctly(self):
 
561
        """_set_mode stats to determine file mode."""
 
562
        if sys.platform == 'win32':
 
563
            raise TestSkipped('chmod has no effect on win32')
 
564
 
 
565
        stat_paths = []
 
566
        real_stat = os.stat
 
567
        def instrumented_stat(path):
 
568
            stat_paths.append(path)
 
569
            return real_stat(path)
 
570
 
 
571
        transform, root = self.get_transform()
 
572
 
 
573
        bar1_id = transform.new_file('bar', root, 'bar contents 1\n',
 
574
                                     file_id='bar-id-1', executable=False)
 
575
        transform.apply()
 
576
 
 
577
        transform, root = self.get_transform()
 
578
        bar1_id = transform.trans_id_tree_path('bar')
 
579
        bar2_id = transform.trans_id_tree_path('bar2')
 
580
        try:
 
581
            os.stat = instrumented_stat
 
582
            transform.create_file('bar2 contents\n', bar2_id, mode_id=bar1_id)
 
583
        finally:
 
584
            os.stat = real_stat
 
585
            transform.finalize()
 
586
 
 
587
        bar1_abspath = self.wt.abspath('bar')
 
588
        self.assertEqual([bar1_abspath], stat_paths)
 
589
 
519
590
 
520
591
class TransformGroup(object):
521
 
    def __init__(self, dirname):
 
592
    def __init__(self, dirname, root_id):
522
593
        self.name = dirname
523
594
        os.mkdir(dirname)
524
595
        self.wt = BzrDir.create_standalone_workingtree(dirname)
 
596
        self.wt.set_root_id(root_id)
525
597
        self.b = self.wt.branch
526
598
        self.tt = TreeTransform(self.wt)
527
599
        self.root = self.tt.trans_id_tree_file_id(self.wt.get_root_id())
533
605
 
534
606
class TestTransformMerge(TestCaseInTempDir):
535
607
    def test_text_merge(self):
536
 
        base = TransformGroup("base")
 
608
        root_id = generate_ids.gen_root_id()
 
609
        base = TransformGroup("base", root_id)
537
610
        base.tt.new_file('a', base.root, 'a\nb\nc\nd\be\n', 'a')
538
611
        base.tt.new_file('b', base.root, 'b1', 'b')
539
612
        base.tt.new_file('c', base.root, 'c', 'c')
543
616
        base.tt.new_directory('g', base.root, 'g')
544
617
        base.tt.new_directory('h', base.root, 'h')
545
618
        base.tt.apply()
546
 
        other = TransformGroup("other")
 
619
        other = TransformGroup("other", root_id)
547
620
        other.tt.new_file('a', other.root, 'y\nb\nc\nd\be\n', 'a')
548
621
        other.tt.new_file('b', other.root, 'b2', 'b')
549
622
        other.tt.new_file('c', other.root, 'c2', 'c')
554
627
        other.tt.new_file('h', other.root, 'h\ni\nj\nk\n', 'h')
555
628
        other.tt.new_file('i', other.root, 'h\ni\nj\nk\n', 'i')
556
629
        other.tt.apply()
557
 
        this = TransformGroup("this")
 
630
        this = TransformGroup("this", root_id)
558
631
        this.tt.new_file('a', this.root, 'a\nb\nc\nd\bz\n', 'a')
559
632
        this.tt.new_file('b', this.root, 'b', 'b')
560
633
        this.tt.new_file('c', this.root, 'c', 'c')
611
684
    def test_file_merge(self):
612
685
        if not has_symlinks():
613
686
            raise TestSkipped('Symlinks are not supported on this platform')
614
 
        base = TransformGroup("BASE")
615
 
        this = TransformGroup("THIS")
616
 
        other = TransformGroup("OTHER")
 
687
        root_id = generate_ids.gen_root_id()
 
688
        base = TransformGroup("BASE", root_id)
 
689
        this = TransformGroup("THIS", root_id)
 
690
        other = TransformGroup("OTHER", root_id)
617
691
        for tg in this, base, other:
618
692
            tg.tt.new_directory('a', tg.root, 'a')
619
693
            tg.tt.new_symlink('b', tg.root, 'b', 'b')
651
725
        self.assertIs(os.path.lexists(this.wt.abspath('h.OTHER')), True)
652
726
 
653
727
    def test_filename_merge(self):
654
 
        base = TransformGroup("BASE")
655
 
        this = TransformGroup("THIS")
656
 
        other = TransformGroup("OTHER")
 
728
        root_id = generate_ids.gen_root_id()
 
729
        base = TransformGroup("BASE", root_id)
 
730
        this = TransformGroup("THIS", root_id)
 
731
        other = TransformGroup("OTHER", root_id)
657
732
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a') 
658
733
                                   for t in [base, this, other]]
659
734
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b') 
683
758
        self.assertEqual(this.wt.id2path('f'), pathjoin('b/f1'))
684
759
 
685
760
    def test_filename_merge_conflicts(self):
686
 
        base = TransformGroup("BASE")
687
 
        this = TransformGroup("THIS")
688
 
        other = TransformGroup("OTHER")
 
761
        root_id = generate_ids.gen_root_id()
 
762
        base = TransformGroup("BASE", root_id)
 
763
        this = TransformGroup("THIS", root_id)
 
764
        other = TransformGroup("OTHER", root_id)
689
765
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a') 
690
766
                                   for t in [base, this, other]]
691
767
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b') 
712
788
        self.assertIs(os.path.lexists(this.wt.abspath('b/h1.OTHER')), False)
713
789
        self.assertEqual(this.wt.id2path('i'), pathjoin('b/i1.OTHER'))
714
790
 
715
 
class TestBuildTree(TestCaseInTempDir):
 
791
 
 
792
class TestBuildTree(tests.TestCaseWithTransport):
 
793
 
716
794
    def test_build_tree(self):
717
795
        if not has_symlinks():
718
796
            raise TestSkipped('Test requires symlink support')
728
806
        self.assertIs(os.path.isdir('b/foo'), True)
729
807
        self.assertEqual(file('b/foo/bar', 'rb').read(), "contents")
730
808
        self.assertEqual(os.readlink('b/foo/baz'), 'a/foo/bar')
 
809
 
 
810
    def test_file_conflict_handling(self):
 
811
        """Ensure that when building trees, conflict handling is done"""
 
812
        source = self.make_branch_and_tree('source')
 
813
        target = self.make_branch_and_tree('target')
 
814
        self.build_tree(['source/file', 'target/file'])
 
815
        source.add('file', 'new-file')
 
816
        source.commit('added file')
 
817
        build_tree(source.basis_tree(), target)
 
818
        self.assertEqual([DuplicateEntry('Moved existing file to',
 
819
                          'file.moved', 'file', None, 'new-file')],
 
820
                         target.conflicts())
 
821
        target2 = self.make_branch_and_tree('target2')
 
822
        target_file = file('target2/file', 'wb')
 
823
        try:
 
824
            source_file = file('source/file', 'rb')
 
825
            try:
 
826
                target_file.write(source_file.read())
 
827
            finally:
 
828
                source_file.close()
 
829
        finally:
 
830
            target_file.close()
 
831
        build_tree(source.basis_tree(), target2)
 
832
        self.assertEqual([], target2.conflicts())
 
833
 
 
834
    def test_symlink_conflict_handling(self):
 
835
        """Ensure that when building trees, conflict handling is done"""
 
836
        if not has_symlinks():
 
837
            raise TestSkipped('Test requires symlink support')
 
838
        source = self.make_branch_and_tree('source')
 
839
        os.symlink('foo', 'source/symlink')
 
840
        source.add('symlink', 'new-symlink')
 
841
        source.commit('added file')
 
842
        target = self.make_branch_and_tree('target')
 
843
        os.symlink('bar', 'target/symlink')
 
844
        build_tree(source.basis_tree(), target)
 
845
        self.assertEqual([DuplicateEntry('Moved existing file to',
 
846
            'symlink.moved', 'symlink', None, 'new-symlink')],
 
847
            target.conflicts())
 
848
        target = self.make_branch_and_tree('target2')
 
849
        os.symlink('foo', 'target2/symlink')
 
850
        build_tree(source.basis_tree(), target)
 
851
        self.assertEqual([], target.conflicts())
731
852
        
 
853
    def test_directory_conflict_handling(self):
 
854
        """Ensure that when building trees, conflict handling is done"""
 
855
        source = self.make_branch_and_tree('source')
 
856
        target = self.make_branch_and_tree('target')
 
857
        self.build_tree(['source/dir1/', 'source/dir1/file', 'target/dir1/'])
 
858
        source.add(['dir1', 'dir1/file'], ['new-dir1', 'new-file'])
 
859
        source.commit('added file')
 
860
        build_tree(source.basis_tree(), target)
 
861
        self.assertEqual([], target.conflicts())
 
862
        self.failUnlessExists('target/dir1/file')
 
863
 
 
864
        # Ensure contents are merged
 
865
        target = self.make_branch_and_tree('target2')
 
866
        self.build_tree(['target2/dir1/', 'target2/dir1/file2'])
 
867
        build_tree(source.basis_tree(), target)
 
868
        self.assertEqual([], target.conflicts())
 
869
        self.failUnlessExists('target2/dir1/file2')
 
870
        self.failUnlessExists('target2/dir1/file')
 
871
 
 
872
        # Ensure new contents are suppressed for existing branches
 
873
        target = self.make_branch_and_tree('target3')
 
874
        self.make_branch('target3/dir1')
 
875
        self.build_tree(['target3/dir1/file2'])
 
876
        build_tree(source.basis_tree(), target)
 
877
        self.failIfExists('target3/dir1/file')
 
878
        self.failUnlessExists('target3/dir1/file2')
 
879
        self.failUnlessExists('target3/dir1.diverted/file')
 
880
        self.assertEqual([DuplicateEntry('Diverted to',
 
881
            'dir1.diverted', 'dir1', 'new-dir1', None)],
 
882
            target.conflicts())
 
883
 
 
884
        target = self.make_branch_and_tree('target4')
 
885
        self.build_tree(['target4/dir1/'])
 
886
        self.make_branch('target4/dir1/file')
 
887
        build_tree(source.basis_tree(), target)
 
888
        self.failUnlessExists('target4/dir1/file')
 
889
        self.assertEqual('directory', file_kind('target4/dir1/file'))
 
890
        self.failUnlessExists('target4/dir1/file.diverted')
 
891
        self.assertEqual([DuplicateEntry('Diverted to',
 
892
            'dir1/file.diverted', 'dir1/file', 'new-file', None)],
 
893
            target.conflicts())
 
894
 
 
895
    def test_mixed_conflict_handling(self):
 
896
        """Ensure that when building trees, conflict handling is done"""
 
897
        source = self.make_branch_and_tree('source')
 
898
        target = self.make_branch_and_tree('target')
 
899
        self.build_tree(['source/name', 'target/name/'])
 
900
        source.add('name', 'new-name')
 
901
        source.commit('added file')
 
902
        build_tree(source.basis_tree(), target)
 
903
        self.assertEqual([DuplicateEntry('Moved existing file to',
 
904
            'name.moved', 'name', None, 'new-name')], target.conflicts())
 
905
 
 
906
    def test_raises_in_populated(self):
 
907
        source = self.make_branch_and_tree('source')
 
908
        self.build_tree(['source/name'])
 
909
        source.add('name')
 
910
        source.commit('added name')
 
911
        target = self.make_branch_and_tree('target')
 
912
        self.build_tree(['target/name'])
 
913
        target.add('name')
 
914
        self.assertRaises(errors.WorkingTreeAlreadyPopulated, 
 
915
            build_tree, source.basis_tree(), target)
 
916
 
 
917
 
732
918
class MockTransform(object):
733
919
 
734
920
    def has_named_child(self, by_parent, parent_id, name):