/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
1534.7.106 by Aaron Bentley
Cleaned up imports, added copyright statements
1
# Copyright (C) 2006 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
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
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 (
2090.2.1 by Martin Pool
Fix some code which relies on assertions and breaks under python -O
23
    errors,
2116.4.1 by John Arbash Meinel
Update file and revision id generators.
24
    generate_ids,
3008.1.17 by Aaron Bentley
Test unused parameters of preview_tree._iter_changes
25
    progress,
3008.1.18 by Aaron Bentley
Get supported PreviewTree functionality under test
26
    revision as _mod_revision,
2255.7.48 by Robert Collins
Deprecated and make work with DirState trees 'transform.find_interesting'.
27
    symbol_versioning,
2027.1.1 by John Arbash Meinel
Fix bug #56549, and write a direct test that the right path is being statted
28
    tests,
29
    urlutils,
30
    )
1558.1.3 by Aaron Bentley
Fixed deprecated op use in test suite
31
from bzrlib.bzrdir import BzrDir
1534.10.24 by Aaron Bentley
Eliminated conflicts_to_strings, made remove_files a ConflictList member
32
from bzrlib.conflicts import (DuplicateEntry, DuplicateID, MissingParent,
3144.4.2 by Aaron Bentley
Handle non-directory parent conflicts (abentley, #177390)
33
                              UnversionedParent, ParentLoop, DeletingParent,
34
                              NonDirectoryParent)
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
35
from bzrlib.diff import show_diff_trees
1534.7.32 by Aaron Bentley
Got conflict handling working when conflicts involve existing files
36
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
1551.7.17 by Aaron Bentley
Switch to PathsNotVersioned, accept extra_trees
37
                           ReusingTransform, CantMoveRoot, 
38
                           PathsNotVersionedError, ExistingLimbo,
2733.2.11 by Aaron Bentley
Detect irregularities with the pending-deletion directory
39
                           ExistingPendingDeletion, ImmortalLimbo,
40
                           ImmortalPendingDeletion, LockError)
