~bzr-pqm/bzr/bzr.dev

3566.1.2 by Aaron Bentley
Fix copyright dates
1
# Copyright (C) 2006, 2007, 2008 Canonical Ltd
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
2
#
1534.7.106 by Aaron Bentley
Cleaned up imports, added copyright statements
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
7
#
1534.7.106 by Aaron Bentley
Cleaned up imports, added copyright statements
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
12
#
1534.7.106 by Aaron Bentley
Cleaned up imports, added copyright statements
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1534.7.106 by Aaron Bentley
Cleaned up imports, added copyright statements
16
17
import os
2027.1.1 by John Arbash Meinel
Fix bug #56549, and write a direct test that the right path is being statted
18
import stat
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
19
from StringIO import StringIO
2027.1.1 by John Arbash Meinel
Fix bug #56549, and write a direct test that the right path is being statted
20
import sys
1558.1.3 by Aaron Bentley
Fixed deprecated op use in test suite
21
2027.1.1 by John Arbash Meinel
Fix bug #56549, and write a direct test that the right path is being statted
22
from bzrlib import (
2694.5.4 by Jelmer Vernooij
Move bzrlib.util.bencode to bzrlib._bencode_py.
23
    bencode,
2090.2.1 by Martin Pool
Fix some code which relies on assertions and breaks under python -O
24
    errors,
2116.4.1 by John Arbash Meinel
Update file and revision id generators.
25
    generate_ids,
3199.1.4 by Vincent Ladeuil
Fix 16 leaked tmp dirs. Probably indicates a lock handling problem with TransformPreview
26
    osutils,
3008.1.17 by Aaron Bentley
Test unused parameters of preview_tree._iter_changes
27
    progress,
3008.1.18 by Aaron Bentley
Get supported PreviewTree functionality under test
28
    revision as _mod_revision,
2255.7.48 by Robert Collins
Deprecated and make work with DirState trees 'transform.find_interesting'.
29
    symbol_versioning,
2027.1.1 by John Arbash Meinel
Fix bug #56549, and write a direct test that the right path is being statted
30
    tests,
31
    urlutils,
32
    )
1558.1.3 by Aaron Bentley
Fixed deprecated op use in test suite
33
from bzrlib.bzrdir import BzrDir
1534.10.24 by Aaron Bentley
Eliminated conflicts_to_strings, made remove_files a ConflictList member
34
from bzrlib.conflicts import (DuplicateEntry, DuplicateID, MissingParent,
3144.4.2 by Aaron Bentley
Handle non-directory parent conflicts (abentley, #177390)
35
                              UnversionedParent, ParentLoop, DeletingParent,
36
                              NonDirectoryParent)
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
37
from bzrlib.diff import show_diff_trees
1534.7.32 by Aaron Bentley
Got conflict handling working when conflicts involve existing files
38
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
39
                           ReusingTransform, CantMoveRoot,
1551.7.17 by Aaron Bentley
Switch to PathsNotVersioned, accept extra_trees
40
                           PathsNotVersionedError, ExistingLimbo,
2733.2.11 by Aaron Bentley
Detect irregularities with the pending-deletion directory
41
                           ExistingPendingDeletion, ImmortalLimbo,
42
                           ImmortalPendingDeletion, LockError)
2949.5.1 by Alexander Belchenko
selftest: use SymlinkFeature instead of TestSkipped where appropriate
43
from bzrlib.osutils import file_kind, pathjoin
3363.17.1 by Aaron Bentley
Avoid inventory for merge and transform code
44
from bzrlib.merge import Merge3Merger, Merger
2949.5.1 by Alexander Belchenko
selftest: use SymlinkFeature instead of TestSkipped where appropriate
45
from bzrlib.tests import (
3136.1.1 by Aaron Bentley
Add support for hardlinks to TreeTransform
46
    HardlinkFeature,
2949.5.1 by Alexander Belchenko
selftest: use SymlinkFeature instead of TestSkipped where appropriate
47
    SymlinkFeature,
48
    TestCase,
49
    TestCaseInTempDir,
50
    TestSkipped,
51
    )
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
52
from bzrlib.transform import (TreeTransform, ROOT_PARENT, FinalPaths,
53
                              resolve_conflicts, cook_conflicts,
3400.3.6 by Martin Pool
Remove code deprecated prior to 1.1 and its tests
54
                              build_tree, get_backup_name,
55
                              _FileMover, resolve_checkout,
3363.17.24 by Aaron Bentley
Implement create_by_tree
56
                              TransformPreview, create_from_tree)
0.13.13 by Aaron Bentley
Add direct test of serialization records
57
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
58
2100.3.21 by Aaron Bentley
Work on checking out by-reference trees
59
class TestTreeTransform(tests.TestCaseWithTransport):
1740.2.4 by Aaron Bentley
Update transform tests and docs
60
1534.7.59 by Aaron Bentley
Simplified tests
61
    def setUp(self):
62
        super(TestTreeTransform, self).setUp()
2255.2.194 by Robert Collins
[BROKEN] Many updates to stop using experimental formats in tests.
63
        self.wt = self.make_branch_and_tree('.', format='dirstate-with-subtree')
1534.7.161 by Aaron Bentley
Used appropriate control_files
64
        os.chdir('..')
1534.7.59 by Aaron Bentley
Simplified tests
65
66
    def get_transform(self):
67
        transform = TreeTransform(self.wt)
3453.2.7 by Aaron Bentley
Remove test kipple
68
        self.addCleanup(transform.finalize)
1731.1.33 by Aaron Bentley
Revert no-special-root changes
69
        return transform, transform.root
1534.7.59 by Aaron Bentley
Simplified tests
70
1534.7.162 by Aaron Bentley
Handle failures creating/deleting the Limbo directory
71
    def test_existing_limbo(self):
72
        transform, root = self.get_transform()
2733.2.6 by Aaron Bentley
Make TreeTransform commits rollbackable
73
        limbo_name = transform._limbodir
74
        deletion_path = transform._deletiondir
1534.7.176 by abentley
Fixed up tests for Windows
75
        os.mkdir(pathjoin(limbo_name, 'hehe'))
1534.7.162 by Aaron Bentley
Handle failures creating/deleting the Limbo directory
76
        self.assertRaises(ImmortalLimbo, transform.apply)
77
        self.assertRaises(LockError, self.wt.unlock)
78
        self.assertRaises(ExistingLimbo, self.get_transform)
79
        self.assertRaises(LockError, self.wt.unlock)
1534.7.176 by abentley
Fixed up tests for Windows
80
        os.rmdir(pathjoin(limbo_name, 'hehe'))
1534.7.162 by Aaron Bentley
Handle failures creating/deleting the Limbo directory
81
        os.rmdir(limbo_name)
2733.2.6 by Aaron Bentley
Make TreeTransform commits rollbackable
82
        os.rmdir(deletion_path)
1534.7.162 by Aaron Bentley
Handle failures creating/deleting the Limbo directory
83
        transform, root = self.get_transform()
84
        transform.apply()
1534.7.59 by Aaron Bentley
Simplified tests
85
2733.2.11 by Aaron Bentley
Detect irregularities with the pending-deletion directory
86
    def test_existing_pending_deletion(self):
87
        transform, root = self.get_transform()
88
        deletion_path = self._limbodir = urlutils.local_path_from_url(
3407.2.8 by Martin Pool
Deprecate LockableFiles.controlfilename
89
            transform._tree._transport.abspath('pending-deletion'))
2733.2.11 by Aaron Bentley
Detect irregularities with the pending-deletion directory
90
        os.mkdir(pathjoin(deletion_path, 'blocking-directory'))
91
        self.assertRaises(ImmortalPendingDeletion, transform.apply)
92
        self.assertRaises(LockError, self.wt.unlock)
93
        self.assertRaises(ExistingPendingDeletion, self.get_transform)
94
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
95
    def test_build(self):
3034.2.1 by Aaron Bentley
Fix is_executable tests for win32
96
        transform, root = self.get_transform()
97
        self.wt.lock_tree_write()
98
        self.addCleanup(self.wt.unlock)
1534.7.59 by Aaron Bentley
Simplified tests
99
        self.assertIs(transform.get_tree_parent(root), ROOT_PARENT)
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
100
        imaginary_id = transform.trans_id_tree_path('imaginary')
1534.10.32 by Aaron Bentley
Test and fix case where name has trailing slash
101
        imaginary_id2 = transform.trans_id_tree_path('imaginary/')
102
        self.assertEqual(imaginary_id, imaginary_id2)
1534.7.59 by Aaron Bentley
Simplified tests
103
        self.assertEqual(transform.get_tree_parent(imaginary_id), root)
104
        self.assertEqual(transform.final_kind(root), 'directory')
105
        self.assertEqual(transform.final_file_id(root), self.wt.get_root_id())
106
        trans_id = transform.create_path('name', root)
107
        self.assertIs(transform.final_file_id(trans_id), None)
108
        self.assertRaises(NoSuchFile, transform.final_kind, trans_id)
109
        transform.create_file('contents', trans_id)
110
        transform.set_executability(True, trans_id)
111
        transform.version_file('my_pretties', trans_id)
112
        self.assertRaises(DuplicateKey, transform.version_file,
113
                          'my_pretties', trans_id)
114
        self.assertEqual(transform.final_file_id(trans_id), 'my_pretties')
115
        self.assertEqual(transform.final_parent(trans_id), root)
116
        self.assertIs(transform.final_parent(root), ROOT_PARENT)
117
        self.assertIs(transform.get_tree_parent(root), ROOT_PARENT)
118
        oz_id = transform.create_path('oz', root)
119
        transform.create_directory(oz_id)
120
        transform.version_file('ozzie', oz_id)
121
        trans_id2 = transform.create_path('name2', root)
122
        transform.create_file('contents', trans_id2)
123
        transform.set_executability(False, trans_id2)
124
        transform.version_file('my_pretties2', trans_id2)
1534.7.191 by Aaron Bentley
Got transform.apply to list modified paths
125
        modified_paths = transform.apply().modified_paths
1534.7.100 by Aaron Bentley
Fixed path-relative test cases
126
        self.assertEqual('contents', self.wt.get_file_byname('name').read())
1534.7.59 by Aaron Bentley
Simplified tests
127
        self.assertEqual(self.wt.path2id('name'), 'my_pretties')
128
        self.assertIs(self.wt.is_executable('my_pretties'), True)
129
        self.assertIs(self.wt.is_executable('my_pretties2'), False)
1534.7.100 by Aaron Bentley
Fixed path-relative test cases
130
        self.assertEqual('directory', file_kind(self.wt.abspath('oz')))
1534.7.191 by Aaron Bentley
Got transform.apply to list modified paths
131
        self.assertEqual(len(modified_paths), 3)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
132
        tree_mod_paths = [self.wt.id2abspath(f) for f in
1534.7.191 by Aaron Bentley
Got transform.apply to list modified paths
133
                          ('ozzie', 'my_pretties', 'my_pretties2')]
134
        self.assertSubset(tree_mod_paths, modified_paths)
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
135
        # is it safe to finalize repeatedly?
136
        transform.finalize()
1534.7.59 by Aaron Bentley
Simplified tests
137
        transform.finalize()
1534.7.2 by Aaron Bentley
Added convenience function
138
3136.1.1 by Aaron Bentley
Add support for hardlinks to TreeTransform
139
    def test_hardlink(self):
140
        self.requireFeature(HardlinkFeature)
141
        transform, root = self.get_transform()
142
        transform.new_file('file1', root, 'contents')
143
        transform.apply()
144
        target = self.make_branch_and_tree('target')
145
        target_transform = TreeTransform(target)
146
        trans_id = target_transform.create_path('file1', target_transform.root)
147
        target_transform.create_hardlink(self.wt.abspath('file1'), trans_id)
148
        target_transform.apply()
149
        self.failUnlessExists('target/file1')
150
        source_stat = os.stat(self.wt.abspath('file1'))
151
        target_stat = os.stat('target/file1')
152
        self.assertEqual(source_stat, target_stat)
153
1534.7.2 by Aaron Bentley
Added convenience function
154
    def test_convenience(self):
1534.7.59 by Aaron Bentley
Simplified tests
155
        transform, root = self.get_transform()
3034.2.1 by Aaron Bentley
Fix is_executable tests for win32
156
        self.wt.lock_tree_write()
157
        self.addCleanup(self.wt.unlock)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
158
        trans_id = transform.new_file('name', root, 'contents',
1534.7.59 by Aaron Bentley
Simplified tests
159
                                      'my_pretties', True)
160
        oz = transform.new_directory('oz', root, 'oz-id')
161
        dorothy = transform.new_directory('dorothy', oz, 'dorothy-id')
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
162
        toto = transform.new_file('toto', dorothy, 'toto-contents',
1534.7.59 by Aaron Bentley
Simplified tests
163
                                  'toto-id', False)
164
165
        self.assertEqual(len(transform.find_conflicts()), 0)
166
        transform.apply()
167
        self.assertRaises(ReusingTransform, transform.find_conflicts)
1534.7.100 by Aaron Bentley
Fixed path-relative test cases
168
        self.assertEqual('contents', file(self.wt.abspath('name')).read())
1534.7.59 by Aaron Bentley
Simplified tests
169
        self.assertEqual(self.wt.path2id('name'), 'my_pretties')
170
        self.assertIs(self.wt.is_executable('my_pretties'), True)
171
        self.assertEqual(self.wt.path2id('oz'), 'oz-id')
172
        self.assertEqual(self.wt.path2id('oz/dorothy'), 'dorothy-id')
173
        self.assertEqual(self.wt.path2id('oz/dorothy/toto'), 'toto-id')
174
2255.2.194 by Robert Collins
[BROKEN] Many updates to stop using experimental formats in tests.
175
        self.assertEqual('toto-contents',
1534.7.100 by Aaron Bentley
Fixed path-relative test cases
176
                         self.wt.get_file_byname('oz/dorothy/toto').read())
1534.7.59 by Aaron Bentley
Simplified tests
177
        self.assertIs(self.wt.is_executable('toto-id'), False)
1534.7.6 by Aaron Bentley
Added conflict handling
178
2100.3.21 by Aaron Bentley
Work on checking out by-reference trees
179
    def test_tree_reference(self):
180
        transform, root = self.get_transform()
181
        tree = transform._tree
182
        trans_id = transform.new_directory('reference', root, 'subtree-id')
183
        transform.set_tree_reference('subtree-revision', trans_id)
184
        transform.apply()
2255.2.194 by Robert Collins
[BROKEN] Many updates to stop using experimental formats in tests.
185
        tree.lock_read()
186
        self.addCleanup(tree.unlock)
187
        self.assertEqual('subtree-revision',
2100.3.21 by Aaron Bentley
Work on checking out by-reference trees
188
                         tree.inventory['subtree-id'].reference_revision)
189
1534.7.6 by Aaron Bentley
Added conflict handling
190
    def test_conflicts(self):
1534.7.59 by Aaron Bentley
Simplified tests
191
        transform, root = self.get_transform()
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
192
        trans_id = transform.new_file('name', root, 'contents',
1534.7.59 by Aaron Bentley
Simplified tests
193
                                      'my_pretties')
194
        self.assertEqual(len(transform.find_conflicts()), 0)
195
        trans_id2 = transform.new_file('name', root, 'Crontents', 'toto')
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
196
        self.assertEqual(transform.find_conflicts(),
1534.7.59 by Aaron Bentley
Simplified tests
197
                         [('duplicate', trans_id, trans_id2, 'name')])
198
        self.assertRaises(MalformedTransform, transform.apply)
199
        transform.adjust_path('name', trans_id, trans_id2)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
200
        self.assertEqual(transform.find_conflicts(),
1534.7.59 by Aaron Bentley
Simplified tests
201
                         [('non-directory parent', trans_id)])
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
202
        tinman_id = transform.trans_id_tree_path('tinman')
1534.7.59 by Aaron Bentley
Simplified tests
203
        transform.adjust_path('name', tinman_id, trans_id2)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
204
        self.assertEqual(transform.find_conflicts(),
205
                         [('unversioned parent', tinman_id),
1534.7.59 by Aaron Bentley
Simplified tests
206
                          ('missing parent', tinman_id)])
207
        lion_id = transform.create_path('lion', root)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
208
        self.assertEqual(transform.find_conflicts(),
209
                         [('unversioned parent', tinman_id),
1534.7.59 by Aaron Bentley
Simplified tests
210
                          ('missing parent', tinman_id)])
211
        transform.adjust_path('name', lion_id, trans_id2)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
212
        self.assertEqual(transform.find_conflicts(),
1534.7.59 by Aaron Bentley
Simplified tests
213
                         [('unversioned parent', lion_id),
214
                          ('missing parent', lion_id)])
215
        transform.version_file("Courage", lion_id)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
216
        self.assertEqual(transform.find_conflicts(),
217
                         [('missing parent', lion_id),
1534.7.59 by Aaron Bentley
Simplified tests
218
                          ('versioning no contents', lion_id)])
219
        transform.adjust_path('name2', root, trans_id2)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
220
        self.assertEqual(transform.find_conflicts(),
1534.7.59 by Aaron Bentley
Simplified tests
221
                         [('versioning no contents', lion_id)])
222
        transform.create_file('Contents, okay?', lion_id)
223
        transform.adjust_path('name2', trans_id2, trans_id2)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
224
        self.assertEqual(transform.find_conflicts(),
225
                         [('parent loop', trans_id2),
1534.7.59 by Aaron Bentley
Simplified tests
226
                          ('non-directory parent', trans_id2)])
227
        transform.adjust_path('name2', root, trans_id2)
228
        oz_id = transform.new_directory('oz', root)
229
        transform.set_executability(True, oz_id)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
230
        self.assertEqual(transform.find_conflicts(),
1534.7.59 by Aaron Bentley
Simplified tests
231
                         [('unversioned executability', oz_id)])
232
        transform.version_file('oz-id', oz_id)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
233
        self.assertEqual(transform.find_conflicts(),
1534.7.59 by Aaron Bentley
Simplified tests
234
                         [('non-file executability', oz_id)])
235
        transform.set_executability(None, oz_id)
1534.7.71 by abentley
All tests pass under Windows
236
        tip_id = transform.new_file('tip', oz_id, 'ozma', 'tip-id')
1534.7.59 by Aaron Bentley
Simplified tests
237
        transform.apply()
238
        self.assertEqual(self.wt.path2id('name'), 'my_pretties')
239
        self.assertEqual('contents', file(self.wt.abspath('name')).read())
240
        transform2, root = self.get_transform()
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
241
        oz_id = transform2.trans_id_tree_file_id('oz-id')
1534.7.59 by Aaron Bentley
Simplified tests
242
        newtip = transform2.new_file('tip', oz_id, 'other', 'tip-id')
243
        result = transform2.find_conflicts()
1534.7.135 by Aaron Bentley
Fixed deletion handling
244
        fp = FinalPaths(transform2)
1534.7.59 by Aaron Bentley
Simplified tests
245
        self.assert_('oz/tip' in transform2._tree_path_ids)
1534.7.176 by abentley
Fixed up tests for Windows
246
        self.assertEqual(fp.get_path(newtip), pathjoin('oz', 'tip'))
1534.7.59 by Aaron Bentley
Simplified tests
247
        self.assertEqual(len(result), 2)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
248
        self.assertEqual((result[0][0], result[0][1]),
1534.7.59 by Aaron Bentley
Simplified tests
249
                         ('duplicate', newtip))
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
250
        self.assertEqual((result[1][0], result[1][2]),
1534.7.59 by Aaron Bentley
Simplified tests
251
                         ('duplicate id', newtip))
1534.7.73 by Aaron Bentley
Changed model again. Now iterator is used immediately.
252
        transform2.finalize()
1534.7.59 by Aaron Bentley
Simplified tests
253
        transform3 = TreeTransform(self.wt)
254
        self.addCleanup(transform3.finalize)
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
255
        oz_id = transform3.trans_id_tree_file_id('oz-id')
1534.7.59 by Aaron Bentley
Simplified tests
256
        transform3.delete_contents(oz_id)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
257
        self.assertEqual(transform3.find_conflicts(),
1534.7.59 by Aaron Bentley
Simplified tests
258
                         [('missing parent', oz_id)])
1731.1.33 by Aaron Bentley
Revert no-special-root changes
259
        root_id = transform3.root
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
260
        tip_id = transform3.trans_id_tree_file_id('tip-id')
1534.7.59 by Aaron Bentley
Simplified tests
261
        transform3.adjust_path('tip', root_id, tip_id)
262
        transform3.apply()
1534.7.36 by Aaron Bentley
Added rename tests
263
3034.4.1 by Aaron Bentley
Start handling case-insensitivity
264
    def test_conflict_on_case_insensitive(self):
265
        tree = self.make_branch_and_tree('tree')
266
        # Don't try this at home, kids!
267
        # Force the tree to report that it is case sensitive, for conflict
268
        # resolution tests
269
        tree.case_sensitive = True
270
        transform = TreeTransform(tree)
271
        self.addCleanup(transform.finalize)
272
        transform.new_file('file', transform.root, 'content')
273
        transform.new_file('FiLe', transform.root, 'content')
274
        result = transform.find_conflicts()
275
        self.assertEqual([], result)
3008.1.15 by Aaron Bentley
Make case_sensitive an aspect of the transform, not the source tree
276
        transform.finalize()
3034.4.1 by Aaron Bentley
Start handling case-insensitivity
277
        # Force the tree to report that it is case insensitive, for conflict
278
        # generation tests
279
        tree.case_sensitive = False
3008.1.15 by Aaron Bentley
Make case_sensitive an aspect of the transform, not the source tree
280
        transform = TreeTransform(tree)
281
        self.addCleanup(transform.finalize)
282
        transform.new_file('file', transform.root, 'content')
283
        transform.new_file('FiLe', transform.root, 'content')
3034.4.1 by Aaron Bentley
Start handling case-insensitivity
284
        result = transform.find_conflicts()
285
        self.assertEqual([('duplicate', 'new-1', 'new-2', 'file')], result)
286
3034.4.7 by Aaron Bentley
Add test for filename conflicts with existing files
287
    def test_conflict_on_case_insensitive_existing(self):
288
        tree = self.make_branch_and_tree('tree')
289
        self.build_tree(['tree/FiLe'])
290
        # Don't try this at home, kids!
291
        # Force the tree to report that it is case sensitive, for conflict
292
        # resolution tests
293
        tree.case_sensitive = True
294
        transform = TreeTransform(tree)
295
        self.addCleanup(transform.finalize)
296
        transform.new_file('file', transform.root, 'content')
297
        result = transform.find_conflicts()
298
        self.assertEqual([], result)
3008.1.15 by Aaron Bentley
Make case_sensitive an aspect of the transform, not the source tree
299
        transform.finalize()
3034.4.7 by Aaron Bentley
Add test for filename conflicts with existing files
300
        # Force the tree to report that it is case insensitive, for conflict
301
        # generation tests
302
        tree.case_sensitive = False
3008.1.15 by Aaron Bentley
Make case_sensitive an aspect of the transform, not the source tree
303
        transform = TreeTransform(tree)
304
        self.addCleanup(transform.finalize)
305
        transform.new_file('file', transform.root, 'content')
3034.4.7 by Aaron Bentley
Add test for filename conflicts with existing files
306
        result = transform.find_conflicts()
307
        self.assertEqual([('duplicate', 'new-1', 'new-2', 'file')], result)
308
3034.4.1 by Aaron Bentley
Start handling case-insensitivity
309
    def test_resolve_case_insensitive_conflict(self):
310
        tree = self.make_branch_and_tree('tree')
311
        # Don't try this at home, kids!
312
        # Force the tree to report that it is case insensitive, for conflict
313
        # resolution tests
314
        tree.case_sensitive = False
315
        transform = TreeTransform(tree)
316
        self.addCleanup(transform.finalize)
317
        transform.new_file('file', transform.root, 'content')
318
        transform.new_file('FiLe', transform.root, 'content')
319
        resolve_conflicts(transform)
320
        transform.apply()
321
        self.failUnlessExists('tree/file')
322
        self.failUnlessExists('tree/FiLe.moved')
323
3034.4.2 by Aaron Bentley
Get conflict handling and case-insensitive tree creation under test
324
    def test_resolve_checkout_case_conflict(self):
3034.4.1 by Aaron Bentley
Start handling case-insensitivity
325
        tree = self.make_branch_and_tree('tree')
326
        # Don't try this at home, kids!
327
        # Force the tree to report that it is case insensitive, for conflict
328
        # resolution tests
329
        tree.case_sensitive = False
330
        transform = TreeTransform(tree)
331
        self.addCleanup(transform.finalize)
332
        transform.new_file('file', transform.root, 'content')
333
        transform.new_file('FiLe', transform.root, 'content')
3034.4.2 by Aaron Bentley
Get conflict handling and case-insensitive tree creation under test
334
        resolve_conflicts(transform,
335
                          pass_func=lambda t, c: resolve_checkout(t, c, []))
3034.4.1 by Aaron Bentley
Start handling case-insensitivity
336
        transform.apply()
337
        self.failUnlessExists('tree/file')
338
        self.failUnlessExists('tree/FiLe.moved')
339
3034.4.2 by Aaron Bentley
Get conflict handling and case-insensitive tree creation under test
340
    def test_apply_case_conflict(self):
3034.4.6 by Aaron Bentley
Update apply_case_conflict tests
341
        """Ensure that a transform with case conflicts can always be applied"""
