/brz/remove-bazaar

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

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_transform.py

  • Committer: Alexander Belchenko
  • Date: 2008-01-21 10:46:32 UTC
  • mto: This revision was merged to the branch mainline in revision 3201.
  • Revision ID: bialix@ukr.net-20080121104632-ipdnpm6z4k5piv3q
show path to plugin module as *.py instead of *.pyc if python source available

(e.g. C:\bzr.dev\bzrlib\plugins\multiparent.py)

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
from StringIO import StringIO
 
20
import sys
 
21
 
 
22
from bzrlib import (
 
23
    errors,
 
24
    generate_ids,
 
25
    progress,
 
26
    revision as _mod_revision,
 
27
    symbol_versioning,
 
28
    tests,
 
29
    urlutils,
 
30
    )
 
31
from bzrlib.bzrdir import BzrDir
 
32
from bzrlib.conflicts import (DuplicateEntry, DuplicateID, MissingParent,
 
33
                              UnversionedParent, ParentLoop, DeletingParent,
 
34
                              NonDirectoryParent)
 
35
from bzrlib.diff import show_diff_trees
 
36
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
 
37
                           ReusingTransform, CantMoveRoot, 
 
38
                           PathsNotVersionedError, ExistingLimbo,
 
39
                           ExistingPendingDeletion, ImmortalLimbo,
 
40
                           ImmortalPendingDeletion, LockError)
 
41
from bzrlib.osutils import file_kind, pathjoin
 
42
from bzrlib.merge import Merge3Merger
 
43
from bzrlib.tests import (
 
44
    CaseInsensitiveFilesystemFeature,
 
45
    SymlinkFeature,
 
46
    TestCase,
 
47
    TestCaseInTempDir,
 
48
    TestSkipped,
 
49
    )
 
50
from bzrlib.transform import (TreeTransform, ROOT_PARENT, FinalPaths, 
 
51
                              resolve_conflicts, cook_conflicts, 
 
52
                              find_interesting, build_tree, get_backup_name,
 
53
                              change_entry, _FileMover, resolve_checkout,
 
54
                              TransformPreview)
 
55
 
 
56
class TestTreeTransform(tests.TestCaseWithTransport):
 
57
 
 
58
    def setUp(self):
 
59
        super(TestTreeTransform, self).setUp()
 
60
        self.wt = self.make_branch_and_tree('.', format='dirstate-with-subtree')
 
61
        os.chdir('..')
 
62
 
 
63
    def get_transform(self):
 
64
        transform = TreeTransform(self.wt)
 
65
        #self.addCleanup(transform.finalize)
 
66
        return transform, transform.root
 
67
 
 
68
    def test_existing_limbo(self):
 
69
        transform, root = self.get_transform()
 
70
        limbo_name = transform._limbodir
 
71
        deletion_path = transform._deletiondir
 
72
        os.mkdir(pathjoin(limbo_name, 'hehe'))
 
73
        self.assertRaises(ImmortalLimbo, transform.apply)
 
74
        self.assertRaises(LockError, self.wt.unlock)
 
75
        self.assertRaises(ExistingLimbo, self.get_transform)
 
76
        self.assertRaises(LockError, self.wt.unlock)
 
77
        os.rmdir(pathjoin(limbo_name, 'hehe'))
 
78
        os.rmdir(limbo_name)
 
79
        os.rmdir(deletion_path)
 
80
        transform, root = self.get_transform()
 
81
        transform.apply()
 
82
 
 
83
    def test_existing_pending_deletion(self):
 
84
        transform, root = self.get_transform()
 
85
        deletion_path = self._limbodir = urlutils.local_path_from_url(
 
86
            transform._tree._control_files.controlfilename('pending-deletion'))
 
87
        os.mkdir(pathjoin(deletion_path, 'blocking-directory'))
 
88
        self.assertRaises(ImmortalPendingDeletion, transform.apply)
 
89
        self.assertRaises(LockError, self.wt.unlock)
 
90
        self.assertRaises(ExistingPendingDeletion, self.get_transform)
 
91
 
 
92
    def test_build(self):
 
93
        transform, root = self.get_transform()
 
94
        self.wt.lock_tree_write()
 
95
        self.addCleanup(self.wt.unlock)
 
96
        self.assertIs(transform.get_tree_parent(root), ROOT_PARENT)
 
97
        imaginary_id = transform.trans_id_tree_path('imaginary')
 
98
        imaginary_id2 = transform.trans_id_tree_path('imaginary/')
 
99
        self.assertEqual(imaginary_id, imaginary_id2)
 
100
        self.assertEqual(transform.get_tree_parent(imaginary_id), root)
 
101
        self.assertEqual(transform.final_kind(root), 'directory')
 
102
        self.assertEqual(transform.final_file_id(root), self.wt.get_root_id())
 
103
        trans_id = transform.create_path('name', root)
 
104
        self.assertIs(transform.final_file_id(trans_id), None)
 
105
        self.assertRaises(NoSuchFile, transform.final_kind, trans_id)
 
106
        transform.create_file('contents', trans_id)
 
107
        transform.set_executability(True, trans_id)
 
108
        transform.version_file('my_pretties', trans_id)
 
109
        self.assertRaises(DuplicateKey, transform.version_file,
 
110
                          'my_pretties', trans_id)
 
111
        self.assertEqual(transform.final_file_id(trans_id), 'my_pretties')
 
112
        self.assertEqual(transform.final_parent(trans_id), root)
 
113
        self.assertIs(transform.final_parent(root), ROOT_PARENT)
 
114
        self.assertIs(transform.get_tree_parent(root), ROOT_PARENT)
 
115
        oz_id = transform.create_path('oz', root)
 
116
        transform.create_directory(oz_id)
 
117
        transform.version_file('ozzie', oz_id)
 
118
        trans_id2 = transform.create_path('name2', root)
 
119
        transform.create_file('contents', trans_id2)
 
120
        transform.set_executability(False, trans_id2)
 
121
        transform.version_file('my_pretties2', trans_id2)
 
122
        modified_paths = transform.apply().modified_paths
 
123
        self.assertEqual('contents', self.wt.get_file_byname('name').read())
 
124
        self.assertEqual(self.wt.path2id('name'), 'my_pretties')
 
125
        self.assertIs(self.wt.is_executable('my_pretties'), True)
 
126
        self.assertIs(self.wt.is_executable('my_pretties2'), False)
 
127
        self.assertEqual('directory', file_kind(self.wt.abspath('oz')))
 
128
        self.assertEqual(len(modified_paths), 3)
 
129
        tree_mod_paths = [self.wt.id2abspath(f) for f in 
 
130
                          ('ozzie', 'my_pretties', 'my_pretties2')]
 
131
        self.assertSubset(tree_mod_paths, modified_paths)
 
132
        # is it safe to finalize repeatedly?
 
133
        transform.finalize()
 
134
        transform.finalize()
 
135
 
 
136
    def test_convenience(self):
 
137
        transform, root = self.get_transform()
 
138
        self.wt.lock_tree_write()
 
139
        self.addCleanup(self.wt.unlock)
 
140
        trans_id = transform.new_file('name', root, 'contents', 
 
141
                                      'my_pretties', True)
 
142
        oz = transform.new_directory('oz', root, 'oz-id')
 
143
        dorothy = transform.new_directory('dorothy', oz, 'dorothy-id')
 
144
        toto = transform.new_file('toto', dorothy, 'toto-contents', 
 
145
                                  'toto-id', False)
 
146
 
 
147
        self.assertEqual(len(transform.find_conflicts()), 0)
 
148
        transform.apply()
 
149
        self.assertRaises(ReusingTransform, transform.find_conflicts)
 
150
        self.assertEqual('contents', file(self.wt.abspath('name')).read())
 
151
        self.assertEqual(self.wt.path2id('name'), 'my_pretties')
 
152
        self.assertIs(self.wt.is_executable('my_pretties'), True)
 
153
        self.assertEqual(self.wt.path2id('oz'), 'oz-id')
 
154
        self.assertEqual(self.wt.path2id('oz/dorothy'), 'dorothy-id')
 
155
        self.assertEqual(self.wt.path2id('oz/dorothy/toto'), 'toto-id')
 
156
 
 
157
        self.assertEqual('toto-contents',
 
158
                         self.wt.get_file_byname('oz/dorothy/toto').read())
 
159
        self.assertIs(self.wt.is_executable('toto-id'), False)
 
160
 
 
161
    def test_tree_reference(self):
 
162
        transform, root = self.get_transform()
 
163
        tree = transform._tree
 
164
        trans_id = transform.new_directory('reference', root, 'subtree-id')
 
165
        transform.set_tree_reference('subtree-revision', trans_id)
 
166
        transform.apply()
 
167
        tree.lock_read()
 
168
        self.addCleanup(tree.unlock)
 
169
        self.assertEqual('subtree-revision',
 
170
                         tree.inventory['subtree-id'].reference_revision)
 
171
 
 
172
    def test_conflicts(self):
 
173
        transform, root = self.get_transform()
 
174
        trans_id = transform.new_file('name', root, 'contents', 
 
175
                                      'my_pretties')
 
176
        self.assertEqual(len(transform.find_conflicts()), 0)
 
177
        trans_id2 = transform.new_file('name', root, 'Crontents', 'toto')
 
178
        self.assertEqual(transform.find_conflicts(), 
 
179
                         [('duplicate', trans_id, trans_id2, 'name')])
 
180
        self.assertRaises(MalformedTransform, transform.apply)
 
181
        transform.adjust_path('name', trans_id, trans_id2)
 
182
        self.assertEqual(transform.find_conflicts(), 
 
183
                         [('non-directory parent', trans_id)])
 
184
        tinman_id = transform.trans_id_tree_path('tinman')
 
185
        transform.adjust_path('name', tinman_id, trans_id2)
 
186
        self.assertEqual(transform.find_conflicts(), 
 
187
                         [('unversioned parent', tinman_id), 
 
188
                          ('missing parent', tinman_id)])
 
189
        lion_id = transform.create_path('lion', root)
 
190
        self.assertEqual(transform.find_conflicts(), 
 
191
                         [('unversioned parent', tinman_id), 
 
192
                          ('missing parent', tinman_id)])
 
193
        transform.adjust_path('name', lion_id, trans_id2)
 
194
        self.assertEqual(transform.find_conflicts(), 
 
195
                         [('unversioned parent', lion_id),
 
196
                          ('missing parent', lion_id)])
 
197
        transform.version_file("Courage", lion_id)
 
198
        self.assertEqual(transform.find_conflicts(), 
 
199
                         [('missing parent', lion_id), 
 
200
                          ('versioning no contents', lion_id)])
 
201
        transform.adjust_path('name2', root, trans_id2)
 
202
        self.assertEqual(transform.find_conflicts(), 
 
203
                         [('versioning no contents', lion_id)])
 
204
        transform.create_file('Contents, okay?', lion_id)
 
205
        transform.adjust_path('name2', trans_id2, trans_id2)
 
206
        self.assertEqual(transform.find_conflicts(), 
 
207
                         [('parent loop', trans_id2), 
 
208
                          ('non-directory parent', trans_id2)])
 
209
        transform.adjust_path('name2', root, trans_id2)
 
210
        oz_id = transform.new_directory('oz', root)
 
211
        transform.set_executability(True, oz_id)
 
212
        self.assertEqual(transform.find_conflicts(), 
 
213
                         [('unversioned executability', oz_id)])
 
214
        transform.version_file('oz-id', oz_id)
 
215
        self.assertEqual(transform.find_conflicts(), 
 
216
                         [('non-file executability', oz_id)])
 
217
        transform.set_executability(None, oz_id)
 
218
        tip_id = transform.new_file('tip', oz_id, 'ozma', 'tip-id')
 
219
        transform.apply()
 
220
        self.assertEqual(self.wt.path2id('name'), 'my_pretties')
 
221
        self.assertEqual('contents', file(self.wt.abspath('name')).read())
 
222
        transform2, root = self.get_transform()
 
223
        oz_id = transform2.trans_id_tree_file_id('oz-id')
 
224
        newtip = transform2.new_file('tip', oz_id, 'other', 'tip-id')
 
225
        result = transform2.find_conflicts()
 
226
        fp = FinalPaths(transform2)
 
227
        self.assert_('oz/tip' in transform2._tree_path_ids)
 
228
        self.assertEqual(fp.get_path(newtip), pathjoin('oz', 'tip'))
 
229
        self.assertEqual(len(result), 2)
 
230
        self.assertEqual((result[0][0], result[0][1]), 
 
231
                         ('duplicate', newtip))
 
232
        self.assertEqual((result[1][0], result[1][2]), 
 
233
                         ('duplicate id', newtip))
 
234
        transform2.finalize()
 
235
        transform3 = TreeTransform(self.wt)
 
236
        self.addCleanup(transform3.finalize)
 
237
        oz_id = transform3.trans_id_tree_file_id('oz-id')
 
238
        transform3.delete_contents(oz_id)
 
239
        self.assertEqual(transform3.find_conflicts(), 
 
240
                         [('missing parent', oz_id)])
 
241
        root_id = transform3.root
 
242
        tip_id = transform3.trans_id_tree_file_id('tip-id')
 
