~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_transform.py

  • Committer: Ian Clatworthy
  • Date: 2010-05-26 04:26:59 UTC
  • mto: (5255.2.1 integration)
  • mto: This revision was merged to the branch mainline in revision 5256.
  • Revision ID: ian.clatworthy@canonical.com-20100526042659-2e3p4qdjr0sby0bt
Fix PDF generation of User Reference

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006, 2007, 2008 Canonical Ltd
 
1
# Copyright (C) 2006-2010 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
19
18
from StringIO import StringIO
20
19
import sys
 
20
import time
21
21
 
22
22
from bzrlib import (
 
23
    bencode,
23
24
    errors,
 
25
    filters,
24
26
    generate_ids,
25
27
    osutils,
26
 
    progress,
27
28
    revision as _mod_revision,
28
 
    symbol_versioning,
 
29
    rules,
29
30
    tests,
30
31
    urlutils,
31
32
    )
32
33
from bzrlib.bzrdir import BzrDir
33
 
from bzrlib.conflicts import (DuplicateEntry, DuplicateID, MissingParent,
34
 
                              UnversionedParent, ParentLoop, DeletingParent,
35
 
                              NonDirectoryParent)
 
34
from bzrlib.conflicts import (
 
35
    DeletingParent,
 
36
    DuplicateEntry,
 
37
    DuplicateID,
 
38
    MissingParent,
 
39
    NonDirectoryParent,
 
40
    ParentLoop,
 
41
    UnversionedParent,
 
42
)
36
43
from bzrlib.diff import show_diff_trees
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
 
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
)
43
59
from bzrlib.merge import Merge3Merger, Merger
44
60
from bzrlib.tests import (
45
61
    HardlinkFeature,
47
63
    TestCase,
48
64
    TestCaseInTempDir,
49
65
    TestSkipped,
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
 
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
)
57
80
 
58
81
 
59
82
class TestTreeTransform(tests.TestCaseWithTransport):
136
159
        transform.finalize()
137
160
        transform.finalize()
138
161
 
 
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
 
139
215
    def test_hardlink(self):
140
216
        self.requireFeature(HardlinkFeature)
141
217
        transform, root = self.get_transform()
369
445
        self.assertContainsRe(transform._limbo_name(first), 'new-1/file')
370
446
        self.assertNotContainsRe(transform._limbo_name(second), 'new-1/FiLe')
371
447
 
 
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
 
372
460
    def test_add_del(self):
373
461
        start, root = self.get_transform()
374
462
        start.new_directory('a', root, 'a')
525
613
        resolve_conflicts(replace)
526
614
        replace.apply()
527
615
 
528
 
    def test_symlinks(self):
 
616
    def _test_symlinks(self, link_name1,link_target1,
 
617
                       link_name2, link_target2):
 
618
 
 
619
        def ozpath(p): return 'oz/' + p
 
620
 
529
621
        self.requireFeature(SymlinkFeature)
530
 
        transform,root = self.get_transform()
 
622
        transform, root = self.get_transform()
531
623
        oz_id = transform.new_directory('oz', root, 'oz-id')
532
 
        wizard = transform.new_symlink('wizard', oz_id, 'wizard-target',
 
624
        wizard = transform.new_symlink(link_name1, oz_id, link_target1,
533
625
                                       'wizard-id')
534
 
        wiz_id = transform.create_path('wizard2', oz_id)
535
 
        transform.create_symlink('behind_curtain', wiz_id)
 
626
        wiz_id = transform.create_path(link_name2, oz_id)
 
627
        transform.create_symlink(link_target2, wiz_id)
536
628
        transform.version_file('wiz-id2', wiz_id)
537
629
        transform.set_executability(True, wiz_id)
538
630
        self.assertEqual(transform.find_conflicts(),
539
631
                         [('non-file executability', wiz_id)])
540
632
        transform.set_executability(None, wiz_id)
541
633
        transform.apply()
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')
 
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')
548
652
 
549
653
    def test_unable_create_symlink(self):
550
654
        def tt_helper():
653
757
                                         ' versioned, but has versioned'
654
758
                                         ' children.  Versioned directory.')
655
759
        self.assertEqual(conflicts_s[6], 'Conflict moving oz/emeraldcity into'
656
 
                                         ' oz/emeraldcity.  Cancelled move.')
 
760
                                         ' oz/emeraldcity. Cancelled move.')
