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