~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_transform.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2009-04-11 22:45:46 UTC
  • mfrom: (4286.1.1 integration)
  • Revision ID: pqm@pqm.ubuntu.com-20090411224546-82f5xlgs2r51k164
(vila)(trivial) Cleanup test imports and use features to better track
        skipped tests.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2010 Canonical Ltd
 
1
# Copyright (C) 2006, 2007, 2008 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
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
import os
 
18
import stat
18
19
from StringIO import StringIO
19
20
import sys
20
 
import time
21
21
 
22
22
from bzrlib import (
23
 
    bencode,
24
23
    errors,
25
 
    filters,
26
24
    generate_ids,
27
25
    osutils,
 
26
    progress,
28
27
    revision as _mod_revision,
29
 
    rules,
 
28
    symbol_versioning,
30
29
    tests,
31
30
    urlutils,
32
31
    )
33
32
from bzrlib.bzrdir import BzrDir
34
 
from bzrlib.conflicts import (
35
 
    DeletingParent,
36
 
    DuplicateEntry,
37
 
    DuplicateID,
38
 
    MissingParent,
39
 
    NonDirectoryParent,
40
 
    ParentLoop,
41
 
    UnversionedParent,
42
 
)
 
33
from bzrlib.conflicts import (DuplicateEntry, DuplicateID, MissingParent,
 
34
                              UnversionedParent, ParentLoop, DeletingParent,
 
35
                              NonDirectoryParent)
43
36
from bzrlib.diff import show_diff_trees
44
 
from bzrlib.errors import (
45
 
    DuplicateKey,
46
 
    ExistingLimbo,
47
 
    ExistingPendingDeletion,
48
 
    ImmortalLimbo,
49
 
    ImmortalPendingDeletion,
50
 
    LockError,
51
 
    MalformedTransform,
52
 
    NoSuchFile,
53
 
    ReusingTransform,
54
 
)
55
 
from bzrlib.osutils import (
56
 
    file_kind,
57
 
    pathjoin,
58
 
)
 
37
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
 
38
                           ReusingTransform, CantMoveRoot,
 
39
                           PathsNotVersionedError, ExistingLimbo,
 
40
                           ExistingPendingDeletion, ImmortalLimbo,
 
41
                           ImmortalPendingDeletion, LockError)
 
42
from bzrlib.osutils import file_kind, pathjoin
59
43
from bzrlib.merge import Merge3Merger, Merger
60
44
from bzrlib.tests import (
61
45
    HardlinkFeature,
63
47
    TestCase,
64
48
    TestCaseInTempDir,
65
49
    TestSkipped,
66
 
)
67
 
from bzrlib.transform import (
68
 
    build_tree,
69
 
    create_from_tree,
70
 
    cook_conflicts,
71
 
    _FileMover,
72
 
    FinalPaths,
73
 
    get_backup_name,
74
 
    resolve_conflicts,
75
 
    resolve_checkout,
76
 
    ROOT_PARENT,
77
 
    TransformPreview,
78
 
    TreeTransform,
79
 
)
 
50
    )
 
51
from bzrlib.transform import (TreeTransform, ROOT_PARENT, FinalPaths,
 
52
                              resolve_conflicts, cook_conflicts,
 
53
                              build_tree, get_backup_name,
 
54
                              _FileMover, resolve_checkout,
 
55
                              TransformPreview, create_from_tree)
 
56
from bzrlib.util import bencode
80
57
 
81
58
 
82
59
class TestTreeTransform(tests.TestCaseWithTransport):
159
136
        transform.finalize()
160
137
        transform.finalize()
161
138
 
162
 
    def test_create_files_same_timestamp(self):
163
 
        transform, root = self.get_transform()
164
 
        self.wt.lock_tree_write()
165
 
        self.addCleanup(self.wt.unlock)
166
 
        # Roll back the clock, so that we know everything is being set to the
167
 
        # exact time
168
 
        transform._creation_mtime = creation_mtime = time.time() - 20.0
169
 
        transform.create_file('content-one',
170
 
                              transform.create_path('one', root))
171
 
        time.sleep(1) # *ugly*
172
 
        transform.create_file('content-two',
173
 
                              transform.create_path('two', root))
174
 
        transform.apply()
