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