657
761
 
658
762
    def prepare_wrong_parent_kind(self):
659
763
        tt, root = self.get_transform()
730
834
        create.apply()
731
835
        transform, root = self.get_transform()
732
836
        transform.adjust_root_path('oldroot', fun)
733
 
        new_root=transform.trans_id_tree_path('')
 
837
        new_root = transform.trans_id_tree_path('')
734
838
        transform.version_file('new-root', new_root)
735
839
        transform.apply()
736
840
 
748
852
        rename.set_executability(True, myfile)
749
853
        rename.apply()
750
854
 
 
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
        # make the file and directory readonly in the hope this will prevent
 
863
        # renames
 
864
        osutils.make_readonly(self.wt.abspath('first-dir'))
 
865
        osutils.make_readonly(self.wt.abspath('myfile'))
 
866
        # now transform to rename
 
867
        rename_transform, root_id = self.get_transform()
 
868
        file_trans_id = rename_transform.trans_id_file_id('myfile-id')
 
869
        dir_id = rename_transform.trans_id_file_id('first-id')
 
870
        rename_transform.adjust_path('newname', dir_id, file_trans_id)
 
871
        e = self.assertRaises(errors.TransformRenameFailed,
 
872
            rename_transform.apply)
 
873
        # Looks like: 
 
874
        # "Failed to rename .../work/.bzr/checkout/limbo/new-1
 
875
        # to .../first-dir/newname: [Errno 13] Permission denied"
 
876
        # so the first filename is not visible in it; we expect a strerror but
 
877
        # it may vary per OS and language so it's not checked here
 
878
        self.assertContainsRe(str(e),
 
879
            "Failed to rename .*first-dir.newname:")
 
880
 
751
881
    def test_set_executability_order(self):
752
882
        """Ensure that executability behaves the same, no matter what order.
753
883
 
1840
1970
        self.assertEqual([], list(target.iter_changes(revision_tree)))
1841
1971
        self.assertTrue(source.is_executable('file1-id'))
1842
1972
 
 
1973
    def install_rot13_content_filter(self, pattern):
 
1974
        # We could use
 
1975
        # self.addCleanup(filters._reset_registry, filters._reset_registry())
 
1976
        # below, but that looks a bit... hard to read even if it's exactly
 
1977
        # the same thing.
 
1978
        original_registry = filters._reset_registry()
 
1979
        def restore_registry():
 
1980
            filters._reset_registry(original_registry)
 
1981
        self.addCleanup(restore_registry)
 
1982
        def rot13(chunks, context=None):
 
1983
            return [''.join(chunks).encode('rot13')]
 
1984
        rot13filter = filters.ContentFilter(rot13, rot13)
 
1985
        filters.register_filter_stack_map('rot13', {'yes': [rot13filter]}.get)
 
1986
        os.mkdir(self.test_home_dir + '/.bazaar')
 
1987
        rules_filename = self.test_home_dir + '/.bazaar/rules'
 
1988
        f = open(rules_filename, 'wb')
 
1989
        f.write('[name %s]\nrot13=yes\n' % (pattern,))
 
1990
        f.close()
 
1991
        def uninstall_rules():
 
1992
            os.remove(rules_filename)
 
1993
            rules.reset_rules()
 
1994
        self.addCleanup(uninstall_rules)
 
1995
        rules.reset_rules()
 
1996
 
 
1997
    def test_build_tree_content_filtered_files_are_not_hardlinked(self):
 
1998
        """build_tree will not hardlink files that have content filtering rules
 
1999
        applied to them (but will still hardlink other files from the same tree
 
2000
        if it can).
 
