1
1
# Copyright (C) 2006 Canonical Ltd
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.
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.
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
19
from bzrlib import tests
19
20
from bzrlib.bzrdir import BzrDir
20
21
from bzrlib.conflicts import (DuplicateEntry, DuplicateID, MissingParent,
21
UnversionedParent, ParentLoop)
22
UnversionedParent, ParentLoop, DeletingParent,)
22
23
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
23
ReusingTransform, CantMoveRoot, NotVersionedError,
24
ExistingLimbo, ImmortalLimbo, LockError)
24
ReusingTransform, CantMoveRoot,
25
PathsNotVersionedError, ExistingLimbo,
26
ImmortalLimbo, LockError)
25
27
from bzrlib.osutils import file_kind, has_symlinks, pathjoin
26
28
from bzrlib.merge import Merge3Merger
27
29
from bzrlib.tests import TestCaseInTempDir, TestSkipped, TestCase
28
30
from bzrlib.transform import (TreeTransform, ROOT_PARENT, FinalPaths,
29
31
resolve_conflicts, cook_conflicts,
30
32
find_interesting, build_tree, get_backup_name)
33
import bzrlib.urlutils as urlutils
32
35
class TestTreeTransform(TestCaseInTempDir):
34
38
super(TestTreeTransform, self).setUp()
35
39
self.wt = BzrDir.create_standalone_workingtree('.')
410
421
'dorothy.moved', 'dorothy', None,
412
423
self.assertEqual(cooked_conflicts[1], duplicate_id)
413
missing_parent = MissingParent('Not deleting', 'oz', 'oz-id')
424
missing_parent = MissingParent('Created directory', 'munchkincity',
426
deleted_parent = DeletingParent('Not deleting', 'oz', 'oz-id')
414
427
self.assertEqual(cooked_conflicts[2], missing_parent)
415
unversioned_parent = UnversionedParent('Versioned directory', 'oz',
428
unversioned_parent = UnversionedParent('Versioned directory',
431
unversioned_parent2 = UnversionedParent('Versioned directory', 'oz',
417
433
self.assertEqual(cooked_conflicts[3], unversioned_parent)
418
434
parent_loop = ParentLoop('Cancelled move', 'oz/emeraldcity',
419
435
'oz/emeraldcity', 'emerald-id', 'emerald-id')
420
self.assertEqual(cooked_conflicts[4], parent_loop)
421
self.assertEqual(len(cooked_conflicts), 5)
436
self.assertEqual(cooked_conflicts[4], deleted_parent)
437
self.assertEqual(cooked_conflicts[5], unversioned_parent2)
438
self.assertEqual(cooked_conflicts[6], parent_loop)
439
self.assertEqual(len(cooked_conflicts), 7)
424
442
def test_string_conflicts(self):
434
452
self.assertEqual(conflicts_s[1], 'Conflict adding id to dorothy. '
435
453
'Unversioned existing file '
436
454
'dorothy.moved.')
437
self.assertEqual(conflicts_s[2], 'Conflict adding files to oz. '
439
self.assertEqual(conflicts_s[3], 'Conflict adding versioned files to '
440
'oz. Versioned directory.')
441
self.assertEqual(conflicts_s[4], 'Conflict moving oz/emeraldcity into'
455
self.assertEqual(conflicts_s[2], 'Conflict adding files to'
456
' munchkincity. Created directory.')
457
self.assertEqual(conflicts_s[3], 'Conflict because munchkincity is not'
458
' versioned, but has versioned'
459
' children. Versioned directory.')
460
self.assertEqualDiff(conflicts_s[4], "Conflict: can't delete oz because it"
461
" is not empty. Not deleting.")
462
self.assertEqual(conflicts_s[5], 'Conflict because oz is not'
463
' versioned, but has versioned'
464
' children. Versioned directory.')
465
self.assertEqual(conflicts_s[6], 'Conflict moving oz/emeraldcity into'
442
466
' oz/emeraldcity. Cancelled move.')
444
468
def test_moving_versioned_directories(self):
488
512
self.assertEqual(find_interesting(wt, wt, ['vfile']),
489
513
set(['myfile-id']))
490
self.assertRaises(NotVersionedError, find_interesting, wt, wt,
514
self.assertRaises(PathsNotVersionedError, find_interesting, wt, wt,
517
def test_set_executability_order(self):
518
"""Ensure that executability behaves the same, no matter what order.
520
- create file and set executability simultaneously
521
- create file and set executability afterward
522
- unsetting the executability of a file whose executability has not been
523
declared should throw an exception (this may happen when a
524
merge attempts to create a file with a duplicate ID)
526
transform, root = self.get_transform()
528
transform.new_file('set_on_creation', root, 'Set on creation', 'soc',
530
sac = transform.new_file('set_after_creation', root, 'Set after creation', 'sac')
531
transform.set_executability(True, sac)
532
uws = transform.new_file('unset_without_set', root, 'Unset badly', 'uws')
533
self.assertRaises(KeyError, transform.set_executability, None, uws)
535
self.assertTrue(wt.is_executable('soc'))
536
self.assertTrue(wt.is_executable('sac'))
494
539
class TransformGroup(object):
495
540
def __init__(self, dirname):
702
748
self.assertIs(os.path.isdir('b/foo'), True)
703
749
self.assertEqual(file('b/foo/bar', 'rb').read(), "contents")
704
750
self.assertEqual(os.readlink('b/foo/baz'), 'a/foo/bar')
752
def test_file_conflict_handling(self):
753
"""Ensure that when building trees, conflict handling is done"""
754
source = self.make_branch_and_tree('source')
755
target = self.make_branch_and_tree('target')
756
self.build_tree(['source/file', 'target/file'])
757
source.add('file', 'new-file')
758
source.commit('added file')
759
build_tree(source.basis_tree(), target)
760
self.assertEqual([DuplicateEntry('Moved existing file to',
761
'file.moved', 'file', None, 'new-file')],
763
target2 = self.make_branch_and_tree('target2')
764
target_file = file('target2/file', 'wb')
766
source_file = file('source/file', 'rb')
768
target_file.write(source_file.read())
773
build_tree(source.basis_tree(), target2)
774
self.assertEqual([], target2.conflicts())
776
def test_symlink_conflict_handling(self):
777
"""Ensure that when building trees, conflict handling is done"""
778
if not has_symlinks():
779
raise TestSkipped('Test requires symlink support')
780
source = self.make_branch_and_tree('source')
781
os.symlink('foo', 'source/symlink')
782
source.add('symlink', 'new-symlink')
783
source.commit('added file')
784
target = self.make_branch_and_tree('target')
785
os.symlink('bar', 'target/symlink')
786
build_tree(source.basis_tree(), target)
787
self.assertEqual([DuplicateEntry('Moved existing file to',
788
'symlink.moved', 'symlink', None, 'new-symlink')],
790
target = self.make_branch_and_tree('target2')
791
os.symlink('foo', 'target2/symlink')
792
build_tree(source.basis_tree(), target)
793
self.assertEqual([], target.conflicts())
795
def test_directory_conflict_handling(self):
796
"""Ensure that when building trees, conflict handling is done"""
797
source = self.make_branch_and_tree('source')
798
target = self.make_branch_and_tree('target')
799
self.build_tree(['source/dir1/', 'source/dir1/file', 'target/dir1/'])
800
source.add(['dir1', 'dir1/file'], ['new-dir1', 'new-file'])
801
source.commit('added file')
802
build_tree(source.basis_tree(), target)
803
self.assertEqual([], target.conflicts())
804
self.failUnlessExists('target/dir1/file')
806
# Ensure contents are merged
807
target = self.make_branch_and_tree('target2')
808
self.build_tree(['target2/dir1/', 'target2/dir1/file2'])
809
build_tree(source.basis_tree(), target)
810
self.assertEqual([], target.conflicts())
811
self.failUnlessExists('target2/dir1/file2')
812
self.failUnlessExists('target2/dir1/file')
814
# Ensure new contents are suppressed for existing branches
815
target = self.make_branch_and_tree('target3')
816
self.make_branch('target3/dir1')
817
self.build_tree(['target3/dir1/file2'])
818
build_tree(source.basis_tree(), target)
819
self.failIfExists('target3/dir1/file')
820
self.failUnlessExists('target3/dir1/file2')
821
self.failUnlessExists('target3/dir1.diverted/file')
822
self.assertEqual([DuplicateEntry('Diverted to',
823
'dir1.diverted', 'dir1', 'new-dir1', None)],
826
target = self.make_branch_and_tree('target4')
827
self.build_tree(['target4/dir1/'])
828
self.make_branch('target4/dir1/file')
829
build_tree(source.basis_tree(), target)
830
self.failUnlessExists('target4/dir1/file')
831
self.assertEqual('directory', file_kind('target4/dir1/file'))
832
self.failUnlessExists('target4/dir1/file.diverted')
833
self.assertEqual([DuplicateEntry('Diverted to',
834
'dir1/file.diverted', 'dir1/file', 'new-file', None)],
837
def test_mixed_conflict_handling(self):
838
"""Ensure that when building trees, conflict handling is done"""
839
source = self.make_branch_and_tree('source')
840
target = self.make_branch_and_tree('target')
841
self.build_tree(['source/name', 'target/name/'])
842
source.add('name', 'new-name')
843
source.commit('added file')
844
build_tree(source.basis_tree(), target)
845
self.assertEqual([DuplicateEntry('Moved existing file to',
846
'name.moved', 'name', None, 'new-name')], target.conflicts())
848
def test_raises_in_populated(self):
849
source = self.make_branch_and_tree('source')
850
self.build_tree(['source/name'])
852
source.commit('added name')
853
target = self.make_branch_and_tree('target')
854
self.build_tree(['target/name'])
856
self.assertRaises(AssertionError, build_tree, source.basis_tree(),
706
860
class MockTransform(object):
708
862
def has_named_child(self, by_parent, parent_id, name):