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