~bzr-pqm/bzr/bzr.dev

4634.125.4 by John Arbash Meinel
Merge bzr/2.0@4725, resolve NEWS
1
# Copyright (C) 2006-2010 Canonical Ltd
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
2
#
1534.7.106 by Aaron Bentley
Cleaned up imports, added copyright statements
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.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
7
#
1534.7.106 by Aaron Bentley
Cleaned up imports, added copyright statements
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.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
12
#
1534.7.106 by Aaron Bentley
Cleaned up imports, added copyright statements
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
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1534.7.106 by Aaron Bentley
Cleaned up imports, added copyright statements
16
17
import os
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
18
from StringIO import StringIO
2027.1.1 by John Arbash Meinel
Fix bug #56549, and write a direct test that the right path is being statted
19
import sys
4934.1.1 by John Arbash Meinel
Basic implementation for windows and bug #488724.
20
import time
1558.1.3 by Aaron Bentley
Fixed deprecated op use in test suite
21
2027.1.1 by John Arbash Meinel
Fix bug #56549, and write a direct test that the right path is being statted
22
from bzrlib import (
2694.5.4 by Jelmer Vernooij
Move bzrlib.util.bencode to bzrlib._bencode_py.
23
    bencode,
2090.2.1 by Martin Pool
Fix some code which relies on assertions and breaks under python -O
24
    errors,
4826.1.5 by Andrew Bennetts
Add test that content filtered files are not hardlinked by build_tree.
25
    filters,
2116.4.1 by John Arbash Meinel
Update file and revision id generators.
26
    generate_ids,
3199.1.4 by Vincent Ladeuil
Fix 16 leaked tmp dirs. Probably indicates a lock handling problem with TransformPreview
27
    osutils,
3008.1.18 by Aaron Bentley
Get supported PreviewTree functionality under test
28
    revision as _mod_revision,
4826.1.5 by Andrew Bennetts
Add test that content filtered files are not hardlinked by build_tree.
29
    rules,
5409.1.11 by Vincent Ladeuil
Slightly cleanup bzrlib.transform and deprecate unused code.
30
    symbol_versioning,
2027.1.1 by John Arbash Meinel
Fix bug #56549, and write a direct test that the right path is being statted
31
    tests,
5409.1.17 by Vincent Ladeuil
Ensures we fallback to the default policy if a bogus one is specified.
32
    trace,
5409.1.7 by Vincent Ladeuil
First orphaning implementation (some tests lacking).
33
    transform,
2027.1.1 by John Arbash Meinel
Fix bug #56549, and write a direct test that the right path is being statted
34
    urlutils,
35
    )
1558.1.3 by Aaron Bentley
Fixed deprecated op use in test suite
36
from bzrlib.bzrdir import BzrDir
5162.4.1 by Aaron Bentley
TreeTransform supports normal commit parameters and includes branch nick.
37
from bzrlib.conflicts import (
38
    DeletingParent,
39
    DuplicateEntry,
40
    DuplicateID,
41
    MissingParent,
42
    NonDirectoryParent,
43
    ParentLoop,
44
    UnversionedParent,
45
)
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
46
from bzrlib.diff import show_diff_trees
5162.4.1 by Aaron Bentley
TreeTransform supports normal commit parameters and includes branch nick.
47
from bzrlib.errors import (
48
    DuplicateKey,
49
    ExistingLimbo,
50
    ExistingPendingDeletion,
51
    ImmortalLimbo,
52
    ImmortalPendingDeletion,
53
    LockError,
54
    MalformedTransform,
55
    NoSuchFile,
56
    ReusingTransform,
57
)
58
from bzrlib.osutils import (
59
    file_kind,
60
    pathjoin,
61
)
3363.17.1 by Aaron Bentley
Avoid inventory for merge and transform code
62
from bzrlib.merge import Merge3Merger, Merger
2949.5.1 by Alexander Belchenko
selftest: use SymlinkFeature instead of TestSkipped where appropriate
63
from bzrlib.tests import (
5050.44.1 by Vincent Ladeuil
One more fixed test related to bug #646133
64
    features,
3136.1.1 by Aaron Bentley
Add support for hardlinks to TreeTransform
65
    HardlinkFeature,
2949.5.1 by Alexander Belchenko
selftest: use SymlinkFeature instead of TestSkipped where appropriate
66
    SymlinkFeature,
67
    TestCase,
68
    TestCaseInTempDir,
69
    TestSkipped,
5162.4.1 by Aaron Bentley
TreeTransform supports normal commit parameters and includes branch nick.
70
)
71
from bzrlib.transform import (
72
    build_tree,
73
    create_from_tree,
74
    cook_conflicts,
75
    _FileMover,
76
    FinalPaths,
77
    get_backup_name,
78
    resolve_conflicts,
79
    resolve_checkout,
80
    ROOT_PARENT,
81
    TransformPreview,
82
    TreeTransform,
83
)
0.13.13 by Aaron Bentley
Add direct test of serialization records
84
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
85
2100.3.21 by Aaron Bentley
Work on checking out by-reference trees
86
class TestTreeTransform(tests.TestCaseWithTransport):
1740.2.4 by Aaron Bentley
Update transform tests and docs
87
1534.7.59 by Aaron Bentley
Simplified tests
88
    def setUp(self):
89
        super(TestTreeTransform, self).setUp()
2255.2.194 by Robert Collins
[BROKEN] Many updates to stop using experimental formats in tests.
90
        self.wt = self.make_branch_and_tree('.', format='dirstate-with-subtree')
1534.7.161 by Aaron Bentley
Used appropriate control_files
91
        os.chdir('..')
1534.7.59 by Aaron Bentley
Simplified tests
92
93
    def get_transform(self):
94
        transform = TreeTransform(self.wt)
3453.2.7 by Aaron Bentley
Remove test kipple
95
        self.addCleanup(transform.finalize)
1731.1.33 by Aaron Bentley
Revert no-special-root changes
96
        return transform, transform.root
1534.7.59 by Aaron Bentley
Simplified tests
97
1534.7.162 by Aaron Bentley
Handle failures creating/deleting the Limbo directory
98
    def test_existing_limbo(self):
99
        transform, root = self.get_transform()
2733.2.6 by Aaron Bentley
Make TreeTransform commits rollbackable
100
        limbo_name = transform._limbodir
101
        deletion_path = transform._deletiondir
1534.7.176 by abentley
Fixed up tests for Windows
102
        os.mkdir(pathjoin(limbo_name, 'hehe'))
1534.7.162 by Aaron Bentley
Handle failures creating/deleting the Limbo directory
103
        self.assertRaises(ImmortalLimbo, transform.apply)
104
        self.assertRaises(LockError, self.wt.unlock)
105
        self.assertRaises(ExistingLimbo, self.get_transform)
106
        self.assertRaises(LockError, self.wt.unlock)
1534.7.176 by abentley
Fixed up tests for Windows
107
        os.rmdir(pathjoin(limbo_name, 'hehe'))
1534.7.162 by Aaron Bentley
Handle failures creating/deleting the Limbo directory
108
        os.rmdir(limbo_name)
2733.2.6 by Aaron Bentley
Make TreeTransform commits rollbackable
109
        os.rmdir(deletion_path)
1534.7.162 by Aaron Bentley
Handle failures creating/deleting the Limbo directory
110
        transform, root = self.get_transform()
111
        transform.apply()
1534.7.59 by Aaron Bentley
Simplified tests
112
2733.2.11 by Aaron Bentley
Detect irregularities with the pending-deletion directory
113
    def test_existing_pending_deletion(self):
114
        transform, root = self.get_transform()
115
        deletion_path = self._limbodir = urlutils.local_path_from_url(
3407.2.8 by Martin Pool
Deprecate LockableFiles.controlfilename
116
            transform._tree._transport.abspath('pending-deletion'))
2733.2.11 by Aaron Bentley
Detect irregularities with the pending-deletion directory
117
        os.mkdir(pathjoin(deletion_path, 'blocking-directory'))
118
        self.assertRaises(ImmortalPendingDeletion, transform.apply)
119
        self.assertRaises(LockError, self.wt.unlock)
120
        self.assertRaises(ExistingPendingDeletion, self.get_transform)
121
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
122
    def test_build(self):
3034.2.1 by Aaron Bentley
Fix is_executable tests for win32
123
        transform, root = self.get_transform()
124
        self.wt.lock_tree_write()
125
        self.addCleanup(self.wt.unlock)
1534.7.59 by Aaron Bentley
Simplified tests
126
        self.assertIs(transform.get_tree_parent(root), ROOT_PARENT)
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
127
        imaginary_id = transform.trans_id_tree_path('imaginary')
1534.10.32 by Aaron Bentley
Test and fix case where name has trailing slash
128
        imaginary_id2 = transform.trans_id_tree_path('imaginary/')
129
        self.assertEqual(imaginary_id, imaginary_id2)
4597.9.7 by Vincent Ladeuil
transform.final_kind() now returns None instead of raising NoSuchFile.
130
        self.assertEqual(root, transform.get_tree_parent(imaginary_id))
131
        self.assertEqual('directory', transform.final_kind(root))
132
        self.assertEqual(self.wt.get_root_id(), transform.final_file_id(root))
1534.7.59 by Aaron Bentley
Simplified tests
133
        trans_id = transform.create_path('name', root)
134
        self.assertIs(transform.final_file_id(trans_id), None)
4597.9.7 by Vincent Ladeuil
transform.final_kind() now returns None instead of raising NoSuchFile.
135
        self.assertIs(None, transform.final_kind(trans_id))
1534.7.59 by Aaron Bentley
Simplified tests
136
        transform.create_file('contents', trans_id)
137
        transform.set_executability(True, trans_id)
138
        transform.version_file('my_pretties', trans_id)
139
        self.assertRaises(DuplicateKey, transform.version_file,
140
                          'my_pretties', trans_id)
141
        self.assertEqual(transform.final_file_id(trans_id), 'my_pretties')
142
        self.assertEqual(transform.final_parent(trans_id), root)
143
        self.assertIs(transform.final_parent(root), ROOT_PARENT)
144
        self.assertIs(transform.get_tree_parent(root), ROOT_PARENT)
145
        oz_id = transform.create_path('oz', root)
146
        transform.create_directory(oz_id)
147
        transform.version_file('ozzie', oz_id)
148
        trans_id2 = transform.create_path('name2', root)
149
        transform.create_file('contents', trans_id2)
150
        transform.set_executability(False, trans_id2)
151
        transform.version_file('my_pretties2', trans_id2)
1534.7.191 by Aaron Bentley
Got transform.apply to list modified paths
152
        modified_paths = transform.apply().modified_paths
1534.7.100 by Aaron Bentley
Fixed path-relative test cases
153
        self.assertEqual('contents', self.wt.get_file_byname('name').read())
1534.7.59 by Aaron Bentley
Simplified tests
154
        self.assertEqual(self.wt.path2id('name'), 'my_pretties')
155
        self.assertIs(self.wt.is_executable('my_pretties'), True)
156
        self.assertIs(self.wt.is_executable('my_pretties2'), False)
1534.7.100 by Aaron Bentley
Fixed path-relative test cases
157
        self.assertEqual('directory', file_kind(self.wt.abspath('oz')))
1534.7.191 by Aaron Bentley
Got transform.apply to list modified paths
158
        self.assertEqual(len(modified_paths), 3)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
159
        tree_mod_paths = [self.wt.id2abspath(f) for f in
1534.7.191 by Aaron Bentley
Got transform.apply to list modified paths
160
                          ('ozzie', 'my_pretties', 'my_pretties2')]
161
        self.assertSubset(tree_mod_paths, modified_paths)
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
162
        # is it safe to finalize repeatedly?
163
        transform.finalize()
1534.7.59 by Aaron Bentley
Simplified tests
164
        transform.finalize()
1534.7.2 by Aaron Bentley
Added convenience function
165
4934.1.1 by John Arbash Meinel
Basic implementation for windows and bug #488724.
166
    def test_create_files_same_timestamp(self):
167
        transform, root = self.get_transform()
168
        self.wt.lock_tree_write()
169
        self.addCleanup(self.wt.unlock)
170
        # Roll back the clock, so that we know everything is being set to the
171
        # exact time
4934.1.10 by John Arbash Meinel
Rework the tests a bit.
172
        transform._creation_mtime = creation_mtime = time.time() - 20.0
4934.1.1 by John Arbash Meinel
Basic implementation for windows and bug #488724.
173
        transform.create_file('content-one',
174
                              transform.create_path('one', root))
175
        time.sleep(1) # *ugly*
176
        transform.create_file('content-two',
177
                              transform.create_path('two', root))
178
        transform.apply()
179
        fo, st1 = self.wt.get_file_with_stat(None, path='one', filtered=False)
180
        fo.close()
181
        fo, st2 = self.wt.get_file_with_stat(None, path='two', filtered=False)
182
        fo.close()
4934.2.2 by Martin
Rearrange osutils.fset_mtime tests a little and make them robust on FAT filesystems
183
        # We only guarantee 2s resolution
4934.1.11 by John Arbash Meinel
2 => 2.0 because it looks better.
184
        self.assertTrue(abs(creation_mtime - st1.st_mtime) < 2.0,
4934.1.10 by John Arbash Meinel
Rework the tests a bit.
185
            "%s != %s within 2 seconds" % (creation_mtime, st1.st_mtime))
4934.1.6 by John Arbash Meinel
It turns out that with os/python/C buffering, we need to flush
186
        # But if we have more than that, all files should get the same result
4934.1.1 by John Arbash Meinel
Basic implementation for windows and bug #488724.
187
        self.assertEqual(st1.st_mtime, st2.st_mtime)
188
4634.122.2 by Aaron Bentley, John Arbash Meinel
Bring the fixup_new_roots code from the nested-trees code.
189
    def test_change_root_id(self):
190
        transform, root = self.get_transform()
191
        self.assertNotEqual('new-root-id', self.wt.get_root_id())
192
        transform.new_directory('', ROOT_PARENT, 'new-root-id')
193
        transform.delete_contents(root)
194
        transform.unversion_file(root)
195
        transform.fixup_new_roots()
196
        transform.apply()
197
        self.assertEqual('new-root-id', self.wt.get_root_id())
198
199
    def test_change_root_id_add_files(self):
200
        transform, root = self.get_transform()
201
        self.assertNotEqual('new-root-id', self.wt.get_root_id())
202
        new_trans_id = transform.new_directory('', ROOT_PARENT, 'new-root-id')
203
        transform.new_file('file', new_trans_id, ['new-contents\n'],
204
                           'new-file-id')
205
        transform.delete_contents(root)
206
        transform.unversion_file(root)
207
        transform.fixup_new_roots()
208
        transform.apply()
209
        self.assertEqual('new-root-id', self.wt.get_root_id())
210
        self.assertEqual('new-file-id', self.wt.path2id('file'))
211
        self.assertFileEqual('new-contents\n', self.wt.abspath('file'))
212
213
    def test_add_two_roots(self):
214
        transform, root = self.get_transform()
215
        new_trans_id = transform.new_directory('', ROOT_PARENT, 'new-root-id')
216
        new_trans_id = transform.new_directory('', ROOT_PARENT, 'alt-root-id')
217
        self.assertRaises(ValueError, transform.fixup_new_roots)
218
3136.1.1 by Aaron Bentley
Add support for hardlinks to TreeTransform
219
    def test_hardlink(self):
220
        self.requireFeature(HardlinkFeature)
221
        transform, root = self.get_transform()
222
        transform.new_file('file1', root, 'contents')
223
        transform.apply()
224
        target = self.make_branch_and_tree('target')
225
        target_transform = TreeTransform(target)
226
        trans_id = target_transform.create_path('file1', target_transform.root)
227
        target_transform.create_hardlink(self.wt.abspath('file1'), trans_id)
228
        target_transform.apply()
229
        self.failUnlessExists('target/file1')
230
        source_stat = os.stat(self.wt.abspath('file1'))
231
        target_stat = os.stat('target/file1')
232
        self.assertEqual(source_stat, target_stat)
233
1534.7.2 by Aaron Bentley
Added convenience function
234
    def test_convenience(self):
1534.7.59 by Aaron Bentley
Simplified tests
235
        transform, root = self.get_transform()
3034.2.1 by Aaron Bentley
Fix is_executable tests for win32
236
        self.wt.lock_tree_write()
237
        self.addCleanup(self.wt.unlock)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
238
        trans_id = transform.new_file('name', root, 'contents',
1534.7.59 by Aaron Bentley
Simplified tests
239
                                      'my_pretties', True)
240
        oz = transform.new_directory('oz', root, 'oz-id')
241
        dorothy = transform.new_directory('dorothy', oz, 'dorothy-id')
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
242
        toto = transform.new_file('toto', dorothy, 'toto-contents',
1534.7.59 by Aaron Bentley
Simplified tests
243
                                  'toto-id', False)
244
245
        self.assertEqual(len(transform.find_conflicts()), 0)
246
        transform.apply()
247
        self.assertRaises(ReusingTransform, transform.find_conflicts)
1534.7.100 by Aaron Bentley
Fixed path-relative test cases
248
        self.assertEqual('contents', file(self.wt.abspath('name')).read())
1534.7.59 by Aaron Bentley
Simplified tests
249
        self.assertEqual(self.wt.path2id('name'), 'my_pretties')
250
        self.assertIs(self.wt.is_executable('my_pretties'), True)
251
        self.assertEqual(self.wt.path2id('oz'), 'oz-id')
252
        self.assertEqual(self.wt.path2id('oz/dorothy'), 'dorothy-id')
253
        self.assertEqual(self.wt.path2id('oz/dorothy/toto'), 'toto-id')
254
2255.2.194 by Robert Collins
[BROKEN] Many updates to stop using experimental formats in tests.
255
        self.assertEqual('toto-contents',
1534.7.100 by Aaron Bentley
Fixed path-relative test cases
256
                         self.wt.get_file_byname('oz/dorothy/toto').read())
1534.7.59 by Aaron Bentley
Simplified tests
257
        self.assertIs(self.wt.is_executable('toto-id'), False)
1534.7.6 by Aaron Bentley
Added conflict handling
258
2100.3.21 by Aaron Bentley
Work on checking out by-reference trees
259
    def test_tree_reference(self):
260
        transform, root = self.get_transform()
261
        tree = transform._tree
262
        trans_id = transform.new_directory('reference', root, 'subtree-id')
263
        transform.set_tree_reference('subtree-revision', trans_id)
264
        transform.apply()
2255.2.194 by Robert Collins
[BROKEN] Many updates to stop using experimental formats in tests.
265
        tree.lock_read()
266
        self.addCleanup(tree.unlock)
267
        self.assertEqual('subtree-revision',
2100.3.21 by Aaron Bentley
Work on checking out by-reference trees
268
                         tree.inventory['subtree-id'].reference_revision)
269
1534.7.6 by Aaron Bentley
Added conflict handling
270
    def test_conflicts(self):
1534.7.59 by Aaron Bentley
Simplified tests
271
        transform, root = self.get_transform()
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
272
        trans_id = transform.new_file('name', root, 'contents',
1534.7.59 by Aaron Bentley
Simplified tests
273
                                      'my_pretties')
274
        self.assertEqual(len(transform.find_conflicts()), 0)
275
        trans_id2 = transform.new_file('name', root, 'Crontents', 'toto')
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
276
        self.assertEqual(transform.find_conflicts(),
1534.7.59 by Aaron Bentley
Simplified tests
277
                         [('duplicate', trans_id, trans_id2, 'name')])
278
        self.assertRaises(MalformedTransform, transform.apply)
279
        transform.adjust_path('name', trans_id, trans_id2)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
280
        self.assertEqual(transform.find_conflicts(),
1534.7.59 by Aaron Bentley
Simplified tests
281
                         [('non-directory parent', trans_id)])
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
282
        tinman_id = transform.trans_id_tree_path('tinman')
1534.7.59 by Aaron Bentley
Simplified tests
283
        transform.adjust_path('name', tinman_id, trans_id2)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
284
        self.assertEqual(transform.find_conflicts(),
285
                         [('unversioned parent', tinman_id),
1534.7.59 by Aaron Bentley
Simplified tests
286
                          ('missing parent', tinman_id)])
287
        lion_id = transform.create_path('lion', root)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
288
        self.assertEqual(transform.find_conflicts(),
289
                         [('unversioned parent', tinman_id),
1534.7.59 by Aaron Bentley
Simplified tests
290
                          ('missing parent', tinman_id)])
291
        transform.adjust_path('name', lion_id, trans_id2)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
292
        self.assertEqual(transform.find_conflicts(),
1534.7.59 by Aaron Bentley
Simplified tests
293
                         [('unversioned parent', lion_id),
294
                          ('missing parent', lion_id)])
295
        transform.version_file("Courage", lion_id)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
296
        self.assertEqual(transform.find_conflicts(),
297
                         [('missing parent', lion_id),
1534.7.59 by Aaron Bentley
Simplified tests
298
                          ('versioning no contents', lion_id)])
299
        transform.adjust_path('name2', root, trans_id2)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
300
        self.assertEqual(transform.find_conflicts(),
1534.7.59 by Aaron Bentley
Simplified tests
301
                         [('versioning no contents', lion_id)])
302
        transform.create_file('Contents, okay?', lion_id)
303
        transform.adjust_path('name2', trans_id2, trans_id2)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
304
        self.assertEqual(transform.find_conflicts(),
305
                         [('parent loop', trans_id2),
1534.7.59 by Aaron Bentley
Simplified tests
306
                          ('non-directory parent', trans_id2)])
307
        transform.adjust_path('name2', root, trans_id2)
308
        oz_id = transform.new_directory('oz', root)
309
        transform.set_executability(True, oz_id)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
310
        self.assertEqual(transform.find_conflicts(),
1534.7.59 by Aaron Bentley
Simplified tests
311
                         [('unversioned executability', oz_id)])
312
        transform.version_file('oz-id', oz_id)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
313
        self.assertEqual(transform.find_conflicts(),
1534.7.59 by Aaron Bentley
Simplified tests
314
                         [('non-file executability', oz_id)])
315
        transform.set_executability(None, oz_id)
1534.7.71 by abentley
All tests pass under Windows
316
        tip_id = transform.new_file('tip', oz_id, 'ozma', 'tip-id')
1534.7.59 by Aaron Bentley
Simplified tests
317
        transform.apply()
318
        self.assertEqual(self.wt.path2id('name'), 'my_pretties')
319
        self.assertEqual('contents', file(self.wt.abspath('name')).read())
320
        transform2, root = self.get_transform()
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
321
        oz_id = transform2.trans_id_tree_file_id('oz-id')
1534.7.59 by Aaron Bentley
Simplified tests
322
        newtip = transform2.new_file('tip', oz_id, 'other', 'tip-id')
323
        result = transform2.find_conflicts()
1534.7.135 by Aaron Bentley
Fixed deletion handling
324
        fp = FinalPaths(transform2)
1534.7.59 by Aaron Bentley
Simplified tests
325
        self.assert_('oz/tip' in transform2._tree_path_ids)
1534.7.176 by abentley
Fixed up tests for Windows
326
        self.assertEqual(fp.get_path(newtip), pathjoin('oz', 'tip'))
1534.7.59 by Aaron Bentley
Simplified tests
327
        self.assertEqual(len(result), 2)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
328
        self.assertEqual((result[0][0], result[0][1]),
1534.7.59 by Aaron Bentley
Simplified tests
329
                         ('duplicate', newtip))
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
330
        self.assertEqual((result[1][0], result[1][2]),
1534.7.59 by Aaron Bentley
Simplified tests
331
                         ('duplicate id', newtip))
1534.7.73 by Aaron Bentley
Changed model again. Now iterator is used immediately.
332
        transform2.finalize()
1534.7.59 by Aaron Bentley
Simplified tests
333
        transform3 = TreeTransform(self.wt)
334
        self.addCleanup(transform3.finalize)
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
335
        oz_id = transform3.trans_id_tree_file_id('oz-id')
1534.7.59 by Aaron Bentley
Simplified tests
336
        transform3.delete_contents(oz_id)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
337
        self.assertEqual(transform3.find_conflicts(),
1534.7.59 by Aaron Bentley
Simplified tests
338
                         [('missing parent', oz_id)])
1731.1.33 by Aaron Bentley
Revert no-special-root changes
339
        root_id = transform3.root
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
340
        tip_id = transform3.trans_id_tree_file_id('tip-id')
1534.7.59 by Aaron Bentley
Simplified tests
341
        transform3.adjust_path('tip', root_id, tip_id)
342
        transform3.apply()
1534.7.36 by Aaron Bentley
Added rename tests
343
3034.4.1 by Aaron Bentley
Start handling case-insensitivity
344
    def test_conflict_on_case_insensitive(self):
345
        tree = self.make_branch_and_tree('tree')
346
        # Don't try this at home, kids!
347
        # Force the tree to report that it is case sensitive, for conflict
348
        # resolution tests
349
        tree.case_sensitive = True
350
        transform = TreeTransform(tree)
351
        self.addCleanup(transform.finalize)
352
        transform.new_file('file', transform.root, 'content')
353
        transform.new_file('FiLe', transform.root, 'content')
354
        result = transform.find_conflicts()
355
        self.assertEqual([], result)
3008.1.15 by Aaron Bentley
Make case_sensitive an aspect of the transform, not the source tree
356
        transform.finalize()
3034.4.1 by Aaron Bentley
Start handling case-insensitivity
357
        # Force the tree to report that it is case insensitive, for conflict
358
        # generation tests
359
        tree.case_sensitive = False
3008.1.15 by Aaron Bentley
Make case_sensitive an aspect of the transform, not the source tree
360
        transform = TreeTransform(tree)
361
        self.addCleanup(transform.finalize)
362
        transform.new_file('file', transform.root, 'content')
363
        transform.new_file('FiLe', transform.root, 'content')
3034.4.1 by Aaron Bentley
Start handling case-insensitivity
364
        result = transform.find_conflicts()
365
        self.assertEqual([('duplicate', 'new-1', 'new-2', 'file')], result)
366
3034.4.7 by Aaron Bentley
Add test for filename conflicts with existing files
367
    def test_conflict_on_case_insensitive_existing(self):
368
        tree = self.make_branch_and_tree('tree')
369
        self.build_tree(['tree/FiLe'])
370
        # Don't try this at home, kids!
371
        # Force the tree to report that it is case sensitive, for conflict
372
        # resolution tests
373
        tree.case_sensitive = True
374
        transform = TreeTransform(tree)
375
        self.addCleanup(transform.finalize)
376
        transform.new_file('file', transform.root, 'content')
377
        result = transform.find_conflicts()
378
        self.assertEqual([], result)
3008.1.15 by Aaron Bentley
Make case_sensitive an aspect of the transform, not the source tree
379
        transform.finalize()
3034.4.7 by Aaron Bentley
Add test for filename conflicts with existing files
380
        # Force the tree to report that it is case insensitive, for conflict
381
        # generation tests
382
        tree.case_sensitive = False
3008.1.15 by Aaron Bentley
Make case_sensitive an aspect of the transform, not the source tree
383
        transform = TreeTransform(tree)
384
        self.addCleanup(transform.finalize)
385
        transform.new_file('file', transform.root, 'content')
3034.4.7 by Aaron Bentley
Add test for filename conflicts with existing files
386
        result = transform.find_conflicts()
387
        self.assertEqual([('duplicate', 'new-1', 'new-2', 'file')], result)
388
3034.4.1 by Aaron Bentley
Start handling case-insensitivity
389
    def test_resolve_case_insensitive_conflict(self):
390
        tree = self.make_branch_and_tree('tree')
391
        # Don't try this at home, kids!
392
        # Force the tree to report that it is case insensitive, for conflict
393
        # resolution tests
394
        tree.case_sensitive = False
395
        transform = TreeTransform(tree)
396
        self.addCleanup(transform.finalize)
397
        transform.new_file('file', transform.root, 'content')
398
        transform.new_file('FiLe', transform.root, 'content')
399
        resolve_conflicts(transform)
400
        transform.apply()
401
        self.failUnlessExists('tree/file')
402
        self.failUnlessExists('tree/FiLe.moved')
403
3034.4.2 by Aaron Bentley
Get conflict handling and case-insensitive tree creation under test
404
    def test_resolve_checkout_case_conflict(self):
3034.4.1 by Aaron Bentley
Start handling case-insensitivity
405
        tree = self.make_branch_and_tree('tree')
406
        # Don't try this at home, kids!
407
        # Force the tree to report that it is case insensitive, for conflict
408
        # resolution tests
409
        tree.case_sensitive = False
410
        transform = TreeTransform(tree)
411
        self.addCleanup(transform.finalize)
412
        transform.new_file('file', transform.root, 'content')
413
        transform.new_file('FiLe', transform.root, 'content')
