~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_transform.py

  • Committer: mbp at sourcefrog
  • Date: 2005-04-07 02:40:18 UTC
  • Revision ID: mbp@sourcefrog.net-20050407024018-cf7130ea991f4ebc0c353ed2
more notes on svk

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~')