/brz/remove-bazaar

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