175
 
        fo, st1 = self.wt.get_file_with_stat(None, path='one', filtered=False)
176
 
        fo.close()
177
 
        fo, st2 = self.wt.get_file_with_stat(None, path='two', filtered=False)
178
 
        fo.close()
179
 
        # We only guarantee 2s resolution
180
 
        self.assertTrue(abs(creation_mtime - st1.st_mtime) < 2.0,
181
 
            "%s != %s within 2 seconds" % (creation_mtime, st1.st_mtime))
182
 
        # But if we have more than that, all files should get the same result
183
 
        self.assertEqual(st1.st_mtime, st2.st_mtime)
184
 
 
185
 
    def test_change_root_id(self):
186
 
        transform, root = self.get_transform()
187
 
        self.assertNotEqual('new-root-id', self.wt.get_root_id())
188
 
        transform.new_directory('', ROOT_PARENT, 'new-root-id')
189
 
        transform.delete_contents(root)
190
 
        transform.unversion_file(root)
191
 
        transform.fixup_new_roots()
192
 
        transform.apply()
193
 
        self.assertEqual('new-root-id', self.wt.get_root_id())
194
 
 
195
 
    def test_change_root_id_add_files(self):
196
 
        transform, root = self.get_transform()
197
 
        self.assertNotEqual('new-root-id', self.wt.get_root_id())
198
 
        new_trans_id = transform.new_directory('', ROOT_PARENT, 'new-root-id')
199
 
        transform.new_file('file', new_trans_id, ['new-contents\n'],
200
 
                           'new-file-id')
201
 
        transform.delete_contents(root)
202
 
        transform.unversion_file(root)
203
 
        transform.fixup_new_roots()
204
 
        transform.apply()
205
 
        self.assertEqual('new-root-id', self.wt.get_root_id())
206
 
        self.assertEqual('new-file-id', self.wt.path2id('file'))
207
 
        self.assertFileEqual('new-contents\n', self.wt.abspath('file'))
208
 
 
209
 
    def test_add_two_roots(self):
210
 
        transform, root = self.get_transform()
211
 
        new_trans_id = transform.new_directory('', ROOT_PARENT, 'new-root-id')
212
 
        new_trans_id = transform.new_directory('', ROOT_PARENT, 'alt-root-id')
213
 
        self.assertRaises(ValueError, transform.fixup_new_roots)
214
 
 
215
139
    def test_hardlink(self):
216
140
        self.requireFeature(HardlinkFeature)
217
141
        transform, root = self.get_transform()
445
369
        self.assertContainsRe(transform._limbo_name(first), 'new-1/file')
446
370
        self.assertNotContainsRe(transform._limbo_name(second), 'new-1/FiLe')
447
371
 
448
 
    def test_adjust_path_updates_child_limbo_names(self):
449
 
        tree = self.make_branch_and_tree('tree')
450
 
        transform = TreeTransform(tree)
451
 
        self.addCleanup(transform.finalize)
452
 
        foo_id = transform.new_directory('foo', transform.root)
453
 
        bar_id = transform.new_directory('bar', foo_id)
454
 
        baz_id = transform.new_directory('baz', bar_id)
455
 
        qux_id = transform.new_directory('qux', baz_id)
456
 
        transform.adjust_path('quxx', foo_id, bar_id)
457
 
        self.assertStartsWith(transform._limbo_name(qux_id),
458
 
                              transform._limbo_name(bar_id))
459
 
 
460
372
    def test_add_del(self):
461
373
        start, root = self.get_transform()
462
374
        start.new_directory('a', root, 'a')
613
525
        resolve_conflicts(replace)
614
526
        replace.apply()
615
527
 
616
 
    def _test_symlinks(self, link_name1,link_target1,
617
 
                       link_name2, link_target2):
618
 
 
619
 
        def ozpath(p): return 'oz/' + p
620
 
 
 
528
    def test_symlinks(self):
621
529
        self.requireFeature(SymlinkFeature)
622
 
        transform, root = self.get_transform()
 
530
        transform,root = self.get_transform()
623
531
        oz_id = transform.new_directory('oz', root, 'oz-id')
