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