243
        transform3.adjust_path('tip', root_id, tip_id)
 
244
        transform3.apply()
 
245
 
 
246
    def test_conflict_on_case_insensitive(self):
 
247
        tree = self.make_branch_and_tree('tree')
 
248
        # Don't try this at home, kids!
 
249
        # Force the tree to report that it is case sensitive, for conflict
 
250
        # resolution tests
 
251
        tree.case_sensitive = True
 
252
        transform = TreeTransform(tree)
 
253
        self.addCleanup(transform.finalize)
 
254
        transform.new_file('file', transform.root, 'content')
 
255
        transform.new_file('FiLe', transform.root, 'content')
 
256
        result = transform.find_conflicts()
 
257
        self.assertEqual([], result)
 
258
        transform.finalize()
 
259
        # Force the tree to report that it is case insensitive, for conflict
 
260
        # generation tests
 
261
        tree.case_sensitive = False
 
262
        transform = TreeTransform(tree)
 
263
        self.addCleanup(transform.finalize)
 
264
        transform.new_file('file', transform.root, 'content')
 
265
        transform.new_file('FiLe', transform.root, 'content')
 
266
        result = transform.find_conflicts()
 
267
        self.assertEqual([('duplicate', 'new-1', 'new-2', 'file')], result)
 
268
 
 
269
    def test_conflict_on_case_insensitive_existing(self):
 
270
        tree = self.make_branch_and_tree('tree')
 
271
        self.build_tree(['tree/FiLe'])
 
272
        # Don't try this at home, kids!
 
273
        # Force the tree to report that it is case sensitive, for conflict
 
274
        # resolution tests
 
275
        tree.case_sensitive = True
 
276
        transform = TreeTransform(tree)
 
277
        self.addCleanup(transform.finalize)
 
278
        transform.new_file('file', transform.root, 'content')
 
279
        result = transform.find_conflicts()
 
280
        self.assertEqual([], result)
 
281
        transform.finalize()
 
282
        # Force the tree to report that it is case insensitive, for conflict
 
283
        # generation tests
 
284
        tree.case_sensitive = False
 
285
        transform = TreeTransform(tree)
 
286
        self.addCleanup(transform.finalize)
 
287
        transform.new_file('file', transform.root, 'content')
 
288
        result = transform.find_conflicts()
 
289
        self.assertEqual([('duplicate', 'new-1', 'new-2', 'file')], result)
 
290
 
 
291
    def test_resolve_case_insensitive_conflict(self):
 
292
        tree = self.make_branch_and_tree('tree')
 
293
        # Don't try this at home, kids!
 
294
        # Force the tree to report that it is case insensitive, for conflict
 
295
        # resolution tests
 
296
        tree.case_sensitive = False
 
297
        transform = TreeTransform(tree)
 
298
        self.addCleanup(transform.finalize)
 
299
        transform.new_file('file', transform.root, 'content')
 
300
        transform.new_file('FiLe', transform.root, 'content')
 
301
        resolve_conflicts(transform)
 
302
        transform.apply()
 
303
        self.failUnlessExists('tree/file')
 
304
        self.failUnlessExists('tree/FiLe.moved')
 
305
 
 
306
    def test_resolve_checkout_case_conflict(self):
 
307
        tree = self.make_branch_and_tree('tree')
 
308
        # Don't try this at home, kids!
 
309
        # Force the tree to report that it is case insensitive, for conflict
 
310
        # resolution tests
 
311
        tree.case_sensitive = False
 
312
        transform = TreeTransform(tree)
 
313
        self.addCleanup(transform.finalize)
 
314
        transform.new_file('file', transform.root, 'content')
 
315
        transform.new_file('FiLe', transform.root, 'content')
 
316
        resolve_conflicts(transform,
 
317
                          pass_func=lambda t, c: resolve_checkout(t, c, []))
 
318
        transform.apply()
 
319
        self.failUnlessExists('tree/file')
 
320
        self.failUnlessExists('tree/FiLe.moved')
 
321
 
 
322
    def test_apply_case_conflict(self):
 
323
        """Ensure that a transform with case conflicts can always be applied"""
 
324
        tree = self.make_branch_and_tree('tree')
 
325
        transform = TreeTransform(tree)
 
326
        self.addCleanup(transform.finalize)
 
327
        transform.new_file('file', transform.root, 'content')
 
328
        transform.new_file('FiLe', transform.root, 'content')
 
329
        dir = transform.new_directory('dir', transform.root)
 
330
        transform.new_file('dirfile', dir, 'content')
 
331
        transform.new_file('dirFiLe', dir, 'content')
 
332
        resolve_conflicts(transform)
 
333
        transform.apply()
 
334
        self.failUnlessExists('tree/file')
 
335
        if not os.path.exists('tree/FiLe.moved'):
 
336
            self.failUnlessExists('tree/FiLe')
 
337
        self.failUnlessExists('tree/dir/dirfile')
 
338
        if not os.path.exists('tree/dir/dirFiLe.moved'):
 
339
            self.failUnlessExists('tree/dir/dirFiLe')
 
340
 
 
341
    def test_case_insensitive_limbo(self):
 
342
        tree = self.make_branch_and_tree('tree')
 
343
        # Don't try this at home, kids!
 
344
        # Force the tree to report that it is case insensitive
 
345
        tree.case_sensitive = False
 
346
        transform = TreeTransform(tree)
 
347
        self.addCleanup(transform.finalize)
 
348
        dir = transform.new_directory('dir', transform.root)
 
349
        first = transform.new_file('file', dir, 'content')
 
350
        second = transform.new_file('FiLe', dir, 'content')
 
351
        self.assertContainsRe(transform._limbo_name(first), 'new-1/file')
 
352
        self.assertNotContainsRe(transform._limbo_name(second), 'new-1/FiLe')
 
353
 
 
354
    def test_add_del(self):
 
355
        start, root = self.get_transform()
 
356
        start.new_directory('a', root, 'a')
 
357
        start.apply()
 
358
        transform, root = self.get_transform()
 
359
        transform.delete_versioned(transform.trans_id_tree_file_id('a'))
 
360
        transform.new_directory('a', root, 'a')
 
361
        transform.apply()
 
362
 
 
363
    def test_unversioning(self):
 
364
        create_tree, root = self.get_transform()
 
365
        parent_id = create_tree.new_directory('parent', root, 'parent-id')
 
366
        create_tree.new_file('child', parent_id, 'child', 'child-id')
 
367
        create_tree.apply()
 
368
        unversion = TreeTransform(self.wt)
 
369
        self.addCleanup(unversion.finalize)
 
370
        parent = unversion.trans_id_tree_path('parent')
 
371
        unversion.unversion_file(parent)
 
372
        self.assertEqual(unversion.find_conflicts(), 
 
373
                         [('unversioned parent', parent_id)])
 
374
        file_id = unversion.trans_id_tree_file_id('child-id')
 
375
        unversion.unversion_file(file_id)
 
376
        unversion.apply()
 
377
 
 
378
    def test_name_invariants(self):
 
379
        create_tree, root = self.get_transform()
 
380
        # prepare tree
 
381
        root = create_tree.root
 
382
        create_tree.new_file('name1', root, 'hello1', 'name1')
 
383
        create_tree.new_file('name2', root, 'hello2', 'name2')
 
384
        ddir = create_tree.new_directory('dying_directory', root, 'ddir')
 
385
        create_tree.new_file('dying_file', ddir, 'goodbye1', 'dfile')
 
386
        create_tree.new_file('moving_file', ddir, 'later1', 'mfile')
 
387
        create_tree.new_file('moving_file2', root, 'later2', 'mfile2')
 
388
        create_tree.apply()
 
389
 
 
390
        mangle_tree,root = self.get_transform()
 
391
        root = mangle_tree.root
 
392
        #swap names
 
393
        name1 = mangle_tree.trans_id_tree_file_id('name1')
 
394
        name2 = mangle_tree.trans_id_tree_file_id('name2')
 
395
        mangle_tree.adjust_path('name2', root, name1)
 
396
        mangle_tree.adjust_path('name1', root, name2)
 
397
 
 
398
        #tests for deleting parent directories 
 
399
        ddir = mangle_tree.trans_id_tree_file_id('ddir')
 
400
        mangle_tree.delete_contents(ddir)
 
401
        dfile = mangle_tree.trans_id_tree_file_id('dfile')
 
402
        mangle_tree.delete_versioned(dfile)
 
403
        mangle_tree.unversion_file(dfile)
 
404
        mfile = mangle_tree.trans_id_tree_file_id('mfile')
 
405
        mangle_tree.adjust_path('mfile', root, mfile)
 
406
 
 
407
        #tests for adding parent directories
 
408
        newdir = mangle_tree.new_directory('new_directory', root, 'newdir')
 
409
        mfile2 = mangle_tree.trans_id_tree_file_id('mfile2')
 
410
        mangle_tree.adjust_path('mfile2', newdir, mfile2)
 
411
        mangle_tree.new_file('newfile', newdir, 'hello3', 'dfile')
 
412
        self.assertEqual(mangle_tree.final_file_id(mfile2), 'mfile2')
 
413
        self.assertEqual(mangle_tree.final_parent(mfile2), newdir)
 
414
        self.assertEqual(mangle_tree.final_file_id(mfile2), 'mfile2')
 
415
        mangle_tree.apply()
 
416
        self.assertEqual(file(self.wt.abspath('name1')).read(), 'hello2')
 
417
        self.assertEqual(file(self.wt.abspath('name2')).read(), 'hello1')
 
418
        mfile2_path = self.wt.abspath(pathjoin('new_directory','mfile2'))
 
419
        self.assertEqual(mangle_tree.final_parent(mfile2), newdir)
 
420
        self.assertEqual(file(mfile2_path).read(), 'later2')
 
421
        self.assertEqual(self.wt.id2path('mfile2'), 'new_directory/mfile2')
 
422
        self.assertEqual(self.wt.path2id('new_directory/mfile2'), 'mfile2')
 
423
        newfile_path = self.wt.abspath(pathjoin('new_directory','newfile'))
 
424
        self.assertEqual(file(newfile_path).read(), 'hello3')
 
425
        self.assertEqual(self.wt.path2id('dying_directory'), 'ddir')
 
426
        self.assertIs(self.wt.path2id('dying_directory/dying_file'), None)
 
427
        mfile2_path = self.wt.abspath(pathjoin('new_directory','mfile2'))
 
428
 
 
429
    def test_both_rename(self):
 
430
        create_tree,root = self.get_transform()
 
431
        newdir = create_tree.new_directory('selftest', root, 'selftest-id')
 
432
        create_tree.new_file('blackbox.py', newdir, 'hello1', 'blackbox-id')
 
433
        create_tree.apply()        
 
434
        mangle_tree,root = self.get_transform()
 
435
        selftest = mangle_tree.trans_id_tree_file_id('selftest-id')
 
436
        blackbox = mangle_tree.trans_id_tree_file_id('blackbox-id')
 
437
        mangle_tree.adjust_path('test', root, selftest)
 
438
        mangle_tree.adjust_path('test_too_much', root, selftest)
 
439
        mangle_tree.set_executability(True, blackbox)
 
440
        mangle_tree.apply()
 
441
 
 
442
    def test_both_rename2(self):
 
443
        create_tree,root = self.get_transform()
 
444
        bzrlib = create_tree.new_directory('bzrlib', root, 'bzrlib-id')
 
445
        tests = create_tree.new_directory('tests', bzrlib, 'tests-id')
 
446
        blackbox = create_tree.new_directory('blackbox', tests, 'blackbox-id')
 
447
        create_tree.new_file('test_too_much.py', blackbox, 'hello1', 
 
448
                             'test_too_much-id')
 
449
        create_tree.apply()        
 
450
        mangle_tree,root = self.get_transform()
 
451
        bzrlib = mangle_tree.trans_id_tree_file_id('bzrlib-id')
 
452
        tests = mangle_tree.trans_id_tree_file_id('tests-id')
 
453
        test_too_much = mangle_tree.trans_id_tree_file_id('test_too_much-id')
 
454
        mangle_tree.adjust_path('selftest', bzrlib, tests)
 
455
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much) 
 
456
        mangle_tree.set_executability(True, test_too_much)
 
457
        mangle_tree.apply()
 
458
 
 
459
    def test_both_rename3(self):
 
460
        create_tree,root = self.get_transform()
 
461
        tests = create_tree.new_directory('tests', root, 'tests-id')
 
462
        create_tree.new_file('test_too_much.py', tests, 'hello1', 
 
463
                             'test_too_much-id')
 
464
        create_tree.apply()        
 
465
        mangle_tree,root = self.get_transform()
 
466
        tests = mangle_tree.trans_id_tree_file_id('tests-id')
 
467
        test_too_much = mangle_tree.trans_id_tree_file_id('test_too_much-id')
 
468
        mangle_tree.adjust_path('selftest', root, tests)
 
469
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much) 
 
470
        mangle_tree.set_executability(True, test_too_much)
 
471
        mangle_tree.apply()
 
472
 
 
473
    def test_move_dangling_ie(self):
 
474
        create_tree, root = self.get_transform()
 
475
        # prepare tree
 