624
 
        wizard = transform.new_symlink(link_name1, oz_id, link_target1,
 
532
        wizard = transform.new_symlink('wizard', oz_id, 'wizard-target',
625
533
                                       'wizard-id')
626
 
        wiz_id = transform.create_path(link_name2, oz_id)
627
 
        transform.create_symlink(link_target2, wiz_id)
 
534
        wiz_id = transform.create_path('wizard2', oz_id)
 
535
        transform.create_symlink('behind_curtain', wiz_id)
628
536
        transform.version_file('wiz-id2', wiz_id)
629
537
        transform.set_executability(True, wiz_id)
630
538
        self.assertEqual(transform.find_conflicts(),
631
539
                         [('non-file executability', wiz_id)])
632
540
        transform.set_executability(None, wiz_id)
633
541
        transform.apply()
634
 
        self.assertEqual(self.wt.path2id(ozpath(link_name1)), 'wizard-id')
635
 
        self.assertEqual('symlink',
636
 
                         file_kind(self.wt.abspath(ozpath(link_name1))))
637
 
        self.assertEqual(link_target2,
638
 
                         osutils.readlink(self.wt.abspath(ozpath(link_name2))))
639
 
        self.assertEqual(link_target1,
640
 
                         osutils.readlink(self.wt.abspath(ozpath(link_name1))))
641
 
 
642
 
    def test_symlinks(self):
643
 
        self._test_symlinks('wizard', 'wizard-target',
644
 
                            'wizard2', 'behind_curtain')
645
 
 
646
 
    def test_symlinks_unicode(self):
647
 
        self.requireFeature(tests.UnicodeFilenameFeature)
648
 
        self._test_symlinks(u'\N{Euro Sign}wizard',
649
 
                            u'wizard-targ\N{Euro Sign}t',
650
 
                            u'\N{Euro Sign}wizard2',
651
 
                            u'b\N{Euro Sign}hind_curtain')
 
542
        self.assertEqual(self.wt.path2id('oz/wizard'), 'wizard-id')
 
543
        self.assertEqual(file_kind(self.wt.abspath('oz/wizard')), 'symlink')
 
544
        self.assertEqual(os.readlink(self.wt.abspath('oz/wizard2')),
 
545
                         'behind_curtain')
 
546
        self.assertEqual(os.readlink(self.wt.abspath('oz/wizard')),
 
547
                         'wizard-target')
652
548
 
653
549
    def test_unable_create_symlink(self):
654
550
        def tt_helper():
757
653
                                         ' versioned, but has versioned'
758
654
                                         ' children.  Versioned directory.')
759
655
        self.assertEqual(conflicts_s[6], 'Conflict moving oz/emeraldcity into'
760
 
                                         ' oz/emeraldcity. Cancelled move.')
 
656
                                         ' oz/emeraldcity.  Cancelled move.')
761
657
 
762
658
    def prepare_wrong_parent_kind(self):
763
659
        tt, root = self.get_transform()
834
730
        create.apply()
835
731
        transform, root = self.get_transform()
836
732
        transform.adjust_root_path('oldroot', fun)
837
 
        new_root = transform.trans_id_tree_path('')
 
733
        new_root=transform.trans_id_tree_path('')
838
734
        transform.version_file('new-root', new_root)
839
735
        transform.apply()
840
736
 
852
748
        rename.set_executability(True, myfile)
853
749
        rename.apply()
854
750
 
855
 
    def test_rename_fails(self):
856
 
        # see https://bugs.launchpad.net/bzr/+bug/491763
857
 
        create, root_id = self.get_transform()
858
 
        first_dir = create.new_directory('first-dir', root_id, 'first-id')
859
 
        myfile = create.new_file('myfile', root_id, 'myfile-text',
860
 
                                 'myfile-id')
861
 
        create.apply()
862
 
        if os.name == "posix" and sys.platform != "cygwin":
863
 
            # posix filesystems fail on renaming if the readonly bit is set
864
 
            osutils.make_readonly(self.wt.abspath('first-dir'))
865
 
        elif os.name == "nt":
866
 
            # windows filesystems fail on renaming open files
867
 
            self.addCleanup(file(self.wt.abspath('myfile')).close)
868
 
        else:
869
 
            self.skip("Don't know how to force a permissions error on rename")
870
 
        # now transform to rename
871
 
        rename_transform, root_id = self.get_transform()
