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