476
        root = create_tree.root
 
477
        create_tree.new_file('name1', root, 'hello1', 'name1')
 
478
        create_tree.apply()
 
479
        delete_contents, root = self.get_transform()
 
480
        file = delete_contents.trans_id_tree_file_id('name1')
 
481
        delete_contents.delete_contents(file)
 
482
        delete_contents.apply()
 
483
        move_id, root = self.get_transform()
 
484
        name1 = move_id.trans_id_tree_file_id('name1')
 
485
        newdir = move_id.new_directory('dir', root, 'newdir')
 
486
        move_id.adjust_path('name2', newdir, name1)
 
487
        move_id.apply()
 
488
        
 
489
    def test_replace_dangling_ie(self):
 
490
        create_tree, root = self.get_transform()
 
491
        # prepare tree
 
492
        root = create_tree.root
 
493
        create_tree.new_file('name1', root, 'hello1', 'name1')
 
494
        create_tree.apply()
 
495
        delete_contents = TreeTransform(self.wt)
 
496
        self.addCleanup(delete_contents.finalize)
 
497
        file = delete_contents.trans_id_tree_file_id('name1')
 
498
        delete_contents.delete_contents(file)
 
499
        delete_contents.apply()
 
500
        delete_contents.finalize()
 
501
        replace = TreeTransform(self.wt)
 
502
        self.addCleanup(replace.finalize)
 
503
        name2 = replace.new_file('name2', root, 'hello2', 'name1')
 
504
        conflicts = replace.find_conflicts()
 
505
        name1 = replace.trans_id_tree_file_id('name1')
 
506
        self.assertEqual(conflicts, [('duplicate id', name1, name2)])
 
507
        resolve_conflicts(replace)
 
508
        replace.apply()
 
509
 
 
510
    def test_symlinks(self):
 
511
        self.requireFeature(SymlinkFeature)
 
512
        transform,root = self.get_transform()
 
513
        oz_id = transform.new_directory('oz', root, 'oz-id')
 
514
        wizard = transform.new_symlink('wizard', oz_id, 'wizard-target', 
 
515
                                       'wizard-id')
 
516
        wiz_id = transform.create_path('wizard2', oz_id)
 
517
        transform.create_symlink('behind_curtain', wiz_id)
 
518
        transform.version_file('wiz-id2', wiz_id)            
 
519
        transform.set_executability(True, wiz_id)
 
520
        self.assertEqual(transform.find_conflicts(), 
 
521
                         [('non-file executability', wiz_id)])
 
522
        transform.set_executability(None, wiz_id)
 
523
        transform.apply()
 
524
        self.assertEqual(self.wt.path2id('oz/wizard'), 'wizard-id')
 
525
        self.assertEqual(file_kind(self.wt.abspath('oz/wizard')), 'symlink')
 
526
        self.assertEqual(os.readlink(self.wt.abspath('oz/wizard2')), 
 
527
                         'behind_curtain')
 
528
        self.assertEqual(os.readlink(self.wt.abspath('oz/wizard')),
 
529
                         'wizard-target')
 
530
 
 
531
    def test_unable_create_symlink(self):
 
532
        def tt_helper():
 
533
            wt = self.make_branch_and_tree('.')
 
534
            tt = TreeTransform(wt)  # TreeTransform obtains write lock
 
535
            try:
 
536
                tt.new_symlink('foo', tt.root, 'bar')
 
537
                tt.apply()
 
538
            finally:
 
539
                wt.unlock()
 
540
        os_symlink = getattr(os, 'symlink', None)
 
541
        os.symlink = None
 
542
        try:
 
543
            err = self.assertRaises(errors.UnableCreateSymlink, tt_helper)
 
544
            self.assertEquals(
 
545
                "Unable to create symlink 'foo' on this platform",
 
546
                str(err))
 
547
        finally:
 
548
            if os_symlink:
 
549
                os.symlink = os_symlink
 
550
 
 
551
    def get_conflicted(self):
 
552
        create,root = self.get_transform()
 
553
        create.new_file('dorothy', root, 'dorothy', 'dorothy-id')
 
554
        oz = create.new_directory('oz', root, 'oz-id')
 
555
        create.new_directory('emeraldcity', oz, 'emerald-id')
 
556
        create.apply()
 
557
        conflicts,root = self.get_transform()
 
558
        # set up duplicate entry, duplicate id
 
559
        new_dorothy = conflicts.new_file('dorothy', root, 'dorothy', 
 
560
                                         'dorothy-id')
 
561
        old_dorothy = conflicts.trans_id_tree_file_id('dorothy-id')
 
562
        oz = conflicts.trans_id_tree_file_id('oz-id')
 
563
        # set up DeletedParent parent conflict
 
564
        conflicts.delete_versioned(oz)
 
565
        emerald = conflicts.trans_id_tree_file_id('emerald-id')
 
566
        # set up MissingParent conflict
 
567
        munchkincity = conflicts.trans_id_file_id('munchkincity-id')
 
568
        conflicts.adjust_path('munchkincity', root, munchkincity)
 
569
        conflicts.new_directory('auntem', munchkincity, 'auntem-id')
 
570
        # set up parent loop
 
571
        conflicts.adjust_path('emeraldcity', emerald, emerald)
 
572
        return conflicts, emerald, oz, old_dorothy, new_dorothy
 
573
 
 
574
    def test_conflict_resolution(self):
 
575
        conflicts, emerald, oz, old_dorothy, new_dorothy =\
 
576
            self.get_conflicted()
 
577
        resolve_conflicts(conflicts)
 
578
        self.assertEqual(conflicts.final_name(old_dorothy), 'dorothy.moved')
 
579
        self.assertIs(conflicts.final_file_id(old_dorothy), None)
 
580
        self.assertEqual(conflicts.final_name(new_dorothy), 'dorothy')
 
581
        self.assertEqual(conflicts.final_file_id(new_dorothy), 'dorothy-id')
 
582
        self.assertEqual(conflicts.final_parent(emerald), oz)
 
583
        conflicts.apply()
 
584
 
 
585
    def test_cook_conflicts(self):
 
586
        tt, emerald, oz, old_dorothy, new_dorothy = self.get_conflicted()
 
587
        raw_conflicts = resolve_conflicts(tt)
 
588
        cooked_conflicts = cook_conflicts(raw_conflicts, tt)
 
589
        duplicate = DuplicateEntry('Moved existing file to', 'dorothy.moved', 
 
590
                                   'dorothy', None, 'dorothy-id')
 
591
        self.assertEqual(cooked_conflicts[0], duplicate)
 
592
        duplicate_id = DuplicateID('Unversioned existing file', 
 
593
                                   'dorothy.moved', 'dorothy', None,
 
594
                                   'dorothy-id')
 
595
        self.assertEqual(cooked_conflicts[1], duplicate_id)
 
596
        missing_parent = MissingParent('Created directory', 'munchkincity',
 
597
                                       'munchkincity-id')
 
598
        deleted_parent = DeletingParent('Not deleting', 'oz', 'oz-id')
 
599
        self.assertEqual(cooked_conflicts[2], missing_parent)
 
600
        unversioned_parent = UnversionedParent('Versioned directory',
 
601
                                               'munchkincity',
 
602
                                               'munchkincity-id')
 
603
        unversioned_parent2 = UnversionedParent('Versioned directory', 'oz',
 
604
                                               'oz-id')
 
605
        self.assertEqual(cooked_conflicts[3], unversioned_parent)
 
606
        parent_loop = ParentLoop('Cancelled move', 'oz/emeraldcity', 
 
607
                                 'oz/emeraldcity', 'emerald-id', 'emerald-id')
 
608
        self.assertEqual(cooked_conflicts[4], deleted_parent)
 
609
        self.assertEqual(cooked_conflicts[5], unversioned_parent2)
 
610
        self.assertEqual(cooked_conflicts[6], parent_loop)
 
611
        self.assertEqual(len(cooked_conflicts), 7)
 
612
        tt.finalize()
 
613
 
 
614
    def test_string_conflicts(self):
 
615
        tt, emerald, oz, old_dorothy, new_dorothy = self.get_conflicted()
 
616
        raw_conflicts = resolve_conflicts(tt)
 
617
        cooked_conflicts = cook_conflicts(raw_conflicts, tt)
 
618
        tt.finalize()
 
619
        conflicts_s = [str(c) for c in cooked_conflicts]
 
620
        self.assertEqual(len(cooked_conflicts), len(conflicts_s))
 
621
        self.assertEqual(conflicts_s[0], 'Conflict adding file dorothy.  '
 
622
                                         'Moved existing file to '
 
623
                                         'dorothy.moved.')
 
624
        self.assertEqual(conflicts_s[1], 'Conflict adding id to dorothy.  '
 
625
                                         'Unversioned existing file '
 
626
                                         'dorothy.moved.')
 
627
        self.assertEqual(conflicts_s[2], 'Conflict adding files to'
 
628
                                         ' munchkincity.  Created directory.')
 
629
        self.assertEqual(conflicts_s[3], 'Conflict because munchkincity is not'
 
630
                                         ' versioned, but has versioned'
 
631
                                         ' children.  Versioned directory.')
 
632
        self.assertEqualDiff(conflicts_s[4], "Conflict: can't delete oz because it"
 
633
                                         " is not empty.  Not deleting.")
 
634
        self.assertEqual(conflicts_s[5], 'Conflict because oz is not'
 
635
                                         ' versioned, but has versioned'
 
636
                                         ' children.  Versioned directory.')
 
637
        self.assertEqual(conflicts_s[6], 'Conflict moving oz/emeraldcity into'
 
638
                                         ' oz/emeraldcity.  Cancelled move.')
 
639
 
 
640
    def prepare_wrong_parent_kind(self):
 
641
        tt, root = self.get_transform()
 
642
        tt.new_file('parent', root, 'contents', 'parent-id')
 
643
        tt.apply()
 
644
        tt, root = self.get_transform()
 
645
        parent_id = tt.trans_id_file_id('parent-id')
 
646
        tt.new_file('child,', parent_id, 'contents2', 'file-id')
 
647
        return tt
 
648
 
 
649
    def test_find_conflicts_wrong_parent_kind(self):
 
650
        tt = self.prepare_wrong_parent_kind()
 
651
        tt.find_conflicts()
 
652
 
 
653
    def test_resolve_conflicts_wrong_existing_parent_kind(self):
 
654
        tt = self.prepare_wrong_parent_kind()
 
655
        raw_conflicts = resolve_conflicts(tt)
 
656
        self.assertEqual(set([('non-directory parent', 'Created directory',
 
657
                         'new-3')]), raw_conflicts)
 
658
        cooked_conflicts = cook_conflicts(raw_conflicts, tt)
 
659
        self.assertEqual([NonDirectoryParent('Created directory', 'parent.new',
 
660
        'parent-id')], cooked_conflicts)
 
661
        tt.apply()
 
662
        self.assertEqual(None, self.wt.path2id('parent'))
 
663
        self.assertEqual('parent-id', self.wt.path2id('parent.new'))
 
664
 
 
665
    def test_resolve_conflicts_wrong_new_parent_kind(self):
 
666
        tt, root = self.get_transform()
 
667
        parent_id = tt.new_directory('parent', root, 'parent-id')
 
668
        tt.new_file('child,', parent_id, 'contents2', 'file-id')
 
669
        tt.apply()
 
670
        tt, root = self.get_transform()
 
671
        parent_id = tt.trans_id_file_id('parent-id')
 
672
        tt.delete_contents(parent_id)
 
673
        tt.create_file('contents', parent_id)
 
674
        raw_conflicts = resolve_conflicts(tt)
 
675
        self.assertEqual(set([('non-directory parent', 'Created directory',
 
676
                         'new-3')]), raw_conflicts)
 
677
        tt.apply()
 
678
        self.assertEqual(None, self.wt.path2id('parent'))
 
679
        self.assertEqual('parent-id', self.wt.path2id('parent.new'))
 
680
 
 
681
    def test_resolve_conflicts_wrong_parent_kind_unversioned(self):
 
682
        tt, root = self.get_transform()
 
683
        parent_id = tt.new_directory('parent', root)
 
684
        tt.new_file('child,', parent_id, 'contents2')
 
685
        tt.apply()
 
686
        tt, root = self.get_transform()
 
687
        parent_id = tt.trans_id_tree_path('parent')
 
688
        tt.delete_contents(parent_id)
 
689
        tt.create_file('contents', parent_id)
 
690
        resolve_conflicts(tt)
 
691
        tt.apply()
 
692
        self.assertIs(None, self.wt.path2id('parent'))
 
693
        self.assertIs(None, self.wt.path2id('parent.new'))
 
694
 
 
695
    def test_moving_versioned_directories(self):
 
696
        create, root = self.get_transform()
 
697
        kansas = create.new_directory('kansas', root, 'kansas-id')
 
698
        create.new_directory('house', kansas, 'house-id')
 
699
        create.new_directory('oz', root, 'oz-id')
 
700
        create.apply()
 
701
        cyclone, root = self.get_transform()
 
702
        oz = cyclone.trans_id_tree_file_id('oz-id')
 
703
        house = cyclone.trans_id_tree_file_id('house-id')
 
