~bzr-pqm/bzr/bzr.dev

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