2949.5.1 by Alexander Belchenko
selftest: use SymlinkFeature instead of TestSkipped where appropriate
41
from bzrlib.osutils import file_kind, pathjoin
1534.7.140 by Aaron Bentley
Moved the merge stuff into merge.py
42
from bzrlib.merge import Merge3Merger
2949.5.1 by Alexander Belchenko
selftest: use SymlinkFeature instead of TestSkipped where appropriate
43
from bzrlib.tests import (
3063.1.1 by Alexander Belchenko
Catch OSError 17 (file exists) in final phase of tree transform and show filename to user (#111758).
44
    CaseInsensitiveFilesystemFeature,
2949.5.1 by Alexander Belchenko
selftest: use SymlinkFeature instead of TestSkipped where appropriate
45
    SymlinkFeature,
46
    TestCase,
47
    TestCaseInTempDir,
48
    TestSkipped,
49
    )
1534.7.106 by Aaron Bentley
Cleaned up imports, added copyright statements
50
from bzrlib.transform import (TreeTransform, ROOT_PARENT, FinalPaths, 
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
51
                              resolve_conflicts, cook_conflicts, 
2625.4.3 by Ian Clatworthy
Add a test to ensure the deprecation worked.
52
                              find_interesting, build_tree, get_backup_name,
3008.1.13 by Michael Hudson
merge bzr.dev
53
                              change_entry, _FileMover, resolve_checkout,
54
                              TransformPreview)
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
55
2100.3.21 by Aaron Bentley
Work on checking out by-reference trees
56
class TestTreeTransform(tests.TestCaseWithTransport):
1740.2.4 by Aaron Bentley
Update transform tests and docs
57
1534.7.59 by Aaron Bentley
Simplified tests
58
    def setUp(self):
59
        super(TestTreeTransform, self).setUp()
2255.2.194 by Robert Collins
[BROKEN] Many updates to stop using experimental formats in tests.
60
        self.wt = self.make_branch_and_tree('.', format='dirstate-with-subtree')
1534.7.161 by Aaron Bentley
Used appropriate control_files
61
        os.chdir('..')
1534.7.59 by Aaron Bentley
Simplified tests
62
63
    def get_transform(self):
64
        transform = TreeTransform(self.wt)
1534.7.100 by Aaron Bentley
Fixed path-relative test cases
65
        #self.addCleanup(transform.finalize)
1731.1.33 by Aaron Bentley
Revert no-special-root changes
66
        return transform, transform.root
1534.7.59 by Aaron Bentley
Simplified tests
67
1534.7.162 by Aaron Bentley
Handle failures creating/deleting the Limbo directory
68
    def test_existing_limbo(self):
69
        transform, root = self.get_transform()
2733.2.6 by Aaron Bentley
Make TreeTransform commits rollbackable
70
        limbo_name = transform._limbodir
71
        deletion_path = transform._deletiondir
1534.7.176 by abentley
Fixed up tests for Windows
72
        os.mkdir(pathjoin(limbo_name, 'hehe'))
1534.7.162 by Aaron Bentley
Handle failures creating/deleting the Limbo directory
73
        self.assertRaises(ImmortalLimbo, transform.apply)
74
        self.assertRaises(LockError, self.wt.unlock)
75
        self.assertRaises(ExistingLimbo, self.get_transform)
76
        self.assertRaises(LockError, self.wt.unlock)
1534.7.176 by abentley
Fixed up tests for Windows
77
        os.rmdir(pathjoin(limbo_name, 'hehe'))
1534.7.162 by Aaron Bentley
Handle failures creating/deleting the Limbo directory
78
        os.rmdir(limbo_name)
2733.2.6 by Aaron Bentley
Make TreeTransform commits rollbackable
79
        os.rmdir(deletion_path)
1534.7.162 by Aaron Bentley
Handle failures creating/deleting the Limbo directory
80
        transform, root = self.get_transform()
81
        transform.apply()
1534.7.59 by Aaron Bentley
Simplified tests
82
2733.2.11 by Aaron Bentley
Detect irregularities with the pending-deletion directory
83
    def test_existing_pending_deletion(self):
84
        transform, root = self.get_transform()
85
        deletion_path = self._limbodir = urlutils.local_path_from_url(
86
            transform._tree._control_files.controlfilename('pending-deletion'))
87
        os.mkdir(pathjoin(deletion_path, 'blocking-directory'))
88
        self.assertRaises(ImmortalPendingDeletion, transform.apply)
89
        self.assertRaises(LockError, self.wt.unlock)
90
        self.assertRaises(ExistingPendingDeletion, self.get_transform)
91
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
92
    def test_build(self):
3034.2.1 by Aaron Bentley
Fix is_executable tests for win32
93
        transform, root = self.get_transform()
94
        self.wt.lock_tree_write()
95
        self.addCleanup(self.wt.unlock)
1534.7.59 by Aaron Bentley
Simplified tests
96
        self.assertIs(transform.get_tree_parent(root), ROOT_PARENT)
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
97
        imaginary_id = transform.trans_id_tree_path('imaginary')
1534.10.32 by Aaron Bentley
Test and fix case where name has trailing slash
98
        imaginary_id2 = transform.trans_id_tree_path('imaginary/')
99
        self.assertEqual(imaginary_id, imaginary_id2)
1534.7.59 by Aaron Bentley
Simplified tests
100
        self.assertEqual(transform.get_tree_parent(imaginary_id), root)
101
        self.assertEqual(transform.final_kind(root), 'directory')
102
        self.assertEqual(transform.final_file_id(root), self.wt.get_root_id())
103
        trans_id = transform.create_path('name', root)
104
        self.assertIs(transform.final_file_id(trans_id), None)
105
        self.assertRaises(NoSuchFile, transform.final_kind, trans_id)
106
        transform.create_file('contents', trans_id)
107
        transform.set_executability(True, trans_id)
108
        transform.version_file('my_pretties', trans_id)
109
        self.assertRaises(DuplicateKey, transform.version_file,
110
                          'my_pretties', trans_id)
111
        self.assertEqual(transform.final_file_id(trans_id), 'my_pretties')
112
        self.assertEqual(transform.final_parent(trans_id), root)
113
        self.assertIs(transform.final_parent(root), ROOT_PARENT)
114
        self.assertIs(transform.get_tree_parent(root), ROOT_PARENT)
115
        oz_id = transform.create_path('oz', root)
116
        transform.create_directory(oz_id)
117
        transform.version_file('ozzie', oz_id)
118
        trans_id2 = transform.create_path('name2', root)
119
        transform.create_file('contents', trans_id2)
120
        transform.set_executability(False, trans_id2)
121
        transform.version_file('my_pretties2', trans_id2)
1534.7.191 by Aaron Bentley
Got transform.apply to list modified paths
122
        modified_paths = transform.apply().modified_paths
1534.7.100 by Aaron Bentley
Fixed path-relative test cases
123
        self.assertEqual('contents', self.wt.get_file_byname('name').read())
1534.7.59 by Aaron Bentley
Simplified tests
124
        self.assertEqual(self.wt.path2id('name'), 'my_pretties')
125
        self.assertIs(self.wt.is_executable('my_pretties'), True)
126
        self.assertIs(self.wt.is_executable('my_pretties2'), False)
1534.7.100 by Aaron Bentley
Fixed path-relative test cases
127
        self.assertEqual('directory', file_kind(self.wt.abspath('oz')))
1534.7.191 by Aaron Bentley
Got transform.apply to list modified paths
128
        self.assertEqual(len(modified_paths), 3)
129
        tree_mod_paths = [self.wt.id2abspath(f) for f in 
130
                          ('ozzie', 'my_pretties', 'my_pretties2')]
131
        self.assertSubset(tree_mod_paths, modified_paths)
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
132
        # is it safe to finalize repeatedly?
133
        transform.finalize()
1534.7.59 by Aaron Bentley
Simplified tests
134
        transform.finalize()
1534.7.2 by Aaron Bentley
Added convenience function
135
136
    def test_convenience(self):
1534.7.59 by Aaron Bentley
Simplified tests
137
        transform, root = self.get_transform()
3034.2.1 by Aaron Bentley
Fix is_executable tests for win32
138
        self.wt.lock_tree_write()
139
        self.addCleanup(self.wt.unlock)
1534.7.59 by Aaron Bentley
Simplified tests
140
        trans_id = transform.new_file('name', root, 'contents', 
141
                                      'my_pretties', True)
142
        oz = transform.new_directory('oz', root, 'oz-id')
143
        dorothy = transform.new_directory('dorothy', oz, 'dorothy-id')
144
        toto = transform.new_file('toto', dorothy, 'toto-contents', 
145
                                  'toto-id', False)
146
147
        self.assertEqual(len(transform.find_conflicts()), 0)
148
        transform.apply()
149
        self.assertRaises(ReusingTransform, transform.find_conflicts)
1534.7.100 by Aaron Bentley
Fixed path-relative test cases
150
        self.assertEqual('contents', file(self.wt.abspath('name')).read())
1534.7.59 by Aaron Bentley
Simplified tests
151
        self.assertEqual(self.wt.path2id('name'), 'my_pretties')
152
        self.assertIs(self.wt.is_executable('my_pretties'), True)
153
        self.assertEqual(self.wt.path2id('oz'), 'oz-id')
154
        self.assertEqual(self.wt.path2id('oz/dorothy'), 'dorothy-id')
155
        self.assertEqual(self.wt.path2id('oz/dorothy/toto'), 'toto-id')
156
2255.2.194 by Robert Collins
[BROKEN] Many updates to stop using experimental formats in tests.
157
        self.assertEqual('toto-contents',
1534.7.100 by Aaron Bentley
Fixed path-relative test cases
158
                         self.wt.get_file_byname('oz/dorothy/toto').read())
1534.7.59 by Aaron Bentley
Simplified tests
159
        self.assertIs(self.wt.is_executable('toto-id'), False)
1534.7.6 by Aaron Bentley
Added conflict handling
160
2100.3.21 by Aaron Bentley
Work on checking out by-reference trees
161
    def test_tree_reference(self):
162
        transform, root = self.get_transform()
163
        tree = transform._tree
164
        trans_id = transform.new_directory('reference', root, 'subtree-id')
165
        transform.set_tree_reference('subtree-revision', trans_id)
166
        transform.apply()
2255.2.194 by Robert Collins
[BROKEN] Many updates to stop using experimental formats in tests.
167
        tree.lock_read()
168
        self.addCleanup(tree.unlock)
169
        self.assertEqual('subtree-revision',
2100.3.21 by Aaron Bentley
Work on checking out by-reference trees
170
                         tree.inventory['subtree-id'].reference_revision)
171
1534.7.6 by Aaron Bentley
Added conflict handling
172
    def test_conflicts(self):
1534.7.59 by Aaron Bentley
Simplified tests
173
        transform, root = self.get_transform()
174
        trans_id = transform.new_file('name', root, 'contents', 
175
                                      'my_pretties')
176
        self.assertEqual(len(transform.find_conflicts()), 0)
177
        trans_id2 = transform.new_file('name', root, 'Crontents', 'toto')
178
        self.assertEqual(transform.find_conflicts(), 
179
                         [('duplicate', trans_id, trans_id2, 'name')])
180
        self.assertRaises(MalformedTransform, transform.apply)
181
        transform.adjust_path('name', trans_id, trans_id2)
182
        self.assertEqual(transform.find_conflicts(), 
183
                         [('non-directory parent', trans_id)])
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
184
        tinman_id = transform.trans_id_tree_path('tinman')
1534.7.59 by Aaron Bentley
Simplified tests
185
        transform.adjust_path('name', tinman_id, trans_id2)
186
        self.assertEqual(transform.find_conflicts(), 
187
                         [('unversioned parent', tinman_id), 
188
                          ('missing parent', tinman_id)])
189
        lion_id = transform.create_path('lion', root)
190
        self.assertEqual(transform.find_conflicts(), 
191
                         [('unversioned parent', tinman_id), 
192
                          ('missing parent', tinman_id)])
193
        transform.adjust_path('name', lion_id, trans_id2)
194
        self.assertEqual(transform.find_conflicts(), 
195
                         [('unversioned parent', lion_id),
196
                          ('missing parent', lion_id)])
197
        transform.version_file("Courage", lion_id)
198
        self.assertEqual(transform.find_conflicts(), 
199
                         [('missing parent', lion_id), 
200
                          ('versioning no contents', lion_id)])
201
        transform.adjust_path('name2', root, trans_id2)
202
        self.assertEqual(transform.find_conflicts(), 
203
                         [('versioning no contents', lion_id)])
204
        transform.create_file('Contents, okay?', lion_id)
205
        transform.adjust_path('name2', trans_id2, trans_id2)
206
        self.assertEqual(transform.find_conflicts(), 
207
                         [('parent loop', trans_id2), 
208
                          ('non-directory parent', trans_id2)])
209
        transform.adjust_path('name2', root, trans_id2)
210
        oz_id = transform.new_directory('oz', root)
211
        transform.set_executability(True, oz_id)
212
        self.assertEqual(transform.find_conflicts(), 
213
                         [('unversioned executability', oz_id)])
214
        transform.version_file('oz-id', oz_id)
215
        self.assertEqual(transform.find_conflicts(), 
216
                         [('non-file executability', oz_id)])
217
        transform.set_executability(None, oz_id)
1534.7.71 by abentley
All tests pass under Windows
218
        tip_id = transform.new_file('tip', oz_id, 'ozma', 'tip-id')
1534.7.59 by Aaron Bentley
Simplified tests
219
        transform.apply()
220
        self.assertEqual(self.wt.path2id('name'), 'my_pretties')
221
        self.assertEqual('contents', file(self.wt.abspath('name')).read())
222
        transform2, root = self.get_transform()
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
223
        oz_id = transform2.trans_id_tree_file_id('oz-id')
1534.7.59 by Aaron Bentley
Simplified tests
224
        newtip = transform2.new_file('tip', oz_id, 'other', 'tip-id')
225
        result = transform2.find_conflicts()
1534.7.135 by Aaron Bentley
Fixed deletion handling
226
        fp = FinalPaths(transform2)
1534.7.59 by Aaron Bentley
Simplified tests
227
        self.assert_('oz/tip' in transform2._tree_path_ids)
1534.7.176 by abentley
Fixed up tests for Windows
228
        self.assertEqual(fp.get_path(newtip), pathjoin('oz', 'tip'))
1534.7.59 by Aaron Bentley
Simplified tests
229
        self.assertEqual(len(result), 2)
230
        self.assertEqual((result[0][0], result[0][1]), 
231
                         ('duplicate', newtip))
232
        self.assertEqual((result[1][0], result[1][2]), 
233
                         ('duplicate id', newtip))
1534.7.73 by Aaron Bentley
Changed model again. Now iterator is used immediately.
234
        transform2.finalize()
1534.7.59 by Aaron Bentley
Simplified tests
235
        transform3 = TreeTransform(self.wt)
236
        self.addCleanup(transform3.finalize)
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
237
        oz_id = transform3.trans_id_tree_file_id('oz-id')
1534.7.59 by Aaron Bentley
Simplified tests
238
        transform3.delete_contents(oz_id)
239
        self.assertEqual(transform3.find_conflicts(), 
240
                         [('missing parent', oz_id)])
1731.1.33 by Aaron Bentley
Revert no-special-root changes
241
        root_id = transform3.root
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
242
        tip_id = transform3.trans_id_tree_file_id('tip-id')
1534.7.59 by Aaron Bentley
Simplified tests
243
        transform3.adjust_path('tip', root_id, tip_id)
244
        transform3.apply()
1534.7.36 by Aaron Bentley
Added rename tests
245
3034.4.1 by Aaron Bentley
Start handling case-insensitivity
246
    def test_conflict_on_case_insensitive(self):
247
        tree = self.make_branch_and_tree('tree')
248
        # Don't try this at home, kids!
249
        # Force the tree to report that it is case sensitive, for conflict
250
        # resolution tests
251
        tree.case_sensitive = True
252
        transform = TreeTransform(tree)
253
        self.addCleanup(transform.finalize)
254
        transform.new_file('file', transform.root, 'content')
255
        transform.new_file('FiLe', transform.root, 'content')
256
        result = transform.find_conflicts()
257
        self.assertEqual([], result)
3008.1.15 by Aaron Bentley
Make case_sensitive an aspect of the transform, not the source tree
258
        transform.finalize()
3034.4.1 by Aaron Bentley
Start handling case-insensitivity
259
        # Force the tree to report that it is case insensitive, for conflict
260
        # generation tests
261
        tree.case_sensitive = False
3008.1.15 by Aaron Bentley
Make case_sensitive an aspect of the transform, not the source tree
262
        transform = TreeTransform(tree)
263
        self.addCleanup(transform.finalize)
264
        transform.new_file('file', transform.root, 'content')
265
        transform.new_file('FiLe', transform.root, 'content')
3034.4.1 by Aaron Bentley
Start handling case-insensitivity
266
        result = transform.find_conflicts()
267
        self.assertEqual([('duplicate', 'new-1', 'new-2', 'file')], result)
268
3034.4.7 by Aaron Bentley
Add test for filename conflicts with existing files
269
    def test_conflict_on_case_insensitive_existing(self):
270
        tree = self.make_branch_and_tree('tree')
271
        self.build_tree(['tree/FiLe'])
272
        # Don't try this at home, kids!
273
        # Force the tree to report that it is case sensitive, for conflict
274
        # resolution tests
275
        tree.case_sensitive = True
276
        transform = TreeTransform(tree)
277
        self.addCleanup(transform.finalize)
278
        transform.new_file('file', transform.root, 'content')
279
        result = transform.find_conflicts()
280
        self.assertEqual([], result)
3008.1.15 by Aaron Bentley
Make case_sensitive an aspect of the transform, not the source tree
281
        transform.finalize()
3034.4.7 by Aaron Bentley
Add test for filename conflicts with existing files
282
        # Force the tree to report that it is case insensitive, for conflict
283
        # generation tests
284
        tree.case_sensitive = False
3008.1.15 by Aaron Bentley
Make case_sensitive an aspect of the transform, not the source tree
285
        transform = TreeTransform(tree)
286
        self.addCleanup(transform.finalize)
287
        transform.new_file('file', transform.root, 'content')
3034.4.7 by Aaron Bentley
Add test for filename conflicts with existing files
288
        result = transform.find_conflicts()
289
        self.assertEqual([('duplicate', 'new-1', 'new-2', 'file')], result)
290
3034.4.1 by Aaron Bentley
Start handling case-insensitivity
291
    def test_resolve_case_insensitive_conflict(self):
292
        tree = self.make_branch_and_tree('tree')
293
        # Don't try this at home, kids!
294
        # Force the tree to report that it is case insensitive, for conflict
295
        # resolution tests
296
        tree.case_sensitive = False
297
        transform = TreeTransform(tree)
298
        self.addCleanup(transform.finalize)
299
        transform.new_file('file', transform.root, 'content')
300
        transform.new_file('FiLe', transform.root, 'content')
301
        resolve_conflicts(transform)
302
        transform.apply()
303
        self.failUnlessExists('tree/file')
304
        self.failUnlessExists('tree/FiLe.moved')
305
3034.4.2 by Aaron Bentley
Get conflict handling and case-insensitive tree creation under test
306
    def test_resolve_checkout_case_conflict(self):
3034.4.1 by Aaron Bentley
Start handling case-insensitivity
307
        tree = self.make_branch_and_tree('tree')
308
        # Don't try this at home, kids!
309
        # Force the tree to report that it is case insensitive, for conflict
310
        # resolution tests
311
        tree.case_sensitive = False
312
        transform = TreeTransform(tree)
313
        self.addCleanup(transform.finalize)
314
        transform.new_file('file', transform.root, 'content')
315
        transform.new_file('FiLe', transform.root, 'content')
3034.4.2 by Aaron Bentley
Get conflict handling and case-insensitive tree creation under test
316
        resolve_conflicts(transform,
317
                          pass_func=lambda t, c: resolve_checkout(t, c, []))
3034.4.1 by Aaron Bentley
Start handling case-insensitivity
318
        transform.apply()
319
        self.failUnlessExists('tree/file')
320
        self.failUnlessExists('tree/FiLe.moved')
321
3034.4.2 by Aaron Bentley
Get conflict handling and case-insensitive tree creation under test
322
    def test_apply_case_conflict(self):
3034.4.6 by Aaron Bentley
Update apply_case_conflict tests
323
        """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
324
        tree = self.make_branch_and_tree('tree')
325
        transform = TreeTransform(tree)
326
        self.addCleanup(transform.finalize)
327
        transform.new_file('file', transform.root, 'content')
328
        transform.new_file('FiLe', transform.root, 'content')
3034.4.6 by Aaron Bentley
Update apply_case_conflict tests
329
        dir = transform.new_directory('dir', transform.root)
330
        transform.new_file('dirfile', dir, 'content')
331
        transform.new_file('dirFiLe', dir, 'content')
332
        resolve_conflicts(transform)
3034.4.2 by Aaron Bentley
Get conflict handling and case-insensitive tree creation under test
333
        transform.apply()
3034.4.3 by Aaron Bentley
Add case-sensitivity handling to WorkingTree
334
        self.failUnlessExists('tree/file')
335
        if not os.path.exists('tree/FiLe.moved'):
336
            self.failUnlessExists('tree/FiLe')
3034.4.6 by Aaron Bentley
Update apply_case_conflict tests
337
        self.failUnlessExists('tree/dir/dirfile')
338
        if not os.path.exists('tree/dir/dirFiLe.moved'):
339
            self.failUnlessExists('tree/dir/dirFiLe')
3034.4.2 by Aaron Bentley
Get conflict handling and case-insensitive tree creation under test
340
3034.4.1 by Aaron Bentley
Start handling case-insensitivity
341
    def test_case_insensitive_limbo(self):
342
        tree = self.make_branch_and_tree('tree')
343
        # Don't try this at home, kids!
344
        # Force the tree to report that it is case insensitive
345
        tree.case_sensitive = False
346
        transform = TreeTransform(tree)
347
        self.addCleanup(transform.finalize)
348
        dir = transform.new_directory('dir', transform.root)
349
        first = transform.new_file('file', dir, 'content')
350
        second = transform.new_file('FiLe', dir, 'content')
351
        self.assertContainsRe(transform._limbo_name(first), 'new-1/file')
352
        self.assertNotContainsRe(transform._limbo_name(second), 'new-1/FiLe')
353
1558.7.11 by Aaron Bentley
Avoid spurious conflict on add/delete
354
    def test_add_del(self):
355
        start, root = self.get_transform()
356
        start.new_directory('a', root, 'a')
357
        start.apply()
358
        transform, root = self.get_transform()
359
        transform.delete_versioned(transform.trans_id_tree_file_id('a'))
360
        transform.new_directory('a', root, 'a')
361
        transform.apply()
362
1534.7.46 by Aaron Bentley
Ensured a conflict when parents of versioned files are unversioned
363
    def test_unversioning(self):
1534.7.59 by Aaron Bentley
Simplified tests
364
        create_tree, root = self.get_transform()
365
        parent_id = create_tree.new_directory('parent', root, 'parent-id')
366
        create_tree.new_file('child', parent_id, 'child', 'child-id')
367
        create_tree.apply()
368
        unversion = TreeTransform(self.wt)
369
        self.addCleanup(unversion.finalize)
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
370
        parent = unversion.trans_id_tree_path('parent')
1534.7.59 by Aaron Bentley
Simplified tests
371
        unversion.unversion_file(parent)
372
        self.assertEqual(unversion.find_conflicts(), 
373
                         [('unversioned parent', parent_id)])
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
374
        file_id = unversion.trans_id_tree_file_id('child-id')
1534.7.59 by Aaron Bentley
Simplified tests
375
        unversion.unversion_file(file_id)
376
        unversion.apply()
1534.7.46 by Aaron Bentley
Ensured a conflict when parents of versioned files are unversioned
377
1534.7.36 by Aaron Bentley
Added rename tests
378
    def test_name_invariants(self):
1534.7.59 by Aaron Bentley
Simplified tests
379
        create_tree, root = self.get_transform()
380
        # prepare tree
1731.1.33 by Aaron Bentley
Revert no-special-root changes
381
        root = create_tree.root
1534.7.59 by Aaron Bentley
Simplified tests
382
        create_tree.new_file('name1', root, 'hello1', 'name1')
383
        create_tree.new_file('name2', root, 'hello2', 'name2')
384
        ddir = create_tree.new_directory('dying_directory', root, 'ddir')
385
        create_tree.new_file('dying_file', ddir, 'goodbye1', 'dfile')
386
        create_tree.new_file('moving_file', ddir, 'later1', 'mfile')
387
        create_tree.new_file('moving_file2', root, 'later2', 'mfile2')
388
        create_tree.apply()
389
390
        mangle_tree,root = self.get_transform()
1731.1.33 by Aaron Bentley
Revert no-special-root changes
391
        root = mangle_tree.root
1534.7.59 by Aaron Bentley
Simplified tests
392
        #swap names
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
393
        name1 = mangle_tree.trans_id_tree_file_id('name1')
394
        name2 = mangle_tree.trans_id_tree_file_id('name2')
1534.7.59 by Aaron Bentley
Simplified tests
395
        mangle_tree.adjust_path('name2', root, name1)
396
        mangle_tree.adjust_path('name1', root, name2)
397
398
        #tests for deleting parent directories 
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
399
        ddir = mangle_tree.trans_id_tree_file_id('ddir')
1534.7.59 by Aaron Bentley
Simplified tests
400
        mangle_tree.delete_contents(ddir)
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
401
        dfile = mangle_tree.trans_id_tree_file_id('dfile')
1534.7.59 by Aaron Bentley
Simplified tests
402
        mangle_tree.delete_versioned(dfile)
403
        mangle_tree.unversion_file(dfile)
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
404
        mfile = mangle_tree.trans_id_tree_file_id('mfile')
1534.7.59 by Aaron Bentley
Simplified tests
405
        mangle_tree.adjust_path('mfile', root, mfile)
406
407
        #tests for adding parent directories
408
        newdir = mangle_tree.new_directory('new_directory', root, 'newdir')
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
409
        mfile2 = mangle_tree.trans_id_tree_file_id('mfile2')
1534.7.59 by Aaron Bentley
Simplified tests
410
        mangle_tree.adjust_path('mfile2', newdir, mfile2)
411
        mangle_tree.new_file('newfile', newdir, 'hello3', 'dfile')
412
        self.assertEqual(mangle_tree.final_file_id(mfile2), 'mfile2')
413
        self.assertEqual(mangle_tree.final_parent(mfile2), newdir)
414
        self.assertEqual(mangle_tree.final_file_id(mfile2), 'mfile2')
415
        mangle_tree.apply()
416
        self.assertEqual(file(self.wt.abspath('name1')).read(), 'hello2')
417
        self.assertEqual(file(self.wt.abspath('name2')).read(), 'hello1')
1534.7.176 by abentley
Fixed up tests for Windows
418
        mfile2_path = self.wt.abspath(pathjoin('new_directory','mfile2'))
1534.7.41 by Aaron Bentley
Got inventory ID movement working
419
        self.assertEqual(mangle_tree.final_parent(mfile2), newdir)
1534.7.38 by Aaron Bentley
Tested adding paths
420
        self.assertEqual(file(mfile2_path).read(), 'later2')
1534.7.59 by Aaron Bentley
Simplified tests
421
        self.assertEqual(self.wt.id2path('mfile2'), 'new_directory/mfile2')
422
        self.assertEqual(self.wt.path2id('new_directory/mfile2'), 'mfile2')
1534.7.176 by abentley
Fixed up tests for Windows
423
        newfile_path = self.wt.abspath(pathjoin('new_directory','newfile'))
1534.7.38 by Aaron Bentley
Tested adding paths
424
        self.assertEqual(file(newfile_path).read(), 'hello3')
1534.7.59 by Aaron Bentley
Simplified tests
425
        self.assertEqual(self.wt.path2id('dying_directory'), 'ddir')
426
        self.assertIs(self.wt.path2id('dying_directory/dying_file'), None)
1534.7.176 by abentley
Fixed up tests for Windows
427
        mfile2_path = self.wt.abspath(pathjoin('new_directory','mfile2'))
1534.7.43 by abentley
Fixed some Windows bugs, introduced a conflicts bug
428
1534.7.150 by Aaron Bentley
Handled simultaneous renames of parent and child better
429
    def test_both_rename(self):
430
        create_tree,root = self.get_transform()
431
        newdir = create_tree.new_directory('selftest', root, 'selftest-id')
432
        create_tree.new_file('blackbox.py', newdir, 'hello1', 'blackbox-id')
433
        create_tree.apply()        
434
        mangle_tree,root = self.get_transform()
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
435
        selftest = mangle_tree.trans_id_tree_file_id('selftest-id')
436
        blackbox = mangle_tree.trans_id_tree_file_id('blackbox-id')
1534.7.150 by Aaron Bentley
Handled simultaneous renames of parent and child better
437
        mangle_tree.adjust_path('test', root, selftest)
438
        mangle_tree.adjust_path('test_too_much', root, selftest)
439
        mangle_tree.set_executability(True, blackbox)
440
        mangle_tree.apply()
441
442
    def test_both_rename2(self):
443
        create_tree,root = self.get_transform()
444
        bzrlib = create_tree.new_directory('bzrlib', root, 'bzrlib-id')
445
        tests = create_tree.new_directory('tests', bzrlib, 'tests-id')
446
        blackbox = create_tree.new_directory('blackbox', tests, 'blackbox-id')
447
        create_tree.new_file('test_too_much.py', blackbox, 'hello1', 
448
                             'test_too_much-id')
449
        create_tree.apply()        
450
        mangle_tree,root = self.get_transform()
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
451
        bzrlib = mangle_tree.trans_id_tree_file_id('bzrlib-id')
452
        tests = mangle_tree.trans_id_tree_file_id('tests-id')
453
        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
454
        mangle_tree.adjust_path('selftest', bzrlib, tests)
455
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much) 
456
        mangle_tree.set_executability(True, test_too_much)
457
        mangle_tree.apply()
458
459
    def test_both_rename3(self):
460
        create_tree,root = self.get_transform()
461
        tests = create_tree.new_directory('tests', root, 'tests-id')
462
        create_tree.new_file('test_too_much.py', tests, 'hello1', 
463
                             'test_too_much-id')
464
        create_tree.apply()        
465
        mangle_tree,root = self.get_transform()
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
466
        tests = mangle_tree.trans_id_tree_file_id('tests-id')
467
        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
468
        mangle_tree.adjust_path('selftest', root, tests)
469
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much) 
470
        mangle_tree.set_executability(True, test_too_much)
471
        mangle_tree.apply()
472
1534.7.48 by Aaron Bentley
Ensured we can move/rename dangling inventory entries
473
    def test_move_dangling_ie(self):
1534.7.59 by Aaron Bentley
Simplified tests
474
        create_tree, root = self.get_transform()
475
        # prepare tree
1731.1.33 by Aaron Bentley
Revert no-special-root changes
476
        root = create_tree.root
1534.7.59 by Aaron Bentley
Simplified tests
477
        create_tree.new_file('name1', root, 'hello1', 'name1')
478
        create_tree.apply()
479
        delete_contents, root = self.get_transform()
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
480
        file = delete_contents.trans_id_tree_file_id('name1')
1534.7.59 by Aaron Bentley
Simplified tests
481
        delete_contents.delete_contents(file)
482
        delete_contents.apply()
483
        move_id, root = self.get_transform()
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
484
        name1 = move_id.trans_id_tree_file_id('name1')
1534.7.59 by Aaron Bentley
Simplified tests
485
        newdir = move_id.new_directory('dir', root, 'newdir')
486
        move_id.adjust_path('name2', newdir, name1)
487
        move_id.apply()
1534.7.48 by Aaron Bentley
Ensured we can move/rename dangling inventory entries
488
        
1534.7.50 by Aaron Bentley
Detect duplicate inventory ids
489
    def test_replace_dangling_ie(self):
1534.7.59 by Aaron Bentley
Simplified tests
490
        create_tree, root = self.get_transform()
491
        # prepare tree
1731.1.33 by Aaron Bentley
Revert no-special-root changes
492
        root = create_tree.root
1534.7.59 by Aaron Bentley
Simplified tests
493
        create_tree.new_file('name1', root, 'hello1', 'name1')
494
        create_tree.apply()
495
        delete_contents = TreeTransform(self.wt)
496
        self.addCleanup(delete_contents.finalize)
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
497
        file = delete_contents.trans_id_tree_file_id('name1')
1534.7.59 by Aaron Bentley
Simplified tests
498
        delete_contents.delete_contents(file)
499
        delete_contents.apply()
500
        delete_contents.finalize()
501
        replace = TreeTransform(self.wt)
502
        self.addCleanup(replace.finalize)
503
        name2 = replace.new_file('name2', root, 'hello2', 'name1')
504
        conflicts = replace.find_conflicts()
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
505
        name1 = replace.trans_id_tree_file_id('name1')
1534.7.59 by Aaron Bentley
Simplified tests
506
        self.assertEqual(conflicts, [('duplicate id', name1, name2)])
507
        resolve_conflicts(replace)
508
        replace.apply()
1534.7.48 by Aaron Bentley
Ensured we can move/rename dangling inventory entries
509
1534.7.43 by abentley
Fixed some Windows bugs, introduced a conflicts bug
510
    def test_symlinks(self):
2949.5.1 by Alexander Belchenko
selftest: use SymlinkFeature instead of TestSkipped where appropriate
511
        self.requireFeature(SymlinkFeature)
1534.7.59 by Aaron Bentley
Simplified tests
512
        transform,root = self.get_transform()
513
        oz_id = transform.new_directory('oz', root, 'oz-id')
514
        wizard = transform.new_symlink('wizard', oz_id, 'wizard-target', 
515
                                       'wizard-id')
516
        wiz_id = transform.create_path('wizard2', oz_id)
517
        transform.create_symlink('behind_curtain', wiz_id)
518
        transform.version_file('wiz-id2', wiz_id)            
1534.7.71 by abentley
All tests pass under Windows
519
        transform.set_executability(True, wiz_id)
520
        self.assertEqual(transform.find_conflicts(), 
521
                         [('non-file executability', wiz_id)])
522
        transform.set_executability(None, wiz_id)
1534.7.59 by Aaron Bentley
Simplified tests
523
        transform.apply()
524
        self.assertEqual(self.wt.path2id('oz/wizard'), 'wizard-id')
1534.7.100 by Aaron Bentley
Fixed path-relative test cases
525
        self.assertEqual(file_kind(self.wt.abspath('oz/wizard')), 'symlink')
526
        self.assertEqual(os.readlink(self.wt.abspath('oz/wizard2')), 
527
                         'behind_curtain')
528
        self.assertEqual(os.readlink(self.wt.abspath('oz/wizard')),
529
                         'wizard-target')
1534.7.60 by Aaron Bentley
Tested existing conflict resolution functionality
530
3006.2.2 by Alexander Belchenko
tests added.
531
    def test_unable_create_symlink(self):
532
        def tt_helper():
533
            wt = self.make_branch_and_tree('.')
534
            tt = TreeTransform(wt)  # TreeTransform obtains write lock
535
            try:
536
                tt.new_symlink('foo', tt.root, 'bar')
537
                tt.apply()
538
            finally:
539
                wt.unlock()
540
        os_symlink = getattr(os, 'symlink', None)
541
        os.symlink = None
542
        try:
543
            err = self.assertRaises(errors.UnableCreateSymlink, tt_helper)
544
            self.assertEquals(
545
                "Unable to create symlink 'foo' on this platform",
546
                str(err))
547
        finally:
548
            if os_symlink:
549
                os.symlink = os_symlink
550
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
551
    def get_conflicted(self):
1534.7.60 by Aaron Bentley
Tested existing conflict resolution functionality
552
        create,root = self.get_transform()
553
        create.new_file('dorothy', root, 'dorothy', 'dorothy-id')
1534.7.61 by Aaron Bentley
Handled parent loops, missing parents, unversioned parents
554
        oz = create.new_directory('oz', root, 'oz-id')
555
        create.new_directory('emeraldcity', oz, 'emerald-id')
1534.7.60 by Aaron Bentley
Tested existing conflict resolution functionality
556
        create.apply()
557
        conflicts,root = self.get_transform()
1534.7.65 by Aaron Bentley
Text cleaup/docs
558
        # set up duplicate entry, duplicate id
1534.7.60 by Aaron Bentley
Tested existing conflict resolution functionality
559
        new_dorothy = conflicts.new_file('dorothy', root, 'dorothy', 
560
                                         'dorothy-id')
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
561
        old_dorothy = conflicts.trans_id_tree_file_id('dorothy-id')
562
        oz = conflicts.trans_id_tree_file_id('oz-id')
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
563
        # set up DeletedParent parent conflict
1534.7.65 by Aaron Bentley
Text cleaup/docs
564
        conflicts.delete_versioned(oz)
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
565
        emerald = conflicts.trans_id_tree_file_id('emerald-id')
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
566
        # set up MissingParent conflict
567
        munchkincity = conflicts.trans_id_file_id('munchkincity-id')
568
        conflicts.adjust_path('munchkincity', root, munchkincity)
569
        conflicts.new_directory('auntem', munchkincity, 'auntem-id')
1534.7.65 by Aaron Bentley
Text cleaup/docs
570
        # set up parent loop
1534.7.61 by Aaron Bentley
Handled parent loops, missing parents, unversioned parents
571
        conflicts.adjust_path('emeraldcity', emerald, emerald)
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
572
        return conflicts, emerald, oz, old_dorothy, new_dorothy
573
574
    def test_conflict_resolution(self):
575
        conflicts, emerald, oz, old_dorothy, new_dorothy =\
576
            self.get_conflicted()
1534.7.60 by Aaron Bentley
Tested existing conflict resolution functionality
577
        resolve_conflicts(conflicts)
578
        self.assertEqual(conflicts.final_name(old_dorothy), 'dorothy.moved')
579
        self.assertIs(conflicts.final_file_id(old_dorothy), None)
580
        self.assertEqual(conflicts.final_name(new_dorothy), 'dorothy')
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
581
        self.assertEqual(conflicts.final_file_id(new_dorothy), 'dorothy-id')
1534.7.64 by Aaron Bentley
Extra testing
582
        self.assertEqual(conflicts.final_parent(emerald), oz)
1534.7.63 by Aaron Bentley
Ensure transform can be applied after resolution
583
        conflicts.apply()
1534.7.62 by Aaron Bentley
Fixed moving versioned directories
584
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
585
    def test_cook_conflicts(self):
586
        tt, emerald, oz, old_dorothy, new_dorothy = self.get_conflicted()
587
        raw_conflicts = resolve_conflicts(tt)
588
        cooked_conflicts = cook_conflicts(raw_conflicts, tt)
1534.10.20 by Aaron Bentley
Got all tests passing
589
        duplicate = DuplicateEntry('Moved existing file to', 'dorothy.moved', 
590
                                   'dorothy', None, 'dorothy-id')
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
591
        self.assertEqual(cooked_conflicts[0], duplicate)
1534.10.20 by Aaron Bentley
Got all tests passing
592
        duplicate_id = DuplicateID('Unversioned existing file', 
593
                                   'dorothy.moved', 'dorothy', None,
594
                                   'dorothy-id')
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
595
        self.assertEqual(cooked_conflicts[1], duplicate_id)
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
596
        missing_parent = MissingParent('Created directory', 'munchkincity',
597
                                       'munchkincity-id')
598
        deleted_parent = DeletingParent('Not deleting', 'oz', 'oz-id')
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
599
        self.assertEqual(cooked_conflicts[2], missing_parent)
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
600
        unversioned_parent = UnversionedParent('Versioned directory',
601
                                               'munchkincity',
602
                                               'munchkincity-id')
603
        unversioned_parent2 = UnversionedParent('Versioned directory', 'oz',
1534.10.20 by Aaron Bentley
Got all tests passing
604
                                               'oz-id')
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
605
        self.assertEqual(cooked_conflicts[3], unversioned_parent)
1534.10.20 by Aaron Bentley
Got all tests passing
606
        parent_loop = ParentLoop('Cancelled move', 'oz/emeraldcity', 
607
                                 'oz/emeraldcity', 'emerald-id', 'emerald-id')
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
608
        self.assertEqual(cooked_conflicts[4], deleted_parent)
609
        self.assertEqual(cooked_conflicts[5], unversioned_parent2)
610
        self.assertEqual(cooked_conflicts[6], parent_loop)
611
        self.assertEqual(len(cooked_conflicts), 7)
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
612
        tt.finalize()
613
614
    def test_string_conflicts(self):
615
        tt, emerald, oz, old_dorothy, new_dorothy = self.get_conflicted()
616
        raw_conflicts = resolve_conflicts(tt)
617
        cooked_conflicts = cook_conflicts(raw_conflicts, tt)
618
        tt.finalize()
1534.10.24 by Aaron Bentley
Eliminated conflicts_to_strings, made remove_files a ConflictList member
619
        conflicts_s = [str(c) for c in cooked_conflicts]
1534.7.171 by Aaron Bentley
Implemented stringifying filesystem conflicts
620
        self.assertEqual(len(cooked_conflicts), len(conflicts_s))
621
        self.assertEqual(conflicts_s[0], 'Conflict adding file dorothy.  '
622
                                         'Moved existing file to '
623
                                         'dorothy.moved.')
624
        self.assertEqual(conflicts_s[1], 'Conflict adding id to dorothy.  '
625
                                         'Unversioned existing file '
626
                                         'dorothy.moved.')
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
627
        self.assertEqual(conflicts_s[2], 'Conflict adding files to'
628
                                         ' munchkincity.  Created directory.')
629
        self.assertEqual(conflicts_s[3], 'Conflict because munchkincity is not'
630
                                         ' versioned, but has versioned'
631
                                         ' children.  Versioned directory.')
1551.8.23 by Aaron Bentley
Tweaked conflict message to be more understandable
632
        self.assertEqualDiff(conflicts_s[4], "Conflict: can't delete oz because it"
633
                                         " is not empty.  Not deleting.")
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
634
        self.assertEqual(conflicts_s[5], 'Conflict because oz is not'
635
                                         ' versioned, but has versioned'
636
                                         ' children.  Versioned directory.')
637
        self.assertEqual(conflicts_s[6], 'Conflict moving oz/emeraldcity into'
1534.7.171 by Aaron Bentley
Implemented stringifying filesystem conflicts
638
                                         ' oz/emeraldcity.  Cancelled move.')
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
639
3144.4.2 by Aaron Bentley
Handle non-directory parent conflicts (abentley, #177390)
640
    def prepare_wrong_parent_kind(self):
641
        tt, root = self.get_transform()
642
        tt.new_file('parent', root, 'contents', 'parent-id')
643
        tt.apply()
644
        tt, root = self.get_transform()
645
        parent_id = tt.trans_id_file_id('parent-id')
646
        tt.new_file('child,', parent_id, 'contents2', 'file-id')
647
        return tt
648
3144.4.1 by Aaron Bentley
Handle trying to list parents of a non-directory
649
    def test_find_conflicts_wrong_parent_kind(self):
3144.4.2 by Aaron Bentley
Handle non-directory parent conflicts (abentley, #177390)
650
        tt = self.prepare_wrong_parent_kind()
3144.4.1 by Aaron Bentley
Handle trying to list parents of a non-directory
651
        tt.find_conflicts()
652
3144.4.2 by Aaron Bentley
Handle non-directory parent conflicts (abentley, #177390)
653
    def test_resolve_conflicts_wrong_existing_parent_kind(self):
654
        tt = self.prepare_wrong_parent_kind()
655
        raw_conflicts = resolve_conflicts(tt)
656
        self.assertEqual(set([('non-directory parent', 'Created directory',
657
                         'new-3')]), raw_conflicts)
658
        cooked_conflicts = cook_conflicts(raw_conflicts, tt)
659
        self.assertEqual([NonDirectoryParent('Created directory', 'parent.new',
660
        'parent-id')], cooked_conflicts)
661
        tt.apply()
662
        self.assertEqual(None, self.wt.path2id('parent'))
663
        self.assertEqual('parent-id', self.wt.path2id('parent.new'))
664
665
    def test_resolve_conflicts_wrong_new_parent_kind(self):
666
        tt, root = self.get_transform()
667
        parent_id = tt.new_directory('parent', root, 'parent-id')
668
        tt.new_file('child,', parent_id, 'contents2', 'file-id')
669
        tt.apply()
670
        tt, root = self.get_transform()
671
        parent_id = tt.trans_id_file_id('parent-id')
672
        tt.delete_contents(parent_id)
673
        tt.create_file('contents', parent_id)
674
        raw_conflicts = resolve_conflicts(tt)
675
        self.assertEqual(set([('non-directory parent', 'Created directory',
676
                         'new-3')]), raw_conflicts)
677
        tt.apply()
678
        self.assertEqual(None, self.wt.path2id('parent'))
679
        self.assertEqual('parent-id', self.wt.path2id('parent.new'))
680
681
    def test_resolve_conflicts_wrong_parent_kind_unversioned(self):
682
        tt, root = self.get_transform()
683
        parent_id = tt.new_directory('parent', root)
684
        tt.new_file('child,', parent_id, 'contents2')
685
        tt.apply()
686
        tt, root = self.get_transform()
687
        parent_id = tt.trans_id_tree_path('parent')
688
        tt.delete_contents(parent_id)
689
        tt.create_file('contents', parent_id)
690
        resolve_conflicts(tt)
691
        tt.apply()
692
        self.assertIs(None, self.wt.path2id('parent'))
693
        self.assertIs(None, self.wt.path2id('parent.new'))
694
1534.7.62 by Aaron Bentley
Fixed moving versioned directories
695
    def test_moving_versioned_directories(self):
696
        create, root = self.get_transform()
697
        kansas = create.new_directory('kansas', root, 'kansas-id')
698
        create.new_directory('house', kansas, 'house-id')
699
        create.new_directory('oz', root, 'oz-id')
700
        create.apply()
701
        cyclone, root = self.get_transform()
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
702
        oz = cyclone.trans_id_tree_file_id('oz-id')
703
        house = cyclone.trans_id_tree_file_id('house-id')
1534.7.62 by Aaron Bentley
Fixed moving versioned directories
704
        cyclone.adjust_path('house', oz, house)
705
        cyclone.apply()
1534.7.66 by Aaron Bentley
Ensured we don't accidentally move the root directory
706
707
    def test_moving_root(self):
1534.7.68 by Aaron Bentley
Got semi-reasonable root directory renaming working
708
        create, root = self.get_transform()
709
        fun = create.new_directory('fun', root, 'fun-id')
710
        create.new_directory('sun', root, 'sun-id')
711
        create.new_directory('moon', root, 'moon')
712
        create.apply()
1534.7.66 by Aaron Bentley
Ensured we don't accidentally move the root directory
713
        transform, root = self.get_transform()
1534.7.68 by Aaron Bentley
Got semi-reasonable root directory renaming working
714
        transform.adjust_root_path('oldroot', fun)
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
715
        new_root=transform.trans_id_tree_path('')
1534.7.69 by Aaron Bentley
Got real root moves working
716
        transform.version_file('new-root', new_root)
1534.7.68 by Aaron Bentley
Got semi-reasonable root directory renaming working
717
        transform.apply()
1534.7.93 by Aaron Bentley
Added text merge test
718
1534.7.114 by Aaron Bentley
Added file renaming test case
719
    def test_renames(self):
720
        create, root = self.get_transform()
721
        old = create.new_directory('old-parent', root, 'old-id')
722
        intermediate = create.new_directory('intermediate', old, 'im-id')
723
        myfile = create.new_file('myfile', intermediate, 'myfile-text',
724
                                 'myfile-id')
725
        create.apply()
726
        rename, root = self.get_transform()
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
727
        old = rename.trans_id_file_id('old-id')
1534.7.114 by Aaron Bentley
Added file renaming test case
728
        rename.adjust_path('new', root, old)
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
729
        myfile = rename.trans_id_file_id('myfile-id')
1534.7.114 by Aaron Bentley
Added file renaming test case
730
        rename.set_executability(True, myfile)
731
        rename.apply()
732
1534.7.123 by Aaron Bentley
Fixed handling of unversioned files
733
    def test_find_interesting(self):
734
        create, root = self.get_transform()
735
        wt = create._tree
736
        create.new_file('vfile', root, 'myfile-text', 'myfile-id')
737
        create.new_file('uvfile', root, 'othertext')
738
        create.apply()
2255.7.48 by Robert Collins
Deprecated and make work with DirState trees 'transform.find_interesting'.
739
        result = self.applyDeprecated(symbol_versioning.zero_fifteen,
740
            find_interesting, wt, wt, ['vfile'])
741
        self.assertEqual(result, set(['myfile-id']))
1534.7.123 by Aaron Bentley
Fixed handling of unversioned files
742
1740.2.4 by Aaron Bentley
Update transform tests and docs
743
    def test_set_executability_order(self):
744
        """Ensure that executability behaves the same, no matter what order.
745
        
746
        - create file and set executability simultaneously
747
        - create file and set executability afterward
748
        - unsetting the executability of a file whose executability has not been
749
        declared should throw an exception (this may happen when a
750
        merge attempts to create a file with a duplicate ID)
751
        """
752
        transform, root = self.get_transform()
753
        wt = transform._tree
3034.2.1 by Aaron Bentley
Fix is_executable tests for win32
754
        wt.lock_read()
755
        self.addCleanup(wt.unlock)
1740.2.4 by Aaron Bentley
Update transform tests and docs
756
        transform.new_file('set_on_creation', root, 'Set on creation', 'soc',
757
                           True)
2502.1.1 by Aaron Bentley
Ensure renames only root children are renamed when building trees
758
        sac = transform.new_file('set_after_creation', root,
759
                                 'Set after creation', 'sac')
1740.2.4 by Aaron Bentley
Update transform tests and docs
760
        transform.set_executability(True, sac)
2502.1.1 by Aaron Bentley
Ensure renames only root children are renamed when building trees
761
        uws = transform.new_file('unset_without_set', root, 'Unset badly',
762
                                 'uws')
1740.2.4 by Aaron Bentley
Update transform tests and docs
763
        self.assertRaises(KeyError, transform.set_executability, None, uws)
764
        transform.apply()
765
        self.assertTrue(wt.is_executable('soc'))
766
        self.assertTrue(wt.is_executable('sac'))
767
1534.12.2 by Aaron Bentley
Added test for preserving file mode
768
    def test_preserve_mode(self):
769
        """File mode is preserved when replacing content"""
770
        if sys.platform == 'win32':
771
            raise TestSkipped('chmod has no effect on win32')
772
        transform, root = self.get_transform()
773
        transform.new_file('file1', root, 'contents', 'file1-id', True)
774
        transform.apply()
3146.4.12 by Aaron Bentley
Add needed write lock to test
775
        self.wt.lock_write()
776
        self.addCleanup(self.wt.unlock)
1534.12.2 by Aaron Bentley
Added test for preserving file mode
777
        self.assertTrue(self.wt.is_executable('file1-id'))
778
        transform, root = self.get_transform()
779
        file1_id = transform.trans_id_tree_file_id('file1-id')
780
        transform.delete_contents(file1_id)
781
        transform.create_file('contents2', file1_id)
782
        transform.apply()
783
        self.assertTrue(self.wt.is_executable('file1-id'))
784
2027.1.1 by John Arbash Meinel
Fix bug #56549, and write a direct test that the right path is being statted
785
    def test__set_mode_stats_correctly(self):
786
        """_set_mode stats to determine file mode."""
787
        if sys.platform == 'win32':
788
            raise TestSkipped('chmod has no effect on win32')
789
790
        stat_paths = []
791
        real_stat = os.stat
792
        def instrumented_stat(path):
793
            stat_paths.append(path)
794
            return real_stat(path)
795
796
        transform, root = self.get_transform()
797
798
        bar1_id = transform.new_file('bar', root, 'bar contents 1\n',
799
                                     file_id='bar-id-1', executable=False)
800
        transform.apply()
801
802
        transform, root = self.get_transform()
803
        bar1_id = transform.trans_id_tree_path('bar')
804
        bar2_id = transform.trans_id_tree_path('bar2')
805
        try:
806
            os.stat = instrumented_stat
807
            transform.create_file('bar2 contents\n', bar2_id, mode_id=bar1_id)
808
        finally:
809
            os.stat = real_stat
810
            transform.finalize()
811
812
        bar1_abspath = self.wt.abspath('bar')
813
        self.assertEqual([bar1_abspath], stat_paths)
814
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
815
    def test_iter_changes(self):
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
816
        self.wt.set_root_id('eert_toor')
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
817
        transform, root = self.get_transform()
818
        transform.new_file('old', root, 'blah', 'id-1', True)
819
        transform.apply()
820
        transform, root = self.get_transform()
821
        try:
822
            self.assertEqual([], list(transform._iter_changes()))
823
            old = transform.trans_id_tree_file_id('id-1')
824
            transform.unversion_file(old)
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
825
            self.assertEqual([('id-1', ('old', None), False, (True, False),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
826
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
827
                (True, True))], list(transform._iter_changes()))
828
            transform.new_directory('new', root, 'id-1')
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
829
            self.assertEqual([('id-1', ('old', 'new'), True, (True, True),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
830
                ('eert_toor', 'eert_toor'), ('old', 'new'),
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
831
                ('file', 'directory'),
832
                (True, False))], list(transform._iter_changes()))
833
        finally:
834
            transform.finalize()
835
836
    def test_iter_changes_new(self):
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
837
        self.wt.set_root_id('eert_toor')
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
838
        transform, root = self.get_transform()
839
        transform.new_file('old', root, 'blah')
840
        transform.apply()
841
        transform, root = self.get_transform()
842
        try:
843
            old = transform.trans_id_tree_path('old')
844
            transform.version_file('id-1', old)
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
845
            self.assertEqual([('id-1', (None, 'old'), False, (False, True),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
846
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
847
                (False, False))], list(transform._iter_changes()))
848
        finally:
849
            transform.finalize()
850
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
851
    def test_iter_changes_modifications(self):
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
852
        self.wt.set_root_id('eert_toor')
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
853
        transform, root = self.get_transform()
854
        transform.new_file('old', root, 'blah', 'id-1')
855
        transform.new_file('new', root, 'blah')
856
        transform.new_directory('subdir', root, 'subdir-id')
857
        transform.apply()
858
        transform, root = self.get_transform()
859
        try:
860
            old = transform.trans_id_tree_path('old')
861
            subdir = transform.trans_id_tree_file_id('subdir-id')
862
            new = transform.trans_id_tree_path('new')
863
            self.assertEqual([], list(transform._iter_changes()))
864
865
            #content deletion
866
            transform.delete_contents(old)
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
867
            self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
868
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', None),
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
869
                (False, False))], list(transform._iter_changes()))
870
871
            #content change
872
            transform.create_file('blah', old)
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
873
            self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
874
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
875
                (False, False))], list(transform._iter_changes()))
876
            transform.cancel_deletion(old)
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
877
            self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
878
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
879
                (False, False))], list(transform._iter_changes()))
880
            transform.cancel_creation(old)
881
882
            # move file_id to a different file
883
            self.assertEqual([], list(transform._iter_changes()))
884
            transform.unversion_file(old)
885
            transform.version_file('id-1', new)
886
            transform.adjust_path('old', root, new)
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
887
            self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
888
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
889
                (False, False))], list(transform._iter_changes()))
890
            transform.cancel_versioning(new)
891
            transform._removed_id = set()
892
893
            #execute bit
894
            self.assertEqual([], list(transform._iter_changes()))
895
            transform.set_executability(True, old)
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
896
            self.assertEqual([('id-1', ('old', 'old'), False, (True, True),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
897
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
898
                (False, True))], list(transform._iter_changes()))
899
            transform.set_executability(None, old)
900
901
            # filename
902
            self.assertEqual([], list(transform._iter_changes()))
903
            transform.adjust_path('new', root, old)
904
            transform._new_parent = {}
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
905
            self.assertEqual([('id-1', ('old', 'new'), False, (True, True),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
906
                ('eert_toor', 'eert_toor'), ('old', 'new'), ('file', 'file'),
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
907
                (False, False))], list(transform._iter_changes()))
908
            transform._new_name = {}
909
910
            # parent directory
911
            self.assertEqual([], list(transform._iter_changes()))
912
            transform.adjust_path('new', subdir, old)
913
            transform._new_name = {}
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
914
            self.assertEqual([('id-1', ('old', 'subdir/old'), False,
2255.2.180 by Martin Pool
merge dirstate
915
                (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.
916
                ('file', 'file'), (False, False))],
917
                list(transform._iter_changes()))
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
918
            transform._new_path = {}
919
920
        finally:
921
            transform.finalize()
922
1551.11.7 by Aaron Bentley
Stop modified flag bleeding into later changes
923
    def test_iter_changes_modified_bleed(self):
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
924
        self.wt.set_root_id('eert_toor')
1551.11.7 by Aaron Bentley
Stop modified flag bleeding into later changes
925
        """Modified flag should not bleed from one change to another"""
926
        # unfortunately, we have no guarantee that file1 (which is modified)
927
        # will be applied before file2.  And if it's applied after file2, it
928
        # obviously can't bleed into file2's change output.  But for now, it
929
        # works.
930
        transform, root = self.get_transform()
931
        transform.new_file('file1', root, 'blah', 'id-1')
932
        transform.new_file('file2', root, 'blah', 'id-2')
933
        transform.apply()
934
        transform, root = self.get_transform()
935
        try:
936
            transform.delete_contents(transform.trans_id_file_id('id-1'))
937
            transform.set_executability(True,
938
            transform.trans_id_file_id('id-2'))
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
939
            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
940
                ('eert_toor', 'eert_toor'), ('file1', u'file1'),
1551.11.7 by Aaron Bentley
Stop modified flag bleeding into later changes
941
                ('file', None), (False, False)),
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
942
                ('id-2', (u'file2', u'file2'), False, (True, True),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
943
                ('eert_toor', 'eert_toor'), ('file2', u'file2'),
1551.11.7 by Aaron Bentley
Stop modified flag bleeding into later changes
944
                ('file', 'file'), (False, True))],
945
                list(transform._iter_changes()))
946
        finally:
947
            transform.finalize()
948
1551.10.37 by Aaron Bentley
recommit of TreeTransform._iter_changes fix with missing files
949
    def test_iter_changes_move_missing(self):
950
        """Test moving ids with no files around"""
951
        self.wt.set_root_id('toor_eert')
952
        # Need two steps because versioning a non-existant file is a conflict.
953
        transform, root = self.get_transform()
954
        transform.new_directory('floater', root, 'floater-id')
955
        transform.apply()
956
        transform, root = self.get_transform()
957
        transform.delete_contents(transform.trans_id_tree_path('floater'))
958
        transform.apply()
959
        transform, root = self.get_transform()
960
        floater = transform.trans_id_tree_path('floater')
961
        try:
962
            transform.adjust_path('flitter', root, floater)
963
            self.assertEqual([('floater-id', ('floater', 'flitter'), False,
964
            (True, True), ('toor_eert', 'toor_eert'), ('floater', 'flitter'),
965
            (None, None), (False, False))], list(transform._iter_changes()))
966
        finally:
967
            transform.finalize()
968
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
969
    def test_iter_changes_pointless(self):
970
        """Ensure that no-ops are not treated as modifications"""
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
971
        self.wt.set_root_id('eert_toor')
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
972
        transform, root = self.get_transform()
973
        transform.new_file('old', root, 'blah', 'id-1')
974
        transform.new_directory('subdir', root, 'subdir-id')
975
        transform.apply()
976
        transform, root = self.get_transform()
977
        try:
978
            old = transform.trans_id_tree_path('old')
979
            subdir = transform.trans_id_tree_file_id('subdir-id')
980
            self.assertEqual([], list(transform._iter_changes()))
981
            transform.delete_contents(subdir)
982
            transform.create_directory(subdir)
983
            transform.set_executability(False, old)
984
            transform.unversion_file(old)
985
            transform.version_file('id-1', old)
986
            transform.adjust_path('old', root, old)
987
            self.assertEqual([], list(transform._iter_changes()))
988
        finally:
989
            transform.finalize()
1534.7.93 by Aaron Bentley
Added text merge test
990
2502.1.1 by Aaron Bentley
Ensure renames only root children are renamed when building trees
991
    def test_rename_count(self):
992
        transform, root = self.get_transform()
2502.1.8 by Aaron Bentley
Updates from review comments
993
        transform.new_file('name1', root, 'contents')
2502.1.1 by Aaron Bentley
Ensure renames only root children are renamed when building trees
994
        self.assertEqual(transform.rename_count, 0)
995
        transform.apply()
996
        self.assertEqual(transform.rename_count, 1)
997
        transform2, root = self.get_transform()
2502.1.8 by Aaron Bentley
Updates from review comments
998
        transform2.adjust_path('name2', root,
999
                               transform2.trans_id_tree_path('name1'))
2502.1.1 by Aaron Bentley
Ensure renames only root children are renamed when building trees
1000
        self.assertEqual(transform2.rename_count, 0)
1001
        transform2.apply()
1002
        self.assertEqual(transform2.rename_count, 2)
1003
2502.1.2 by Aaron Bentley
Make the limited-renames functionality safer in the general case
1004
    def test_change_parent(self):
2502.1.8 by Aaron Bentley
Updates from review comments
1005
        """Ensure that after we change a parent, the results are still right.
1006
1007
        Renames and parent changes on pending transforms can happen as part
1008
        of conflict resolution, and are explicitly permitted by the
1009
        TreeTransform API.
1010
1011
        This test ensures they work correctly with the rename-avoidance
1012
        optimization.
1013
        """
2502.1.2 by Aaron Bentley
Make the limited-renames functionality safer in the general case
1014
        transform, root = self.get_transform()
2502.1.8 by Aaron Bentley
Updates from review comments
1015
        parent1 = transform.new_directory('parent1', root)
1016
        child1 = transform.new_file('child1', parent1, 'contents')
1017
        parent2 = transform.new_directory('parent2', root)
1018
        transform.adjust_path('child1', parent2, child1)
2502.1.2 by Aaron Bentley
Make the limited-renames functionality safer in the general case
1019
        transform.apply()
2502.1.8 by Aaron Bentley
Updates from review comments
1020
        self.failIfExists(self.wt.abspath('parent1/child1'))
1021
        self.failUnlessExists(self.wt.abspath('parent2/child1'))
1022
        # rename limbo/new-1 => parent1, rename limbo/new-3 => parent2
1023
        # 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
1024
        self.failUnlessEqual(2, transform.rename_count)
1025
1026
    def test_cancel_parent(self):
1027
        """Cancelling a parent doesn't cause deletion of a non-empty directory
1028
1029
        This is like the test_change_parent, except that we cancel the parent
1030
        before adjusting the path.  The transform must detect that the
1031
        directory is non-empty, and move children to safe locations.
1032
        """
1033
        transform, root = self.get_transform()
2502.1.8 by Aaron Bentley
Updates from review comments
1034
        parent1 = transform.new_directory('parent1', root)
1035
        child1 = transform.new_file('child1', parent1, 'contents')
1036
        child2 = transform.new_file('child2', parent1, 'contents')
1037
        try:
1038
            transform.cancel_creation(parent1)
1039
        except OSError:
1040
            self.fail('Failed to move child1 before deleting parent1')
1041
        transform.cancel_creation(child2)
1042
        transform.create_directory(parent1)
1043
        try:
1044
            transform.cancel_creation(parent1)
1045
        # If the transform incorrectly believes that child2 is still in
1046
        # parent1's limbo directory, it will try to rename it and fail
1047
        # because was already moved by the first cancel_creation.
1048
        except OSError:
1049
            self.fail('Transform still thinks child2 is a child of parent1')
1050
        parent2 = transform.new_directory('parent2', root)
1051
        transform.adjust_path('child1', parent2, child1)
2502.1.2 by Aaron Bentley
Make the limited-renames functionality safer in the general case
1052
        transform.apply()
2502.1.8 by Aaron Bentley
Updates from review comments
1053
        self.failIfExists(self.wt.abspath('parent1'))
1054
        self.failUnlessExists(self.wt.abspath('parent2/child1'))
1055
        # 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
1056
        self.failUnlessEqual(2, transform.rename_count)
1057
1058
    def test_adjust_and_cancel(self):
2502.1.8 by Aaron Bentley
Updates from review comments
1059
        """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
1060
        transform, root = self.get_transform()
2502.1.8 by Aaron Bentley
Updates from review comments
1061
        parent1 = transform.new_directory('parent1', root)
1062
        child1 = transform.new_file('child1', parent1, 'contents')
1063
        parent2 = transform.new_directory('parent2', root)
1064
        transform.adjust_path('child1', parent2, child1)
1065
        transform.cancel_creation(child1)
2502.1.2 by Aaron Bentley
Make the limited-renames functionality safer in the general case
1066
        try:
2502.1.8 by Aaron Bentley
Updates from review comments
1067
            transform.cancel_creation(parent1)
1068
        # if the transform thinks child1 is still in parent1's limbo
1069
        # 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
1070
        except OSError:
2502.1.8 by Aaron Bentley
Updates from review comments
1071
            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
1072
        transform.finalize()
1073
2502.1.3 by Aaron Bentley
Don't cause errors when creating contents for trans_ids with no parent/name
1074
    def test_noname_contents(self):
2502.1.8 by Aaron Bentley
Updates from review comments
1075
        """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
1076
        transform, root = self.get_transform()
2502.1.8 by Aaron Bentley
Updates from review comments
1077
        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
1078
        try:
2502.1.8 by Aaron Bentley
Updates from review comments
1079
            transform.create_directory(parent)
2502.1.3 by Aaron Bentley
Don't cause errors when creating contents for trans_ids with no parent/name
1080
        except KeyError:
1081
            self.fail("Can't handle contents with no name")
1082
        transform.finalize()
1083
2502.1.9 by Aaron Bentley
Add additional test for no-name contents
1084
    def test_noname_contents_nested(self):
1085
        """TreeTransform should permit deferring naming files."""
1086
        transform, root = self.get_transform()
1087
        parent = transform.trans_id_file_id('parent-id')
1088
        try:
1089
            transform.create_directory(parent)
1090
        except KeyError:
1091
            self.fail("Can't handle contents with no name")
1092
        child = transform.new_directory('child', parent)
1093
        transform.adjust_path('parent', root, parent)
1094
        transform.apply()
1095
        self.failUnlessExists(self.wt.abspath('parent/child'))
1096
        self.assertEqual(1, transform.rename_count)
1097
2502.1.4 by Aaron Bentley
Ensure we only reuse limbo names appropriately
1098
    def test_reuse_name(self):
1099
        """Avoid reusing the same limbo name for different files"""
1100
        transform, root = self.get_transform()
2502.1.8 by Aaron Bentley
Updates from review comments
1101
        parent = transform.new_directory('parent', root)
1102
        child1 = transform.new_directory('child', parent)
2502.1.4 by Aaron Bentley
Ensure we only reuse limbo names appropriately
1103
        try:
2502.1.8 by Aaron Bentley
Updates from review comments
1104
            child2 = transform.new_directory('child', parent)
2502.1.4 by Aaron Bentley
Ensure we only reuse limbo names appropriately
1105
        except OSError:
1106
            self.fail('Tranform tried to use the same limbo name twice')
2502.1.8 by Aaron Bentley
Updates from review comments
1107
        transform.adjust_path('child2', parent, child2)
2502.1.4 by Aaron Bentley
Ensure we only reuse limbo names appropriately
1108
        transform.apply()
2502.1.8 by Aaron Bentley
Updates from review comments
1109
        # limbo/new-1 => parent, limbo/new-3 => parent/child2
1110
        # child2 is put into top-level limbo because child1 has already
1111
        # claimed the direct limbo path when child2 is created.  There is no
1112
        # advantage in renaming files once they're in top-level limbo, except
1113
        # as part of apply.
2502.1.4 by Aaron Bentley
Ensure we only reuse limbo names appropriately
1114
        self.assertEqual(2, transform.rename_count)
1115
1116
    def test_reuse_when_first_moved(self):
1117
        """Don't avoid direct paths when it is safe to use them"""
1118
        transform, root = self.get_transform()
2502.1.8 by Aaron Bentley
Updates from review comments
1119
        parent = transform.new_directory('parent', root)
1120
        child1 = transform.new_directory('child', parent)
1121
        transform.adjust_path('child1', parent, child1)
1122
        child2 = transform.new_directory('child', parent)
2502.1.4 by Aaron Bentley
Ensure we only reuse limbo names appropriately
1123
        transform.apply()
2502.1.8 by Aaron Bentley
Updates from review comments
1124
        # limbo/new-1 => parent
2502.1.4 by Aaron Bentley
Ensure we only reuse limbo names appropriately
1125
        self.assertEqual(1, transform.rename_count)
1126
1127
    def test_reuse_after_cancel(self):
1128
        """Don't avoid direct paths when it is safe to use them"""
1129
        transform, root = self.get_transform()
2502.1.8 by Aaron Bentley
Updates from review comments
1130
        parent2 = transform.new_directory('parent2', root)
1131
        child1 = transform.new_directory('child1', parent2)
1132
        transform.cancel_creation(parent2)
1133
        transform.create_directory(parent2)
1134
        child2 = transform.new_directory('child1', parent2)
1135
        transform.adjust_path('child2', parent2, child1)
2502.1.4 by Aaron Bentley
Ensure we only reuse limbo names appropriately
1136
        transform.apply()
2502.1.8 by Aaron Bentley
Updates from review comments
1137
        # limbo/new-1 => parent2, limbo/new-2 => parent2/child1
2502.1.4 by Aaron Bentley
Ensure we only reuse limbo names appropriately
1138
        self.assertEqual(2, transform.rename_count)
1139
2502.1.7 by Aaron Bentley
Fix finalize deletion ordering
1140
    def test_finalize_order(self):
2502.1.8 by Aaron Bentley
Updates from review comments
1141
        """Finalize must be done in child-to-parent order"""
2502.1.7 by Aaron Bentley
Fix finalize deletion ordering
1142
        transform, root = self.get_transform()
2502.1.8 by Aaron Bentley
Updates from review comments
1143
        parent = transform.new_directory('parent', root)
1144
        child = transform.new_directory('child', parent)
2502.1.7 by Aaron Bentley
Fix finalize deletion ordering
1145
        try:
1146
            transform.finalize()
1147
        except OSError:
2502.1.8 by Aaron Bentley
Updates from review comments
1148
            self.fail('Tried to remove parent before child1')
2502.1.7 by Aaron Bentley
Fix finalize deletion ordering
1149
2502.1.13 by Aaron Bentley
Updates from review
1150
    def test_cancel_with_cancelled_child_should_succeed(self):
2502.1.12 by Aaron Bentley
Avoid renaming children with no content
1151
        transform, root = self.get_transform()
1152
        parent = transform.new_directory('parent', root)
1153
        child = transform.new_directory('child', parent)
1154
        transform.cancel_creation(child)
2502.1.13 by Aaron Bentley
Updates from review
1155
        transform.cancel_creation(parent)
2502.1.12 by Aaron Bentley
Avoid renaming children with no content
1156
        transform.finalize()
1157
2625.4.3 by Ian Clatworthy
Add a test to ensure the deprecation worked.
1158
    def test_change_entry(self):
2687.2.2 by Martin Pool
Fix up other references to 0.19
1159
        txt = 'bzrlib.transform.change_entry was deprecated in version 0.90.'
2625.4.3 by Ian Clatworthy
Add a test to ensure the deprecation worked.
1160
        self.callDeprecated([txt], change_entry, None, None, None, None, None,
1161
            None, None, None)
1162
3063.1.1 by Alexander Belchenko
Catch OSError 17 (file exists) in final phase of tree transform and show filename to user (#111758).
1163
    def test_case_insensitive_clash(self):
1164
        self.requireFeature(CaseInsensitiveFilesystemFeature)
1165
        def tt_helper():
1166
            wt = self.make_branch_and_tree('.')
1167
            tt = TreeTransform(wt)  # TreeTransform obtains write lock
1168
            try:
1169
                tt.new_file('foo', tt.root, 'bar')
1170
                tt.new_file('Foo', tt.root, 'spam')
3063.1.3 by Aaron Bentley
Update for Linux
1171
                # 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).
1172
                tt.apply(no_conflicts=True)
3063.1.3 by Aaron Bentley
Update for Linux
1173
            except:
3063.1.1 by Alexander Belchenko
Catch OSError 17 (file exists) in final phase of tree transform and show filename to user (#111758).
1174
                wt.unlock()
3063.1.3 by Aaron Bentley
Update for Linux
1175
                raise
3063.1.1 by Alexander Belchenko
Catch OSError 17 (file exists) in final phase of tree transform and show filename to user (#111758).
1176
        err = self.assertRaises(errors.FileExists, tt_helper)
1177
        self.assertContainsRe(str(err),
1178
            "^File exists: .+/foo")
1179
3063.1.2 by Alexander Belchenko
test for two directories clash
1180
    def test_two_directories_clash(self):
1181
        def tt_helper():
1182
            wt = self.make_branch_and_tree('.')
1183
            tt = TreeTransform(wt)  # TreeTransform obtains write lock
1184
            try:
3063.1.3 by Aaron Bentley
Update for Linux
1185
                foo_1 = tt.new_directory('foo', tt.root)
1186
                tt.new_directory('bar', foo_1)
1187
                foo_2 = tt.new_directory('foo', tt.root)
1188
                tt.new_directory('baz', foo_2)
1189
                # Lie to tt that we've already resolved all conflicts.
3063.1.2 by Alexander Belchenko
test for two directories clash
1190
                tt.apply(no_conflicts=True)
3063.1.3 by Aaron Bentley
Update for Linux
1191
            except:
3063.1.2 by Alexander Belchenko
test for two directories clash
1192
                wt.unlock()
3063.1.3 by Aaron Bentley
Update for Linux
1193
                raise
3063.1.2 by Alexander Belchenko
test for two directories clash
1194
        err = self.assertRaises(errors.FileExists, tt_helper)
1195
        self.assertContainsRe(str(err),
1196
            "^File exists: .+/foo")
1197
3100.1.1 by Aaron Bentley
Fix ImmortalLimbo errors when transforms fail
1198
    def test_two_directories_clash_finalize(self):
1199
        def tt_helper():
1200
            wt = self.make_branch_and_tree('.')
1201
            tt = TreeTransform(wt)  # TreeTransform obtains write lock
1202
            try:
1203
                foo_1 = tt.new_directory('foo', tt.root)
1204
                tt.new_directory('bar', foo_1)
1205
                foo_2 = tt.new_directory('foo', tt.root)
1206
                tt.new_directory('baz', foo_2)
1207
                # Lie to tt that we've already resolved all conflicts.
1208
                tt.apply(no_conflicts=True)
1209
            except:
1210
                tt.finalize()
1211
                raise
1212
        err = self.assertRaises(errors.FileExists, tt_helper)
1213
        self.assertContainsRe(str(err),
1214
            "^File exists: .+/foo")
1215
2502.1.3 by Aaron Bentley
Don't cause errors when creating contents for trans_ids with no parent/name
1216
1534.7.93 by Aaron Bentley
Added text merge test
1217
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).
1218
1731.1.33 by Aaron Bentley
Revert no-special-root changes
1219
    def __init__(self, dirname, root_id):
1534.7.101 by Aaron Bentley
Got conflicts on symlinks working properly
1220
        self.name = dirname
1534.7.93 by Aaron Bentley
Added text merge test
1221
        os.mkdir(dirname)
1558.1.3 by Aaron Bentley
Fixed deprecated op use in test suite
1222
        self.wt = BzrDir.create_standalone_workingtree(dirname)
1731.1.33 by Aaron Bentley
Revert no-special-root changes
1223
        self.wt.set_root_id(root_id)
1558.1.3 by Aaron Bentley
Fixed deprecated op use in test suite
1224
        self.b = self.wt.branch
1534.7.93 by Aaron Bentley
Added text merge test
1225
        self.tt = TreeTransform(self.wt)
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
1226
        self.root = self.tt.trans_id_tree_file_id(self.wt.get_root_id())
1534.7.93 by Aaron Bentley
Added text merge test
1227
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
1228
1534.7.95 by Aaron Bentley
Added more text merge tests
1229
def conflict_text(tree, merge):
1230
    template = '%s TREE\n%s%s\n%s%s MERGE-SOURCE\n'
1231
    return template % ('<' * 7, tree, '=' * 7, merge, '>' * 7)
1232
1534.7.93 by Aaron Bentley
Added text merge test
1233
1234
class TestTransformMerge(TestCaseInTempDir):
1235
    def test_text_merge(self):
2116.4.1 by John Arbash Meinel
Update file and revision id generators.
1236
        root_id = generate_ids.gen_root_id()
1731.1.33 by Aaron Bentley
Revert no-special-root changes
1237
        base = TransformGroup("base", root_id)
1534.7.93 by Aaron Bentley
Added text merge test
1238
        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
1239
        base.tt.new_file('b', base.root, 'b1', 'b')
1240
        base.tt.new_file('c', base.root, 'c', 'c')
1241
        base.tt.new_file('d', base.root, 'd', 'd')
1242
        base.tt.new_file('e', base.root, 'e', 'e')
1243
        base.tt.new_file('f', base.root, 'f', 'f')
1534.7.96 by Aaron Bentley
Tested with BASE as directory
1244
        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
1245
        base.tt.new_directory('h', base.root, 'h')
1534.7.93 by Aaron Bentley
Added text merge test
1246
        base.tt.apply()
1731.1.33 by Aaron Bentley
Revert no-special-root changes
1247
        other = TransformGroup("other", root_id)
1534.7.93 by Aaron Bentley
Added text merge test
1248
        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
1249
        other.tt.new_file('b', other.root, 'b2', 'b')
1250
        other.tt.new_file('c', other.root, 'c2', 'c')
1251
        other.tt.new_file('d', other.root, 'd', 'd')
1252
        other.tt.new_file('e', other.root, 'e2', 'e')
1253
        other.tt.new_file('f', other.root, 'f', 'f')
1534.7.96 by Aaron Bentley
Tested with BASE as directory
1254
        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
1255
        other.tt.new_file('h', other.root, 'h\ni\nj\nk\n', 'h')
1534.7.99 by Aaron Bentley
Handle non-existent BASE properly
1256
        other.tt.new_file('i', other.root, 'h\ni\nj\nk\n', 'i')
1534.7.93 by Aaron Bentley
Added text merge test
1257
        other.tt.apply()
1731.1.33 by Aaron Bentley
Revert no-special-root changes
1258
        this = TransformGroup("this", root_id)
1534.7.93 by Aaron Bentley
Added text merge test
1259
        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
1260
        this.tt.new_file('b', this.root, 'b', 'b')
1261
        this.tt.new_file('c', this.root, 'c', 'c')
1262
        this.tt.new_file('d', this.root, 'd2', 'd')
1263
        this.tt.new_file('e', this.root, 'e2', 'e')
1264
        this.tt.new_file('f', this.root, 'f', 'f')
1534.7.96 by Aaron Bentley
Tested with BASE as directory
1265
        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
1266
        this.tt.new_file('h', this.root, '1\n2\n3\n4\n', 'h')
1534.7.99 by Aaron Bentley
Handle non-existent BASE properly
1267
        this.tt.new_file('i', this.root, '1\n2\n3\n4\n', 'i')
1534.7.93 by Aaron Bentley
Added text merge test
1268
        this.tt.apply()
3008.1.11 by Michael Hudson
restore the default behaviour of Merge3Merger.__init__().
1269
        Merge3Merger(this.wt, this.wt, base.wt, other.wt)
3008.1.6 by Michael Hudson
chop up Merge3Merger.__init__ into pieces
1270
1534.7.95 by Aaron Bentley
Added more text merge tests
1271
        # textual merge
1534.7.93 by Aaron Bentley
Added text merge test
1272
        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
1273
        # three-way text conflict
1274
        self.assertEqual(this.wt.get_file('b').read(), 
1275
                         conflict_text('b', 'b2'))
1276
        # OTHER wins
1277
        self.assertEqual(this.wt.get_file('c').read(), 'c2')
1278
        # THIS wins
1279
        self.assertEqual(this.wt.get_file('d').read(), 'd2')
1280
        # Ambigious clean merge
1281
        self.assertEqual(this.wt.get_file('e').read(), 'e2')
1282
        # No change
1283
        self.assertEqual(this.wt.get_file('f').read(), 'f')
1534.7.97 by Aaron Bentley
Ensured foo.BASE is a directory if there's a conflict
1284
        # Correct correct results when THIS == OTHER 
1534.7.96 by Aaron Bentley
Tested with BASE as directory
1285
        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
1286
        # Text conflict when THIS & OTHER are text and BASE is dir
1287
        self.assertEqual(this.wt.get_file('h').read(), 
1288
                         conflict_text('1\n2\n3\n4\n', 'h\ni\nj\nk\n'))
1289
        self.assertEqual(this.wt.get_file_byname('h.THIS').read(),
1290
                         '1\n2\n3\n4\n')
1291
        self.assertEqual(this.wt.get_file_byname('h.OTHER').read(),
1292
                         'h\ni\nj\nk\n')
1293
        self.assertEqual(file_kind(this.wt.abspath('h.BASE')), 'directory')
1534.7.99 by Aaron Bentley
Handle non-existent BASE properly
1294
        self.assertEqual(this.wt.get_file('i').read(), 
1295
                         conflict_text('1\n2\n3\n4\n', 'h\ni\nj\nk\n'))
1296
        self.assertEqual(this.wt.get_file_byname('i.THIS').read(),
1297
                         '1\n2\n3\n4\n')
1298
        self.assertEqual(this.wt.get_file_byname('i.OTHER').read(),
1299
                         'h\ni\nj\nk\n')
1300
        self.assertEqual(os.path.exists(this.wt.abspath('i.BASE')), False)
1534.7.192 by Aaron Bentley
Record hashes produced by merges
1301
        modified = ['a', 'b', 'c', 'h', 'i']
1302
        merge_modified = this.wt.merge_modified()
1303
        self.assertSubset(merge_modified, modified)
1304
        self.assertEqual(len(merge_modified), len(modified))
1305
        file(this.wt.id2abspath('a'), 'wb').write('booga')
1306
        modified.pop(0)
1307
        merge_modified = this.wt.merge_modified()
1308
        self.assertSubset(merge_modified, modified)
1309
        self.assertEqual(len(merge_modified), len(modified))
1558.12.10 by Aaron Bentley
Be robust when merge_hash file_id not in inventory
1310
        this.wt.remove('b')
2796.1.4 by Aaron Bentley
Fix up various test cases
1311
        this.wt.revert()
1534.7.101 by Aaron Bentley
Got conflicts on symlinks working properly
1312
1313
    def test_file_merge(self):
2949.5.1 by Alexander Belchenko
selftest: use SymlinkFeature instead of TestSkipped where appropriate
1314
        self.requireFeature(SymlinkFeature)
2116.4.1 by John Arbash Meinel
Update file and revision id generators.
1315
        root_id = generate_ids.gen_root_id()
1731.1.33 by Aaron Bentley
Revert no-special-root changes
1316
        base = TransformGroup("BASE", root_id)
1317
        this = TransformGroup("THIS", root_id)
1318
        other = TransformGroup("OTHER", root_id)
1534.7.101 by Aaron Bentley
Got conflicts on symlinks working properly
1319
        for tg in this, base, other:
1320
            tg.tt.new_directory('a', tg.root, 'a')
1321
            tg.tt.new_symlink('b', tg.root, 'b', 'b')
1322
            tg.tt.new_file('c', tg.root, 'c', 'c')
1323
            tg.tt.new_symlink('d', tg.root, tg.name, 'd')
1534.7.104 by Aaron Bentley
Fixed set_versioned, enhanced conflict testing
1324
        targets = ((base, 'base-e', 'base-f', None, None), 
1325
                   (this, 'other-e', 'this-f', 'other-g', 'this-h'), 
1326
                   (other, 'other-e', None, 'other-g', 'other-h'))
1327
        for tg, e_target, f_target, g_target, h_target in targets:
1328
            for link, target in (('e', e_target), ('f', f_target), 
1329
                                 ('g', g_target), ('h', h_target)):
1330
                if target is not None:
1331
                    tg.tt.new_symlink(link, tg.root, target, link)
1534.7.102 by Aaron Bentley
Deleted old pre-conflict contents
1332
1333
        for tg in this, base, other:
1534.7.101 by Aaron Bentley
Got conflicts on symlinks working properly
1334
            tg.tt.apply()
3008.1.11 by Michael Hudson
restore the default behaviour of Merge3Merger.__init__().
1335
        Merge3Merger(this.wt, this.wt, base.wt, other.wt)
1534.7.101 by Aaron Bentley
Got conflicts on symlinks working properly
1336
        self.assertIs(os.path.isdir(this.wt.abspath('a')), True)
1337
        self.assertIs(os.path.islink(this.wt.abspath('b')), True)
1338
        self.assertIs(os.path.isfile(this.wt.abspath('c')), True)
1339
        for suffix in ('THIS', 'BASE', 'OTHER'):
1340
            self.assertEqual(os.readlink(this.wt.abspath('d.'+suffix)), suffix)
1534.7.102 by Aaron Bentley
Deleted old pre-conflict contents
1341
        self.assertIs(os.path.lexists(this.wt.abspath('d')), False)
1534.7.104 by Aaron Bentley
Fixed set_versioned, enhanced conflict testing
1342
        self.assertEqual(this.wt.id2path('d'), 'd.OTHER')
1343
        self.assertEqual(this.wt.id2path('f'), 'f.THIS')
1534.7.102 by Aaron Bentley
Deleted old pre-conflict contents
1344
        self.assertEqual(os.readlink(this.wt.abspath('e')), 'other-e')
1345
        self.assertIs(os.path.lexists(this.wt.abspath('e.THIS')), False)
1346
        self.assertIs(os.path.lexists(this.wt.abspath('e.OTHER')), False)
1347
        self.assertIs(os.path.lexists(this.wt.abspath('e.BASE')), False)
1534.7.104 by Aaron Bentley
Fixed set_versioned, enhanced conflict testing
1348
        self.assertIs(os.path.lexists(this.wt.abspath('g')), True)
1349
        self.assertIs(os.path.lexists(this.wt.abspath('g.BASE')), False)
1350
        self.assertIs(os.path.lexists(this.wt.abspath('h')), False)
1351
        self.assertIs(os.path.lexists(this.wt.abspath('h.BASE')), False)
1352
        self.assertIs(os.path.lexists(this.wt.abspath('h.THIS')), True)
1353
        self.assertIs(os.path.lexists(this.wt.abspath('h.OTHER')), True)
1534.7.105 by Aaron Bentley
Got merge with rename working
1354
1355
    def test_filename_merge(self):
2116.4.1 by John Arbash Meinel
Update file and revision id generators.
1356
        root_id = generate_ids.gen_root_id()
1731.1.33 by Aaron Bentley
Revert no-special-root changes
1357
        base = TransformGroup("BASE", root_id)
1358
        this = TransformGroup("THIS", root_id)
1359
        other = TransformGroup("OTHER", root_id)
1534.7.105 by Aaron Bentley
Got merge with rename working
1360
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a') 
1361
                                   for t in [base, this, other]]
1362
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b') 
1363
                                   for t in [base, this, other]]
1364
        base.tt.new_directory('c', base_a, 'c')
1365
        this.tt.new_directory('c1', this_a, 'c')
1366
        other.tt.new_directory('c', other_b, 'c')
1367
1368
        base.tt.new_directory('d', base_a, 'd')
1369
        this.tt.new_directory('d1', this_b, 'd')
1370
        other.tt.new_directory('d', other_a, 'd')
1371
1372
        base.tt.new_directory('e', base_a, 'e')
1373
        this.tt.new_directory('e', this_a, 'e')
1374
        other.tt.new_directory('e1', other_b, 'e')
1375
1376
        base.tt.new_directory('f', base_a, 'f')
1377
        this.tt.new_directory('f1', this_b, 'f')
1378
        other.tt.new_directory('f1', other_b, 'f')
1379
1380
        for tg in [this, base, other]:
1381
            tg.tt.apply()
3008.1.11 by Michael Hudson
restore the default behaviour of Merge3Merger.__init__().
1382
        Merge3Merger(this.wt, this.wt, base.wt, other.wt)
1534.7.176 by abentley
Fixed up tests for Windows
1383
        self.assertEqual(this.wt.id2path('c'), pathjoin('b/c1'))
1384
        self.assertEqual(this.wt.id2path('d'), pathjoin('b/d1'))
1385
        self.assertEqual(this.wt.id2path('e'), pathjoin('b/e1'))
1386
        self.assertEqual(this.wt.id2path('f'), pathjoin('b/f1'))
1534.7.105 by Aaron Bentley
Got merge with rename working
1387
1388
    def test_filename_merge_conflicts(self):
2116.4.1 by John Arbash Meinel
Update file and revision id generators.
1389
        root_id = generate_ids.gen_root_id()
1731.1.33 by Aaron Bentley
Revert no-special-root changes
1390
        base = TransformGroup("BASE", root_id)
1391
        this = TransformGroup("THIS", root_id)
1392
        other = TransformGroup("OTHER", root_id)
1534.7.105 by Aaron Bentley
Got merge with rename working
1393
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a') 
1394
                                   for t in [base, this, other]]
