~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_transform.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2006-09-10 18:57:20 UTC
  • mfrom: (1910.2.48 format-bumps)
  • Revision ID: pqm@pqm.ubuntu.com-20060910185720-1bcc17b99fe77687
Add new format to support nested trees

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