/brz/remove-bazaar

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