3034.4.2 by Aaron Bentley
Get conflict handling and case-insensitive tree creation under test
342
        tree = self.make_branch_and_tree('tree')
343
        transform = TreeTransform(tree)
344
        self.addCleanup(transform.finalize)
345
        transform.new_file('file', transform.root, 'content')
346
        transform.new_file('FiLe', transform.root, 'content')
3034.4.6 by Aaron Bentley
Update apply_case_conflict tests
347
        dir = transform.new_directory('dir', transform.root)
348
        transform.new_file('dirfile', dir, 'content')
349
        transform.new_file('dirFiLe', dir, 'content')
350
        resolve_conflicts(transform)
3034.4.2 by Aaron Bentley
Get conflict handling and case-insensitive tree creation under test
351
        transform.apply()
3034.4.3 by Aaron Bentley
Add case-sensitivity handling to WorkingTree
352
        self.failUnlessExists('tree/file')
353
        if not os.path.exists('tree/FiLe.moved'):
354
            self.failUnlessExists('tree/FiLe')
3034.4.6 by Aaron Bentley
Update apply_case_conflict tests
355
        self.failUnlessExists('tree/dir/dirfile')
356
        if not os.path.exists('tree/dir/dirFiLe.moved'):
357
            self.failUnlessExists('tree/dir/dirFiLe')
3034.4.2 by Aaron Bentley
Get conflict handling and case-insensitive tree creation under test
358
3034.4.1 by Aaron Bentley
Start handling case-insensitivity
359
    def test_case_insensitive_limbo(self):
360
        tree = self.make_branch_and_tree('tree')
361
        # Don't try this at home, kids!
362
        # Force the tree to report that it is case insensitive
363
        tree.case_sensitive = False
364
        transform = TreeTransform(tree)
365
        self.addCleanup(transform.finalize)
366
        dir = transform.new_directory('dir', transform.root)
367
        first = transform.new_file('file', dir, 'content')
368
        second = transform.new_file('FiLe', dir, 'content')
369
        self.assertContainsRe(transform._limbo_name(first), 'new-1/file')
370
        self.assertNotContainsRe(transform._limbo_name(second), 'new-1/FiLe')
371
1558.7.11 by Aaron Bentley
Avoid spurious conflict on add/delete
372
    def test_add_del(self):
373
        start, root = self.get_transform()
374
        start.new_directory('a', root, 'a')
375
        start.apply()
376
        transform, root = self.get_transform()
377
        transform.delete_versioned(transform.trans_id_tree_file_id('a'))
378
        transform.new_directory('a', root, 'a')
379
        transform.apply()
380
1534.7.46 by Aaron Bentley
Ensured a conflict when parents of versioned files are unversioned
381
    def test_unversioning(self):
1534.7.59 by Aaron Bentley
Simplified tests
382
        create_tree, root = self.get_transform()
383
        parent_id = create_tree.new_directory('parent', root, 'parent-id')
384
        create_tree.new_file('child', parent_id, 'child', 'child-id')
385
        create_tree.apply()
386
        unversion = TreeTransform(self.wt)
387
        self.addCleanup(unversion.finalize)
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
388
        parent = unversion.trans_id_tree_path('parent')
1534.7.59 by Aaron Bentley
Simplified tests
389
        unversion.unversion_file(parent)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
390
        self.assertEqual(unversion.find_conflicts(),
1534.7.59 by Aaron Bentley
Simplified tests
391
                         [('unversioned parent', parent_id)])
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
392
        file_id = unversion.trans_id_tree_file_id('child-id')
1534.7.59 by Aaron Bentley
Simplified tests
393
        unversion.unversion_file(file_id)
394
        unversion.apply()
1534.7.46 by Aaron Bentley
Ensured a conflict when parents of versioned files are unversioned
395
1534.7.36 by Aaron Bentley
Added rename tests
396
    def test_name_invariants(self):
1534.7.59 by Aaron Bentley
Simplified tests
397
        create_tree, root = self.get_transform()
398
        # prepare tree
1731.1.33 by Aaron Bentley
Revert no-special-root changes
399
        root = create_tree.root
1534.7.59 by Aaron Bentley
Simplified tests
400
        create_tree.new_file('name1', root, 'hello1', 'name1')
401
        create_tree.new_file('name2', root, 'hello2', 'name2')
402
        ddir = create_tree.new_directory('dying_directory', root, 'ddir')
403
        create_tree.new_file('dying_file', ddir, 'goodbye1', 'dfile')
404
        create_tree.new_file('moving_file', ddir, 'later1', 'mfile')
405
        create_tree.new_file('moving_file2', root, 'later2', 'mfile2')
406
        create_tree.apply()
407
408
        mangle_tree,root = self.get_transform()
1731.1.33 by Aaron Bentley
Revert no-special-root changes
409
        root = mangle_tree.root
1534.7.59 by Aaron Bentley
Simplified tests
410
        #swap names
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
411
        name1 = mangle_tree.trans_id_tree_file_id('name1')
412
        name2 = mangle_tree.trans_id_tree_file_id('name2')
1534.7.59 by Aaron Bentley
Simplified tests
413
        mangle_tree.adjust_path('name2', root, name1)
414
        mangle_tree.adjust_path('name1', root, name2)
415
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
416
        #tests for deleting parent directories
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
417
        ddir = mangle_tree.trans_id_tree_file_id('ddir')
1534.7.59 by Aaron Bentley
Simplified tests
418
        mangle_tree.delete_contents(ddir)
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
419
        dfile = mangle_tree.trans_id_tree_file_id('dfile')
1534.7.59 by Aaron Bentley
Simplified tests
420
        mangle_tree.delete_versioned(dfile)
421
        mangle_tree.unversion_file(dfile)
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
422
        mfile = mangle_tree.trans_id_tree_file_id('mfile')
1534.7.59 by Aaron Bentley
Simplified tests
423
        mangle_tree.adjust_path('mfile', root, mfile)
424
425
        #tests for adding parent directories
426
        newdir = mangle_tree.new_directory('new_directory', root, 'newdir')
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
427
        mfile2 = mangle_tree.trans_id_tree_file_id('mfile2')
1534.7.59 by Aaron Bentley
Simplified tests
428
        mangle_tree.adjust_path('mfile2', newdir, mfile2)
429
        mangle_tree.new_file('newfile', newdir, 'hello3', 'dfile')
430
        self.assertEqual(mangle_tree.final_file_id(mfile2), 'mfile2')
431
        self.assertEqual(mangle_tree.final_parent(mfile2), newdir)
432
        self.assertEqual(mangle_tree.final_file_id(mfile2), 'mfile2')
433
        mangle_tree.apply()
434
        self.assertEqual(file(self.wt.abspath('name1')).read(), 'hello2')
435
        self.assertEqual(file(self.wt.abspath('name2')).read(), 'hello1')
1534.7.176 by abentley
Fixed up tests for Windows
436
        mfile2_path = self.wt.abspath(pathjoin('new_directory','mfile2'))
1534.7.41 by Aaron Bentley
Got inventory ID movement working
437
        self.assertEqual(mangle_tree.final_parent(mfile2), newdir)
1534.7.38 by Aaron Bentley
Tested adding paths
438
        self.assertEqual(file(mfile2_path).read(), 'later2')
1534.7.59 by Aaron Bentley
Simplified tests
439
        self.assertEqual(self.wt.id2path('mfile2'), 'new_directory/mfile2')
440
        self.assertEqual(self.wt.path2id('new_directory/mfile2'), 'mfile2')
1534.7.176 by abentley
Fixed up tests for Windows
441
        newfile_path = self.wt.abspath(pathjoin('new_directory','newfile'))
1534.7.38 by Aaron Bentley
Tested adding paths
442
        self.assertEqual(file(newfile_path).read(), 'hello3')
1534.7.59 by Aaron Bentley
Simplified tests
443
        self.assertEqual(self.wt.path2id('dying_directory'), 'ddir')
444
        self.assertIs(self.wt.path2id('dying_directory/dying_file'), None)
1534.7.176 by abentley
Fixed up tests for Windows
445
        mfile2_path = self.wt.abspath(pathjoin('new_directory','mfile2'))
1534.7.43 by abentley
Fixed some Windows bugs, introduced a conflicts bug
446
1534.7.150 by Aaron Bentley
Handled simultaneous renames of parent and child better
447
    def test_both_rename(self):
448
        create_tree,root = self.get_transform()
449
        newdir = create_tree.new_directory('selftest', root, 'selftest-id')
450
        create_tree.new_file('blackbox.py', newdir, 'hello1', 'blackbox-id')
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
451
        create_tree.apply()
1534.7.150 by Aaron Bentley
Handled simultaneous renames of parent and child better
452
        mangle_tree,root = self.get_transform()
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
453
        selftest = mangle_tree.trans_id_tree_file_id('selftest-id')
454
        blackbox = mangle_tree.trans_id_tree_file_id('blackbox-id')
1534.7.150 by Aaron Bentley
Handled simultaneous renames of parent and child better
455
        mangle_tree.adjust_path('test', root, selftest)
456
        mangle_tree.adjust_path('test_too_much', root, selftest)
457
        mangle_tree.set_executability(True, blackbox)
458
        mangle_tree.apply()
459
460
    def test_both_rename2(self):
461
        create_tree,root = self.get_transform()
462
        bzrlib = create_tree.new_directory('bzrlib', root, 'bzrlib-id')
463
        tests = create_tree.new_directory('tests', bzrlib, 'tests-id')
464
        blackbox = create_tree.new_directory('blackbox', tests, 'blackbox-id')
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
465
        create_tree.new_file('test_too_much.py', blackbox, 'hello1',
1534.7.150 by Aaron Bentley
Handled simultaneous renames of parent and child better
466
                             'test_too_much-id')
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
467
        create_tree.apply()
1534.7.150 by Aaron Bentley
Handled simultaneous renames of parent and child better
468
        mangle_tree,root = self.get_transform()
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
469
        bzrlib = mangle_tree.trans_id_tree_file_id('bzrlib-id')
470
        tests = mangle_tree.trans_id_tree_file_id('tests-id')
471
        test_too_much = mangle_tree.trans_id_tree_file_id('test_too_much-id')
1534.7.150 by Aaron Bentley
Handled simultaneous renames of parent and child better
472
        mangle_tree.adjust_path('selftest', bzrlib, tests)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
473
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much)
1534.7.150 by Aaron Bentley
Handled simultaneous renames of parent and child better
474
        mangle_tree.set_executability(True, test_too_much)
475
        mangle_tree.apply()
476
477
    def test_both_rename3(self):
478
        create_tree,root = self.get_transform()
479
        tests = create_tree.new_directory('tests', root, 'tests-id')
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
480
        create_tree.new_file('test_too_much.py', tests, 'hello1',
1534.7.150 by Aaron Bentley
Handled simultaneous renames of parent and child better
481
                             'test_too_much-id')
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
482
        create_tree.apply()
1534.7.150 by Aaron Bentley
Handled simultaneous renames of parent and child better
483
        mangle_tree,root = self.get_transform()
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
484
        tests = mangle_tree.trans_id_tree_file_id('tests-id')
485
        test_too_much = mangle_tree.trans_id_tree_file_id('test_too_much-id')
1534.7.150 by Aaron Bentley
Handled simultaneous renames of parent and child better
486
        mangle_tree.adjust_path('selftest', root, tests)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
487
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much)
1534.7.150 by Aaron Bentley
Handled simultaneous renames of parent and child better
488
        mangle_tree.set_executability(True, test_too_much)
489
        mangle_tree.apply()
490
1534.7.48 by Aaron Bentley
Ensured we can move/rename dangling inventory entries
491
    def test_move_dangling_ie(self):
1534.7.59 by Aaron Bentley
Simplified tests
492
        create_tree, root = self.get_transform()
493
        # prepare tree
1731.1.33 by Aaron Bentley
Revert no-special-root changes
494
        root = create_tree.root
1534.7.59 by Aaron Bentley
Simplified tests
495
        create_tree.new_file('name1', root, 'hello1', 'name1')
496
        create_tree.apply()
497
        delete_contents, root = self.get_transform()
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
498
        file = delete_contents.trans_id_tree_file_id('name1')
1534.7.59 by Aaron Bentley
Simplified tests
499
        delete_contents.delete_contents(file)
500
        delete_contents.apply()
501
        move_id, root = self.get_transform()
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
502
        name1 = move_id.trans_id_tree_file_id('name1')
1534.7.59 by Aaron Bentley
Simplified tests
503
        newdir = move_id.new_directory('dir', root, 'newdir')
504
        move_id.adjust_path('name2', newdir, name1)
505
        move_id.apply()
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
506
1534.7.50 by Aaron Bentley
Detect duplicate inventory ids
507
    def test_replace_dangling_ie(self):
1534.7.59 by Aaron Bentley
Simplified tests
508
        create_tree, root = self.get_transform()
509
        # prepare tree
1731.1.33 by Aaron Bentley
Revert no-special-root changes
510
        root = create_tree.root
1534.7.59 by Aaron Bentley
Simplified tests
511
        create_tree.new_file('name1', root, 'hello1', 'name1')
512
        create_tree.apply()
513
        delete_contents = TreeTransform(self.wt)
514
        self.addCleanup(delete_contents.finalize)
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
515
        file = delete_contents.trans_id_tree_file_id('name1')
1534.7.59 by Aaron Bentley
Simplified tests
516
        delete_contents.delete_contents(file)
517
        delete_contents.apply()
518
        delete_contents.finalize()
519
        replace = TreeTransform(self.wt)
520
        self.addCleanup(replace.finalize)
521
        name2 = replace.new_file('name2', root, 'hello2', 'name1')
522
        conflicts = replace.find_conflicts()
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
523
        name1 = replace.trans_id_tree_file_id('name1')
1534.7.59 by Aaron Bentley
Simplified tests
524
        self.assertEqual(conflicts, [('duplicate id', name1, name2)])
525
        resolve_conflicts(replace)
526
        replace.apply()
1534.7.48 by Aaron Bentley
Ensured we can move/rename dangling inventory entries
527
4241.14.17 by Vincent Ladeuil
Add more tests for unicode symlinks to test_transform.
528
    def _test_symlinks(self, link_name1,link_target1,
529
                       link_name2, link_target2):
530
531
        def ozpath(p): return 'oz/' + p
532
2949.5.1 by Alexander Belchenko
selftest: use SymlinkFeature instead of TestSkipped where appropriate
533
        self.requireFeature(SymlinkFeature)
4241.14.12 by Vincent Ladeuil
Far too many modifications for a single commit, need to restart.
534
        transform, root = self.get_transform()
1534.7.59 by Aaron Bentley
Simplified tests
535
        oz_id = transform.new_directory('oz', root, 'oz-id')
4241.14.17 by Vincent Ladeuil
Add more tests for unicode symlinks to test_transform.
536
        wizard = transform.new_symlink(link_name1, oz_id, link_target1,
1534.7.59 by Aaron Bentley
Simplified tests
537
                                       'wizard-id')
4241.14.17 by Vincent Ladeuil
Add more tests for unicode symlinks to test_transform.
538
        wiz_id = transform.create_path(link_name2, oz_id)
539
        transform.create_symlink(link_target2, wiz_id)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
540
        transform.version_file('wiz-id2', wiz_id)
1534.7.71 by abentley
All tests pass under Windows
541
        transform.set_executability(True, wiz_id)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
542
        self.assertEqual(transform.find_conflicts(),
1534.7.71 by abentley
All tests pass under Windows
543
                         [('non-file executability', wiz_id)])
544
        transform.set_executability(None, wiz_id)
1534.7.59 by Aaron Bentley
Simplified tests
545
        transform.apply()
4241.14.17 by Vincent Ladeuil
Add more tests for unicode symlinks to test_transform.
546
        self.assertEqual(self.wt.path2id(ozpath(link_name1)), 'wizard-id')
547
        self.assertEqual('symlink',
548
                         file_kind(self.wt.abspath(ozpath(link_name1))))
549
        self.assertEqual(link_target2,
550
                         osutils.readlink(self.wt.abspath(ozpath(link_name2))))
551
        self.assertEqual(link_target1,
552
                         osutils.readlink(self.wt.abspath(ozpath(link_name1))))
553
554
    def test_symlinks(self):
555
        self._test_symlinks('wizard', 'wizard-target',
556
                            'wizard2', 'behind_curtain')
557
558
    def test_symlinks_unicode(self):
559
        self.requireFeature(tests.UnicodeFilenameFeature)
560
        self._test_symlinks(u'\N{Euro Sign}wizard',
561
                            u'wizard-targ\N{Euro Sign}t',
562
                            u'\N{Euro Sign}wizard2',
563
                            u'b\N{Euro Sign}hind_curtain')
1534.7.60 by Aaron Bentley
Tested existing conflict resolution functionality
564
3006.2.2 by Alexander Belchenko
tests added.
565
    def test_unable_create_symlink(self):
566
        def tt_helper():
567
            wt = self.make_branch_and_tree('.')
568
            tt = TreeTransform(wt)  # TreeTransform obtains write lock
569
            try:
570
                tt.new_symlink('foo', tt.root, 'bar')
571
                tt.apply()
572
            finally:
573
                wt.unlock()
574
        os_symlink = getattr(os, 'symlink', None)
575
        os.symlink = None
576
        try:
577
            err = self.assertRaises(errors.UnableCreateSymlink, tt_helper)
578
            self.assertEquals(
579
                "Unable to create symlink 'foo' on this platform",
580
                str(err))
581
        finally:
582
            if os_symlink:
583
                os.symlink = os_symlink
584
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
585
    def get_conflicted(self):
1534.7.60 by Aaron Bentley
Tested existing conflict resolution functionality
586
        create,root = self.get_transform()
587
        create.new_file('dorothy', root, 'dorothy', 'dorothy-id')
1534.7.61 by Aaron Bentley
Handled parent loops, missing parents, unversioned parents
588
        oz = create.new_directory('oz', root, 'oz-id')
589
        create.new_directory('emeraldcity', oz, 'emerald-id')
1534.7.60 by Aaron Bentley
Tested existing conflict resolution functionality
590
        create.apply()
591
        conflicts,root = self.get_transform()
1534.7.65 by Aaron Bentley
Text cleaup/docs
592
        # set up duplicate entry, duplicate id
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
593
        new_dorothy = conflicts.new_file('dorothy', root, 'dorothy',
1534.7.60 by Aaron Bentley
Tested existing conflict resolution functionality
594
                                         'dorothy-id')
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
595
        old_dorothy = conflicts.trans_id_tree_file_id('dorothy-id')
596
        oz = conflicts.trans_id_tree_file_id('oz-id')
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
597
        # set up DeletedParent parent conflict
1534.7.65 by Aaron Bentley
Text cleaup/docs
598
        conflicts.delete_versioned(oz)
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
599
        emerald = conflicts.trans_id_tree_file_id('emerald-id')
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
600
        # set up MissingParent conflict
601
        munchkincity = conflicts.trans_id_file_id('munchkincity-id')
602
        conflicts.adjust_path('munchkincity', root, munchkincity)
603
        conflicts.new_directory('auntem', munchkincity, 'auntem-id')
1534.7.65 by Aaron Bentley
Text cleaup/docs
604
        # set up parent loop
1534.7.61 by Aaron Bentley
Handled parent loops, missing parents, unversioned parents
605
        conflicts.adjust_path('emeraldcity', emerald, emerald)
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
606
        return conflicts, emerald, oz, old_dorothy, new_dorothy
607
608
    def test_conflict_resolution(self):
609
        conflicts, emerald, oz, old_dorothy, new_dorothy =\
610
            self.get_conflicted()
1534.7.60 by Aaron Bentley
Tested existing conflict resolution functionality
611
        resolve_conflicts(conflicts)
612
        self.assertEqual(conflicts.final_name(old_dorothy), 'dorothy.moved')
613
        self.assertIs(conflicts.final_file_id(old_dorothy), None)
614
        self.assertEqual(conflicts.final_name(new_dorothy), 'dorothy')
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
615
        self.assertEqual(conflicts.final_file_id(new_dorothy), 'dorothy-id')
1534.7.64 by Aaron Bentley
Extra testing
616
        self.assertEqual(conflicts.final_parent(emerald), oz)
1534.7.63 by Aaron Bentley
Ensure transform can be applied after resolution
617
        conflicts.apply()
1534.7.62 by Aaron Bentley
Fixed moving versioned directories
618
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
619
    def test_cook_conflicts(self):
620
        tt, emerald, oz, old_dorothy, new_dorothy = self.get_conflicted()
621
        raw_conflicts = resolve_conflicts(tt)
622
        cooked_conflicts = cook_conflicts(raw_conflicts, tt)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
623
        duplicate = DuplicateEntry('Moved existing file to', 'dorothy.moved',
1534.10.20 by Aaron Bentley
Got all tests passing
624
                                   'dorothy', None, 'dorothy-id')
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
625
        self.assertEqual(cooked_conflicts[0], duplicate)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
626
        duplicate_id = DuplicateID('Unversioned existing file',
1534.10.20 by Aaron Bentley
Got all tests passing
627
                                   'dorothy.moved', 'dorothy', None,
628
                                   'dorothy-id')
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
629
        self.assertEqual(cooked_conflicts[1], duplicate_id)
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
630
        missing_parent = MissingParent('Created directory', 'munchkincity',
631
                                       'munchkincity-id')
632
        deleted_parent = DeletingParent('Not deleting', 'oz', 'oz-id')
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
633
        self.assertEqual(cooked_conflicts[2], missing_parent)
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
634
        unversioned_parent = UnversionedParent('Versioned directory',
635
                                               'munchkincity',
636
                                               'munchkincity-id')
637
        unversioned_parent2 = UnversionedParent('Versioned directory', 'oz',
1534.10.20 by Aaron Bentley
Got all tests passing
638
                                               'oz-id')
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
639
        self.assertEqual(cooked_conflicts[3], unversioned_parent)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
640
        parent_loop = ParentLoop('Cancelled move', 'oz/emeraldcity',
1534.10.20 by Aaron Bentley
Got all tests passing
641
                                 'oz/emeraldcity', 'emerald-id', 'emerald-id')
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
642
        self.assertEqual(cooked_conflicts[4], deleted_parent)
643
        self.assertEqual(cooked_conflicts[5], unversioned_parent2)
644
        self.assertEqual(cooked_conflicts[6], parent_loop)
645
        self.assertEqual(len(cooked_conflicts), 7)
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
646
        tt.finalize()
647
648
    def test_string_conflicts(self):
649
        tt, emerald, oz, old_dorothy, new_dorothy = self.get_conflicted()
650
        raw_conflicts = resolve_conflicts(tt)
651
        cooked_conflicts = cook_conflicts(raw_conflicts, tt)
652
        tt.finalize()
1534.10.24 by Aaron Bentley
Eliminated conflicts_to_strings, made remove_files a ConflictList member
653
        conflicts_s = [str(c) for c in cooked_conflicts]
1534.7.171 by Aaron Bentley
Implemented stringifying filesystem conflicts
654
        self.assertEqual(len(cooked_conflicts), len(conflicts_s))
655
        self.assertEqual(conflicts_s[0], 'Conflict adding file dorothy.  '
656
                                         'Moved existing file to '
657
                                         'dorothy.moved.')
658
        self.assertEqual(conflicts_s[1], 'Conflict adding id to dorothy.  '
659
                                         'Unversioned existing file '
660
                                         'dorothy.moved.')
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
661
        self.assertEqual(conflicts_s[2], 'Conflict adding files to'
662
                                         ' munchkincity.  Created directory.')
663
        self.assertEqual(conflicts_s[3], 'Conflict because munchkincity is not'
664
                                         ' versioned, but has versioned'
665
                                         ' children.  Versioned directory.')
1551.8.23 by Aaron Bentley
Tweaked conflict message to be more understandable
666
        self.assertEqualDiff(conflicts_s[4], "Conflict: can't delete oz because it"
667
                                         " is not empty.  Not deleting.")
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
668
        self.assertEqual(conflicts_s[5], 'Conflict because oz is not'
669
                                         ' versioned, but has versioned'
670
                                         ' children.  Versioned directory.')