3034.4.2 by Aaron Bentley
Get conflict handling and case-insensitive tree creation under test
414
        resolve_conflicts(transform,
415
                          pass_func=lambda t, c: resolve_checkout(t, c, []))
3034.4.1 by Aaron Bentley
Start handling case-insensitivity
416
        transform.apply()
417
        self.failUnlessExists('tree/file')
418
        self.failUnlessExists('tree/FiLe.moved')
419
3034.4.2 by Aaron Bentley
Get conflict handling and case-insensitive tree creation under test
420
    def test_apply_case_conflict(self):
3034.4.6 by Aaron Bentley
Update apply_case_conflict tests
421
        """Ensure that a transform with case conflicts can always be applied"""
3034.4.2 by Aaron Bentley
Get conflict handling and case-insensitive tree creation under test
422
        tree = self.make_branch_and_tree('tree')
423
        transform = TreeTransform(tree)
424
        self.addCleanup(transform.finalize)
425
        transform.new_file('file', transform.root, 'content')
426
        transform.new_file('FiLe', transform.root, 'content')
3034.4.6 by Aaron Bentley
Update apply_case_conflict tests
427
        dir = transform.new_directory('dir', transform.root)
428
        transform.new_file('dirfile', dir, 'content')
429
        transform.new_file('dirFiLe', dir, 'content')
430
        resolve_conflicts(transform)
3034.4.2 by Aaron Bentley
Get conflict handling and case-insensitive tree creation under test
431
        transform.apply()
3034.4.3 by Aaron Bentley
Add case-sensitivity handling to WorkingTree
432
        self.failUnlessExists('tree/file')
433
        if not os.path.exists('tree/FiLe.moved'):
434
            self.failUnlessExists('tree/FiLe')
3034.4.6 by Aaron Bentley
Update apply_case_conflict tests
435
        self.failUnlessExists('tree/dir/dirfile')
436
        if not os.path.exists('tree/dir/dirFiLe.moved'):
437
            self.failUnlessExists('tree/dir/dirFiLe')
3034.4.2 by Aaron Bentley
Get conflict handling and case-insensitive tree creation under test
438
3034.4.1 by Aaron Bentley
Start handling case-insensitivity
439
    def test_case_insensitive_limbo(self):
440
        tree = self.make_branch_and_tree('tree')
441
        # Don't try this at home, kids!
442
        # Force the tree to report that it is case insensitive
443
        tree.case_sensitive = False
444
        transform = TreeTransform(tree)
445
        self.addCleanup(transform.finalize)
446
        dir = transform.new_directory('dir', transform.root)
447
        first = transform.new_file('file', dir, 'content')
448
        second = transform.new_file('FiLe', dir, 'content')
449
        self.assertContainsRe(transform._limbo_name(first), 'new-1/file')
450
        self.assertNotContainsRe(transform._limbo_name(second), 'new-1/FiLe')
451
4634.78.1 by Aaron Bentley
adjust_path updatest limbo paths.
452
    def test_adjust_path_updates_child_limbo_names(self):
453
        tree = self.make_branch_and_tree('tree')
454
        transform = TreeTransform(tree)
455
        self.addCleanup(transform.finalize)
456
        foo_id = transform.new_directory('foo', transform.root)
457
        bar_id = transform.new_directory('bar', foo_id)
458
        baz_id = transform.new_directory('baz', bar_id)
459
        qux_id = transform.new_directory('qux', baz_id)
460
        transform.adjust_path('quxx', foo_id, bar_id)
461
        self.assertStartsWith(transform._limbo_name(qux_id),
462
                              transform._limbo_name(bar_id))
463
1558.7.11 by Aaron Bentley
Avoid spurious conflict on add/delete
464
    def test_add_del(self):
465
        start, root = self.get_transform()
466
        start.new_directory('a', root, 'a')
467
        start.apply()
468
        transform, root = self.get_transform()
469
        transform.delete_versioned(transform.trans_id_tree_file_id('a'))
470
        transform.new_directory('a', root, 'a')
471
        transform.apply()
472
1534.7.46 by Aaron Bentley
Ensured a conflict when parents of versioned files are unversioned
473
    def test_unversioning(self):
1534.7.59 by Aaron Bentley
Simplified tests
474
        create_tree, root = self.get_transform()
475
        parent_id = create_tree.new_directory('parent', root, 'parent-id')
476
        create_tree.new_file('child', parent_id, 'child', 'child-id')
477
        create_tree.apply()
478
        unversion = TreeTransform(self.wt)
479
        self.addCleanup(unversion.finalize)
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
480
        parent = unversion.trans_id_tree_path('parent')
1534.7.59 by Aaron Bentley
Simplified tests
481
        unversion.unversion_file(parent)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
482
        self.assertEqual(unversion.find_conflicts(),
1534.7.59 by Aaron Bentley
Simplified tests
483
                         [('unversioned parent', parent_id)])
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
484
        file_id = unversion.trans_id_tree_file_id('child-id')
1534.7.59 by Aaron Bentley
Simplified tests
485
        unversion.unversion_file(file_id)
486
        unversion.apply()
1534.7.46 by Aaron Bentley
Ensured a conflict when parents of versioned files are unversioned
487
1534.7.36 by Aaron Bentley
Added rename tests
488
    def test_name_invariants(self):
1534.7.59 by Aaron Bentley
Simplified tests
489
        create_tree, root = self.get_transform()
490
        # prepare tree
1731.1.33 by Aaron Bentley
Revert no-special-root changes
491
        root = create_tree.root
1534.7.59 by Aaron Bentley
Simplified tests
492
        create_tree.new_file('name1', root, 'hello1', 'name1')
493
        create_tree.new_file('name2', root, 'hello2', 'name2')
494
        ddir = create_tree.new_directory('dying_directory', root, 'ddir')
495
        create_tree.new_file('dying_file', ddir, 'goodbye1', 'dfile')
496
        create_tree.new_file('moving_file', ddir, 'later1', 'mfile')
497
        create_tree.new_file('moving_file2', root, 'later2', 'mfile2')
498
        create_tree.apply()
499
500
        mangle_tree,root = self.get_transform()
1731.1.33 by Aaron Bentley
Revert no-special-root changes
501
        root = mangle_tree.root
1534.7.59 by Aaron Bentley
Simplified tests
502
        #swap names
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
503
        name1 = mangle_tree.trans_id_tree_file_id('name1')
504
        name2 = mangle_tree.trans_id_tree_file_id('name2')
1534.7.59 by Aaron Bentley
Simplified tests
505
        mangle_tree.adjust_path('name2', root, name1)
506
        mangle_tree.adjust_path('name1', root, name2)
507
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
508
        #tests for deleting parent directories
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
509
        ddir = mangle_tree.trans_id_tree_file_id('ddir')
1534.7.59 by Aaron Bentley
Simplified tests
510
        mangle_tree.delete_contents(ddir)
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
511
        dfile = mangle_tree.trans_id_tree_file_id('dfile')
1534.7.59 by Aaron Bentley
Simplified tests
512
        mangle_tree.delete_versioned(dfile)
513
        mangle_tree.unversion_file(dfile)
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
514
        mfile = mangle_tree.trans_id_tree_file_id('mfile')
1534.7.59 by Aaron Bentley
Simplified tests
515
        mangle_tree.adjust_path('mfile', root, mfile)
516
517
        #tests for adding parent directories
518
        newdir = mangle_tree.new_directory('new_directory', root, 'newdir')
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
519
        mfile2 = mangle_tree.trans_id_tree_file_id('mfile2')
1534.7.59 by Aaron Bentley
Simplified tests
520
        mangle_tree.adjust_path('mfile2', newdir, mfile2)
521
        mangle_tree.new_file('newfile', newdir, 'hello3', 'dfile')
522
        self.assertEqual(mangle_tree.final_file_id(mfile2), 'mfile2')
523
        self.assertEqual(mangle_tree.final_parent(mfile2), newdir)
524
        self.assertEqual(mangle_tree.final_file_id(mfile2), 'mfile2')
525
        mangle_tree.apply()
526
        self.assertEqual(file(self.wt.abspath('name1')).read(), 'hello2')
527
        self.assertEqual(file(self.wt.abspath('name2')).read(), 'hello1')
1534.7.176 by abentley
Fixed up tests for Windows
528
        mfile2_path = self.wt.abspath(pathjoin('new_directory','mfile2'))
1534.7.41 by Aaron Bentley
Got inventory ID movement working
529
        self.assertEqual(mangle_tree.final_parent(mfile2), newdir)
1534.7.38 by Aaron Bentley
Tested adding paths
530
        self.assertEqual(file(mfile2_path).read(), 'later2')
1534.7.59 by Aaron Bentley
Simplified tests
531
        self.assertEqual(self.wt.id2path('mfile2'), 'new_directory/mfile2')
532
        self.assertEqual(self.wt.path2id('new_directory/mfile2'), 'mfile2')
1534.7.176 by abentley
Fixed up tests for Windows
533
        newfile_path = self.wt.abspath(pathjoin('new_directory','newfile'))
1534.7.38 by Aaron Bentley
Tested adding paths
534
        self.assertEqual(file(newfile_path).read(), 'hello3')
1534.7.59 by Aaron Bentley
Simplified tests
535
        self.assertEqual(self.wt.path2id('dying_directory'), 'ddir')
536
        self.assertIs(self.wt.path2id('dying_directory/dying_file'), None)
1534.7.176 by abentley
Fixed up tests for Windows
537
        mfile2_path = self.wt.abspath(pathjoin('new_directory','mfile2'))
1534.7.43 by abentley
Fixed some Windows bugs, introduced a conflicts bug
538
1534.7.150 by Aaron Bentley
Handled simultaneous renames of parent and child better
539
    def test_both_rename(self):
540
        create_tree,root = self.get_transform()
541
        newdir = create_tree.new_directory('selftest', root, 'selftest-id')
542
        create_tree.new_file('blackbox.py', newdir, 'hello1', 'blackbox-id')
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
543
        create_tree.apply()
1534.7.150 by Aaron Bentley
Handled simultaneous renames of parent and child better
544
        mangle_tree,root = self.get_transform()
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
545
        selftest = mangle_tree.trans_id_tree_file_id('selftest-id')
546
        blackbox = mangle_tree.trans_id_tree_file_id('blackbox-id')
1534.7.150 by Aaron Bentley
Handled simultaneous renames of parent and child better
547
        mangle_tree.adjust_path('test', root, selftest)
548
        mangle_tree.adjust_path('test_too_much', root, selftest)
549
        mangle_tree.set_executability(True, blackbox)
550
        mangle_tree.apply()
551
552
    def test_both_rename2(self):
553
        create_tree,root = self.get_transform()
554
        bzrlib = create_tree.new_directory('bzrlib', root, 'bzrlib-id')
555
        tests = create_tree.new_directory('tests', bzrlib, 'tests-id')
556
        blackbox = create_tree.new_directory('blackbox', tests, 'blackbox-id')
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
557
        create_tree.new_file('test_too_much.py', blackbox, 'hello1',
1534.7.150 by Aaron Bentley
Handled simultaneous renames of parent and child better
558
                             'test_too_much-id')
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
559
        create_tree.apply()
1534.7.150 by Aaron Bentley
Handled simultaneous renames of parent and child better
560
        mangle_tree,root = self.get_transform()
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
561
        bzrlib = mangle_tree.trans_id_tree_file_id('bzrlib-id')
562
        tests = mangle_tree.trans_id_tree_file_id('tests-id')
563
        test_too_much = mangle_tree.trans_id_tree_file_id('test_too_much-id')
1534.7.150 by Aaron Bentley
Handled simultaneous renames of parent and child better
564
        mangle_tree.adjust_path('selftest', bzrlib, tests)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
565
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much)
1534.7.150 by Aaron Bentley
Handled simultaneous renames of parent and child better
566
        mangle_tree.set_executability(True, test_too_much)
567
        mangle_tree.apply()
568
569
    def test_both_rename3(self):
570
        create_tree,root = self.get_transform()
571
        tests = create_tree.new_directory('tests', root, 'tests-id')
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
572
        create_tree.new_file('test_too_much.py', tests, 'hello1',
1534.7.150 by Aaron Bentley
Handled simultaneous renames of parent and child better
573
                             'test_too_much-id')
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
574
        create_tree.apply()
1534.7.150 by Aaron Bentley
Handled simultaneous renames of parent and child better
575
        mangle_tree,root = self.get_transform()
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
576
        tests = mangle_tree.trans_id_tree_file_id('tests-id')
577
        test_too_much = mangle_tree.trans_id_tree_file_id('test_too_much-id')
1534.7.150 by Aaron Bentley
Handled simultaneous renames of parent and child better
578
        mangle_tree.adjust_path('selftest', root, tests)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
579
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much)
1534.7.150 by Aaron Bentley
Handled simultaneous renames of parent and child better
580
        mangle_tree.set_executability(True, test_too_much)
581
        mangle_tree.apply()
582
1534.7.48 by Aaron Bentley
Ensured we can move/rename dangling inventory entries
583
    def test_move_dangling_ie(self):
1534.7.59 by Aaron Bentley
Simplified tests
584
        create_tree, root = self.get_transform()
585
        # prepare tree
1731.1.33 by Aaron Bentley
Revert no-special-root changes
586
        root = create_tree.root
1534.7.59 by Aaron Bentley
Simplified tests
587
        create_tree.new_file('name1', root, 'hello1', 'name1')
588
        create_tree.apply()
589
        delete_contents, root = self.get_transform()
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
590
        file = delete_contents.trans_id_tree_file_id('name1')
1534.7.59 by Aaron Bentley
Simplified tests
591
        delete_contents.delete_contents(file)
592
        delete_contents.apply()
593
        move_id, root = self.get_transform()
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
594
        name1 = move_id.trans_id_tree_file_id('name1')
1534.7.59 by Aaron Bentley
Simplified tests
595
        newdir = move_id.new_directory('dir', root, 'newdir')
596
        move_id.adjust_path('name2', newdir, name1)
597
        move_id.apply()
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
598
1534.7.50 by Aaron Bentley
Detect duplicate inventory ids
599
    def test_replace_dangling_ie(self):
1534.7.59 by Aaron Bentley
Simplified tests
600
        create_tree, root = self.get_transform()
601
        # prepare tree
1731.1.33 by Aaron Bentley
Revert no-special-root changes
602
        root = create_tree.root
1534.7.59 by Aaron Bentley
Simplified tests
603
        create_tree.new_file('name1', root, 'hello1', 'name1')
604
        create_tree.apply()
605
        delete_contents = TreeTransform(self.wt)
606
        self.addCleanup(delete_contents.finalize)
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
607
        file = delete_contents.trans_id_tree_file_id('name1')
1534.7.59 by Aaron Bentley
Simplified tests
608
        delete_contents.delete_contents(file)
609
        delete_contents.apply()
610
        delete_contents.finalize()
611
        replace = TreeTransform(self.wt)
612
        self.addCleanup(replace.finalize)
613
        name2 = replace.new_file('name2', root, 'hello2', 'name1')
614
        conflicts = replace.find_conflicts()
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
615
        name1 = replace.trans_id_tree_file_id('name1')
1534.7.59 by Aaron Bentley
Simplified tests
616
        self.assertEqual(conflicts, [('duplicate id', name1, name2)])
617
        resolve_conflicts(replace)
618
        replace.apply()
1534.7.48 by Aaron Bentley
Ensured we can move/rename dangling inventory entries
619
4241.14.17 by Vincent Ladeuil
Add more tests for unicode symlinks to test_transform.
620
    def _test_symlinks(self, link_name1,link_target1,
621
                       link_name2, link_target2):
622
623
        def ozpath(p): return 'oz/' + p
624
2949.5.1 by Alexander Belchenko
selftest: use SymlinkFeature instead of TestSkipped where appropriate
625
        self.requireFeature(SymlinkFeature)
4241.14.12 by Vincent Ladeuil
Far too many modifications for a single commit, need to restart.
626
        transform, root = self.get_transform()
1534.7.59 by Aaron Bentley
Simplified tests
627
        oz_id = transform.new_directory('oz', root, 'oz-id')
4241.14.17 by Vincent Ladeuil
Add more tests for unicode symlinks to test_transform.
628
        wizard = transform.new_symlink(link_name1, oz_id, link_target1,
1534.7.59 by Aaron Bentley
Simplified tests
629
                                       'wizard-id')
4241.14.17 by Vincent Ladeuil
Add more tests for unicode symlinks to test_transform.
630
        wiz_id = transform.create_path(link_name2, oz_id)
631
        transform.create_symlink(link_target2, wiz_id)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
632
        transform.version_file('wiz-id2', wiz_id)
1534.7.71 by abentley
All tests pass under Windows
633
        transform.set_executability(True, wiz_id)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
634
        self.assertEqual(transform.find_conflicts(),
1534.7.71 by abentley
All tests pass under Windows
635
                         [('non-file executability', wiz_id)])
636
        transform.set_executability(None, wiz_id)
1534.7.59 by Aaron Bentley
Simplified tests
637
        transform.apply()
4241.14.17 by Vincent Ladeuil
Add more tests for unicode symlinks to test_transform.
638
        self.assertEqual(self.wt.path2id(ozpath(link_name1)), 'wizard-id')
639
        self.assertEqual('symlink',
640
                         file_kind(self.wt.abspath(ozpath(link_name1))))
641
        self.assertEqual(link_target2,
642
                         osutils.readlink(self.wt.abspath(ozpath(link_name2))))
643
        self.assertEqual(link_target1,
644
                         osutils.readlink(self.wt.abspath(ozpath(link_name1))))
645
646
    def test_symlinks(self):
647
        self._test_symlinks('wizard', 'wizard-target',
648
                            'wizard2', 'behind_curtain')
649
650
    def test_symlinks_unicode(self):
651
        self.requireFeature(tests.UnicodeFilenameFeature)
652
        self._test_symlinks(u'\N{Euro Sign}wizard',
653
                            u'wizard-targ\N{Euro Sign}t',
654
                            u'\N{Euro Sign}wizard2',
655
                            u'b\N{Euro Sign}hind_curtain')
1534.7.60 by Aaron Bentley
Tested existing conflict resolution functionality
656
3006.2.2 by Alexander Belchenko
tests added.
657
    def test_unable_create_symlink(self):
658
        def tt_helper():
659
            wt = self.make_branch_and_tree('.')
660
            tt = TreeTransform(wt)  # TreeTransform obtains write lock
661
            try:
662
                tt.new_symlink('foo', tt.root, 'bar')
663
                tt.apply()
664
            finally:
665
                wt.unlock()
666
        os_symlink = getattr(os, 'symlink', None)
667
        os.symlink = None
668
        try:
669
            err = self.assertRaises(errors.UnableCreateSymlink, tt_helper)
670
            self.assertEquals(
671
                "Unable to create symlink 'foo' on this platform",
672
                str(err))
673
        finally:
674
            if os_symlink:
675
                os.symlink = os_symlink
676
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
677
    def get_conflicted(self):
1534.7.60 by Aaron Bentley
Tested existing conflict resolution functionality
678
        create,root = self.get_transform()
679
        create.new_file('dorothy', root, 'dorothy', 'dorothy-id')
1534.7.61 by Aaron Bentley
Handled parent loops, missing parents, unversioned parents
680
        oz = create.new_directory('oz', root, 'oz-id')
681
        create.new_directory('emeraldcity', oz, 'emerald-id')
1534.7.60 by Aaron Bentley
Tested existing conflict resolution functionality
682
        create.apply()
683
        conflicts,root = self.get_transform()
1534.7.65 by Aaron Bentley
Text cleaup/docs
684
        # set up duplicate entry, duplicate id
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
685
        new_dorothy = conflicts.new_file('dorothy', root, 'dorothy',
1534.7.60 by Aaron Bentley
Tested existing conflict resolution functionality
686
                                         'dorothy-id')
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
687
        old_dorothy = conflicts.trans_id_tree_file_id('dorothy-id')
688
        oz = conflicts.trans_id_tree_file_id('oz-id')
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
689
        # set up DeletedParent parent conflict
1534.7.65 by Aaron Bentley
Text cleaup/docs
690
        conflicts.delete_versioned(oz)
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
691
        emerald = conflicts.trans_id_tree_file_id('emerald-id')
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
692
        # set up MissingParent conflict
693
        munchkincity = conflicts.trans_id_file_id('munchkincity-id')
694
        conflicts.adjust_path('munchkincity', root, munchkincity)
695
        conflicts.new_directory('auntem', munchkincity, 'auntem-id')
1534.7.65 by Aaron Bentley
Text cleaup/docs
696
        # set up parent loop
1534.7.61 by Aaron Bentley
Handled parent loops, missing parents, unversioned parents
697
        conflicts.adjust_path('emeraldcity', emerald, emerald)
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
698
        return conflicts, emerald, oz, old_dorothy, new_dorothy
699
700
    def test_conflict_resolution(self):
701
        conflicts, emerald, oz, old_dorothy, new_dorothy =\
702
            self.get_conflicted()
1534.7.60 by Aaron Bentley
Tested existing conflict resolution functionality
703
        resolve_conflicts(conflicts)
704
        self.assertEqual(conflicts.final_name(old_dorothy), 'dorothy.moved')
705
        self.assertIs(conflicts.final_file_id(old_dorothy), None)
706
        self.assertEqual(conflicts.final_name(new_dorothy), 'dorothy')
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
707
        self.assertEqual(conflicts.final_file_id(new_dorothy), 'dorothy-id')
1534.7.64 by Aaron Bentley
Extra testing
708
        self.assertEqual(conflicts.final_parent(emerald), oz)
1534.7.63 by Aaron Bentley
Ensure transform can be applied after resolution
709
        conflicts.apply()
1534.7.62 by Aaron Bentley
Fixed moving versioned directories
710
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
711
    def test_cook_conflicts(self):
712
        tt, emerald, oz, old_dorothy, new_dorothy = self.get_conflicted()
713
        raw_conflicts = resolve_conflicts(tt)
714
        cooked_conflicts = cook_conflicts(raw_conflicts, tt)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
715
        duplicate = DuplicateEntry('Moved existing file to', 'dorothy.moved',
1534.10.20 by Aaron Bentley
Got all tests passing
716
                                   'dorothy', None, 'dorothy-id')
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
717
        self.assertEqual(cooked_conflicts[0], duplicate)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
718
        duplicate_id = DuplicateID('Unversioned existing file',
1534.10.20 by Aaron Bentley
Got all tests passing
719
                                   'dorothy.moved', 'dorothy', None,
720
                                   'dorothy-id')
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
721
        self.assertEqual(cooked_conflicts[1], duplicate_id)
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
722
        missing_parent = MissingParent('Created directory', 'munchkincity',
723
                                       'munchkincity-id')
724
        deleted_parent = DeletingParent('Not deleting', 'oz', 'oz-id')
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
725
        self.assertEqual(cooked_conflicts[2], missing_parent)
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
726
        unversioned_parent = UnversionedParent('Versioned directory',
727
                                               'munchkincity',
728
                                               'munchkincity-id')
729
        unversioned_parent2 = UnversionedParent('Versioned directory', 'oz',
1534.10.20 by Aaron Bentley
Got all tests passing
730
                                               'oz-id')
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
731
        self.assertEqual(cooked_conflicts[3], unversioned_parent)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
732
        parent_loop = ParentLoop('Cancelled move', 'oz/emeraldcity',
1534.10.20 by Aaron Bentley
Got all tests passing
733
                                 'oz/emeraldcity', 'emerald-id', 'emerald-id')
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
734
        self.assertEqual(cooked_conflicts[4], deleted_parent)
735
        self.assertEqual(cooked_conflicts[5], unversioned_parent2)
736
        self.assertEqual(cooked_conflicts[6], parent_loop)
737
        self.assertEqual(len(cooked_conflicts), 7)
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
738
        tt.finalize()
739
740
    def test_string_conflicts(self):
741
        tt, emerald, oz, old_dorothy, new_dorothy = self.get_conflicted()
742
        raw_conflicts = resolve_conflicts(tt)
743
        cooked_conflicts = cook_conflicts(raw_conflicts, tt)
744
        tt.finalize()
1534.10.24 by Aaron Bentley
Eliminated conflicts_to_strings, made remove_files a ConflictList member
745
        conflicts_s = [str(c) for c in cooked_conflicts]
1534.7.171 by Aaron Bentley
Implemented stringifying filesystem conflicts
746
        self.assertEqual(len(cooked_conflicts), len(conflicts_s))
747
        self.assertEqual(conflicts_s[0], 'Conflict adding file dorothy.  '
748
                                         'Moved existing file to '
749
                                         'dorothy.moved.')
750
        self.assertEqual(conflicts_s[1], 'Conflict adding id to dorothy.  '
751
                                         'Unversioned existing file '
752
                                         'dorothy.moved.')
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
753
        self.assertEqual(conflicts_s[2], 'Conflict adding files to'
754
                                         ' munchkincity.  Created directory.')
755
        self.assertEqual(conflicts_s[3], 'Conflict because munchkincity is not'
756
                                         ' versioned, but has versioned'
757
                                         ' children.  Versioned directory.')
1551.8.23 by Aaron Bentley
Tweaked conflict message to be more understandable
758
        self.assertEqualDiff(conflicts_s[4], "Conflict: can't delete oz because it"
759
                                         " is not empty.  Not deleting.")
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
760
        self.assertEqual(conflicts_s[5], 'Conflict because oz is not'
761
                                         ' versioned, but has versioned'
762
                                         ' children.  Versioned directory.')
