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