704
        cyclone.adjust_path('house', oz, house)
 
705
        cyclone.apply()
 
706
 
 
707
    def test_moving_root(self):
 
708
        create, root = self.get_transform()
 
709
        fun = create.new_directory('fun', root, 'fun-id')
 
710
        create.new_directory('sun', root, 'sun-id')
 
711
        create.new_directory('moon', root, 'moon')
 
712
        create.apply()
 
713
        transform, root = self.get_transform()
 
714
        transform.adjust_root_path('oldroot', fun)
 
715
        new_root=transform.trans_id_tree_path('')
 
716
        transform.version_file('new-root', new_root)
 
717
        transform.apply()
 
718
 
 
719
    def test_renames(self):
 
720
        create, root = self.get_transform()
 
721
        old = create.new_directory('old-parent', root, 'old-id')
 
722
        intermediate = create.new_directory('intermediate', old, 'im-id')
 
723
        myfile = create.new_file('myfile', intermediate, 'myfile-text',
 
724
                                 'myfile-id')
 
725
        create.apply()
 
726
        rename, root = self.get_transform()
 
727
        old = rename.trans_id_file_id('old-id')
 
728
        rename.adjust_path('new', root, old)
 
729
        myfile = rename.trans_id_file_id('myfile-id')
 
730
        rename.set_executability(True, myfile)
 
731
        rename.apply()
 
732
 
 
733
    def test_find_interesting(self):
 
734
        create, root = self.get_transform()
 
735
        wt = create._tree
 
736
        create.new_file('vfile', root, 'myfile-text', 'myfile-id')
 
737
        create.new_file('uvfile', root, 'othertext')
 
738
        create.apply()
 
739
        result = self.applyDeprecated(symbol_versioning.zero_fifteen,
 
740
            find_interesting, wt, wt, ['vfile'])
 
741
        self.assertEqual(result, set(['myfile-id']))
 
742
 
 
743
    def test_set_executability_order(self):
 
744
        """Ensure that executability behaves the same, no matter what order.
 
745
        
 
746
        - create file and set executability simultaneously
 
747
        - create file and set executability afterward
 
748
        - unsetting the executability of a file whose executability has not been
 
749
        declared should throw an exception (this may happen when a
 
750
        merge attempts to create a file with a duplicate ID)
 
751
        """
 
752
        transform, root = self.get_transform()
 
753
        wt = transform._tree
 
754
        wt.lock_read()
 
755
        self.addCleanup(wt.unlock)
 
756
        transform.new_file('set_on_creation', root, 'Set on creation', 'soc',
 
757
                           True)
 
758
        sac = transform.new_file('set_after_creation', root,
 
759
                                 'Set after creation', 'sac')
 
760
        transform.set_executability(True, sac)
 
761
        uws = transform.new_file('unset_without_set', root, 'Unset badly',
 
762
                                 'uws')
 
763
        self.assertRaises(KeyError, transform.set_executability, None, uws)
 
764
        transform.apply()
 
765
        self.assertTrue(wt.is_executable('soc'))
 
766
        self.assertTrue(wt.is_executable('sac'))
 
767
 
 
768
    def test_preserve_mode(self):
 
769
        """File mode is preserved when replacing content"""
 
770
        if sys.platform == 'win32':
 
771
            raise TestSkipped('chmod has no effect on win32')
 
772
        transform, root = self.get_transform()
 
773
        transform.new_file('file1', root, 'contents', 'file1-id', True)
 
774
        transform.apply()
 
775
        self.wt.lock_write()
 
776
        self.addCleanup(self.wt.unlock)
 
777
        self.assertTrue(self.wt.is_executable('file1-id'))
 
778
        transform, root = self.get_transform()
 
779
        file1_id = transform.trans_id_tree_file_id('file1-id')
 
780
        transform.delete_contents(file1_id)
 
781
        transform.create_file('contents2', file1_id)
 
782
        transform.apply()
 
783
        self.assertTrue(self.wt.is_executable('file1-id'))
 
784
 
 
785
    def test__set_mode_stats_correctly(self):
 
786
        """_set_mode stats to determine file mode."""
 
787
        if sys.platform == 'win32':
 
788
            raise TestSkipped('chmod has no effect on win32')
 
789
 
 
790
        stat_paths = []
 
791
        real_stat = os.stat
 
792
        def instrumented_stat(path):
 
793
            stat_paths.append(path)
 
794
            return real_stat(path)
 
795
 
 
796
        transform, root = self.get_transform()
 
797
 
 
798
        bar1_id = transform.new_file('bar', root, 'bar contents 1\n',
 
799
                                     file_id='bar-id-1', executable=False)
 
800
        transform.apply()
 
801
 
 
802
        transform, root = self.get_transform()
 
803
        bar1_id = transform.trans_id_tree_path('bar')
 
804
        bar2_id = transform.trans_id_tree_path('bar2')
 
805
        try:
 
806
            os.stat = instrumented_stat
 
807
            transform.create_file('bar2 contents\n', bar2_id, mode_id=bar1_id)
 
808
        finally:
 
809
            os.stat = real_stat
 
810
            transform.finalize()
 
811
 
 
812
        bar1_abspath = self.wt.abspath('bar')
 
813
        self.assertEqual([bar1_abspath], stat_paths)
 
814
 
 
815
    def test_iter_changes(self):
 
816
        self.wt.set_root_id('eert_toor')
 
817
        transform, root = self.get_transform()
 
818
        transform.new_file('old', root, 'blah', 'id-1', True)
 
819
        transform.apply()
 
820
        transform, root = self.get_transform()
 
821
        try:
 
822
            self.assertEqual([], list(transform._iter_changes()))
 
823
            old = transform.trans_id_tree_file_id('id-1')
 
824
            transform.unversion_file(old)
 
825
            self.assertEqual([('id-1', ('old', None), False, (True, False),
 
826
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
 
827
                (True, True))], list(transform._iter_changes()))
 
828
            transform.new_directory('new', root, 'id-1')
 
829
            self.assertEqual([('id-1', ('old', 'new'), True, (True, True),
 
830
                ('eert_toor', 'eert_toor'), ('old', 'new'),
 
831
                ('file', 'directory'),
 
832
                (True, False))], list(transform._iter_changes()))
 
833
        finally:
 
834
            transform.finalize()
 
835
 
 
836
    def test_iter_changes_new(self):
 
837
        self.wt.set_root_id('eert_toor')
 
838
        transform, root = self.get_transform()
 
839
        transform.new_file('old', root, 'blah')
 
840
        transform.apply()
 
841
        transform, root = self.get_transform()
 
842
        try:
 
843
            old = transform.trans_id_tree_path('old')
 
844
            transform.version_file('id-1', old)
 
845
            self.assertEqual([('id-1', (None, 'old'), False, (False, True),
 
846
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
 
847
                (False, False))], list(transform._iter_changes()))
 
848
        finally:
 
849
            transform.finalize()
 
850
 
 
851
    def test_iter_changes_modifications(self):
 
852
        self.wt.set_root_id('eert_toor')
 
853
        transform, root = self.get_transform()
 
854
        transform.new_file('old', root, 'blah', 'id-1')
 
855
        transform.new_file('new', root, 'blah')
 
856
        transform.new_directory('subdir', root, 'subdir-id')
 
857
        transform.apply()
 
858
        transform, root = self.get_transform()
 
859
        try:
 
860
            old = transform.trans_id_tree_path('old')
 
861
            subdir = transform.trans_id_tree_file_id('subdir-id')
 
862
            new = transform.trans_id_tree_path('new')
 
863
            self.assertEqual([], list(transform._iter_changes()))
 
864
 
 
865
            #content deletion
 
866
            transform.delete_contents(old)
 
867
            self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
 
868
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', None),
 
869
                (False, False))], list(transform._iter_changes()))
 
870
 
 
871
            #content change
 
872
            transform.create_file('blah', old)
 
873
            self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
 
874
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
 
875
                (False, False))], list(transform._iter_changes()))
 
876
            transform.cancel_deletion(old)
 
877
            self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
 
878
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
 
879
                (False, False))], list(transform._iter_changes()))
 
880
            transform.cancel_creation(old)
 
881
 
 
882
            # move file_id to a different file
 
883
            self.assertEqual([], list(transform._iter_changes()))
 
884
            transform.unversion_file(old)
 
885
            transform.version_file('id-1', new)
 
886
            transform.adjust_path('old', root, new)
 
887
            self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
 
888
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
 
889
                (False, False))], list(transform._iter_changes()))
 
890
            transform.cancel_versioning(new)
 
891
            transform._removed_id = set()
 
892
 
 
893
            #execute bit
 
894
            self.assertEqual([], list(transform._iter_changes()))
 
895
            transform.set_executability(True, old)
 
896
            self.assertEqual([('id-1', ('old', 'old'), False, (True, True),
 
897
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
 
898
                (False, True))], list(transform._iter_changes()))
 
899
            transform.set_executability(None, old)
 
900
 
 
901
            # filename
 
902
            self.assertEqual([], list(transform._iter_changes()))
 
903
            transform.adjust_path('new', root, old)
 
904
            transform._new_parent = {}
 
905
            self.assertEqual([('id-1', ('old', 'new'), False, (True, True),
 
906
                ('eert_toor', 'eert_toor'), ('old', 'new'), ('file', 'file'),
 
907
                (False, False))], list(transform._iter_changes()))
 
908
            transform._new_name = {}
 
909
 
 
910
            # parent directory
 
911
            self.assertEqual([], list(transform._iter_changes()))
 
912
            transform.adjust_path('new', subdir, old)
 
913
            transform._new_name = {}
 
914
            self.assertEqual([('id-1', ('old', 'subdir/old'), False,
 
915
                (True, True), ('eert_toor', 'subdir-id'), ('old', 'old'),
 
916
                ('file', 'file'), (False, False))],
 
917
                list(transform._iter_changes()))
 
918
            transform._new_path = {}
 
919
 
 
920
        finally:
 
921
            transform.finalize()
 
922
 
 
923
    def test_iter_changes_modified_bleed(self):
 
924
        self.wt.set_root_id('eert_toor')
 
925
        """Modified flag should not bleed from one change to another"""
 
926
        # unfortunately, we have no guarantee that file1 (which is modified)
 
927
        # will be applied before file2.  And if it's applied after file2, it
 
928
        # obviously can't bleed into file2's change output.  But for now, it
 
929
        # works.
 
930
        transform, root = self.get_transform()
 
931
        transform.new_file('file1', root, 'blah', 'id-1')
 
932
        transform.new_file('file2', root, 'blah', 'id-2')
 
933
        transform.apply()
 
934
        transform, root = self.get_transform()
 
935
        try:
 
936
            transform.delete_contents(transform.trans_id_file_id('id-1'))
 
937
            transform.set_executability(True,
 
938
            transform.trans_id_file_id('id-2'))
 
939
            self.assertEqual([('id-1', (u'file1', u'file1'), True, (True, True),
 
940
                ('eert_toor', 'eert_toor'), ('file1', u'file1'),
 
941
                ('file', None), (False, False)),
 
942
                ('id-2', (u'file2', u'file2'), False, (True, True),
 
943
                ('eert_toor', 'eert_toor'), ('file2', u'file2'),
 
944
                ('file', 'file'), (False, True))],
 
945
                list(transform._iter_changes()))
 
946
        finally:
 
947
            transform.finalize()
 
948
 
 
949
    def test_iter_changes_move_missing(self):
 
950
        """Test moving ids with no files around"""
 
951
        self.wt.set_root_id('toor_eert')
 
952
        # Need two steps because versioning a non-existant file is a conflict.
 
953
        transform, root = self.get_transform()
 
954
        transform.new_directory('floater', root, 'floater-id')
 
955
        transform.apply()
 
956
        transform, root = self.get_transform()
 
957
        transform.delete_contents(transform.trans_id_tree_path('floater'))
 
958
        transform.apply()
 
959
        transform, root = self.get_transform()
 
960
        floater = transform.trans_id_tree_path('floater')
 
961
        try:
 
962
            transform.adjust_path('flitter', root, floater)
 
963
            self.assertEqual([('floater-id', ('floater', 'flitter'), False,
 
964
            (True, True), ('toor_eert', 'toor_eert'), ('floater', 'flitter'),
 
965
            (None, None), (False, False))], list(transform._iter_changes()))
 
966
        finally:
 
967
            transform.finalize()
 
968
 
 
969
    def test_iter_changes_pointless(self):
 
970
        """Ensure that no-ops are not treated as modifications"""
 
971
        self.wt.set_root_id('eert_toor')
 
972
        transform, root = self.get_transform()
 
973
        transform.new_file('old', root, 'blah', 'id-1')
 
974
        transform.new_directory('subdir', root, 'subdir-id')
 
975
        transform.apply()
 
976
        transform, root = self.get_transform()
 
977
        try:
 
978
            old = transform.trans_id_tree_path('old')
 
979
            subdir = transform.trans_id_tree_file_id('subdir-id')
 
980
            self.assertEqual([], list(transform._iter_changes()))
 
981
            transform.delete_contents(subdir)
 
982
            transform.create_directory(subdir)
 
983
            transform.set_executability(False, old)
 