763
        self.assertEqual(conflicts_s[6], 'Conflict moving oz/emeraldcity into'
4597.8.11 by Vincent Ladeuil
The ParentLoop.format has been updated, fix fallouts.
764
                                         ' oz/emeraldcity. Cancelled move.')
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
765
3144.4.2 by Aaron Bentley
Handle non-directory parent conflicts (abentley, #177390)
766
    def prepare_wrong_parent_kind(self):
767
        tt, root = self.get_transform()
768
        tt.new_file('parent', root, 'contents', 'parent-id')
769
        tt.apply()
770
        tt, root = self.get_transform()
771
        parent_id = tt.trans_id_file_id('parent-id')
772
        tt.new_file('child,', parent_id, 'contents2', 'file-id')
773
        return tt
774
3144.4.1 by Aaron Bentley
Handle trying to list parents of a non-directory
775
    def test_find_conflicts_wrong_parent_kind(self):
3144.4.2 by Aaron Bentley
Handle non-directory parent conflicts (abentley, #177390)
776
        tt = self.prepare_wrong_parent_kind()
3144.4.1 by Aaron Bentley
Handle trying to list parents of a non-directory
777
        tt.find_conflicts()
778
3144.4.2 by Aaron Bentley
Handle non-directory parent conflicts (abentley, #177390)
779
    def test_resolve_conflicts_wrong_existing_parent_kind(self):
780
        tt = self.prepare_wrong_parent_kind()
781
        raw_conflicts = resolve_conflicts(tt)
782
        self.assertEqual(set([('non-directory parent', 'Created directory',
783
                         'new-3')]), raw_conflicts)
784
        cooked_conflicts = cook_conflicts(raw_conflicts, tt)
785
        self.assertEqual([NonDirectoryParent('Created directory', 'parent.new',
786
        'parent-id')], cooked_conflicts)
787
        tt.apply()
788
        self.assertEqual(None, self.wt.path2id('parent'))
789
        self.assertEqual('parent-id', self.wt.path2id('parent.new'))
790
791
    def test_resolve_conflicts_wrong_new_parent_kind(self):
792
        tt, root = self.get_transform()
793
        parent_id = tt.new_directory('parent', root, 'parent-id')
794
        tt.new_file('child,', parent_id, 'contents2', 'file-id')
795
        tt.apply()
796
        tt, root = self.get_transform()
797
        parent_id = tt.trans_id_file_id('parent-id')
798
        tt.delete_contents(parent_id)
799
        tt.create_file('contents', parent_id)
800
        raw_conflicts = resolve_conflicts(tt)
801
        self.assertEqual(set([('non-directory parent', 'Created directory',
802
                         'new-3')]), raw_conflicts)
803
        tt.apply()
804
        self.assertEqual(None, self.wt.path2id('parent'))
805
        self.assertEqual('parent-id', self.wt.path2id('parent.new'))
806
807
    def test_resolve_conflicts_wrong_parent_kind_unversioned(self):
808
        tt, root = self.get_transform()
809
        parent_id = tt.new_directory('parent', root)
810
        tt.new_file('child,', parent_id, 'contents2')
811
        tt.apply()
812
        tt, root = self.get_transform()
813
        parent_id = tt.trans_id_tree_path('parent')
814
        tt.delete_contents(parent_id)
815
        tt.create_file('contents', parent_id)
816
        resolve_conflicts(tt)
817
        tt.apply()
818
        self.assertIs(None, self.wt.path2id('parent'))
819
        self.assertIs(None, self.wt.path2id('parent.new'))
820
5409.5.1 by Vincent Ladeuil
Tweak test_resolve_conflicts_missing_parent (renamed from test_resolve_no_parent), puttinh it in the right place.
821
    def test_resolve_conflicts_missing_parent(self):
822
        wt = self.make_branch_and_tree('.')
823
        tt = TreeTransform(wt)
824
        self.addCleanup(tt.finalize)
825
        parent = tt.trans_id_file_id('parent-id')
826
        tt.new_file('file', parent, 'Contents')
827
        raw_conflicts = resolve_conflicts(tt)
5409.6.2 by Vincent Ladeuil
Fix typo in the comment.
828
        # Since the directory doesn't exist it's seen as 'missing'.  So
829
        # 'resolve_conflicts' create a conflict asking for it to be created.
5409.5.1 by Vincent Ladeuil
Tweak test_resolve_conflicts_missing_parent (renamed from test_resolve_no_parent), puttinh it in the right place.
830
        self.assertLength(1, raw_conflicts)
831
        self.assertEqual(('missing parent', 'Created directory', 'new-1'),
832
                         raw_conflicts.pop())
5409.6.1 by Vincent Ladeuil
Clarify test intent and behaviour.
833
        # apply fail since the missing directory doesn't exist
834
        self.assertRaises(errors.NoFinalPath, tt.apply)
5409.5.1 by Vincent Ladeuil
Tweak test_resolve_conflicts_missing_parent (renamed from test_resolve_no_parent), puttinh it in the right place.
835
1534.7.62 by Aaron Bentley
Fixed moving versioned directories
836
    def test_moving_versioned_directories(self):
837
        create, root = self.get_transform()
838
        kansas = create.new_directory('kansas', root, 'kansas-id')
839
        create.new_directory('house', kansas, 'house-id')
840
        create.new_directory('oz', root, 'oz-id')
841
        create.apply()
842
        cyclone, root = self.get_transform()
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
843
        oz = cyclone.trans_id_tree_file_id('oz-id')
844
        house = cyclone.trans_id_tree_file_id('house-id')
1534.7.62 by Aaron Bentley
Fixed moving versioned directories
845
        cyclone.adjust_path('house', oz, house)
846
        cyclone.apply()
1534.7.66 by Aaron Bentley
Ensured we don't accidentally move the root directory
847
848
    def test_moving_root(self):
1534.7.68 by Aaron Bentley
Got semi-reasonable root directory renaming working
849
        create, root = self.get_transform()
850
        fun = create.new_directory('fun', root, 'fun-id')
851
        create.new_directory('sun', root, 'sun-id')
852
        create.new_directory('moon', root, 'moon')
853
        create.apply()
1534.7.66 by Aaron Bentley
Ensured we don't accidentally move the root directory
854
        transform, root = self.get_transform()
1534.7.68 by Aaron Bentley
Got semi-reasonable root directory renaming working
855
        transform.adjust_root_path('oldroot', fun)
4634.122.2 by Aaron Bentley, John Arbash Meinel
Bring the fixup_new_roots code from the nested-trees code.
856
        new_root = transform.trans_id_tree_path('')
1534.7.69 by Aaron Bentley
Got real root moves working
857
        transform.version_file('new-root', new_root)
1534.7.68 by Aaron Bentley
Got semi-reasonable root directory renaming working
858
        transform.apply()
1534.7.93 by Aaron Bentley
Added text merge test
859
1534.7.114 by Aaron Bentley
Added file renaming test case
860
    def test_renames(self):
861
        create, root = self.get_transform()
862
        old = create.new_directory('old-parent', root, 'old-id')
863
        intermediate = create.new_directory('intermediate', old, 'im-id')
864
        myfile = create.new_file('myfile', intermediate, 'myfile-text',
865
                                 'myfile-id')
866
        create.apply()
867
        rename, root = self.get_transform()
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
868
        old = rename.trans_id_file_id('old-id')
1534.7.114 by Aaron Bentley
Added file renaming test case
869
        rename.adjust_path('new', root, old)
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
870
        myfile = rename.trans_id_file_id('myfile-id')
1534.7.114 by Aaron Bentley
Added file renaming test case
871
        rename.set_executability(True, myfile)
872
        rename.apply()
873
5186.2.4 by Martin Pool
Add failing specific test for 491763
874
    def test_rename_fails(self):
5050.43.1 by Vincent Ladeuil
Fix one more failure when the test suite is run as root.
875
        self.requireFeature(features.not_running_as_root)
5186.2.4 by Martin Pool
Add failing specific test for 491763
876
        # see https://bugs.launchpad.net/bzr/+bug/491763
877
        create, root_id = self.get_transform()
878
        first_dir = create.new_directory('first-dir', root_id, 'first-id')
879
        myfile = create.new_file('myfile', root_id, 'myfile-text',
880
                                 'myfile-id')
881
        create.apply()
5050.15.2 by Martin
Correct bt.test_transform.TestTreeTransform.test_rename_fails on non-posix platforms
882
        if os.name == "posix" and sys.platform != "cygwin":
883
            # posix filesystems fail on renaming if the readonly bit is set
884
            osutils.make_readonly(self.wt.abspath('first-dir'))
885
        elif os.name == "nt":
886
            # windows filesystems fail on renaming open files
887
            self.addCleanup(file(self.wt.abspath('myfile')).close)
888
        else:
889
            self.skip("Don't know how to force a permissions error on rename")
5186.2.4 by Martin Pool
Add failing specific test for 491763
890
        # now transform to rename
891
        rename_transform, root_id = self.get_transform()
892
        file_trans_id = rename_transform.trans_id_file_id('myfile-id')
893
        dir_id = rename_transform.trans_id_file_id('first-id')
894
        rename_transform.adjust_path('newname', dir_id, file_trans_id)
5186.2.5 by Martin Pool
Raise a specific clearer error when a rename fails inside transform
895
        e = self.assertRaises(errors.TransformRenameFailed,
896
            rename_transform.apply)
5050.15.2 by Martin
Correct bt.test_transform.TestTreeTransform.test_rename_fails on non-posix platforms
897
        # On nix looks like: 
5186.2.5 by Martin Pool
Raise a specific clearer error when a rename fails inside transform
898
        # "Failed to rename .../work/.bzr/checkout/limbo/new-1
899
        # to .../first-dir/newname: [Errno 13] Permission denied"
5050.15.2 by Martin
Correct bt.test_transform.TestTreeTransform.test_rename_fails on non-posix platforms
900
        # On windows looks like:
901
        # "Failed to rename .../work/myfile to 
902
        # .../work/.bzr/checkout/limbo/new-1: [Errno 13] Permission denied"
903
        # The strerror will vary per OS and language so it's not checked here
5186.2.5 by Martin Pool
Raise a specific clearer error when a rename fails inside transform
904
        self.assertContainsRe(str(e),
5050.15.2 by Martin
Correct bt.test_transform.TestTreeTransform.test_rename_fails on non-posix platforms
905
            "Failed to rename .*(first-dir.newname:|myfile)")
5186.2.4 by Martin Pool
Add failing specific test for 491763
906
1740.2.4 by Aaron Bentley
Update transform tests and docs
907
    def test_set_executability_order(self):
908
        """Ensure that executability behaves the same, no matter what order.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
909
1740.2.4 by Aaron Bentley
Update transform tests and docs
910
        - create file and set executability simultaneously
911
        - create file and set executability afterward
912
        - unsetting the executability of a file whose executability has not been
913
        declared should throw an exception (this may happen when a
914
        merge attempts to create a file with a duplicate ID)
915
        """
916
        transform, root = self.get_transform()
917
        wt = transform._tree
3034.2.1 by Aaron Bentley
Fix is_executable tests for win32
918
        wt.lock_read()
919
        self.addCleanup(wt.unlock)
1740.2.4 by Aaron Bentley
Update transform tests and docs
920
        transform.new_file('set_on_creation', root, 'Set on creation', 'soc',
921
                           True)
2502.1.1 by Aaron Bentley
Ensure renames only root children are renamed when building trees
922
        sac = transform.new_file('set_after_creation', root,
923
                                 'Set after creation', 'sac')
1740.2.4 by Aaron Bentley
Update transform tests and docs
924
        transform.set_executability(True, sac)
2502.1.1 by Aaron Bentley
Ensure renames only root children are renamed when building trees
925
        uws = transform.new_file('unset_without_set', root, 'Unset badly',
926
                                 'uws')
1740.2.4 by Aaron Bentley
Update transform tests and docs
927
        self.assertRaises(KeyError, transform.set_executability, None, uws)
928
        transform.apply()
929
        self.assertTrue(wt.is_executable('soc'))
930
        self.assertTrue(wt.is_executable('sac'))
931
1534.12.2 by Aaron Bentley
Added test for preserving file mode
932
    def test_preserve_mode(self):
933
        """File mode is preserved when replacing content"""
934
        if sys.platform == 'win32':
935
            raise TestSkipped('chmod has no effect on win32')
936
        transform, root = self.get_transform()
937
        transform.new_file('file1', root, 'contents', 'file1-id', True)
938
        transform.apply()
3146.4.12 by Aaron Bentley
Add needed write lock to test
939
        self.wt.lock_write()
940
        self.addCleanup(self.wt.unlock)
1534.12.2 by Aaron Bentley
Added test for preserving file mode
941
        self.assertTrue(self.wt.is_executable('file1-id'))
942
        transform, root = self.get_transform()
943
        file1_id = transform.trans_id_tree_file_id('file1-id')
944
        transform.delete_contents(file1_id)
945
        transform.create_file('contents2', file1_id)
946
        transform.apply()
947
        self.assertTrue(self.wt.is_executable('file1-id'))
948
2027.1.1 by John Arbash Meinel
Fix bug #56549, and write a direct test that the right path is being statted
949
    def test__set_mode_stats_correctly(self):
950
        """_set_mode stats to determine file mode."""
951
        if sys.platform == 'win32':
952
            raise TestSkipped('chmod has no effect on win32')
953
954
        stat_paths = []
955
        real_stat = os.stat
956
        def instrumented_stat(path):
957
            stat_paths.append(path)
958
            return real_stat(path)
959
960
        transform, root = self.get_transform()
961
962
        bar1_id = transform.new_file('bar', root, 'bar contents 1\n',
963
                                     file_id='bar-id-1', executable=False)
964
        transform.apply()
965
966
        transform, root = self.get_transform()
967
        bar1_id = transform.trans_id_tree_path('bar')
968
        bar2_id = transform.trans_id_tree_path('bar2')
969
        try:
970
            os.stat = instrumented_stat
971
            transform.create_file('bar2 contents\n', bar2_id, mode_id=bar1_id)
972
        finally:
973
            os.stat = real_stat
974
            transform.finalize()
975
976
        bar1_abspath = self.wt.abspath('bar')
977
        self.assertEqual([bar1_abspath], stat_paths)
978
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
979
    def test_iter_changes(self):
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
980
        self.wt.set_root_id('eert_toor')
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
981
        transform, root = self.get_transform()
982
        transform.new_file('old', root, 'blah', 'id-1', True)
983
        transform.apply()
984
        transform, root = self.get_transform()
985
        try:
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
986
            self.assertEqual([], list(transform.iter_changes()))
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
987
            old = transform.trans_id_tree_file_id('id-1')
988
            transform.unversion_file(old)
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
989
            self.assertEqual([('id-1', ('old', None), False, (True, False),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
990
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
991
                (True, True))], list(transform.iter_changes()))
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
992
            transform.new_directory('new', root, 'id-1')
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
993
            self.assertEqual([('id-1', ('old', 'new'), True, (True, True),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
994
                ('eert_toor', 'eert_toor'), ('old', 'new'),
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
995
                ('file', 'directory'),
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
996
                (True, False))], list(transform.iter_changes()))
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
997
        finally:
998
            transform.finalize()
999
1000
    def test_iter_changes_new(self):
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
1001
        self.wt.set_root_id('eert_toor')
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
1002
        transform, root = self.get_transform()
1003
        transform.new_file('old', root, 'blah')
1004
        transform.apply()
1005
        transform, root = self.get_transform()
1006
        try:
1007
            old = transform.trans_id_tree_path('old')
1008
            transform.version_file('id-1', old)
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
1009
            self.assertEqual([('id-1', (None, 'old'), False, (False, True),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
1010
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
1011
                (False, False))], list(transform.iter_changes()))
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
1012
        finally:
1013
            transform.finalize()
1014
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
1015
    def test_iter_changes_modifications(self):
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
1016
        self.wt.set_root_id('eert_toor')
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
1017
        transform, root = self.get_transform()
1018
        transform.new_file('old', root, 'blah', 'id-1')
1019
        transform.new_file('new', root, 'blah')
1020
        transform.new_directory('subdir', root, 'subdir-id')
1021
        transform.apply()
1022
        transform, root = self.get_transform()
1023
        try:
1024
            old = transform.trans_id_tree_path('old')
1025
            subdir = transform.trans_id_tree_file_id('subdir-id')
1026
            new = transform.trans_id_tree_path('new')
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
1027
            self.assertEqual([], list(transform.iter_changes()))
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
1028
1029
            #content deletion
1030
            transform.delete_contents(old)
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
1031
            self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
1032
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', None),
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
1033
                (False, False))], list(transform.iter_changes()))
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
1034
1035
            #content change
1036
            transform.create_file('blah', old)
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
1037
            self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
1038
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
1039
                (False, False))], list(transform.iter_changes()))
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
1040
            transform.cancel_deletion(old)
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
1041
            self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
1042
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
1043
                (False, False))], list(transform.iter_changes()))
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
1044
            transform.cancel_creation(old)
1045
1046
            # move file_id to a different file
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
1047
            self.assertEqual([], list(transform.iter_changes()))
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
1048
            transform.unversion_file(old)
1049
            transform.version_file('id-1', new)
1050
            transform.adjust_path('old', root, new)
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
1051
            self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
1052
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
1053
                (False, False))], list(transform.iter_changes()))
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
1054
            transform.cancel_versioning(new)
1055
            transform._removed_id = set()
1056
1057
            #execute bit
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
1058
            self.assertEqual([], list(transform.iter_changes()))
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
1059
            transform.set_executability(True, old)
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
1060
            self.assertEqual([('id-1', ('old', 'old'), False, (True, True),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
1061
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
1062
                (False, True))], list(transform.iter_changes()))
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
1063
            transform.set_executability(None, old)
1064
1065
            # filename
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
1066
            self.assertEqual([], list(transform.iter_changes()))
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
1067
            transform.adjust_path('new', root, old)
1068
            transform._new_parent = {}
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
1069
            self.assertEqual([('id-1', ('old', 'new'), False, (True, True),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
1070
                ('eert_toor', 'eert_toor'), ('old', 'new'), ('file', 'file'),
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
1071
                (False, False))], list(transform.iter_changes()))
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
1072
            transform._new_name = {}
1073
1074
            # parent directory
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
1075
            self.assertEqual([], list(transform.iter_changes()))
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
1076
            transform.adjust_path('new', subdir, old)
1077
            transform._new_name = {}
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
1078
            self.assertEqual([('id-1', ('old', 'subdir/old'), False,
2255.2.180 by Martin Pool
merge dirstate
1079
                (True, True), ('eert_toor', 'subdir-id'), ('old', 'old'),
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
1080
                ('file', 'file'), (False, False))],
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
1081
                list(transform.iter_changes()))
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
1082
            transform._new_path = {}
1083
1084
        finally:
1085
            transform.finalize()
1086
1551.11.7 by Aaron Bentley
Stop modified flag bleeding into later changes
1087
    def test_iter_changes_modified_bleed(self):
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
1088
        self.wt.set_root_id('eert_toor')
1551.11.7 by Aaron Bentley
Stop modified flag bleeding into later changes
1089
        """Modified flag should not bleed from one change to another"""
1090
        # unfortunately, we have no guarantee that file1 (which is modified)
1091
        # will be applied before file2.  And if it's applied after file2, it
1092
        # obviously can't bleed into file2's change output.  But for now, it
1093
        # works.
1094
        transform, root = self.get_transform()
1095
        transform.new_file('file1', root, 'blah', 'id-1')
1096
        transform.new_file('file2', root, 'blah', 'id-2')
1097
        transform.apply()
1098
        transform, root = self.get_transform()
1099
        try:
1100
            transform.delete_contents(transform.trans_id_file_id('id-1'))
1101
            transform.set_executability(True,
1102
            transform.trans_id_file_id('id-2'))
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
1103
            self.assertEqual([('id-1', (u'file1', u'file1'), True, (True, True),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
1104
                ('eert_toor', 'eert_toor'), ('file1', u'file1'),
1551.11.7 by Aaron Bentley
Stop modified flag bleeding into later changes
1105
                ('file', None), (False, False)),
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
1106
                ('id-2', (u'file2', u'file2'), False, (True, True),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
1107
                ('eert_toor', 'eert_toor'), ('file2', u'file2'),
1551.11.7 by Aaron Bentley
Stop modified flag bleeding into later changes
1108
                ('file', 'file'), (False, True))],
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
1109
                list(transform.iter_changes()))
1551.11.7 by Aaron Bentley
Stop modified flag bleeding into later changes
1110
        finally:
1111
            transform.finalize()
1112
1551.10.37 by Aaron Bentley
recommit of TreeTransform._iter_changes fix with missing files
1113
    def test_iter_changes_move_missing(self):
1114
        """Test moving ids with no files around"""
1115
        self.wt.set_root_id('toor_eert')
1116
        # Need two steps because versioning a non-existant file is a conflict.
1117
        transform, root = self.get_transform()
1118
        transform.new_directory('floater', root, 'floater-id')
1119
        transform.apply()
1120
        transform, root = self.get_transform()
1121
        transform.delete_contents(transform.trans_id_tree_path('floater'))
1122
        transform.apply()
1123
        transform, root = self.get_transform()
1124
        floater = transform.trans_id_tree_path('floater')
1125
        try:
1126
            transform.adjust_path('flitter', root, floater)
1127
            self.assertEqual([('floater-id', ('floater', 'flitter'), False,
1128
            (True, True), ('toor_eert', 'toor_eert'), ('floater', 'flitter'),
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
1129
            (None, None), (False, False))], list(transform.iter_changes()))
1551.10.37 by Aaron Bentley
recommit of TreeTransform._iter_changes fix with missing files
1130
        finally:
1131
            transform.finalize()
1132
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
1133
    def test_iter_changes_pointless(self):
1134
        """Ensure that no-ops are not treated as modifications"""
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
1135
        self.wt.set_root_id('eert_toor')
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
1136
        transform, root = self.get_transform()
1137
        transform.new_file('old', root, 'blah', 'id-1')
1138
        transform.new_directory('subdir', root, 'subdir-id')
1139
        transform.apply()
1140
        transform, root = self.get_transform()
1141
        try:
1142
            old = transform.trans_id_tree_path('old')
1143
            subdir = transform.trans_id_tree_file_id('subdir-id')
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
1144
            self.assertEqual([], list(transform.iter_changes()))
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
1145
            transform.delete_contents(subdir)
1146
            transform.create_directory(subdir)
1147
            transform.set_executability(False, old)
1148
            transform.unversion_file(old)
1149
            transform.version_file('id-1', old)
1150
            transform.adjust_path('old', root, old)
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
1151
            self.assertEqual([], list(transform.iter_changes()))
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
1152
        finally:
1153
            transform.finalize()
1534.7.93 by Aaron Bentley
Added text merge test
1154
2502.1.1 by Aaron Bentley
Ensure renames only root children are renamed when building trees
1155
    def test_rename_count(self):
1156
        transform, root = self.get_transform()
2502.1.8 by Aaron Bentley
Updates from review comments
1157
        transform.new_file('name1', root, 'contents')
2502.1.1 by Aaron Bentley
Ensure renames only root children are renamed when building trees
1158
        self.assertEqual(transform.rename_count, 0)
1159
        transform.apply()
1160
        self.assertEqual(transform.rename_count, 1)
1161
        transform2, root = self.get_transform()
2502.1.8 by Aaron Bentley
Updates from review comments
1162
        transform2.adjust_path('name2', root,
1163
                               transform2.trans_id_tree_path('name1'))
2502.1.1 by Aaron Bentley
Ensure renames only root children are renamed when building trees
1164
        self.assertEqual(transform2.rename_count, 0)
1165
        transform2.apply()
1166
        self.assertEqual(transform2.rename_count, 2)
1167
2502.1.2 by Aaron Bentley
Make the limited-renames functionality safer in the general case
1168
    def test_change_parent(self):
2502.1.8 by Aaron Bentley
Updates from review comments
1169
        """Ensure that after we change a parent, the results are still right.
1170
1171
        Renames and parent changes on pending transforms can happen as part
1172
        of conflict resolution, and are explicitly permitted by the
1173
        TreeTransform API.
1174
1175
        This test ensures they work correctly with the rename-avoidance
1176
        optimization.
1177
        """
2502.1.2 by Aaron Bentley
Make the limited-renames functionality safer in the general case
1178
        transform, root = self.get_transform()
2502.1.8 by Aaron Bentley
Updates from review comments
1179
        parent1 = transform.new_directory('parent1', root)
1180
        child1 = transform.new_file('child1', parent1, 'contents')
1181
        parent2 = transform.new_directory('parent2', root)
1182
        transform.adjust_path('child1', parent2, child1)
2502.1.2 by Aaron Bentley
Make the limited-renames functionality safer in the general case
1183
        transform.apply()
2502.1.8 by Aaron Bentley
Updates from review comments
1184
        self.failIfExists(self.wt.abspath('parent1/child1'))
1185
        self.failUnlessExists(self.wt.abspath('parent2/child1'))
1186
        # rename limbo/new-1 => parent1, rename limbo/new-3 => parent2
1187
        # no rename for child1 (counting only renames during apply)
2502.1.2 by Aaron Bentley
Make the limited-renames functionality safer in the general case
1188
        self.failUnlessEqual(2, transform.rename_count)
1189
1190
    def test_cancel_parent(self):
1191
        """Cancelling a parent doesn't cause deletion of a non-empty directory
1192
1193
        This is like the test_change_parent, except that we cancel the parent
1194
        before adjusting the path.  The transform must detect that the
1195
        directory is non-empty, and move children to safe locations.
1196
        """
1197
        transform, root = self.get_transform()
2502.1.8 by Aaron Bentley
Updates from review comments
1198
        parent1 = transform.new_directory('parent1', root)
1199
        child1 = transform.new_file('child1', parent1, 'contents')
1200
        child2 = transform.new_file('child2', parent1, 'contents')
1201
        try:
1202
            transform.cancel_creation(parent1)
1203
        except OSError:
1204
            self.fail('Failed to move child1 before deleting parent1')
1205
        transform.cancel_creation(child2)
1206
        transform.create_directory(parent1)
1207
        try:
1208
            transform.cancel_creation(parent1)
1209
        # If the transform incorrectly believes that child2 is still in
1210
        # parent1's limbo directory, it will try to rename it and fail
1211
        # because was already moved by the first cancel_creation.
1212
        except OSError:
1213
            self.fail('Transform still thinks child2 is a child of parent1')
1214
        parent2 = transform.new_directory('parent2', root)
1215
        transform.adjust_path('child1', parent2, child1)
2502.1.2 by Aaron Bentley
Make the limited-renames functionality safer in the general case
1216
        transform.apply()
2502.1.8 by Aaron Bentley
Updates from review comments
1217
        self.failIfExists(self.wt.abspath('parent1'))
1218
        self.failUnlessExists(self.wt.abspath('parent2/child1'))
1219
        # rename limbo/new-3 => parent2, rename limbo/new-2 => child1
2502.1.2 by Aaron Bentley
Make the limited-renames functionality safer in the general case
1220
        self.failUnlessEqual(2, transform.rename_count)
1221
1222
    def test_adjust_and_cancel(self):
2502.1.8 by Aaron Bentley
Updates from review comments
1223
        """Make sure adjust_path keeps track of limbo children properly"""
2502.1.2 by Aaron Bentley
Make the limited-renames functionality safer in the general case
1224
        transform, root = self.get_transform()
2502.1.8 by Aaron Bentley
Updates from review comments
1225
        parent1 = transform.new_directory('parent1', root)
1226
        child1 = transform.new_file('child1', parent1, 'contents')
1227
        parent2 = transform.new_directory('parent2', root)
1228
        transform.adjust_path('child1', parent2, child1)
1229
        transform.cancel_creation(child1)
2502.1.2 by Aaron Bentley
Make the limited-renames functionality safer in the general case
1230
        try:
2502.1.8 by Aaron Bentley
Updates from review comments
1231
            transform.cancel_creation(parent1)
1232
        # if the transform thinks child1 is still in parent1's limbo
1233
        # directory, it will attempt to move it and fail.
2502.1.2 by Aaron Bentley
Make the limited-renames functionality safer in the general case
1234
        except OSError:
2502.1.8 by Aaron Bentley
Updates from review comments
1235
            self.fail('Transform still thinks child1 is a child of parent1')
2502.1.2 by Aaron Bentley
Make the limited-renames functionality safer in the general case
1236
        transform.finalize()
1237
2502.1.3 by Aaron Bentley
Don't cause errors when creating contents for trans_ids with no parent/name
1238
    def test_noname_contents(self):
2502.1.8 by Aaron Bentley
Updates from review comments
1239
        """TreeTransform should permit deferring naming files."""
2502.1.3 by Aaron Bentley
Don't cause errors when creating contents for trans_ids with no parent/name
1240
        transform, root = self.get_transform()
2502.1.8 by Aaron Bentley
Updates from review comments
1241
        parent = transform.trans_id_file_id('parent-id')
2502.1.3 by Aaron Bentley
Don't cause errors when creating contents for trans_ids with no parent/name
1242
        try:
2502.1.8 by Aaron Bentley
Updates from review comments
1243
            transform.create_directory(parent)
2502.1.3 by Aaron Bentley
Don't cause errors when creating contents for trans_ids with no parent/name
1244
        except KeyError:
1245
            self.fail("Can't handle contents with no name")
1246
        transform.finalize()
1247
2502.1.9 by Aaron Bentley
Add additional test for no-name contents
1248
    def test_noname_contents_nested(self):
1249
        """TreeTransform should permit deferring naming files."""
1250
        transform, root = self.get_transform()
1251
        parent = transform.trans_id_file_id('parent-id')
1252
        try:
1253
            transform.create_directory(parent)
1254
        except KeyError:
1255
            self.fail("Can't handle contents with no name")
1256
        child = transform.new_directory('child', parent)
1257
        transform.adjust_path('parent', root, parent)
1258
        transform.apply()
1259
        self.failUnlessExists(self.wt.abspath('parent/child'))
1260
        self.assertEqual(1, transform.rename_count)
1261
2502.1.4 by Aaron Bentley
Ensure we only reuse limbo names appropriately
1262
    def test_reuse_name(self):
1263
        """Avoid reusing the same limbo name for different files"""
1264
        transform, root = self.get_transform()
2502.1.8 by Aaron Bentley
Updates from review comments
1265
        parent = transform.new_directory('parent', root)
1266
        child1 = transform.new_directory('child', parent)
2502.1.4 by Aaron Bentley
Ensure we only reuse limbo names appropriately
1267
        try:
2502.1.8 by Aaron Bentley
Updates from review comments
1268
            child2 = transform.new_directory('child', parent)
2502.1.4 by Aaron Bentley
Ensure we only reuse limbo names appropriately
1269
        except OSError:
1270
            self.fail('Tranform tried to use the same limbo name twice')
2502.1.8 by Aaron Bentley
Updates from review comments
1271
        transform.adjust_path('child2', parent, child2)
2502.1.4 by Aaron Bentley
Ensure we only reuse limbo names appropriately
1272
        transform.apply()
2502.1.8 by Aaron Bentley
Updates from review comments
1273
        # limbo/new-1 => parent, limbo/new-3 => parent/child2
1274
        # child2 is put into top-level limbo because child1 has already
1275
        # claimed the direct limbo path when child2 is created.  There is no
1276
        # advantage in renaming files once they're in top-level limbo, except
1277
        # as part of apply.
2502.1.4 by Aaron Bentley
Ensure we only reuse limbo names appropriately
1278
        self.assertEqual(2, transform.rename_count)
1279
1280
    def test_reuse_when_first_moved(self):
1281
        """Don't avoid direct paths when it is safe to use them"""
1282
        transform, root = self.get_transform()
2502.1.8 by Aaron Bentley
Updates from review comments
1283
        parent = transform.new_directory('parent', root)
1284
        child1 = transform.new_directory('child', parent)