671
        self.assertEqual(conflicts_s[6], 'Conflict moving oz/emeraldcity into'
1534.7.171 by Aaron Bentley
Implemented stringifying filesystem conflicts
672
                                         ' oz/emeraldcity.  Cancelled move.')
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
673
3144.4.2 by Aaron Bentley
Handle non-directory parent conflicts (abentley, #177390)
674
    def prepare_wrong_parent_kind(self):
675
        tt, root = self.get_transform()
676
        tt.new_file('parent', root, 'contents', 'parent-id')
677
        tt.apply()
678
        tt, root = self.get_transform()
679
        parent_id = tt.trans_id_file_id('parent-id')
680
        tt.new_file('child,', parent_id, 'contents2', 'file-id')
681
        return tt
682
3144.4.1 by Aaron Bentley
Handle trying to list parents of a non-directory
683
    def test_find_conflicts_wrong_parent_kind(self):
3144.4.2 by Aaron Bentley
Handle non-directory parent conflicts (abentley, #177390)
684
        tt = self.prepare_wrong_parent_kind()
3144.4.1 by Aaron Bentley
Handle trying to list parents of a non-directory
685
        tt.find_conflicts()
686
3144.4.2 by Aaron Bentley
Handle non-directory parent conflicts (abentley, #177390)
687
    def test_resolve_conflicts_wrong_existing_parent_kind(self):
688
        tt = self.prepare_wrong_parent_kind()
689
        raw_conflicts = resolve_conflicts(tt)
690
        self.assertEqual(set([('non-directory parent', 'Created directory',
691
                         'new-3')]), raw_conflicts)
692
        cooked_conflicts = cook_conflicts(raw_conflicts, tt)
693
        self.assertEqual([NonDirectoryParent('Created directory', 'parent.new',
694
        'parent-id')], cooked_conflicts)
695
        tt.apply()
696
        self.assertEqual(None, self.wt.path2id('parent'))
697
        self.assertEqual('parent-id', self.wt.path2id('parent.new'))
698
699
    def test_resolve_conflicts_wrong_new_parent_kind(self):
700
        tt, root = self.get_transform()
701
        parent_id = tt.new_directory('parent', root, 'parent-id')
702
        tt.new_file('child,', parent_id, 'contents2', 'file-id')
703
        tt.apply()
704
        tt, root = self.get_transform()
705
        parent_id = tt.trans_id_file_id('parent-id')
706
        tt.delete_contents(parent_id)
707
        tt.create_file('contents', parent_id)
708
        raw_conflicts = resolve_conflicts(tt)
709
        self.assertEqual(set([('non-directory parent', 'Created directory',
710
                         'new-3')]), raw_conflicts)
711
        tt.apply()
712
        self.assertEqual(None, self.wt.path2id('parent'))
713
        self.assertEqual('parent-id', self.wt.path2id('parent.new'))
714
715
    def test_resolve_conflicts_wrong_parent_kind_unversioned(self):
716
        tt, root = self.get_transform()
717
        parent_id = tt.new_directory('parent', root)
718
        tt.new_file('child,', parent_id, 'contents2')
719
        tt.apply()
720
        tt, root = self.get_transform()
721
        parent_id = tt.trans_id_tree_path('parent')
722
        tt.delete_contents(parent_id)
723
        tt.create_file('contents', parent_id)
724
        resolve_conflicts(tt)
725
        tt.apply()
726
        self.assertIs(None, self.wt.path2id('parent'))
727
        self.assertIs(None, self.wt.path2id('parent.new'))
728
1534.7.62 by Aaron Bentley
Fixed moving versioned directories
729
    def test_moving_versioned_directories(self):
730
        create, root = self.get_transform()
731
        kansas = create.new_directory('kansas', root, 'kansas-id')
732
        create.new_directory('house', kansas, 'house-id')
733
        create.new_directory('oz', root, 'oz-id')
734
        create.apply()
735
        cyclone, root = self.get_transform()
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
736
        oz = cyclone.trans_id_tree_file_id('oz-id')
737
        house = cyclone.trans_id_tree_file_id('house-id')
1534.7.62 by Aaron Bentley
Fixed moving versioned directories
738
        cyclone.adjust_path('house', oz, house)
739
        cyclone.apply()
1534.7.66 by Aaron Bentley
Ensured we don't accidentally move the root directory
740
741
    def test_moving_root(self):
1534.7.68 by Aaron Bentley
Got semi-reasonable root directory renaming working
742
        create, root = self.get_transform()
743
        fun = create.new_directory('fun', root, 'fun-id')
744
        create.new_directory('sun', root, 'sun-id')
745
        create.new_directory('moon', root, 'moon')
746
        create.apply()
1534.7.66 by Aaron Bentley
Ensured we don't accidentally move the root directory
747
        transform, root = self.get_transform()
1534.7.68 by Aaron Bentley
Got semi-reasonable root directory renaming working
748
        transform.adjust_root_path('oldroot', fun)
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
749
        new_root=transform.trans_id_tree_path('')
1534.7.69 by Aaron Bentley
Got real root moves working
750
        transform.version_file('new-root', new_root)
1534.7.68 by Aaron Bentley
Got semi-reasonable root directory renaming working
751
        transform.apply()
1534.7.93 by Aaron Bentley
Added text merge test
752
1534.7.114 by Aaron Bentley
Added file renaming test case
753
    def test_renames(self):
754
        create, root = self.get_transform()
755
        old = create.new_directory('old-parent', root, 'old-id')
756
        intermediate = create.new_directory('intermediate', old, 'im-id')
757
        myfile = create.new_file('myfile', intermediate, 'myfile-text',
758
                                 'myfile-id')
759
        create.apply()
760
        rename, root = self.get_transform()
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
761
        old = rename.trans_id_file_id('old-id')
1534.7.114 by Aaron Bentley
Added file renaming test case
762
        rename.adjust_path('new', root, old)
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
763
        myfile = rename.trans_id_file_id('myfile-id')
1534.7.114 by Aaron Bentley
Added file renaming test case
764
        rename.set_executability(True, myfile)
765
        rename.apply()
766
1740.2.4 by Aaron Bentley
Update transform tests and docs
767
    def test_set_executability_order(self):
768
        """Ensure that executability behaves the same, no matter what order.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
769
1740.2.4 by Aaron Bentley
Update transform tests and docs
770
        - create file and set executability simultaneously
771
        - create file and set executability afterward
772
        - unsetting the executability of a file whose executability has not been
773
        declared should throw an exception (this may happen when a
774
        merge attempts to create a file with a duplicate ID)
775
        """
776
        transform, root = self.get_transform()
777
        wt = transform._tree
3034.2.1 by Aaron Bentley
Fix is_executable tests for win32
778
        wt.lock_read()
779
        self.addCleanup(wt.unlock)
1740.2.4 by Aaron Bentley
Update transform tests and docs
780
        transform.new_file('set_on_creation', root, 'Set on creation', 'soc',
781
                           True)
2502.1.1 by Aaron Bentley
Ensure renames only root children are renamed when building trees
782
        sac = transform.new_file('set_after_creation', root,
783
                                 'Set after creation', 'sac')
1740.2.4 by Aaron Bentley
Update transform tests and docs
784
        transform.set_executability(True, sac)
2502.1.1 by Aaron Bentley
Ensure renames only root children are renamed when building trees
785
        uws = transform.new_file('unset_without_set', root, 'Unset badly',
786
                                 'uws')
1740.2.4 by Aaron Bentley
Update transform tests and docs
787
        self.assertRaises(KeyError, transform.set_executability, None, uws)
788
        transform.apply()
789
        self.assertTrue(wt.is_executable('soc'))
790
        self.assertTrue(wt.is_executable('sac'))
791
1534.12.2 by Aaron Bentley
Added test for preserving file mode
792
    def test_preserve_mode(self):
793
        """File mode is preserved when replacing content"""
794
        if sys.platform == 'win32':
795
            raise TestSkipped('chmod has no effect on win32')
796
        transform, root = self.get_transform()
797
        transform.new_file('file1', root, 'contents', 'file1-id', True)
798
        transform.apply()
3146.4.12 by Aaron Bentley
Add needed write lock to test
799
        self.wt.lock_write()
800
        self.addCleanup(self.wt.unlock)
1534.12.2 by Aaron Bentley
Added test for preserving file mode
801
        self.assertTrue(self.wt.is_executable('file1-id'))
802
        transform, root = self.get_transform()
803
        file1_id = transform.trans_id_tree_file_id('file1-id')
804
        transform.delete_contents(file1_id)
805
        transform.create_file('contents2', file1_id)
806
        transform.apply()
807
        self.assertTrue(self.wt.is_executable('file1-id'))
808
2027.1.1 by John Arbash Meinel
Fix bug #56549, and write a direct test that the right path is being statted
809
    def test__set_mode_stats_correctly(self):
810
        """_set_mode stats to determine file mode."""
811
        if sys.platform == 'win32':
812
            raise TestSkipped('chmod has no effect on win32')
813
814
        stat_paths = []
815
        real_stat = os.stat
816
        def instrumented_stat(path):
817
            stat_paths.append(path)
818
            return real_stat(path)
819
820
        transform, root = self.get_transform()
821
822
        bar1_id = transform.new_file('bar', root, 'bar contents 1\n',
823
                                     file_id='bar-id-1', executable=False)
824
        transform.apply()
825
826
        transform, root = self.get_transform()
827
        bar1_id = transform.trans_id_tree_path('bar')
828
        bar2_id = transform.trans_id_tree_path('bar2')
829
        try:
830
            os.stat = instrumented_stat
831
            transform.create_file('bar2 contents\n', bar2_id, mode_id=bar1_id)
832
        finally:
833
            os.stat = real_stat
834
            transform.finalize()
835
836
        bar1_abspath = self.wt.abspath('bar')
837
        self.assertEqual([bar1_abspath], stat_paths)
838
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
839
    def test_iter_changes(self):
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
840
        self.wt.set_root_id('eert_toor')
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
841
        transform, root = self.get_transform()
842
        transform.new_file('old', root, 'blah', 'id-1', True)
843
        transform.apply()
844
        transform, root = self.get_transform()
845
        try:
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
846
            self.assertEqual([], list(transform.iter_changes()))
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
847
            old = transform.trans_id_tree_file_id('id-1')
848
            transform.unversion_file(old)
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
849
            self.assertEqual([('id-1', ('old', None), False, (True, False),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
850
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
851
                (True, True))], list(transform.iter_changes()))
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
852
            transform.new_directory('new', root, 'id-1')
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
853
            self.assertEqual([('id-1', ('old', 'new'), True, (True, True),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
854
                ('eert_toor', 'eert_toor'), ('old', 'new'),
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
855
                ('file', 'directory'),
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
856
                (True, False))], list(transform.iter_changes()))
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
857
        finally:
858
            transform.finalize()
859
860
    def test_iter_changes_new(self):
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
861
        self.wt.set_root_id('eert_toor')
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
862
        transform, root = self.get_transform()
863
        transform.new_file('old', root, 'blah')
864
        transform.apply()
865
        transform, root = self.get_transform()
866
        try:
867
            old = transform.trans_id_tree_path('old')
868
            transform.version_file('id-1', old)
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
869
            self.assertEqual([('id-1', (None, 'old'), False, (False, True),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
870
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
871
                (False, False))], list(transform.iter_changes()))
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
872
        finally:
873
            transform.finalize()
874
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
875
    def test_iter_changes_modifications(self):
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
876
        self.wt.set_root_id('eert_toor')
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
877
        transform, root = self.get_transform()
878
        transform.new_file('old', root, 'blah', 'id-1')
879
        transform.new_file('new', root, 'blah')
880
        transform.new_directory('subdir', root, 'subdir-id')
881
        transform.apply()
882
        transform, root = self.get_transform()
883
        try:
884
            old = transform.trans_id_tree_path('old')
885
            subdir = transform.trans_id_tree_file_id('subdir-id')
886
            new = transform.trans_id_tree_path('new')
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
887
            self.assertEqual([], list(transform.iter_changes()))
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
888
889
            #content deletion
890
            transform.delete_contents(old)
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
891
            self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
892
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', None),
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
893
                (False, False))], list(transform.iter_changes()))
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
894
895
            #content change
896
            transform.create_file('blah', old)
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
897
            self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
898
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
899
                (False, False))], list(transform.iter_changes()))
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
900
            transform.cancel_deletion(old)
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
901
            self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
902
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
903
                (False, False))], list(transform.iter_changes()))
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
904
            transform.cancel_creation(old)
905
906
            # move file_id to a different file
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
907
            self.assertEqual([], list(transform.iter_changes()))
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
908
            transform.unversion_file(old)
909
            transform.version_file('id-1', new)
910
            transform.adjust_path('old', root, new)
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
911
            self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
912
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
913
                (False, False))], list(transform.iter_changes()))
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
914
            transform.cancel_versioning(new)
915
            transform._removed_id = set()
916
917
            #execute bit
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
918
            self.assertEqual([], list(transform.iter_changes()))
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
919
            transform.set_executability(True, old)
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
920
            self.assertEqual([('id-1', ('old', 'old'), False, (True, True),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
921
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
922
                (False, True))], list(transform.iter_changes()))
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
923
            transform.set_executability(None, old)
924
925
            # filename
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
926
            self.assertEqual([], list(transform.iter_changes()))
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
927
            transform.adjust_path('new', root, old)
928
            transform._new_parent = {}
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
929
            self.assertEqual([('id-1', ('old', 'new'), False, (True, True),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
930
                ('eert_toor', 'eert_toor'), ('old', 'new'), ('file', 'file'),
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
931
                (False, False))], list(transform.iter_changes()))
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
932
            transform._new_name = {}
933
934
            # parent directory
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
935
            self.assertEqual([], list(transform.iter_changes()))
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
936
            transform.adjust_path('new', subdir, old)
937
            transform._new_name = {}
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
938
            self.assertEqual([('id-1', ('old', 'subdir/old'), False,
2255.2.180 by Martin Pool
merge dirstate
939
                (True, True), ('eert_toor', 'subdir-id'), ('old', 'old'),
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
940
                ('file', 'file'), (False, False))],
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
941
                list(transform.iter_changes()))
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
942
            transform._new_path = {}
943
944
        finally:
945
            transform.finalize()
946
1551.11.7 by Aaron Bentley
Stop modified flag bleeding into later changes
947
    def test_iter_changes_modified_bleed(self):
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
948
        self.wt.set_root_id('eert_toor')
1551.11.7 by Aaron Bentley
Stop modified flag bleeding into later changes
949
        """Modified flag should not bleed from one change to another"""
950
        # unfortunately, we have no guarantee that file1 (which is modified)
951
        # will be applied before file2.  And if it's applied after file2, it
952
        # obviously can't bleed into file2's change output.  But for now, it
953
        # works.
954
        transform, root = self.get_transform()
955
        transform.new_file('file1', root, 'blah', 'id-1')
956
        transform.new_file('file2', root, 'blah', 'id-2')
957
        transform.apply()
958
        transform, root = self.get_transform()
959
        try:
960
            transform.delete_contents(transform.trans_id_file_id('id-1'))
961
            transform.set_executability(True,
962
            transform.trans_id_file_id('id-2'))
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
963
            self.assertEqual([('id-1', (u'file1', u'file1'), True, (True, True),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
964
                ('eert_toor', 'eert_toor'), ('file1', u'file1'),
1551.11.7 by Aaron Bentley
Stop modified flag bleeding into later changes
965
                ('file', None), (False, False)),
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
966
                ('id-2', (u'file2', u'file2'), False, (True, True),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
967
                ('eert_toor', 'eert_toor'), ('file2', u'file2'),
1551.11.7 by Aaron Bentley
Stop modified flag bleeding into later changes
968
                ('file', 'file'), (False, True))],
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
969
                list(transform.iter_changes()))
1551.11.7 by Aaron Bentley
Stop modified flag bleeding into later changes
970
        finally:
971
            transform.finalize()
972
1551.10.37 by Aaron Bentley
recommit of TreeTransform._iter_changes fix with missing files
973
    def test_iter_changes_move_missing(self):
974
        """Test moving ids with no files around"""
975
        self.wt.set_root_id('toor_eert')
976
        # Need two steps because versioning a non-existant file is a conflict.
977
        transform, root = self.get_transform()
978
        transform.new_directory('floater', root, 'floater-id')
979
        transform.apply()
980
        transform, root = self.get_transform()
981
        transform.delete_contents(transform.trans_id_tree_path('floater'))
982
        transform.apply()
983
        transform, root = self.get_transform()
984
        floater = transform.trans_id_tree_path('floater')
985
        try:
986
            transform.adjust_path('flitter', root, floater)
987
            self.assertEqual([('floater-id', ('floater', 'flitter'), False,
988
            (True, True), ('toor_eert', 'toor_eert'), ('floater', 'flitter'),
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
989
            (None, None), (False, False))], list(transform.iter_changes()))
1551.10.37 by Aaron Bentley
recommit of TreeTransform._iter_changes fix with missing files
990
        finally:
991
            transform.finalize()
992
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
993
    def test_iter_changes_pointless(self):
994
        """Ensure that no-ops are not treated as modifications"""
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
995
        self.wt.set_root_id('eert_toor')
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
996
        transform, root = self.get_transform()
997
        transform.new_file('old', root, 'blah', 'id-1')
998
        transform.new_directory('subdir', root, 'subdir-id')
999
        transform.apply()
1000
        transform, root = self.get_transform()
1001
        try:
1002
            old = transform.trans_id_tree_path('old')
1003
            subdir = transform.trans_id_tree_file_id('subdir-id')
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
1004
            self.assertEqual([], list(transform.iter_changes()))
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
1005
            transform.delete_contents(subdir)
1006
            transform.create_directory(subdir)
1007
            transform.set_executability(False, old)
1008
            transform.unversion_file(old)
1009
            transform.version_file('id-1', old)
1010
            transform.adjust_path('old', root, old)
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
1011
            self.assertEqual([], list(transform.iter_changes()))
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
1012
        finally:
1013
            transform.finalize()
1534.7.93 by Aaron Bentley
Added text merge test
1014
2502.1.1 by Aaron Bentley
Ensure renames only root children are renamed when building trees
1015
    def test_rename_count(self):
1016
        transform, root = self.get_transform()
2502.1.8 by Aaron Bentley
Updates from review comments
1017
        transform.new_file('name1', root, 'contents')
2502.1.1 by Aaron Bentley
Ensure renames only root children are renamed when building trees
1018
        self.assertEqual(transform.rename_count, 0)
1019
        transform.apply()
1020
        self.assertEqual(transform.rename_count, 1)
1021
        transform2, root = self.get_transform()
2502.1.8 by Aaron Bentley
Updates from review comments
1022
        transform2.adjust_path('name2', root,
1023
                               transform2.trans_id_tree_path('name1'))
2502.1.1 by Aaron Bentley
Ensure renames only root children are renamed when building trees
1024
        self.assertEqual(transform2.rename_count, 0)
1025
        transform2.apply()
1026
        self.assertEqual(transform2.rename_count, 2)
1027
2502.1.2 by Aaron Bentley
Make the limited-renames functionality safer in the general case
1028
    def test_change_parent(self):
2502.1.8 by Aaron Bentley
Updates from review comments
1029
        """Ensure that after we change a parent, the results are still right.
1030
1031
        Renames and parent changes on pending transforms can happen as part
1032
        of conflict resolution, and are explicitly permitted by the
1033
        TreeTransform API.
1034
1035
        This test ensures they work correctly with the rename-avoidance
1036
        optimization.
1037
        """
2502.1.2 by Aaron Bentley
Make the limited-renames functionality safer in the general case
1038
        transform, root = self.get_transform()
2502.1.8 by Aaron Bentley
Updates from review comments
1039
        parent1 = transform.new_directory('parent1', root)
1040
        child1 = transform.new_file('child1', parent1, 'contents')
1041
        parent2 = transform.new_directory('parent2', root)
1042
        transform.adjust_path('child1', parent2, child1)
2502.1.2 by Aaron Bentley
Make the limited-renames functionality safer in the general case
1043
        transform.apply()
2502.1.8 by Aaron Bentley
Updates from review comments
1044
        self.failIfExists(self.wt.abspath('parent1/child1'))
1045
        self.failUnlessExists(self.wt.abspath('parent2/child1'))
1046
        # rename limbo/new-1 => parent1, rename limbo/new-3 => parent2
1047
        # no rename for child1 (counting only renames during apply)
2502.1.2 by Aaron Bentley
Make the limited-renames functionality safer in the general case
1048
        self.failUnlessEqual(2, transform.rename_count)
1049
1050
    def test_cancel_parent(self):
1051
        """Cancelling a parent doesn't cause deletion of a non-empty directory
1052
1053
        This is like the test_change_parent, except that we cancel the parent
1054
        before adjusting the path.  The transform must detect that the
1055
        directory is non-empty, and move children to safe locations.
1056
        """
1057
        transform, root = self.get_transform()
2502.1.8 by Aaron Bentley
Updates from review comments
1058
        parent1 = transform.new_directory('parent1', root)
1059
        child1 = transform.new_file('child1', parent1, 'contents')
1060
        child2 = transform.new_file('child2', parent1, 'contents')
1061
        try:
1062
            transform.cancel_creation(parent1)
1063
        except OSError:
1064
            self.fail('Failed to move child1 before deleting parent1')
1065
        transform.cancel_creation(child2)
1066
        transform.create_directory(parent1)
1067
        try:
1068
            transform.cancel_creation(parent1)
1069
        # If the transform incorrectly believes that child2 is still in
1070
        # parent1's limbo directory, it will try to rename it and fail
1071
        # because was already moved by the first cancel_creation.
1072
        except OSError:
1073
            self.fail('Transform still thinks child2 is a child of parent1')
1074
        parent2 = transform.new_directory('parent2', root)
1075
        transform.adjust_path('child1', parent2, child1)
2502.1.2 by Aaron Bentley
Make the limited-renames functionality safer in the general case
1076
        transform.apply()
2502.1.8 by Aaron Bentley
Updates from review comments
1077
        self.failIfExists(self.wt.abspath('parent1'))
1078
        self.failUnlessExists(self.wt.abspath('parent2/child1'))
1079
        # rename limbo/new-3 => parent2, rename limbo/new-2 => child1
2502.1.2 by Aaron Bentley
Make the limited-renames functionality safer in the general case
1080
        self.failUnlessEqual(2, transform.rename_count)
1081
1082
    def test_adjust_and_cancel(self):
2502.1.8 by Aaron Bentley
Updates from review comments
1083
        """Make sure adjust_path keeps track of limbo children properly"""
2502.1.2 by Aaron Bentley
Make the limited-renames functionality safer in the general case
1084
        transform, root = self.get_transform()
2502.1.8 by Aaron Bentley
Updates from review comments
1085
        parent1 = transform.new_directory('parent1', root)
1086
        child1 = transform.new_file('child1', parent1, 'contents')
1087
        parent2 = transform.new_directory('parent2', root)
1088
        transform.adjust_path('child1', parent2, child1)
1089
        transform.cancel_creation(child1)
2502.1.2 by Aaron Bentley
Make the limited-renames functionality safer in the general case
1090
        try:
2502.1.8 by Aaron Bentley
Updates from review comments
1091
            transform.cancel_creation(parent1)
1092
        # if the transform thinks child1 is still in parent1's limbo
1093
        # directory, it will attempt to move it and fail.
2502.1.2 by Aaron Bentley
Make the limited-renames functionality safer in the general case
1094
        except OSError:
2502.1.8 by Aaron Bentley
Updates from review comments
1095
            self.fail('Transform still thinks child1 is a child of parent1')
2502.1.2 by Aaron Bentley
Make the limited-renames functionality safer in the general case
1096
        transform.finalize()
1097
2502.1.3 by Aaron Bentley
Don't cause errors when creating contents for trans_ids with no parent/name
1098
    def test_noname_contents(self):
2502.1.8 by Aaron Bentley
Updates from review comments
1099
        """TreeTransform should permit deferring naming files."""
2502.1.3 by Aaron Bentley
Don't cause errors when creating contents for trans_ids with no parent/name
1100
        transform, root = self.get_transform()
2502.1.8 by Aaron Bentley
Updates from review comments
1101
        parent = transform.trans_id_file_id('parent-id')
2502.1.3 by Aaron Bentley
Don't cause errors when creating contents for trans_ids with no parent/name
1102
        try:
2502.1.8 by Aaron Bentley
Updates from review comments
1103
            transform.create_directory(parent)
2502.1.3 by Aaron Bentley
Don't cause errors when creating contents for trans_ids with no parent/name
1104
        except KeyError:
1105
            self.fail("Can't handle contents with no name")
1106
        transform.finalize()
1107
2502.1.9 by Aaron Bentley
Add additional test for no-name contents
1108
    def test_noname_contents_nested(self):
1109
        """TreeTransform should permit deferring naming files."""
1110
        transform, root = self.get_transform()
1111
        parent = transform.trans_id_file_id('parent-id')
1112
        try:
1113
            transform.create_directory(parent)
1114
        except KeyError:
1115
            self.fail("Can't handle contents with no name")
1116
        child = transform.new_directory('child', parent)
1117
        transform.adjust_path('parent', root, parent)
1118
        transform.apply()
1119
        self.failUnlessExists(self.wt.abspath('parent/child'))
1120
        self.assertEqual(1, transform.rename_count)
1121
2502.1.4 by Aaron Bentley
Ensure we only reuse limbo names appropriately
1122
    def test_reuse_name(self):
1123
        """Avoid reusing the same limbo name for different files"""
1124
        transform, root = self.get_transform()
2502.1.8 by Aaron Bentley
Updates from review comments
1125
        parent = transform.new_directory('parent', root)
1126
        child1 = transform.new_directory('child', parent)
2502.1.4 by Aaron Bentley
Ensure we only reuse limbo names appropriately
1127
        try:
2502.1.8 by Aaron Bentley
Updates from review comments
1128
            child2 = transform.new_directory('child', parent)
2502.1.4 by Aaron Bentley
Ensure we only reuse limbo names appropriately
1129
        except OSError:
1130
            self.fail('Tranform tried to use the same limbo name twice')
2502.1.8 by Aaron Bentley
Updates from review comments
1131
        transform.adjust_path('child2', parent, child2)
2502.1.4 by Aaron Bentley
Ensure we only reuse limbo names appropriately
1132
        transform.apply()
2502.1.8 by Aaron Bentley
Updates from review comments
1133
        # limbo/new-1 => parent, limbo/new-3 => parent/child2
1134
        # child2 is put into top-level limbo because child1 has already
1135
        # claimed the direct limbo path when child2 is created.  There is no
