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