1285
        transform.adjust_path('child1', parent, child1)
1286
        child2 = transform.new_directory('child', parent)
2502.1.4 by Aaron Bentley
Ensure we only reuse limbo names appropriately
1287
        transform.apply()
2502.1.8 by Aaron Bentley
Updates from review comments
1288
        # limbo/new-1 => parent
2502.1.4 by Aaron Bentley
Ensure we only reuse limbo names appropriately
1289
        self.assertEqual(1, transform.rename_count)
1290
1291
    def test_reuse_after_cancel(self):
1292
        """Don't avoid direct paths when it is safe to use them"""
1293
        transform, root = self.get_transform()
2502.1.8 by Aaron Bentley
Updates from review comments
1294
        parent2 = transform.new_directory('parent2', root)
1295
        child1 = transform.new_directory('child1', parent2)
1296
        transform.cancel_creation(parent2)
1297
        transform.create_directory(parent2)
1298
        child2 = transform.new_directory('child1', parent2)
1299
        transform.adjust_path('child2', parent2, child1)
2502.1.4 by Aaron Bentley
Ensure we only reuse limbo names appropriately
1300
        transform.apply()
2502.1.8 by Aaron Bentley
Updates from review comments
1301
        # limbo/new-1 => parent2, limbo/new-2 => parent2/child1
2502.1.4 by Aaron Bentley
Ensure we only reuse limbo names appropriately
1302
        self.assertEqual(2, transform.rename_count)
1303
2502.1.7 by Aaron Bentley
Fix finalize deletion ordering
1304
    def test_finalize_order(self):
2502.1.8 by Aaron Bentley
Updates from review comments
1305
        """Finalize must be done in child-to-parent order"""
2502.1.7 by Aaron Bentley
Fix finalize deletion ordering
1306
        transform, root = self.get_transform()
2502.1.8 by Aaron Bentley
Updates from review comments
1307
        parent = transform.new_directory('parent', root)
1308
        child = transform.new_directory('child', parent)
2502.1.7 by Aaron Bentley
Fix finalize deletion ordering
1309
        try:
1310
            transform.finalize()
1311
        except OSError:
2502.1.8 by Aaron Bentley
Updates from review comments
1312
            self.fail('Tried to remove parent before child1')
2502.1.7 by Aaron Bentley
Fix finalize deletion ordering
1313
2502.1.13 by Aaron Bentley
Updates from review
1314
    def test_cancel_with_cancelled_child_should_succeed(self):
2502.1.12 by Aaron Bentley
Avoid renaming children with no content
1315
        transform, root = self.get_transform()
1316
        parent = transform.new_directory('parent', root)
1317
        child = transform.new_directory('child', parent)
1318
        transform.cancel_creation(child)
2502.1.13 by Aaron Bentley
Updates from review
1319
        transform.cancel_creation(parent)
2502.1.12 by Aaron Bentley
Avoid renaming children with no content
1320
        transform.finalize()
1321
3638.3.15 by Vincent Ladeuil
Fix test_case_insensitive_clash to pass on all platforms (renamed too).
1322
    def test_rollback_on_directory_clash(self):
3063.1.1 by Alexander Belchenko
Catch OSError 17 (file exists) in final phase of tree transform and show filename to user (#111758).
1323
        def tt_helper():
3638.3.17 by Vincent Ladeuil
Fixed as per Aaron's review.
1324
            wt = self.make_branch_and_tree('.')
3063.1.1 by Alexander Belchenko
Catch OSError 17 (file exists) in final phase of tree transform and show filename to user (#111758).
1325
            tt = TreeTransform(wt)  # TreeTransform obtains write lock
1326
            try:
3638.3.15 by Vincent Ladeuil
Fix test_case_insensitive_clash to pass on all platforms (renamed too).
1327
                foo = tt.new_directory('foo', tt.root)
1328
                tt.new_file('bar', foo, 'foobar')
1329
                baz = tt.new_directory('baz', tt.root)
1330
                tt.new_file('qux', baz, 'quux')
1331
                # Ask for a rename 'foo' -> 'baz'
1332
                tt.adjust_path('baz', tt.root, foo)
3063.1.3 by Aaron Bentley
Update for Linux
1333
                # Lie to tt that we've already resolved all conflicts.
3063.1.1 by Alexander Belchenko
Catch OSError 17 (file exists) in final phase of tree transform and show filename to user (#111758).
1334
                tt.apply(no_conflicts=True)
3063.1.3 by Aaron Bentley
Update for Linux
1335
            except:
3063.1.1 by Alexander Belchenko
Catch OSError 17 (file exists) in final phase of tree transform and show filename to user (#111758).
1336
                wt.unlock()
3063.1.3 by Aaron Bentley
Update for Linux
1337
                raise
3638.3.17 by Vincent Ladeuil
Fixed as per Aaron's review.
1338
        # The rename will fail because the target directory is not empty (but
1339
        # raises FileExists anyway).
3063.1.1 by Alexander Belchenko
Catch OSError 17 (file exists) in final phase of tree transform and show filename to user (#111758).
1340
        err = self.assertRaises(errors.FileExists, tt_helper)
1341
        self.assertContainsRe(str(err),
3638.3.15 by Vincent Ladeuil
Fix test_case_insensitive_clash to pass on all platforms (renamed too).
1342
            "^File exists: .+/baz")
3063.1.1 by Alexander Belchenko
Catch OSError 17 (file exists) in final phase of tree transform and show filename to user (#111758).
1343
3063.1.2 by Alexander Belchenko
test for two directories clash
1344
    def test_two_directories_clash(self):
1345
        def tt_helper():
1346
            wt = self.make_branch_and_tree('.')
1347
            tt = TreeTransform(wt)  # TreeTransform obtains write lock
1348
            try:
3063.1.3 by Aaron Bentley
Update for Linux
1349
                foo_1 = tt.new_directory('foo', tt.root)
1350
                tt.new_directory('bar', foo_1)
3638.3.15 by Vincent Ladeuil
Fix test_case_insensitive_clash to pass on all platforms (renamed too).
1351
                # Adding the same directory with a different content
3063.1.3 by Aaron Bentley
Update for Linux
1352
                foo_2 = tt.new_directory('foo', tt.root)
1353
                tt.new_directory('baz', foo_2)
1354
                # Lie to tt that we've already resolved all conflicts.
3063.1.2 by Alexander Belchenko
test for two directories clash
1355
                tt.apply(no_conflicts=True)
3063.1.3 by Aaron Bentley
Update for Linux
1356
            except:
3063.1.2 by Alexander Belchenko
test for two directories clash
1357
                wt.unlock()
3063.1.3 by Aaron Bentley
Update for Linux
1358
                raise
3063.1.2 by Alexander Belchenko
test for two directories clash
1359
        err = self.assertRaises(errors.FileExists, tt_helper)
1360
        self.assertContainsRe(str(err),
1361
            "^File exists: .+/foo")
1362
3100.1.1 by Aaron Bentley
Fix ImmortalLimbo errors when transforms fail
1363
    def test_two_directories_clash_finalize(self):
1364
        def tt_helper():
1365
            wt = self.make_branch_and_tree('.')
1366
            tt = TreeTransform(wt)  # TreeTransform obtains write lock
1367
            try:
1368
                foo_1 = tt.new_directory('foo', tt.root)
1369
                tt.new_directory('bar', foo_1)
3638.3.15 by Vincent Ladeuil
Fix test_case_insensitive_clash to pass on all platforms (renamed too).
1370
                # Adding the same directory with a different content
3100.1.1 by Aaron Bentley
Fix ImmortalLimbo errors when transforms fail
1371
                foo_2 = tt.new_directory('foo', tt.root)
1372
                tt.new_directory('baz', foo_2)
1373
                # Lie to tt that we've already resolved all conflicts.
1374
                tt.apply(no_conflicts=True)
1375
            except:
1376
                tt.finalize()
1377
                raise
1378
        err = self.assertRaises(errors.FileExists, tt_helper)
1379
        self.assertContainsRe(str(err),
1380
            "^File exists: .+/foo")
1381
3535.6.1 by James Westby
Handle a file turning in to a directory in TreeTransform.
1382
    def test_file_to_directory(self):
1383
        wt = self.make_branch_and_tree('.')
3535.6.2 by James Westby
Fixes from review. Thanks Aaron and John.
1384
        self.build_tree(['foo'])
3535.6.1 by James Westby
Handle a file turning in to a directory in TreeTransform.
1385
        wt.add(['foo'])
3590.3.1 by James Westby
Make TreeTransform update the inventory with new kind information.
1386
        wt.commit("one")
3535.6.1 by James Westby
Handle a file turning in to a directory in TreeTransform.
1387
        tt = TreeTransform(wt)
3535.6.2 by James Westby
Fixes from review. Thanks Aaron and John.
1388
        self.addCleanup(tt.finalize)
3535.6.3 by James Westby
Fix the test to not create transform conflicts.
1389
        foo_trans_id = tt.trans_id_tree_path("foo")
1390
        tt.delete_contents(foo_trans_id)
1391
        tt.create_directory(foo_trans_id)
1392
        bar_trans_id = tt.trans_id_tree_path("foo/bar")
1393
        tt.create_file(["aa\n"], bar_trans_id)
1394
        tt.version_file("bar-1", bar_trans_id)
3535.6.2 by James Westby
Fixes from review. Thanks Aaron and John.
1395
        tt.apply()
3535.6.1 by James Westby
Handle a file turning in to a directory in TreeTransform.
1396
        self.failUnlessExists("foo/bar")
3590.3.2 by James Westby
Handle ->symlink changes as well.
1397
        wt.lock_read()
1398
        try:
1399
            self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1400
                    "directory")
1401
        finally:
1402
            wt.unlock()
3590.3.1 by James Westby
Make TreeTransform update the inventory with new kind information.
1403
        wt.commit("two")
1404
        changes = wt.changes_from(wt.basis_tree())
1405
        self.assertFalse(changes.has_changed(), changes)
3535.6.1 by James Westby
Handle a file turning in to a directory in TreeTransform.
1406
3590.3.2 by James Westby
Handle ->symlink changes as well.
1407
    def test_file_to_symlink(self):
3590.3.3 by James Westby
Make ->file changes work as well.
1408
        self.requireFeature(SymlinkFeature)
3590.3.2 by James Westby
Handle ->symlink changes as well.
1409
        wt = self.make_branch_and_tree('.')
1410
        self.build_tree(['foo'])
1411
        wt.add(['foo'])
1412
        wt.commit("one")
1413
        tt = TreeTransform(wt)
1414
        self.addCleanup(tt.finalize)
1415
        foo_trans_id = tt.trans_id_tree_path("foo")
1416
        tt.delete_contents(foo_trans_id)
1417
        tt.create_symlink("bar", foo_trans_id)
1418
        tt.apply()
1419
        self.failUnlessExists("foo")
1420
        wt.lock_read()
1421
        self.addCleanup(wt.unlock)
1422
        self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1423
                "symlink")
1424
3590.3.3 by James Westby
Make ->file changes work as well.
1425
    def test_dir_to_file(self):
1426
        wt = self.make_branch_and_tree('.')
1427
        self.build_tree(['foo/', 'foo/bar'])
1428
        wt.add(['foo', 'foo/bar'])
1429
        wt.commit("one")
1430
        tt = TreeTransform(wt)
1431
        self.addCleanup(tt.finalize)
1432
        foo_trans_id = tt.trans_id_tree_path("foo")
1433
        bar_trans_id = tt.trans_id_tree_path("foo/bar")
1434
        tt.delete_contents(foo_trans_id)
1435
        tt.delete_versioned(bar_trans_id)
1436
        tt.create_file(["aa\n"], foo_trans_id)
1437
        tt.apply()
1438
        self.failUnlessExists("foo")
1439
        wt.lock_read()
1440
        self.addCleanup(wt.unlock)
1441
        self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1442
                "file")
1443
3590.3.4 by James Westby
Add a test for creating hardlinks as well.
1444
    def test_dir_to_hardlink(self):
3590.3.5 by James Westby
Use HardlinkFeature for the hardlink test.
1445
        self.requireFeature(HardlinkFeature)
3590.3.4 by James Westby
Add a test for creating hardlinks as well.
1446
        wt = self.make_branch_and_tree('.')
1447
        self.build_tree(['foo/', 'foo/bar'])
1448
        wt.add(['foo', 'foo/bar'])
1449
        wt.commit("one")
1450
        tt = TreeTransform(wt)
1451
        self.addCleanup(tt.finalize)
1452
        foo_trans_id = tt.trans_id_tree_path("foo")
1453
        bar_trans_id = tt.trans_id_tree_path("foo/bar")
1454
        tt.delete_contents(foo_trans_id)
1455
        tt.delete_versioned(bar_trans_id)
1456
        self.build_tree(['baz'])
1457
        tt.create_hardlink("baz", foo_trans_id)
1458
        tt.apply()
1459
        self.failUnlessExists("foo")
1460
        self.failUnlessExists("baz")
1461
        wt.lock_read()
1462
        self.addCleanup(wt.unlock)
1463
        self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1464
                "file")
1465
3619.2.10 by Aaron Bentley
Compensate for stale entries in TT._needs_rename
1466
    def test_no_final_path(self):
1467
        transform, root = self.get_transform()
1468
        trans_id = transform.trans_id_file_id('foo')
1469
        transform.create_file('bar', trans_id)
1470
        transform.cancel_creation(trans_id)
1471
        transform.apply()
1472
3363.17.24 by Aaron Bentley
Implement create_by_tree
1473
    def test_create_from_tree(self):
1474
        tree1 = self.make_branch_and_tree('tree1')
1475
        self.build_tree_contents([('tree1/foo/',), ('tree1/bar', 'baz')])
1476
        tree1.add(['foo', 'bar'], ['foo-id', 'bar-id'])
1477
        tree2 = self.make_branch_and_tree('tree2')
1478
        tt = TreeTransform(tree2)
1479
        foo_trans_id = tt.create_path('foo', tt.root)
1480
        create_from_tree(tt, foo_trans_id, tree1, 'foo-id')
1481
        bar_trans_id = tt.create_path('bar', tt.root)
1482
        create_from_tree(tt, bar_trans_id, tree1, 'bar-id')
1483
        tt.apply()
1484
        self.assertEqual('directory', osutils.file_kind('tree2/foo'))
1485
        self.assertFileEqual('baz', 'tree2/bar')
1486
3363.17.25 by Aaron Bentley
remove get_inventory_entry, replace with create_from_tree
1487
    def test_create_from_tree_bytes(self):
1488
        """Provided lines are used instead of tree content."""
1489
        tree1 = self.make_branch_and_tree('tree1')
1490
        self.build_tree_contents([('tree1/foo', 'bar'),])
1491
        tree1.add('foo', 'foo-id')
1492
        tree2 = self.make_branch_and_tree('tree2')
1493
        tt = TreeTransform(tree2)
1494
        foo_trans_id = tt.create_path('foo', tt.root)
1495
        create_from_tree(tt, foo_trans_id, tree1, 'foo-id', bytes='qux')
1496
        tt.apply()
1497
        self.assertFileEqual('qux', 'tree2/foo')
1498
1499
    def test_create_from_tree_symlink(self):
3363.17.24 by Aaron Bentley
Implement create_by_tree
1500
        self.requireFeature(SymlinkFeature)
1501
        tree1 = self.make_branch_and_tree('tree1')
1502
        os.symlink('bar', 'tree1/foo')
1503
        tree1.add('foo', 'foo-id')
1504
        tt = TreeTransform(self.make_branch_and_tree('tree2'))
1505
        foo_trans_id = tt.create_path('foo', tt.root)
1506
        create_from_tree(tt, foo_trans_id, tree1, 'foo-id')
1507
        tt.apply()
1508
        self.assertEqual('bar', os.readlink('tree2/foo'))
1509
2502.1.3 by Aaron Bentley
Don't cause errors when creating contents for trans_ids with no parent/name
1510
1534.7.93 by Aaron Bentley
Added text merge test
1511
class TransformGroup(object):
3063.1.1 by Alexander Belchenko
Catch OSError 17 (file exists) in final phase of tree transform and show filename to user (#111758).
1512
1731.1.33 by Aaron Bentley
Revert no-special-root changes
1513
    def __init__(self, dirname, root_id):
1534.7.101 by Aaron Bentley
Got conflicts on symlinks working properly
1514
        self.name = dirname
1534.7.93 by Aaron Bentley
Added text merge test
1515
        os.mkdir(dirname)
1558.1.3 by Aaron Bentley
Fixed deprecated op use in test suite
1516
        self.wt = BzrDir.create_standalone_workingtree(dirname)
1731.1.33 by Aaron Bentley
Revert no-special-root changes
1517
        self.wt.set_root_id(root_id)
1558.1.3 by Aaron Bentley
Fixed deprecated op use in test suite
1518
        self.b = self.wt.branch
1534.7.93 by Aaron Bentley
Added text merge test
1519
        self.tt = TreeTransform(self.wt)
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
1520
        self.root = self.tt.trans_id_tree_file_id(self.wt.get_root_id())
1534.7.93 by Aaron Bentley
Added text merge test
1521
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
1522
1534.7.95 by Aaron Bentley
Added more text merge tests
1523
def conflict_text(tree, merge):
1524
    template = '%s TREE\n%s%s\n%s%s MERGE-SOURCE\n'
1525
    return template % ('<' * 7, tree, '=' * 7, merge, '>' * 7)
1526
1534.7.93 by Aaron Bentley
Added text merge test
1527
1528
class TestTransformMerge(TestCaseInTempDir):
3199.1.5 by Vincent Ladeuil
Fix two more leaking tmp dirs, by reworking TransformPreview lock handling.
1529
1534.7.93 by Aaron Bentley
Added text merge test
1530
    def test_text_merge(self):
2116.4.1 by John Arbash Meinel
Update file and revision id generators.
1531
        root_id = generate_ids.gen_root_id()
1731.1.33 by Aaron Bentley
Revert no-special-root changes
1532
        base = TransformGroup("base", root_id)
1534.7.93 by Aaron Bentley
Added text merge test
1533
        base.tt.new_file('a', base.root, 'a\nb\nc\nd\be\n', 'a')
1534.7.95 by Aaron Bentley
Added more text merge tests
1534
        base.tt.new_file('b', base.root, 'b1', 'b')
1535
        base.tt.new_file('c', base.root, 'c', 'c')
1536
        base.tt.new_file('d', base.root, 'd', 'd')
1537
        base.tt.new_file('e', base.root, 'e', 'e')
1538
        base.tt.new_file('f', base.root, 'f', 'f')
1534.7.96 by Aaron Bentley
Tested with BASE as directory
1539
        base.tt.new_directory('g', base.root, 'g')
1534.7.97 by Aaron Bentley
Ensured foo.BASE is a directory if there's a conflict
1540
        base.tt.new_directory('h', base.root, 'h')
1534.7.93 by Aaron Bentley
Added text merge test
1541
        base.tt.apply()
1731.1.33 by Aaron Bentley
Revert no-special-root changes
1542
        other = TransformGroup("other", root_id)
1534.7.93 by Aaron Bentley
Added text merge test
1543
        other.tt.new_file('a', other.root, 'y\nb\nc\nd\be\n', 'a')
1534.7.95 by Aaron Bentley
Added more text merge tests
1544
        other.tt.new_file('b', other.root, 'b2', 'b')
1545
        other.tt.new_file('c', other.root, 'c2', 'c')
1546
        other.tt.new_file('d', other.root, 'd', 'd')
1547
        other.tt.new_file('e', other.root, 'e2', 'e')
1548
        other.tt.new_file('f', other.root, 'f', 'f')
1534.7.96 by Aaron Bentley
Tested with BASE as directory
1549
        other.tt.new_file('g', other.root, 'g', 'g')
1534.7.97 by Aaron Bentley
Ensured foo.BASE is a directory if there's a conflict
1550
        other.tt.new_file('h', other.root, 'h\ni\nj\nk\n', 'h')
1534.7.99 by Aaron Bentley
Handle non-existent BASE properly
1551
        other.tt.new_file('i', other.root, 'h\ni\nj\nk\n', 'i')
1534.7.93 by Aaron Bentley
Added text merge test
1552
        other.tt.apply()
1731.1.33 by Aaron Bentley
Revert no-special-root changes
1553
        this = TransformGroup("this", root_id)
1534.7.93 by Aaron Bentley
Added text merge test
1554
        this.tt.new_file('a', this.root, 'a\nb\nc\nd\bz\n', 'a')
1534.7.95 by Aaron Bentley
Added more text merge tests
1555
        this.tt.new_file('b', this.root, 'b', 'b')
1556
        this.tt.new_file('c', this.root, 'c', 'c')
1557
        this.tt.new_file('d', this.root, 'd2', 'd')
1558
        this.tt.new_file('e', this.root, 'e2', 'e')
1559
        this.tt.new_file('f', this.root, 'f', 'f')
1534.7.96 by Aaron Bentley
Tested with BASE as directory
1560
        this.tt.new_file('g', this.root, 'g', 'g')
1534.7.97 by Aaron Bentley
Ensured foo.BASE is a directory if there's a conflict
1561
        this.tt.new_file('h', this.root, '1\n2\n3\n4\n', 'h')
1534.7.99 by Aaron Bentley
Handle non-existent BASE properly
1562
        this.tt.new_file('i', this.root, '1\n2\n3\n4\n', 'i')
1534.7.93 by Aaron Bentley
Added text merge test
1563
        this.tt.apply()
3008.1.11 by Michael Hudson
restore the default behaviour of Merge3Merger.__init__().
1564
        Merge3Merger(this.wt, this.wt, base.wt, other.wt)
3008.1.6 by Michael Hudson
chop up Merge3Merger.__init__ into pieces
1565
1534.7.95 by Aaron Bentley
Added more text merge tests
1566
        # textual merge
1534.7.93 by Aaron Bentley
Added text merge test
1567
        self.assertEqual(this.wt.get_file('a').read(), 'y\nb\nc\nd\bz\n')
1534.7.95 by Aaron Bentley
Added more text merge tests
1568
        # three-way text conflict
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
1569
        self.assertEqual(this.wt.get_file('b').read(),
1534.7.95 by Aaron Bentley
Added more text merge tests
1570
                         conflict_text('b', 'b2'))
1571
        # OTHER wins
1572
        self.assertEqual(this.wt.get_file('c').read(), 'c2')
1573
        # THIS wins
1574
        self.assertEqual(this.wt.get_file('d').read(), 'd2')
1575
        # Ambigious clean merge
1576
        self.assertEqual(this.wt.get_file('e').read(), 'e2')
1577
        # No change
1578
        self.assertEqual(this.wt.get_file('f').read(), 'f')
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
1579
        # Correct correct results when THIS == OTHER
1534.7.96 by Aaron Bentley
Tested with BASE as directory
1580
        self.assertEqual(this.wt.get_file('g').read(), 'g')
1534.7.97 by Aaron Bentley
Ensured foo.BASE is a directory if there's a conflict
1581
        # Text conflict when THIS & OTHER are text and BASE is dir
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
1582
        self.assertEqual(this.wt.get_file('h').read(),
1534.7.97 by Aaron Bentley
Ensured foo.BASE is a directory if there's a conflict
1583
                         conflict_text('1\n2\n3\n4\n', 'h\ni\nj\nk\n'))
1584
        self.assertEqual(this.wt.get_file_byname('h.THIS').read(),
1585
                         '1\n2\n3\n4\n')
1586
        self.assertEqual(this.wt.get_file_byname('h.OTHER').read(),
1587
                         'h\ni\nj\nk\n')
1588
        self.assertEqual(file_kind(this.wt.abspath('h.BASE')), 'directory')
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
1589
        self.assertEqual(this.wt.get_file('i').read(),
1534.7.99 by Aaron Bentley
Handle non-existent BASE properly
1590
                         conflict_text('1\n2\n3\n4\n', 'h\ni\nj\nk\n'))
1591
        self.assertEqual(this.wt.get_file_byname('i.THIS').read(),
1592
                         '1\n2\n3\n4\n')
1593
        self.assertEqual(this.wt.get_file_byname('i.OTHER').read(),
1594
                         'h\ni\nj\nk\n')
1595
        self.assertEqual(os.path.exists(this.wt.abspath('i.BASE')), False)
1534.7.192 by Aaron Bentley
Record hashes produced by merges
1596
        modified = ['a', 'b', 'c', 'h', 'i']
1597
        merge_modified = this.wt.merge_modified()
1598
        self.assertSubset(merge_modified, modified)
1599
        self.assertEqual(len(merge_modified), len(modified))
1600
        file(this.wt.id2abspath('a'), 'wb').write('booga')
1601
        modified.pop(0)
1602
        merge_modified = this.wt.merge_modified()
1603
        self.assertSubset(merge_modified, modified)
1604
        self.assertEqual(len(merge_modified), len(modified))
1558.12.10 by Aaron Bentley
Be robust when merge_hash file_id not in inventory
1605
        this.wt.remove('b')
2796.1.4 by Aaron Bentley
Fix up various test cases
1606
        this.wt.revert()
1534.7.101 by Aaron Bentley
Got conflicts on symlinks working properly
1607
1608
    def test_file_merge(self):
2949.5.1 by Alexander Belchenko
selftest: use SymlinkFeature instead of TestSkipped where appropriate
1609
        self.requireFeature(SymlinkFeature)
2116.4.1 by John Arbash Meinel
Update file and revision id generators.
1610
        root_id = generate_ids.gen_root_id()
1731.1.33 by Aaron Bentley
Revert no-special-root changes
1611
        base = TransformGroup("BASE", root_id)
1612
        this = TransformGroup("THIS", root_id)
1613
        other = TransformGroup("OTHER", root_id)
1534.7.101 by Aaron Bentley
Got conflicts on symlinks working properly
1614
        for tg in this, base, other:
1615
            tg.tt.new_directory('a', tg.root, 'a')
1616
            tg.tt.new_symlink('b', tg.root, 'b', 'b')
1617
            tg.tt.new_file('c', tg.root, 'c', 'c')
1618
            tg.tt.new_symlink('d', tg.root, tg.name, 'd')
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
1619
        targets = ((base, 'base-e', 'base-f', None, None),
1620
                   (this, 'other-e', 'this-f', 'other-g', 'this-h'),
1534.7.104 by Aaron Bentley
Fixed set_versioned, enhanced conflict testing
1621
                   (other, 'other-e', None, 'other-g', 'other-h'))
1622
        for tg, e_target, f_target, g_target, h_target in targets:
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
1623
            for link, target in (('e', e_target), ('f', f_target),
1534.7.104 by Aaron Bentley
Fixed set_versioned, enhanced conflict testing
1624
                                 ('g', g_target), ('h', h_target)):
1625
                if target is not None:
1626
                    tg.tt.new_symlink(link, tg.root, target, link)
1534.7.102 by Aaron Bentley
Deleted old pre-conflict contents
1627
1628
        for tg in this, base, other:
1534.7.101 by Aaron Bentley
Got conflicts on symlinks working properly
1629
            tg.tt.apply()
3008.1.11 by Michael Hudson
restore the default behaviour of Merge3Merger.__init__().
1630
        Merge3Merger(this.wt, this.wt, base.wt, other.wt)
1534.7.101 by Aaron Bentley
Got conflicts on symlinks working properly
1631
        self.assertIs(os.path.isdir(this.wt.abspath('a')), True)
1632
        self.assertIs(os.path.islink(this.wt.abspath('b')), True)
1633
        self.assertIs(os.path.isfile(this.wt.abspath('c')), True)
1634
        for suffix in ('THIS', 'BASE', 'OTHER'):
1635
            self.assertEqual(os.readlink(this.wt.abspath('d.'+suffix)), suffix)
1534.7.102 by Aaron Bentley
Deleted old pre-conflict contents
1636
        self.assertIs(os.path.lexists(this.wt.abspath('d')), False)
1534.7.104 by Aaron Bentley
Fixed set_versioned, enhanced conflict testing
1637
        self.assertEqual(this.wt.id2path('d'), 'd.OTHER')
1638
        self.assertEqual(this.wt.id2path('f'), 'f.THIS')
1534.7.102 by Aaron Bentley
Deleted old pre-conflict contents
1639
        self.assertEqual(os.readlink(this.wt.abspath('e')), 'other-e')
1640
        self.assertIs(os.path.lexists(this.wt.abspath('e.THIS')), False)
1641
        self.assertIs(os.path.lexists(this.wt.abspath('e.OTHER')), False)
1642
        self.assertIs(os.path.lexists(this.wt.abspath('e.BASE')), False)
1534.7.104 by Aaron Bentley
Fixed set_versioned, enhanced conflict testing
1643
        self.assertIs(os.path.lexists(this.wt.abspath('g')), True)
1644
        self.assertIs(os.path.lexists(this.wt.abspath('g.BASE')), False)
1645
        self.assertIs(os.path.lexists(this.wt.abspath('h')), False)
1646
        self.assertIs(os.path.lexists(this.wt.abspath('h.BASE')), False)
1647
        self.assertIs(os.path.lexists(this.wt.abspath('h.THIS')), True)
1648
        self.assertIs(os.path.lexists(this.wt.abspath('h.OTHER')), True)
1534.7.105 by Aaron Bentley
Got merge with rename working
1649
1650
    def test_filename_merge(self):
2116.4.1 by John Arbash Meinel
Update file and revision id generators.
1651
        root_id = generate_ids.gen_root_id()
1731.1.33 by Aaron Bentley
Revert no-special-root changes
1652
        base = TransformGroup("BASE", root_id)
1653
        this = TransformGroup("THIS", root_id)
1654
        other = TransformGroup("OTHER", root_id)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
1655
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a')
1534.7.105 by Aaron Bentley
Got merge with rename working
1656
                                   for t in [base, this, other]]
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
1657
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b')
1534.7.105 by Aaron Bentley
Got merge with rename working
1658
                                   for t in [base, this, other]]
1659
        base.tt.new_directory('c', base_a, 'c')
1660
        this.tt.new_directory('c1', this_a, 'c')
1661
        other.tt.new_directory('c', other_b, 'c')
1662
1663
        base.tt.new_directory('d', base_a, 'd')
1664
        this.tt.new_directory('d1', this_b, 'd')
1665
        other.tt.new_directory('d', other_a, 'd')
1666
1667
        base.tt.new_directory('e', base_a, 'e')
1668
        this.tt.new_directory('e', this_a, 'e')
1669
        other.tt.new_directory('e1', other_b, 'e')
1670
1671
        base.tt.new_directory('f', base_a, 'f')
1672
        this.tt.new_directory('f1', this_b, 'f')
1673
        other.tt.new_directory('f1', other_b, 'f')
1674
1675
        for tg in [this, base, other]:
1676
            tg.tt.apply()
3008.1.11 by Michael Hudson
restore the default behaviour of Merge3Merger.__init__().
1677
        Merge3Merger(this.wt, this.wt, base.wt, other.wt)
1534.7.176 by abentley
Fixed up tests for Windows
1678
        self.assertEqual(this.wt.id2path('c'), pathjoin('b/c1'))
1679
        self.assertEqual(this.wt.id2path('d'), pathjoin('b/d1'))
1680
        self.assertEqual(this.wt.id2path('e'), pathjoin('b/e1'))
1681
        self.assertEqual(this.wt.id2path('f'), pathjoin('b/f1'))
1534.7.105 by Aaron Bentley
Got merge with rename working
1682
1683
    def test_filename_merge_conflicts(self):
2116.4.1 by John Arbash Meinel
Update file and revision id generators.
1684
        root_id = generate_ids.gen_root_id()
1731.1.33 by Aaron Bentley
Revert no-special-root changes
1685
        base = TransformGroup("BASE", root_id)
1686
        this = TransformGroup("THIS", root_id)
1687
        other = TransformGroup("OTHER", root_id)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
1688
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a')
1534.7.105 by Aaron Bentley
Got merge with rename working
1689
                                   for t in [base, this, other]]
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
1690
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b')
1534.7.105 by Aaron Bentley
Got merge with rename working
1691
                                   for t in [base, this, other]]