1136
        # advantage in renaming files once they're in top-level limbo, except
1137
        # as part of apply.
2502.1.4 by Aaron Bentley
Ensure we only reuse limbo names appropriately
1138
        self.assertEqual(2, transform.rename_count)
1139
1140
    def test_reuse_when_first_moved(self):
1141
        """Don't avoid direct paths when it is safe to use them"""
1142
        transform, root = self.get_transform()
2502.1.8 by Aaron Bentley
Updates from review comments
1143
        parent = transform.new_directory('parent', root)
1144
        child1 = transform.new_directory('child', parent)
1145
        transform.adjust_path('child1', parent, child1)
1146
        child2 = transform.new_directory('child', parent)
2502.1.4 by Aaron Bentley
Ensure we only reuse limbo names appropriately
1147
        transform.apply()
2502.1.8 by Aaron Bentley
Updates from review comments
1148
        # limbo/new-1 => parent
2502.1.4 by Aaron Bentley
Ensure we only reuse limbo names appropriately
1149
        self.assertEqual(1, transform.rename_count)
1150
1151
    def test_reuse_after_cancel(self):
1152
        """Don't avoid direct paths when it is safe to use them"""
1153
        transform, root = self.get_transform()
2502.1.8 by Aaron Bentley
Updates from review comments
1154
        parent2 = transform.new_directory('parent2', root)
1155
        child1 = transform.new_directory('child1', parent2)
1156
        transform.cancel_creation(parent2)
1157
        transform.create_directory(parent2)
1158
        child2 = transform.new_directory('child1', parent2)
1159
        transform.adjust_path('child2', parent2, child1)
2502.1.4 by Aaron Bentley
Ensure we only reuse limbo names appropriately
1160
        transform.apply()
2502.1.8 by Aaron Bentley
Updates from review comments
1161
        # limbo/new-1 => parent2, limbo/new-2 => parent2/child1
2502.1.4 by Aaron Bentley
Ensure we only reuse limbo names appropriately
1162
        self.assertEqual(2, transform.rename_count)
1163
2502.1.7 by Aaron Bentley
Fix finalize deletion ordering
1164
    def test_finalize_order(self):
2502.1.8 by Aaron Bentley
Updates from review comments
1165
        """Finalize must be done in child-to-parent order"""
2502.1.7 by Aaron Bentley
Fix finalize deletion ordering
1166
        transform, root = self.get_transform()
2502.1.8 by Aaron Bentley
Updates from review comments
1167
        parent = transform.new_directory('parent', root)
1168
        child = transform.new_directory('child', parent)
2502.1.7 by Aaron Bentley
Fix finalize deletion ordering
1169
        try:
1170
            transform.finalize()
1171
        except OSError:
2502.1.8 by Aaron Bentley
Updates from review comments
1172
            self.fail('Tried to remove parent before child1')
2502.1.7 by Aaron Bentley
Fix finalize deletion ordering
1173
2502.1.13 by Aaron Bentley
Updates from review
1174
    def test_cancel_with_cancelled_child_should_succeed(self):
2502.1.12 by Aaron Bentley
Avoid renaming children with no content
1175
        transform, root = self.get_transform()
1176
        parent = transform.new_directory('parent', root)
1177
        child = transform.new_directory('child', parent)
1178
        transform.cancel_creation(child)
2502.1.13 by Aaron Bentley
Updates from review
1179
        transform.cancel_creation(parent)
2502.1.12 by Aaron Bentley
Avoid renaming children with no content
1180
        transform.finalize()
1181
3638.3.15 by Vincent Ladeuil
Fix test_case_insensitive_clash to pass on all platforms (renamed too).
1182
    def test_rollback_on_directory_clash(self):
3063.1.1 by Alexander Belchenko
Catch OSError 17 (file exists) in final phase of tree transform and show filename to user (#111758).
1183
        def tt_helper():
3638.3.17 by Vincent Ladeuil
Fixed as per Aaron's review.
1184
            wt = self.make_branch_and_tree('.')
3063.1.1 by Alexander Belchenko
Catch OSError 17 (file exists) in final phase of tree transform and show filename to user (#111758).
1185
            tt = TreeTransform(wt)  # TreeTransform obtains write lock
1186
            try:
3638.3.15 by Vincent Ladeuil
Fix test_case_insensitive_clash to pass on all platforms (renamed too).
1187
                foo = tt.new_directory('foo', tt.root)
1188
                tt.new_file('bar', foo, 'foobar')
1189
                baz = tt.new_directory('baz', tt.root)
1190
                tt.new_file('qux', baz, 'quux')
1191
                # Ask for a rename 'foo' -> 'baz'
1192
                tt.adjust_path('baz', tt.root, foo)
3063.1.3 by Aaron Bentley
Update for Linux
1193
                # Lie to tt that we've already resolved all conflicts.
3063.1.1 by Alexander Belchenko
Catch OSError 17 (file exists) in final phase of tree transform and show filename to user (#111758).
1194
                tt.apply(no_conflicts=True)
3063.1.3 by Aaron Bentley
Update for Linux
1195
            except:
3063.1.1 by Alexander Belchenko
Catch OSError 17 (file exists) in final phase of tree transform and show filename to user (#111758).
1196
                wt.unlock()
3063.1.3 by Aaron Bentley
Update for Linux
1197
                raise
3638.3.17 by Vincent Ladeuil
Fixed as per Aaron's review.
1198
        # The rename will fail because the target directory is not empty (but
1199
        # raises FileExists anyway).
3063.1.1 by Alexander Belchenko
Catch OSError 17 (file exists) in final phase of tree transform and show filename to user (#111758).
1200
        err = self.assertRaises(errors.FileExists, tt_helper)
1201
        self.assertContainsRe(str(err),
3638.3.15 by Vincent Ladeuil
Fix test_case_insensitive_clash to pass on all platforms (renamed too).
1202
            "^File exists: .+/baz")
3063.1.1 by Alexander Belchenko
Catch OSError 17 (file exists) in final phase of tree transform and show filename to user (#111758).
1203
3063.1.2 by Alexander Belchenko
test for two directories clash
1204
    def test_two_directories_clash(self):
1205
        def tt_helper():
1206
            wt = self.make_branch_and_tree('.')
1207
            tt = TreeTransform(wt)  # TreeTransform obtains write lock
1208
            try:
3063.1.3 by Aaron Bentley
Update for Linux
1209
                foo_1 = tt.new_directory('foo', tt.root)
1210
                tt.new_directory('bar', foo_1)
3638.3.15 by Vincent Ladeuil
Fix test_case_insensitive_clash to pass on all platforms (renamed too).
1211
                # Adding the same directory with a different content
3063.1.3 by Aaron Bentley
Update for Linux
1212
                foo_2 = tt.new_directory('foo', tt.root)
1213
                tt.new_directory('baz', foo_2)
1214
                # Lie to tt that we've already resolved all conflicts.
3063.1.2 by Alexander Belchenko
test for two directories clash
1215
                tt.apply(no_conflicts=True)
3063.1.3 by Aaron Bentley
Update for Linux
1216
            except:
3063.1.2 by Alexander Belchenko
test for two directories clash
1217
                wt.unlock()
3063.1.3 by Aaron Bentley
Update for Linux
1218
                raise
3063.1.2 by Alexander Belchenko
test for two directories clash
1219
        err = self.assertRaises(errors.FileExists, tt_helper)
1220
        self.assertContainsRe(str(err),
1221
            "^File exists: .+/foo")
1222
3100.1.1 by Aaron Bentley
Fix ImmortalLimbo errors when transforms fail
1223
    def test_two_directories_clash_finalize(self):
1224
        def tt_helper():
1225
            wt = self.make_branch_and_tree('.')
1226
            tt = TreeTransform(wt)  # TreeTransform obtains write lock
1227
            try:
1228
                foo_1 = tt.new_directory('foo', tt.root)
1229
                tt.new_directory('bar', foo_1)
3638.3.15 by Vincent Ladeuil
Fix test_case_insensitive_clash to pass on all platforms (renamed too).
1230
                # Adding the same directory with a different content
3100.1.1 by Aaron Bentley
Fix ImmortalLimbo errors when transforms fail
1231
                foo_2 = tt.new_directory('foo', tt.root)
1232
                tt.new_directory('baz', foo_2)
1233
                # Lie to tt that we've already resolved all conflicts.
1234
                tt.apply(no_conflicts=True)
1235
            except:
1236
                tt.finalize()
1237
                raise
1238
        err = self.assertRaises(errors.FileExists, tt_helper)
1239
        self.assertContainsRe(str(err),
1240
            "^File exists: .+/foo")
1241
3535.6.1 by James Westby
Handle a file turning in to a directory in TreeTransform.
1242
    def test_file_to_directory(self):
1243
        wt = self.make_branch_and_tree('.')
3535.6.2 by James Westby
Fixes from review. Thanks Aaron and John.
1244
        self.build_tree(['foo'])
3535.6.1 by James Westby
Handle a file turning in to a directory in TreeTransform.
1245
        wt.add(['foo'])
3590.3.1 by James Westby
Make TreeTransform update the inventory with new kind information.
1246
        wt.commit("one")
3535.6.1 by James Westby
Handle a file turning in to a directory in TreeTransform.
1247
        tt = TreeTransform(wt)
3535.6.2 by James Westby
Fixes from review. Thanks Aaron and John.
1248
        self.addCleanup(tt.finalize)
3535.6.3 by James Westby
Fix the test to not create transform conflicts.
1249
        foo_trans_id = tt.trans_id_tree_path("foo")
1250
        tt.delete_contents(foo_trans_id)
1251
        tt.create_directory(foo_trans_id)
1252
        bar_trans_id = tt.trans_id_tree_path("foo/bar")
1253
        tt.create_file(["aa\n"], bar_trans_id)
1254
        tt.version_file("bar-1", bar_trans_id)
3535.6.2 by James Westby
Fixes from review. Thanks Aaron and John.
1255
        tt.apply()
3535.6.1 by James Westby
Handle a file turning in to a directory in TreeTransform.
1256
        self.failUnlessExists("foo/bar")
3590.3.2 by James Westby
Handle ->symlink changes as well.
1257
        wt.lock_read()
1258
        try:
1259
            self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1260
                    "directory")
1261
        finally:
1262
            wt.unlock()
3590.3.1 by James Westby
Make TreeTransform update the inventory with new kind information.
1263
        wt.commit("two")
1264
        changes = wt.changes_from(wt.basis_tree())
1265
        self.assertFalse(changes.has_changed(), changes)
3535.6.1 by James Westby
Handle a file turning in to a directory in TreeTransform.
1266
3590.3.2 by James Westby
Handle ->symlink changes as well.
1267
    def test_file_to_symlink(self):
3590.3.3 by James Westby
Make ->file changes work as well.
1268
        self.requireFeature(SymlinkFeature)
3590.3.2 by James Westby
Handle ->symlink changes as well.
1269
        wt = self.make_branch_and_tree('.')
1270
        self.build_tree(['foo'])
1271
        wt.add(['foo'])
1272
        wt.commit("one")
1273
        tt = TreeTransform(wt)
1274
        self.addCleanup(tt.finalize)
1275
        foo_trans_id = tt.trans_id_tree_path("foo")
1276
        tt.delete_contents(foo_trans_id)
1277
        tt.create_symlink("bar", foo_trans_id)
1278
        tt.apply()
1279
        self.failUnlessExists("foo")
1280
        wt.lock_read()
1281
        self.addCleanup(wt.unlock)
1282
        self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1283
                "symlink")
1284
3590.3.3 by James Westby
Make ->file changes work as well.
1285
    def test_dir_to_file(self):
1286
        wt = self.make_branch_and_tree('.')
1287
        self.build_tree(['foo/', 'foo/bar'])
1288
        wt.add(['foo', 'foo/bar'])
1289
        wt.commit("one")
1290
        tt = TreeTransform(wt)
1291
        self.addCleanup(tt.finalize)
1292
        foo_trans_id = tt.trans_id_tree_path("foo")
1293
        bar_trans_id = tt.trans_id_tree_path("foo/bar")
1294
        tt.delete_contents(foo_trans_id)
1295
        tt.delete_versioned(bar_trans_id)
1296
        tt.create_file(["aa\n"], foo_trans_id)
1297
        tt.apply()
1298
        self.failUnlessExists("foo")
1299
        wt.lock_read()
1300
        self.addCleanup(wt.unlock)
1301
        self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1302
                "file")
1303
3590.3.4 by James Westby
Add a test for creating hardlinks as well.
1304
    def test_dir_to_hardlink(self):
3590.3.5 by James Westby
Use HardlinkFeature for the hardlink test.
1305
        self.requireFeature(HardlinkFeature)
3590.3.4 by James Westby
Add a test for creating hardlinks as well.
1306
        wt = self.make_branch_and_tree('.')
1307
        self.build_tree(['foo/', 'foo/bar'])
1308
        wt.add(['foo', 'foo/bar'])
1309
        wt.commit("one")
1310
        tt = TreeTransform(wt)
1311
        self.addCleanup(tt.finalize)
1312
        foo_trans_id = tt.trans_id_tree_path("foo")
1313
        bar_trans_id = tt.trans_id_tree_path("foo/bar")
1314
        tt.delete_contents(foo_trans_id)
1315
        tt.delete_versioned(bar_trans_id)
1316
        self.build_tree(['baz'])
1317
        tt.create_hardlink("baz", foo_trans_id)
1318
        tt.apply()
1319
        self.failUnlessExists("foo")
1320
        self.failUnlessExists("baz")
1321
        wt.lock_read()
1322
        self.addCleanup(wt.unlock)
1323
        self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1324
                "file")
1325
3619.2.10 by Aaron Bentley
Compensate for stale entries in TT._needs_rename
1326
    def test_no_final_path(self):
1327
        transform, root = self.get_transform()
1328
        trans_id = transform.trans_id_file_id('foo')
1329
        transform.create_file('bar', trans_id)
1330
        transform.cancel_creation(trans_id)
1331
        transform.apply()
1332
3363.17.24 by Aaron Bentley
Implement create_by_tree
1333
    def test_create_from_tree(self):
1334
        tree1 = self.make_branch_and_tree('tree1')
1335
        self.build_tree_contents([('tree1/foo/',), ('tree1/bar', 'baz')])
1336
        tree1.add(['foo', 'bar'], ['foo-id', 'bar-id'])
1337
        tree2 = self.make_branch_and_tree('tree2')
1338
        tt = TreeTransform(tree2)
1339
        foo_trans_id = tt.create_path('foo', tt.root)
1340
        create_from_tree(tt, foo_trans_id, tree1, 'foo-id')
1341
        bar_trans_id = tt.create_path('bar', tt.root)
1342
        create_from_tree(tt, bar_trans_id, tree1, 'bar-id')
1343
        tt.apply()
1344
        self.assertEqual('directory', osutils.file_kind('tree2/foo'))
1345
        self.assertFileEqual('baz', 'tree2/bar')
1346
3363.17.25 by Aaron Bentley
remove get_inventory_entry, replace with create_from_tree
1347
    def test_create_from_tree_bytes(self):
1348
        """Provided lines are used instead of tree content."""
1349
        tree1 = self.make_branch_and_tree('tree1')
1350
        self.build_tree_contents([('tree1/foo', 'bar'),])
1351
        tree1.add('foo', 'foo-id')
1352
        tree2 = self.make_branch_and_tree('tree2')
1353
        tt = TreeTransform(tree2)
1354
        foo_trans_id = tt.create_path('foo', tt.root)
1355
        create_from_tree(tt, foo_trans_id, tree1, 'foo-id', bytes='qux')
1356
        tt.apply()
1357
        self.assertFileEqual('qux', 'tree2/foo')
1358
1359
    def test_create_from_tree_symlink(self):
3363.17.24 by Aaron Bentley
Implement create_by_tree
1360
        self.requireFeature(SymlinkFeature)
1361
        tree1 = self.make_branch_and_tree('tree1')
1362
        os.symlink('bar', 'tree1/foo')
1363
        tree1.add('foo', 'foo-id')
1364
        tt = TreeTransform(self.make_branch_and_tree('tree2'))
1365
        foo_trans_id = tt.create_path('foo', tt.root)
1366
        create_from_tree(tt, foo_trans_id, tree1, 'foo-id')
1367
        tt.apply()
1368
        self.assertEqual('bar', os.readlink('tree2/foo'))
1369
2502.1.3 by Aaron Bentley
Don't cause errors when creating contents for trans_ids with no parent/name
1370
1534.7.93 by Aaron Bentley
Added text merge test
1371
class TransformGroup(object):
3063.1.1 by Alexander Belchenko
Catch OSError 17 (file exists) in final phase of tree transform and show filename to user (#111758).
1372
1731.1.33 by Aaron Bentley
Revert no-special-root changes
1373
    def __init__(self, dirname, root_id):
1534.7.101 by Aaron Bentley
Got conflicts on symlinks working properly
1374
        self.name = dirname
1534.7.93 by Aaron Bentley
Added text merge test
1375
        os.mkdir(dirname)
1558.1.3 by Aaron Bentley
Fixed deprecated op use in test suite
1376
        self.wt = BzrDir.create_standalone_workingtree(dirname)
1731.1.33 by Aaron Bentley
Revert no-special-root changes
1377
        self.wt.set_root_id(root_id)
1558.1.3 by Aaron Bentley
Fixed deprecated op use in test suite
1378
        self.b = self.wt.branch
1534.7.93 by Aaron Bentley
Added text merge test
1379
        self.tt = TreeTransform(self.wt)
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
1380
        self.root = self.tt.trans_id_tree_file_id(self.wt.get_root_id())
1534.7.93 by Aaron Bentley
Added text merge test
1381
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
1382
1534.7.95 by Aaron Bentley
Added more text merge tests
1383
def conflict_text(tree, merge):
1384
    template = '%s TREE\n%s%s\n%s%s MERGE-SOURCE\n'
1385
    return template % ('<' * 7, tree, '=' * 7, merge, '>' * 7)
1386
1534.7.93 by Aaron Bentley
Added text merge test
1387
1388
class TestTransformMerge(TestCaseInTempDir):
3199.1.5 by Vincent Ladeuil
Fix two more leaking tmp dirs, by reworking TransformPreview lock handling.
1389
1534.7.93 by Aaron Bentley
Added text merge test
1390
    def test_text_merge(self):
2116.4.1 by John Arbash Meinel
Update file and revision id generators.
1391
        root_id = generate_ids.gen_root_id()
1731.1.33 by Aaron Bentley
Revert no-special-root changes
1392
        base = TransformGroup("base", root_id)
1534.7.93 by Aaron Bentley
Added text merge test
1393
        base.tt.new_file('a', base.root, 'a\nb\nc\nd\be\n', 'a')
1534.7.95 by Aaron Bentley
Added more text merge tests
1394
        base.tt.new_file('b', base.root, 'b1', 'b')
1395
        base.tt.new_file('c', base.root, 'c', 'c')
1396
        base.tt.new_file('d', base.root, 'd', 'd')
1397
        base.tt.new_file('e', base.root, 'e', 'e')
1398
        base.tt.new_file('f', base.root, 'f', 'f')
1534.7.96 by Aaron Bentley
Tested with BASE as directory
1399
        base.tt.new_directory('g', base.root, 'g')
1534.7.97 by Aaron Bentley
Ensured foo.BASE is a directory if there's a conflict
1400
        base.tt.new_directory('h', base.root, 'h')
1534.7.93 by Aaron Bentley
Added text merge test
1401
        base.tt.apply()
1731.1.33 by Aaron Bentley
Revert no-special-root changes
1402
        other = TransformGroup("other", root_id)
1534.7.93 by Aaron Bentley
Added text merge test
1403
        other.tt.new_file('a', other.root, 'y\nb\nc\nd\be\n', 'a')
1534.7.95 by Aaron Bentley
Added more text merge tests
1404
        other.tt.new_file('b', other.root, 'b2', 'b')
1405
        other.tt.new_file('c', other.root, 'c2', 'c')
1406
        other.tt.new_file('d', other.root, 'd', 'd')
1407
        other.tt.new_file('e', other.root, 'e2', 'e')
1408
        other.tt.new_file('f', other.root, 'f', 'f')
1534.7.96 by Aaron Bentley
Tested with BASE as directory
1409
        other.tt.new_file('g', other.root, 'g', 'g')
1534.7.97 by Aaron Bentley
Ensured foo.BASE is a directory if there's a conflict
1410
        other.tt.new_file('h', other.root, 'h\ni\nj\nk\n', 'h')
1534.7.99 by Aaron Bentley
Handle non-existent BASE properly
1411
        other.tt.new_file('i', other.root, 'h\ni\nj\nk\n', 'i')
1534.7.93 by Aaron Bentley
Added text merge test
1412
        other.tt.apply()
1731.1.33 by Aaron Bentley
Revert no-special-root changes
1413
        this = TransformGroup("this", root_id)
1534.7.93 by Aaron Bentley
Added text merge test
1414
        this.tt.new_file('a', this.root, 'a\nb\nc\nd\bz\n', 'a')
1534.7.95 by Aaron Bentley
Added more text merge tests
1415
        this.tt.new_file('b', this.root, 'b', 'b')
1416
        this.tt.new_file('c', this.root, 'c', 'c')
1417
        this.tt.new_file('d', this.root, 'd2', 'd')
1418
        this.tt.new_file('e', this.root, 'e2', 'e')
1419
        this.tt.new_file('f', this.root, 'f', 'f')
1534.7.96 by Aaron Bentley
Tested with BASE as directory
1420
        this.tt.new_file('g', this.root, 'g', 'g')
1534.7.97 by Aaron Bentley
Ensured foo.BASE is a directory if there's a conflict
1421
        this.tt.new_file('h', this.root, '1\n2\n3\n4\n', 'h')
1534.7.99 by Aaron Bentley
Handle non-existent BASE properly
1422
        this.tt.new_file('i', this.root, '1\n2\n3\n4\n', 'i')
1534.7.93 by Aaron Bentley
Added text merge test
1423
        this.tt.apply()
3008.1.11 by Michael Hudson
restore the default behaviour of Merge3Merger.__init__().
1424
        Merge3Merger(this.wt, this.wt, base.wt, other.wt)
3008.1.6 by Michael Hudson
chop up Merge3Merger.__init__ into pieces
1425
1534.7.95 by Aaron Bentley
Added more text merge tests
1426
        # textual merge
1534.7.93 by Aaron Bentley
Added text merge test
1427
        self.assertEqual(this.wt.get_file('a').read(), 'y\nb\nc\nd\bz\n')
1534.7.95 by Aaron Bentley
Added more text merge tests
1428
        # three-way text conflict
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
1429
        self.assertEqual(this.wt.get_file('b').read(),
1534.7.95 by Aaron Bentley
Added more text merge tests
1430
                         conflict_text('b', 'b2'))
1431
        # OTHER wins
1432
        self.assertEqual(this.wt.get_file('c').read(), 'c2')
1433
        # THIS wins
1434
        self.assertEqual(this.wt.get_file('d').read(), 'd2')
1435
        # Ambigious clean merge
1436
        self.assertEqual(this.wt.get_file('e').read(), 'e2')
1437
        # No change
1438
        self.assertEqual(this.wt.get_file('f').read(), 'f')
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
1439
        # Correct correct results when THIS == OTHER
1534.7.96 by Aaron Bentley
Tested with BASE as directory
1440
        self.assertEqual(this.wt.get_file('g').read(), 'g')
1534.7.97 by Aaron Bentley
Ensured foo.BASE is a directory if there's a conflict
1441
        # Text conflict when THIS & OTHER are text and BASE is dir
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
1442
        self.assertEqual(this.wt.get_file('h').read(),
1534.7.97 by Aaron Bentley
Ensured foo.BASE is a directory if there's a conflict
1443
                         conflict_text('1\n2\n3\n4\n', 'h\ni\nj\nk\n'))
1444
        self.assertEqual(this.wt.get_file_byname('h.THIS').read(),
1445
                         '1\n2\n3\n4\n')
1446
        self.assertEqual(this.wt.get_file_byname('h.OTHER').read(),
1447
                         'h\ni\nj\nk\n')
1448
        self.assertEqual(file_kind(this.wt.abspath('h.BASE')), 'directory')
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
1449
        self.assertEqual(this.wt.get_file('i').read(),
1534.7.99 by Aaron Bentley
Handle non-existent BASE properly
1450
                         conflict_text('1\n2\n3\n4\n', 'h\ni\nj\nk\n'))
1451
        self.assertEqual(this.wt.get_file_byname('i.THIS').read(),
1452
                         '1\n2\n3\n4\n')
1453
        self.assertEqual(this.wt.get_file_byname('i.OTHER').read(),
1454
                         'h\ni\nj\nk\n')
1455
        self.assertEqual(os.path.exists(this.wt.abspath('i.BASE')), False)
1534.7.192 by Aaron Bentley
Record hashes produced by merges
1456
        modified = ['a', 'b', 'c', 'h', 'i']
1457
        merge_modified = this.wt.merge_modified()
1458
        self.assertSubset(merge_modified, modified)
1459
        self.assertEqual(len(merge_modified), len(modified))
1460
        file(this.wt.id2abspath('a'), 'wb').write('booga')
1461
        modified.pop(0)
1462
        merge_modified = this.wt.merge_modified()
1463
        self.assertSubset(merge_modified, modified)
1464
        self.assertEqual(len(merge_modified), len(modified))
1558.12.10 by Aaron Bentley
Be robust when merge_hash file_id not in inventory
1465
        this.wt.remove('b')
2796.1.4 by Aaron Bentley
Fix up various test cases
1466
        this.wt.revert()
1534.7.101 by Aaron Bentley
Got conflicts on symlinks working properly
1467
1468
    def test_file_merge(self):
2949.5.1 by Alexander Belchenko
selftest: use SymlinkFeature instead of TestSkipped where appropriate
1469
        self.requireFeature(SymlinkFeature)
2116.4.1 by John Arbash Meinel
Update file and revision id generators.
1470
        root_id = generate_ids.gen_root_id()
1731.1.33 by Aaron Bentley
Revert no-special-root changes
1471
        base = TransformGroup("BASE", root_id)
1472
        this = TransformGroup("THIS", root_id)
1473
        other = TransformGroup("OTHER", root_id)
1534.7.101 by Aaron Bentley
Got conflicts on symlinks working properly
1474
        for tg in this, base, other:
1475
            tg.tt.new_directory('a', tg.root, 'a')
1476
            tg.tt.new_symlink('b', tg.root, 'b', 'b')
1477
            tg.tt.new_file('c', tg.root, 'c', 'c')
1478
            tg.tt.new_symlink('d', tg.root, tg.name, 'd')
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
1479
        targets = ((base, 'base-e', 'base-f', None, None),
1480
                   (this, 'other-e', 'this-f', 'other-g', 'this-h'),
1534.7.104 by Aaron Bentley
Fixed set_versioned, enhanced conflict testing
1481
                   (other, 'other-e', None, 'other-g', 'other-h'))
1482
        for tg, e_target, f_target, g_target, h_target in targets:
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
1483
            for link, target in (('e', e_target), ('f', f_target),
1534.7.104 by Aaron Bentley
Fixed set_versioned, enhanced conflict testing
1484
                                 ('g', g_target), ('h', h_target)):
1485
                if target is not None:
1486
                    tg.tt.new_symlink(link, tg.root, target, link)
1534.7.102 by Aaron Bentley
Deleted old pre-conflict contents
1487
1488
        for tg in this, base, other:
1534.7.101 by Aaron Bentley
Got conflicts on symlinks working properly
1489
            tg.tt.apply()
3008.1.11 by Michael Hudson
restore the default behaviour of Merge3Merger.__init__().
1490
        Merge3Merger(this.wt, this.wt, base.wt, other.wt)
1534.7.101 by Aaron Bentley
Got conflicts on symlinks working properly
1491
        self.assertIs(os.path.isdir(this.wt.abspath('a')), True)
1492
        self.assertIs(os.path.islink(this.wt.abspath('b')), True)
1493
        self.assertIs(os.path.isfile(this.wt.abspath('c')), True)
1494
        for suffix in ('THIS', 'BASE', 'OTHER'):
1495
            self.assertEqual(os.readlink(this.wt.abspath('d.'+suffix)), suffix)
1534.7.102 by Aaron Bentley
Deleted old pre-conflict contents
1496
        self.assertIs(os.path.lexists(this.wt.abspath('d')), False)
1534.7.104 by Aaron Bentley
Fixed set_versioned, enhanced conflict testing
1497
        self.assertEqual(this.wt.id2path('d'), 'd.OTHER')
1498
        self.assertEqual(this.wt.id2path('f'), 'f.THIS')
1534.7.102 by Aaron Bentley
Deleted old pre-conflict contents
1499
        self.assertEqual(os.readlink(this.wt.abspath('e')), 'other-e')
1500
        self.assertIs(os.path.lexists(this.wt.abspath('e.THIS')), False)
1501
        self.assertIs(os.path.lexists(this.wt.abspath('e.OTHER')), False)
1502
        self.assertIs(os.path.lexists(this.wt.abspath('e.BASE')), False)
1534.7.104 by Aaron Bentley
Fixed set_versioned, enhanced conflict testing
1503
        self.assertIs(os.path.lexists(this.wt.abspath('g')), True)
1504
        self.assertIs(os.path.lexists(this.wt.abspath('g.BASE')), False)
1505
        self.assertIs(os.path.lexists(this.wt.abspath('h')), False)
1506
        self.assertIs(os.path.lexists(this.wt.abspath('h.BASE')), False)
1507
        self.assertIs(os.path.lexists(this.wt.abspath('h.THIS')), True)
1508
        self.assertIs(os.path.lexists(this.wt.abspath('h.OTHER')), True)
1534.7.105 by Aaron Bentley
Got merge with rename working
1509
1510
    def test_filename_merge(self):
2116.4.1 by John Arbash Meinel
Update file and revision id generators.
1511
        root_id = generate_ids.gen_root_id()
1731.1.33 by Aaron Bentley
Revert no-special-root changes
1512
        base = TransformGroup("BASE", root_id)
1513
        this = TransformGroup("THIS", root_id)
1514
        other = TransformGroup("OTHER", root_id)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
1515
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a')
1534.7.105 by Aaron Bentley
Got merge with rename working
1516
                                   for t in [base, this, other]]
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
1517
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b')
1534.7.105 by Aaron Bentley
Got merge with rename working
1518
                                   for t in [base, this, other]]
1519
        base.tt.new_directory('c', base_a, 'c')
1520
        this.tt.new_directory('c1', this_a, 'c')
1521
        other.tt.new_directory('c', other_b, 'c')
1522
1523
        base.tt.new_directory('d', base_a, 'd')
1524
        this.tt.new_directory('d1', this_b, 'd')
1525
        other.tt.new_directory('d', other_a, 'd')
1526
1527
        base.tt.new_directory('e', base_a, 'e')
1528
        this.tt.new_directory('e', this_a, 'e')
1529
        other.tt.new_directory('e1', other_b, 'e')
1530
1531
        base.tt.new_directory('f', base_a, 'f')
1532
        this.tt.new_directory('f1', this_b, 'f')
1533
        other.tt.new_directory('f1', other_b, 'f')
1534
1535
        for tg in [this, base, other]:
1536
            tg.tt.apply()
3008.1.11 by Michael Hudson
restore the default behaviour of Merge3Merger.__init__().
1537
        Merge3Merger(this.wt, this.wt, base.wt, other.wt)
1534.7.176 by abentley
Fixed up tests for Windows
1538
        self.assertEqual(this.wt.id2path('c'), pathjoin('b/c1'))
1539
        self.assertEqual(this.wt.id2path('d'), pathjoin('b/d1'))
1540
        self.assertEqual(this.wt.id2path('e'), pathjoin('b/e1'))
1541
        self.assertEqual(this.wt.id2path('f'), pathjoin('b/f1'))
1534.7.105 by Aaron Bentley
Got merge with rename working
1542
1543
    def test_filename_merge_conflicts(self):
2116.4.1 by John Arbash Meinel
Update file and revision id generators.
1544
        root_id = generate_ids.gen_root_id()
1731.1.33 by Aaron Bentley
Revert no-special-root changes
1545
        base = TransformGroup("BASE", root_id)
1546
        this = TransformGroup("THIS", root_id)
1547
        other = TransformGroup("OTHER", root_id)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
1548
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a')
1534.7.105 by Aaron Bentley
Got merge with rename working
1549
                                   for t in [base, this, other]]
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
1550
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b')
1534.7.105 by Aaron Bentley
Got merge with rename working
1551
                                   for t in [base, this, other]]
