/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

remove AB1 WorkingTree and experimental-knit3

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