1692
1693
        base.tt.new_file('g', base_a, 'g', 'g')
1694
        other.tt.new_file('g1', other_b, 'g1', 'g')
1695
1696
        base.tt.new_file('h', base_a, 'h', 'h')
1697
        this.tt.new_file('h1', this_b, 'h1', 'h')
1698
1699
        base.tt.new_file('i', base.root, 'i', 'i')
1534.7.153 by Aaron Bentley
Handled test cases involving symlinks
1700
        other.tt.new_directory('i1', this_b, 'i')
1534.7.105 by Aaron Bentley
Got merge with rename working
1701
1702
        for tg in [this, base, other]:
1703
            tg.tt.apply()
3008.1.11 by Michael Hudson
restore the default behaviour of Merge3Merger.__init__().
1704
        Merge3Merger(this.wt, this.wt, base.wt, other.wt)
1534.7.105 by Aaron Bentley
Got merge with rename working
1705
1534.7.176 by abentley
Fixed up tests for Windows
1706
        self.assertEqual(this.wt.id2path('g'), pathjoin('b/g1.OTHER'))
1534.7.105 by Aaron Bentley
Got merge with rename working
1707
        self.assertIs(os.path.lexists(this.wt.abspath('b/g1.BASE')), True)
1708
        self.assertIs(os.path.lexists(this.wt.abspath('b/g1.THIS')), False)
1534.7.176 by abentley
Fixed up tests for Windows
1709
        self.assertEqual(this.wt.id2path('h'), pathjoin('b/h1.THIS'))
1534.7.105 by Aaron Bentley
Got merge with rename working
1710
        self.assertIs(os.path.lexists(this.wt.abspath('b/h1.BASE')), True)
1711
        self.assertIs(os.path.lexists(this.wt.abspath('b/h1.OTHER')), False)
1534.7.176 by abentley
Fixed up tests for Windows
1712
        self.assertEqual(this.wt.id2path('i'), pathjoin('b/i1.OTHER'))
1534.7.183 by Aaron Bentley
Fixed build_tree with symlinks
1713
2027.1.1 by John Arbash Meinel
Fix bug #56549, and write a direct test that the right path is being statted
1714
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1715
class TestBuildTree(tests.TestCaseWithTransport):
1716
3006.2.2 by Alexander Belchenko
tests added.
1717
    def test_build_tree_with_symlinks(self):
2949.5.1 by Alexander Belchenko
selftest: use SymlinkFeature instead of TestSkipped where appropriate
1718
        self.requireFeature(SymlinkFeature)
1534.7.183 by Aaron Bentley
Fixed build_tree with symlinks
1719
        os.mkdir('a')
1720
        a = BzrDir.create_standalone_workingtree('a')
1721
        os.mkdir('a/foo')
1722
        file('a/foo/bar', 'wb').write('contents')
1723
        os.symlink('a/foo/bar', 'a/foo/baz')
1724
        a.add(['foo', 'foo/bar', 'foo/baz'])
1725
        a.commit('initial commit')
1726
        b = BzrDir.create_standalone_workingtree('b')
2255.2.194 by Robert Collins
[BROKEN] Many updates to stop using experimental formats in tests.
1727
        basis = a.basis_tree()
1728
        basis.lock_read()
1729
        self.addCleanup(basis.unlock)
1730
        build_tree(basis, b)
1534.7.183 by Aaron Bentley
Fixed build_tree with symlinks
1731
        self.assertIs(os.path.isdir('b/foo'), True)
1732
        self.assertEqual(file('b/foo/bar', 'rb').read(), "contents")
1733
        self.assertEqual(os.readlink('b/foo/baz'), 'a/foo/bar')
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1734
2100.3.21 by Aaron Bentley
Work on checking out by-reference trees
1735
    def test_build_with_references(self):
2255.2.194 by Robert Collins
[BROKEN] Many updates to stop using experimental formats in tests.
1736
        tree = self.make_branch_and_tree('source',
1737
            format='dirstate-with-subtree')
1738
        subtree = self.make_branch_and_tree('source/subtree',
1739
            format='dirstate-with-subtree')
2100.3.21 by Aaron Bentley
Work on checking out by-reference trees
1740
        tree.add_reference(subtree)
1741
        tree.commit('a revision')
1742
        tree.branch.create_checkout('target')
1743
        self.failUnlessExists('target')
1744
        self.failUnlessExists('target/subtree')
1745
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1746
    def test_file_conflict_handling(self):
1747
        """Ensure that when building trees, conflict handling is done"""
1748
        source = self.make_branch_and_tree('source')
1749
        target = self.make_branch_and_tree('target')
1750
        self.build_tree(['source/file', 'target/file'])
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1751
        source.add('file', 'new-file')
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1752
        source.commit('added file')
1753
        build_tree(source.basis_tree(), target)
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1754
        self.assertEqual([DuplicateEntry('Moved existing file to',
1755
                          'file.moved', 'file', None, 'new-file')],
1756
                         target.conflicts())
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1757
        target2 = self.make_branch_and_tree('target2')
1758
        target_file = file('target2/file', 'wb')
1759
        try:
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1760
            source_file = file('source/file', 'rb')
1761
            try:
1762
                target_file.write(source_file.read())
1763
            finally:
1764
                source_file.close()
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1765
        finally:
1766
            target_file.close()
1767
        build_tree(source.basis_tree(), target2)
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1768
        self.assertEqual([], target2.conflicts())
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1769
1770
    def test_symlink_conflict_handling(self):
1771
        """Ensure that when building trees, conflict handling is done"""
2949.5.1 by Alexander Belchenko
selftest: use SymlinkFeature instead of TestSkipped where appropriate
1772
        self.requireFeature(SymlinkFeature)
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1773
        source = self.make_branch_and_tree('source')
1774
        os.symlink('foo', 'source/symlink')
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1775
        source.add('symlink', 'new-symlink')
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1776
        source.commit('added file')
1777
        target = self.make_branch_and_tree('target')
1778
        os.symlink('bar', 'target/symlink')
1779
        build_tree(source.basis_tree(), target)
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1780
        self.assertEqual([DuplicateEntry('Moved existing file to',
1781
            'symlink.moved', 'symlink', None, 'new-symlink')],
1782
            target.conflicts())
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1783
        target = self.make_branch_and_tree('target2')
1784
        os.symlink('foo', 'target2/symlink')
1785
        build_tree(source.basis_tree(), target)
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1786
        self.assertEqual([], target.conflicts())
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
1787
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1788
    def test_directory_conflict_handling(self):
1789
        """Ensure that when building trees, conflict handling is done"""
1790
        source = self.make_branch_and_tree('source')
1791
        target = self.make_branch_and_tree('target')
1792
        self.build_tree(['source/dir1/', 'source/dir1/file', 'target/dir1/'])
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1793
        source.add(['dir1', 'dir1/file'], ['new-dir1', 'new-file'])
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1794
        source.commit('added file')
1795
        build_tree(source.basis_tree(), target)
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1796
        self.assertEqual([], target.conflicts())
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1797
        self.failUnlessExists('target/dir1/file')
1798
1799
        # Ensure contents are merged
1800
        target = self.make_branch_and_tree('target2')
1801
        self.build_tree(['target2/dir1/', 'target2/dir1/file2'])
1802
        build_tree(source.basis_tree(), target)
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1803
        self.assertEqual([], target.conflicts())
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1804
        self.failUnlessExists('target2/dir1/file2')
1805
        self.failUnlessExists('target2/dir1/file')
1806
1807
        # Ensure new contents are suppressed for existing branches
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1808
        target = self.make_branch_and_tree('target3')
1809
        self.make_branch('target3/dir1')
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1810
        self.build_tree(['target3/dir1/file2'])
1811
        build_tree(source.basis_tree(), target)
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1812
        self.failIfExists('target3/dir1/file')
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1813
        self.failUnlessExists('target3/dir1/file2')
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1814
        self.failUnlessExists('target3/dir1.diverted/file')
1815
        self.assertEqual([DuplicateEntry('Diverted to',
1816
            'dir1.diverted', 'dir1', 'new-dir1', None)],
1817
            target.conflicts())
1818
1819
        target = self.make_branch_and_tree('target4')
1820
        self.build_tree(['target4/dir1/'])
1821
        self.make_branch('target4/dir1/file')
1822
        build_tree(source.basis_tree(), target)
1823
        self.failUnlessExists('target4/dir1/file')
1824
        self.assertEqual('directory', file_kind('target4/dir1/file'))
1825
        self.failUnlessExists('target4/dir1/file.diverted')
1826
        self.assertEqual([DuplicateEntry('Diverted to',
1827
            'dir1/file.diverted', 'dir1/file', 'new-file', None)],
1828
            target.conflicts())
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1829
1830
    def test_mixed_conflict_handling(self):
1831
        """Ensure that when building trees, conflict handling is done"""
1832
        source = self.make_branch_and_tree('source')
1833
        target = self.make_branch_and_tree('target')
1834
        self.build_tree(['source/name', 'target/name/'])
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1835
        source.add('name', 'new-name')
1836
        source.commit('added file')
1837
        build_tree(source.basis_tree(), target)
1838
        self.assertEqual([DuplicateEntry('Moved existing file to',
1839
            'name.moved', 'name', None, 'new-name')], target.conflicts())
1840
1841
    def test_raises_in_populated(self):
1842
        source = self.make_branch_and_tree('source')
1843
        self.build_tree(['source/name'])
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1844
        source.add('name')
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1845
        source.commit('added name')
1846
        target = self.make_branch_and_tree('target')
1847
        self.build_tree(['target/name'])
1848
        target.add('name')
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
1849
        self.assertRaises(errors.WorkingTreeAlreadyPopulated,
2090.2.1 by Martin Pool
Fix some code which relies on assertions and breaks under python -O
1850
            build_tree, source.basis_tree(), target)
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1851
2502.1.1 by Aaron Bentley
Ensure renames only root children are renamed when building trees
1852
    def test_build_tree_rename_count(self):
1853
        source = self.make_branch_and_tree('source')
1854
        self.build_tree(['source/file1', 'source/dir1/'])
1855
        source.add(['file1', 'dir1'])
1856
        source.commit('add1')
1857
        target1 = self.make_branch_and_tree('target1')
2502.1.6 by Aaron Bentley
Update from review comments
1858
        transform_result = build_tree(source.basis_tree(), target1)
1859
        self.assertEqual(2, transform_result.rename_count)
2502.1.1 by Aaron Bentley
Ensure renames only root children are renamed when building trees
1860
1861
        self.build_tree(['source/dir1/file2'])
1862
        source.add(['dir1/file2'])
1863
        source.commit('add3')
1864
        target2 = self.make_branch_and_tree('target2')
2502.1.6 by Aaron Bentley
Update from review comments
1865
        transform_result = build_tree(source.basis_tree(), target2)
2502.1.1 by Aaron Bentley
Ensure renames only root children are renamed when building trees
1866
        # children of non-root directories should not be renamed
2502.1.6 by Aaron Bentley
Update from review comments
1867
        self.assertEqual(2, transform_result.rename_count)
2502.1.1 by Aaron Bentley
Ensure renames only root children are renamed when building trees
1868
3136.1.2 by Aaron Bentley
Implement hard-linking for build_tree
1869
    def create_ab_tree(self):
1870
        """Create a committed test tree with two files"""
1871
        source = self.make_branch_and_tree('source')
1872
        self.build_tree_contents([('source/file1', 'A')])
1873
        self.build_tree_contents([('source/file2', 'B')])
1874
        source.add(['file1', 'file2'], ['file1-id', 'file2-id'])
1875
        source.commit('commit files')
1876
        source.lock_write()
1877
        self.addCleanup(source.unlock)
1878
        return source
1879
3123.5.1 by Aaron Bentley
Make build-tree able to use an additional 'accelerator' tree
1880
    def test_build_tree_accelerator_tree(self):
3136.1.2 by Aaron Bentley
Implement hard-linking for build_tree
1881
        source = self.create_ab_tree()
3123.5.1 by Aaron Bentley
Make build-tree able to use an additional 'accelerator' tree
1882
        self.build_tree_contents([('source/file2', 'C')])
1883
        calls = []
1884
        real_source_get_file = source.get_file
1885
        def get_file(file_id, path=None):
1886
            calls.append(file_id)
1887
            return real_source_get_file(file_id, path)
1888
        source.get_file = get_file
1889
        target = self.make_branch_and_tree('target')
3123.5.19 by Aaron Bentley
Ensure content is exactly the same, when accelerator used
1890
        revision_tree = source.basis_tree()
1891
        revision_tree.lock_read()
1892
        self.addCleanup(revision_tree.unlock)
1893
        build_tree(revision_tree, target, source)
3123.5.1 by Aaron Bentley
Make build-tree able to use an additional 'accelerator' tree
1894
        self.assertEqual(['file1-id'], calls)
3123.5.19 by Aaron Bentley
Ensure content is exactly the same, when accelerator used
1895
        target.lock_read()
1896
        self.addCleanup(target.unlock)
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
1897
        self.assertEqual([], list(target.iter_changes(revision_tree)))
3123.5.1 by Aaron Bentley
Make build-tree able to use an additional 'accelerator' tree
1898
3123.5.4 by Aaron Bentley
Use an accelerator tree when branching, handle no-such-id correctly
1899
    def test_build_tree_accelerator_tree_missing_file(self):
3136.1.2 by Aaron Bentley
Implement hard-linking for build_tree
1900
        source = self.create_ab_tree()
3123.5.4 by Aaron Bentley
Use an accelerator tree when branching, handle no-such-id correctly
1901
        os.unlink('source/file1')
1902
        source.remove(['file2'])
1903
        target = self.make_branch_and_tree('target')
3123.5.19 by Aaron Bentley
Ensure content is exactly the same, when accelerator used
1904
        revision_tree = source.basis_tree()
1905
        revision_tree.lock_read()
1906
        self.addCleanup(revision_tree.unlock)
1907
        build_tree(revision_tree, target, source)
1908
        target.lock_read()
1909
        self.addCleanup(target.unlock)
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
1910
        self.assertEqual([], list(target.iter_changes(revision_tree)))
3123.5.4 by Aaron Bentley
Use an accelerator tree when branching, handle no-such-id correctly
1911
3123.5.16 by Aaron Bentley
Test handling of conversion to non-file
1912
    def test_build_tree_accelerator_wrong_kind(self):
3146.4.8 by Aaron Bentley
Add missing symlink requirement
1913
        self.requireFeature(SymlinkFeature)
3123.5.16 by Aaron Bentley
Test handling of conversion to non-file
1914
        source = self.make_branch_and_tree('source')
1915
        self.build_tree_contents([('source/file1', '')])
1916
        self.build_tree_contents([('source/file2', '')])
1917
        source.add(['file1', 'file2'], ['file1-id', 'file2-id'])
1918
        source.commit('commit files')
1919
        os.unlink('source/file2')
1920
        self.build_tree_contents([('source/file2/', 'C')])
1921
        os.unlink('source/file1')
1922
        os.symlink('file2', 'source/file1')
1923
        calls = []
1924
        real_source_get_file = source.get_file
1925
        def get_file(file_id, path=None):
1926
            calls.append(file_id)
1927
            return real_source_get_file(file_id, path)
1928
        source.get_file = get_file
1929
        target = self.make_branch_and_tree('target')
3123.5.19 by Aaron Bentley
Ensure content is exactly the same, when accelerator used
1930
        revision_tree = source.basis_tree()
1931
        revision_tree.lock_read()
1932
        self.addCleanup(revision_tree.unlock)
1933
        build_tree(revision_tree, target, source)
3123.5.16 by Aaron Bentley
Test handling of conversion to non-file
1934
        self.assertEqual([], calls)
3123.5.19 by Aaron Bentley
Ensure content is exactly the same, when accelerator used
1935
        target.lock_read()
1936
        self.addCleanup(target.unlock)
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
1937
        self.assertEqual([], list(target.iter_changes(revision_tree)))
3123.5.16 by Aaron Bentley
Test handling of conversion to non-file
1938
3136.1.2 by Aaron Bentley
Implement hard-linking for build_tree
1939
    def test_build_tree_hardlink(self):
1940
        self.requireFeature(HardlinkFeature)
1941
        source = self.create_ab_tree()
1942
        target = self.make_branch_and_tree('target')
1943
        revision_tree = source.basis_tree()
1944
        revision_tree.lock_read()
1945
        self.addCleanup(revision_tree.unlock)
1946
        build_tree(revision_tree, target, source, hardlink=True)
1947
        target.lock_read()
1948
        self.addCleanup(target.unlock)
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
1949
        self.assertEqual([], list(target.iter_changes(revision_tree)))
3136.1.2 by Aaron Bentley
Implement hard-linking for build_tree
1950
        source_stat = os.stat('source/file1')
1951
        target_stat = os.stat('target/file1')
1952
        self.assertEqual(source_stat, target_stat)
1953
1954
        # Explicitly disallowing hardlinks should prevent them.
1955
        target2 = self.make_branch_and_tree('target2')
1956
        build_tree(revision_tree, target2, source, hardlink=False)
1957
        target2.lock_read()
1958
        self.addCleanup(target2.unlock)
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
1959
        self.assertEqual([], list(target2.iter_changes(revision_tree)))
3136.1.2 by Aaron Bentley
Implement hard-linking for build_tree
1960
        source_stat = os.stat('source/file1')
1961
        target2_stat = os.stat('target2/file1')
1962
        self.assertNotEqual(source_stat, target2_stat)
1963
3137.1.1 by Aaron Bentley
Fix build_tree acceleration when file is moved in accelerator_tree
1964
    def test_build_tree_accelerator_tree_moved(self):
1965
        source = self.make_branch_and_tree('source')
1966
        self.build_tree_contents([('source/file1', 'A')])
1967
        source.add(['file1'], ['file1-id'])
1968
        source.commit('commit files')
1969
        source.rename_one('file1', 'file2')
1970
        source.lock_read()
1971
        self.addCleanup(source.unlock)
1972
        target = self.make_branch_and_tree('target')
1973
        revision_tree = source.basis_tree()
1974
        revision_tree.lock_read()
1975
        self.addCleanup(revision_tree.unlock)
1976
        build_tree(revision_tree, target, source)
1977
        target.lock_read()
1978
        self.addCleanup(target.unlock)
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
1979
        self.assertEqual([], list(target.iter_changes(revision_tree)))
3137.1.1 by Aaron Bentley
Fix build_tree acceleration when file is moved in accelerator_tree
1980
3136.1.2 by Aaron Bentley
Implement hard-linking for build_tree
1981
    def test_build_tree_hardlinks_preserve_execute(self):
1982
        self.requireFeature(HardlinkFeature)
1983
        source = self.create_ab_tree()
1984
        tt = TreeTransform(source)
1985
        trans_id = tt.trans_id_tree_file_id('file1-id')
1986
        tt.set_executability(True, trans_id)
1987
        tt.apply()
1988
        self.assertTrue(source.is_executable('file1-id'))
1989
        target = self.make_branch_and_tree('target')
1990
        revision_tree = source.basis_tree()
1991
        revision_tree.lock_read()
1992
        self.addCleanup(revision_tree.unlock)
1993
        build_tree(revision_tree, target, source, hardlink=True)
1994
        target.lock_read()
1995
        self.addCleanup(target.unlock)
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
1996
        self.assertEqual([], list(target.iter_changes(revision_tree)))
3136.1.2 by Aaron Bentley
Implement hard-linking for build_tree
1997
        self.assertTrue(source.is_executable('file1-id'))
1998
4826.1.5 by Andrew Bennetts
Add test that content filtered files are not hardlinked by build_tree.
1999
    def install_rot13_content_filter(self, pattern):
4985.2.1 by Vincent Ladeuil
Deploy addAttrCleanup on the whole test suite.
2000
        # We could use
2001
        # self.addCleanup(filters._reset_registry, filters._reset_registry())
2002
        # below, but that looks a bit... hard to read even if it's exactly
2003
        # the same thing.
4826.1.5 by Andrew Bennetts
Add test that content filtered files are not hardlinked by build_tree.
2004
        original_registry = filters._reset_registry()
2005
        def restore_registry():
2006
            filters._reset_registry(original_registry)
2007
        self.addCleanup(restore_registry)
2008
        def rot13(chunks, context=None):
2009
            return [''.join(chunks).encode('rot13')]
2010
        rot13filter = filters.ContentFilter(rot13, rot13)
2011
        filters.register_filter_stack_map('rot13', {'yes': [rot13filter]}.get)
2012
        os.mkdir(self.test_home_dir + '/.bazaar')
2013
        rules_filename = self.test_home_dir + '/.bazaar/rules'
4826.1.8 by Andrew Bennetts
Tweaks suggested by John.
2014
        f = open(rules_filename, 'wb')
4826.1.5 by Andrew Bennetts
Add test that content filtered files are not hardlinked by build_tree.
2015
        f.write('[name %s]\nrot13=yes\n' % (pattern,))
2016
        f.close()
2017
        def uninstall_rules():
2018
            os.remove(rules_filename)
2019
            rules.reset_rules()
2020
        self.addCleanup(uninstall_rules)
2021
        rules.reset_rules()
2022
2023
    def test_build_tree_content_filtered_files_are_not_hardlinked(self):
2024
        """build_tree will not hardlink files that have content filtering rules
2025
        applied to them (but will still hardlink other files from the same tree
2026
        if it can).
2027
        """
2028
        self.requireFeature(HardlinkFeature)
2029
        self.install_rot13_content_filter('file1')
2030
        source = self.create_ab_tree()
2031
        target = self.make_branch_and_tree('target')
2032
        revision_tree = source.basis_tree()
2033
        revision_tree.lock_read()
2034
        self.addCleanup(revision_tree.unlock)
2035
        build_tree(revision_tree, target, source, hardlink=True)
2036
        target.lock_read()
2037
        self.addCleanup(target.unlock)
2038
        self.assertEqual([], list(target.iter_changes(revision_tree)))
2039
        source_stat = os.stat('source/file1')
2040
        target_stat = os.stat('target/file1')
2041
        self.assertNotEqual(source_stat, target_stat)
2042
        source_stat = os.stat('source/file2')
2043
        target_stat = os.stat('target/file2')
4826.1.8 by Andrew Bennetts
Tweaks suggested by John.
2044
        self.assertEqualStat(source_stat, target_stat)
4826.1.5 by Andrew Bennetts
Add test that content filtered files are not hardlinked by build_tree.
2045
3453.2.4 by Aaron Bentley
Disable fast-path when conflicts are encountered
2046
    def test_case_insensitive_build_tree_inventory(self):
4241.14.12 by Vincent Ladeuil
Far too many modifications for a single commit, need to restart.
2047
        if (tests.CaseInsensitiveFilesystemFeature.available()
2048
            or tests.CaseInsCasePresFilenameFeature.available()):
4241.9.4 by Vincent Ladeuil
Fix test_case_insensitive_build_tree_inventory failure on OSX.
2049
            raise tests.UnavailableFeature('Fully case sensitive filesystem')
3453.2.4 by Aaron Bentley
Disable fast-path when conflicts are encountered
2050
        source = self.make_branch_and_tree('source')
2051
        self.build_tree(['source/file', 'source/FILE'])
2052
        source.add(['file', 'FILE'], ['lower-id', 'upper-id'])
2053
        source.commit('added files')
2054
        # Don't try this at home, kids!
2055
        # Force the tree to report that it is case insensitive
2056
        target = self.make_branch_and_tree('target')
2057
        target.case_sensitive = False
3453.2.6 by Aaron Bentley
Rename mutate_tree to delta_from_tree, add comment
2058
        build_tree(source.basis_tree(), target, source, delta_from_tree=True)
3453.2.4 by Aaron Bentley
Disable fast-path when conflicts are encountered
2059
        self.assertEqual('file.moved', target.id2path('lower-id'))
2060
        self.assertEqual('FILE', target.id2path('upper-id'))
2061
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
2062
4526.8.1 by Aaron Bentley
Support committing a TreeTransform to a branch.
2063
class TestCommitTransform(tests.TestCaseWithTransport):
2064
2065
    def get_branch(self):
2066
        tree = self.make_branch_and_tree('tree')
2067
        tree.lock_write()
2068
        self.addCleanup(tree.unlock)
2069
        tree.commit('empty commit')
2070
        return tree.branch
2071
2072
    def get_branch_and_transform(self):
2073
        branch = self.get_branch()
2074
        tt = TransformPreview(branch.basis_tree())
2075
        self.addCleanup(tt.finalize)
2076
        return branch, tt
2077
2078
    def test_commit_wrong_basis(self):
2079
        branch = self.get_branch()
2080
        basis = branch.repository.revision_tree(
2081
            _mod_revision.NULL_REVISION)
2082
        tt = TransformPreview(basis)
2083
        self.addCleanup(tt.finalize)
4526.8.5 by Aaron Bentley
Updates from review.
2084
        e = self.assertRaises(ValueError, tt.commit, branch, '')
4526.8.1 by Aaron Bentley
Support committing a TreeTransform to a branch.
2085
        self.assertEqual('TreeTransform not based on branch basis: null:',
2086
                         str(e))
2087
2088
    def test_empy_commit(self):
2089
        branch, tt = self.get_branch_and_transform()
2090
        rev = tt.commit(branch, 'my message')
2091
        self.assertEqual(2, branch.revno())
2092
        repo = branch.repository
2093
        self.assertEqual('my message', repo.get_revision(rev).message)
2094
2095
    def test_merge_parents(self):
