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