2001
        """
 
2002
        self.requireFeature(HardlinkFeature)
 
2003
        self.install_rot13_content_filter('file1')
 
2004
        source = self.create_ab_tree()
 
2005
        target = self.make_branch_and_tree('target')
 
2006
        revision_tree = source.basis_tree()
 
2007
        revision_tree.lock_read()
 
2008
        self.addCleanup(revision_tree.unlock)
 
2009
        build_tree(revision_tree, target, source, hardlink=True)
 
2010
        target.lock_read()
 
2011
        self.addCleanup(target.unlock)
 
2012
        self.assertEqual([], list(target.iter_changes(revision_tree)))
 
2013
        source_stat = os.stat('source/file1')
 
2014
        target_stat = os.stat('target/file1')
 
2015
        self.assertNotEqual(source_stat, target_stat)
 
2016
        source_stat = os.stat('source/file2')
 
2017
        target_stat = os.stat('target/file2')
 
2018
        self.assertEqualStat(source_stat, target_stat)
 
2019
 
1843
2020
    def test_case_insensitive_build_tree_inventory(self):
 
2021
        if (tests.CaseInsensitiveFilesystemFeature.available()
 
2022
            or tests.CaseInsCasePresFilenameFeature.available()):
 
2023
            raise tests.UnavailableFeature('Fully case sensitive filesystem')
1844
2024
        source = self.make_branch_and_tree('source')
1845
2025
        self.build_tree(['source/file', 'source/FILE'])
1846
2026
        source.add(['file', 'FILE'], ['lower-id', 'upper-id'])
1854
2034
        self.assertEqual('FILE', target.id2path('upper-id'))
1855
2035
 
1856
2036
 
 
2037
class TestCommitTransform(tests.TestCaseWithTransport):
 
2038
 
 
2039
    def get_branch(self):
 
2040
        tree = self.make_branch_and_tree('tree')
 
2041
        tree.lock_write()
 
2042
        self.addCleanup(tree.unlock)
 
2043
        tree.commit('empty commit')
 
2044
        return tree.branch
 
2045
 
 
2046
    def get_branch_and_transform(self):
 
2047
        branch = self.get_branch()
 
2048
        tt = TransformPreview(branch.basis_tree())
 
2049
        self.addCleanup(tt.finalize)
 
2050
        return branch, tt
 
2051
 
 
2052
    def test_commit_wrong_basis(self):
 
2053
        branch = self.get_branch()
 
2054
        basis = branch.repository.revision_tree(
 
2055
            _mod_revision.NULL_REVISION)
 
2056
        tt = TransformPreview(basis)
 
2057
        self.addCleanup(tt.finalize)
 
2058
        e = self.assertRaises(ValueError, tt.commit, branch, '')
 
2059
        self.assertEqual('TreeTransform not based on branch basis: null:',
 
2060
                         str(e))
 
2061
 
 
2062
    def test_empy_commit(self):
 
2063
        branch, tt = self.get_branch_and_transform()
 
2064
        rev = tt.commit(branch, 'my message')
 
2065
        self.assertEqual(2, branch.revno())
 
2066
        repo = branch.repository
 
2067
        self.assertEqual('my message', repo.get_revision(rev).message)
 
2068
 
 
2069
    def test_merge_parents(self):
 
2070
        branch, tt = self.get_branch_and_transform()
 
2071
        rev = tt.commit(branch, 'my message', ['rev1b', 'rev1c'])
 
2072
        self.assertEqual(['rev1b', 'rev1c'],
 
2073
                         branch.basis_tree().get_parent_ids()[1:])
 
2074
 
 
2075
    def test_first_commit(self):
 
2076
        branch = self.make_branch('branch')
 
2077
        branch.lock_write()
 
2078
        self.addCleanup(branch.unlock)
 
2079
        tt = TransformPreview(branch.basis_tree())
 
2080
        self.addCleanup(tt.finalize)
 
2081
        tt.new_directory('', ROOT_PARENT, 'TREE_ROOT')
 
2082
        rev = tt.commit(branch, 'my message')
 
2083
        self.assertEqual([], branch.basis_tree().get_parent_ids())
 
2084
        self.assertNotEqual(_mod_revision.NULL_REVISION,
 
2085
                            branch.last_revision())
 
2086
 
 
2087
    def test_first_commit_with_merge_parents(self):
 
2088
        branch = self.make_branch('branch')
 
2089
        branch.lock_write()
 
2090
        self.addCleanup(branch.unlock)
 
2091
        tt = TransformPreview(branch.basis_tree())
 
2092
        self.addCleanup(tt.finalize)
 
2093
        e = self.assertRaises(ValueError, tt.commit, branch,
 
2094
                          'my message', ['rev1b-id'])
 
2095
        self.assertEqual('Cannot supply merge parents for first commit.',
 
2096
                         str(e))
 
2097
        self.assertEqual(_mod_revision.NULL_REVISION, branch.last_revision())
 
2098
 
 
2099
    def test_add_files(self):
 
2100
        branch, tt = self.get_branch_and_transform()
 
2101
        tt.new_file('file', tt.root, 'contents', 'file-id')
 
2102
        trans_id = tt.new_directory('dir', tt.root, 'dir-id')
 
2103
        if SymlinkFeature.available():
 
2104
            tt.new_symlink('symlink', trans_id, 'target', 'symlink-id')
 
2105
        rev = tt.commit(branch, 'message')
 
2106
        tree = branch.basis_tree()
 
2107
        self.assertEqual('file', tree.id2path('file-id'))
 
2108
        self.assertEqual('contents', tree.get_file_text('file-id'))
 
2109
        self.assertEqual('dir', tree.id2path('dir-id'))
 
2110
        if SymlinkFeature.available():
 
2111
            self.assertEqual('dir/symlink', tree.id2path('symlink-id'))
 
2112
            self.assertEqual('target', tree.get_symlink_target('symlink-id'))
 
2113
 
 
2114
    def test_add_unversioned(self):
 
2115
        branch, tt = self.get_branch_and_transform()
 
2116
        tt.new_file('file', tt.root, 'contents')
 
2117
        self.assertRaises(errors.StrictCommitFailed, tt.commit, branch,
 
2118
                          'message', strict=True)
 
2119
 
 
2120
    def test_modify_strict(self):
 
2121
        branch, tt = self.get_branch_and_transform()
 
2122
        tt.new_file('file', tt.root, 'contents', 'file-id')
 
2123
        tt.commit(branch, 'message', strict=True)
 
2124
        tt = TransformPreview(branch.basis_tree())
 
2125
        self.addCleanup(tt.finalize)
 
2126
        trans_id = tt.trans_id_file_id('file-id')
 
2127
        tt.delete_contents(trans_id)
 
2128
        tt.create_file('contents', trans_id)
 
2129
        tt.commit(branch, 'message', strict=True)
 
2130
 
 
2131
    def test_commit_malformed(self):
 
2132
        """Committing a malformed transform should raise an exception.
 