1552
1553
        base.tt.new_file('g', base_a, 'g', 'g')
1554
        other.tt.new_file('g1', other_b, 'g1', 'g')
1555
1556
        base.tt.new_file('h', base_a, 'h', 'h')
1557
        this.tt.new_file('h1', this_b, 'h1', 'h')
1558
1559
        base.tt.new_file('i', base.root, 'i', 'i')
1534.7.153 by Aaron Bentley
Handled test cases involving symlinks
1560
        other.tt.new_directory('i1', this_b, 'i')
1534.7.105 by Aaron Bentley
Got merge with rename working
1561
1562
        for tg in [this, base, other]:
1563
            tg.tt.apply()
3008.1.11 by Michael Hudson
restore the default behaviour of Merge3Merger.__init__().
1564
        Merge3Merger(this.wt, this.wt, base.wt, other.wt)
1534.7.105 by Aaron Bentley
Got merge with rename working
1565
1534.7.176 by abentley
Fixed up tests for Windows
1566
        self.assertEqual(this.wt.id2path('g'), pathjoin('b/g1.OTHER'))
1534.7.105 by Aaron Bentley
Got merge with rename working
1567
        self.assertIs(os.path.lexists(this.wt.abspath('b/g1.BASE')), True)
1568
        self.assertIs(os.path.lexists(this.wt.abspath('b/g1.THIS')), False)
1534.7.176 by abentley
Fixed up tests for Windows
1569
        self.assertEqual(this.wt.id2path('h'), pathjoin('b/h1.THIS'))
1534.7.105 by Aaron Bentley
Got merge with rename working
1570
        self.assertIs(os.path.lexists(this.wt.abspath('b/h1.BASE')), True)
1571
        self.assertIs(os.path.lexists(this.wt.abspath('b/h1.OTHER')), False)
1534.7.176 by abentley
Fixed up tests for Windows
1572
        self.assertEqual(this.wt.id2path('i'), pathjoin('b/i1.OTHER'))
1534.7.183 by Aaron Bentley
Fixed build_tree with symlinks
1573
2027.1.1 by John Arbash Meinel
Fix bug #56549, and write a direct test that the right path is being statted
1574
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1575
class TestBuildTree(tests.TestCaseWithTransport):
1576
3006.2.2 by Alexander Belchenko
tests added.
1577
    def test_build_tree_with_symlinks(self):
2949.5.1 by Alexander Belchenko
selftest: use SymlinkFeature instead of TestSkipped where appropriate
1578
        self.requireFeature(SymlinkFeature)
1534.7.183 by Aaron Bentley
Fixed build_tree with symlinks
1579
        os.mkdir('a')
1580
        a = BzrDir.create_standalone_workingtree('a')
1581
        os.mkdir('a/foo')
1582
        file('a/foo/bar', 'wb').write('contents')
1583
        os.symlink('a/foo/bar', 'a/foo/baz')
1584
        a.add(['foo', 'foo/bar', 'foo/baz'])
1585
        a.commit('initial commit')
1586
        b = BzrDir.create_standalone_workingtree('b')
2255.2.194 by Robert Collins
[BROKEN] Many updates to stop using experimental formats in tests.
1587
        basis = a.basis_tree()
1588
        basis.lock_read()
1589
        self.addCleanup(basis.unlock)
1590
        build_tree(basis, b)
1534.7.183 by Aaron Bentley
Fixed build_tree with symlinks
1591
        self.assertIs(os.path.isdir('b/foo'), True)
1592
        self.assertEqual(file('b/foo/bar', 'rb').read(), "contents")
1593
        self.assertEqual(os.readlink('b/foo/baz'), 'a/foo/bar')
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1594
2100.3.21 by Aaron Bentley
Work on checking out by-reference trees
1595
    def test_build_with_references(self):
2255.2.194 by Robert Collins
[BROKEN] Many updates to stop using experimental formats in tests.
1596
        tree = self.make_branch_and_tree('source',
1597
            format='dirstate-with-subtree')
1598
        subtree = self.make_branch_and_tree('source/subtree',
1599
            format='dirstate-with-subtree')
2100.3.21 by Aaron Bentley
Work on checking out by-reference trees
1600
        tree.add_reference(subtree)
1601
        tree.commit('a revision')
1602
        tree.branch.create_checkout('target')
1603
        self.failUnlessExists('target')
1604
        self.failUnlessExists('target/subtree')
1605
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1606
    def test_file_conflict_handling(self):
1607
        """Ensure that when building trees, conflict handling is done"""
1608
        source = self.make_branch_and_tree('source')
1609
        target = self.make_branch_and_tree('target')
1610
        self.build_tree(['source/file', 'target/file'])
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1611
        source.add('file', 'new-file')
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1612
        source.commit('added file')
1613
        build_tree(source.basis_tree(), target)
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1614
        self.assertEqual([DuplicateEntry('Moved existing file to',
1615
                          'file.moved', 'file', None, 'new-file')],
1616
                         target.conflicts())
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1617
        target2 = self.make_branch_and_tree('target2')
1618
        target_file = file('target2/file', 'wb')
1619
        try:
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1620
            source_file = file('source/file', 'rb')
1621
            try:
1622
                target_file.write(source_file.read())
1623
            finally:
1624
                source_file.close()
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1625
        finally:
1626
            target_file.close()
1627
        build_tree(source.basis_tree(), target2)
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1628
        self.assertEqual([], target2.conflicts())
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1629
1630
    def test_symlink_conflict_handling(self):
1631
        """Ensure that when building trees, conflict handling is done"""
2949.5.1 by Alexander Belchenko
selftest: use SymlinkFeature instead of TestSkipped where appropriate
1632
        self.requireFeature(SymlinkFeature)
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1633
        source = self.make_branch_and_tree('source')
1634
        os.symlink('foo', 'source/symlink')
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1635
        source.add('symlink', 'new-symlink')
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1636
        source.commit('added file')
1637
        target = self.make_branch_and_tree('target')
1638
        os.symlink('bar', 'target/symlink')
1639
        build_tree(source.basis_tree(), target)
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1640
        self.assertEqual([DuplicateEntry('Moved existing file to',
1641
            'symlink.moved', 'symlink', None, 'new-symlink')],
1642
            target.conflicts())
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1643
        target = self.make_branch_and_tree('target2')
1644
        os.symlink('foo', 'target2/symlink')
1645
        build_tree(source.basis_tree(), target)
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1646
        self.assertEqual([], target.conflicts())
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
1647
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1648
    def test_directory_conflict_handling(self):
1649
        """Ensure that when building trees, conflict handling is done"""
1650
        source = self.make_branch_and_tree('source')
1651
        target = self.make_branch_and_tree('target')
1652
        self.build_tree(['source/dir1/', 'source/dir1/file', 'target/dir1/'])
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1653
        source.add(['dir1', 'dir1/file'], ['new-dir1', 'new-file'])
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1654
        source.commit('added file')
1655
        build_tree(source.basis_tree(), target)
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1656
        self.assertEqual([], target.conflicts())
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1657
        self.failUnlessExists('target/dir1/file')
1658
1659
        # Ensure contents are merged
1660
        target = self.make_branch_and_tree('target2')
1661
        self.build_tree(['target2/dir1/', 'target2/dir1/file2'])
1662
        build_tree(source.basis_tree(), target)
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1663
        self.assertEqual([], target.conflicts())
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1664
        self.failUnlessExists('target2/dir1/file2')
1665
        self.failUnlessExists('target2/dir1/file')
1666
1667
        # Ensure new contents are suppressed for existing branches
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1668
        target = self.make_branch_and_tree('target3')
1669
        self.make_branch('target3/dir1')
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1670
        self.build_tree(['target3/dir1/file2'])
1671
        build_tree(source.basis_tree(), target)
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1672
        self.failIfExists('target3/dir1/file')
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1673
        self.failUnlessExists('target3/dir1/file2')
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1674
        self.failUnlessExists('target3/dir1.diverted/file')
1675
        self.assertEqual([DuplicateEntry('Diverted to',
1676
            'dir1.diverted', 'dir1', 'new-dir1', None)],
1677
            target.conflicts())
1678
1679
        target = self.make_branch_and_tree('target4')
1680
        self.build_tree(['target4/dir1/'])
1681
        self.make_branch('target4/dir1/file')
1682
        build_tree(source.basis_tree(), target)
1683
        self.failUnlessExists('target4/dir1/file')
1684
        self.assertEqual('directory', file_kind('target4/dir1/file'))
1685
        self.failUnlessExists('target4/dir1/file.diverted')
1686
        self.assertEqual([DuplicateEntry('Diverted to',
1687
            'dir1/file.diverted', 'dir1/file', 'new-file', None)],
1688
            target.conflicts())
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1689
1690
    def test_mixed_conflict_handling(self):
1691
        """Ensure that when building trees, conflict handling is done"""
1692
        source = self.make_branch_and_tree('source')
1693
        target = self.make_branch_and_tree('target')
1694
        self.build_tree(['source/name', 'target/name/'])
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1695
        source.add('name', 'new-name')
1696
        source.commit('added file')
1697
        build_tree(source.basis_tree(), target)
1698
        self.assertEqual([DuplicateEntry('Moved existing file to',
1699
            'name.moved', 'name', None, 'new-name')], target.conflicts())
1700
1701
    def test_raises_in_populated(self):
1702
        source = self.make_branch_and_tree('source')
1703
        self.build_tree(['source/name'])
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1704
        source.add('name')
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1705
        source.commit('added name')
1706
        target = self.make_branch_and_tree('target')
1707
        self.build_tree(['target/name'])
1708
        target.add('name')
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
1709
        self.assertRaises(errors.WorkingTreeAlreadyPopulated,
2090.2.1 by Martin Pool
Fix some code which relies on assertions and breaks under python -O
1710
            build_tree, source.basis_tree(), target)
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1711
2502.1.1 by Aaron Bentley
Ensure renames only root children are renamed when building trees
1712
    def test_build_tree_rename_count(self):
1713
        source = self.make_branch_and_tree('source')
1714
        self.build_tree(['source/file1', 'source/dir1/'])
1715
        source.add(['file1', 'dir1'])
1716
        source.commit('add1')
1717
        target1 = self.make_branch_and_tree('target1')
2502.1.6 by Aaron Bentley
Update from review comments
1718
        transform_result = build_tree(source.basis_tree(), target1)
1719
        self.assertEqual(2, transform_result.rename_count)
2502.1.1 by Aaron Bentley
Ensure renames only root children are renamed when building trees
1720
1721
        self.build_tree(['source/dir1/file2'])
1722
        source.add(['dir1/file2'])
1723
        source.commit('add3')
1724
        target2 = self.make_branch_and_tree('target2')
2502.1.6 by Aaron Bentley
Update from review comments
1725
        transform_result = build_tree(source.basis_tree(), target2)
2502.1.1 by Aaron Bentley
Ensure renames only root children are renamed when building trees
1726
        # children of non-root directories should not be renamed
2502.1.6 by Aaron Bentley
Update from review comments
1727
        self.assertEqual(2, transform_result.rename_count)
2502.1.1 by Aaron Bentley
Ensure renames only root children are renamed when building trees
1728
3136.1.2 by Aaron Bentley
Implement hard-linking for build_tree
1729
    def create_ab_tree(self):
1730
        """Create a committed test tree with two files"""
1731
        source = self.make_branch_and_tree('source')
1732
        self.build_tree_contents([('source/file1', 'A')])
1733
        self.build_tree_contents([('source/file2', 'B')])
1734
        source.add(['file1', 'file2'], ['file1-id', 'file2-id'])
1735
        source.commit('commit files')
1736
        source.lock_write()
1737
        self.addCleanup(source.unlock)
1738
        return source
1739
3123.5.1 by Aaron Bentley
Make build-tree able to use an additional 'accelerator' tree
1740
    def test_build_tree_accelerator_tree(self):
3136.1.2 by Aaron Bentley
Implement hard-linking for build_tree
1741
        source = self.create_ab_tree()
3123.5.1 by Aaron Bentley
Make build-tree able to use an additional 'accelerator' tree
1742
        self.build_tree_contents([('source/file2', 'C')])
1743
        calls = []
1744
        real_source_get_file = source.get_file
1745
        def get_file(file_id, path=None):
1746
            calls.append(file_id)
1747
            return real_source_get_file(file_id, path)
1748
        source.get_file = get_file
1749
        target = self.make_branch_and_tree('target')
3123.5.19 by Aaron Bentley
Ensure content is exactly the same, when accelerator used
1750
        revision_tree = source.basis_tree()
1751
        revision_tree.lock_read()
1752
        self.addCleanup(revision_tree.unlock)
1753
        build_tree(revision_tree, target, source)
3123.5.1 by Aaron Bentley
Make build-tree able to use an additional 'accelerator' tree
1754
        self.assertEqual(['file1-id'], calls)
3123.5.19 by Aaron Bentley
Ensure content is exactly the same, when accelerator used
1755
        target.lock_read()
1756
        self.addCleanup(target.unlock)
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
1757
        self.assertEqual([], list(target.iter_changes(revision_tree)))
3123.5.1 by Aaron Bentley
Make build-tree able to use an additional 'accelerator' tree
1758
3123.5.4 by Aaron Bentley
Use an accelerator tree when branching, handle no-such-id correctly
1759
    def test_build_tree_accelerator_tree_missing_file(self):
3136.1.2 by Aaron Bentley
Implement hard-linking for build_tree
1760
        source = self.create_ab_tree()
3123.5.4 by Aaron Bentley
Use an accelerator tree when branching, handle no-such-id correctly
1761
        os.unlink('source/file1')
1762
        source.remove(['file2'])
1763
        target = self.make_branch_and_tree('target')
3123.5.19 by Aaron Bentley
Ensure content is exactly the same, when accelerator used
1764
        revision_tree = source.basis_tree()
1765
        revision_tree.lock_read()
1766
        self.addCleanup(revision_tree.unlock)
1767
        build_tree(revision_tree, target, source)
1768
        target.lock_read()
1769
        self.addCleanup(target.unlock)
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
1770
        self.assertEqual([], list(target.iter_changes(revision_tree)))
3123.5.4 by Aaron Bentley
Use an accelerator tree when branching, handle no-such-id correctly
1771
3123.5.16 by Aaron Bentley
Test handling of conversion to non-file
1772
    def test_build_tree_accelerator_wrong_kind(self):
3146.4.8 by Aaron Bentley
Add missing symlink requirement
1773
        self.requireFeature(SymlinkFeature)
3123.5.16 by Aaron Bentley
Test handling of conversion to non-file
1774
        source = self.make_branch_and_tree('source')
1775
        self.build_tree_contents([('source/file1', '')])
1776
        self.build_tree_contents([('source/file2', '')])
1777
        source.add(['file1', 'file2'], ['file1-id', 'file2-id'])
1778
        source.commit('commit files')
1779
        os.unlink('source/file2')
1780
        self.build_tree_contents([('source/file2/', 'C')])
1781
        os.unlink('source/file1')
1782
        os.symlink('file2', 'source/file1')
1783
        calls = []
1784
        real_source_get_file = source.get_file
1785
        def get_file(file_id, path=None):
1786
            calls.append(file_id)
1787
            return real_source_get_file(file_id, path)
1788
        source.get_file = get_file
1789
        target = self.make_branch_and_tree('target')
3123.5.19 by Aaron Bentley
Ensure content is exactly the same, when accelerator used
1790
        revision_tree = source.basis_tree()
1791
        revision_tree.lock_read()
1792
        self.addCleanup(revision_tree.unlock)
1793
        build_tree(revision_tree, target, source)
3123.5.16 by Aaron Bentley
Test handling of conversion to non-file
1794
        self.assertEqual([], calls)
3123.5.19 by Aaron Bentley
Ensure content is exactly the same, when accelerator used
1795
        target.lock_read()
1796
        self.addCleanup(target.unlock)
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
1797
        self.assertEqual([], list(target.iter_changes(revision_tree)))
3123.5.16 by Aaron Bentley
Test handling of conversion to non-file
1798
3136.1.2 by Aaron Bentley
Implement hard-linking for build_tree
1799
    def test_build_tree_hardlink(self):
1800
        self.requireFeature(HardlinkFeature)
1801
        source = self.create_ab_tree()
1802
        target = self.make_branch_and_tree('target')
1803
        revision_tree = source.basis_tree()
1804
        revision_tree.lock_read()
1805
        self.addCleanup(revision_tree.unlock)
1806
        build_tree(revision_tree, target, source, hardlink=True)
1807
        target.lock_read()
1808
        self.addCleanup(target.unlock)
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
1809
        self.assertEqual([], list(target.iter_changes(revision_tree)))
3136.1.2 by Aaron Bentley
Implement hard-linking for build_tree
1810
        source_stat = os.stat('source/file1')
1811
        target_stat = os.stat('target/file1')
1812
        self.assertEqual(source_stat, target_stat)
1813
1814
        # Explicitly disallowing hardlinks should prevent them.
1815
        target2 = self.make_branch_and_tree('target2')
1816
        build_tree(revision_tree, target2, source, hardlink=False)
1817
        target2.lock_read()
1818
        self.addCleanup(target2.unlock)
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
1819
        self.assertEqual([], list(target2.iter_changes(revision_tree)))
3136.1.2 by Aaron Bentley
Implement hard-linking for build_tree
1820
        source_stat = os.stat('source/file1')
1821
        target2_stat = os.stat('target2/file1')
1822
        self.assertNotEqual(source_stat, target2_stat)
1823
3137.1.1 by Aaron Bentley
Fix build_tree acceleration when file is moved in accelerator_tree
1824
    def test_build_tree_accelerator_tree_moved(self):
1825
        source = self.make_branch_and_tree('source')
1826
        self.build_tree_contents([('source/file1', 'A')])
1827
        source.add(['file1'], ['file1-id'])
1828
        source.commit('commit files')
1829
        source.rename_one('file1', 'file2')
1830
        source.lock_read()
1831
        self.addCleanup(source.unlock)
1832
        target = self.make_branch_and_tree('target')
1833
        revision_tree = source.basis_tree()
1834
        revision_tree.lock_read()
1835
        self.addCleanup(revision_tree.unlock)
1836
        build_tree(revision_tree, target, source)