984
            transform.unversion_file(old)
 
985
            transform.version_file('id-1', old)
 
986
            transform.adjust_path('old', root, old)
 
987
            self.assertEqual([], list(transform._iter_changes()))
 
988
        finally:
 
989
            transform.finalize()
 
990
 
 
991
    def test_rename_count(self):
 
992
        transform, root = self.get_transform()
 
993
        transform.new_file('name1', root, 'contents')
 
994
        self.assertEqual(transform.rename_count, 0)
 
995
        transform.apply()
 
996
        self.assertEqual(transform.rename_count, 1)
 
997
        transform2, root = self.get_transform()
 
998
        transform2.adjust_path('name2', root,
 
999
                               transform2.trans_id_tree_path('name1'))
 
1000
        self.assertEqual(transform2.rename_count, 0)
 
1001
        transform2.apply()
 
1002
        self.assertEqual(transform2.rename_count, 2)
 
1003
 
 
1004
    def test_change_parent(self):
 
1005
        """Ensure that after we change a parent, the results are still right.
 
1006
 
 
1007
        Renames and parent changes on pending transforms can happen as part
 
1008
        of conflict resolution, and are explicitly permitted by the
 
1009
        TreeTransform API.
 
1010
 
 
1011
        This test ensures they work correctly with the rename-avoidance
 
1012
        optimization.
 
1013
        """
 
1014
        transform, root = self.get_transform()
 
1015
        parent1 = transform.new_directory('parent1', root)
 
1016
        child1 = transform.new_file('child1', parent1, 'contents')
 
1017
        parent2 = transform.new_directory('parent2', root)
 
1018
        transform.adjust_path('child1', parent2, child1)
 
1019
        transform.apply()
 
1020
        self.failIfExists(self.wt.abspath('parent1/child1'))
 
1021
        self.failUnlessExists(self.wt.abspath('parent2/child1'))
 
1022
        # rename limbo/new-1 => parent1, rename limbo/new-3 => parent2
 
1023
        # no rename for child1 (counting only renames during apply)
 
1024
        self.failUnlessEqual(2, transform.rename_count)
 
1025
 
 
1026
    def test_cancel_parent(self):
 
1027
        """Cancelling a parent doesn't cause deletion of a non-empty directory
 
1028
 
 
1029
        This is like the test_change_parent, except that we cancel the parent
 
1030
        before adjusting the path.  The transform must detect that the
 
1031
        directory is non-empty, and move children to safe locations.
 
1032
        """
 
1033
        transform, root = self.get_transform()
 
1034
        parent1 = transform.new_directory('parent1', root)
 
1035
        child1 = transform.new_file('child1', parent1, 'contents')
 
1036
        child2 = transform.new_file('child2', parent1, 'contents')
 
1037
        try:
 
1038
            transform.cancel_creation(parent1)
 
1039
        except OSError:
 
1040
            self.fail('Failed to move child1 before deleting parent1')
 
1041
        transform.cancel_creation(child2)
 
1042
        transform.create_directory(parent1)
 
1043
        try:
 
1044
            transform.cancel_creation(parent1)
 
1045
        # If the transform incorrectly believes that child2 is still in
 
1046
        # parent1's limbo directory, it will try to rename it and fail
 
1047
        # because was already moved by the first cancel_creation.
 
1048
        except OSError:
 
1049
            self.fail('Transform still thinks child2 is a child of parent1')
 
1050
        parent2 = transform.new_directory('parent2', root)
 
1051
        transform.adjust_path('child1', parent2, child1)
 
1052
        transform.apply()
 
1053
        self.failIfExists(self.wt.abspath('parent1'))
 
1054
        self.failUnlessExists(self.wt.abspath('parent2/child1'))
 
1055
        # rename limbo/new-3 => parent2, rename limbo/new-2 => child1
 
1056
        self.failUnlessEqual(2, transform.rename_count)
 
1057
 
 
1058
    def test_adjust_and_cancel(self):
 
1059
        """Make sure adjust_path keeps track of limbo children properly"""
 
1060
        transform, root = self.get_transform()
 
1061
        parent1 = transform.new_directory('parent1', root)
 
1062
        child1 = transform.new_file('child1', parent1, 'contents')
 
1063
        parent2 = transform.new_directory('parent2', root)
 
1064
        transform.adjust_path('child1', parent2, child1)
 
1065
        transform.cancel_creation(child1)
 
1066
        try:
 
1067
            transform.cancel_creation(parent1)
 
1068
        # if the transform thinks child1 is still in parent1's limbo
 
1069
        # directory, it will attempt to move it and fail.
 
1070
        except OSError:
 
1071
            self.fail('Transform still thinks child1 is a child of parent1')
 
1072
        transform.finalize()
 
1073
 
 
1074
    def test_noname_contents(self):
 
1075
        """TreeTransform should permit deferring naming files."""
 
1076
        transform, root = self.get_transform()
 
1077
        parent = transform.trans_id_file_id('parent-id')
 
1078
        try:
 
1079
            transform.create_directory(parent)
 
1080
        except KeyError:
 
1081
            self.fail("Can't handle contents with no name")
 
1082
        transform.finalize()
 
1083
 
 
1084
    def test_noname_contents_nested(self):
 
1085
        """TreeTransform should permit deferring naming files."""
 
1086
        transform, root = self.get_transform()
 
1087
        parent = transform.trans_id_file_id('parent-id')
 
1088
        try:
 
1089
            transform.create_directory(parent)
 
1090
        except KeyError:
 
1091
            self.fail("Can't handle contents with no name")
 
1092
        child = transform.new_directory('child', parent)
 
1093
        transform.adjust_path('parent', root, parent)
 
1094
        transform.apply()
 
1095
        self.failUnlessExists(self.wt.abspath('parent/child'))
 
1096
        self.assertEqual(1, transform.rename_count)
 
1097
 
 
1098
    def test_reuse_name(self):
 
1099
        """Avoid reusing the same limbo name for different files"""
 
1100
        transform, root = self.get_transform()
 
1101
        parent = transform.new_directory('parent', root)
 
1102
        child1 = transform.new_directory('child', parent)
 
1103
        try:
 
1104
            child2 = transform.new_directory('child', parent)
 
1105
        except OSError:
 
1106
            self.fail('Tranform tried to use the same limbo name twice')
 
1107
        transform.adjust_path('child2', parent, child2)
 
1108
        transform.apply()
 
1109
        # limbo/new-1 => parent, limbo/new-3 => parent/child2
 
1110
        # child2 is put into top-level limbo because child1 has already
 
1111
        # claimed the direct limbo path when child2 is created.  There is no
 
1112
        # advantage in renaming files once they're in top-level limbo, except
 
1113
        # as part of apply.
 
1114
        self.assertEqual(2, transform.rename_count)
 
1115
 
 
1116
    def test_reuse_when_first_moved(self):
 
1117
        """Don't avoid direct paths when it is safe to use them"""
 
1118
        transform, root = self.get_transform()
 
1119
        parent = transform.new_directory('parent', root)
 
1120
        child1 = transform.new_directory('child', parent)
 
1121
        transform.adjust_path('child1', parent, child1)
 
1122
        child2 = transform.new_directory('child', parent)
 
1123
        transform.apply()
 
1124
        # limbo/new-1 => parent
 
1125
        self.assertEqual(1, transform.rename_count)
 
1126
 
 
1127
    def test_reuse_after_cancel(self):
 
1128
        """Don't avoid direct paths when it is safe to use them"""
 
1129
        transform, root = self.get_transform()
 
1130
        parent2 = transform.new_directory('parent2', root)
 
1131
        child1 = transform.new_directory('child1', parent2)
 
1132
        transform.cancel_creation(parent2)
 
1133
        transform.create_directory(parent2)
 
1134
        child2 = transform.new_directory('child1', parent2)
 
1135
        transform.adjust_path('child2', parent2, child1)
 
1136
        transform.apply()
 
1137
        # limbo/new-1 => parent2, limbo/new-2 => parent2/child1
 
1138
        self.assertEqual(2, transform.rename_count)
 
1139
 
 
1140
    def test_finalize_order(self):
 
1141
        """Finalize must be done in child-to-parent order"""
 
1142
        transform, root = self.get_transform()
 
1143
        parent = transform.new_directory('parent', root)
 
1144
        child = transform.new_directory('child', parent)
 
1145
        try:
 
1146
            transform.finalize()
 
1147
        except OSError:
 
1148
            self.fail('Tried to remove parent before child1')
 
1149
 
 
1150
    def test_cancel_with_cancelled_child_should_succeed(self):
 
1151
        transform, root = self.get_transform()
 
1152
        parent = transform.new_directory('parent', root)
 
1153
        child = transform.new_directory('child', parent)
 
1154
        transform.cancel_creation(child)
 
1155
        transform.cancel_creation(parent)
 
1156
        transform.finalize()
 
1157
 
 
1158
    def test_change_entry(self):
 
1159
        txt = 'bzrlib.transform.change_entry was deprecated in version 0.90.'
 
1160
        self.callDeprecated([txt], change_entry, None, None, None, None, None,
 
1161
            None, None, None)
 
1162
 
 
1163
    def test_case_insensitive_clash(self):
 
1164
        self.requireFeature(CaseInsensitiveFilesystemFeature)
 
1165
        def tt_helper():
 
1166
            wt = self.make_branch_and_tree('.')
 
1167
            tt = TreeTransform(wt)  # TreeTransform obtains write lock
 
1168
            try:
 
1169
                tt.new_file('foo', tt.root, 'bar')
 
1170
                tt.new_file('Foo', tt.root, 'spam')
 
1171
                # Lie to tt that we've already resolved all conflicts.
 
1172
                tt.apply(no_conflicts=True)
 
1173
            except:
 
1174
                wt.unlock()
 
1175
                raise
 
1176
        err = self.assertRaises(errors.FileExists, tt_helper)
 
1177
        self.assertContainsRe(str(err),
 
1178
            "^File exists: .+/foo")
 
1179
 
 
1180
    def test_two_directories_clash(self):
 
1181
        def tt_helper():
 
1182
            wt = self.make_branch_and_tree('.')
 
1183
            tt = TreeTransform(wt)  # TreeTransform obtains write lock
 
1184
            try:
 
1185
                foo_1 = tt.new_directory('foo', tt.root)
 
1186
                tt.new_directory('bar', foo_1)
 
1187
                foo_2 = tt.new_directory('foo', tt.root)
 
1188
                tt.new_directory('baz', foo_2)
 
1189
                # Lie to tt that we've already resolved all conflicts.
 
1190
                tt.apply(no_conflicts=True)
 
1191
            except:
 
1192
                wt.unlock()
 
1193
                raise
 
1194
        err = self.assertRaises(errors.FileExists, tt_helper)
 
1195
        self.assertContainsRe(str(err),
 
1196
            "^File exists: .+/foo")
 
1197
 
 
1198
    def test_two_directories_clash_finalize(self):
 
1199
        def tt_helper():
 
1200
            wt = self.make_branch_and_tree('.')
 
1201
            tt = TreeTransform(wt)  # TreeTransform obtains write lock
 
1202
            try:
 
1203
                foo_1 = tt.new_directory('foo', tt.root)
 
1204
                tt.new_directory('bar', foo_1)
 
1205
                foo_2 = tt.new_directory('foo', tt.root)
 
1206
                tt.new_directory('baz', foo_2)
 
1207
                # Lie to tt that we've already resolved all conflicts.
 
1208
                tt.apply(no_conflicts=True)
 
1209
            except:
 
1210
                tt.finalize()
 
1211
                raise
 
1212
        err = self.assertRaises(errors.FileExists, tt_helper)
 
1213
        self.assertContainsRe(str(err),
 
1214
            "^File exists: .+/foo")
 
1215
 
 
1216
 
 
1217
class TransformGroup(object):
 
1218
 
 
1219
    def __init__(self, dirname, root_id):
 
1220
        self.name = dirname
 
1221
        os.mkdir(dirname)
 
1222
        self.wt = BzrDir.create_standalone_workingtree(dirname)
 
1223
        self.wt.set_root_id(root_id)
 
1224
        self.b = self.wt.branch
 
1225
        self.tt = TreeTransform(self.wt)
 
1226
        self.root = self.tt.trans_id_tree_file_id(self.wt.get_root_id())
 
1227
 
 
1228
 
 
1229
def conflict_text(tree, merge):
 
1230
    template = '%s TREE\n%s%s\n%s%s MERGE-SOURCE\n'
 
1231
    return template % ('<' * 7, tree, '=' * 7, merge, '>' * 7)
 
1232
 
 
1233
 
 
1234
class TestTransformMerge(TestCaseInTempDir):
 
1235
    def test_text_merge(self):
 
1236
        root_id = generate_ids.gen_root_id()
 
1237
        base = TransformGroup("base", root_id)
 
1238
        base.tt.new_file('a', base.root, 'a\nb\nc\nd\be\n', 'a')
 
1239
        base.tt.new_file('b', base.root, 'b1', 'b')
 
1240
        base.tt.new_file('c', base.root, 'c', 'c')
 
1241
        base.tt.new_file('d', base.root, 'd', 'd')
 