2133
 
 
2134
        In this case, we are adding a file without adding its parent.
 
2135
        """
 
2136
        branch, tt = self.get_branch_and_transform()
 
2137
        parent_id = tt.trans_id_file_id('parent-id')
 
2138
        tt.new_file('file', parent_id, 'contents', 'file-id')
 
2139
        self.assertRaises(errors.MalformedTransform, tt.commit, branch,
 
2140
                          'message')
 
2141
 
 
2142
    def test_commit_rich_revision_data(self):
 
2143
        branch, tt = self.get_branch_and_transform()
 
2144
        rev_id = tt.commit(branch, 'message', timestamp=1, timezone=43201,
 
2145
                           committer='me <me@example.com>',
 
2146
                           revprops={'foo': 'bar'}, revision_id='revid-1',
 
2147
                           authors=['Author1 <author1@example.com>',
 
2148
                              'Author2 <author2@example.com>',
 
2149
                               ])
 
2150
        self.assertEqual('revid-1', rev_id)
 
2151
        revision = branch.repository.get_revision(rev_id)
 
2152
        self.assertEqual(1, revision.timestamp)
 
2153
        self.assertEqual(43201, revision.timezone)
 
2154
        self.assertEqual('me <me@example.com>', revision.committer)
 
2155
        self.assertEqual(['Author1 <author1@example.com>',
 
2156
                          'Author2 <author2@example.com>'],
 
2157
                         revision.get_apparent_authors())
 
2158
        del revision.properties['authors']
 
2159
        self.assertEqual({'foo': 'bar',
 
2160
                          'branch-nick': 'tree'},
 
2161
                         revision.properties)
 
2162
 
 
2163
    def test_no_explicit_revprops(self):
 
2164
        branch, tt = self.get_branch_and_transform()
 
2165
        rev_id = tt.commit(branch, 'message', authors=[
 
2166
            'Author1 <author1@example.com>',
 
2167
            'Author2 <author2@example.com>', ])
 
2168
        revision = branch.repository.get_revision(rev_id)
 
2169
        self.assertEqual(['Author1 <author1@example.com>',
 
2170
                          'Author2 <author2@example.com>'],
 
2171
                         revision.get_apparent_authors())
 
2172
        self.assertEqual('tree', revision.properties['branch-nick'])
 
2173
 
 
2174
 
1857
2175
class MockTransform(object):
1858
2176
 
1859
2177
    def has_named_child(self, by_parent, parent_id, name):
2026
2344
    def create_tree(self):
2027
2345
        tree = self.make_branch_and_tree('.')
2028
2346
        self.build_tree_contents([('a', 'content 1')])
 
2347
        tree.set_root_id('TREE_ROOT')
2029
2348
        tree.add('a', 'a-id')
2030
2349
        tree.commit('rev1', rev_id='rev1')
2031
2350
        return tree.branch.repository.revision_tree('rev1')
2132
2451
    def test_ignore_pb(self):
2133
2452
        # pb could be supported, but TT.iter_changes doesn't support it.
2134
2453
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
2135
 
        preview_tree.iter_changes(revision_tree, pb=progress.DummyProgress())
 
2454
        preview_tree.iter_changes(revision_tree)
2136
2455
 
2137
2456
    def test_kind(self):
2138
2457
        revision_tree = self.create_tree()
2153
2472
        self.assertEqual(os.stat(limbo_path).st_mtime,
2154
2473
                         preview_tree.get_file_mtime('file-id'))
2155
2474
 
 
2475
    def test_get_file_mtime_renamed(self):
 
2476
        work_tree = self.make_branch_and_tree('tree')
 
2477
        self.build_tree(['tree/file'])
 
2478
        work_tree.add('file', 'file-id')
 
2479
        preview = TransformPreview(work_tree)
 
2480
        self.addCleanup(preview.finalize)
 
2481
        file_trans_id = preview.trans_id_tree_file_id('file-id')
 
2482
        preview.adjust_path('renamed', preview.root, file_trans_id)
 
2483
        preview_tree = preview.get_preview_tree()
 
2484
        preview_mtime = preview_tree.get_file_mtime('file-id', 'renamed')
 
2485
        work_mtime = work_tree.get_file_mtime('file-id', 'file')
 
2486
 
2156
2487
    def test_get_file(self):
2157
2488
        preview = self.get_empty_preview()
2158
2489
        preview.new_file('file', preview.root, 'contents', 'file-id')
2306
2637
        self.assertEqual(('missing', None, None, None), summary)
2307
2638
 
2308
2639
    def test_file_content_summary_executable(self):
2309
 
        if not osutils.supports_executable():
2310
 
            raise TestNotApplicable()
2311
2640
        preview = self.get_empty_preview()
2312
2641
        path_id = preview.new_file('path', preview.root, 'contents', 'path-id')
2313
2642
        preview.set_executability(True, path_id)
2322
2651
        self.assertIs(None, summary[3])
2323
2652
 
2324
2653
    def test_change_executability(self):
2325
 
        if not osutils.supports_executable():
2326
 
            raise TestNotApplicable()
2327
2654
        tree = self.make_branch_and_tree('tree')
2328
2655
        self.build_tree(['tree/path'])
2329
2656
        tree.add('path')
2343
2670
        # size must be known
2344
2671
        self.assertEqual(len('contents'), summary[1])
2345
2672
        # not executable
2346
 
        if osutils.supports_executable():
2347
 
            self.assertEqual(False, summary[2])
2348
 
        else:
2349
 
            self.assertEqual(None, summary[2])
 
2673
        self.assertEqual(False, summary[2])
2350
2674
        # will not have hash (not cheap to determine)
2351
2675
        self.assertIs(None, summary[3])
2352
2676
 
2493
2817
 
2494
2818
    def test_walkdirs(self):
2495
2819
        preview = self.get_empty_preview()
2496
 
        preview.version_file('tree-root', preview.root)
 
2820
        root = preview.new_directory('', ROOT_PARENT, 'tree-root')
 
2821
        # FIXME: new_directory should mark root.
 
2822
        preview.fixup_new_roots()
2497
2823
        preview_tree = preview.get_preview_tree()
2498
2824
        file_trans_id = preview.new_file('a', preview.root, 'contents',
2499
2825
                                         'a-id')
2530
2856
        self.addCleanup(work_tree.unlock)
2531
2857
        preview = TransformPreview(work_tree)
2532
2858
        self.addCleanup(preview.finalize)
2533
 
        preview_tree = preview.get_preview_tree()
2534
2859
        file_trans_id = preview.trans_id_file_id('file-id')
2535
2860
        preview.delete_contents(file_trans_id)
2536
2861
        preview.create_file('a\nb\n', file_trans_id)
2537
 
        pb = progress.DummyProgress()
2538
 
        merger = Merger.from_revision_ids(pb, preview_tree,
 
2862
        preview_tree = preview.get_preview_tree()
 
2863
        merger = Merger.from_revision_ids(None, preview_tree,
2539
2864
                                          child_tree.branch.last_revision(),
2540
2865
                                          other_branch=child_tree.branch,
2541
2866
                                          tree_branch=work_tree.branch)
2547
2872
 
2548
2873
    def test_merge_preview_into_workingtree(self):
2549
2874
        tree = self.make_branch_and_tree('tree')
 
2875
        tree.set_root_id('TREE_ROOT')
2550
2876
        tt = TransformPreview(tree)
2551
2877
        self.addCleanup(tt.finalize)
2552
2878
        tt.new_file('name', tt.root, 'content', 'file-id')
2553
2879
        tree2 = self.make_branch_and_tree('tree2')
2554
 
        pb = progress.DummyProgress()
 
2880
        tree2.set_root_id('TREE_ROOT')
2555
2881
        merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
2556
 
                                         pb, tree.basis_tree())
 
2882
                                         None, tree.basis_tree())
2557
2883
        merger.merge_type = Merge3Merger
2558
2884
        merger.do_merge()
2559
2885
 
2569
2895
        tt.create_file('baz', trans_id)
2570
2896
        tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
2571
2897
        self.build_tree_contents([('tree2/foo', 'qux')])
2572
 
        pb = progress.DummyProgress()
 
2898
        pb = None
2573
2899
        merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
2574
2900
                                         pb, tree.basis_tree())
2575
2901
        merger.merge_type = Merge3Merger
2585
2911
                                                           'tree/foo'))
2586
2912
        self.assertEqual(False, preview_tree.is_executable('baz-id'))
2587
2913
 
 
2914
    def test_commit_preview_tree(self):
 
2915
        tree = self.make_branch_and_tree('tree')
 
2916
        rev_id = tree.commit('rev1')
 
2917
        tree.branch.lock_write()
 
2918
        self.addCleanup(tree.branch.unlock)
 
2919
        tt = TransformPreview(tree)
 
2920
        tt.new_file('file', tt.root, 'contents', 'file_id')
 
2921
        self.addCleanup(tt.finalize)
 
2922
        preview = tt.get_preview_tree()
 
2923
        preview.set_parent_ids([rev_id])
 
2924
        builder = tree.branch.get_commit_builder([rev_id])
 
2925
        list(builder.record_iter_changes(preview, rev_id, tt.iter_changes()))
 
2926
        builder.finish_inventory()
 
2927
        rev2_id = builder.commit('rev2')
 
2928
        rev2_tree = tree.branch.repository.revision_tree(rev2_id)
 
2929
        self.assertEqual('contents', rev2_tree.get_file_text('file_id'))
 
2930
 
 
2931
    def test_ascii_limbo_paths(self):
 
2932
        self.requireFeature(tests.UnicodeFilenameFeature)
 
2933
        branch = self.make_branch('any')
 
2934
        tree = branch.repository.revision_tree(_mod_revision.NULL_REVISION)
 
2935
        tt = TransformPreview(tree)
 
2936
        self.addCleanup(tt.finalize)
 
2937
        foo_id = tt.new_directory('', ROOT_PARENT)
 
2938
        bar_id = tt.new_file(u'\u1234bar', foo_id, 'contents')
 
2939
        limbo_path = tt._limbo_name(bar_id)
 
2940
        self.assertEqual(limbo_path.encode('ascii', 'replace'), limbo_path)
 
2941
 
2588
2942
 
2589
2943
class FakeSerializer(object):
2590
2944
    """Serializer implementation that simply returns the input.
2685
3039
        self.assertSerializesTo(self.symlink_creation_records(), tt)
2686
3040
 
2687
3041
    def test_deserialize_symlink_creation(self):
 
3042
        self.requireFeature(tests.SymlinkFeature)
2688
3043
        tt = self.get_preview()
2689
3044
        tt.deserialize(iter(self.symlink_creation_records()))
2690
 
        # XXX readlink should be returning unicode, not utf-8
2691
 
        foo_content = os.readlink(tt._limbo_name('new-1')).decode('utf-8')
 
3045
        abspath = tt._limbo_name('new-1')
 
3046
        foo_content = osutils.readlink(abspath)
2692
3047
        self.assertEqual(u'bar\u1234', foo_content)
2693
3048
 
2694
3049
    def make_destruction_preview(self):