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