1242
        base.tt.new_file('e', base.root, 'e', 'e')
 
1243
        base.tt.new_file('f', base.root, 'f', 'f')
 
1244
        base.tt.new_directory('g', base.root, 'g')
 
1245
        base.tt.new_directory('h', base.root, 'h')
 
1246
        base.tt.apply()
 
1247
        other = TransformGroup("other", root_id)
 
1248
        other.tt.new_file('a', other.root, 'y\nb\nc\nd\be\n', 'a')
 
1249
        other.tt.new_file('b', other.root, 'b2', 'b')
 
1250
        other.tt.new_file('c', other.root, 'c2', 'c')
 
1251
        other.tt.new_file('d', other.root, 'd', 'd')
 
1252
        other.tt.new_file('e', other.root, 'e2', 'e')
 
1253
        other.tt.new_file('f', other.root, 'f', 'f')
 
1254
        other.tt.new_file('g', other.root, 'g', 'g')
 
1255
        other.tt.new_file('h', other.root, 'h\ni\nj\nk\n', 'h')
 
1256
        other.tt.new_file('i', other.root, 'h\ni\nj\nk\n', 'i')
 
1257
        other.tt.apply()
 
1258
        this = TransformGroup("this", root_id)
 
1259
        this.tt.new_file('a', this.root, 'a\nb\nc\nd\bz\n', 'a')
 
1260
        this.tt.new_file('b', this.root, 'b', 'b')
 
1261
        this.tt.new_file('c', this.root, 'c', 'c')
 
1262
        this.tt.new_file('d', this.root, 'd2', 'd')
 
1263
        this.tt.new_file('e', this.root, 'e2', 'e')
 
1264
        this.tt.new_file('f', this.root, 'f', 'f')
 
1265
        this.tt.new_file('g', this.root, 'g', 'g')
 
1266
        this.tt.new_file('h', this.root, '1\n2\n3\n4\n', 'h')
 
1267
        this.tt.new_file('i', this.root, '1\n2\n3\n4\n', 'i')
 
1268
        this.tt.apply()
 
1269
        Merge3Merger(this.wt, this.wt, base.wt, other.wt)
 
1270
 
 
1271
        # textual merge
 
1272
        self.assertEqual(this.wt.get_file('a').read(), 'y\nb\nc\nd\bz\n')
 
1273
        # three-way text conflict
 
1274
        self.assertEqual(this.wt.get_file('b').read(), 
 
1275
                         conflict_text('b', 'b2'))
 
1276
        # OTHER wins
 
1277
        self.assertEqual(this.wt.get_file('c').read(), 'c2')
 
1278
        # THIS wins
 
1279
        self.assertEqual(this.wt.get_file('d').read(), 'd2')
 
1280
        # Ambigious clean merge
 
1281
        self.assertEqual(this.wt.get_file('e').read(), 'e2')
 
1282
        # No change
 
1283
        self.assertEqual(this.wt.get_file('f').read(), 'f')
 
1284
        # Correct correct results when THIS == OTHER 
 
1285
        self.assertEqual(this.wt.get_file('g').read(), 'g')
 
1286
        # Text conflict when THIS & OTHER are text and BASE is dir
 
1287
        self.assertEqual(this.wt.get_file('h').read(), 
 
1288
                         conflict_text('1\n2\n3\n4\n', 'h\ni\nj\nk\n'))
 
1289
        self.assertEqual(this.wt.get_file_byname('h.THIS').read(),
 
1290
                         '1\n2\n3\n4\n')
 
1291
        self.assertEqual(this.wt.get_file_byname('h.OTHER').read(),
 
1292
                         'h\ni\nj\nk\n')
 
1293
        self.assertEqual(file_kind(this.wt.abspath('h.BASE')), 'directory')
 
1294
        self.assertEqual(this.wt.get_file('i').read(), 
 
1295
                         conflict_text('1\n2\n3\n4\n', 'h\ni\nj\nk\n'))
 
1296
        self.assertEqual(this.wt.get_file_byname('i.THIS').read(),
 
1297
                         '1\n2\n3\n4\n')
 
1298
        self.assertEqual(this.wt.get_file_byname('i.OTHER').read(),
 
1299
                         'h\ni\nj\nk\n')
 
1300
        self.assertEqual(os.path.exists(this.wt.abspath('i.BASE')), False)
 
1301
        modified = ['a', 'b', 'c', 'h', 'i']
 
1302
        merge_modified = this.wt.merge_modified()
 
1303
        self.assertSubset(merge_modified, modified)
 
1304
        self.assertEqual(len(merge_modified), len(modified))
 
1305
        file(this.wt.id2abspath('a'), 'wb').write('booga')
 
1306
        modified.pop(0)
 
1307
        merge_modified = this.wt.merge_modified()
 
1308
        self.assertSubset(merge_modified, modified)
 
1309
        self.assertEqual(len(merge_modified), len(modified))
 
1310
        this.wt.remove('b')
 
1311
        this.wt.revert()
 
1312
 
 
1313
    def test_file_merge(self):
 
1314
        self.requireFeature(SymlinkFeature)
 
1315
        root_id = generate_ids.gen_root_id()
 
1316
        base = TransformGroup("BASE", root_id)
 
1317
        this = TransformGroup("THIS", root_id)
 
1318
        other = TransformGroup("OTHER", root_id)
 
1319
        for tg in this, base, other:
 
1320
            tg.tt.new_directory('a', tg.root, 'a')
 
1321
            tg.tt.new_symlink('b', tg.root, 'b', 'b')
 
1322
            tg.tt.new_file('c', tg.root, 'c', 'c')
 
1323
            tg.tt.new_symlink('d', tg.root, tg.name, 'd')
 
1324
        targets = ((base, 'base-e', 'base-f', None, None), 
 
1325
                   (this, 'other-e', 'this-f', 'other-g', 'this-h'), 
 
1326
                   (other, 'other-e', None, 'other-g', 'other-h'))
 
1327
        for tg, e_target, f_target, g_target, h_target in targets:
 
1328
            for link, target in (('e', e_target), ('f', f_target), 
 
1329
                                 ('g', g_target), ('h', h_target)):
 
1330
                if target is not None:
 
1331
                    tg.tt.new_symlink(link, tg.root, target, link)
 
1332
 
 
1333
        for tg in this, base, other:
 
1334
            tg.tt.apply()
 
1335
        Merge3Merger(this.wt, this.wt, base.wt, other.wt)
 
1336
        self.assertIs(os.path.isdir(this.wt.abspath('a')), True)
 
1337
        self.assertIs(os.path.islink(this.wt.abspath('b')), True)
 
1338
        self.assertIs(os.path.isfile(this.wt.abspath('c')), True)
 
1339
        for suffix in ('THIS', 'BASE', 'OTHER'):
 
1340
            self.assertEqual(os.readlink(this.wt.abspath('d.'+suffix)), suffix)
 
1341
        self.assertIs(os.path.lexists(this.wt.abspath('d')), False)
 
1342
        self.assertEqual(this.wt.id2path('d'), 'd.OTHER')
 
1343
        self.assertEqual(this.wt.id2path('f'), 'f.THIS')
 
1344
        self.assertEqual(os.readlink(this.wt.abspath('e')), 'other-e')
 
1345
        self.assertIs(os.path.lexists(this.wt.abspath('e.THIS')), False)
 
1346
        self.assertIs(os.path.lexists(this.wt.abspath('e.OTHER')), False)
 
1347
        self.assertIs(os.path.lexists(this.wt.abspath('e.BASE')), False)
 
1348
        self.assertIs(os.path.lexists(this.wt.abspath('g')), True)
 
1349
        self.assertIs(os.path.lexists(this.wt.abspath('g.BASE')), False)
 
1350
        self.assertIs(os.path.lexists(this.wt.abspath('h')), False)
 
1351
        self.assertIs(os.path.lexists(this.wt.abspath('h.BASE')), False)
 
1352
        self.assertIs(os.path.lexists(this.wt.abspath('h.THIS')), True)
 
1353
        self.assertIs(os.path.lexists(this.wt.abspath('h.OTHER')), True)
 
1354
 
 
1355
    def test_filename_merge(self):
 
1356
        root_id = generate_ids.gen_root_id()
 
1357
        base = TransformGroup("BASE", root_id)
 
1358
        this = TransformGroup("THIS", root_id)
 
1359
        other = TransformGroup("OTHER", root_id)
 
1360
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a') 
 
1361
                                   for t in [base, this, other]]
 
1362
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b') 
 
1363
                                   for t in [base, this, other]]
 
1364
        base.tt.new_directory('c', base_a, 'c')
 
1365
        this.tt.new_directory('c1', this_a, 'c')
 
1366
        other.tt.new_directory('c', other_b, 'c')
 
1367
 
 
1368
        base.tt.new_directory('d', base_a, 'd')
 
1369
        this.tt.new_directory('d1', this_b, 'd')
 
1370
        other.tt.new_directory('d', other_a, 'd')
 
1371
 
 
1372
        base.tt.new_directory('e', base_a, 'e')
 
1373
        this.tt.new_directory('e', this_a, 'e')
 
1374
        other.tt.new_directory('e1', other_b, 'e')
 
1375
 
 
1376
        base.tt.new_directory('f', base_a, 'f')
 
1377
        this.tt.new_directory('f1', this_b, 'f')
 
1378
        other.tt.new_directory('f1', other_b, 'f')
 
1379
 
 
1380
        for tg in [this, base, other]:
 
1381
            tg.tt.apply()
 
1382
        Merge3Merger(this.wt, this.wt, base.wt, other.wt)
 
1383
        self.assertEqual(this.wt.id2path('c'), pathjoin('b/c1'))
 
1384
        self.assertEqual(this.wt.id2path('d'), pathjoin('b/d1'))
 
1385
        self.assertEqual(this.wt.id2path('e'), pathjoin('b/e1'))
 
1386
        self.assertEqual(this.wt.id2path('f'), pathjoin('b/f1'))
 
1387
 
 
1388
    def test_filename_merge_conflicts(self):
 
1389
        root_id = generate_ids.gen_root_id()
 
1390
        base = TransformGroup("BASE", root_id)
 
1391
        this = TransformGroup("THIS", root_id)
 
1392
        other = TransformGroup("OTHER", root_id)
 
1393
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a') 
 
1394
                                   for t in [base, this, other]]
 
1395
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b') 
 
1396
                                   for t in [base, this, other]]
 
1397
 
 
1398
        base.tt.new_file('g', base_a, 'g', 'g')
 
1399
        other.tt.new_file('g1', other_b, 'g1', 'g')
 
1400
 
 
1401
        base.tt.new_file('h', base_a, 'h', 'h')
 
1402
        this.tt.new_file('h1', this_b, 'h1', 'h')
 
1403
 
 
1404
        base.tt.new_file('i', base.root, 'i', 'i')
 
1405
        other.tt.new_directory('i1', this_b, 'i')
 
1406
 
 
1407
        for tg in [this, base, other]:
 
1408
            tg.tt.apply()
 
1409
        Merge3Merger(this.wt, this.wt, base.wt, other.wt)
 
1410
 
 
1411
        self.assertEqual(this.wt.id2path('g'), pathjoin('b/g1.OTHER'))
 
1412
        self.assertIs(os.path.lexists(this.wt.abspath('b/g1.BASE')), True)
 
1413
        self.assertIs(os.path.lexists(this.wt.abspath('b/g1.THIS')), False)
 
1414
        self.assertEqual(this.wt.id2path('h'), pathjoin('b/h1.THIS'))
 
1415
        self.assertIs(os.path.lexists(this.wt.abspath('b/h1.BASE')), True)
 
1416
        self.assertIs(os.path.lexists(this.wt.abspath('b/h1.OTHER')), False)
 
1417
        self.assertEqual(this.wt.id2path('i'), pathjoin('b/i1.OTHER'))
 
1418
 
 
1419
 
 
1420
class TestBuildTree(tests.TestCaseWithTransport):
 
1421
 
 
1422
    def test_build_tree_with_symlinks(self):
 
1423
        self.requireFeature(SymlinkFeature)
 
1424
        os.mkdir('a')
 
1425
        a = BzrDir.create_standalone_workingtree('a')
 
1426
        os.mkdir('a/foo')
 
1427
        file('a/foo/bar', 'wb').write('contents')
 
1428
        os.symlink('a/foo/bar', 'a/foo/baz')
 
1429
        a.add(['foo', 'foo/bar', 'foo/baz'])
 
1430
        a.commit('initial commit')
 
1431
        b = BzrDir.create_standalone_workingtree('b')
 
1432
        basis = a.basis_tree()
 
1433
        basis.lock_read()
 
1434
        self.addCleanup(basis.unlock)
 
1435
        build_tree(basis, b)
 
1436
        self.assertIs(os.path.isdir('b/foo'), True)
 
1437
        self.assertEqual(file('b/foo/bar', 'rb').read(), "contents")
 
1438
        self.assertEqual(os.readlink('b/foo/baz'), 'a/foo/bar')
 
1439
 
 
1440
    def test_build_with_references(self):
 
1441
        tree = self.make_branch_and_tree('source',
 
1442
            format='dirstate-with-subtree')
 
