~bzr-pqm/bzr/bzr.dev

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