872
 
        file_trans_id = rename_transform.trans_id_file_id('myfile-id')
873
 
        dir_id = rename_transform.trans_id_file_id('first-id')
874
 
        rename_transform.adjust_path('newname', dir_id, file_trans_id)
875
 
        e = self.assertRaises(errors.TransformRenameFailed,
876
 
            rename_transform.apply)
877
 
        # On nix looks like: 
878
 
        # "Failed to rename .../work/.bzr/checkout/limbo/new-1
879
 
        # to .../first-dir/newname: [Errno 13] Permission denied"
880
 
        # On windows looks like:
881
 
        # "Failed to rename .../work/myfile to 
882
 
        # .../work/.bzr/checkout/limbo/new-1: [Errno 13] Permission denied"
883
 
        # The strerror will vary per OS and language so it's not checked here
884
 
        self.assertContainsRe(str(e),
885
 
            "Failed to rename .*(first-dir.newname:|myfile)")
886
 
 
887
751
    def test_set_executability_order(self):
888
752
        """Ensure that executability behaves the same, no matter what order.
889
753
 
1976
1840
        self.assertEqual([], list(target.iter_changes(revision_tree)))
1977
1841
        self.assertTrue(source.is_executable('file1-id'))
1978
1842
 
1979
 
    def install_rot13_content_filter(self, pattern):
1980
 
        # We could use
1981
 
        # self.addCleanup(filters._reset_registry, filters._reset_registry())
1982
 
        # below, but that looks a bit... hard to read even if it's exactly
1983
 
        # the same thing.
1984
 
        original_registry = filters._reset_registry()
1985
 
        def restore_registry():
1986
 
            filters._reset_registry(original_registry)
1987
 
        self.addCleanup(restore_registry)
1988
 
        def rot13(chunks, context=None):
1989
 
            return [''.join(chunks).encode('rot13')]
1990
 
        rot13filter = filters.ContentFilter(rot13, rot13)
1991
 
        filters.register_filter_stack_map('rot13', {'yes': [rot13filter]}.get)
1992
 
        os.mkdir(self.test_home_dir + '/.bazaar')
1993
 
        rules_filename = self.test_home_dir + '/.bazaar/rules'
1994
 
        f = open(rules_filename, 'wb')
1995
 
        f.write('[name %s]\nrot13=yes\n' % (pattern,))
1996
 
        f.close()
1997
 
        def uninstall_rules():
1998
 
            os.remove(rules_filename)
1999
 
            rules.reset_rules()
2000
 
        self.addCleanup(uninstall_rules)
2001
 
        rules.reset_rules()
2002
 
 
2003
 
    def test_build_tree_content_filtered_files_are_not_hardlinked(self):
2004
 
        """build_tree will not hardlink files that have content filtering rules
2005
 
        applied to them (but will still hardlink other files from the same tree
2006
 
        if it can).
