/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: Martin Pool
  • Date: 2006-09-22 03:02:40 UTC
  • mfrom: (1913.2.4 bzr.mbp.python25)
  • mto: This revision was merged to the branch mainline in revision 2030.
  • Revision ID: mbp@sourcefrog.net-20060922030240-275cc23f08aaa30a
[merge] additional python2.5 fixes

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