1443
        subtree = self.make_branch_and_tree('source/subtree',
 
1444
            format='dirstate-with-subtree')
 
1445
        tree.add_reference(subtree)
 
1446
        tree.commit('a revision')
 
1447
        tree.branch.create_checkout('target')
 
1448
        self.failUnlessExists('target')
 
1449
        self.failUnlessExists('target/subtree')
 
1450
 
 
1451
    def test_file_conflict_handling(self):
 
1452
        """Ensure that when building trees, conflict handling is done"""
 
1453
        source = self.make_branch_and_tree('source')
 
1454
        target = self.make_branch_and_tree('target')
 
1455
        self.build_tree(['source/file', 'target/file'])
 
1456
        source.add('file', 'new-file')
 
1457
        source.commit('added file')
 
1458
        build_tree(source.basis_tree(), target)
 
1459
        self.assertEqual([DuplicateEntry('Moved existing file to',
 
1460
                          'file.moved', 'file', None, 'new-file')],
 
1461
                         target.conflicts())
 
1462
        target2 = self.make_branch_and_tree('target2')
 
1463
        target_file = file('target2/file', 'wb')
 
1464
        try:
 
1465
            source_file = file('source/file', 'rb')
 
1466
            try:
 
1467
                target_file.write(source_file.read())
 
1468
            finally:
 
1469
                source_file.close()
 
1470
        finally:
 
1471
            target_file.close()
 
1472
        build_tree(source.basis_tree(), target2)
 
1473
        self.assertEqual([], target2.conflicts())
 
1474
 
 
1475
    def test_symlink_conflict_handling(self):
 
1476
        """Ensure that when building trees, conflict handling is done"""
 
1477
        self.requireFeature(SymlinkFeature)
 
1478
        source = self.make_branch_and_tree('source')
 
1479
        os.symlink('foo', 'source/symlink')
 
1480
        source.add('symlink', 'new-symlink')
 
1481
        source.commit('added file')
 
1482
        target = self.make_branch_and_tree('target')
 
1483
        os.symlink('bar', 'target/symlink')
 
1484
        build_tree(source.basis_tree(), target)
 
1485
        self.assertEqual([DuplicateEntry('Moved existing file to',
 
1486
            'symlink.moved', 'symlink', None, 'new-symlink')],
 
1487
            target.conflicts())
 
1488
        target = self.make_branch_and_tree('target2')
 
1489
        os.symlink('foo', 'target2/symlink')
 
1490
        build_tree(source.basis_tree(), target)
 
1491
        self.assertEqual([], target.conflicts())
 
1492
        
 
1493
    def test_directory_conflict_handling(self):
 
1494
        """Ensure that when building trees, conflict handling is done"""
 
1495
        source = self.make_branch_and_tree('source')
 
1496
        target = self.make_branch_and_tree('target')
 
1497
        self.build_tree(['source/dir1/', 'source/dir1/file', 'target/dir1/'])
 
1498
        source.add(['dir1', 'dir1/file'], ['new-dir1', 'new-file'])
 
1499
        source.commit('added file')
 
1500
        build_tree(source.basis_tree(), target)
 
1501
        self.assertEqual([], target.conflicts())
 
1502
        self.failUnlessExists('target/dir1/file')
 
1503
 
 
1504
        # Ensure contents are merged
 
1505
        target = self.make_branch_and_tree('target2')
 
1506
        self.build_tree(['target2/dir1/', 'target2/dir1/file2'])
 
1507
        build_tree(source.basis_tree(), target)
 
1508
        self.assertEqual([], target.conflicts())
 
1509
        self.failUnlessExists('target2/dir1/file2')
 
1510
        self.failUnlessExists('target2/dir1/file')
 
1511
 
 
1512
        # Ensure new contents are suppressed for existing branches
 
1513
        target = self.make_branch_and_tree('target3')
 
1514
        self.make_branch('target3/dir1')
 
1515
        self.build_tree(['target3/dir1/file2'])
 
1516
        build_tree(source.basis_tree(), target)
 
1517
        self.failIfExists('target3/dir1/file')
 
1518
        self.failUnlessExists('target3/dir1/file2')
 
1519
        self.failUnlessExists('target3/dir1.diverted/file')
 
1520
        self.assertEqual([DuplicateEntry('Diverted to',
 
1521
            'dir1.diverted', 'dir1', 'new-dir1', None)],
 
1522
            target.conflicts())
 
1523
 
 
1524
        target = self.make_branch_and_tree('target4')
 
1525
        self.build_tree(['target4/dir1/'])
 
1526
        self.make_branch('target4/dir1/file')
 
1527
        build_tree(source.basis_tree(), target)
 
1528
        self.failUnlessExists('target4/dir1/file')
 
1529
        self.assertEqual('directory', file_kind('target4/dir1/file'))
 
1530
        self.failUnlessExists('target4/dir1/file.diverted')
 
1531
        self.assertEqual([DuplicateEntry('Diverted to',
 
1532
            'dir1/file.diverted', 'dir1/file', 'new-file', None)],
 
1533
            target.conflicts())
 
1534
 
 
1535
    def test_mixed_conflict_handling(self):
 
1536
        """Ensure that when building trees, conflict handling is done"""
 
1537
        source = self.make_branch_and_tree('source')
 
1538
        target = self.make_branch_and_tree('target')
 
1539
        self.build_tree(['source/name', 'target/name/'])
 
1540
        source.add('name', 'new-name')
 
1541
        source.commit('added file')
 
1542
        build_tree(source.basis_tree(), target)
 
1543
        self.assertEqual([DuplicateEntry('Moved existing file to',
 
1544
            'name.moved', 'name', None, 'new-name')], target.conflicts())
 
1545
 
 
1546
    def test_raises_in_populated(self):
 
1547
        source = self.make_branch_and_tree('source')
 
1548
        self.build_tree(['source/name'])
 
1549
        source.add('name')
 
1550
        source.commit('added name')
 
1551
        target = self.make_branch_and_tree('target')
 
1552
        self.build_tree(['target/name'])
 
1553
        target.add('name')
 
1554
        self.assertRaises(errors.WorkingTreeAlreadyPopulated, 
 
1555
            build_tree, source.basis_tree(), target)
 
1556
 
 
1557
    def test_build_tree_rename_count(self):
 
1558
        source = self.make_branch_and_tree('source')
 
1559
        self.build_tree(['source/file1', 'source/dir1/'])
 
1560
        source.add(['file1', 'dir1'])
 
1561
        source.commit('add1')
 
1562
        target1 = self.make_branch_and_tree('target1')
 
1563
        transform_result = build_tree(source.basis_tree(), target1)
 
1564
        self.assertEqual(2, transform_result.rename_count)
 
1565
 
 
1566
        self.build_tree(['source/dir1/file2'])
 
1567
        source.add(['dir1/file2'])
 
1568
        source.commit('add3')
 
1569
        target2 = self.make_branch_and_tree('target2')
 
1570
        transform_result = build_tree(source.basis_tree(), target2)
 
1571
        # children of non-root directories should not be renamed
 
1572
        self.assertEqual(2, transform_result.rename_count)
 
1573
 
 
1574
    def test_build_tree_accelerator_tree(self):
 
1575
        source = self.make_branch_and_tree('source')
 
1576
        self.build_tree_contents([('source/file1', 'A')])
 
1577
        self.build_tree_contents([('source/file2', 'B')])
 
1578
        source.add(['file1', 'file2'], ['file1-id', 'file2-id'])
 
1579
        source.commit('commit files')
 
1580
        self.build_tree_contents([('source/file2', 'C')])
 
1581
        calls = []
 
1582
        real_source_get_file = source.get_file
 
1583
        def get_file(file_id, path=None):
 
1584
            calls.append(file_id)
 
1585
            return real_source_get_file(file_id, path)
 
1586
        source.get_file = get_file
 
1587
        source.lock_read()
 
1588
        self.addCleanup(source.unlock)
 
1589
        target = self.make_branch_and_tree('target')
 
1590
        revision_tree = source.basis_tree()
 
1591
        revision_tree.lock_read()
 
1592
        self.addCleanup(revision_tree.unlock)
 
1593
        build_tree(revision_tree, target, source)
 
1594
        self.assertEqual(['file1-id'], calls)
 
1595
        target.lock_read()
 
1596
        self.addCleanup(target.unlock)
 
1597
        self.assertEqual([], list(target._iter_changes(revision_tree)))
 
1598
 
 
1599
    def test_build_tree_accelerator_tree_missing_file(self):
 
1600
        source = self.make_branch_and_tree('source')
 
1601
        self.build_tree_contents([('source/file1', 'A')])
 
1602
        self.build_tree_contents([('source/file2', 'B')])
 
1603
        source.add(['file1', 'file2'])
 
1604
        source.commit('commit files')
 
1605
        os.unlink('source/file1')
 
1606
        source.remove(['file2'])
 
1607
        target = self.make_branch_and_tree('target')
 
1608
        revision_tree = source.basis_tree()
 
1609
        revision_tree.lock_read()
 
1610
        self.addCleanup(revision_tree.unlock)
 
1611
        build_tree(revision_tree, target, source)
 
1612
        target.lock_read()
 
1613
        self.addCleanup(target.unlock)
 
1614
        self.assertEqual([], list(target._iter_changes(revision_tree)))
 
1615
 
 
1616
    def test_build_tree_accelerator_wrong_kind(self):
 
1617
        self.requireFeature(SymlinkFeature)
 
1618
        source = self.make_branch_and_tree('source')
 
1619
        self.build_tree_contents([('source/file1', '')])
 
1620
        self.build_tree_contents([('source/file2', '')])
 
1621
        source.add(['file1', 'file2'], ['file1-id', 'file2-id'])
 
1622
        source.commit('commit files')
 
1623
        os.unlink('source/file2')
 
1624
        self.build_tree_contents([('source/file2/', 'C')])
 
1625
        os.unlink('source/file1')
 
1626
        os.symlink('file2', 'source/file1')
 
1627
        calls = []
 
1628
        real_source_get_file = source.get_file
 
1629
        def get_file(file_id, path=None):
 
1630
            calls.append(file_id)
 
1631
            return real_source_get_file(file_id, path)
 
1632
        source.get_file = get_file
 
1633
        source.lock_read()
 
1634
        self.addCleanup(source.unlock)
 
1635
        target = self.make_branch_and_tree('target')
 
1636
        revision_tree = source.basis_tree()
 
1637
        revision_tree.lock_read()
 
1638
        self.addCleanup(revision_tree.unlock)
 
1639
        build_tree(revision_tree, target, source)
 
1640
        self.assertEqual([], calls)
 
1641
        target.lock_read()
 
1642
        self.addCleanup(target.unlock)
 
1643
        self.assertEqual([], list(target._iter_changes(revision_tree)))
 
1644
 
 
1645
    def test_build_tree_accelerator_tree_moved(self):
 
1646
        source = self.make_branch_and_tree('source')
 
1647
        self.build_tree_contents([('source/file1', 'A')])
 
1648
        source.add(['file1'], ['file1-id'])
 
1649
        source.commit('commit files')
 
1650
        source.rename_one('file1', 'file2')
 
1651
        source.lock_read()
 
1652
        self.addCleanup(source.unlock)
 
1653
        target = self.make_branch_and_tree('target')
 
1654
        revision_tree = source.basis_tree()
 
1655
        revision_tree.lock_read()
 
1656
        self.addCleanup(revision_tree.unlock)
 
1657
        build_tree(revision_tree, target, source)
 
1658
        target.lock_read()
 
1659
        self.addCleanup(target.unlock)
 
1660
        self.assertEqual([], list(target._iter_changes(revision_tree)))
 
1661
 
 
1662
 
 
1663
class MockTransform(object):
 
1664
 
 
1665
    def has_named_child(self, by_parent, parent_id, name):
 
1666
        for child_id in by_parent[parent_id]:
 
1667
            if child_id == '0':
 
1668
                if name == "name~":
 
1669
                    return True
 
1670
            elif name == "name.~%s~" % child_id:
 
1671
                return True
 
1672
        return False
 
1673
 
 
1674
 
 
1675
class MockEntry(object):
 
1676
    def __init__(self):
 
1677
        object.__init__(self)
 
1678
        self.name = "name"
 
1679
 
 
1680
 
 
1681
class TestGetBackupName(TestCase):
 
1682
    def test_get_backup_name(self):
 
1683
        tt = MockTransform()
 
1684
        name = get_backup_name(MockEntry(), {'a':[]}, 'a', tt)
 
1685
        self.assertEqual(name, 'name.~1~')
 
1686
        name = get_backup_name(MockEntry(), {'a':['1']}, 'a', tt)
 
1687
        self.assertEqual(name, 'name.~2~')
 
1688
        name = get_backup_name(MockEntry(), {'a':['2']}, 'a', tt)
 
1689
        self.assertEqual(name, 'name.~1~')
 
1690
        name = get_backup_name(MockEntry(), {'a':['2'], 'b':[]}, 'b', tt)
 
1691
        self.assertEqual(name, 'name.~1~')
 
1692
        name = get_backup_name(MockEntry(), {'a':['1', '2', '3']}, 'a', tt)
 
1693
        self.assertEqual(name, 'name.~4~')
 