2096
        branch, tt = self.get_branch_and_transform()
2097
        rev = tt.commit(branch, 'my message', ['rev1b', 'rev1c'])
2098
        self.assertEqual(['rev1b', 'rev1c'],
2099
                         branch.basis_tree().get_parent_ids()[1:])
2100
4526.8.3 by Aaron Bentley
Clean up merge parent handling.
2101
    def test_first_commit(self):
2102
        branch = self.make_branch('branch')
2103
        branch.lock_write()
2104
        self.addCleanup(branch.unlock)
2105
        tt = TransformPreview(branch.basis_tree())
4659.2.4 by Vincent Ladeuil
Cleanup remaining bzr-limbo-XXXXXX leaks in /tmp during selftest.
2106
        self.addCleanup(tt.finalize)
4526.9.23 by Robert Collins
Change the tree transform test_first_commit test to set a root id in the new tree, and workaround an apparent bug in TreeTransform._determine_path.
2107
        tt.new_directory('', ROOT_PARENT, 'TREE_ROOT')
4526.8.3 by Aaron Bentley
Clean up merge parent handling.
2108
        rev = tt.commit(branch, 'my message')
2109
        self.assertEqual([], branch.basis_tree().get_parent_ids())
4526.8.5 by Aaron Bentley
Updates from review.
2110
        self.assertNotEqual(_mod_revision.NULL_REVISION,
2111
                            branch.last_revision())
4526.8.3 by Aaron Bentley
Clean up merge parent handling.
2112
4526.8.5 by Aaron Bentley
Updates from review.
2113
    def test_first_commit_with_merge_parents(self):
4526.8.3 by Aaron Bentley
Clean up merge parent handling.
2114
        branch = self.make_branch('branch')
2115
        branch.lock_write()
2116
        self.addCleanup(branch.unlock)
2117
        tt = TransformPreview(branch.basis_tree())
4659.2.4 by Vincent Ladeuil
Cleanup remaining bzr-limbo-XXXXXX leaks in /tmp during selftest.
2118
        self.addCleanup(tt.finalize)
4526.8.5 by Aaron Bentley
Updates from review.
2119
        e = self.assertRaises(ValueError, tt.commit, branch,
4526.8.3 by Aaron Bentley
Clean up merge parent handling.
2120
                          'my message', ['rev1b-id'])
4526.8.5 by Aaron Bentley
Updates from review.
2121
        self.assertEqual('Cannot supply merge parents for first commit.',
2122
                         str(e))
2123
        self.assertEqual(_mod_revision.NULL_REVISION, branch.last_revision())
4526.8.3 by Aaron Bentley
Clean up merge parent handling.
2124
4526.8.1 by Aaron Bentley
Support committing a TreeTransform to a branch.
2125
    def test_add_files(self):
2126
        branch, tt = self.get_branch_and_transform()
2127
        tt.new_file('file', tt.root, 'contents', 'file-id')
2128
        trans_id = tt.new_directory('dir', tt.root, 'dir-id')
4789.25.2 by John Arbash Meinel
Fix 3 tests that meant to skip exec or symlink testing on win32
2129
        if SymlinkFeature.available():
2130
            tt.new_symlink('symlink', trans_id, 'target', 'symlink-id')
4526.8.1 by Aaron Bentley
Support committing a TreeTransform to a branch.
2131
        rev = tt.commit(branch, 'message')
2132
        tree = branch.basis_tree()
2133
        self.assertEqual('file', tree.id2path('file-id'))
2134
        self.assertEqual('contents', tree.get_file_text('file-id'))
2135
        self.assertEqual('dir', tree.id2path('dir-id'))
4789.25.2 by John Arbash Meinel
Fix 3 tests that meant to skip exec or symlink testing on win32
2136
        if SymlinkFeature.available():
2137
            self.assertEqual('dir/symlink', tree.id2path('symlink-id'))
2138
            self.assertEqual('target', tree.get_symlink_target('symlink-id'))
4526.8.1 by Aaron Bentley
Support committing a TreeTransform to a branch.
2139
4526.8.2 by Aaron Bentley
Proved strict commit handling.
2140
    def test_add_unversioned(self):
2141
        branch, tt = self.get_branch_and_transform()
2142
        tt.new_file('file', tt.root, 'contents')
2143
        self.assertRaises(errors.StrictCommitFailed, tt.commit, branch,
2144
                          'message', strict=True)
2145
2146
    def test_modify_strict(self):
2147
        branch, tt = self.get_branch_and_transform()
2148
        tt.new_file('file', tt.root, 'contents', 'file-id')
2149
        tt.commit(branch, 'message', strict=True)
2150
        tt = TransformPreview(branch.basis_tree())
4659.2.4 by Vincent Ladeuil
Cleanup remaining bzr-limbo-XXXXXX leaks in /tmp during selftest.
2151
        self.addCleanup(tt.finalize)
4526.8.2 by Aaron Bentley
Proved strict commit handling.
2152
        trans_id = tt.trans_id_file_id('file-id')
2153
        tt.delete_contents(trans_id)
2154
        tt.create_file('contents', trans_id)
2155
        tt.commit(branch, 'message', strict=True)
2156
4526.8.6 by Aaron Bentley
Check for malformed transforms before committing.
2157
    def test_commit_malformed(self):
2158
        """Committing a malformed transform should raise an exception.
2159
2160
        In this case, we are adding a file without adding its parent.
2161
        """
2162
        branch, tt = self.get_branch_and_transform()
2163
        parent_id = tt.trans_id_file_id('parent-id')
2164
        tt.new_file('file', parent_id, 'contents', 'file-id')
2165
        self.assertRaises(errors.MalformedTransform, tt.commit, branch,
2166
                          'message')
2167
5162.4.1 by Aaron Bentley
TreeTransform supports normal commit parameters and includes branch nick.
2168
    def test_commit_rich_revision_data(self):
2169
        branch, tt = self.get_branch_and_transform()
5162.4.3 by Aaron Bentley
Fix failing test.
2170
        rev_id = tt.commit(branch, 'message', timestamp=1, timezone=43201,
5162.4.1 by Aaron Bentley
TreeTransform supports normal commit parameters and includes branch nick.
2171
                           committer='me <me@example.com>',
2172
                           revprops={'foo': 'bar'}, revision_id='revid-1',
2173
                           authors=['Author1 <author1@example.com>',
2174
                              'Author2 <author2@example.com>',
2175
                               ])
2176
        self.assertEqual('revid-1', rev_id)
2177
        revision = branch.repository.get_revision(rev_id)
2178
        self.assertEqual(1, revision.timestamp)
5162.4.3 by Aaron Bentley
Fix failing test.
2179
        self.assertEqual(43201, revision.timezone)
5162.4.1 by Aaron Bentley
TreeTransform supports normal commit parameters and includes branch nick.
2180
        self.assertEqual('me <me@example.com>', revision.committer)
2181
        self.assertEqual(['Author1 <author1@example.com>',
2182
                          'Author2 <author2@example.com>'],
2183
                         revision.get_apparent_authors())
2184
        del revision.properties['authors']
2185
        self.assertEqual({'foo': 'bar',
2186
                          'branch-nick': 'tree'},
2187
                         revision.properties)
2188
2189
    def test_no_explicit_revprops(self):
2190
        branch, tt = self.get_branch_and_transform()
2191
        rev_id = tt.commit(branch, 'message', authors=[
2192
            'Author1 <author1@example.com>',
2193
            'Author2 <author2@example.com>', ])
2194
        revision = branch.repository.get_revision(rev_id)
2195
        self.assertEqual(['Author1 <author1@example.com>',
2196
                          'Author2 <author2@example.com>'],
2197
                         revision.get_apparent_authors())
2198
        self.assertEqual('tree', revision.properties['branch-nick'])
2199
4526.8.1 by Aaron Bentley
Support committing a TreeTransform to a branch.
2200
5409.1.11 by Vincent Ladeuil
Slightly cleanup bzrlib.transform and deprecate unused code.
2201
class TestBackupName(tests.TestCase):
2202
2203
    def test_deprecations(self):
2204
        class MockTransform(object):
2205
2206
            def has_named_child(self, by_parent, parent_id, name):
2207
                return name in by_parent.get(parent_id, [])
2208
2209
        class MockEntry(object):
2210
2211
            def __init__(self):
2212
                object.__init__(self)
2213
                self.name = "name"
2214
1534.10.28 by Aaron Bentley
Use numbered backup files
2215
        tt = MockTransform()
5409.1.11 by Vincent Ladeuil
Slightly cleanup bzrlib.transform and deprecate unused code.
2216
        name1 = self.applyDeprecated(
5409.8.4 by Vincent Ladeuil
Fix indentations.
2217
            symbol_versioning.deprecated_in((2, 3, 0)),
2218
            transform.get_backup_name, MockEntry(), {'a':[]}, 'a', tt)
5409.1.11 by Vincent Ladeuil
Slightly cleanup bzrlib.transform and deprecate unused code.
2219
        self.assertEqual('name.~1~', name1)
2220
        name2 = self.applyDeprecated(
5409.8.4 by Vincent Ladeuil
Fix indentations.
2221
            symbol_versioning.deprecated_in((2, 3, 0)),
2222
            transform._get_backup_name, 'name', {'a':['name.~1~']}, 'a', tt)
5409.1.11 by Vincent Ladeuil
Slightly cleanup bzrlib.transform and deprecate unused code.
2223
        self.assertEqual('name.~2~', name2)
2733.2.1 by Aaron Bentley
Implement FileMover, to support TreeTransform rollback
2224
2225
2226
class TestFileMover(tests.TestCaseWithTransport):
2227
2228
    def test_file_mover(self):
2229
        self.build_tree(['a/', 'a/b', 'c/', 'c/d'])
2230
        mover = _FileMover()
2231
        mover.rename('a', 'q')
2232
        self.failUnlessExists('q')
2233
        self.failIfExists('a')
2733.2.12 by Aaron Bentley
Updates from review
2234
        self.failUnlessExists('q/b')
2235
        self.failUnlessExists('c')
2236
        self.failUnlessExists('c/d')
2733.2.1 by Aaron Bentley
Implement FileMover, to support TreeTransform rollback
2237
2733.2.5 by Aaron Bentley
Implement FileMover.pre_delete and FileMover.apply_deletions
2238
    def test_pre_delete_rollback(self):
2239
        self.build_tree(['a/'])
2240
        mover = _FileMover()
2241
        mover.pre_delete('a', 'q')
2242
        self.failUnlessExists('q')
2243
        self.failIfExists('a')
2244
        mover.rollback()
2245
        self.failIfExists('q')
2246
        self.failUnlessExists('a')
2247
2248
    def test_apply_deletions(self):
2733.2.12 by Aaron Bentley
Updates from review
2249
        self.build_tree(['a/', 'b/'])
2733.2.5 by Aaron Bentley
Implement FileMover.pre_delete and FileMover.apply_deletions
2250
        mover = _FileMover()
2251
        mover.pre_delete('a', 'q')
2733.2.12 by Aaron Bentley
Updates from review
2252
        mover.pre_delete('b', 'r')
2733.2.5 by Aaron Bentley
Implement FileMover.pre_delete and FileMover.apply_deletions
2253
        self.failUnlessExists('q')
2733.2.12 by Aaron Bentley
Updates from review
2254
        self.failUnlessExists('r')
2733.2.5 by Aaron Bentley
Implement FileMover.pre_delete and FileMover.apply_deletions
2255
        self.failIfExists('a')
2733.2.12 by Aaron Bentley
Updates from review
2256
        self.failIfExists('b')
2733.2.5 by Aaron Bentley
Implement FileMover.pre_delete and FileMover.apply_deletions
2257
        mover.apply_deletions()
2258
        self.failIfExists('q')
2733.2.12 by Aaron Bentley
Updates from review
2259
        self.failIfExists('r')
2733.2.5 by Aaron Bentley
Implement FileMover.pre_delete and FileMover.apply_deletions
2260
        self.failIfExists('a')
2733.2.12 by Aaron Bentley
Updates from review
2261
        self.failIfExists('b')
2733.2.5 by Aaron Bentley
Implement FileMover.pre_delete and FileMover.apply_deletions
2262
2733.2.1 by Aaron Bentley
Implement FileMover, to support TreeTransform rollback
2263
    def test_file_mover_rollback(self):
2264
        self.build_tree(['a/', 'a/b', 'c/', 'c/d/', 'c/e/'])
2265
        mover = _FileMover()
2266
        mover.rename('c/d', 'c/f')
2267
        mover.rename('c/e', 'c/d')
2268
        try:
2269
            mover.rename('a', 'c')
3063.1.3 by Aaron Bentley
Update for Linux
2270
        except errors.FileExists, e:
2733.2.1 by Aaron Bentley
Implement FileMover, to support TreeTransform rollback
2271
            mover.rollback()
2272
        self.failUnlessExists('a')
2273
        self.failUnlessExists('c/d')
2733.2.3 by Aaron Bentley
Test tranform rollback
2274
2275
2276
class Bogus(Exception):
2277
    pass
2278
2279
2280
class TestTransformRollback(tests.TestCaseWithTransport):
2281
2282
    class ExceptionFileMover(_FileMover):
2283
2733.2.4 by Aaron Bentley
Test transform rollback when renaming into place
2284
        def __init__(self, bad_source=None, bad_target=None):
2285
            _FileMover.__init__(self)
2286
            self.bad_source = bad_source
2287
            self.bad_target = bad_target
2288
2733.2.3 by Aaron Bentley
Test tranform rollback
2289
        def rename(self, source, target):
2733.2.4 by Aaron Bentley
Test transform rollback when renaming into place
2290
            if (self.bad_source is not None and
2291
                source.endswith(self.bad_source)):
2292
                raise Bogus
2293
            elif (self.bad_target is not None and
2294
                target.endswith(self.bad_target)):
2733.2.3 by Aaron Bentley
Test tranform rollback
2295
                raise Bogus
2296
            else:
2297
                _FileMover.rename(self, source, target)
2298
2299
    def test_rollback_rename(self):
2300
        tree = self.make_branch_and_tree('.')
2301
        self.build_tree(['a/', 'a/b'])
2302
        tt = TreeTransform(tree)
2303
        self.addCleanup(tt.finalize)
2304
        a_id = tt.trans_id_tree_path('a')
2305
        tt.adjust_path('c', tt.root, a_id)
2306
        tt.adjust_path('d', a_id, tt.trans_id_tree_path('a/b'))
2733.2.4 by Aaron Bentley
Test transform rollback when renaming into place
2307
        self.assertRaises(Bogus, tt.apply,
2308
                          _mover=self.ExceptionFileMover(bad_source='a'))
2309
        self.failUnlessExists('a')
2310
        self.failUnlessExists('a/b')
2311
        tt.apply()
2312
        self.failUnlessExists('c')
2313
        self.failUnlessExists('c/d')
2314
2315
    def test_rollback_rename_into_place(self):
2316
        tree = self.make_branch_and_tree('.')
2317
        self.build_tree(['a/', 'a/b'])
2318
        tt = TreeTransform(tree)
2319
        self.addCleanup(tt.finalize)
2320
        a_id = tt.trans_id_tree_path('a')
2321
        tt.adjust_path('c', tt.root, a_id)
2322
        tt.adjust_path('d', a_id, tt.trans_id_tree_path('a/b'))
2323
        self.assertRaises(Bogus, tt.apply,
2324
                          _mover=self.ExceptionFileMover(bad_target='c/d'))
2325
        self.failUnlessExists('a')
2326
        self.failUnlessExists('a/b')
2327
        tt.apply()
2328
        self.failUnlessExists('c')
2329
        self.failUnlessExists('c/d')
2733.2.6 by Aaron Bentley
Make TreeTransform commits rollbackable
2330
2331
    def test_rollback_deletion(self):
2332
        tree = self.make_branch_and_tree('.')
2333
        self.build_tree(['a/', 'a/b'])
2334
        tt = TreeTransform(tree)
2335
        self.addCleanup(tt.finalize)
2336
        a_id = tt.trans_id_tree_path('a')
2337
        tt.delete_contents(a_id)
2338
        tt.adjust_path('d', tt.root, tt.trans_id_tree_path('a/b'))
2339
        self.assertRaises(Bogus, tt.apply,
2340
                          _mover=self.ExceptionFileMover(bad_target='d'))
2341
        self.failUnlessExists('a')
2342
        self.failUnlessExists('a/b')
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
2343
2344
5409.1.6 by Vincent Ladeuil
Add failing tests for bug #323111.
2345
class TestTransformMissingParent(tests.TestCaseWithTransport):
2346
5409.7.4 by Vincent Ladeuil
Take jam's review comments into account.
2347
    def make_tt_with_versioned_dir(self):
5409.1.6 by Vincent Ladeuil
Add failing tests for bug #323111.
2348
        wt = self.make_branch_and_tree('.')
2349
        self.build_tree(['dir/',])
2350
        wt.add(['dir'], ['dir-id'])
2351
        wt.commit('Create dir')
2352
        tt = TreeTransform(wt)
2353
        self.addCleanup(tt.finalize)
2354
        return wt, tt
2355
2356
    def test_resolve_create_parent_for_versioned_file(self):
5409.7.4 by Vincent Ladeuil
Take jam's review comments into account.
2357
        wt, tt = self.make_tt_with_versioned_dir()
5409.1.6 by Vincent Ladeuil
Add failing tests for bug #323111.
2358
        dir_tid = tt.trans_id_tree_file_id('dir-id')
2359
        file_tid = tt.new_file('file', dir_tid, 'Contents', file_id='file-id')
2360
        tt.delete_contents(dir_tid)
2361
        tt.unversion_file(dir_tid)
2362
        conflicts = resolve_conflicts(tt)
2363
        # one conflict for the missing directory, one for the unversioned
2364
        # parent
2365
        self.assertLength(2, conflicts)
2366
5409.1.20 by Vincent Ladeuil
Revert to 'conflict' being the default orphaning policy and fix fallouts.
2367
    def test_non_versioned_file_create_conflict(self):
5409.7.4 by Vincent Ladeuil
Take jam's review comments into account.
2368
        wt, tt = self.make_tt_with_versioned_dir()
5409.1.6 by Vincent Ladeuil
Add failing tests for bug #323111.
2369
        dir_tid = tt.trans_id_tree_file_id('dir-id')
5409.1.7 by Vincent Ladeuil
First orphaning implementation (some tests lacking).
2370
        tt.new_file('file', dir_tid, 'Contents')
5409.1.6 by Vincent Ladeuil
Add failing tests for bug #323111.
2371
        tt.delete_contents(dir_tid)
2372
        tt.unversion_file(dir_tid)
2373
        conflicts = resolve_conflicts(tt)
5409.7.2 by Vincent Ladeuil
Add NEWS entry, a missing test and some cleanup.
2374
        # no conflicts or rather: orphaning 'file' resolve the 'dir' conflict
5409.1.20 by Vincent Ladeuil
Revert to 'conflict' being the default orphaning policy and fix fallouts.
2375
        self.assertLength(1, conflicts)
2376
        self.assertEqual(('deleting parent', 'Not deleting', 'new-1'),
2377
                         conflicts.pop())
5409.1.6 by Vincent Ladeuil
Add failing tests for bug #323111.
2378
2379
3363.19.1 by Aaron Bentley
Make PreviewTree.iter_changes accept all options.
2380
A_ENTRY = ('a-id', ('a', 'a'), True, (True, True),
2381
                  ('TREE_ROOT', 'TREE_ROOT'), ('a', 'a'), ('file', 'file'),
2382
                  (False, False))
2383
ROOT_ENTRY = ('TREE_ROOT', ('', ''), False, (True, True), (None, None),
5050.57.1 by Aaron Bentley
Make is_executable treat symlinks and directories the same across tree types.
2384
              ('', ''), ('directory', 'directory'), (False, False))
3363.19.1 by Aaron Bentley
Make PreviewTree.iter_changes accept all options.
2385
2386
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
2387
class TestTransformPreview(tests.TestCaseWithTransport):
2388
2389
    def create_tree(self):
2390
        tree = self.make_branch_and_tree('.')
2391
        self.build_tree_contents([('a', 'content 1')])
4600.3.1 by Robert Collins
Set tree root ID in tree transform tests that don't care about the root id.
2392
        tree.set_root_id('TREE_ROOT')
3008.2.1 by Aaron Bentley
Ensure conflict resolution works
2393
        tree.add('a', 'a-id')
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
2394
        tree.commit('rev1', rev_id='rev1')
2395
        return tree.branch.repository.revision_tree('rev1')
2396
3008.1.18 by Aaron Bentley
Get supported PreviewTree functionality under test
2397
    def get_empty_preview(self):
2398
        repository = self.make_repository('repo')
2399
        tree = repository.revision_tree(_mod_revision.NULL_REVISION)
3199.1.4 by Vincent Ladeuil
Fix 16 leaked tmp dirs. Probably indicates a lock handling problem with TransformPreview
2400
        preview = TransformPreview(tree)
3199.1.5 by Vincent Ladeuil
Fix two more leaking tmp dirs, by reworking TransformPreview lock handling.
2401
        self.addCleanup(preview.finalize)
3199.1.4 by Vincent Ladeuil
Fix 16 leaked tmp dirs. Probably indicates a lock handling problem with TransformPreview
2402
        return preview
2403
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
2404
    def test_transform_preview(self):
2405
        revision_tree = self.create_tree()
2406
        preview = TransformPreview(revision_tree)
3199.1.5 by Vincent Ladeuil
Fix two more leaking tmp dirs, by reworking TransformPreview lock handling.
2407
        self.addCleanup(preview.finalize)
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
2408
2409
    def test_transform_preview_tree(self):
2410
        revision_tree = self.create_tree()
2411
        preview = TransformPreview(revision_tree)
3199.1.5 by Vincent Ladeuil
Fix two more leaking tmp dirs, by reworking TransformPreview lock handling.
2412
        self.addCleanup(preview.finalize)
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
2413
        preview.get_preview_tree()
2414
3008.1.5 by Michael Hudson
a more precise test
2415
    def test_transform_new_file(self):
2416
        revision_tree = self.create_tree()
2417
        preview = TransformPreview(revision_tree)
3199.1.5 by Vincent Ladeuil
Fix two more leaking tmp dirs, by reworking TransformPreview lock handling.
2418
        self.addCleanup(preview.finalize)
3008.1.5 by Michael Hudson
a more precise test
2419
        preview.new_file('file2', preview.root, 'content B\n', 'file2-id')
2420
        preview_tree = preview.get_preview_tree()
2421
        self.assertEqual(preview_tree.kind('file2-id'), 'file')
2422
        self.assertEqual(
2423
            preview_tree.get_file('file2-id').read(), 'content B\n')
2424
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
2425
    def test_diff_preview_tree(self):
2426
        revision_tree = self.create_tree()
2427
        preview = TransformPreview(revision_tree)
3199.1.5 by Vincent Ladeuil
Fix two more leaking tmp dirs, by reworking TransformPreview lock handling.
2428
        self.addCleanup(preview.finalize)
3008.1.4 by Michael Hudson
Merge test enhancements
2429
        preview.new_file('file2', preview.root, 'content B\n', 'file2-id')
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
2430
        preview_tree = preview.get_preview_tree()
2431
        out = StringIO()
2432
        show_diff_trees(revision_tree, preview_tree, out)
3008.1.4 by Michael Hudson
Merge test enhancements
2433
        lines = out.getvalue().splitlines()
2434
        self.assertEqual(lines[0], "=== added file 'file2'")
2435
        # 3 lines of diff administrivia
2436
        self.assertEqual(lines[4], "+content B")
3008.2.1 by Aaron Bentley
Ensure conflict resolution works
2437
2438
    def test_transform_conflicts(self):
2439
        revision_tree = self.create_tree()
2440
        preview = TransformPreview(revision_tree)
3199.1.5 by Vincent Ladeuil
Fix two more leaking tmp dirs, by reworking TransformPreview lock handling.
2441
        self.addCleanup(preview.finalize)
3008.2.1 by Aaron Bentley
Ensure conflict resolution works
2442
        preview.new_file('a', preview.root, 'content 2')
2443
        resolve_conflicts(preview)
2444
        trans_id = preview.trans_id_file_id('a-id')
2445
        self.assertEqual('a.moved', preview.final_name(trans_id))
3008.1.17 by Aaron Bentley
Test unused parameters of preview_tree._iter_changes
2446
3008.1.31 by Aaron Bentley
Split PreviewTree._iter_changes parameter tests into smaller tests
2447
    def get_tree_and_preview_tree(self):
3008.1.17 by Aaron Bentley
Test unused parameters of preview_tree._iter_changes
2448
        revision_tree = self.create_tree()
2449
        preview = TransformPreview(revision_tree)
3199.1.5 by Vincent Ladeuil
Fix two more leaking tmp dirs, by reworking TransformPreview lock handling.
2450
        self.addCleanup(preview.finalize)
3008.1.17 by Aaron Bentley
Test unused parameters of preview_tree._iter_changes
2451
        a_trans_id = preview.trans_id_file_id('a-id')
2452
        preview.delete_contents(a_trans_id)
2453
        preview.create_file('b content', a_trans_id)
2454
        preview_tree = preview.get_preview_tree()
3008.1.31 by Aaron Bentley
Split PreviewTree._iter_changes parameter tests into smaller tests
2455
        return revision_tree, preview_tree
2456
2457
    def test_iter_changes(self):
2458
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
2459
        root = revision_tree.inventory.root.file_id
3008.1.17 by Aaron Bentley
Test unused parameters of preview_tree._iter_changes
2460
        self.assertEqual([('a-id', ('a', 'a'), True, (True, True),
2461
                          (root, root), ('a', 'a'), ('file', 'file'),
2462
                          (False, False))],
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
2463
                          list(preview_tree.iter_changes(revision_tree)))
3008.1.31 by Aaron Bentley
Split PreviewTree._iter_changes parameter tests into smaller tests
2464
3363.19.1 by Aaron Bentley
Make PreviewTree.iter_changes accept all options.
2465
    def test_include_unchanged_succeeds(self):
3008.1.31 by Aaron Bentley
Split PreviewTree._iter_changes parameter tests into smaller tests
2466
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
3363.19.1 by Aaron Bentley
Make PreviewTree.iter_changes accept all options.
2467
        changes = preview_tree.iter_changes(revision_tree,
2468
                                            include_unchanged=True)
2469
        root = revision_tree.inventory.root.file_id
2470
2471
        self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
3008.1.31 by Aaron Bentley
Split PreviewTree._iter_changes parameter tests into smaller tests
2472
2473
    def test_specific_files(self):
2474
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
3363.19.1 by Aaron Bentley
Make PreviewTree.iter_changes accept all options.
2475
        changes = preview_tree.iter_changes(revision_tree,
2476
                                            specific_files=[''])
5050.57.1 by Aaron Bentley
Make is_executable treat symlinks and directories the same across tree types.
2477
        self.assertEqual([A_ENTRY], list(changes))
3008.1.31 by Aaron Bentley
Split PreviewTree._iter_changes parameter tests into smaller tests
2478
3363.19.1 by Aaron Bentley
Make PreviewTree.iter_changes accept all options.
2479
    def test_want_unversioned(self):
3008.1.31 by Aaron Bentley
Split PreviewTree._iter_changes parameter tests into smaller tests
2480
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
3363.19.1 by Aaron Bentley
Make PreviewTree.iter_changes accept all options.
2481
        changes = preview_tree.iter_changes(revision_tree,
2482
                                            want_unversioned=True)
5050.57.1 by Aaron Bentley
Make is_executable treat symlinks and directories the same across tree types.
2483
        self.assertEqual([A_ENTRY], list(changes))
3008.1.17 by Aaron Bentley
Test unused parameters of preview_tree._iter_changes
2484
3008.1.31 by Aaron Bentley
Split PreviewTree._iter_changes parameter tests into smaller tests
2485
    def test_ignore_extra_trees_no_specific_files(self):
3008.1.17 by Aaron Bentley
Test unused parameters of preview_tree._iter_changes
2486
        # extra_trees is harmless without specific_files, so we'll silently
2487
        # accept it, even though we won't use it.
3008.1.31 by Aaron Bentley
Split PreviewTree._iter_changes parameter tests into smaller tests
2488
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
2489
        preview_tree.iter_changes(revision_tree, extra_trees=[preview_tree])
3008.1.31 by Aaron Bentley
Split PreviewTree._iter_changes parameter tests into smaller tests
2490
2491
    def test_ignore_require_versioned_no_specific_files(self):
3008.1.17 by Aaron Bentley
Test unused parameters of preview_tree._iter_changes
2492
        # require_versioned is meaningless without specific_files.
