/brz/remove-bazaar

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