/brz/remove-bazaar

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