~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_transform.py

  • Committer: Martin Pool
  • Date: 2005-09-30 05:56:05 UTC
  • mto: (1185.14.2)
  • mto: This revision was merged to the branch mainline in revision 1396.
  • Revision ID: mbp@sourcefrog.net-20050930055605-a2c534529b392a7d
- fix upgrade for transport changes

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