1837
        target.lock_read()
1838
        self.addCleanup(target.unlock)
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
1839
        self.assertEqual([], list(target.iter_changes(revision_tree)))
3137.1.1 by Aaron Bentley
Fix build_tree acceleration when file is moved in accelerator_tree
1840
3136.1.2 by Aaron Bentley
Implement hard-linking for build_tree
1841
    def test_build_tree_hardlinks_preserve_execute(self):
1842
        self.requireFeature(HardlinkFeature)
1843
        source = self.create_ab_tree()
1844
        tt = TreeTransform(source)
1845
        trans_id = tt.trans_id_tree_file_id('file1-id')
1846
        tt.set_executability(True, trans_id)
1847
        tt.apply()
1848
        self.assertTrue(source.is_executable('file1-id'))
1849
        target = self.make_branch_and_tree('target')
1850
        revision_tree = source.basis_tree()
1851
        revision_tree.lock_read()
1852
        self.addCleanup(revision_tree.unlock)
1853
        build_tree(revision_tree, target, source, hardlink=True)
1854
        target.lock_read()
1855
        self.addCleanup(target.unlock)
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
1856
        self.assertEqual([], list(target.iter_changes(revision_tree)))
3136.1.2 by Aaron Bentley
Implement hard-linking for build_tree
1857
        self.assertTrue(source.is_executable('file1-id'))
1858
3453.2.4 by Aaron Bentley
Disable fast-path when conflicts are encountered
1859
    def test_case_insensitive_build_tree_inventory(self):
4241.14.12 by Vincent Ladeuil
Far too many modifications for a single commit, need to restart.
1860
        if (tests.CaseInsensitiveFilesystemFeature.available()
1861
            or tests.CaseInsCasePresFilenameFeature.available()):
4241.9.4 by Vincent Ladeuil
Fix test_case_insensitive_build_tree_inventory failure on OSX.
1862
            raise tests.UnavailableFeature('Fully case sensitive filesystem')
3453.2.4 by Aaron Bentley
Disable fast-path when conflicts are encountered
1863
        source = self.make_branch_and_tree('source')
1864
        self.build_tree(['source/file', 'source/FILE'])
1865
        source.add(['file', 'FILE'], ['lower-id', 'upper-id'])
1866
        source.commit('added files')
1867
        # Don't try this at home, kids!
1868
        # Force the tree to report that it is case insensitive
1869
        target = self.make_branch_and_tree('target')
1870
        target.case_sensitive = False
3453.2.6 by Aaron Bentley
Rename mutate_tree to delta_from_tree, add comment
1871
        build_tree(source.basis_tree(), target, source, delta_from_tree=True)
3453.2.4 by Aaron Bentley
Disable fast-path when conflicts are encountered
1872
        self.assertEqual('file.moved', target.id2path('lower-id'))
1873
        self.assertEqual('FILE', target.id2path('upper-id'))
1874
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1875
4526.8.1 by Aaron Bentley
Support committing a TreeTransform to a branch.
1876
class TestCommitTransform(tests.TestCaseWithTransport):
1877
1878
    def get_branch(self):
1879
        tree = self.make_branch_and_tree('tree')
1880
        tree.lock_write()
1881
        self.addCleanup(tree.unlock)
1882
        tree.commit('empty commit')
1883
        return tree.branch
1884
1885
    def get_branch_and_transform(self):
1886
        branch = self.get_branch()
1887
        tt = TransformPreview(branch.basis_tree())
1888
        self.addCleanup(tt.finalize)
1889
        return branch, tt
1890
1891
    def test_commit_wrong_basis(self):
1892
        branch = self.get_branch()
1893
        basis = branch.repository.revision_tree(
1894
            _mod_revision.NULL_REVISION)
1895
        tt = TransformPreview(basis)
1896
        self.addCleanup(tt.finalize)
4526.8.5 by Aaron Bentley
Updates from review.
1897
        e = self.assertRaises(ValueError, tt.commit, branch, '')
4526.8.1 by Aaron Bentley
Support committing a TreeTransform to a branch.
1898
        self.assertEqual('TreeTransform not based on branch basis: null:',
1899
                         str(e))
1900
1901
    def test_empy_commit(self):
1902
        branch, tt = self.get_branch_and_transform()
1903
        rev = tt.commit(branch, 'my message')
1904
        self.assertEqual(2, branch.revno())
1905
        repo = branch.repository
1906
        self.assertEqual('my message', repo.get_revision(rev).message)
1907
1908
    def test_merge_parents(self):
1909
        branch, tt = self.get_branch_and_transform()
1910
        rev = tt.commit(branch, 'my message', ['rev1b', 'rev1c'])
1911
        self.assertEqual(['rev1b', 'rev1c'],
1912
                         branch.basis_tree().get_parent_ids()[1:])
1913
4526.8.3 by Aaron Bentley
Clean up merge parent handling.
1914
    def test_first_commit(self):
1915
        branch = self.make_branch('branch')
1916
        branch.lock_write()
1917
        self.addCleanup(branch.unlock)
1918
        tt = TransformPreview(branch.basis_tree())
4526.9.23 by Robert Collins
Change the tree transform test_first_commit test to set a root id in the new tree, and workaround an apparent bug in TreeTransform._determine_path.
1919
        tt.new_directory('', ROOT_PARENT, 'TREE_ROOT')
4526.8.3 by Aaron Bentley
Clean up merge parent handling.
1920
        rev = tt.commit(branch, 'my message')
1921
        self.assertEqual([], branch.basis_tree().get_parent_ids())
4526.8.5 by Aaron Bentley
Updates from review.
1922
        self.assertNotEqual(_mod_revision.NULL_REVISION,
1923
                            branch.last_revision())
4526.8.3 by Aaron Bentley
Clean up merge parent handling.
1924
4526.8.5 by Aaron Bentley
Updates from review.
1925
    def test_first_commit_with_merge_parents(self):
4526.8.3 by Aaron Bentley
Clean up merge parent handling.
1926
        branch = self.make_branch('branch')
1927
        branch.lock_write()
1928
        self.addCleanup(branch.unlock)
1929
        tt = TransformPreview(branch.basis_tree())
4526.8.5 by Aaron Bentley
Updates from review.
1930
        e = self.assertRaises(ValueError, tt.commit, branch,
4526.8.3 by Aaron Bentley
Clean up merge parent handling.
1931
                          'my message', ['rev1b-id'])
4526.8.5 by Aaron Bentley
Updates from review.
1932
        self.assertEqual('Cannot supply merge parents for first commit.',
1933
                         str(e))
1934
        self.assertEqual(_mod_revision.NULL_REVISION, branch.last_revision())
4526.8.3 by Aaron Bentley
Clean up merge parent handling.
1935
4526.8.1 by Aaron Bentley
Support committing a TreeTransform to a branch.
1936
    def test_add_files(self):
1937
        branch, tt = self.get_branch_and_transform()
1938
        tt.new_file('file', tt.root, 'contents', 'file-id')
1939
        trans_id = tt.new_directory('dir', tt.root, 'dir-id')
1940
        tt.new_symlink('symlink', trans_id, 'target', 'symlink-id')
1941
        rev = tt.commit(branch, 'message')
1942
        tree = branch.basis_tree()
1943
        self.assertEqual('file', tree.id2path('file-id'))
1944
        self.assertEqual('contents', tree.get_file_text('file-id'))
1945
        self.assertEqual('dir', tree.id2path('dir-id'))
1946
        self.assertEqual('dir/symlink', tree.id2path('symlink-id'))
1947
        self.assertEqual('target', tree.get_symlink_target('symlink-id'))
1948
4526.8.2 by Aaron Bentley
Proved strict commit handling.
1949
    def test_add_unversioned(self):
1950
        branch, tt = self.get_branch_and_transform()
1951
        tt.new_file('file', tt.root, 'contents')
1952
        self.assertRaises(errors.StrictCommitFailed, tt.commit, branch,
1953
                          'message', strict=True)
1954
1955
    def test_modify_strict(self):
1956
        branch, tt = self.get_branch_and_transform()
1957
        tt.new_file('file', tt.root, 'contents', 'file-id')
1958
        tt.commit(branch, 'message', strict=True)
1959
        tt = TransformPreview(branch.basis_tree())
1960
        trans_id = tt.trans_id_file_id('file-id')
1961
        tt.delete_contents(trans_id)
1962
        tt.create_file('contents', trans_id)
1963
        tt.commit(branch, 'message', strict=True)
1964
4526.8.6 by Aaron Bentley
Check for malformed transforms before committing.
1965
    def test_commit_malformed(self):
1966
        """Committing a malformed transform should raise an exception.
1967
1968
        In this case, we are adding a file without adding its parent.
1969
        """
1970
        branch, tt = self.get_branch_and_transform()
1971
        parent_id = tt.trans_id_file_id('parent-id')
1972
        tt.new_file('file', parent_id, 'contents', 'file-id')
1973
        self.assertRaises(errors.MalformedTransform, tt.commit, branch,
1974
                          'message')
1975
4526.8.1 by Aaron Bentley
Support committing a TreeTransform to a branch.
1976
1534.10.28 by Aaron Bentley
Use numbered backup files
1977
class MockTransform(object):
1978
1979
    def has_named_child(self, by_parent, parent_id, name):
1980
        for child_id in by_parent[parent_id]:
1981
            if child_id == '0':
1982
                if name == "name~":
1983
                    return True
1534.10.29 by Aaron Bentley
Fixed backup numbering to match GNU standard better
1984
            elif name == "name.~%s~" % child_id:
1534.10.28 by Aaron Bentley
Use numbered backup files
1985
                return True
1986
        return False
1987
2502.1.1 by Aaron Bentley
Ensure renames only root children are renamed when building trees
1988
1534.10.28 by Aaron Bentley
Use numbered backup files
1989
class MockEntry(object):
1990
    def __init__(self):
1991
        object.__init__(self)
1992
        self.name = "name"
