/brz/remove-bazaar

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

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_transform.py

  • Committer: John Arbash Meinel
  • Date: 2006-10-16 06:31:55 UTC
  • mto: This revision was merged to the branch mainline in revision 2082.
  • Revision ID: john@arbash-meinel.com-20061016063155-4ebed425a35c9adb
Make 'bzr remerge' not use deprecated WorkingTree.iter_conflicts

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