1395
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b') 
1396
                                   for t in [base, this, other]]
1397
1398
        base.tt.new_file('g', base_a, 'g', 'g')
1399
        other.tt.new_file('g1', other_b, 'g1', 'g')
1400
1401
        base.tt.new_file('h', base_a, 'h', 'h')
1402
        this.tt.new_file('h1', this_b, 'h1', 'h')
1403
1404
        base.tt.new_file('i', base.root, 'i', 'i')
1534.7.153 by Aaron Bentley
Handled test cases involving symlinks
1405
        other.tt.new_directory('i1', this_b, 'i')
1534.7.105 by Aaron Bentley
Got merge with rename working
1406
1407
        for tg in [this, base, other]:
1408
            tg.tt.apply()
3008.1.11 by Michael Hudson
restore the default behaviour of Merge3Merger.__init__().
1409
        Merge3Merger(this.wt, this.wt, base.wt, other.wt)
1534.7.105 by Aaron Bentley
Got merge with rename working
1410
1534.7.176 by abentley
Fixed up tests for Windows
1411
        self.assertEqual(this.wt.id2path('g'), pathjoin('b/g1.OTHER'))
1534.7.105 by Aaron Bentley
Got merge with rename working
1412
        self.assertIs(os.path.lexists(this.wt.abspath('b/g1.BASE')), True)
