/brz/remove-bazaar

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