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