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