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