1993
3063.1.1 by Alexander Belchenko
Catch OSError 17 (file exists) in final phase of tree transform and show filename to user (#111758).
1994
1534.10.28 by Aaron Bentley
Use numbered backup files
1995
class TestGetBackupName(TestCase):
1996
    def test_get_backup_name(self):
1997
        tt = MockTransform()
1998
        name = get_backup_name(MockEntry(), {'a':[]}, 'a', tt)
1534.10.29 by Aaron Bentley
Fixed backup numbering to match GNU standard better
1999
        self.assertEqual(name, 'name.~1~')
2000
        name = get_backup_name(MockEntry(), {'a':['1']}, 'a', tt)
2001
        self.assertEqual(name, 'name.~2~')
1534.10.28 by Aaron Bentley
Use numbered backup files
2002
        name = get_backup_name(MockEntry(), {'a':['2']}, 'a', tt)
1534.10.29 by Aaron Bentley
Fixed backup numbering to match GNU standard better
2003
        self.assertEqual(name, 'name.~1~')
1534.10.28 by Aaron Bentley
Use numbered backup files
2004
        name = get_backup_name(MockEntry(), {'a':['2'], 'b':[]}, 'b', tt)
1534.10.29 by Aaron Bentley
Fixed backup numbering to match GNU standard better
2005
        self.assertEqual(name, 'name.~1~')
2006
        name = get_backup_name(MockEntry(), {'a':['1', '2', '3']}, 'a', tt)
2007
        self.assertEqual(name, 'name.~4~')
2733.2.1 by Aaron Bentley
Implement FileMover, to support TreeTransform rollback
2008
2009
2010
class TestFileMover(tests.TestCaseWithTransport):
2011
2012
    def test_file_mover(self):
2013
        self.build_tree(['a/', 'a/b', 'c/', 'c/d'])
2014
        mover = _FileMover()
2015
        mover.rename('a', 'q')
2016
        self.failUnlessExists('q')
2017
        self.failIfExists('a')
2733.2.12 by Aaron Bentley
Updates from review
2018
        self.failUnlessExists('q/b')
2019
        self.failUnlessExists('c')
2020
        self.failUnlessExists('c/d')
2733.2.1 by Aaron Bentley
Implement FileMover, to support TreeTransform rollback
2021
2733.2.5 by Aaron Bentley
Implement FileMover.pre_delete and FileMover.apply_deletions
2022
    def test_pre_delete_rollback(self):
2023
        self.build_tree(['a/'])
2024
        mover = _FileMover()
2025
        mover.pre_delete('a', 'q')
2026
        self.failUnlessExists('q')
2027
        self.failIfExists('a')
2028
        mover.rollback()
2029
        self.failIfExists('q')
2030
        self.failUnlessExists('a')
2031
2032
    def test_apply_deletions(self):
2733.2.12 by Aaron Bentley
Updates from review
2033
        self.build_tree(['a/', 'b/'])
2733.2.5 by Aaron Bentley
Implement FileMover.pre_delete and FileMover.apply_deletions
2034
        mover = _FileMover()
2035
        mover.pre_delete('a', 'q')
2733.2.12 by Aaron Bentley
Updates from review
2036
        mover.pre_delete('b', 'r')
2733.2.5 by Aaron Bentley
Implement FileMover.pre_delete and FileMover.apply_deletions
2037
        self.failUnlessExists('q')
2733.2.12 by Aaron Bentley
Updates from review
2038
        self.failUnlessExists('r')
2733.2.5 by Aaron Bentley
Implement FileMover.pre_delete and FileMover.apply_deletions
2039
        self.failIfExists('a')
2733.2.12 by Aaron Bentley
Updates from review
2040
        self.failIfExists('b')
2733.2.5 by Aaron Bentley
Implement FileMover.pre_delete and FileMover.apply_deletions
2041
        mover.apply_deletions()
2042
        self.failIfExists('q')
2733.2.12 by Aaron Bentley
Updates from review
2043
        self.failIfExists('r')
2733.2.5 by Aaron Bentley
Implement FileMover.pre_delete and FileMover.apply_deletions
2044
        self.failIfExists('a')
2733.2.12 by Aaron Bentley
Updates from review
2045
        self.failIfExists('b')
2733.2.5 by Aaron Bentley
Implement FileMover.pre_delete and FileMover.apply_deletions
2046
2733.2.1 by Aaron Bentley
Implement FileMover, to support TreeTransform rollback
2047
    def test_file_mover_rollback(self):
2048
        self.build_tree(['a/', 'a/b', 'c/', 'c/d/', 'c/e/'])
2049
        mover = _FileMover()
2050
        mover.rename('c/d', 'c/f')
2051
        mover.rename('c/e', 'c/d')
2052
        try:
2053
            mover.rename('a', 'c')
3063.1.3 by Aaron Bentley
Update for Linux
2054
        except errors.FileExists, e:
2733.2.1 by Aaron Bentley
Implement FileMover, to support TreeTransform rollback
2055
            mover.rollback()
2056
        self.failUnlessExists('a')
2057
        self.failUnlessExists('c/d')
2733.2.3 by Aaron Bentley
Test tranform rollback
2058
2059
2060
class Bogus(Exception):
2061
    pass
2062
2063
2064
class TestTransformRollback(tests.TestCaseWithTransport):
2065
2066
    class ExceptionFileMover(_FileMover):
2067
2733.2.4 by Aaron Bentley
Test transform rollback when renaming into place
2068
        def __init__(self, bad_source=None, bad_target=None):
2069
            _FileMover.__init__(self)
2070
            self.bad_source = bad_source
2071
            self.bad_target = bad_target
2072
2733.2.3 by Aaron Bentley
Test tranform rollback
2073
        def rename(self, source, target):
2733.2.4 by Aaron Bentley
Test transform rollback when renaming into place
2074
            if (self.bad_source is not None and
2075
                source.endswith(self.bad_source)):
2076
                raise Bogus
2077
            elif (self.bad_target is not None and
2078
                target.endswith(self.bad_target)):
2733.2.3 by Aaron Bentley
Test tranform rollback
2079
                raise Bogus
2080
            else:
2081
                _FileMover.rename(self, source, target)
2082
2083
    def test_rollback_rename(self):
2084
        tree = self.make_branch_and_tree('.')
2085
        self.build_tree(['a/', 'a/b'])
2086
        tt = TreeTransform(tree)
2087
        self.addCleanup(tt.finalize)
2088
        a_id = tt.trans_id_tree_path('a')
2089
        tt.adjust_path('c', tt.root, a_id)
2090
        tt.adjust_path('d', a_id, tt.trans_id_tree_path('a/b'))
2733.2.4 by Aaron Bentley
Test transform rollback when renaming into place
2091
        self.assertRaises(Bogus, tt.apply,
2092
                          _mover=self.ExceptionFileMover(bad_source='a'))
2093
        self.failUnlessExists('a')
2094
        self.failUnlessExists('a/b')
2095
        tt.apply()
2096
        self.failUnlessExists('c')
2097
        self.failUnlessExists('c/d')
2098
2099
    def test_rollback_rename_into_place(self):
2100
        tree = self.make_branch_and_tree('.')
2101
        self.build_tree(['a/', 'a/b'])
2102
        tt = TreeTransform(tree)
2103
        self.addCleanup(tt.finalize)
2104
        a_id = tt.trans_id_tree_path('a')
2105
        tt.adjust_path('c', tt.root, a_id)
2106
        tt.adjust_path('d', a_id, tt.trans_id_tree_path('a/b'))
2107
        self.assertRaises(Bogus, tt.apply,
2108
                          _mover=self.ExceptionFileMover(bad_target='c/d'))
2109
        self.failUnlessExists('a')
2110
        self.failUnlessExists('a/b')
2111
        tt.apply()
2112
        self.failUnlessExists('c')
2113
        self.failUnlessExists('c/d')
2733.2.6 by Aaron Bentley
Make TreeTransform commits rollbackable
2114
2115
    def test_rollback_deletion(self):
2116
        tree = self.make_branch_and_tree('.')
2117
        self.build_tree(['a/', 'a/b'])
2118
        tt = TreeTransform(tree)
2119
        self.addCleanup(tt.finalize)
2120
        a_id = tt.trans_id_tree_path('a')
2121
        tt.delete_contents(a_id)
2122
        tt.adjust_path('d', tt.root, tt.trans_id_tree_path('a/b'))
2123
        self.assertRaises(Bogus, tt.apply,
2124
                          _mover=self.ExceptionFileMover(bad_target='d'))
2125
        self.failUnlessExists('a')
2126
        self.failUnlessExists('a/b')
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
2127
1551.19.6 by Aaron Bentley
Revert doesn't crash restoring a file from a deleted directory
2128
    def test_resolve_no_parent(self):
2129
        wt = self.make_branch_and_tree('.')
2130
        tt = TreeTransform(wt)
2131
        self.addCleanup(tt.finalize)
2132
        parent = tt.trans_id_file_id('parent-id')
2133
        tt.new_file('file', parent, 'Contents')
2134
        resolve_conflicts(tt)
3008.1.13 by Michael Hudson
merge bzr.dev
2135
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
2136
3363.19.1 by Aaron Bentley
Make PreviewTree.iter_changes accept all options.
2137
A_ENTRY = ('a-id', ('a', 'a'), True, (True, True),
2138
                  ('TREE_ROOT', 'TREE_ROOT'), ('a', 'a'), ('file', 'file'),
2139
                  (False, False))
2140
ROOT_ENTRY = ('TREE_ROOT', ('', ''), False, (True, True), (None, None),
2141
              ('', ''), ('directory', 'directory'), (False, None))
2142
2143
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
2144
class TestTransformPreview(tests.TestCaseWithTransport):
2145
2146
    def create_tree(self):
2147
        tree = self.make_branch_and_tree('.')
2148
        self.build_tree_contents([('a', 'content 1')])
4600.3.1 by Robert Collins
Set tree root ID in tree transform tests that don't care about the root id.
2149
        tree.set_root_id('TREE_ROOT')
3008.2.1 by Aaron Bentley
Ensure conflict resolution works
2150
        tree.add('a', 'a-id')
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
2151
        tree.commit('rev1', rev_id='rev1')
2152
        return tree.branch.repository.revision_tree('rev1')
2153
3008.1.18 by Aaron Bentley
Get supported PreviewTree functionality under test
2154
    def get_empty_preview(self):
2155
        repository = self.make_repository('repo')
2156
        tree = repository.revision_tree(_mod_revision.NULL_REVISION)
3199.1.4 by Vincent Ladeuil
Fix 16 leaked tmp dirs. Probably indicates a lock handling problem with TransformPreview
2157
        preview = TransformPreview(tree)
3199.1.5 by Vincent Ladeuil
Fix two more leaking tmp dirs, by reworking TransformPreview lock handling.
2158
        self.addCleanup(preview.finalize)
3199.1.4 by Vincent Ladeuil
Fix 16 leaked tmp dirs. Probably indicates a lock handling problem with TransformPreview
2159
        return preview
2160
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
2161
    def test_transform_preview(self):
2162
        revision_tree = self.create_tree()
2163
        preview = TransformPreview(revision_tree)
3199.1.5 by Vincent Ladeuil
Fix two more leaking tmp dirs, by reworking TransformPreview lock handling.
2164
        self.addCleanup(preview.finalize)
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
2165
2166
    def test_transform_preview_tree(self):
2167
        revision_tree = self.create_tree()
2168
        preview = TransformPreview(revision_tree)
3199.1.5 by Vincent Ladeuil
Fix two more leaking tmp dirs, by reworking TransformPreview lock handling.
2169
        self.addCleanup(preview.finalize)
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
2170
        preview.get_preview_tree()
2171
3008.1.5 by Michael Hudson
a more precise test
2172
    def test_transform_new_file(self):
2173
        revision_tree = self.create_tree()
2174
        preview = TransformPreview(revision_tree)
3199.1.5 by Vincent Ladeuil
Fix two more leaking tmp dirs, by reworking TransformPreview lock handling.
2175
        self.addCleanup(preview.finalize)
3008.1.5 by Michael Hudson
a more precise test
2176
        preview.new_file('file2', preview.root, 'content B\n', 'file2-id')
2177
        preview_tree = preview.get_preview_tree()
2178
        self.assertEqual(preview_tree.kind('file2-id'), 'file')
2179
        self.assertEqual(
2180
            preview_tree.get_file('file2-id').read(), 'content B\n')
2181
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
2182
    def test_diff_preview_tree(self):
2183
        revision_tree = self.create_tree()
2184
        preview = TransformPreview(revision_tree)
3199.1.5 by Vincent Ladeuil
Fix two more leaking tmp dirs, by reworking TransformPreview lock handling.
2185
        self.addCleanup(preview.finalize)
3008.1.4 by Michael Hudson
Merge test enhancements
2186
        preview.new_file('file2', preview.root, 'content B\n', 'file2-id')
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
2187
        preview_tree = preview.get_preview_tree()
2188
        out = StringIO()
2189
        show_diff_trees(revision_tree, preview_tree, out)
3008.1.4 by Michael Hudson
Merge test enhancements
2190
        lines = out.getvalue().splitlines()
2191
        self.assertEqual(lines[0], "=== added file 'file2'")
2192
        # 3 lines of diff administrivia
2193
        self.assertEqual(lines[4], "+content B")
3008.2.1 by Aaron Bentley
Ensure conflict resolution works
2194
2195
    def test_transform_conflicts(self):
2196
        revision_tree = self.create_tree()
2197
        preview = TransformPreview(revision_tree)
3199.1.5 by Vincent Ladeuil
Fix two more leaking tmp dirs, by reworking TransformPreview lock handling.
2198
        self.addCleanup(preview.finalize)
3008.2.1 by Aaron Bentley
Ensure conflict resolution works
2199
        preview.new_file('a', preview.root, 'content 2')
2200
        resolve_conflicts(preview)
2201
        trans_id = preview.trans_id_file_id('a-id')
2202
        self.assertEqual('a.moved', preview.final_name(trans_id))
3008.1.17 by Aaron Bentley
Test unused parameters of preview_tree._iter_changes
2203
3008.1.31 by Aaron Bentley
Split PreviewTree._iter_changes parameter tests into smaller tests
2204
    def get_tree_and_preview_tree(self):
3008.1.17 by Aaron Bentley
Test unused parameters of preview_tree._iter_changes
2205
        revision_tree = self.create_tree()
2206
        preview = TransformPreview(revision_tree)
3199.1.5 by Vincent Ladeuil
Fix two more leaking tmp dirs, by reworking TransformPreview lock handling.
2207
        self.addCleanup(preview.finalize)
3008.1.17 by Aaron Bentley
Test unused parameters of preview_tree._iter_changes
2208
        a_trans_id = preview.trans_id_file_id('a-id')
2209
        preview.delete_contents(a_trans_id)
2210
        preview.create_file('b content', a_trans_id)
2211
        preview_tree = preview.get_preview_tree()
3008.1.31 by Aaron Bentley
Split PreviewTree._iter_changes parameter tests into smaller tests
2212
        return revision_tree, preview_tree
2213
2214
    def test_iter_changes(self):
2215
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
2216
        root = revision_tree.inventory.root.file_id
3008.1.17 by Aaron Bentley
Test unused parameters of preview_tree._iter_changes
2217
        self.assertEqual([('a-id', ('a', 'a'), True, (True, True),
2218
                          (root, root), ('a', 'a'), ('file', 'file'),
2219
                          (False, False))],
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
2220
                          list(preview_tree.iter_changes(revision_tree)))
3008.1.31 by Aaron Bentley
Split PreviewTree._iter_changes parameter tests into smaller tests
2221
3363.19.1 by Aaron Bentley
Make PreviewTree.iter_changes accept all options.
2222
    def test_include_unchanged_succeeds(self):
3008.1.31 by Aaron Bentley
Split PreviewTree._iter_changes parameter tests into smaller tests
2223
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
3363.19.1 by Aaron Bentley
Make PreviewTree.iter_changes accept all options.
2224
        changes = preview_tree.iter_changes(revision_tree,
2225
                                            include_unchanged=True)
2226
        root = revision_tree.inventory.root.file_id
2227
2228
        self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
3008.1.31 by Aaron Bentley
Split PreviewTree._iter_changes parameter tests into smaller tests
2229
2230
    def test_specific_files(self):
2231
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
3363.19.1 by Aaron Bentley
Make PreviewTree.iter_changes accept all options.
2232
        changes = preview_tree.iter_changes(revision_tree,
2233
                                            specific_files=[''])
2234
        self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
3008.1.31 by Aaron Bentley
Split PreviewTree._iter_changes parameter tests into smaller tests
2235
3363.19.1 by Aaron Bentley
Make PreviewTree.iter_changes accept all options.
2236
    def test_want_unversioned(self):
3008.1.31 by Aaron Bentley
Split PreviewTree._iter_changes parameter tests into smaller tests
2237
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
3363.19.1 by Aaron Bentley
Make PreviewTree.iter_changes accept all options.
2238
        changes = preview_tree.iter_changes(revision_tree,
2239
                                            want_unversioned=True)
2240
        self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
3008.1.17 by Aaron Bentley
Test unused parameters of preview_tree._iter_changes
2241
3008.1.31 by Aaron Bentley
Split PreviewTree._iter_changes parameter tests into smaller tests
2242
    def test_ignore_extra_trees_no_specific_files(self):
3008.1.17 by Aaron Bentley
Test unused parameters of preview_tree._iter_changes
2243
        # extra_trees is harmless without specific_files, so we'll silently
2244
        # accept it, even though we won't use it.
3008.1.31 by Aaron Bentley
Split PreviewTree._iter_changes parameter tests into smaller tests
2245
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
2246
        preview_tree.iter_changes(revision_tree, extra_trees=[preview_tree])
3008.1.31 by Aaron Bentley
Split PreviewTree._iter_changes parameter tests into smaller tests
2247
2248
    def test_ignore_require_versioned_no_specific_files(self):
3008.1.17 by Aaron Bentley
Test unused parameters of preview_tree._iter_changes
2249
        # require_versioned is meaningless without specific_files.
3008.1.31 by Aaron Bentley
Split PreviewTree._iter_changes parameter tests into smaller tests
2250
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
2251
        preview_tree.iter_changes(revision_tree, require_versioned=False)
3008.1.31 by Aaron Bentley
Split PreviewTree._iter_changes parameter tests into smaller tests
2252
2253
    def test_ignore_pb(self):
3008.1.17 by Aaron Bentley
Test unused parameters of preview_tree._iter_changes
2254
        # pb could be supported, but TT.iter_changes doesn't support it.
3008.1.31 by Aaron Bentley
Split PreviewTree._iter_changes parameter tests into smaller tests
2255
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
2256
        preview_tree.iter_changes(revision_tree, pb=progress.DummyProgress())
3008.1.18 by Aaron Bentley
Get supported PreviewTree functionality under test
2257
2258
    def test_kind(self):
2259
        revision_tree = self.create_tree()
2260
        preview = TransformPreview(revision_tree)
3199.1.5 by Vincent Ladeuil
Fix two more leaking tmp dirs, by reworking TransformPreview lock handling.
2261
        self.addCleanup(preview.finalize)
3008.1.18 by Aaron Bentley
Get supported PreviewTree functionality under test
2262
        preview.new_file('file', preview.root, 'contents', 'file-id')
2263
        preview.new_directory('directory', preview.root, 'dir-id')
2264
        preview_tree = preview.get_preview_tree()
2265
        self.assertEqual('file', preview_tree.kind('file-id'))
2266
        self.assertEqual('directory', preview_tree.kind('dir-id'))
2267
2268
    def test_get_file_mtime(self):
2269
        preview = self.get_empty_preview()
2270
        file_trans_id = preview.new_file('file', preview.root, 'contents',
2271
                                         'file-id')
2272
        limbo_path = preview._limbo_name(file_trans_id)
2273
        preview_tree = preview.get_preview_tree()
2274
        self.assertEqual(os.stat(limbo_path).st_mtime,
2275
                         preview_tree.get_file_mtime('file-id'))
2276
2277
    def test_get_file(self):
2278
        preview = self.get_empty_preview()
2279
        preview.new_file('file', preview.root, 'contents', 'file-id')
2280
        preview_tree = preview.get_preview_tree()
2281
        tree_file = preview_tree.get_file('file-id')
2282
        try:
2283
            self.assertEqual('contents', tree_file.read())
2284
        finally:
2285
            tree_file.close()
3228.1.2 by James Henstridge
Simplify test, and move it down to be next to the other _PreviewTree tests.
2286
2287
    def test_get_symlink_target(self):
2288
        self.requireFeature(SymlinkFeature)
2289
        preview = self.get_empty_preview()
2290
        preview.new_symlink('symlink', preview.root, 'target', 'symlink-id')
2291
        preview_tree = preview.get_preview_tree()
2292
        self.assertEqual('target',
2293
                         preview_tree.get_symlink_target('symlink-id'))
3363.2.18 by Aaron Bentley
Implement correct all_file_ids for PreviewTree
2294
2295
    def test_all_file_ids(self):
2296
        tree = self.make_branch_and_tree('tree')
2297
        self.build_tree(['tree/a', 'tree/b', 'tree/c'])
2298
        tree.add(['a', 'b', 'c'], ['a-id', 'b-id', 'c-id'])
2299
        preview = TransformPreview(tree)
2300
        self.addCleanup(preview.finalize)
2301
        preview.unversion_file(preview.trans_id_file_id('b-id'))
2302
        c_trans_id = preview.trans_id_file_id('c-id')
2303
        preview.unversion_file(c_trans_id)
2304
        preview.version_file('c-id', c_trans_id)
2305
        preview_tree = preview.get_preview_tree()
2306
        self.assertEqual(set(['a-id', 'c-id', tree.get_root_id()]),
2307
                         preview_tree.all_file_ids())
3363.2.19 by Aaron Bentley
Make PreviewTree.path2id correct
2308
2309
    def test_path2id_deleted_unchanged(self):
2310
        tree = self.make_branch_and_tree('tree')
2311
        self.build_tree(['tree/unchanged', 'tree/deleted'])
2312
        tree.add(['unchanged', 'deleted'], ['unchanged-id', 'deleted-id'])
2313
        preview = TransformPreview(tree)
2314
        self.addCleanup(preview.finalize)
2315
        preview.unversion_file(preview.trans_id_file_id('deleted-id'))
2316
        preview_tree = preview.get_preview_tree()
2317
        self.assertEqual('unchanged-id', preview_tree.path2id('unchanged'))
2318
        self.assertIs(None, preview_tree.path2id('deleted'))
2319
2320
    def test_path2id_created(self):
2321
        tree = self.make_branch_and_tree('tree')
2322
        self.build_tree(['tree/unchanged'])
2323
        tree.add(['unchanged'], ['unchanged-id'])
2324
        preview = TransformPreview(tree)
2325
        self.addCleanup(preview.finalize)
2326
        preview.new_file('new', preview.trans_id_file_id('unchanged-id'),
2327
            'contents', 'new-id')
2328
        preview_tree = preview.get_preview_tree()
2329
        self.assertEqual('new-id', preview_tree.path2id('unchanged/new'))
2330
2331
    def test_path2id_moved(self):
2332
        tree = self.make_branch_and_tree('tree')
2333
        self.build_tree(['tree/old_parent/', 'tree/old_parent/child'])
2334
        tree.add(['old_parent', 'old_parent/child'],
2335
                 ['old_parent-id', 'child-id'])
2336
        preview = TransformPreview(tree)
2337
        self.addCleanup(preview.finalize)
2338
        new_parent = preview.new_directory('new_parent', preview.root,
2339
                                           'new_parent-id')
2340
        preview.adjust_path('child', new_parent,
2341
                            preview.trans_id_file_id('child-id'))
2342
        preview_tree = preview.get_preview_tree()
2343
        self.assertIs(None, preview_tree.path2id('old_parent/child'))
2344
        self.assertEqual('child-id', preview_tree.path2id('new_parent/child'))
2345
2346
    def test_path2id_renamed_parent(self):
2347
        tree = self.make_branch_and_tree('tree')
2348
        self.build_tree(['tree/old_name/', 'tree/old_name/child'])
2349
        tree.add(['old_name', 'old_name/child'],
2350
                 ['parent-id', 'child-id'])
2351
        preview = TransformPreview(tree)
2352
        self.addCleanup(preview.finalize)
2353
        preview.adjust_path('new_name', preview.root,
2354
                            preview.trans_id_file_id('parent-id'))
2355
        preview_tree = preview.get_preview_tree()
2356
        self.assertIs(None, preview_tree.path2id('old_name/child'))
2357
        self.assertEqual('child-id', preview_tree.path2id('new_name/child'))
3363.2.21 by Aaron Bentley
Implement iter_entries_by_dir
2358
2359
    def assertMatchingIterEntries(self, tt, specific_file_ids=None):
2360
        preview_tree = tt.get_preview_tree()
2361
        preview_result = list(preview_tree.iter_entries_by_dir(
2362
                              specific_file_ids))
2363
        tree = tt._tree
2364
        tt.apply()
2365
        actual_result = list(tree.iter_entries_by_dir(specific_file_ids))
2366
        self.assertEqual(actual_result, preview_result)
2367
2368
    def test_iter_entries_by_dir_new(self):
2369
        tree = self.make_branch_and_tree('tree')
2370
        tt = TreeTransform(tree)
2371
        tt.new_file('new', tt.root, 'contents', 'new-id')
2372
        self.assertMatchingIterEntries(tt)
2373
2374
    def test_iter_entries_by_dir_deleted(self):
2375
        tree = self.make_branch_and_tree('tree')
2376
        self.build_tree(['tree/deleted'])
2377
        tree.add('deleted', 'deleted-id')
2378
        tt = TreeTransform(tree)
2379
        tt.delete_contents(tt.trans_id_file_id('deleted-id'))
2380
        self.assertMatchingIterEntries(tt)
2381
2382
    def test_iter_entries_by_dir_unversioned(self):
2383
        tree = self.make_branch_and_tree('tree')
2384
        self.build_tree(['tree/removed'])
2385
        tree.add('removed', 'removed-id')
2386
        tt = TreeTransform(tree)
2387
        tt.unversion_file(tt.trans_id_file_id('removed-id'))
2388
        self.assertMatchingIterEntries(tt)
2389
2390
    def test_iter_entries_by_dir_moved(self):
2391
        tree = self.make_branch_and_tree('tree')
2392
        self.build_tree(['tree/moved', 'tree/new_parent/'])
2393
        tree.add(['moved', 'new_parent'], ['moved-id', 'new_parent-id'])
2394
        tt = TreeTransform(tree)
2395
        tt.adjust_path('moved', tt.trans_id_file_id('new_parent-id'),
2396
                       tt.trans_id_file_id('moved-id'))
2397
        self.assertMatchingIterEntries(tt)
2398
2399
    def test_iter_entries_by_dir_specific_file_ids(self):
2400
        tree = self.make_branch_and_tree('tree')
2401
        tree.set_root_id('tree-root-id')
2402
        self.build_tree(['tree/parent/', 'tree/parent/child'])
2403
        tree.add(['parent', 'parent/child'], ['parent-id', 'child-id'])
2404
        tt = TreeTransform(tree)
2405
        self.assertMatchingIterEntries(tt, ['tree-root-id', 'child-id'])
3363.2.26 by Aaron Bentley
Get symlinks working
2406
2407
    def test_symlink_content_summary(self):
2408
        self.requireFeature(SymlinkFeature)
2409
        preview = self.get_empty_preview()
2410
        preview.new_symlink('path', preview.root, 'target', 'path-id')
2411
        summary = preview.get_preview_tree().path_content_summary('path')
2412
        self.assertEqual(('symlink', None, None, 'target'), summary)
3363.2.27 by Aaron Bentley
Make path_content_summary a core API
2413
2414
    def test_missing_content_summary(self):
2415
        preview = self.get_empty_preview()
2416
        summary = preview.get_preview_tree().path_content_summary('path')
2417
        self.assertEqual(('missing', None, None, None), summary)
2418
2419
    def test_deleted_content_summary(self):
2420
        tree = self.make_branch_and_tree('tree')
2421
        self.build_tree(['tree/path/'])
2422
        tree.add('path')
2423
        preview = TransformPreview(tree)
2424
        self.addCleanup(preview.finalize)
2425
        preview.delete_contents(preview.trans_id_tree_path('path'))
2426
        summary = preview.get_preview_tree().path_content_summary('path')
2427
        self.assertEqual(('missing', None, None, None), summary)
2428
3363.2.30 by Aaron Bentley
Improve execute bit testing
2429
    def test_file_content_summary_executable(self):
2430
        if not osutils.supports_executable():
2431
            raise TestNotApplicable()
2432
        preview = self.get_empty_preview()
2433
        path_id = preview.new_file('path', preview.root, 'contents', 'path-id')
2434
        preview.set_executability(True, path_id)
2435
        summary = preview.get_preview_tree().path_content_summary('path')
2436
        self.assertEqual(4, len(summary))
2437
        self.assertEqual('file', summary[0])
2438
        # size must be known
2439
        self.assertEqual(len('contents'), summary[1])
2440
        # executable
2441
        self.assertEqual(True, summary[2])
3363.2.31 by Aaron Bentley
Tweak tests
2442
        # will not have hash (not cheap to determine)
2443
        self.assertIs(None, summary[3])
3363.2.30 by Aaron Bentley
Improve execute bit testing
2444
2445
    def test_change_executability(self):
2446
        if not osutils.supports_executable():
2447
            raise TestNotApplicable()
2448
        tree = self.make_branch_and_tree('tree')
2449
        self.build_tree(['tree/path'])
2450
        tree.add('path')
2451
        preview = TransformPreview(tree)
2452
        self.addCleanup(preview.finalize)
2453
        path_id = preview.trans_id_tree_path('path')
2454
        preview.set_executability(True, path_id)
2455
        summary = preview.get_preview_tree().path_content_summary('path')
2456
        self.assertEqual(True, summary[2])
2457
3363.2.27 by Aaron Bentley
Make path_content_summary a core API
2458
    def test_file_content_summary_non_exec(self):
2459
        preview = self.get_empty_preview()
2460
        preview.new_file('path', preview.root, 'contents', 'path-id')
2461
        summary = preview.get_preview_tree().path_content_summary('path')
2462
        self.assertEqual(4, len(summary))
2463
        self.assertEqual('file', summary[0])
2464
        # size must be known
3363.2.30 by Aaron Bentley
Improve execute bit testing
2465
        self.assertEqual(len('contents'), summary[1])
3363.2.27 by Aaron Bentley
Make path_content_summary a core API
2466
        # not executable
2467
        if osutils.supports_executable():
2468
            self.assertEqual(False, summary[2])
2469
        else:
2470
            self.assertEqual(None, summary[2])
3363.2.31 by Aaron Bentley
Tweak tests
2471
        # will not have hash (not cheap to determine)
2472
        self.assertIs(None, summary[3])
3363.2.27 by Aaron Bentley
Make path_content_summary a core API
2473
2474
    def test_dir_content_summary(self):
2475
        preview = self.get_empty_preview()
2476
        preview.new_directory('path', preview.root, 'path-id')
2477
        summary = preview.get_preview_tree().path_content_summary('path')
2478
        self.assertEqual(('directory', None, None, None), summary)
2479
2480
    def test_tree_content_summary(self):
2481
        preview = self.get_empty_preview()
2482
        path = preview.new_directory('path', preview.root, 'path-id')
2483
        preview.set_tree_reference('rev-1', path)
2484
        summary = preview.get_preview_tree().path_content_summary('path')
2485
        self.assertEqual(4, len(summary))
2486
        self.assertEqual('tree-reference', summary[0])
3363.2.33 by Aaron Bentley
Implement PreviewTree.annotate_iter
2487
2488
    def test_annotate(self):
2489
        tree = self.make_branch_and_tree('tree')
2490
        self.build_tree_contents([('tree/file', 'a\n')])
2491
        tree.add('file', 'file-id')
2492
        tree.commit('a', rev_id='one')
2493
        self.build_tree_contents([('tree/file', 'a\nb\n')])
2494
        preview = TransformPreview(tree)
2495
        self.addCleanup(preview.finalize)
2496
        file_trans_id = preview.trans_id_file_id('file-id')
2497
        preview.delete_contents(file_trans_id)
2498
        preview.create_file('a\nb\nc\n', file_trans_id)
2499
        preview_tree = preview.get_preview_tree()
2500
        expected = [
2501
            ('one', 'a\n'),
2502
            ('me:', 'b\n'),
2503
            ('me:', 'c\n'),
2504
        ]
2505
        annotation = preview_tree.annotate_iter('file-id', 'me:')
2506
        self.assertEqual(expected, annotation)
2507
2508
    def test_annotate_missing(self):
2509
        preview = self.get_empty_preview()
2510
        preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
2511
        preview_tree = preview.get_preview_tree()
2512
        expected = [
2513
            ('me:', 'a\n'),
2514
            ('me:', 'b\n'),
2515
            ('me:', 'c\n'),
2516
         ]
2517
        annotation = preview_tree.annotate_iter('file-id', 'me:')
2518
        self.assertEqual(expected, annotation)
2519
3363.7.3 by Aaron Bentley
Add test that annotate correctly handles renames
2520
    def test_annotate_rename(self):
2521
        tree = self.make_branch_and_tree('tree')
2522
        self.build_tree_contents([('tree/file', 'a\n')])
2523
        tree.add('file', 'file-id')
2524
        tree.commit('a', rev_id='one')
2525
        preview = TransformPreview(tree)
2526
        self.addCleanup(preview.finalize)
2527
        file_trans_id = preview.trans_id_file_id('file-id')
2528
        preview.adjust_path('newname', preview.root, file_trans_id)
2529
        preview_tree = preview.get_preview_tree()
2530
        expected = [
2531
            ('one', 'a\n'),
2532
        ]
2533
        annotation = preview_tree.annotate_iter('file-id', 'me:')
2534
        self.assertEqual(expected, annotation)
2535
3363.2.33 by Aaron Bentley
Implement PreviewTree.annotate_iter
2536
    def test_annotate_deleted(self):
2537
        tree = self.make_branch_and_tree('tree')
2538
        self.build_tree_contents([('tree/file', 'a\n')])
2539
        tree.add('file', 'file-id')
2540
        tree.commit('a', rev_id='one')
2541
        self.build_tree_contents([('tree/file', 'a\nb\n')])
2542
        preview = TransformPreview(tree)
2543
        self.addCleanup(preview.finalize)
2544
        file_trans_id = preview.trans_id_file_id('file-id')
2545
        preview.delete_contents(file_trans_id)
2546
        preview_tree = preview.get_preview_tree()
2547
        annotation = preview_tree.annotate_iter('file-id', 'me:')
2548
        self.assertIs(None, annotation)
2549
3363.2.36 by Aaron Bentley
Fix PreviewTree.stored_kind
2550
    def test_stored_kind(self):
2551
        preview = self.get_empty_preview()
2552
        preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
2553
        preview_tree = preview.get_preview_tree()
2554
        self.assertEqual('file', preview_tree.stored_kind('file-id'))
3363.2.37 by Aaron Bentley
Fix is_executable
2555
2556
    def test_is_executable(self):
2557
        preview = self.get_empty_preview()
2558
        preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
2559
        preview.set_executability(True, preview.trans_id_file_id('file-id'))
2560
        preview_tree = preview.get_preview_tree()
2561
        self.assertEqual(True, preview_tree.is_executable('file-id'))
3363.9.1 by Aaron Bentley
Implement plan_merge, refactoring various bits
2562
3571.1.1 by Aaron Bentley
Allow set/get of parent_ids in PreviewTree
2563
    def test_get_set_parent_ids(self):
2564
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
2565
        self.assertEqual([], preview_tree.get_parent_ids())
2566
        preview_tree.set_parent_ids(['rev-1'])
2567
        self.assertEqual(['rev-1'], preview_tree.get_parent_ids())
2568
3363.9.1 by Aaron Bentley
Implement plan_merge, refactoring various bits
2569
    def test_plan_file_merge(self):
2570
        work_a = self.make_branch_and_tree('wta')
2571
        self.build_tree_contents([('wta/file', 'a\nb\nc\nd\n')])
2572
        work_a.add('file', 'file-id')
3363.9.7 by Aaron Bentley
Fix up to use set_parent_ids
2573
        base_id = work_a.commit('base version')
3363.9.1 by Aaron Bentley
Implement plan_merge, refactoring various bits
2574
        tree_b = work_a.bzrdir.sprout('wtb').open_workingtree()
2575
        preview = TransformPreview(work_a)
2576
        self.addCleanup(preview.finalize)
2577
        trans_id = preview.trans_id_file_id('file-id')
2578
        preview.delete_contents(trans_id)
2579
        preview.create_file('b\nc\nd\ne\n', trans_id)
2580
        self.build_tree_contents([('wtb/file', 'a\nc\nd\nf\n')])
2581
        tree_a = preview.get_preview_tree()
3363.9.7 by Aaron Bentley
Fix up to use set_parent_ids
2582
        tree_a.set_parent_ids([base_id])
3363.9.1 by Aaron Bentley
Implement plan_merge, refactoring various bits
2583
        self.assertEqual([
3363.9.5 by Aaron Bentley
Move killed-a from top to bottom
2584
            ('killed-a', 'a\n'),
3363.9.1 by Aaron Bentley
Implement plan_merge, refactoring various bits
2585
            ('killed-b', 'b\n'),
2586
            ('unchanged', 'c\n'),
2587
            ('unchanged', 'd\n'),
2588
            ('new-a', 'e\n'),
2589
            ('new-b', 'f\n'),
2590
        ], list(tree_a.plan_file_merge('file-id', tree_b)))
3363.9.8 by Aaron Bentley
Ensure plan_file_merge works with a RevisionTree as the basis
2591
2592
    def test_plan_file_merge_revision_tree(self):
2593
        work_a = self.make_branch_and_tree('wta')
2594
        self.build_tree_contents([('wta/file', 'a\nb\nc\nd\n')])
2595
        work_a.add('file', 'file-id')
2596
        base_id = work_a.commit('base version')
2597
        tree_b = work_a.bzrdir.sprout('wtb').open_workingtree()
2598
        preview = TransformPreview(work_a.basis_tree())
2599
        self.addCleanup(preview.finalize)
2600
        trans_id = preview.trans_id_file_id('file-id')
2601
        preview.delete_contents(trans_id)
2602
        preview.create_file('b\nc\nd\ne\n', trans_id)
2603
        self.build_tree_contents([('wtb/file', 'a\nc\nd\nf\n')])
2604
        tree_a = preview.get_preview_tree()
2605
        tree_a.set_parent_ids([base_id])
2606
        self.assertEqual([
2607
            ('killed-a', 'a\n'),
2608
            ('killed-b', 'b\n'),
2609
            ('unchanged', 'c\n'),
2610
            ('unchanged', 'd\n'),
2611
            ('new-a', 'e\n'),
2612
            ('new-b', 'f\n'),
2613
        ], list(tree_a.plan_file_merge('file-id', tree_b)))
3363.9.9 by Aaron Bentley
Implement walkdirs in terms of TreeTransform
2614
2615
    def test_walkdirs(self):
2616
        preview = self.get_empty_preview()
4634.57.3 by Aaron Bentley
Fix failing test.
2617
        root = preview.new_directory('', ROOT_PARENT, 'tree-root')
2618
        # FIXME: new_directory should mark root.
2619
        preview.adjust_path('', ROOT_PARENT, root)
3363.9.9 by Aaron Bentley
Implement walkdirs in terms of TreeTransform
2620
        preview_tree = preview.get_preview_tree()
2621
        file_trans_id = preview.new_file('a', preview.root, 'contents',
2622
                                         'a-id')
2623
        expected = [(('', 'tree-root'),
2624
                    [('a', 'a', 'file', None, 'a-id', 'file')])]
2625
        self.assertEqual(expected, list(preview_tree.walkdirs()))
3363.13.2 by Aaron Bentley
Test specific cases for PreviewTree.extras
2626
2627
    def test_extras(self):
2628
        work_tree = self.make_branch_and_tree('tree')
2629
        self.build_tree(['tree/removed-file', 'tree/existing-file',
2630
                         'tree/not-removed-file'])
2631
        work_tree.add(['removed-file', 'not-removed-file'])
2632
        preview = TransformPreview(work_tree)
3363.13.3 by Aaron Bentley
Add cleanup
2633
        self.addCleanup(preview.finalize)
3363.13.2 by Aaron Bentley
Test specific cases for PreviewTree.extras
2634
        preview.new_file('new-file', preview.root, 'contents')
2635
        preview.new_file('new-versioned-file', preview.root, 'contents',
2636
                         'new-versioned-id')
2637
        tree = preview.get_preview_tree()
2638
        preview.unversion_file(preview.trans_id_tree_path('removed-file'))
2639
        self.assertEqual(set(['new-file', 'removed-file', 'existing-file']),
2640
                         set(tree.extras()))
3363.17.1 by Aaron Bentley
Avoid inventory for merge and transform code
2641
3363.17.2 by Aaron Bentley
Add text checking
2642
    def test_merge_into_preview(self):
3363.17.1 by Aaron Bentley
Avoid inventory for merge and transform code
2643
        work_tree = self.make_branch_and_tree('tree')
3363.17.2 by Aaron Bentley
Add text checking
2644
        self.build_tree_contents([('tree/file','b\n')])
2645
        work_tree.add('file', 'file-id')
3363.17.1 by Aaron Bentley
Avoid inventory for merge and transform code
2646
        work_tree.commit('first commit')
2647
        child_tree = work_tree.bzrdir.sprout('child').open_workingtree()
3363.17.2 by Aaron Bentley
Add text checking
2648
        self.build_tree_contents([('child/file','b\nc\n')])
3363.17.1 by Aaron Bentley
Avoid inventory for merge and transform code
2649
        child_tree.commit('child commit')
2650
        child_tree.lock_write()
2651
        self.addCleanup(child_tree.unlock)
2652
        work_tree.lock_write()
2653
        self.addCleanup(work_tree.unlock)
2654
        preview = TransformPreview(work_tree)
2655
        self.addCleanup(preview.finalize)
3363.17.6 by Aaron Bentley
Improve test scenario
2656
        file_trans_id = preview.trans_id_file_id('file-id')
2657
        preview.delete_contents(file_trans_id)
2658
        preview.create_file('a\nb\n', file_trans_id)
3363.17.1 by Aaron Bentley
Avoid inventory for merge and transform code
2659
        pb = progress.DummyProgress()
4634.57.2 by Aaron Bentley
Fix failing test.
2660
        preview_tree = preview.get_preview_tree()
3363.17.1 by Aaron Bentley
Avoid inventory for merge and transform code
2661
        merger = Merger.from_revision_ids(pb, preview_tree,
2662
                                          child_tree.branch.last_revision(),
2663
                                          other_branch=child_tree.branch,
2664
                                          tree_branch=work_tree.branch)
2665
        merger.merge_type = Merge3Merger
2666
        tt = merger.make_merger().make_preview_transform()
3363.17.2 by Aaron Bentley
Add text checking
2667
        self.addCleanup(tt.finalize)
2668
        final_tree = tt.get_preview_tree()
2669
        self.assertEqual('a\nb\nc\n', final_tree.get_file_text('file-id'))
3363.17.17 by Aaron Bentley
Start testing merging PreviewTree as OTHER
2670
2671
    def test_merge_preview_into_workingtree(self):
2672
        tree = self.make_branch_and_tree('tree')
4600.3.1 by Robert Collins
Set tree root ID in tree transform tests that don't care about the root id.
2673
        tree.set_root_id('TREE_ROOT')
3363.17.17 by Aaron Bentley
Start testing merging PreviewTree as OTHER
2674
        tt = TransformPreview(tree)
2675
        self.addCleanup(tt.finalize)
2676
        tt.new_file('name', tt.root, 'content', 'file-id')
2677
        tree2 = self.make_branch_and_tree('tree2')
4600.3.1 by Robert Collins
Set tree root ID in tree transform tests that don't care about the root id.
2678
        tree2.set_root_id('TREE_ROOT')
3363.17.17 by Aaron Bentley
Start testing merging PreviewTree as OTHER
2679
        pb = progress.DummyProgress()
2680
        merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
2681
                                         pb, tree.basis_tree())