3008.1.31 by Aaron Bentley
Split PreviewTree._iter_changes parameter tests into smaller tests
2493
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
2494
        preview_tree.iter_changes(revision_tree, require_versioned=False)
3008.1.31 by Aaron Bentley
Split PreviewTree._iter_changes parameter tests into smaller tests
2495
2496
    def test_ignore_pb(self):
3008.1.17 by Aaron Bentley
Test unused parameters of preview_tree._iter_changes
2497
        # pb could be supported, but TT.iter_changes doesn't support it.
3008.1.31 by Aaron Bentley
Split PreviewTree._iter_changes parameter tests into smaller tests
2498
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
4961.2.9 by Martin Pool
Rip out most remaining uses of DummyProgressBar
2499
        preview_tree.iter_changes(revision_tree)
3008.1.18 by Aaron Bentley
Get supported PreviewTree functionality under test
2500
2501
    def test_kind(self):
2502
        revision_tree = self.create_tree()
2503
        preview = TransformPreview(revision_tree)
3199.1.5 by Vincent Ladeuil
Fix two more leaking tmp dirs, by reworking TransformPreview lock handling.
2504
        self.addCleanup(preview.finalize)
3008.1.18 by Aaron Bentley
Get supported PreviewTree functionality under test
2505
        preview.new_file('file', preview.root, 'contents', 'file-id')
2506
        preview.new_directory('directory', preview.root, 'dir-id')
2507
        preview_tree = preview.get_preview_tree()
2508
        self.assertEqual('file', preview_tree.kind('file-id'))
2509
        self.assertEqual('directory', preview_tree.kind('dir-id'))
2510
2511
    def test_get_file_mtime(self):
2512
        preview = self.get_empty_preview()
2513
        file_trans_id = preview.new_file('file', preview.root, 'contents',
2514
                                         'file-id')
2515
        limbo_path = preview._limbo_name(file_trans_id)
2516
        preview_tree = preview.get_preview_tree()
2517
        self.assertEqual(os.stat(limbo_path).st_mtime,
2518
                         preview_tree.get_file_mtime('file-id'))
2519
4635.1.1 by Aaron Bentley
Fix OSError with renamed files in PreviewTree.
2520
    def test_get_file_mtime_renamed(self):
2521
        work_tree = self.make_branch_and_tree('tree')
2522
        self.build_tree(['tree/file'])
2523
        work_tree.add('file', 'file-id')
2524
        preview = TransformPreview(work_tree)
2525
        self.addCleanup(preview.finalize)
2526
        file_trans_id = preview.trans_id_tree_file_id('file-id')
2527
        preview.adjust_path('renamed', preview.root, file_trans_id)
2528
        preview_tree = preview.get_preview_tree()
2529
        preview_mtime = preview_tree.get_file_mtime('file-id', 'renamed')
2530
        work_mtime = work_tree.get_file_mtime('file-id', 'file')
2531
3008.1.18 by Aaron Bentley
Get supported PreviewTree functionality under test
2532
    def test_get_file(self):
2533
        preview = self.get_empty_preview()
2534
        preview.new_file('file', preview.root, 'contents', 'file-id')
2535
        preview_tree = preview.get_preview_tree()
2536
        tree_file = preview_tree.get_file('file-id')
2537
        try:
2538
            self.assertEqual('contents', tree_file.read())
2539
        finally:
2540
            tree_file.close()
3228.1.2 by James Henstridge
Simplify test, and move it down to be next to the other _PreviewTree tests.
2541
2542
    def test_get_symlink_target(self):
2543
        self.requireFeature(SymlinkFeature)
2544
        preview = self.get_empty_preview()
2545
        preview.new_symlink('symlink', preview.root, 'target', 'symlink-id')
2546
        preview_tree = preview.get_preview_tree()
2547
        self.assertEqual('target',
2548
                         preview_tree.get_symlink_target('symlink-id'))
3363.2.18 by Aaron Bentley
Implement correct all_file_ids for PreviewTree
2549
2550
    def test_all_file_ids(self):
2551
        tree = self.make_branch_and_tree('tree')
2552
        self.build_tree(['tree/a', 'tree/b', 'tree/c'])
2553
        tree.add(['a', 'b', 'c'], ['a-id', 'b-id', 'c-id'])
2554
        preview = TransformPreview(tree)
2555
        self.addCleanup(preview.finalize)
2556
        preview.unversion_file(preview.trans_id_file_id('b-id'))
2557
        c_trans_id = preview.trans_id_file_id('c-id')
2558
        preview.unversion_file(c_trans_id)
2559
        preview.version_file('c-id', c_trans_id)
2560
        preview_tree = preview.get_preview_tree()
2561
        self.assertEqual(set(['a-id', 'c-id', tree.get_root_id()]),
2562
                         preview_tree.all_file_ids())
3363.2.19 by Aaron Bentley
Make PreviewTree.path2id correct
2563
2564
    def test_path2id_deleted_unchanged(self):
2565
        tree = self.make_branch_and_tree('tree')
2566
        self.build_tree(['tree/unchanged', 'tree/deleted'])
2567
        tree.add(['unchanged', 'deleted'], ['unchanged-id', 'deleted-id'])
2568
        preview = TransformPreview(tree)
2569
        self.addCleanup(preview.finalize)
2570
        preview.unversion_file(preview.trans_id_file_id('deleted-id'))
2571
        preview_tree = preview.get_preview_tree()
2572
        self.assertEqual('unchanged-id', preview_tree.path2id('unchanged'))
2573
        self.assertIs(None, preview_tree.path2id('deleted'))
2574
2575
    def test_path2id_created(self):
2576
        tree = self.make_branch_and_tree('tree')
2577
        self.build_tree(['tree/unchanged'])
2578
        tree.add(['unchanged'], ['unchanged-id'])
2579
        preview = TransformPreview(tree)
2580
        self.addCleanup(preview.finalize)
2581
        preview.new_file('new', preview.trans_id_file_id('unchanged-id'),
2582
            'contents', 'new-id')
2583
        preview_tree = preview.get_preview_tree()
2584
        self.assertEqual('new-id', preview_tree.path2id('unchanged/new'))
2585
2586
    def test_path2id_moved(self):
2587
        tree = self.make_branch_and_tree('tree')
2588
        self.build_tree(['tree/old_parent/', 'tree/old_parent/child'])
2589
        tree.add(['old_parent', 'old_parent/child'],
2590
                 ['old_parent-id', 'child-id'])
2591
        preview = TransformPreview(tree)
2592
        self.addCleanup(preview.finalize)
2593
        new_parent = preview.new_directory('new_parent', preview.root,
2594
                                           'new_parent-id')
2595
        preview.adjust_path('child', new_parent,
2596
                            preview.trans_id_file_id('child-id'))
2597
        preview_tree = preview.get_preview_tree()
2598
        self.assertIs(None, preview_tree.path2id('old_parent/child'))
2599
        self.assertEqual('child-id', preview_tree.path2id('new_parent/child'))
2600
2601
    def test_path2id_renamed_parent(self):
2602
        tree = self.make_branch_and_tree('tree')
2603
        self.build_tree(['tree/old_name/', 'tree/old_name/child'])
2604
        tree.add(['old_name', 'old_name/child'],
2605
                 ['parent-id', 'child-id'])
2606
        preview = TransformPreview(tree)
2607
        self.addCleanup(preview.finalize)
2608
        preview.adjust_path('new_name', preview.root,
2609
                            preview.trans_id_file_id('parent-id'))
2610
        preview_tree = preview.get_preview_tree()
2611
        self.assertIs(None, preview_tree.path2id('old_name/child'))
2612
        self.assertEqual('child-id', preview_tree.path2id('new_name/child'))
3363.2.21 by Aaron Bentley
Implement iter_entries_by_dir
2613
2614
    def assertMatchingIterEntries(self, tt, specific_file_ids=None):
2615
        preview_tree = tt.get_preview_tree()
2616
        preview_result = list(preview_tree.iter_entries_by_dir(
2617
                              specific_file_ids))
2618
        tree = tt._tree
2619
        tt.apply()
2620
        actual_result = list(tree.iter_entries_by_dir(specific_file_ids))
2621
        self.assertEqual(actual_result, preview_result)
2622
2623
    def test_iter_entries_by_dir_new(self):
2624
        tree = self.make_branch_and_tree('tree')
2625
        tt = TreeTransform(tree)
2626
        tt.new_file('new', tt.root, 'contents', 'new-id')
2627
        self.assertMatchingIterEntries(tt)
2628
2629
    def test_iter_entries_by_dir_deleted(self):
2630
        tree = self.make_branch_and_tree('tree')
2631
        self.build_tree(['tree/deleted'])
2632
        tree.add('deleted', 'deleted-id')
2633
        tt = TreeTransform(tree)
2634
        tt.delete_contents(tt.trans_id_file_id('deleted-id'))
2635
        self.assertMatchingIterEntries(tt)
2636
2637
    def test_iter_entries_by_dir_unversioned(self):
2638
        tree = self.make_branch_and_tree('tree')
2639
        self.build_tree(['tree/removed'])
2640
        tree.add('removed', 'removed-id')
2641
        tt = TreeTransform(tree)
2642
        tt.unversion_file(tt.trans_id_file_id('removed-id'))
2643
        self.assertMatchingIterEntries(tt)
2644
2645
    def test_iter_entries_by_dir_moved(self):
2646
        tree = self.make_branch_and_tree('tree')
2647
        self.build_tree(['tree/moved', 'tree/new_parent/'])
2648
        tree.add(['moved', 'new_parent'], ['moved-id', 'new_parent-id'])
2649
        tt = TreeTransform(tree)
2650
        tt.adjust_path('moved', tt.trans_id_file_id('new_parent-id'),
2651
                       tt.trans_id_file_id('moved-id'))
2652
        self.assertMatchingIterEntries(tt)
2653
2654
    def test_iter_entries_by_dir_specific_file_ids(self):
2655
        tree = self.make_branch_and_tree('tree')
2656
        tree.set_root_id('tree-root-id')
2657
        self.build_tree(['tree/parent/', 'tree/parent/child'])
2658
        tree.add(['parent', 'parent/child'], ['parent-id', 'child-id'])
2659
        tt = TreeTransform(tree)
2660
        self.assertMatchingIterEntries(tt, ['tree-root-id', 'child-id'])
3363.2.26 by Aaron Bentley
Get symlinks working
2661
2662
    def test_symlink_content_summary(self):
2663
        self.requireFeature(SymlinkFeature)
2664
        preview = self.get_empty_preview()
2665
        preview.new_symlink('path', preview.root, 'target', 'path-id')
2666
        summary = preview.get_preview_tree().path_content_summary('path')
2667
        self.assertEqual(('symlink', None, None, 'target'), summary)
3363.2.27 by Aaron Bentley
Make path_content_summary a core API
2668
2669
    def test_missing_content_summary(self):
2670
        preview = self.get_empty_preview()
2671
        summary = preview.get_preview_tree().path_content_summary('path')
2672
        self.assertEqual(('missing', None, None, None), summary)
2673
2674
    def test_deleted_content_summary(self):
2675
        tree = self.make_branch_and_tree('tree')
2676
        self.build_tree(['tree/path/'])
2677
        tree.add('path')
2678
        preview = TransformPreview(tree)
2679
        self.addCleanup(preview.finalize)
2680
        preview.delete_contents(preview.trans_id_tree_path('path'))
2681
        summary = preview.get_preview_tree().path_content_summary('path')
2682
        self.assertEqual(('missing', None, None, None), summary)
2683
3363.2.30 by Aaron Bentley
Improve execute bit testing
2684
    def test_file_content_summary_executable(self):
2685
        preview = self.get_empty_preview()
2686
        path_id = preview.new_file('path', preview.root, 'contents', 'path-id')
2687
        preview.set_executability(True, path_id)
2688
        summary = preview.get_preview_tree().path_content_summary('path')
2689
        self.assertEqual(4, len(summary))
2690
        self.assertEqual('file', summary[0])
2691
        # size must be known
2692
        self.assertEqual(len('contents'), summary[1])
2693
        # executable
2694
        self.assertEqual(True, summary[2])
3363.2.31 by Aaron Bentley
Tweak tests
2695
        # will not have hash (not cheap to determine)
2696
        self.assertIs(None, summary[3])
3363.2.30 by Aaron Bentley
Improve execute bit testing
2697
2698
    def test_change_executability(self):
2699
        tree = self.make_branch_and_tree('tree')
2700
        self.build_tree(['tree/path'])
2701
        tree.add('path')
2702
        preview = TransformPreview(tree)
2703
        self.addCleanup(preview.finalize)
2704
        path_id = preview.trans_id_tree_path('path')
2705
        preview.set_executability(True, path_id)
2706
        summary = preview.get_preview_tree().path_content_summary('path')
2707
        self.assertEqual(True, summary[2])
2708
3363.2.27 by Aaron Bentley
Make path_content_summary a core API
2709
    def test_file_content_summary_non_exec(self):
2710
        preview = self.get_empty_preview()
2711
        preview.new_file('path', preview.root, 'contents', 'path-id')
2712
        summary = preview.get_preview_tree().path_content_summary('path')
2713
        self.assertEqual(4, len(summary))
2714
        self.assertEqual('file', summary[0])
2715
        # size must be known
3363.2.30 by Aaron Bentley
Improve execute bit testing
2716
        self.assertEqual(len('contents'), summary[1])
3363.2.27 by Aaron Bentley
Make path_content_summary a core API
2717
        # not executable
4789.15.3 by John Arbash Meinel
PreviewTree now returns False for exec bit properly.
2718
        self.assertEqual(False, summary[2])
3363.2.31 by Aaron Bentley
Tweak tests
2719
        # will not have hash (not cheap to determine)
2720
        self.assertIs(None, summary[3])
3363.2.27 by Aaron Bentley
Make path_content_summary a core API
2721
2722
    def test_dir_content_summary(self):
2723
        preview = self.get_empty_preview()
2724
        preview.new_directory('path', preview.root, 'path-id')
2725
        summary = preview.get_preview_tree().path_content_summary('path')
2726
        self.assertEqual(('directory', None, None, None), summary)
2727
2728
    def test_tree_content_summary(self):
2729
        preview = self.get_empty_preview()
2730
        path = preview.new_directory('path', preview.root, 'path-id')
2731
        preview.set_tree_reference('rev-1', path)
2732
        summary = preview.get_preview_tree().path_content_summary('path')
2733
        self.assertEqual(4, len(summary))
2734
        self.assertEqual('tree-reference', summary[0])
3363.2.33 by Aaron Bentley
Implement PreviewTree.annotate_iter
2735
2736
    def test_annotate(self):
2737
        tree = self.make_branch_and_tree('tree')
2738
        self.build_tree_contents([('tree/file', 'a\n')])
2739
        tree.add('file', 'file-id')
2740
        tree.commit('a', rev_id='one')
2741
        self.build_tree_contents([('tree/file', 'a\nb\n')])
2742
        preview = TransformPreview(tree)
2743
        self.addCleanup(preview.finalize)
2744
        file_trans_id = preview.trans_id_file_id('file-id')
2745
        preview.delete_contents(file_trans_id)
2746
        preview.create_file('a\nb\nc\n', file_trans_id)
2747
        preview_tree = preview.get_preview_tree()
2748
        expected = [
2749
            ('one', 'a\n'),
2750
            ('me:', 'b\n'),
2751
            ('me:', 'c\n'),
2752
        ]
2753
        annotation = preview_tree.annotate_iter('file-id', 'me:')
2754
        self.assertEqual(expected, annotation)
2755
2756
    def test_annotate_missing(self):
2757
        preview = self.get_empty_preview()
2758
        preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
2759
        preview_tree = preview.get_preview_tree()
2760
        expected = [
2761
            ('me:', 'a\n'),
2762
            ('me:', 'b\n'),
2763
            ('me:', 'c\n'),
2764
         ]
2765
        annotation = preview_tree.annotate_iter('file-id', 'me:')
2766
        self.assertEqual(expected, annotation)
2767
3363.7.3 by Aaron Bentley
Add test that annotate correctly handles renames
2768
    def test_annotate_rename(self):
2769
        tree = self.make_branch_and_tree('tree')
2770
        self.build_tree_contents([('tree/file', 'a\n')])
2771
        tree.add('file', 'file-id')
2772
        tree.commit('a', rev_id='one')
2773
        preview = TransformPreview(tree)
2774
        self.addCleanup(preview.finalize)
2775
        file_trans_id = preview.trans_id_file_id('file-id')
2776
        preview.adjust_path('newname', preview.root, file_trans_id)
2777
        preview_tree = preview.get_preview_tree()
2778
        expected = [
2779
            ('one', 'a\n'),
2780
        ]
2781
        annotation = preview_tree.annotate_iter('file-id', 'me:')
2782
        self.assertEqual(expected, annotation)
2783
3363.2.33 by Aaron Bentley
Implement PreviewTree.annotate_iter
2784
    def test_annotate_deleted(self):
2785
        tree = self.make_branch_and_tree('tree')
2786
        self.build_tree_contents([('tree/file', 'a\n')])
2787
        tree.add('file', 'file-id')
2788
        tree.commit('a', rev_id='one')
2789
        self.build_tree_contents([('tree/file', 'a\nb\n')])
2790
        preview = TransformPreview(tree)
2791
        self.addCleanup(preview.finalize)
2792
        file_trans_id = preview.trans_id_file_id('file-id')
2793
        preview.delete_contents(file_trans_id)
2794
        preview_tree = preview.get_preview_tree()
2795
        annotation = preview_tree.annotate_iter('file-id', 'me:')
2796
        self.assertIs(None, annotation)
2797
3363.2.36 by Aaron Bentley
Fix PreviewTree.stored_kind
2798
    def test_stored_kind(self):
2799
        preview = self.get_empty_preview()
2800
        preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
2801
        preview_tree = preview.get_preview_tree()
2802
        self.assertEqual('file', preview_tree.stored_kind('file-id'))
3363.2.37 by Aaron Bentley
Fix is_executable
2803
2804
    def test_is_executable(self):
2805
        preview = self.get_empty_preview()
2806
        preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
2807
        preview.set_executability(True, preview.trans_id_file_id('file-id'))
2808
        preview_tree = preview.get_preview_tree()
2809
        self.assertEqual(True, preview_tree.is_executable('file-id'))
3363.9.1 by Aaron Bentley
Implement plan_merge, refactoring various bits
2810
3571.1.1 by Aaron Bentley
Allow set/get of parent_ids in PreviewTree
2811
    def test_get_set_parent_ids(self):
2812
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
2813
        self.assertEqual([], preview_tree.get_parent_ids())
2814
        preview_tree.set_parent_ids(['rev-1'])
2815
        self.assertEqual(['rev-1'], preview_tree.get_parent_ids())
2816
3363.9.1 by Aaron Bentley
Implement plan_merge, refactoring various bits
2817
    def test_plan_file_merge(self):
2818
        work_a = self.make_branch_and_tree('wta')
2819
        self.build_tree_contents([('wta/file', 'a\nb\nc\nd\n')])
2820
        work_a.add('file', 'file-id')
3363.9.7 by Aaron Bentley
Fix up to use set_parent_ids
2821
        base_id = work_a.commit('base version')
3363.9.1 by Aaron Bentley
Implement plan_merge, refactoring various bits
2822
        tree_b = work_a.bzrdir.sprout('wtb').open_workingtree()
2823
        preview = TransformPreview(work_a)
2824
        self.addCleanup(preview.finalize)
2825
        trans_id = preview.trans_id_file_id('file-id')
2826
        preview.delete_contents(trans_id)
2827
        preview.create_file('b\nc\nd\ne\n', trans_id)
2828
        self.build_tree_contents([('wtb/file', 'a\nc\nd\nf\n')])
2829
        tree_a = preview.get_preview_tree()
3363.9.7 by Aaron Bentley
Fix up to use set_parent_ids
2830
        tree_a.set_parent_ids([base_id])
3363.9.1 by Aaron Bentley
Implement plan_merge, refactoring various bits
2831
        self.assertEqual([
3363.9.5 by Aaron Bentley
Move killed-a from top to bottom
2832
            ('killed-a', 'a\n'),
3363.9.1 by Aaron Bentley
Implement plan_merge, refactoring various bits
2833
            ('killed-b', 'b\n'),
2834
            ('unchanged', 'c\n'),
2835
            ('unchanged', 'd\n'),
2836
            ('new-a', 'e\n'),
2837
            ('new-b', 'f\n'),
2838
        ], list(tree_a.plan_file_merge('file-id', tree_b)))
3363.9.8 by Aaron Bentley
Ensure plan_file_merge works with a RevisionTree as the basis
2839
2840
    def test_plan_file_merge_revision_tree(self):
2841
        work_a = self.make_branch_and_tree('wta')
2842
        self.build_tree_contents([('wta/file', 'a\nb\nc\nd\n')])
2843
        work_a.add('file', 'file-id')
2844
        base_id = work_a.commit('base version')
2845
        tree_b = work_a.bzrdir.sprout('wtb').open_workingtree()
2846
        preview = TransformPreview(work_a.basis_tree())
2847
        self.addCleanup(preview.finalize)
2848
        trans_id = preview.trans_id_file_id('file-id')
2849
        preview.delete_contents(trans_id)
2850
        preview.create_file('b\nc\nd\ne\n', trans_id)
2851
        self.build_tree_contents([('wtb/file', 'a\nc\nd\nf\n')])
2852
        tree_a = preview.get_preview_tree()
2853
        tree_a.set_parent_ids([base_id])
2854
        self.assertEqual([
2855
            ('killed-a', 'a\n'),
2856
            ('killed-b', 'b\n'),
2857
            ('unchanged', 'c\n'),
2858
            ('unchanged', 'd\n'),
2859
            ('new-a', 'e\n'),
2860
            ('new-b', 'f\n'),
2861
        ], list(tree_a.plan_file_merge('file-id', tree_b)))
3363.9.9 by Aaron Bentley
Implement walkdirs in terms of TreeTransform
2862
2863
    def test_walkdirs(self):
2864
        preview = self.get_empty_preview()
4634.57.3 by Aaron Bentley
Fix failing test.
2865
        root = preview.new_directory('', ROOT_PARENT, 'tree-root')
2866
        # FIXME: new_directory should mark root.
4634.122.6 by John Arbash Meinel
Fix a test that used 'adjust_path' to set the root.
2867
        preview.fixup_new_roots()
3363.9.9 by Aaron Bentley
Implement walkdirs in terms of TreeTransform
2868
        preview_tree = preview.get_preview_tree()
2869
        file_trans_id = preview.new_file('a', preview.root, 'contents',
2870
                                         'a-id')
2871
        expected = [(('', 'tree-root'),
2872
                    [('a', 'a', 'file', None, 'a-id', 'file')])]
2873
        self.assertEqual(expected, list(preview_tree.walkdirs()))
3363.13.2 by Aaron Bentley
Test specific cases for PreviewTree.extras
2874
2875
    def test_extras(self):
2876
        work_tree = self.make_branch_and_tree('tree')
2877
        self.build_tree(['tree/removed-file', 'tree/existing-file',
2878
                         'tree/not-removed-file'])
2879
        work_tree.add(['removed-file', 'not-removed-file'])
2880
        preview = TransformPreview(work_tree)
3363.13.3 by Aaron Bentley
Add cleanup
2881
        self.addCleanup(preview.finalize)
3363.13.2 by Aaron Bentley
Test specific cases for PreviewTree.extras
2882
        preview.new_file('new-file', preview.root, 'contents')
2883
        preview.new_file('new-versioned-file', preview.root, 'contents',
2884
                         'new-versioned-id')
2885
        tree = preview.get_preview_tree()
2886
        preview.unversion_file(preview.trans_id_tree_path('removed-file'))
2887
        self.assertEqual(set(['new-file', 'removed-file', 'existing-file']),
2888
                         set(tree.extras()))
3363.17.1 by Aaron Bentley
Avoid inventory for merge and transform code
2889
3363.17.2 by Aaron Bentley
Add text checking
2890
    def test_merge_into_preview(self):
3363.17.1 by Aaron Bentley
Avoid inventory for merge and transform code
2891
        work_tree = self.make_branch_and_tree('tree')
3363.17.2 by Aaron Bentley
Add text checking
2892
        self.build_tree_contents([('tree/file','b\n')])
2893
        work_tree.add('file', 'file-id')
3363.17.1 by Aaron Bentley
Avoid inventory for merge and transform code
2894
        work_tree.commit('first commit')
2895
        child_tree = work_tree.bzrdir.sprout('child').open_workingtree()
3363.17.2 by Aaron Bentley
Add text checking
2896
        self.build_tree_contents([('child/file','b\nc\n')])
3363.17.1 by Aaron Bentley
Avoid inventory for merge and transform code
2897
        child_tree.commit('child commit')
2898
        child_tree.lock_write()
2899
        self.addCleanup(child_tree.unlock)
2900
        work_tree.lock_write()
2901
        self.addCleanup(work_tree.unlock)
2902
        preview = TransformPreview(work_tree)
2903
        self.addCleanup(preview.finalize)
3363.17.6 by Aaron Bentley
Improve test scenario
2904
        file_trans_id = preview.trans_id_file_id('file-id')
2905
        preview.delete_contents(file_trans_id)
2906
        preview.create_file('a\nb\n', file_trans_id)
4634.57.2 by Aaron Bentley
Fix failing test.
2907
        preview_tree = preview.get_preview_tree()
4961.2.9 by Martin Pool
Rip out most remaining uses of DummyProgressBar
2908
        merger = Merger.from_revision_ids(None, preview_tree,
3363.17.1 by Aaron Bentley
Avoid inventory for merge and transform code
2909
                                          child_tree.branch.last_revision(),
2910
                                          other_branch=child_tree.branch,
2911
                                          tree_branch=work_tree.branch)
2912
        merger.merge_type = Merge3Merger
2913
        tt = merger.make_merger().make_preview_transform()
3363.17.2 by Aaron Bentley
Add text checking
2914
        self.addCleanup(tt.finalize)
2915
        final_tree = tt.get_preview_tree()
2916
        self.assertEqual('a\nb\nc\n', final_tree.get_file_text('file-id'))
3363.17.17 by Aaron Bentley
Start testing merging PreviewTree as OTHER
2917
2918
    def test_merge_preview_into_workingtree(self):
2919
        tree = self.make_branch_and_tree('tree')
4600.3.1 by Robert Collins
Set tree root ID in tree transform tests that don't care about the root id.
2920
        tree.set_root_id('TREE_ROOT')
3363.17.17 by Aaron Bentley
Start testing merging PreviewTree as OTHER
2921
        tt = TransformPreview(tree)
2922
        self.addCleanup(tt.finalize)
2923
        tt.new_file('name', tt.root, 'content', 'file-id')
2924
        tree2 = self.make_branch_and_tree('tree2')
4600.3.1 by Robert Collins
Set tree root ID in tree transform tests that don't care about the root id.
2925
        tree2.set_root_id('TREE_ROOT')
3363.17.17 by Aaron Bentley
Start testing merging PreviewTree as OTHER
2926
        merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
4961.2.9 by Martin Pool
Rip out most remaining uses of DummyProgressBar
2927
                                         None, tree.basis_tree())
3363.17.17 by Aaron Bentley
Start testing merging PreviewTree as OTHER
2928
        merger.merge_type = Merge3Merger
2929
        merger.do_merge()
3363.17.18 by Aaron Bentley
Fix is_executable for PreviewTree
2930
3363.17.21 by Aaron Bentley
Conflicts are handled when merging from preview trees
2931
    def test_merge_preview_into_workingtree_handles_conflicts(self):
2932
        tree = self.make_branch_and_tree('tree')
2933
        self.build_tree_contents([('tree/foo', 'bar')])
2934
        tree.add('foo', 'foo-id')
2935
        tree.commit('foo')
2936
        tt = TransformPreview(tree)
2937
        self.addCleanup(tt.finalize)
2938
        trans_id = tt.trans_id_file_id('foo-id')
2939
        tt.delete_contents(trans_id)
2940
        tt.create_file('baz', trans_id)
2941
        tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
2942
        self.build_tree_contents([('tree2/foo', 'qux')])
4961.2.9 by Martin Pool
Rip out most remaining uses of DummyProgressBar
2943
        pb = None
3363.17.21 by Aaron Bentley
Conflicts are handled when merging from preview trees
2944
        merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
2945
                                         pb, tree.basis_tree())
2946
        merger.merge_type = Merge3Merger
2947
        merger.do_merge()
2948
3363.17.18 by Aaron Bentley
Fix is_executable for PreviewTree
2949
    def test_is_executable(self):
2950
        tree = self.make_branch_and_tree('tree')
2951
        preview = TransformPreview(tree)
2952
        self.addCleanup(preview.finalize)
