/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_transform.py

Merge bzr.dev

Show diffs side-by-side

added added

removed removed

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