1413
        self.assertIs(os.path.lexists(this.wt.abspath('b/g1.THIS')), False)
1534.7.176 by abentley
Fixed up tests for Windows
1414
        self.assertEqual(this.wt.id2path('h'), pathjoin('b/h1.THIS'))
1534.7.105 by Aaron Bentley
Got merge with rename working
1415
        self.assertIs(os.path.lexists(this.wt.abspath('b/h1.BASE')), True)
1416
        self.assertIs(os.path.lexists(this.wt.abspath('b/h1.OTHER')), False)
1534.7.176 by abentley
Fixed up tests for Windows
1417
        self.assertEqual(this.wt.id2path('i'), pathjoin('b/i1.OTHER'))
1534.7.183 by Aaron Bentley
Fixed build_tree with symlinks
1418
2027.1.1 by John Arbash Meinel
Fix bug #56549, and write a direct test that the right path is being statted
1419
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1420
class TestBuildTree(tests.TestCaseWithTransport):
1421
3006.2.2 by Alexander Belchenko
tests added.
1422
    def test_build_tree_with_symlinks(self):
2949.5.1 by Alexander Belchenko
selftest: use SymlinkFeature instead of TestSkipped where appropriate
1423
        self.requireFeature(SymlinkFeature)
1534.7.183 by Aaron Bentley
Fixed build_tree with symlinks
1424
        os.mkdir('a')
