/brz/remove-bazaar

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