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