1425
        a = BzrDir.create_standalone_workingtree('a')
1426
        os.mkdir('a/foo')
1427
        file('a/foo/bar', 'wb').write('contents')
1428
        os.symlink('a/foo/bar', 'a/foo/baz')
1429
        a.add(['foo', 'foo/bar', 'foo/baz'])
1430
        a.commit('initial commit')
1431
        b = BzrDir.create_standalone_workingtree('b')
2255.2.194 by Robert Collins
[BROKEN] Many updates to stop using experimental formats in tests.
1432
        basis = a.basis_tree()
1433
        basis.lock_read()
1434
        self.addCleanup(basis.unlock)
1435
        build_tree(basis, b)
1534.7.183 by Aaron Bentley
Fixed build_tree with symlinks
1436
        self.assertIs(os.path.isdir('b/foo'), True)
1437
        self.assertEqual(file('b/foo/bar', 'rb').read(), "contents")
1438
        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
1439
2100.3.21 by Aaron Bentley
Work on checking out by-reference trees
1440
    def test_build_with_references(self):
2255.2.194 by Robert Collins
[BROKEN] Many updates to stop using experimental formats in tests.
1441
        tree = self.make_branch_and_tree('source',
1442
            format='dirstate-with-subtree')
1443
        subtree = self.make_branch_and_tree('source/subtree',
1444
            format='dirstate-with-subtree')