1694
 
 
1695
 
 
1696
class TestFileMover(tests.TestCaseWithTransport):
 
1697
 
 
1698
    def test_file_mover(self):
 
1699
        self.build_tree(['a/', 'a/b', 'c/', 'c/d'])
 
1700
        mover = _FileMover()
 
1701
        mover.rename('a', 'q')
 
1702
        self.failUnlessExists('q')
 
1703
        self.failIfExists('a')
 
1704
        self.failUnlessExists('q/b')
 
1705
        self.failUnlessExists('c')
 
1706
        self.failUnlessExists('c/d')
 
1707
 
 
1708
    def test_pre_delete_rollback(self):
 
1709
        self.build_tree(['a/'])
 
1710
        mover = _FileMover()
 
1711
        mover.pre_delete('a', 'q')
 
1712
        self.failUnlessExists('q')
 
1713
        self.failIfExists('a')
 
1714
        mover.rollback()
 
1715
        self.failIfExists('q')
 
1716
        self.failUnlessExists('a')
 
1717
 
 
1718
    def test_apply_deletions(self):
 
1719
        self.build_tree(['a/', 'b/'])
 
1720
        mover = _FileMover()
 
1721
        mover.pre_delete('a', 'q')
 
1722
        mover.pre_delete('b', 'r')
 
1723
        self.failUnlessExists('q')
 
1724
        self.failUnlessExists('r')
 
1725
        self.failIfExists('a')
 
1726
        self.failIfExists('b')
 
1727
        mover.apply_deletions()
 
1728
        self.failIfExists('q')
 
1729
        self.failIfExists('r')
 
1730
        self.failIfExists('a')
 
1731
        self.failIfExists('b')
 
1732
 
 
1733
    def test_file_mover_rollback(self):
 
1734
        self.build_tree(['a/', 'a/b', 'c/', 'c/d/', 'c/e/'])
 
1735
        mover = _FileMover()
 
1736
        mover.rename('c/d', 'c/f')
 
1737
        mover.rename('c/e', 'c/d')
 
1738
        try:
 
1739
            mover.rename('a', 'c')
 
1740
        except errors.FileExists, e:
 
1741
            mover.rollback()
 
1742
        self.failUnlessExists('a')
 
1743
        self.failUnlessExists('c/d')
 
1744
 
 
1745
 
 
1746
class Bogus(Exception):
 
1747
    pass
 
1748
 
 
1749
 
 
1750
class TestTransformRollback(tests.TestCaseWithTransport):
 
1751
 
 
1752
    class ExceptionFileMover(_FileMover):
 
1753
 
 
1754
        def __init__(self, bad_source=None, bad_target=None):
 
1755
            _FileMover.__init__(self)
 
1756
            self.bad_source = bad_source
 
1757
            self.bad_target = bad_target
 
1758
 
 
1759
        def rename(self, source, target):
 
1760
            if (self.bad_source is not None and
 
1761
                source.endswith(self.bad_source)):
 
1762
                raise Bogus
 
1763
            elif (self.bad_target is not None and
 
1764
                target.endswith(self.bad_target)):
 
1765
                raise Bogus
 
1766
            else:
 
1767
                _FileMover.rename(self, source, target)
 
1768
 
 
1769
    def test_rollback_rename(self):
 
1770
        tree = self.make_branch_and_tree('.')
 
1771
        self.build_tree(['a/', 'a/b'])
 
1772
        tt = TreeTransform(tree)
 
1773
        self.addCleanup(tt.finalize)
 
1774
        a_id = tt.trans_id_tree_path('a')
 
1775
        tt.adjust_path('c', tt.root, a_id)
 
1776
        tt.adjust_path('d', a_id, tt.trans_id_tree_path('a/b'))
 
1777
        self.assertRaises(Bogus, tt.apply,
 
1778
                          _mover=self.ExceptionFileMover(bad_source='a'))
 
1779
        self.failUnlessExists('a')
 
1780
        self.failUnlessExists('a/b')
 
1781
        tt.apply()
 
1782
        self.failUnlessExists('c')
 
1783
        self.failUnlessExists('c/d')
 
1784
 
 
1785
    def test_rollback_rename_into_place(self):
 
1786
        tree = self.make_branch_and_tree('.')
 
1787
        self.build_tree(['a/', 'a/b'])
 
1788
        tt = TreeTransform(tree)
 
1789
        self.addCleanup(tt.finalize)
 
1790
        a_id = tt.trans_id_tree_path('a')
 
1791
        tt.adjust_path('c', tt.root, a_id)
 
1792
        tt.adjust_path('d', a_id, tt.trans_id_tree_path('a/b'))
 
1793
        self.assertRaises(Bogus, tt.apply,
 
1794
                          _mover=self.ExceptionFileMover(bad_target='c/d'))
 
1795
        self.failUnlessExists('a')
 
1796
        self.failUnlessExists('a/b')
 
1797
        tt.apply()
 
1798
        self.failUnlessExists('c')
 
1799
        self.failUnlessExists('c/d')
 
1800
 
 
1801
    def test_rollback_deletion(self):
 
1802
        tree = self.make_branch_and_tree('.')
 
1803
        self.build_tree(['a/', 'a/b'])
 
1804
        tt = TreeTransform(tree)
 
1805
        self.addCleanup(tt.finalize)
 
1806
        a_id = tt.trans_id_tree_path('a')
 
1807
        tt.delete_contents(a_id)
 
1808
        tt.adjust_path('d', tt.root, tt.trans_id_tree_path('a/b'))
 
1809
        self.assertRaises(Bogus, tt.apply,
 
1810
                          _mover=self.ExceptionFileMover(bad_target='d'))
 
1811
        self.failUnlessExists('a')
 
1812
        self.failUnlessExists('a/b')
 
1813
 
 
1814
    def test_resolve_no_parent(self):
 
1815
        wt = self.make_branch_and_tree('.')
 
1816
        tt = TreeTransform(wt)
 
1817
        self.addCleanup(tt.finalize)
 
1818
        parent = tt.trans_id_file_id('parent-id')
 
1819
        tt.new_file('file', parent, 'Contents')
 
1820
        resolve_conflicts(tt)
 
1821
 
 
1822
 
 
1823
class TestTransformPreview(tests.TestCaseWithTransport):
 
1824
 
 
1825
    def create_tree(self):
 
1826
        tree = self.make_branch_and_tree('.')
 
1827
        self.build_tree_contents([('a', 'content 1')])
 
1828
        tree.add('a', 'a-id')
 
1829
        tree.commit('rev1', rev_id='rev1')
 
1830
        return tree.branch.repository.revision_tree('rev1')
 
1831
 
 
1832
    def get_empty_preview(self):
 
1833
        repository = self.make_repository('repo')
 
1834
        tree = repository.revision_tree(_mod_revision.NULL_REVISION)
 
1835
        return TransformPreview(tree)
 
1836
 
 
1837
    def test_transform_preview(self):
 
1838
        revision_tree = self.create_tree()
 
1839
        preview = TransformPreview(revision_tree)
 
1840
 
 
1841
    def test_transform_preview_tree(self):
 
1842
        revision_tree = self.create_tree()
 
1843
        preview = TransformPreview(revision_tree)
 
1844
        preview.get_preview_tree()
 
1845
 
 
1846
    def test_transform_new_file(self):
 
1847
        revision_tree = self.create_tree()
 
1848
        preview = TransformPreview(revision_tree)
 
1849
        preview.new_file('file2', preview.root, 'content B\n', 'file2-id')
 
1850
        preview_tree = preview.get_preview_tree()
 
1851
        self.assertEqual(preview_tree.kind('file2-id'), 'file')
 
1852
        self.assertEqual(
 
1853
            preview_tree.get_file('file2-id').read(), 'content B\n')
 
1854
 
 
1855
    def test_diff_preview_tree(self):
 
1856
        revision_tree = self.create_tree()
 
1857
        preview = TransformPreview(revision_tree)
 
1858
        preview.new_file('file2', preview.root, 'content B\n', 'file2-id')
 
1859
        preview_tree = preview.get_preview_tree()
 
1860
        out = StringIO()
 
1861
        show_diff_trees(revision_tree, preview_tree, out)
 
1862
        lines = out.getvalue().splitlines()
 
1863
        self.assertEqual(lines[0], "=== added file 'file2'")
 
1864
        # 3 lines of diff administrivia
 
1865
        self.assertEqual(lines[4], "+content B")
 
1866
 
 
1867
    def test_transform_conflicts(self):
 
1868
        revision_tree = self.create_tree()
 
1869
        preview = TransformPreview(revision_tree)
 
1870
        preview.new_file('a', preview.root, 'content 2')
 
1871
        resolve_conflicts(preview)
 
1872
        trans_id = preview.trans_id_file_id('a-id')
 
1873
        self.assertEqual('a.moved', preview.final_name(trans_id))
 
1874
 
 
1875
    def get_tree_and_preview_tree(self):
 
1876
        revision_tree = self.create_tree()
 
1877
        preview = TransformPreview(revision_tree)
 
1878
        a_trans_id = preview.trans_id_file_id('a-id')
 
1879
        preview.delete_contents(a_trans_id)
 
1880
        preview.create_file('b content', a_trans_id)
 
1881
        preview_tree = preview.get_preview_tree()
 
1882
        return revision_tree, preview_tree
 
1883
 
 
1884
    def test_iter_changes(self):
 
1885
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
 
1886
        root = revision_tree.inventory.root.file_id
 
1887
        self.assertEqual([('a-id', ('a', 'a'), True, (True, True),
 
1888
                          (root, root), ('a', 'a'), ('file', 'file'),
 
1889
                          (False, False))],
 
1890
                          list(preview_tree._iter_changes(revision_tree)))
 
1891
 
 
1892
    def test_wrong_tree_value_error(self):
 
1893
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
 
1894
        e = self.assertRaises(ValueError, preview_tree._iter_changes,
 
1895
                              preview_tree)
 
1896
        self.assertEqual('from_tree must be transform source tree.', str(e))
 
1897
 
 
1898
    def test_include_unchanged_value_error(self):
 
1899
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
 
1900
        e = self.assertRaises(ValueError, preview_tree._iter_changes,
 
1901
                              revision_tree, include_unchanged=True)
 
1902
        self.assertEqual('include_unchanged is not supported', str(e))
 
1903
 
 
1904
    def test_specific_files(self):
 
1905
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
 
1906
        e = self.assertRaises(ValueError, preview_tree._iter_changes,
 
1907
                              revision_tree, specific_files=['pete'])
 
1908
        self.assertEqual('specific_files is not supported', str(e))
 
1909
 
 
1910
    def test_want_unversioned_value_error(self):
 
1911
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
 
1912
        e = self.assertRaises(ValueError, preview_tree._iter_changes,
 
1913
                              revision_tree, want_unversioned=True)
 
1914
        self.assertEqual('want_unversioned is not supported', str(e))
 
1915
 
 
1916
    def test_ignore_extra_trees_no_specific_files(self):
 
1917
        # extra_trees is harmless without specific_files, so we'll silently
 
1918
        # accept it, even though we won't use it.
 
1919
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
 
1920
        preview_tree._iter_changes(revision_tree, extra_trees=[preview_tree])
 
1921
 
 
1922
    def test_ignore_require_versioned_no_specific_files(self):
 
1923
        # require_versioned is meaningless without specific_files.
 
1924
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
 
1925
        preview_tree._iter_changes(revision_tree, require_versioned=False)
 
1926
 
 
1927
    def test_ignore_pb(self):
 
1928
        # pb could be supported, but TT.iter_changes doesn't support it.
 
1929
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
 
1930
        preview_tree._iter_changes(revision_tree, pb=progress.DummyProgress())
 
1931
 
 
1932
    def test_kind(self):
 
1933
        revision_tree = self.create_tree()
 
1934
        preview = TransformPreview(revision_tree)
 
1935
        preview.new_file('file', preview.root, 'contents', 'file-id')
 
1936
        preview.new_directory('directory', preview.root, 'dir-id')
 
1937
        preview_tree = preview.get_preview_tree()
 
1938
        self.assertEqual('file', preview_tree.kind('file-id'))
 
1939
        self.assertEqual('directory', preview_tree.kind('dir-id'))
 
1940
 
 
1941
    def test_get_file_mtime(self):
 
1942
        preview = self.get_empty_preview()
 
1943
        file_trans_id = preview.new_file('file', preview.root, 'contents',
 
1944
                                         'file-id')
 
1945
        limbo_path = preview._limbo_name(file_trans_id)
 
1946
        preview_tree = preview.get_preview_tree()
 
1947
        self.assertEqual(os.stat(limbo_path).st_mtime,
 
1948
                         preview_tree.get_file_mtime('file-id'))
 
1949
 
 
1950
    def test_get_file(self):
 
1951
        preview = self.get_empty_preview()
 
1952
        preview.new_file('file', preview.root, 'contents', 'file-id')
 
1953
        preview_tree = preview.get_preview_tree()
 
1954
        tree_file = preview_tree.get_file('file-id')
 
1955
        try:
 
1956
            self.assertEqual('contents', tree_file.read())
 
1957
        finally:
 
1958
            tree_file.close()