2682
        merger.merge_type = Merge3Merger
2683
        merger.do_merge()
3363.17.18 by Aaron Bentley
Fix is_executable for PreviewTree
2684
3363.17.21 by Aaron Bentley
Conflicts are handled when merging from preview trees
2685
    def test_merge_preview_into_workingtree_handles_conflicts(self):
2686
        tree = self.make_branch_and_tree('tree')
2687
        self.build_tree_contents([('tree/foo', 'bar')])
2688
        tree.add('foo', 'foo-id')
2689
        tree.commit('foo')
2690
        tt = TransformPreview(tree)
2691
        self.addCleanup(tt.finalize)
2692
        trans_id = tt.trans_id_file_id('foo-id')
2693
        tt.delete_contents(trans_id)
2694
        tt.create_file('baz', trans_id)
2695
        tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
2696
        self.build_tree_contents([('tree2/foo', 'qux')])
2697
        pb = progress.DummyProgress()
2698
        merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
2699
                                         pb, tree.basis_tree())
2700
        merger.merge_type = Merge3Merger
2701
        merger.do_merge()
2702
3363.17.18 by Aaron Bentley
Fix is_executable for PreviewTree
2703
    def test_is_executable(self):
2704
        tree = self.make_branch_and_tree('tree')
2705
        preview = TransformPreview(tree)
2706
        self.addCleanup(preview.finalize)
2707
        preview.new_file('foo', preview.root, 'bar', 'baz-id')
2708
        preview_tree = preview.get_preview_tree()
2709
        self.assertEqual(False, preview_tree.is_executable('baz-id',
2710
                                                           'tree/foo'))
2711
        self.assertEqual(False, preview_tree.is_executable('baz-id'))
0.13.8 by Aaron Bentley
Integrate serialization into TreeTransforms
2712
4354.4.4 by Aaron Bentley
Simplify by using CommitBuilder directly
2713
    def test_commit_preview_tree(self):
2714
        tree = self.make_branch_and_tree('tree')
2715
        rev_id = tree.commit('rev1')
2716
        tree.branch.lock_write()
2717
        self.addCleanup(tree.branch.unlock)
2718
        tt = TransformPreview(tree)
2719
        tt.new_file('file', tt.root, 'contents', 'file_id')
2720
        self.addCleanup(tt.finalize)
2721
        preview = tt.get_preview_tree()
2722
        preview.set_parent_ids([rev_id])
2723
        builder = tree.branch.get_commit_builder([rev_id])
2724
        list(builder.record_iter_changes(preview, rev_id, tt.iter_changes()))
2725
        builder.finish_inventory()
2726
        rev2_id = builder.commit('rev2')
2727
        rev2_tree = tree.branch.repository.revision_tree(rev2_id)
2728
        self.assertEqual('contents', rev2_tree.get_file_text('file_id'))
2729
4634.79.1 by Aaron Bentley
TransformPreview uses ascii-only filenames.
2730
    def test_ascii_limbo_paths(self):
4634.79.2 by Aaron Bentley
Avoid runing test on non-unicode filesystems.
2731
        self.requireFeature(tests.UnicodeFilenameFeature)
4634.79.1 by Aaron Bentley
TransformPreview uses ascii-only filenames.
2732
        branch = self.make_branch('any')
2733
        tree = branch.repository.revision_tree(_mod_revision.NULL_REVISION)
2734
        tt = TransformPreview(tree)
2735
        foo_id = tt.new_directory('', ROOT_PARENT)
2736
        bar_id = tt.new_file(u'\u1234bar', foo_id, 'contents')
2737
        limbo_path = tt._limbo_name(bar_id)
2738
        self.assertEqual(limbo_path.encode('ascii', 'replace'), limbo_path)
2739
0.13.8 by Aaron Bentley
Integrate serialization into TreeTransforms
2740
0.13.13 by Aaron Bentley
Add direct test of serialization records
2741
class FakeSerializer(object):
2742
    """Serializer implementation that simply returns the input.
2743
2744
    The input is returned in the order used by pack.ContainerPushParser.
2745
    """
2746
    @staticmethod
2747
    def bytes_record(bytes, names):
2748
        return names, bytes
2749
2750
0.13.8 by Aaron Bentley
Integrate serialization into TreeTransforms
2751
class TestSerializeTransform(tests.TestCaseWithTransport):
2752
0.13.22 by Aaron Bentley
More unicodeness for Shelf tests
2753
    _test_needs_features = [tests.UnicodeFilenameFeature]
2754
0.13.17 by Aaron Bentley
Convert roundtrip destruction test to serialization/deserialization pair
2755
    def get_preview(self, tree=None):
2756
        if tree is None:
2757
            tree = self.make_branch_and_tree('tree')
0.13.14 by Aaron Bentley
Add deserialization test, remove roundtrip test.
2758
        tt = TransformPreview(tree)
2759
        self.addCleanup(tt.finalize)
2760
        return tt
2761
0.13.18 by Aaron Bentley
Finish converting tests to direct serialize/deserialize tests, clean up
2762
    def assertSerializesTo(self, expected, tt):
2763
        records = list(tt.serialize(FakeSerializer()))
2764
        self.assertEqual(expected, records)
0.13.8 by Aaron Bentley
Integrate serialization into TreeTransforms
2765
0.13.13 by Aaron Bentley
Add direct test of serialization records
2766
    @staticmethod
2767
    def default_attribs():
2768
        return {
0.13.15 by Aaron Bentley
Convert symlink tests to avoid roundtripping
2769
            '_id_number': 1,
0.13.13 by Aaron Bentley
Add direct test of serialization records
2770
            '_new_name': {},
2771
            '_new_parent': {},
2772
            '_new_executability': {},
2773
            '_new_id': {},
0.13.15 by Aaron Bentley
Convert symlink tests to avoid roundtripping
2774
            '_tree_path_ids': {'': 'new-0'},
0.13.13 by Aaron Bentley
Add direct test of serialization records
2775
            '_removed_id': [],
2776
            '_removed_contents': [],
2777
            '_non_present_ids': {},
2778
            }
2779
0.13.18 by Aaron Bentley
Finish converting tests to direct serialize/deserialize tests, clean up
2780
    def make_records(self, attribs, contents):
2781
        records = [
2782
            (((('attribs'),),), bencode.bencode(attribs))]
2783
        records.extend([(((n, k),), c) for n, k, c in contents])
2784
        return records
2785
0.13.13 by Aaron Bentley
Add direct test of serialization records
2786
    def creation_records(self):
2787
        attribs = self.default_attribs()
2788
        attribs['_id_number'] = 3
2789
        attribs['_new_name'] = {
2790
            'new-1': u'foo\u1234'.encode('utf-8'), 'new-2': 'qux'}
2791
        attribs['_new_id'] = {'new-1': 'baz', 'new-2': 'quxx'}
2792
        attribs['_new_parent'] = {'new-1': 'new-0', 'new-2': 'new-0'}
2793
        attribs['_new_executability'] = {'new-1': 1}
2794
        contents = [
2795
            ('new-1', 'file', 'i 1\nbar\n'),
2796
            ('new-2', 'directory', ''),
2797
            ]
2798
        return self.make_records(attribs, contents)
2799
2800
    def test_serialize_creation(self):
0.13.14 by Aaron Bentley
Add deserialization test, remove roundtrip test.
2801
        tt = self.get_preview()
0.13.13 by Aaron Bentley
Add direct test of serialization records
2802
        tt.new_file(u'foo\u1234', tt.root, 'bar', 'baz', True)
2803
        tt.new_directory('qux', tt.root, 'quxx')
0.13.21 by Aaron Bentley
Use assertSerializesTo in more places
2804
        self.assertSerializesTo(self.creation_records(), tt)
0.13.13 by Aaron Bentley
Add direct test of serialization records
2805
0.13.14 by Aaron Bentley
Add deserialization test, remove roundtrip test.
2806
    def test_deserialize_creation(self):
2807
        tt = self.get_preview()
2808
        tt.deserialize(iter(self.creation_records()))
2809
        self.assertEqual(3, tt._id_number)
2810
        self.assertEqual({'new-1': u'foo\u1234',
2811
                          'new-2': 'qux'}, tt._new_name)
2812
        self.assertEqual({'new-1': 'baz', 'new-2': 'quxx'}, tt._new_id)
2813
        self.assertEqual({'new-1': tt.root, 'new-2': tt.root}, tt._new_parent)
2814
        self.assertEqual({'baz': 'new-1', 'quxx': 'new-2'}, tt._r_new_id)
2815
        self.assertEqual({'new-1': True}, tt._new_executability)
2816
        self.assertEqual({'new-1': 'file',
2817
                          'new-2': 'directory'}, tt._new_contents)
2818
        foo_limbo = open(tt._limbo_name('new-1'), 'rb')
2819
        try:
2820
            foo_content = foo_limbo.read()
2821
        finally:
2822
            foo_limbo.close()
2823
        self.assertEqual('bar', foo_content)
2824
0.13.18 by Aaron Bentley
Finish converting tests to direct serialize/deserialize tests, clean up
2825
    def symlink_creation_records(self):
2826
        attribs = self.default_attribs()
2827
        attribs['_id_number'] = 2
2828
        attribs['_new_name'] = {'new-1': u'foo\u1234'.encode('utf-8')}
2829
        attribs['_new_parent'] = {'new-1': 'new-0'}
2830
        contents = [('new-1', 'symlink', u'bar\u1234'.encode('utf-8'))]
2831
        return self.make_records(attribs, contents)
2832
0.13.15 by Aaron Bentley
Convert symlink tests to avoid roundtripping
2833
    def test_serialize_symlink_creation(self):
0.13.8 by Aaron Bentley
Integrate serialization into TreeTransforms
2834
        self.requireFeature(tests.SymlinkFeature)
0.13.15 by Aaron Bentley
Convert symlink tests to avoid roundtripping
2835
        tt = self.get_preview()
0.13.16 by Aaron Bentley
Add unicode symlink targets to tests
2836
        tt.new_symlink(u'foo\u1234', tt.root, u'bar\u1234')
0.13.21 by Aaron Bentley
Use assertSerializesTo in more places
2837
        self.assertSerializesTo(self.symlink_creation_records(), tt)
0.13.15 by Aaron Bentley
Convert symlink tests to avoid roundtripping
2838
2839
    def test_deserialize_symlink_creation(self):
4241.14.12 by Vincent Ladeuil
Far too many modifications for a single commit, need to restart.
2840
        self.requireFeature(tests.SymlinkFeature)
0.13.15 by Aaron Bentley
Convert symlink tests to avoid roundtripping
2841
        tt = self.get_preview()
2842
        tt.deserialize(iter(self.symlink_creation_records()))
4241.14.12 by Vincent Ladeuil
Far too many modifications for a single commit, need to restart.
2843
        abspath = tt._limbo_name('new-1')
4241.14.17 by Vincent Ladeuil
Add more tests for unicode symlinks to test_transform.
2844
        foo_content = osutils.readlink(abspath)
0.13.22 by Aaron Bentley
More unicodeness for Shelf tests
2845
        self.assertEqual(u'bar\u1234', foo_content)
0.13.8 by Aaron Bentley
Integrate serialization into TreeTransforms
2846
0.13.19 by Aaron Bentley
Clean up serialization tests
2847
    def make_destruction_preview(self):
0.13.8 by Aaron Bentley
Integrate serialization into TreeTransforms
2848
        tree = self.make_branch_and_tree('.')
2849
        self.build_tree([u'foo\u1234', 'bar'])
2850
        tree.add([u'foo\u1234', 'bar'], ['foo-id', 'bar-id'])
0.13.19 by Aaron Bentley
Clean up serialization tests
2851
        return self.get_preview(tree)
0.13.17 by Aaron Bentley
Convert roundtrip destruction test to serialization/deserialization pair
2852
0.13.18 by Aaron Bentley
Finish converting tests to direct serialize/deserialize tests, clean up
2853
    def destruction_records(self):
2854
        attribs = self.default_attribs()
2855
        attribs['_id_number'] = 3
2856
        attribs['_removed_id'] = ['new-1']
2857
        attribs['_removed_contents'] = ['new-2']
2858
        attribs['_tree_path_ids'] = {
2859
            '': 'new-0',
2860
            u'foo\u1234'.encode('utf-8'): 'new-1',
2861
            'bar': 'new-2',
2862
            }
2863
        return self.make_records(attribs, [])
2864
0.13.17 by Aaron Bentley
Convert roundtrip destruction test to serialization/deserialization pair
2865
    def test_serialize_destruction(self):
0.13.19 by Aaron Bentley
Clean up serialization tests
2866
        tt = self.make_destruction_preview()
0.13.8 by Aaron Bentley
Integrate serialization into TreeTransforms
2867
        foo_trans_id = tt.trans_id_tree_file_id('foo-id')
2868
        tt.unversion_file(foo_trans_id)
2869
        bar_trans_id = tt.trans_id_tree_file_id('bar-id')
2870
        tt.delete_contents(bar_trans_id)
0.13.18 by Aaron Bentley
Finish converting tests to direct serialize/deserialize tests, clean up
2871
        self.assertSerializesTo(self.destruction_records(), tt)
0.13.17 by Aaron Bentley
Convert roundtrip destruction test to serialization/deserialization pair
2872
2873
    def test_deserialize_destruction(self):
0.13.19 by Aaron Bentley
Clean up serialization tests
2874
        tt = self.make_destruction_preview()
0.13.17 by Aaron Bentley
Convert roundtrip destruction test to serialization/deserialization pair
2875
        tt.deserialize(iter(self.destruction_records()))
2876
        self.assertEqual({u'foo\u1234': 'new-1',
2877
                          'bar': 'new-2',
2878
                          '': tt.root}, tt._tree_path_ids)
2879
        self.assertEqual({'new-1': u'foo\u1234',
2880
                          'new-2': 'bar',
2881
                          tt.root: ''}, tt._tree_id_paths)
2882
        self.assertEqual(set(['new-1']), tt._removed_id)
2883
        self.assertEqual(set(['new-2']), tt._removed_contents)
0.13.8 by Aaron Bentley
Integrate serialization into TreeTransforms
2884
0.13.18 by Aaron Bentley
Finish converting tests to direct serialize/deserialize tests, clean up
2885
    def missing_records(self):
2886
        attribs = self.default_attribs()
2887
        attribs['_id_number'] = 2
2888
        attribs['_non_present_ids'] = {
2889
            'boo': 'new-1',}
2890
        return self.make_records(attribs, [])
2891
2892
    def test_serialize_missing(self):
2893
        tt = self.get_preview()
0.13.8 by Aaron Bentley
Integrate serialization into TreeTransforms
2894
        boo_trans_id = tt.trans_id_file_id('boo')
0.13.18 by Aaron Bentley
Finish converting tests to direct serialize/deserialize tests, clean up
2895
        self.assertSerializesTo(self.missing_records(), tt)
2896
2897
    def test_deserialize_missing(self):
2898
        tt = self.get_preview()
2899
        tt.deserialize(iter(self.missing_records()))
2900
        self.assertEqual({'boo': 'new-1'}, tt._non_present_ids)
2901
2902
    def make_modification_preview(self):
0.13.8 by Aaron Bentley
Integrate serialization into TreeTransforms
2903
        LINES_ONE = 'aa\nbb\ncc\ndd\n'
2904
        LINES_TWO = 'z\nbb\nx\ndd\n'
2905
        tree = self.make_branch_and_tree('tree')
2906
        self.build_tree_contents([('tree/file', LINES_ONE)])
2907
        tree.add('file', 'file-id')
0.13.18 by Aaron Bentley
Finish converting tests to direct serialize/deserialize tests, clean up
2908
        return self.get_preview(tree), LINES_TWO
2909
2910
    def modification_records(self):
2911
        attribs = self.default_attribs()
2912
        attribs['_id_number'] = 2
2913
        attribs['_tree_path_ids'] = {
2914
            'file': 'new-1',
2915
            '': 'new-0',}
2916
        attribs['_removed_contents'] = ['new-1']
2917
        contents = [('new-1', 'file',
2918
                     'i 1\nz\n\nc 0 1 1 1\ni 1\nx\n\nc 0 3 3 1\n')]
2919
        return self.make_records(attribs, contents)
2920
2921
    def test_serialize_modification(self):
2922
        tt, LINES = self.make_modification_preview()
0.13.8 by Aaron Bentley
Integrate serialization into TreeTransforms
2923
        trans_id = tt.trans_id_file_id('file-id')
2924
        tt.delete_contents(trans_id)
0.13.18 by Aaron Bentley
Finish converting tests to direct serialize/deserialize tests, clean up
2925
        tt.create_file(LINES, trans_id)
2926
        self.assertSerializesTo(self.modification_records(), tt)
2927
2928
    def test_deserialize_modification(self):
2929
        tt, LINES = self.make_modification_preview()
2930
        tt.deserialize(iter(self.modification_records()))
2931
        self.assertFileEqual(LINES, tt._limbo_name('new-1'))
2932
2933
    def make_kind_change_preview(self):
2934
        LINES = 'a\nb\nc\nd\n'
0.13.8 by Aaron Bentley
Integrate serialization into TreeTransforms
2935
        tree = self.make_branch_and_tree('tree')
2936
        self.build_tree(['tree/foo/'])
2937
        tree.add('foo', 'foo-id')
0.13.18 by Aaron Bentley
Finish converting tests to direct serialize/deserialize tests, clean up
2938
        return self.get_preview(tree), LINES
2939
2940
    def kind_change_records(self):
2941
        attribs = self.default_attribs()
2942
        attribs['_id_number'] = 2
2943
        attribs['_tree_path_ids'] = {
2944
            'foo': 'new-1',
2945
            '': 'new-0',}
2946
        attribs['_removed_contents'] = ['new-1']
2947
        contents = [('new-1', 'file',
2948
                     'i 4\na\nb\nc\nd\n\n')]
2949
        return self.make_records(attribs, contents)
2950
2951
    def test_serialize_kind_change(self):
2952
        tt, LINES = self.make_kind_change_preview()
0.13.8 by Aaron Bentley
Integrate serialization into TreeTransforms
2953
        trans_id = tt.trans_id_file_id('foo-id')
2954
        tt.delete_contents(trans_id)
0.13.18 by Aaron Bentley
Finish converting tests to direct serialize/deserialize tests, clean up
2955
        tt.create_file(LINES, trans_id)
2956
        self.assertSerializesTo(self.kind_change_records(), tt)
2957
2958
    def test_deserialize_kind_change(self):
2959
        tt, LINES = self.make_kind_change_preview()
2960
        tt.deserialize(iter(self.kind_change_records()))
2961
        self.assertFileEqual(LINES, tt._limbo_name('new-1'))
2962
2963
    def make_add_contents_preview(self):
2964
        LINES = 'a\nb\nc\nd\n'
0.13.8 by Aaron Bentley
Integrate serialization into TreeTransforms
2965
        tree = self.make_branch_and_tree('tree')
2966
        self.build_tree(['tree/foo'])
2967
        tree.add('foo')
2968
        os.unlink('tree/foo')
0.13.18 by Aaron Bentley
Finish converting tests to direct serialize/deserialize tests, clean up
2969
        return self.get_preview(tree), LINES
2970
2971
    def add_contents_records(self):
2972
        attribs = self.default_attribs()
2973
        attribs['_id_number'] = 2
2974
        attribs['_tree_path_ids'] = {
2975
            'foo': 'new-1',
2976
            '': 'new-0',}
2977
        contents = [('new-1', 'file',
2978
                     'i 4\na\nb\nc\nd\n\n')]
2979
        return self.make_records(attribs, contents)
2980
2981
    def test_serialize_add_contents(self):
2982
        tt, LINES = self.make_add_contents_preview()
0.13.8 by Aaron Bentley
Integrate serialization into TreeTransforms
2983
        trans_id = tt.trans_id_tree_path('foo')
0.13.18 by Aaron Bentley
Finish converting tests to direct serialize/deserialize tests, clean up
2984
        tt.create_file(LINES, trans_id)
2985
        self.assertSerializesTo(self.add_contents_records(), tt)
2986
2987
    def test_deserialize_add_contents(self):
2988
        tt, LINES = self.make_add_contents_preview()
2989
        tt.deserialize(iter(self.add_contents_records()))
2990
        self.assertFileEqual(LINES, tt._limbo_name('new-1'))
0.13.8 by Aaron Bentley
Integrate serialization into TreeTransforms
2991
2992
    def test_get_parents_lines(self):
2993
        LINES_ONE = 'aa\nbb\ncc\ndd\n'
2994
        LINES_TWO = 'z\nbb\nx\ndd\n'
2995
        tree = self.make_branch_and_tree('tree')
2996
        self.build_tree_contents([('tree/file', LINES_ONE)])
2997
        tree.add('file', 'file-id')
0.13.18 by Aaron Bentley
Finish converting tests to direct serialize/deserialize tests, clean up
2998
        tt = self.get_preview(tree)
0.13.8 by Aaron Bentley
Integrate serialization into TreeTransforms
2999
        trans_id = tt.trans_id_tree_path('file')
3000
        self.assertEqual((['aa\n', 'bb\n', 'cc\n', 'dd\n'],),
3001
            tt._get_parents_lines(trans_id))
3002
3003
    def test_get_parents_texts(self):
3004
        LINES_ONE = 'aa\nbb\ncc\ndd\n'
3005
        LINES_TWO = 'z\nbb\nx\ndd\n'
3006
        tree = self.make_branch_and_tree('tree')
3007
        self.build_tree_contents([('tree/file', LINES_ONE)])
3008
        tree.add('file', 'file-id')
0.13.18 by Aaron Bentley
Finish converting tests to direct serialize/deserialize tests, clean up
3009
        tt = self.get_preview(tree)
0.13.8 by Aaron Bentley
Integrate serialization into TreeTransforms
3010
        trans_id = tt.trans_id_tree_path('file')
3011
        self.assertEqual((LINES_ONE,),
3012
            tt._get_parents_texts(trans_id))