2100.3.21 by Aaron Bentley
Work on checking out by-reference trees
1445
        tree.add_reference(subtree)
1446
        tree.commit('a revision')
1447
        tree.branch.create_checkout('target')
1448
        self.failUnlessExists('target')
1449
        self.failUnlessExists('target/subtree')
1450
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1451
    def test_file_conflict_handling(self):
1452
        """Ensure that when building trees, conflict handling is done"""
1453
        source = self.make_branch_and_tree('source')
1454
        target = self.make_branch_and_tree('target')
1455
        self.build_tree(['source/file', 'target/file'])
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1456
        source.add('file', 'new-file')
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1457
        source.commit('added file')
1458
        build_tree(source.basis_tree(), target)
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1459
        self.assertEqual([DuplicateEntry('Moved existing file to',
1460
                          'file.moved', 'file', None, 'new-file')],
1461
                         target.conflicts())
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1462
        target2 = self.make_branch_and_tree('target2')
1463
        target_file = file('target2/file', 'wb')
1464
        try:
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1465
            source_file = file('source/file', 'rb')
1466
            try:
1467
                target_file.write(source_file.read())
1468
            finally:
1469
                source_file.close()
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1470
        finally:
1471
            target_file.close()
1472
        build_tree(source.basis_tree(), target2)
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1473
        self.assertEqual([], target2.conflicts())
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1474
1475
    def test_symlink_conflict_handling(self):