2007
 
        """
2008
 
        self.requireFeature(HardlinkFeature)
2009
 
        self.install_rot13_content_filter('file1')
2010
 
        source = self.create_ab_tree()
2011
 
        target = self.make_branch_and_tree('target')
2012
 
        revision_tree = source.basis_tree()
2013
 
        revision_tree.lock_read()
2014
 
        self.addCleanup(revision_tree.unlock)
2015
 
        build_tree(revision_tree, target, source, hardlink=True)
2016
 
        target.lock_read()
2017
 
        self.addCleanup(target.unlock)
2018
 
        self.assertEqual([], list(target.iter_changes(revision_tree)))
2019
 
        source_stat = os.stat('source/file1')
2020
 
        target_stat = os.stat('target/file1')
2021
 
        self.assertNotEqual(source_stat, target_stat)
2022
 
        source_stat = os.stat('source/file2')
2023
 
        target_stat = os.stat('target/file2')
2024
 
        self.assertEqualStat(source_stat, target_stat)
2025
 
 
2026
1843
    def test_case_insensitive_build_tree_inventory(self):
2027
 
        if (tests.CaseInsensitiveFilesystemFeature.available()
2028
 
            or tests.CaseInsCasePresFilenameFeature.available()):
 
1844
        if (not tests.CaseInsensitiveFilesystemFeature.available()
 
1845
            or not tests.CaseInsCasePresFilenameFeature.available()):
2029
1846
            raise tests.UnavailableFeature('Fully case sensitive filesystem')
2030
1847
        source = self.make_branch_and_tree('source')
2031
1848
        self.build_tree(['source/file', 'source/FILE'])
2040
1857
        self.assertEqual('FILE', target.id2path('upper-id'))
2041
1858
 
2042
1859
 
2043
 
class TestCommitTransform(tests.TestCaseWithTransport):
2044
 
 
2045
 
    def get_branch(self):
2046
 
        tree = self.make_branch_and_tree('tree')
2047
 
        tree.lock_write()
2048
 
        self.addCleanup(tree.unlock)
2049
 
        tree.commit('empty commit')
2050
 
        return tree.branch
2051
 
 
2052
 
    def get_branch_and_transform(self):
2053
 
        branch = self.get_branch()
2054
 
        tt = TransformPreview(branch.basis_tree())
2055
 
        self.addCleanup(tt.finalize)
2056
 
        return branch, tt
2057
 
 
2058
 
    def test_commit_wrong_basis(self):
2059
 
        branch = self.get_branch()
2060
 
        basis = branch.repository.revision_tree(
2061
 
            _mod_revision.NULL_REVISION)
2062
 
        tt = TransformPreview(basis)
2063
 
        self.addCleanup(tt.finalize)
2064
 
        e = self.assertRaises(ValueError, tt.commit, branch, '')
2065
 
        self.assertEqual('TreeTransform not based on branch basis: null:',
2066
 
                         str(e))
2067
 
 
2068
 
    def test_empy_commit(self):
2069
 
        branch, tt = self.get_branch_and_transform()
2070
 
        rev = tt.commit(branch, 'my message')
2071
 
        self.assertEqual(2, branch.revno())
2072
 
        repo = branch.repository
2073
 
        self.assertEqual('my message', repo.get_revision(rev).message)
2074
 
 
2075
 
    def test_merge_parents(self):
2076
 
        branch, tt = self.get_branch_and_transform()
2077
 
        rev = tt.commit(branch, 'my message', ['rev1b', 'rev1c'])
2078
 
        self.assertEqual(['rev1b', 'rev1c'],
2079
 
                         branch.basis_tree().get_parent_ids()[1:])
2080
 
 
2081
 
    def test_first_commit(self):
2082
 
        branch = self.make_branch('branch')
2083
 
        branch.lock_write()
2084
 
        self.addCleanup(branch.unlock)
2085
 
        tt = TransformPreview(branch.basis_tree())
2086
 
        self.addCleanup(tt.finalize)
2087
 
        tt.new_directory('', ROOT_PARENT, 'TREE_ROOT')
2088
 
        rev = tt.commit(branch, 'my message')
2089
 
        self.assertEqual([], branch.basis_tree().get_parent_ids())
2090
 
        self.assertNotEqual(_mod_revision.NULL_REVISION,
2091
 
                            branch.last_revision())
2092
 
 
2093
 
    def test_first_commit_with_merge_parents(self):
2094
 
        branch = self.make_branch('branch')
2095
 
        branch.lock_write()
2096
 
        self.addCleanup(branch.unlock)
2097
 
        tt = TransformPreview(branch.basis_tree())
2098
 
        self.addCleanup(tt.finalize)
2099
 
        e = self.assertRaises(ValueError, tt.commit, branch,
2100
 
                          'my message', ['rev1b-id'])
2101
 
        self.assertEqual('Cannot supply merge parents for first commit.',
2102
 
                         str(e))
2103
 
        self.assertEqual(_mod_revision.NULL_REVISION, branch.last_revision())
2104
 
 
2105
 
    def test_add_files(self):
2106
 
        branch, tt = self.get_branch_and_transform()
2107
 
        tt.new_file('file', tt.root, 'contents', 'file-id')
2108
 
        trans_id = tt.new_directory('dir', tt.root, 'dir-id')
2109
 
        if SymlinkFeature.available():
2110
 
            tt.new_symlink('symlink', trans_id, 'target', 'symlink-id')
2111
 
        rev = tt.commit(branch, 'message')
2112
 
        tree = branch.basis_tree()
2113
 
        self.assertEqual('file', tree.id2path('file-id'))
2114
 
        self.assertEqual('contents', tree.get_file_text('file-id'))
2115
 
        self.assertEqual('dir', tree.id2path('dir-id'))
2116
 
        if SymlinkFeature.available():
2117
 
            self.assertEqual('dir/symlink', tree.id2path('symlink-id'))
2118
 
            self.assertEqual('target', tree.get_symlink_target('symlink-id'))
2119
 
 
2120
 
    def test_add_unversioned(self):
2121
 
        branch, tt = self.get_branch_and_transform()
2122
 
        tt.new_file('file', tt.root, 'contents')
2123
 
        self.assertRaises(errors.StrictCommitFailed, tt.commit, branch,
2124
 
                          'message', strict=True)
2125
 
 
2126
 
    def test_modify_strict(self):
2127
 
        branch, tt = self.get_branch_and_transform()
2128
 
        tt.new_file('file', tt.root, 'contents', 'file-id')
2129
 
        tt.commit(branch, 'message', strict=True)
2130
 
        tt = TransformPreview(branch.basis_tree())
2131
 
        self.addCleanup(tt.finalize)
2132
 
        trans_id = tt.trans_id_file_id('file-id')
2133
 
        tt.delete_contents(trans_id)
2134
 
        tt.create_file('contents', trans_id)
2135
 
        tt.commit(branch, 'message', strict=True)
2136
 
 
2137
 
    def test_commit_malformed(self):
2138
 
        """Committing a malformed transform should raise an exception.
