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