1476
        """Ensure that when building trees, conflict handling is done"""
2949.5.1 by Alexander Belchenko
selftest: use SymlinkFeature instead of TestSkipped where appropriate
1477
        self.requireFeature(SymlinkFeature)
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1478
        source = self.make_branch_and_tree('source')
1479
        os.symlink('foo', 'source/symlink')
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1480
        source.add('symlink', 'new-symlink')
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1481
        source.commit('added file')
1482
        target = self.make_branch_and_tree('target')
1483
        os.symlink('bar', 'target/symlink')
1484
        build_tree(source.basis_tree(), target)
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1485
        self.assertEqual([DuplicateEntry('Moved existing file to',
1486
            'symlink.moved', 'symlink', None, 'new-symlink')],
1487
            target.conflicts())
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1488
        target = self.make_branch_and_tree('target2')
1489
        os.symlink('foo', 'target2/symlink')
1490
        build_tree(source.basis_tree(), target)
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1491
        self.assertEqual([], target.conflicts())
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1492
        
1493
    def test_directory_conflict_handling(self):
1494
        """Ensure that when building trees, conflict handling is done"""
1495
        source = self.make_branch_and_tree('source')
1496
        target = self.make_branch_and_tree('target')
1497
        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
1498
        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
1499
        source.commit('added file')
1500
        build_tree(source.basis_tree(), target)
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1501
        self.assertEqual([], target.conflicts())
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1502
        self.failUnlessExists('target/dir1/file')
1503
1504
        # Ensure contents are merged
1505
        target = self.make_branch_and_tree('target2')
1506
        self.build_tree(['target2/dir1/', 'target2/dir1/file2'])
1507
        build_tree(source.basis_tree(), target)
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1508
        self.assertEqual([], target.conflicts())
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1509
        self.failUnlessExists('target2/dir1/file2')
1510
        self.failUnlessExists('target2/dir1/file')
1511
1512
        # 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
1513
        target = self.make_branch_and_tree('target3')
1514
        self.make_branch('target3/dir1')
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1515
        self.build_tree(['target3/dir1/file2'])
1516
        build_tree(source.basis_tree(), target)
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1517
        self.failIfExists('target3/dir1/file')
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1518
        self.failUnlessExists('target3/dir1/file2')
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1519
        self.failUnlessExists('target3/dir1.diverted/file')
1520
        self.assertEqual([DuplicateEntry('Diverted to',
1521
            'dir1.diverted', 'dir1', 'new-dir1', None)],
1522
            target.conflicts())
1523
1524
        target = self.make_branch_and_tree('target4')
1525
        self.build_tree(['target4/dir1/'])
1526
        self.make_branch('target4/dir1/file')
1527
        build_tree(source.basis_tree(), target)
1528
        self.failUnlessExists('target4/dir1/file')
1529
        self.assertEqual('directory', file_kind('target4/dir1/file'))
1530
        self.failUnlessExists('target4/dir1/file.diverted')
1531
        self.assertEqual([DuplicateEntry('Diverted to',
1532
            'dir1/file.diverted', 'dir1/file', 'new-file', None)],
1533
            target.conflicts())
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1534
1535
    def test_mixed_conflict_handling(self):
1536
        """Ensure that when building trees, conflict handling is done"""
1537
        source = self.make_branch_and_tree('source')
1538
        target = self.make_branch_and_tree('target')
1539
        self.build_tree(['source/name', 'target/name/'])
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1540
        source.add('name', 'new-name')
1541
        source.commit('added file')
1542
        build_tree(source.basis_tree(), target)
1543
        self.assertEqual([DuplicateEntry('Moved existing file to',
1544
            'name.moved', 'name', None, 'new-name')], target.conflicts())
1545
1546
    def test_raises_in_populated(self):
1547
        source = self.make_branch_and_tree('source')
1548
        self.build_tree(['source/name'])
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1549
        source.add('name')
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1550
        source.commit('added name')
1551
        target = self.make_branch_and_tree('target')
1552
        self.build_tree(['target/name'])
1553
        target.add('name')
2090.2.1 by Martin Pool
Fix some code which relies on assertions and breaks under python -O
1554
        self.assertRaises(errors.WorkingTreeAlreadyPopulated, 
1555
            build_tree, source.basis_tree(), target)
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1556
2502.1.1 by Aaron Bentley
Ensure renames only root children are renamed when building trees
1557
    def test_build_tree_rename_count(self):
1558
        source = self.make_branch_and_tree('source')
1559
        self.build_tree(['source/file1', 'source/dir1/'])
1560
        source.add(['file1', 'dir1'])
1561
        source.commit('add1')
1562
        target1 = self.make_branch_and_tree('target1')
2502.1.6 by Aaron Bentley
Update from review comments
1563
        transform_result = build_tree(source.basis_tree(), target1)
1564
        self.assertEqual(2, transform_result.rename_count)
2502.1.1 by Aaron Bentley
Ensure renames only root children are renamed when building trees
1565
1566
        self.build_tree(['source/dir1/file2'])
1567
        source.add(['dir1/file2'])
1568
        source.commit('add3')
1569
        target2 = self.make_branch_and_tree('target2')
2502.1.6 by Aaron Bentley
Update from review comments
1570
        transform_result = build_tree(source.basis_tree(), target2)
2502.1.1 by Aaron Bentley
Ensure renames only root children are renamed when building trees
1571
        # children of non-root directories should not be renamed
2502.1.6 by Aaron Bentley
Update from review comments
1572
        self.assertEqual(2, transform_result.rename_count)
2502.1.1 by Aaron Bentley
Ensure renames only root children are renamed when building trees
1573
3123.5.1 by Aaron Bentley
Make build-tree able to use an additional 'accelerator' tree
1574
    def test_build_tree_accelerator_tree(self):
1575
        source = self.make_branch_and_tree('source')
1576
        self.build_tree_contents([('source/file1', 'A')])
1577
        self.build_tree_contents([('source/file2', 'B')])
1578
        source.add(['file1', 'file2'], ['file1-id', 'file2-id'])
1579
        source.commit('commit files')
1580
        self.build_tree_contents([('source/file2', 'C')])
1581
        calls = []
1582
        real_source_get_file = source.get_file
1583
        def get_file(file_id, path=None):
1584
            calls.append(file_id)
1585
            return real_source_get_file(file_id, path)
1586
        source.get_file = get_file
3123.5.12 by Aaron Bentley
Try to optimize iter_changes_accelerated
1587
        source.lock_read()
1588
        self.addCleanup(source.unlock)
3123.5.1 by Aaron Bentley
Make build-tree able to use an additional 'accelerator' tree
1589
        target = self.make_branch_and_tree('target')
3123.5.19 by Aaron Bentley
Ensure content is exactly the same, when accelerator used
1590
        revision_tree = source.basis_tree()
1591
        revision_tree.lock_read()
1592
        self.addCleanup(revision_tree.unlock)
1593
        build_tree(revision_tree, target, source)
3123.5.1 by Aaron Bentley
Make build-tree able to use an additional 'accelerator' tree
1594
        self.assertEqual(['file1-id'], calls)
3123.5.19 by Aaron Bentley
Ensure content is exactly the same, when accelerator used
1595
        target.lock_read()
1596
        self.addCleanup(target.unlock)
1597
        self.assertEqual([], list(target._iter_changes(revision_tree)))
3123.5.1 by Aaron Bentley
Make build-tree able to use an additional 'accelerator' tree
1598
3123.5.4 by Aaron Bentley
Use an accelerator tree when branching, handle no-such-id correctly
1599
    def test_build_tree_accelerator_tree_missing_file(self):
1600
        source = self.make_branch_and_tree('source')
1601
        self.build_tree_contents([('source/file1', 'A')])
1602
        self.build_tree_contents([('source/file2', 'B')])
1603
        source.add(['file1', 'file2'])
1604
        source.commit('commit files')
1605
        os.unlink('source/file1')
1606
        source.remove(['file2'])
1607
        target = self.make_branch_and_tree('target')
3123.5.19 by Aaron Bentley
Ensure content is exactly the same, when accelerator used
1608
        revision_tree = source.basis_tree()
1609
        revision_tree.lock_read()
1610
        self.addCleanup(revision_tree.unlock)
1611
        build_tree(revision_tree, target, source)
1612
        target.lock_read()
1613
        self.addCleanup(target.unlock)
1614
        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
1615
3123.5.16 by Aaron Bentley
Test handling of conversion to non-file
1616
    def test_build_tree_accelerator_wrong_kind(self):
3146.4.8 by Aaron Bentley
Add missing symlink requirement
1617
        self.requireFeature(SymlinkFeature)
3123.5.16 by Aaron Bentley
Test handling of conversion to non-file
1618
        source = self.make_branch_and_tree('source')
1619
        self.build_tree_contents([('source/file1', '')])
1620
        self.build_tree_contents([('source/file2', '')])
1621
        source.add(['file1', 'file2'], ['file1-id', 'file2-id'])
1622
        source.commit('commit files')
1623
        os.unlink('source/file2')
1624
        self.build_tree_contents([('source/file2/', 'C')])
1625
        os.unlink('source/file1')
1626
        os.symlink('file2', 'source/file1')
1627
        calls = []
1628
        real_source_get_file = source.get_file
1629
        def get_file(file_id, path=None):
1630
            calls.append(file_id)
1631
            return real_source_get_file(file_id, path)
1632
        source.get_file = get_file
1633
        source.lock_read()
1634
        self.addCleanup(source.unlock)
1635
        target = self.make_branch_and_tree('target')
3123.5.19 by Aaron Bentley
Ensure content is exactly the same, when accelerator used
1636
        revision_tree = source.basis_tree()
1637
        revision_tree.lock_read()
1638
        self.addCleanup(revision_tree.unlock)
1639
        build_tree(revision_tree, target, source)
3123.5.16 by Aaron Bentley
Test handling of conversion to non-file
1640
        self.assertEqual([], calls)
3123.5.19 by Aaron Bentley
Ensure content is exactly the same, when accelerator used
1641
        target.lock_read()
1642
        self.addCleanup(target.unlock)
1643
        self.assertEqual([], list(target._iter_changes(revision_tree)))
3123.5.16 by Aaron Bentley
Test handling of conversion to non-file
1644
3137.1.1 by Aaron Bentley
Fix build_tree acceleration when file is moved in accelerator_tree
1645
    def test_build_tree_accelerator_tree_moved(self):
1646
        source = self.make_branch_and_tree('source')
1647
        self.build_tree_contents([('source/file1', 'A')])
1648
        source.add(['file1'], ['file1-id'])
1649
        source.commit('commit files')
1650
        source.rename_one('file1', 'file2')
1651
        source.lock_read()
1652
        self.addCleanup(source.unlock)
1653
        target = self.make_branch_and_tree('target')
1654
        revision_tree = source.basis_tree()
1655
        revision_tree.lock_read()
1656
        self.addCleanup(revision_tree.unlock)
1657
        build_tree(revision_tree, target, source)
1658
        target.lock_read()
1659
        self.addCleanup(target.unlock)
1660
        self.assertEqual([], list(target._iter_changes(revision_tree)))
1661
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1662
1534.10.28 by Aaron Bentley
Use numbered backup files
1663
class MockTransform(object):
1664
1665
    def has_named_child(self, by_parent, parent_id, name):
1666
        for child_id in by_parent[parent_id]:
1667
            if child_id == '0':
1668
                if name == "name~":
1669
                    return True
1534.10.29 by Aaron Bentley
Fixed backup numbering to match GNU standard better
1670
            elif name == "name.~%s~" % child_id:
1534.10.28 by Aaron Bentley
Use numbered backup files
1671
                return True
1672
        return False
1673
2502.1.1 by Aaron Bentley
Ensure renames only root children are renamed when building trees
1674
1534.10.28 by Aaron Bentley
Use numbered backup files
1675
class MockEntry(object):
1676
    def __init__(self):
1677
        object.__init__(self)
1678
        self.name = "name"
