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