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