2139
 
 
2140
 
        In this case, we are adding a file without adding its parent.
2141
 
        """
2142
 
        branch, tt = self.get_branch_and_transform()
2143
 
        parent_id = tt.trans_id_file_id('parent-id')
2144
 
        tt.new_file('file', parent_id, 'contents', 'file-id')
2145
 
        self.assertRaises(errors.MalformedTransform, tt.commit, branch,
2146
 
                          'message')
2147
 
 
2148
 
    def test_commit_rich_revision_data(self):
2149
 
        branch, tt = self.get_branch_and_transform()
2150
 
        rev_id = tt.commit(branch, 'message', timestamp=1, timezone=43201,
2151
 
                           committer='me <me@example.com>',
2152
 
                           revprops={'foo': 'bar'}, revision_id='revid-1',
2153
 
                           authors=['Author1 <author1@example.com>',
2154
 
                              'Author2 <author2@example.com>',
2155
 
                               ])
2156
 
        self.assertEqual('revid-1', rev_id)
2157
 
        revision = branch.repository.get_revision(rev_id)
2158
 
        self.assertEqual(1, revision.timestamp)
2159
 
        self.assertEqual(43201, revision.timezone)
2160
 
        self.assertEqual('me <me@example.com>', revision.committer)
2161
 
        self.assertEqual(['Author1 <author1@example.com>',
2162
 
                          'Author2 <author2@example.com>'],
2163
 
                         revision.get_apparent_authors())
2164
 
        del revision.properties['authors']
2165
 
        self.assertEqual({'foo': 'bar',
2166
 
                          'branch-nick': 'tree'},
2167
 
                         revision.properties)
2168
 
 
2169
 
    def test_no_explicit_revprops(self):
2170
 
        branch, tt = self.get_branch_and_transform()
2171
 
        rev_id = tt.commit(branch, 'message', authors=[
2172
 
            'Author1 <author1@example.com>',
2173
 
            'Author2 <author2@example.com>', ])
2174
 
        revision = branch.repository.get_revision(rev_id)
2175
 
        self.assertEqual(['Author1 <author1@example.com>',
2176
 
                          'Author2 <author2@example.com>'],
2177
 
                         revision.get_apparent_authors())
2178
 
        self.assertEqual('tree', revision.properties['branch-nick'])
2179
 
 
2180
 
 
2181
1860
class MockTransform(object):
2182
1861
 
2183
1862
    def has_named_child(self, by_parent, parent_id, name):
2350
2029
    def create_tree(self):
2351
2030
        tree = self.make_branch_and_tree('.')
2352
2031
        self.build_tree_contents([('a', 'content 1')])
2353
 
        tree.set_root_id('TREE_ROOT')
2354
2032
        tree.add('a', 'a-id')
2355
2033
        tree.commit('rev1', rev_id='rev1')
2356
2034
        return tree.branch.repository.revision_tree('rev1')
2457
2135
    def test_ignore_pb(self):
2458
2136
        # pb could be supported, but TT.iter_changes doesn't support it.
2459
2137
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
2460
 
        preview_tree.iter_changes(revision_tree)
 
2138
        preview_tree.iter_changes(revision_tree, pb=progress.DummyProgress())
2461
2139
 
2462
2140
    def test_kind(self):
2463
2141
        revision_tree = self.create_tree()
2478
2156
        self.assertEqual(os.stat(limbo_path).st_mtime,
2479
2157
                         preview_tree.get_file_mtime('file-id'))
2480
2158
 
2481
 
    def test_get_file_mtime_renamed(self):
2482
 
        work_tree = self.make_branch_and_tree('tree')
2483
 
        self.build_tree(['tree/file'])
2484
 
        work_tree.add('file', 'file-id')
2485
 
        preview = TransformPreview(work_tree)
2486
 
        self.addCleanup(preview.finalize)
2487
 
        file_trans_id = preview.trans_id_tree_file_id('file-id')
2488
 
        preview.adjust_path('renamed', preview.root, file_trans_id)
2489
 
        preview_tree = preview.get_preview_tree()
2490
 
        preview_mtime = preview_tree.get_file_mtime('file-id', 'renamed')
2491
 
        work_mtime = work_tree.get_file_mtime('file-id', 'file')
2492
 
 
2493
2159
    def test_get_file(self):
2494
2160
        preview = self.get_empty_preview()
2495
2161
        preview.new_file('file', preview.root, 'contents', 'file-id')
2643
2309
        self.assertEqual(('missing', None, None, None), summary)
2644
2310
 
2645
2311
    def test_file_content_summary_executable(self):
 
2312
        if not osutils.supports_executable():
 
2313
            raise TestNotApplicable()
2646
2314
        preview = self.get_empty_preview()
2647
2315
        path_id = preview.new_file('path', preview.root, 'contents', 'path-id')
2648
2316
        preview.set_executability(True, path_id)
2657
2325
        self.assertIs(None, summary[3])
2658
2326
 
2659
2327
    def test_change_executability(self):
 
2328
        if not osutils.supports_executable():
 
2329
            raise TestNotApplicable()
2660
2330
        tree = self.make_branch_and_tree('tree')
2661
2331
        self.build_tree(['tree/path'])
2662
2332
        tree.add('path')
2676
2346
        # size must be known
2677
2347
        self.assertEqual(len('contents'), summary[1])
2678
2348
        # not executable
2679
 
        self.assertEqual(False, summary[2])
 
2349
        if osutils.supports_executable():
 
2350
            self.assertEqual(False, summary[2])
 
2351
        else:
 
2352
            self.assertEqual(None, summary[2])
2680
2353
        # will not have hash (not cheap to determine)
2681
2354
        self.assertIs(None, summary[3])
2682
2355
 
2823
2496
 
2824
2497
    def test_walkdirs(self):
2825
2498
        preview = self.get_empty_preview()
2826
 
        root = preview.new_directory('', ROOT_PARENT, 'tree-root')
2827
 
        # FIXME: new_directory should mark root.
2828
 
        preview.fixup_new_roots()
 
2499
        preview.version_file('tree-root', preview.root)
2829
2500
        preview_tree = preview.get_preview_tree()
2830
2501
        file_trans_id = preview.new_file('a', preview.root, 'contents',
2831
2502
                                         'a-id')
2862
2533
        self.addCleanup(work_tree.unlock)
2863
2534
        preview = TransformPreview(work_tree)
2864
2535
        self.addCleanup(preview.finalize)
 
2536
        preview_tree = preview.get_preview_tree()
2865
2537
        file_trans_id = preview.trans_id_file_id('file-id')
2866
2538
        preview.delete_contents(file_trans_id)
2867
2539
        preview.create_file('a\nb\n', file_trans_id)
2868
 
        preview_tree = preview.get_preview_tree()
2869
 
        merger = Merger.from_revision_ids(None, preview_tree,
 
2540
        pb = progress.DummyProgress()
 
2541
        merger = Merger.from_revision_ids(pb, preview_tree,
2870
2542
                                          child_tree.branch.last_revision(),
2871
2543
                                          other_branch=child_tree.branch,
2872
2544
                                          tree_branch=work_tree.branch)
2878
2550
 
2879
2551
    def test_merge_preview_into_workingtree(self):
2880
2552
        tree = self.make_branch_and_tree('tree')
2881
 
        tree.set_root_id('TREE_ROOT')
2882
2553
        tt = TransformPreview(tree)
2883
2554
        self.addCleanup(tt.finalize)
2884
2555
        tt.new_file('name', tt.root, 'content', 'file-id')
2885
2556
        tree2 = self.make_branch_and_tree('tree2')
2886
 
        tree2.set_root_id('TREE_ROOT')
 
2557
        pb = progress.DummyProgress()
2887
2558
        merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
2888
 
                                         None, tree.basis_tree())
 
2559
                                         pb, tree.basis_tree())
2889
2560
        merger.merge_type = Merge3Merger
2890
2561
        merger.do_merge()
2891
2562
 
2901
2572
        tt.create_file('baz', trans_id)
2902
2573
        tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
2903
2574
        self.build_tree_contents([('tree2/foo', 'qux')])
2904
 
        pb = None
 
2575
        pb = progress.DummyProgress()
2905
2576
        merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
2906
2577
                                         pb, tree.basis_tree())
2907
2578
        merger.merge_type = Merge3Merger
2917
2588
                                                           'tree/foo'))
2918
2589
        self.assertEqual(False, preview_tree.is_executable('baz-id'))
2919
2590
 
2920
 
    def test_commit_preview_tree(self):
2921
 
        tree = self.make_branch_and_tree('tree')
2922
 
        rev_id = tree.commit('rev1')
2923
 
        tree.branch.lock_write()
2924
 
        self.addCleanup(tree.branch.unlock)
2925
 
        tt = TransformPreview(tree)
2926
 
        tt.new_file('file', tt.root, 'contents', 'file_id')
2927
 
        self.addCleanup(tt.finalize)
2928
 
        preview = tt.get_preview_tree()
2929
 
        preview.set_parent_ids([rev_id])
2930
 
        builder = tree.branch.get_commit_builder([rev_id])
2931
 
        list(builder.record_iter_changes(preview, rev_id, tt.iter_changes()))
2932
 
        builder.finish_inventory()
2933
 
        rev2_id = builder.commit('rev2')
2934
 
        rev2_tree = tree.branch.repository.revision_tree(rev2_id)
2935
 
        self.assertEqual('contents', rev2_tree.get_file_text('file_id'))
2936
 
 
2937
 
    def test_ascii_limbo_paths(self):
2938
 
        self.requireFeature(tests.UnicodeFilenameFeature)
2939
 
        branch = self.make_branch('any')
2940
 
        tree = branch.repository.revision_tree(_mod_revision.NULL_REVISION)
2941
 
        tt = TransformPreview(tree)
2942
 
        self.addCleanup(tt.finalize)
2943
 
        foo_id = tt.new_directory('', ROOT_PARENT)
2944
 
        bar_id = tt.new_file(u'\u1234bar', foo_id, 'contents')
2945
 
        limbo_path = tt._limbo_name(bar_id)
2946
 
        self.assertEqual(limbo_path.encode('ascii', 'replace'), limbo_path)
2947
 
 
2948
2591
 
2949
2592
class FakeSerializer(object):
2950
2593
    """Serializer implementation that simply returns the input.
3045
2688
        self.assertSerializesTo(self.symlink_creation_records(), tt)
3046
2689
 
3047
2690
    def test_deserialize_symlink_creation(self):
3048
 
        self.requireFeature(tests.SymlinkFeature)
3049
2691
        tt = self.get_preview()
3050
2692
        tt.deserialize(iter(self.symlink_creation_records()))
3051
 
        abspath = tt._limbo_name('new-1')
3052
 
        foo_content = osutils.readlink(abspath)
 
2693
        # XXX readlink should be returning unicode, not utf-8
 
2694
        foo_content = os.readlink(tt._limbo_name('new-1')).decode('utf-8')
3053
2695
        self.assertEqual(u'bar\u1234', foo_content)
3054
2696
 
3055
2697
    def make_destruction_preview(self):