/brz/remove-bazaar

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