~bzr-pqm/bzr/bzr.dev

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