2953
        preview.new_file('foo', preview.root, 'bar', 'baz-id')
2954
        preview_tree = preview.get_preview_tree()
2955
        self.assertEqual(False, preview_tree.is_executable('baz-id',
2956
                                                           'tree/foo'))
2957
        self.assertEqual(False, preview_tree.is_executable('baz-id'))
0.13.8 by Aaron Bentley
Integrate serialization into TreeTransforms
2958
4354.4.4 by Aaron Bentley
Simplify by using CommitBuilder directly
2959
    def test_commit_preview_tree(self):
2960
        tree = self.make_branch_and_tree('tree')
2961
        rev_id = tree.commit('rev1')
2962
        tree.branch.lock_write()
2963
        self.addCleanup(tree.branch.unlock)
2964
        tt = TransformPreview(tree)
2965
        tt.new_file('file', tt.root, 'contents', 'file_id')
2966
        self.addCleanup(tt.finalize)
2967
        preview = tt.get_preview_tree()
2968
        preview.set_parent_ids([rev_id])
2969
        builder = tree.branch.get_commit_builder([rev_id])
2970
        list(builder.record_iter_changes(preview, rev_id, tt.iter_changes()))
2971
        builder.finish_inventory()
2972
        rev2_id = builder.commit('rev2')
2973
        rev2_tree = tree.branch.repository.revision_tree(rev2_id)
2974
        self.assertEqual('contents', rev2_tree.get_file_text('file_id'))
2975
4634.79.1 by Aaron Bentley
TransformPreview uses ascii-only filenames.
2976
    def test_ascii_limbo_paths(self):
4634.79.2 by Aaron Bentley
Avoid runing test on non-unicode filesystems.
2977
        self.requireFeature(tests.UnicodeFilenameFeature)
4634.79.1 by Aaron Bentley
TransformPreview uses ascii-only filenames.
2978
        branch = self.make_branch('any')
2979
        tree = branch.repository.revision_tree(_mod_revision.NULL_REVISION)
2980
        tt = TransformPreview(tree)
4789.25.7 by John Arbash Meinel
We can run the executable tests.
2981
        self.addCleanup(tt.finalize)
4634.79.1 by Aaron Bentley
TransformPreview uses ascii-only filenames.
2982
        foo_id = tt.new_directory('', ROOT_PARENT)
2983
        bar_id = tt.new_file(u'\u1234bar', foo_id, 'contents')
2984
        limbo_path = tt._limbo_name(bar_id)
2985
        self.assertEqual(limbo_path.encode('ascii', 'replace'), limbo_path)
2986
0.13.8 by Aaron Bentley
Integrate serialization into TreeTransforms
2987
0.13.13 by Aaron Bentley
Add direct test of serialization records
2988
class FakeSerializer(object):
2989
    """Serializer implementation that simply returns the input.
2990
2991
    The input is returned in the order used by pack.ContainerPushParser.
2992
    """
2993
    @staticmethod
2994
    def bytes_record(bytes, names):
2995
        return names, bytes
2996
2997
0.13.8 by Aaron Bentley
Integrate serialization into TreeTransforms
2998
class TestSerializeTransform(tests.TestCaseWithTransport):
2999
0.13.22 by Aaron Bentley
More unicodeness for Shelf tests
3000
    _test_needs_features = [tests.UnicodeFilenameFeature]
3001
0.13.17 by Aaron Bentley
Convert roundtrip destruction test to serialization/deserialization pair
3002
    def get_preview(self, tree=None):
3003
        if tree is None:
3004
            tree = self.make_branch_and_tree('tree')
0.13.14 by Aaron Bentley
Add deserialization test, remove roundtrip test.
3005
        tt = TransformPreview(tree)
3006
        self.addCleanup(tt.finalize)
3007
        return tt
3008
0.13.18 by Aaron Bentley
Finish converting tests to direct serialize/deserialize tests, clean up
3009
    def assertSerializesTo(self, expected, tt):
3010
        records = list(tt.serialize(FakeSerializer()))
3011
        self.assertEqual(expected, records)
0.13.8 by Aaron Bentley
Integrate serialization into TreeTransforms
3012
0.13.13 by Aaron Bentley
Add direct test of serialization records
3013
    @staticmethod
3014
    def default_attribs():
3015
        return {
0.13.15 by Aaron Bentley
Convert symlink tests to avoid roundtripping
3016
            '_id_number': 1,
0.13.13 by Aaron Bentley
Add direct test of serialization records
3017
            '_new_name': {},
3018
            '_new_parent': {},
3019
            '_new_executability': {},
3020
            '_new_id': {},
0.13.15 by Aaron Bentley
Convert symlink tests to avoid roundtripping
3021
            '_tree_path_ids': {'': 'new-0'},
0.13.13 by Aaron Bentley
Add direct test of serialization records
3022
            '_removed_id': [],
3023
            '_removed_contents': [],
3024
            '_non_present_ids': {},
3025
            }
3026
0.13.18 by Aaron Bentley
Finish converting tests to direct serialize/deserialize tests, clean up
3027
    def make_records(self, attribs, contents):
3028
        records = [
3029
            (((('attribs'),),), bencode.bencode(attribs))]
3030
        records.extend([(((n, k),), c) for n, k, c in contents])
3031
        return records
3032
0.13.13 by Aaron Bentley
Add direct test of serialization records
3033
    def creation_records(self):
3034
        attribs = self.default_attribs()
3035
        attribs['_id_number'] = 3
3036
        attribs['_new_name'] = {
3037
            'new-1': u'foo\u1234'.encode('utf-8'), 'new-2': 'qux'}
3038
        attribs['_new_id'] = {'new-1': 'baz', 'new-2': 'quxx'}
3039
        attribs['_new_parent'] = {'new-1': 'new-0', 'new-2': 'new-0'}
3040
        attribs['_new_executability'] = {'new-1': 1}
3041
        contents = [
3042
            ('new-1', 'file', 'i 1\nbar\n'),
3043
            ('new-2', 'directory', ''),
3044
            ]
3045
        return self.make_records(attribs, contents)
3046
3047
    def test_serialize_creation(self):
0.13.14 by Aaron Bentley
Add deserialization test, remove roundtrip test.
3048
        tt = self.get_preview()
0.13.13 by Aaron Bentley
Add direct test of serialization records
3049
        tt.new_file(u'foo\u1234', tt.root, 'bar', 'baz', True)
3050
        tt.new_directory('qux', tt.root, 'quxx')
0.13.21 by Aaron Bentley
Use assertSerializesTo in more places
3051
        self.assertSerializesTo(self.creation_records(), tt)
0.13.13 by Aaron Bentley
Add direct test of serialization records
3052
0.13.14 by Aaron Bentley
Add deserialization test, remove roundtrip test.
3053
    def test_deserialize_creation(self):
3054
        tt = self.get_preview()
3055
        tt.deserialize(iter(self.creation_records()))
3056
        self.assertEqual(3, tt._id_number)
3057
        self.assertEqual({'new-1': u'foo\u1234',
3058
                          'new-2': 'qux'}, tt._new_name)
3059
        self.assertEqual({'new-1': 'baz', 'new-2': 'quxx'}, tt._new_id)
3060
        self.assertEqual({'new-1': tt.root, 'new-2': tt.root}, tt._new_parent)
3061
        self.assertEqual({'baz': 'new-1', 'quxx': 'new-2'}, tt._r_new_id)
3062
        self.assertEqual({'new-1': True}, tt._new_executability)
3063
        self.assertEqual({'new-1': 'file',
3064
                          'new-2': 'directory'}, tt._new_contents)
3065
        foo_limbo = open(tt._limbo_name('new-1'), 'rb')
3066
        try:
3067
            foo_content = foo_limbo.read()
3068
        finally:
3069
            foo_limbo.close()
3070
        self.assertEqual('bar', foo_content)
3071
0.13.18 by Aaron Bentley
Finish converting tests to direct serialize/deserialize tests, clean up
3072
    def symlink_creation_records(self):
3073
        attribs = self.default_attribs()
3074
        attribs['_id_number'] = 2
3075
        attribs['_new_name'] = {'new-1': u'foo\u1234'.encode('utf-8')}
3076
        attribs['_new_parent'] = {'new-1': 'new-0'}
3077
        contents = [('new-1', 'symlink', u'bar\u1234'.encode('utf-8'))]
3078
        return self.make_records(attribs, contents)
3079
0.13.15 by Aaron Bentley
Convert symlink tests to avoid roundtripping
3080
    def test_serialize_symlink_creation(self):
0.13.8 by Aaron Bentley
Integrate serialization into TreeTransforms
3081
        self.requireFeature(tests.SymlinkFeature)
0.13.15 by Aaron Bentley
Convert symlink tests to avoid roundtripping
3082
        tt = self.get_preview()
0.13.16 by Aaron Bentley
Add unicode symlink targets to tests
3083
        tt.new_symlink(u'foo\u1234', tt.root, u'bar\u1234')
0.13.21 by Aaron Bentley
Use assertSerializesTo in more places
3084
        self.assertSerializesTo(self.symlink_creation_records(), tt)
0.13.15 by Aaron Bentley
Convert symlink tests to avoid roundtripping
3085
3086
    def test_deserialize_symlink_creation(self):
4241.14.12 by Vincent Ladeuil
Far too many modifications for a single commit, need to restart.
3087
        self.requireFeature(tests.SymlinkFeature)
0.13.15 by Aaron Bentley
Convert symlink tests to avoid roundtripping
3088
        tt = self.get_preview()
3089
        tt.deserialize(iter(self.symlink_creation_records()))
4241.14.12 by Vincent Ladeuil
Far too many modifications for a single commit, need to restart.
3090
        abspath = tt._limbo_name('new-1')
4241.14.17 by Vincent Ladeuil
Add more tests for unicode symlinks to test_transform.
3091
        foo_content = osutils.readlink(abspath)
0.13.22 by Aaron Bentley
More unicodeness for Shelf tests
3092
        self.assertEqual(u'bar\u1234', foo_content)
0.13.8 by Aaron Bentley
Integrate serialization into TreeTransforms
3093
0.13.19 by Aaron Bentley
Clean up serialization tests
3094
    def make_destruction_preview(self):
0.13.8 by Aaron Bentley
Integrate serialization into TreeTransforms
3095
        tree = self.make_branch_and_tree('.')
3096
        self.build_tree([u'foo\u1234', 'bar'])
3097
        tree.add([u'foo\u1234', 'bar'], ['foo-id', 'bar-id'])
0.13.19 by Aaron Bentley
Clean up serialization tests
3098
        return self.get_preview(tree)
0.13.17 by Aaron Bentley
Convert roundtrip destruction test to serialization/deserialization pair
3099
0.13.18 by Aaron Bentley
Finish converting tests to direct serialize/deserialize tests, clean up
3100
    def destruction_records(self):
3101
        attribs = self.default_attribs()
3102
        attribs['_id_number'] = 3
3103
        attribs['_removed_id'] = ['new-1']
3104
        attribs['_removed_contents'] = ['new-2']
3105
        attribs['_tree_path_ids'] = {
3106
            '': 'new-0',
3107
            u'foo\u1234'.encode('utf-8'): 'new-1',
3108
            'bar': 'new-2',
3109
            }
3110
        return self.make_records(attribs, [])
3111
0.13.17 by Aaron Bentley
Convert roundtrip destruction test to serialization/deserialization pair
3112
    def test_serialize_destruction(self):
0.13.19 by Aaron Bentley
Clean up serialization tests
3113
        tt = self.make_destruction_preview()
0.13.8 by Aaron Bentley
Integrate serialization into TreeTransforms
3114
        foo_trans_id = tt.trans_id_tree_file_id('foo-id')
3115
        tt.unversion_file(foo_trans_id)
3116
        bar_trans_id = tt.trans_id_tree_file_id('bar-id')
3117
        tt.delete_contents(bar_trans_id)
0.13.18 by Aaron Bentley
Finish converting tests to direct serialize/deserialize tests, clean up
3118
        self.assertSerializesTo(self.destruction_records(), tt)
0.13.17 by Aaron Bentley
Convert roundtrip destruction test to serialization/deserialization pair
3119
3120
    def test_deserialize_destruction(self):
0.13.19 by Aaron Bentley
Clean up serialization tests
3121
        tt = self.make_destruction_preview()
0.13.17 by Aaron Bentley
Convert roundtrip destruction test to serialization/deserialization pair
3122
        tt.deserialize(iter(self.destruction_records()))
3123
        self.assertEqual({u'foo\u1234': 'new-1',
3124
                          'bar': 'new-2',
3125
                          '': tt.root}, tt._tree_path_ids)
3126
        self.assertEqual({'new-1': u'foo\u1234',
3127
                          'new-2': 'bar',
3128
                          tt.root: ''}, tt._tree_id_paths)
3129
        self.assertEqual(set(['new-1']), tt._removed_id)
3130
        self.assertEqual(set(['new-2']), tt._removed_contents)
0.13.8 by Aaron Bentley
Integrate serialization into TreeTransforms
3131
0.13.18 by Aaron Bentley
Finish converting tests to direct serialize/deserialize tests, clean up
3132
    def missing_records(self):
3133
        attribs = self.default_attribs()
3134
        attribs['_id_number'] = 2
3135
        attribs['_non_present_ids'] = {
3136
            'boo': 'new-1',}
3137
        return self.make_records(attribs, [])
3138
3139
    def test_serialize_missing(self):
3140
        tt = self.get_preview()
0.13.8 by Aaron Bentley
Integrate serialization into TreeTransforms
3141
        boo_trans_id = tt.trans_id_file_id('boo')
0.13.18 by Aaron Bentley
Finish converting tests to direct serialize/deserialize tests, clean up
3142
        self.assertSerializesTo(self.missing_records(), tt)
3143
3144
    def test_deserialize_missing(self):
3145
        tt = self.get_preview()
3146
        tt.deserialize(iter(self.missing_records()))
3147
        self.assertEqual({'boo': 'new-1'}, tt._non_present_ids)
3148
3149
    def make_modification_preview(self):
0.13.8 by Aaron Bentley
Integrate serialization into TreeTransforms
3150
        LINES_ONE = 'aa\nbb\ncc\ndd\n'
3151
        LINES_TWO = 'z\nbb\nx\ndd\n'
3152
        tree = self.make_branch_and_tree('tree')
3153
        self.build_tree_contents([('tree/file', LINES_ONE)])
3154
        tree.add('file', 'file-id')
0.13.18 by Aaron Bentley
Finish converting tests to direct serialize/deserialize tests, clean up
3155
        return self.get_preview(tree), LINES_TWO
3156
3157
    def modification_records(self):
3158
        attribs = self.default_attribs()
3159
        attribs['_id_number'] = 2
3160
        attribs['_tree_path_ids'] = {
3161
            'file': 'new-1',
3162
            '': 'new-0',}
3163
        attribs['_removed_contents'] = ['new-1']
3164
        contents = [('new-1', 'file',
3165
                     'i 1\nz\n\nc 0 1 1 1\ni 1\nx\n\nc 0 3 3 1\n')]
3166
        return self.make_records(attribs, contents)
3167
3168
    def test_serialize_modification(self):
3169
        tt, LINES = self.make_modification_preview()
0.13.8 by Aaron Bentley
Integrate serialization into TreeTransforms
3170
        trans_id = tt.trans_id_file_id('file-id')
3171
        tt.delete_contents(trans_id)
0.13.18 by Aaron Bentley
Finish converting tests to direct serialize/deserialize tests, clean up
3172
        tt.create_file(LINES, trans_id)
3173
        self.assertSerializesTo(self.modification_records(), tt)
3174
3175
    def test_deserialize_modification(self):
3176
        tt, LINES = self.make_modification_preview()
3177
        tt.deserialize(iter(self.modification_records()))
3178
        self.assertFileEqual(LINES, tt._limbo_name('new-1'))
3179
3180
    def make_kind_change_preview(self):
3181
        LINES = 'a\nb\nc\nd\n'
0.13.8 by Aaron Bentley
Integrate serialization into TreeTransforms
3182
        tree = self.make_branch_and_tree('tree')
3183
        self.build_tree(['tree/foo/'])
3184
        tree.add('foo', 'foo-id')
0.13.18 by Aaron Bentley
Finish converting tests to direct serialize/deserialize tests, clean up
3185
        return self.get_preview(tree), LINES
3186
3187
    def kind_change_records(self):
3188
        attribs = self.default_attribs()
3189
        attribs['_id_number'] = 2
3190
        attribs['_tree_path_ids'] = {
3191
            'foo': 'new-1',
3192
            '': 'new-0',}
3193
        attribs['_removed_contents'] = ['new-1']
3194
        contents = [('new-1', 'file',
3195
                     'i 4\na\nb\nc\nd\n\n')]
3196
        return self.make_records(attribs, contents)
3197
3198
    def test_serialize_kind_change(self):
3199
        tt, LINES = self.make_kind_change_preview()
0.13.8 by Aaron Bentley
Integrate serialization into TreeTransforms
3200
        trans_id = tt.trans_id_file_id('foo-id')
3201
        tt.delete_contents(trans_id)
0.13.18 by Aaron Bentley
Finish converting tests to direct serialize/deserialize tests, clean up
3202
        tt.create_file(LINES, trans_id)
3203
        self.assertSerializesTo(self.kind_change_records(), tt)
3204
3205
    def test_deserialize_kind_change(self):
3206
        tt, LINES = self.make_kind_change_preview()
3207
        tt.deserialize(iter(self.kind_change_records()))
3208
        self.assertFileEqual(LINES, tt._limbo_name('new-1'))
3209
3210
    def make_add_contents_preview(self):
3211
        LINES = 'a\nb\nc\nd\n'
0.13.8 by Aaron Bentley
Integrate serialization into TreeTransforms
3212
        tree = self.make_branch_and_tree('tree')
3213
        self.build_tree(['tree/foo'])
3214
        tree.add('foo')
3215
        os.unlink('tree/foo')
0.13.18 by Aaron Bentley
Finish converting tests to direct serialize/deserialize tests, clean up
3216
        return self.get_preview(tree), LINES
3217
3218
    def add_contents_records(self):
3219
        attribs = self.default_attribs()
3220
        attribs['_id_number'] = 2
3221
        attribs['_tree_path_ids'] = {
3222
            'foo': 'new-1',
3223
            '': 'new-0',}
3224
        contents = [('new-1', 'file',
3225
                     'i 4\na\nb\nc\nd\n\n')]
3226
        return self.make_records(attribs, contents)
3227
3228
    def test_serialize_add_contents(self):
3229
        tt, LINES = self.make_add_contents_preview()
0.13.8 by Aaron Bentley
Integrate serialization into TreeTransforms
3230
        trans_id = tt.trans_id_tree_path('foo')
0.13.18 by Aaron Bentley
Finish converting tests to direct serialize/deserialize tests, clean up
3231
        tt.create_file(LINES, trans_id)
3232
        self.assertSerializesTo(self.add_contents_records(), tt)
3233
3234
    def test_deserialize_add_contents(self):
3235
        tt, LINES = self.make_add_contents_preview()
3236
        tt.deserialize(iter(self.add_contents_records()))
3237
        self.assertFileEqual(LINES, tt._limbo_name('new-1'))
0.13.8 by Aaron Bentley
Integrate serialization into TreeTransforms
3238
3239
    def test_get_parents_lines(self):
3240
        LINES_ONE = 'aa\nbb\ncc\ndd\n'
3241
        LINES_TWO = 'z\nbb\nx\ndd\n'
3242
        tree = self.make_branch_and_tree('tree')
3243
        self.build_tree_contents([('tree/file', LINES_ONE)])
3244
        tree.add('file', 'file-id')
0.13.18 by Aaron Bentley
Finish converting tests to direct serialize/deserialize tests, clean up
3245
        tt = self.get_preview(tree)
0.13.8 by Aaron Bentley
Integrate serialization into TreeTransforms
3246
        trans_id = tt.trans_id_tree_path('file')
3247
        self.assertEqual((['aa\n', 'bb\n', 'cc\n', 'dd\n'],),
3248
            tt._get_parents_lines(trans_id))
3249
3250
    def test_get_parents_texts(self):
3251
        LINES_ONE = 'aa\nbb\ncc\ndd\n'
3252
        LINES_TWO = 'z\nbb\nx\ndd\n'
3253
        tree = self.make_branch_and_tree('tree')
3254
        self.build_tree_contents([('tree/file', LINES_ONE)])
3255
        tree.add('file', 'file-id')
0.13.18 by Aaron Bentley
Finish converting tests to direct serialize/deserialize tests, clean up
3256
        tt = self.get_preview(tree)
0.13.8 by Aaron Bentley
Integrate serialization into TreeTransforms
3257
        trans_id = tt.trans_id_tree_path('file')
3258
        self.assertEqual((LINES_ONE,),
3259
            tt._get_parents_texts(trans_id))
5409.1.7 by Vincent Ladeuil
First orphaning implementation (some tests lacking).
3260
3261
3262
class TestOrphan(tests.TestCaseWithTransport):
3263
3264
    def test_no_orphan_for_transform_preview(self):
3265
        tree = self.make_branch_and_tree('tree')
5409.7.2 by Vincent Ladeuil
Add NEWS entry, a missing test and some cleanup.
3266
        tt = transform.TransformPreview(tree)
5409.1.7 by Vincent Ladeuil
First orphaning implementation (some tests lacking).
3267
        self.addCleanup(tt.finalize)
3268
        self.assertRaises(NotImplementedError, tt.new_orphan, 'foo', 'bar')
3269
5409.1.16 by Vincent Ladeuil
Add ``bzrlib.transform.orphan_policy`` and allows ``never`` to restore the previous behaviour.
3270
    def _set_orphan_policy(self, wt, policy):
5409.1.24 by Vincent Ladeuil
Rename bzrlib.transform.orphan_policy to bzr.transform.orphan_policy.
3271
        wt.branch.get_config().set_user_option('bzr.transform.orphan_policy',
5409.1.16 by Vincent Ladeuil
Add ``bzrlib.transform.orphan_policy`` and allows ``never`` to restore the previous behaviour.
3272
                                               policy)
3273
3274
    def _prepare_orphan(self, wt):
5540.3.1 by Vincent Ladeuil
Fix spurious orphan reports.
3275
        self.build_tree(['dir/', 'dir/file', 'dir/foo'])
3276
        wt.add(['dir', 'dir/file'], ['dir-id', 'file-id'])
3277
        wt.commit('add dir and file ignoring foo')
5409.7.2 by Vincent Ladeuil
Add NEWS entry, a missing test and some cleanup.
3278
        tt = transform.TreeTransform(wt)
3279
        self.addCleanup(tt.finalize)
5540.3.1 by Vincent Ladeuil
Fix spurious orphan reports.
3280
        # dir and bar are deleted
5409.7.2 by Vincent Ladeuil
Add NEWS entry, a missing test and some cleanup.
3281
        dir_tid = tt.trans_id_tree_path('dir')
5540.3.1 by Vincent Ladeuil
Fix spurious orphan reports.
3282
        file_tid = tt.trans_id_tree_path('dir/file')
5409.1.16 by Vincent Ladeuil
Add ``bzrlib.transform.orphan_policy`` and allows ``never`` to restore the previous behaviour.
3283
        orphan_tid = tt.trans_id_tree_path('dir/foo')
5540.3.1 by Vincent Ladeuil
Fix spurious orphan reports.
3284
        tt.delete_contents(file_tid)
3285
        tt.unversion_file(file_tid)
5409.7.2 by Vincent Ladeuil
Add NEWS entry, a missing test and some cleanup.
3286
        tt.delete_contents(dir_tid)
3287
        tt.unversion_file(dir_tid)
5540.3.1 by Vincent Ladeuil
Fix spurious orphan reports.
3288
        # There should be a conflict because dir still contain foo
5409.7.2 by Vincent Ladeuil
Add NEWS entry, a missing test and some cleanup.
3289
        raw_conflicts = tt.find_conflicts()
3290
        self.assertLength(1, raw_conflicts)
3291
        self.assertEqual(('missing parent', 'new-1'), raw_conflicts[0])
5409.1.16 by Vincent Ladeuil
Add ``bzrlib.transform.orphan_policy`` and allows ``never`` to restore the previous behaviour.
3292
        return tt, orphan_tid
3293
3294
    def test_new_orphan_created(self):
3295
        wt = self.make_branch_and_tree('.')
5409.1.20 by Vincent Ladeuil
Revert to 'conflict' being the default orphaning policy and fix fallouts.
3296
        self._set_orphan_policy(wt, 'move')
5409.1.16 by Vincent Ladeuil
Add ``bzrlib.transform.orphan_policy`` and allows ``never`` to restore the previous behaviour.
3297
        tt, orphan_tid = self._prepare_orphan(wt)
5540.3.1 by Vincent Ladeuil
Fix spurious orphan reports.
3298
        warnings = []
3299
        def warning(*args):
3300
            warnings.append(args[0] % args[1:])
3301
        self.overrideAttr(trace, 'warning', warning)
5409.7.2 by Vincent Ladeuil
Add NEWS entry, a missing test and some cleanup.
3302
        remaining_conflicts = resolve_conflicts(tt)
5540.3.1 by Vincent Ladeuil
Fix spurious orphan reports.
3303
        self.assertEquals(['dir/foo has been orphaned in bzr-orphans'],
3304
                          warnings)
5409.7.2 by Vincent Ladeuil
Add NEWS entry, a missing test and some cleanup.
3305
        # Yeah for resolved conflicts !
3306
        self.assertLength(0, remaining_conflicts)
3307
        # We have a new orphan
5409.1.16 by Vincent Ladeuil
Add ``bzrlib.transform.orphan_policy`` and allows ``never`` to restore the previous behaviour.
3308
        self.assertEquals('foo.~1~', tt.final_name(orphan_tid))
5409.7.4 by Vincent Ladeuil
Take jam's review comments into account.
3309
        self.assertEquals('bzr-orphans',
5409.1.16 by Vincent Ladeuil
Add ``bzrlib.transform.orphan_policy`` and allows ``never`` to restore the previous behaviour.
3310
                          tt.final_name(tt.final_parent(orphan_tid)))
3311
3312
    def test_never_orphan(self):
3313
        wt = self.make_branch_and_tree('.')
5409.1.20 by Vincent Ladeuil
Revert to 'conflict' being the default orphaning policy and fix fallouts.
3314
        self._set_orphan_policy(wt, 'conflict')
5409.1.16 by Vincent Ladeuil
Add ``bzrlib.transform.orphan_policy`` and allows ``never`` to restore the previous behaviour.
3315
        tt, orphan_tid = self._prepare_orphan(wt)
3316
        remaining_conflicts = resolve_conflicts(tt)
3317
        self.assertLength(1, remaining_conflicts)
3318
        self.assertEqual(('deleting parent', 'Not deleting', 'new-1'),
3319
                         remaining_conflicts.pop())
3320
3321
    def test_orphan_error(self):
3322
        def bogus_orphan(tt, orphan_id, parent_id):
3323
            raise transform.OrphaningError(tt.final_name(orphan_id),
3324
                                           tt.final_name(parent_id))
3325
        transform.orphaning_registry.register('bogus', bogus_orphan,
3326
                                              'Raise an error when orphaning')
3327
        wt = self.make_branch_and_tree('.')
3328
        self._set_orphan_policy(wt, 'bogus')
3329
        tt, orphan_tid = self._prepare_orphan(wt)
3330
        remaining_conflicts = resolve_conflicts(tt)
3331
        self.assertLength(1, remaining_conflicts)
3332
        self.assertEqual(('deleting parent', 'Not deleting', 'new-1'),
3333
                         remaining_conflicts.pop())
5409.1.17 by Vincent Ladeuil
Ensures we fallback to the default policy if a bogus one is specified.
3334
3335
    def test_unknown_orphan_policy(self):
3336
        wt = self.make_branch_and_tree('.')
3337
        # Set a fictional policy nobody ever implemented
3338
        self._set_orphan_policy(wt, 'donttouchmypreciouuus')
3339
        tt, orphan_tid = self._prepare_orphan(wt)
3340
        warnings = []
3341
        def warning(*args):
3342
            warnings.append(args[0] % args[1:])
3343
        self.overrideAttr(trace, 'warning', warning)
3344
        remaining_conflicts = resolve_conflicts(tt)
5409.1.20 by Vincent Ladeuil
Revert to 'conflict' being the default orphaning policy and fix fallouts.
3345
        # We fallback to the default policy which create a conflict
3346
        self.assertLength(1, remaining_conflicts)
3347
        self.assertEqual(('deleting parent', 'Not deleting', 'new-1'),
3348
                         remaining_conflicts.pop())
3349
        self.assertLength(1, warnings)
3350
        self.assertStartsWith(warnings[0], 'donttouchmypreciouuus')