1679
3063.1.1 by Alexander Belchenko
Catch OSError 17 (file exists) in final phase of tree transform and show filename to user (#111758).
1680
1534.10.28 by Aaron Bentley
Use numbered backup files
1681
class TestGetBackupName(TestCase):
1682
    def test_get_backup_name(self):
1683
        tt = MockTransform()
1684
        name = get_backup_name(MockEntry(), {'a':[]}, 'a', tt)
1534.10.29 by Aaron Bentley
Fixed backup numbering to match GNU standard better
1685
        self.assertEqual(name, 'name.~1~')
1686
        name = get_backup_name(MockEntry(), {'a':['1']}, 'a', tt)
1687
        self.assertEqual(name, 'name.~2~')
1534.10.28 by Aaron Bentley
Use numbered backup files
1688
        name = get_backup_name(MockEntry(), {'a':['2']}, 'a', tt)
1534.10.29 by Aaron Bentley
Fixed backup numbering to match GNU standard better
1689
        self.assertEqual(name, 'name.~1~')
1534.10.28 by Aaron Bentley
Use numbered backup files
1690
        name = get_backup_name(MockEntry(), {'a':['2'], 'b':[]}, 'b', tt)
1534.10.29 by Aaron Bentley
Fixed backup numbering to match GNU standard better
1691
        self.assertEqual(name, 'name.~1~')
1692
        name = get_backup_name(MockEntry(), {'a':['1', '2', '3']}, 'a', tt)
1693
        self.assertEqual(name, 'name.~4~')
2733.2.1 by Aaron Bentley
Implement FileMover, to support TreeTransform rollback
1694
1695
1696
class TestFileMover(tests.TestCaseWithTransport):
1697
1698
    def test_file_mover(self):
1699
        self.build_tree(['a/', 'a/b', 'c/', 'c/d'])
1700
        mover = _FileMover()
1701
        mover.rename('a', 'q')
1702
        self.failUnlessExists('q')
1703
        self.failIfExists('a')
2733.2.12 by Aaron Bentley
Updates from review
1704
        self.failUnlessExists('q/b')
1705
        self.failUnlessExists('c')
1706
        self.failUnlessExists('c/d')
2733.2.1 by Aaron Bentley
Implement FileMover, to support TreeTransform rollback
1707
2733.2.5 by Aaron Bentley
Implement FileMover.pre_delete and FileMover.apply_deletions
1708
    def test_pre_delete_rollback(self):
1709
        self.build_tree(['a/'])
1710
        mover = _FileMover()
1711
        mover.pre_delete('a', 'q')
1712
        self.failUnlessExists('q')
1713
        self.failIfExists('a')
1714
        mover.rollback()
1715
        self.failIfExists('q')
1716
        self.failUnlessExists('a')
1717
1718
    def test_apply_deletions(self):
2733.2.12 by Aaron Bentley
Updates from review
1719
        self.build_tree(['a/', 'b/'])
2733.2.5 by Aaron Bentley
Implement FileMover.pre_delete and FileMover.apply_deletions
1720
        mover = _FileMover()
1721
        mover.pre_delete('a', 'q')
2733.2.12 by Aaron Bentley
Updates from review
1722
        mover.pre_delete('b', 'r')
2733.2.5 by Aaron Bentley
Implement FileMover.pre_delete and FileMover.apply_deletions
1723
        self.failUnlessExists('q')
2733.2.12 by Aaron Bentley
Updates from review
1724
        self.failUnlessExists('r')
2733.2.5 by Aaron Bentley
Implement FileMover.pre_delete and FileMover.apply_deletions
1725
        self.failIfExists('a')
2733.2.12 by Aaron Bentley
Updates from review
1726
        self.failIfExists('b')
2733.2.5 by Aaron Bentley
Implement FileMover.pre_delete and FileMover.apply_deletions
1727
        mover.apply_deletions()
1728
        self.failIfExists('q')
2733.2.12 by Aaron Bentley
Updates from review
1729
        self.failIfExists('r')
2733.2.5 by Aaron Bentley
Implement FileMover.pre_delete and FileMover.apply_deletions
1730
        self.failIfExists('a')
2733.2.12 by Aaron Bentley
Updates from review
1731
        self.failIfExists('b')
2733.2.5 by Aaron Bentley
Implement FileMover.pre_delete and FileMover.apply_deletions
1732
2733.2.1 by Aaron Bentley
Implement FileMover, to support TreeTransform rollback
1733
    def test_file_mover_rollback(self):
1734
        self.build_tree(['a/', 'a/b', 'c/', 'c/d/', 'c/e/'])
1735
        mover = _FileMover()
1736
        mover.rename('c/d', 'c/f')
1737
        mover.rename('c/e', 'c/d')
1738
        try:
1739
            mover.rename('a', 'c')
3063.1.3 by Aaron Bentley
Update for Linux
1740
        except errors.FileExists, e:
2733.2.1 by Aaron Bentley
Implement FileMover, to support TreeTransform rollback
1741
            mover.rollback()
1742
        self.failUnlessExists('a')
1743
        self.failUnlessExists('c/d')
2733.2.3 by Aaron Bentley
Test tranform rollback
1744
1745
1746
class Bogus(Exception):
1747
    pass
1748
1749
1750
class TestTransformRollback(tests.TestCaseWithTransport):
1751
1752
    class ExceptionFileMover(_FileMover):
1753
2733.2.4 by Aaron Bentley
Test transform rollback when renaming into place
1754
        def __init__(self, bad_source=None, bad_target=None):
1755
            _FileMover.__init__(self)
1756
            self.bad_source = bad_source
1757
            self.bad_target = bad_target
1758
2733.2.3 by Aaron Bentley
Test tranform rollback
1759
        def rename(self, source, target):
2733.2.4 by Aaron Bentley
Test transform rollback when renaming into place
1760
            if (self.bad_source is not None and
1761
                source.endswith(self.bad_source)):
1762
                raise Bogus
1763
            elif (self.bad_target is not None and
1764
                target.endswith(self.bad_target)):
2733.2.3 by Aaron Bentley
Test tranform rollback
1765
                raise Bogus
1766
            else:
1767
                _FileMover.rename(self, source, target)
1768
1769
    def test_rollback_rename(self):
1770
        tree = self.make_branch_and_tree('.')
1771
        self.build_tree(['a/', 'a/b'])
1772
        tt = TreeTransform(tree)
1773
        self.addCleanup(tt.finalize)
1774
        a_id = tt.trans_id_tree_path('a')
1775
        tt.adjust_path('c', tt.root, a_id)
1776
        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
1777
        self.assertRaises(Bogus, tt.apply,
1778
                          _mover=self.ExceptionFileMover(bad_source='a'))
1779
        self.failUnlessExists('a')
1780
        self.failUnlessExists('a/b')
1781
        tt.apply()
1782
        self.failUnlessExists('c')
1783
        self.failUnlessExists('c/d')
1784
1785
    def test_rollback_rename_into_place(self):
1786
        tree = self.make_branch_and_tree('.')
1787
        self.build_tree(['a/', 'a/b'])
1788
        tt = TreeTransform(tree)
1789
        self.addCleanup(tt.finalize)
1790
        a_id = tt.trans_id_tree_path('a')
1791
        tt.adjust_path('c', tt.root, a_id)
1792
        tt.adjust_path('d', a_id, tt.trans_id_tree_path('a/b'))
1793
        self.assertRaises(Bogus, tt.apply,
1794
                          _mover=self.ExceptionFileMover(bad_target='c/d'))
1795
        self.failUnlessExists('a')
1796
        self.failUnlessExists('a/b')
1797
        tt.apply()
1798
        self.failUnlessExists('c')
1799
        self.failUnlessExists('c/d')
2733.2.6 by Aaron Bentley
Make TreeTransform commits rollbackable
1800
1801
    def test_rollback_deletion(self):
1802
        tree = self.make_branch_and_tree('.')
1803
        self.build_tree(['a/', 'a/b'])
1804
        tt = TreeTransform(tree)
1805
        self.addCleanup(tt.finalize)
1806
        a_id = tt.trans_id_tree_path('a')
1807
        tt.delete_contents(a_id)
1808
        tt.adjust_path('d', tt.root, tt.trans_id_tree_path('a/b'))
1809
        self.assertRaises(Bogus, tt.apply,
1810
                          _mover=self.ExceptionFileMover(bad_target='d'))
1811
        self.failUnlessExists('a')
1812
        self.failUnlessExists('a/b')
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
1813
1551.19.6 by Aaron Bentley
Revert doesn't crash restoring a file from a deleted directory
1814
    def test_resolve_no_parent(self):
1815
        wt = self.make_branch_and_tree('.')
1816
        tt = TreeTransform(wt)
1817
        self.addCleanup(tt.finalize)
1818
        parent = tt.trans_id_file_id('parent-id')
1819
        tt.new_file('file', parent, 'Contents')
1820
        resolve_conflicts(tt)
3008.1.13 by Michael Hudson
merge bzr.dev
1821
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
1822
1823
class TestTransformPreview(tests.TestCaseWithTransport):
1824
1825
    def create_tree(self):
1826
        tree = self.make_branch_and_tree('.')
1827
        self.build_tree_contents([('a', 'content 1')])
3008.2.1 by Aaron Bentley
Ensure conflict resolution works
1828
        tree.add('a', 'a-id')
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
1829
        tree.commit('rev1', rev_id='rev1')
1830
        return tree.branch.repository.revision_tree('rev1')
1831
3008.1.18 by Aaron Bentley
Get supported PreviewTree functionality under test
1832
    def get_empty_preview(self):
1833
        repository = self.make_repository('repo')
1834
        tree = repository.revision_tree(_mod_revision.NULL_REVISION)
1835
        return TransformPreview(tree)
1836
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
1837
    def test_transform_preview(self):
1838
        revision_tree = self.create_tree()
1839
        preview = TransformPreview(revision_tree)
1840
1841
    def test_transform_preview_tree(self):
1842
        revision_tree = self.create_tree()
1843
        preview = TransformPreview(revision_tree)
1844
        preview.get_preview_tree()
1845
3008.1.5 by Michael Hudson
a more precise test
1846
    def test_transform_new_file(self):
1847
        revision_tree = self.create_tree()
1848
        preview = TransformPreview(revision_tree)
1849
        preview.new_file('file2', preview.root, 'content B\n', 'file2-id')
1850
        preview_tree = preview.get_preview_tree()
1851
        self.assertEqual(preview_tree.kind('file2-id'), 'file')
1852
        self.assertEqual(
1853
            preview_tree.get_file('file2-id').read(), 'content B\n')
1854
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
1855
    def test_diff_preview_tree(self):
1856
        revision_tree = self.create_tree()
1857
        preview = TransformPreview(revision_tree)
3008.1.4 by Michael Hudson
Merge test enhancements
1858
        preview.new_file('file2', preview.root, 'content B\n', 'file2-id')
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
1859
        preview_tree = preview.get_preview_tree()
1860
        out = StringIO()
1861
        show_diff_trees(revision_tree, preview_tree, out)
3008.1.4 by Michael Hudson
Merge test enhancements
1862
        lines = out.getvalue().splitlines()
1863
        self.assertEqual(lines[0], "=== added file 'file2'")
1864
        # 3 lines of diff administrivia
1865
        self.assertEqual(lines[4], "+content B")
3008.2.1 by Aaron Bentley
Ensure conflict resolution works
1866
1867
    def test_transform_conflicts(self):
1868
        revision_tree = self.create_tree()
1869
        preview = TransformPreview(revision_tree)
1870
        preview.new_file('a', preview.root, 'content 2')
1871
        resolve_conflicts(preview)
1872
        trans_id = preview.trans_id_file_id('a-id')
1873
        self.assertEqual('a.moved', preview.final_name(trans_id))
3008.1.17 by Aaron Bentley
Test unused parameters of preview_tree._iter_changes
1874
3008.1.31 by Aaron Bentley
Split PreviewTree._iter_changes parameter tests into smaller tests
1875
    def get_tree_and_preview_tree(self):
3008.1.17 by Aaron Bentley
Test unused parameters of preview_tree._iter_changes
1876
        revision_tree = self.create_tree()
1877
        preview = TransformPreview(revision_tree)
1878
        a_trans_id = preview.trans_id_file_id('a-id')
1879
        preview.delete_contents(a_trans_id)
1880
        preview.create_file('b content', a_trans_id)
1881
        preview_tree = preview.get_preview_tree()
3008.1.31 by Aaron Bentley
Split PreviewTree._iter_changes parameter tests into smaller tests
1882
        return revision_tree, preview_tree
1883
1884
    def test_iter_changes(self):
1885
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
1886
        root = revision_tree.inventory.root.file_id
3008.1.17 by Aaron Bentley
Test unused parameters of preview_tree._iter_changes
1887
        self.assertEqual([('a-id', ('a', 'a'), True, (True, True),
1888
                          (root, root), ('a', 'a'), ('file', 'file'),
1889
                          (False, False))],
1890
                          list(preview_tree._iter_changes(revision_tree)))
3008.1.31 by Aaron Bentley
Split PreviewTree._iter_changes parameter tests into smaller tests
1891
1892
    def test_wrong_tree_value_error(self):
1893
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
3008.1.17 by Aaron Bentley
Test unused parameters of preview_tree._iter_changes
1894
        e = self.assertRaises(ValueError, preview_tree._iter_changes,
1895
                              preview_tree)
1896
        self.assertEqual('from_tree must be transform source tree.', str(e))
3008.1.31 by Aaron Bentley
Split PreviewTree._iter_changes parameter tests into smaller tests
1897
1898
    def test_include_unchanged_value_error(self):
1899
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
3008.1.17 by Aaron Bentley
Test unused parameters of preview_tree._iter_changes
1900
        e = self.assertRaises(ValueError, preview_tree._iter_changes,
1901
                              revision_tree, include_unchanged=True)
1902
        self.assertEqual('include_unchanged is not supported', str(e))
3008.1.31 by Aaron Bentley
Split PreviewTree._iter_changes parameter tests into smaller tests
1903
1904
    def test_specific_files(self):
1905
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
3008.1.17 by Aaron Bentley
Test unused parameters of preview_tree._iter_changes
1906
        e = self.assertRaises(ValueError, preview_tree._iter_changes,
1907
                              revision_tree, specific_files=['pete'])
1908
        self.assertEqual('specific_files is not supported', str(e))
3008.1.31 by Aaron Bentley
Split PreviewTree._iter_changes parameter tests into smaller tests
1909
1910
    def test_want_unversioned_value_error(self):
1911
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
3008.1.17 by Aaron Bentley
Test unused parameters of preview_tree._iter_changes
1912
        e = self.assertRaises(ValueError, preview_tree._iter_changes,
1913
                              revision_tree, want_unversioned=True)
1914
        self.assertEqual('want_unversioned is not supported', str(e))
1915
3008.1.31 by Aaron Bentley
Split PreviewTree._iter_changes parameter tests into smaller tests
1916
    def test_ignore_extra_trees_no_specific_files(self):
3008.1.17 by Aaron Bentley
Test unused parameters of preview_tree._iter_changes
1917
        # extra_trees is harmless without specific_files, so we'll silently
1918
        # accept it, even though we won't use it.
3008.1.31 by Aaron Bentley
Split PreviewTree._iter_changes parameter tests into smaller tests
1919
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
3008.1.17 by Aaron Bentley
Test unused parameters of preview_tree._iter_changes
1920
        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
1921
1922
    def test_ignore_require_versioned_no_specific_files(self):
3008.1.17 by Aaron Bentley
Test unused parameters of preview_tree._iter_changes
1923
        # require_versioned is meaningless without specific_files.
3008.1.31 by Aaron Bentley
Split PreviewTree._iter_changes parameter tests into smaller tests
1924
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
3008.1.17 by Aaron Bentley
Test unused parameters of preview_tree._iter_changes
1925
        preview_tree._iter_changes(revision_tree, require_versioned=False)
3008.1.31 by Aaron Bentley
Split PreviewTree._iter_changes parameter tests into smaller tests
1926
1927
    def test_ignore_pb(self):
3008.1.17 by Aaron Bentley
Test unused parameters of preview_tree._iter_changes
1928
        # 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
1929
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
3008.1.17 by Aaron Bentley
Test unused parameters of preview_tree._iter_changes
1930
        preview_tree._iter_changes(revision_tree, pb=progress.DummyProgress())
3008.1.18 by Aaron Bentley
Get supported PreviewTree functionality under test
1931
1932
    def test_kind(self):
1933
        revision_tree = self.create_tree()
1934
        preview = TransformPreview(revision_tree)
1935
        preview.new_file('file', preview.root, 'contents', 'file-id')
1936
        preview.new_directory('directory', preview.root, 'dir-id')
1937
        preview_tree = preview.get_preview_tree()
1938
        self.assertEqual('file', preview_tree.kind('file-id'))
1939
        self.assertEqual('directory', preview_tree.kind('dir-id'))
1940
1941
    def test_get_file_mtime(self):
1942
        preview = self.get_empty_preview()
1943
        file_trans_id = preview.new_file('file', preview.root, 'contents',
1944
                                         'file-id')
1945
        limbo_path = preview._limbo_name(file_trans_id)
1946
        preview_tree = preview.get_preview_tree()
1947
        self.assertEqual(os.stat(limbo_path).st_mtime,
1948
                         preview_tree.get_file_mtime('file-id'))
1949
1950
    def test_get_file(self):
1951
        preview = self.get_empty_preview()
1952
        preview.new_file('file', preview.root, 'contents', 'file-id')
1953
        preview_tree = preview.get_preview_tree()
1954
        tree_file = preview_tree.get_file('file-id')
1955
        try:
1956
            self.assertEqual('contents', tree_file.read())
1957
        finally:
1958
            tree_file.close()