~bzr-pqm/bzr/bzr.dev

1534.7.106 by Aaron Bentley
Cleaned up imports, added copyright statements
1
# Copyright (C) 2006 Canonical Ltd
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
2
#
1534.7.106 by Aaron Bentley
Cleaned up imports, added copyright statements
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
7
#
1534.7.106 by Aaron Bentley
Cleaned up imports, added copyright statements
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
12
#
1534.7.106 by Aaron Bentley
Cleaned up imports, added copyright statements
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
import os
2027.1.1 by John Arbash Meinel
Fix bug #56549, and write a direct test that the right path is being statted
18
import stat
19
import sys
1558.1.3 by Aaron Bentley
Fixed deprecated op use in test suite
20
2027.1.1 by John Arbash Meinel
Fix bug #56549, and write a direct test that the right path is being statted
21
from bzrlib import (
2090.2.1 by Martin Pool
Fix some code which relies on assertions and breaks under python -O
22
    errors,
2116.4.1 by John Arbash Meinel
Update file and revision id generators.
23
    generate_ids,
2255.7.48 by Robert Collins
Deprecated and make work with DirState trees 'transform.find_interesting'.
24
    symbol_versioning,
2027.1.1 by John Arbash Meinel
Fix bug #56549, and write a direct test that the right path is being statted
25
    tests,
26
    urlutils,
27
    )
1558.1.3 by Aaron Bentley
Fixed deprecated op use in test suite
28
from bzrlib.bzrdir import BzrDir
1534.10.24 by Aaron Bentley
Eliminated conflicts_to_strings, made remove_files a ConflictList member
29
from bzrlib.conflicts import (DuplicateEntry, DuplicateID, MissingParent,
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
30
                              UnversionedParent, ParentLoop, DeletingParent,)
1534.7.32 by Aaron Bentley
Got conflict handling working when conflicts involve existing files
31
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
1551.7.17 by Aaron Bentley
Switch to PathsNotVersioned, accept extra_trees
32
                           ReusingTransform, CantMoveRoot, 
33
                           PathsNotVersionedError, ExistingLimbo,
2733.2.11 by Aaron Bentley
Detect irregularities with the pending-deletion directory
34
                           ExistingPendingDeletion, ImmortalLimbo,
35
                           ImmortalPendingDeletion, LockError)
1685.1.45 by John Arbash Meinel
Moved url functions into bzrlib.urlutils
36
from bzrlib.osutils import file_kind, has_symlinks, pathjoin
1534.7.140 by Aaron Bentley
Moved the merge stuff into merge.py
37
from bzrlib.merge import Merge3Merger
1534.10.28 by Aaron Bentley
Use numbered backup files
38
from bzrlib.tests import TestCaseInTempDir, TestSkipped, TestCase
1534.7.106 by Aaron Bentley
Cleaned up imports, added copyright statements
39
from bzrlib.transform import (TreeTransform, ROOT_PARENT, FinalPaths, 
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
40
                              resolve_conflicts, cook_conflicts, 
2625.4.3 by Ian Clatworthy
Add a test to ensure the deprecation worked.
41
                              find_interesting, build_tree, get_backup_name,
2733.2.1 by Aaron Bentley
Implement FileMover, to support TreeTransform rollback
42
                              change_entry, _FileMover)
1731.1.33 by Aaron Bentley
Revert no-special-root changes
43
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
44
2100.3.21 by Aaron Bentley
Work on checking out by-reference trees
45
class TestTreeTransform(tests.TestCaseWithTransport):
1740.2.4 by Aaron Bentley
Update transform tests and docs
46
1534.7.59 by Aaron Bentley
Simplified tests
47
    def setUp(self):
48
        super(TestTreeTransform, self).setUp()
2255.2.194 by Robert Collins
[BROKEN] Many updates to stop using experimental formats in tests.
49
        self.wt = self.make_branch_and_tree('.', format='dirstate-with-subtree')
1534.7.161 by Aaron Bentley
Used appropriate control_files
50
        os.chdir('..')
1534.7.59 by Aaron Bentley
Simplified tests
51
52
    def get_transform(self):
53
        transform = TreeTransform(self.wt)
1534.7.100 by Aaron Bentley
Fixed path-relative test cases
54
        #self.addCleanup(transform.finalize)
1731.1.33 by Aaron Bentley
Revert no-special-root changes
55
        return transform, transform.root
1534.7.59 by Aaron Bentley
Simplified tests
56
1534.7.162 by Aaron Bentley
Handle failures creating/deleting the Limbo directory
57
    def test_existing_limbo(self):
58
        transform, root = self.get_transform()
2733.2.6 by Aaron Bentley
Make TreeTransform commits rollbackable
59
        limbo_name = transform._limbodir
60
        deletion_path = transform._deletiondir
1534.7.176 by abentley
Fixed up tests for Windows
61
        os.mkdir(pathjoin(limbo_name, 'hehe'))
1534.7.162 by Aaron Bentley
Handle failures creating/deleting the Limbo directory
62
        self.assertRaises(ImmortalLimbo, transform.apply)
63
        self.assertRaises(LockError, self.wt.unlock)
64
        self.assertRaises(ExistingLimbo, self.get_transform)
65
        self.assertRaises(LockError, self.wt.unlock)
1534.7.176 by abentley
Fixed up tests for Windows
66
        os.rmdir(pathjoin(limbo_name, 'hehe'))
1534.7.162 by Aaron Bentley
Handle failures creating/deleting the Limbo directory
67
        os.rmdir(limbo_name)
2733.2.6 by Aaron Bentley
Make TreeTransform commits rollbackable
68
        os.rmdir(deletion_path)
1534.7.162 by Aaron Bentley
Handle failures creating/deleting the Limbo directory
69
        transform, root = self.get_transform()
70
        transform.apply()
1534.7.59 by Aaron Bentley
Simplified tests
71
2733.2.11 by Aaron Bentley
Detect irregularities with the pending-deletion directory
72
    def test_existing_pending_deletion(self):
73
        transform, root = self.get_transform()
74
        deletion_path = self._limbodir = urlutils.local_path_from_url(
75
            transform._tree._control_files.controlfilename('pending-deletion'))
76
        os.mkdir(pathjoin(deletion_path, 'blocking-directory'))
77
        self.assertRaises(ImmortalPendingDeletion, transform.apply)
78
        self.assertRaises(LockError, self.wt.unlock)
79
        self.assertRaises(ExistingPendingDeletion, self.get_transform)
80
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
81
    def test_build(self):
1534.7.59 by Aaron Bentley
Simplified tests
82
        transform, root = self.get_transform() 
83
        self.assertIs(transform.get_tree_parent(root), ROOT_PARENT)
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
84
        imaginary_id = transform.trans_id_tree_path('imaginary')
1534.10.32 by Aaron Bentley
Test and fix case where name has trailing slash
85
        imaginary_id2 = transform.trans_id_tree_path('imaginary/')
86
        self.assertEqual(imaginary_id, imaginary_id2)
1534.7.59 by Aaron Bentley
Simplified tests
87
        self.assertEqual(transform.get_tree_parent(imaginary_id), root)
88
        self.assertEqual(transform.final_kind(root), 'directory')
89
        self.assertEqual(transform.final_file_id(root), self.wt.get_root_id())
90
        trans_id = transform.create_path('name', root)
91
        self.assertIs(transform.final_file_id(trans_id), None)
92
        self.assertRaises(NoSuchFile, transform.final_kind, trans_id)
93
        transform.create_file('contents', trans_id)
94
        transform.set_executability(True, trans_id)
95
        transform.version_file('my_pretties', trans_id)
96
        self.assertRaises(DuplicateKey, transform.version_file,
97
                          'my_pretties', trans_id)
98
        self.assertEqual(transform.final_file_id(trans_id), 'my_pretties')
99
        self.assertEqual(transform.final_parent(trans_id), root)
100
        self.assertIs(transform.final_parent(root), ROOT_PARENT)
101
        self.assertIs(transform.get_tree_parent(root), ROOT_PARENT)
102
        oz_id = transform.create_path('oz', root)
103
        transform.create_directory(oz_id)
104
        transform.version_file('ozzie', oz_id)
105
        trans_id2 = transform.create_path('name2', root)
106
        transform.create_file('contents', trans_id2)
107
        transform.set_executability(False, trans_id2)
108
        transform.version_file('my_pretties2', trans_id2)
1534.7.191 by Aaron Bentley
Got transform.apply to list modified paths
109
        modified_paths = transform.apply().modified_paths
1534.7.100 by Aaron Bentley
Fixed path-relative test cases
110
        self.assertEqual('contents', self.wt.get_file_byname('name').read())
1534.7.59 by Aaron Bentley
Simplified tests
111
        self.assertEqual(self.wt.path2id('name'), 'my_pretties')
112
        self.assertIs(self.wt.is_executable('my_pretties'), True)
113
        self.assertIs(self.wt.is_executable('my_pretties2'), False)
1534.7.100 by Aaron Bentley
Fixed path-relative test cases
114
        self.assertEqual('directory', file_kind(self.wt.abspath('oz')))
1534.7.191 by Aaron Bentley
Got transform.apply to list modified paths
115
        self.assertEqual(len(modified_paths), 3)
116
        tree_mod_paths = [self.wt.id2abspath(f) for f in 
117
                          ('ozzie', 'my_pretties', 'my_pretties2')]
118
        self.assertSubset(tree_mod_paths, modified_paths)
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
119
        # is it safe to finalize repeatedly?
120
        transform.finalize()
1534.7.59 by Aaron Bentley
Simplified tests
121
        transform.finalize()
1534.7.2 by Aaron Bentley
Added convenience function
122
123
    def test_convenience(self):
1534.7.59 by Aaron Bentley
Simplified tests
124
        transform, root = self.get_transform()
125
        trans_id = transform.new_file('name', root, 'contents', 
126
                                      'my_pretties', True)
127
        oz = transform.new_directory('oz', root, 'oz-id')
128
        dorothy = transform.new_directory('dorothy', oz, 'dorothy-id')
129
        toto = transform.new_file('toto', dorothy, 'toto-contents', 
130
                                  'toto-id', False)
131
132
        self.assertEqual(len(transform.find_conflicts()), 0)
133
        transform.apply()
134
        self.assertRaises(ReusingTransform, transform.find_conflicts)
1534.7.100 by Aaron Bentley
Fixed path-relative test cases
135
        self.assertEqual('contents', file(self.wt.abspath('name')).read())
1534.7.59 by Aaron Bentley
Simplified tests
136
        self.assertEqual(self.wt.path2id('name'), 'my_pretties')
137
        self.assertIs(self.wt.is_executable('my_pretties'), True)
138
        self.assertEqual(self.wt.path2id('oz'), 'oz-id')
139
        self.assertEqual(self.wt.path2id('oz/dorothy'), 'dorothy-id')
140
        self.assertEqual(self.wt.path2id('oz/dorothy/toto'), 'toto-id')
141
2255.2.194 by Robert Collins
[BROKEN] Many updates to stop using experimental formats in tests.
142
        self.assertEqual('toto-contents',
1534.7.100 by Aaron Bentley
Fixed path-relative test cases
143
                         self.wt.get_file_byname('oz/dorothy/toto').read())
1534.7.59 by Aaron Bentley
Simplified tests
144
        self.assertIs(self.wt.is_executable('toto-id'), False)
1534.7.6 by Aaron Bentley
Added conflict handling
145
2100.3.21 by Aaron Bentley
Work on checking out by-reference trees
146
    def test_tree_reference(self):
147
        transform, root = self.get_transform()
148
        tree = transform._tree
149
        trans_id = transform.new_directory('reference', root, 'subtree-id')
150
        transform.set_tree_reference('subtree-revision', trans_id)
151
        transform.apply()
2255.2.194 by Robert Collins
[BROKEN] Many updates to stop using experimental formats in tests.
152
        tree.lock_read()
153
        self.addCleanup(tree.unlock)
154
        self.assertEqual('subtree-revision',
2100.3.21 by Aaron Bentley
Work on checking out by-reference trees
155
                         tree.inventory['subtree-id'].reference_revision)
156
1534.7.6 by Aaron Bentley
Added conflict handling
157
    def test_conflicts(self):
1534.7.59 by Aaron Bentley
Simplified tests
158
        transform, root = self.get_transform()
159
        trans_id = transform.new_file('name', root, 'contents', 
160
                                      'my_pretties')
161
        self.assertEqual(len(transform.find_conflicts()), 0)
162
        trans_id2 = transform.new_file('name', root, 'Crontents', 'toto')
163
        self.assertEqual(transform.find_conflicts(), 
164
                         [('duplicate', trans_id, trans_id2, 'name')])
165
        self.assertRaises(MalformedTransform, transform.apply)
166
        transform.adjust_path('name', trans_id, trans_id2)
167
        self.assertEqual(transform.find_conflicts(), 
168
                         [('non-directory parent', trans_id)])
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
169
        tinman_id = transform.trans_id_tree_path('tinman')
1534.7.59 by Aaron Bentley
Simplified tests
170
        transform.adjust_path('name', tinman_id, trans_id2)
171
        self.assertEqual(transform.find_conflicts(), 
172
                         [('unversioned parent', tinman_id), 
173
                          ('missing parent', tinman_id)])
174
        lion_id = transform.create_path('lion', root)
175
        self.assertEqual(transform.find_conflicts(), 
176
                         [('unversioned parent', tinman_id), 
177
                          ('missing parent', tinman_id)])
178
        transform.adjust_path('name', lion_id, trans_id2)
179
        self.assertEqual(transform.find_conflicts(), 
180
                         [('unversioned parent', lion_id),
181
                          ('missing parent', lion_id)])
182
        transform.version_file("Courage", lion_id)
183
        self.assertEqual(transform.find_conflicts(), 
184
                         [('missing parent', lion_id), 
185
                          ('versioning no contents', lion_id)])
186
        transform.adjust_path('name2', root, trans_id2)
187
        self.assertEqual(transform.find_conflicts(), 
188
                         [('versioning no contents', lion_id)])
189
        transform.create_file('Contents, okay?', lion_id)
190
        transform.adjust_path('name2', trans_id2, trans_id2)
191
        self.assertEqual(transform.find_conflicts(), 
192
                         [('parent loop', trans_id2), 
193
                          ('non-directory parent', trans_id2)])
194
        transform.adjust_path('name2', root, trans_id2)
195
        oz_id = transform.new_directory('oz', root)
196
        transform.set_executability(True, oz_id)
197
        self.assertEqual(transform.find_conflicts(), 
198
                         [('unversioned executability', oz_id)])
199
        transform.version_file('oz-id', oz_id)
200
        self.assertEqual(transform.find_conflicts(), 
201
                         [('non-file executability', oz_id)])
202
        transform.set_executability(None, oz_id)
1534.7.71 by abentley
All tests pass under Windows
203
        tip_id = transform.new_file('tip', oz_id, 'ozma', 'tip-id')
1534.7.59 by Aaron Bentley
Simplified tests
204
        transform.apply()
205
        self.assertEqual(self.wt.path2id('name'), 'my_pretties')
206
        self.assertEqual('contents', file(self.wt.abspath('name')).read())
207
        transform2, root = self.get_transform()
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
208
        oz_id = transform2.trans_id_tree_file_id('oz-id')
1534.7.59 by Aaron Bentley
Simplified tests
209
        newtip = transform2.new_file('tip', oz_id, 'other', 'tip-id')
210
        result = transform2.find_conflicts()
1534.7.135 by Aaron Bentley
Fixed deletion handling
211
        fp = FinalPaths(transform2)
1534.7.59 by Aaron Bentley
Simplified tests
212
        self.assert_('oz/tip' in transform2._tree_path_ids)
1534.7.176 by abentley
Fixed up tests for Windows
213
        self.assertEqual(fp.get_path(newtip), pathjoin('oz', 'tip'))
1534.7.59 by Aaron Bentley
Simplified tests
214
        self.assertEqual(len(result), 2)
215
        self.assertEqual((result[0][0], result[0][1]), 
216
                         ('duplicate', newtip))
217
        self.assertEqual((result[1][0], result[1][2]), 
218
                         ('duplicate id', newtip))
1534.7.73 by Aaron Bentley
Changed model again. Now iterator is used immediately.
219
        transform2.finalize()
1534.7.59 by Aaron Bentley
Simplified tests
220
        transform3 = TreeTransform(self.wt)
221
        self.addCleanup(transform3.finalize)
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
222
        oz_id = transform3.trans_id_tree_file_id('oz-id')
1534.7.59 by Aaron Bentley
Simplified tests
223
        transform3.delete_contents(oz_id)
224
        self.assertEqual(transform3.find_conflicts(), 
225
                         [('missing parent', oz_id)])
1731.1.33 by Aaron Bentley
Revert no-special-root changes
226
        root_id = transform3.root
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
227
        tip_id = transform3.trans_id_tree_file_id('tip-id')
1534.7.59 by Aaron Bentley
Simplified tests
228
        transform3.adjust_path('tip', root_id, tip_id)
229
        transform3.apply()
1534.7.36 by Aaron Bentley
Added rename tests
230
1558.7.11 by Aaron Bentley
Avoid spurious conflict on add/delete
231
    def test_add_del(self):
232
        start, root = self.get_transform()
233
        start.new_directory('a', root, 'a')
234
        start.apply()
235
        transform, root = self.get_transform()
236
        transform.delete_versioned(transform.trans_id_tree_file_id('a'))
237
        transform.new_directory('a', root, 'a')
238
        transform.apply()
239
1534.7.46 by Aaron Bentley
Ensured a conflict when parents of versioned files are unversioned
240
    def test_unversioning(self):
1534.7.59 by Aaron Bentley
Simplified tests
241
        create_tree, root = self.get_transform()
242
        parent_id = create_tree.new_directory('parent', root, 'parent-id')
243
        create_tree.new_file('child', parent_id, 'child', 'child-id')
244
        create_tree.apply()
245
        unversion = TreeTransform(self.wt)
246
        self.addCleanup(unversion.finalize)
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
247
        parent = unversion.trans_id_tree_path('parent')
1534.7.59 by Aaron Bentley
Simplified tests
248
        unversion.unversion_file(parent)
249
        self.assertEqual(unversion.find_conflicts(), 
250
                         [('unversioned parent', parent_id)])
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
251
        file_id = unversion.trans_id_tree_file_id('child-id')
1534.7.59 by Aaron Bentley
Simplified tests
252
        unversion.unversion_file(file_id)
253
        unversion.apply()
1534.7.46 by Aaron Bentley
Ensured a conflict when parents of versioned files are unversioned
254
1534.7.36 by Aaron Bentley
Added rename tests
255
    def test_name_invariants(self):
1534.7.59 by Aaron Bentley
Simplified tests
256
        create_tree, root = self.get_transform()
257
        # prepare tree
1731.1.33 by Aaron Bentley
Revert no-special-root changes
258
        root = create_tree.root
1534.7.59 by Aaron Bentley
Simplified tests
259
        create_tree.new_file('name1', root, 'hello1', 'name1')
260
        create_tree.new_file('name2', root, 'hello2', 'name2')
261
        ddir = create_tree.new_directory('dying_directory', root, 'ddir')
262
        create_tree.new_file('dying_file', ddir, 'goodbye1', 'dfile')
263
        create_tree.new_file('moving_file', ddir, 'later1', 'mfile')
264
        create_tree.new_file('moving_file2', root, 'later2', 'mfile2')
265
        create_tree.apply()
266
267
        mangle_tree,root = self.get_transform()
1731.1.33 by Aaron Bentley
Revert no-special-root changes
268
        root = mangle_tree.root
1534.7.59 by Aaron Bentley
Simplified tests
269
        #swap names
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
270
        name1 = mangle_tree.trans_id_tree_file_id('name1')
271
        name2 = mangle_tree.trans_id_tree_file_id('name2')
1534.7.59 by Aaron Bentley
Simplified tests
272
        mangle_tree.adjust_path('name2', root, name1)
273
        mangle_tree.adjust_path('name1', root, name2)
274
275
        #tests for deleting parent directories 
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
276
        ddir = mangle_tree.trans_id_tree_file_id('ddir')
1534.7.59 by Aaron Bentley
Simplified tests
277
        mangle_tree.delete_contents(ddir)
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
278
        dfile = mangle_tree.trans_id_tree_file_id('dfile')
1534.7.59 by Aaron Bentley
Simplified tests
279
        mangle_tree.delete_versioned(dfile)
280
        mangle_tree.unversion_file(dfile)
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
281
        mfile = mangle_tree.trans_id_tree_file_id('mfile')
1534.7.59 by Aaron Bentley
Simplified tests
282
        mangle_tree.adjust_path('mfile', root, mfile)
283
284
        #tests for adding parent directories
285
        newdir = mangle_tree.new_directory('new_directory', root, 'newdir')
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
286
        mfile2 = mangle_tree.trans_id_tree_file_id('mfile2')
1534.7.59 by Aaron Bentley
Simplified tests
287
        mangle_tree.adjust_path('mfile2', newdir, mfile2)
288
        mangle_tree.new_file('newfile', newdir, 'hello3', 'dfile')
289
        self.assertEqual(mangle_tree.final_file_id(mfile2), 'mfile2')
290
        self.assertEqual(mangle_tree.final_parent(mfile2), newdir)
291
        self.assertEqual(mangle_tree.final_file_id(mfile2), 'mfile2')
292
        mangle_tree.apply()
293
        self.assertEqual(file(self.wt.abspath('name1')).read(), 'hello2')
294
        self.assertEqual(file(self.wt.abspath('name2')).read(), 'hello1')
1534.7.176 by abentley
Fixed up tests for Windows
295
        mfile2_path = self.wt.abspath(pathjoin('new_directory','mfile2'))
1534.7.41 by Aaron Bentley
Got inventory ID movement working
296
        self.assertEqual(mangle_tree.final_parent(mfile2), newdir)
1534.7.38 by Aaron Bentley
Tested adding paths
297
        self.assertEqual(file(mfile2_path).read(), 'later2')
1534.7.59 by Aaron Bentley
Simplified tests
298
        self.assertEqual(self.wt.id2path('mfile2'), 'new_directory/mfile2')
299
        self.assertEqual(self.wt.path2id('new_directory/mfile2'), 'mfile2')
1534.7.176 by abentley
Fixed up tests for Windows
300
        newfile_path = self.wt.abspath(pathjoin('new_directory','newfile'))
1534.7.38 by Aaron Bentley
Tested adding paths
301
        self.assertEqual(file(newfile_path).read(), 'hello3')
1534.7.59 by Aaron Bentley
Simplified tests
302
        self.assertEqual(self.wt.path2id('dying_directory'), 'ddir')
303
        self.assertIs(self.wt.path2id('dying_directory/dying_file'), None)
1534.7.176 by abentley
Fixed up tests for Windows
304
        mfile2_path = self.wt.abspath(pathjoin('new_directory','mfile2'))
1534.7.43 by abentley
Fixed some Windows bugs, introduced a conflicts bug
305
1534.7.150 by Aaron Bentley
Handled simultaneous renames of parent and child better
306
    def test_both_rename(self):
307
        create_tree,root = self.get_transform()
308
        newdir = create_tree.new_directory('selftest', root, 'selftest-id')
309
        create_tree.new_file('blackbox.py', newdir, 'hello1', 'blackbox-id')
310
        create_tree.apply()        
311
        mangle_tree,root = self.get_transform()
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
312
        selftest = mangle_tree.trans_id_tree_file_id('selftest-id')
313
        blackbox = mangle_tree.trans_id_tree_file_id('blackbox-id')
1534.7.150 by Aaron Bentley
Handled simultaneous renames of parent and child better
314
        mangle_tree.adjust_path('test', root, selftest)
315
        mangle_tree.adjust_path('test_too_much', root, selftest)
316
        mangle_tree.set_executability(True, blackbox)
317
        mangle_tree.apply()
318
319
    def test_both_rename2(self):
320
        create_tree,root = self.get_transform()
321
        bzrlib = create_tree.new_directory('bzrlib', root, 'bzrlib-id')
322
        tests = create_tree.new_directory('tests', bzrlib, 'tests-id')
323
        blackbox = create_tree.new_directory('blackbox', tests, 'blackbox-id')
324
        create_tree.new_file('test_too_much.py', blackbox, 'hello1', 
325
                             'test_too_much-id')
326
        create_tree.apply()        
327
        mangle_tree,root = self.get_transform()
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
328
        bzrlib = mangle_tree.trans_id_tree_file_id('bzrlib-id')
329
        tests = mangle_tree.trans_id_tree_file_id('tests-id')
330
        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
331
        mangle_tree.adjust_path('selftest', bzrlib, tests)
332
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much) 
333
        mangle_tree.set_executability(True, test_too_much)
334
        mangle_tree.apply()
335
336
    def test_both_rename3(self):
337
        create_tree,root = self.get_transform()
338
        tests = create_tree.new_directory('tests', root, 'tests-id')
339
        create_tree.new_file('test_too_much.py', tests, 'hello1', 
340
                             'test_too_much-id')
341
        create_tree.apply()        
342
        mangle_tree,root = self.get_transform()
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
343
        tests = mangle_tree.trans_id_tree_file_id('tests-id')
344
        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
345
        mangle_tree.adjust_path('selftest', root, tests)
346
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much) 
347
        mangle_tree.set_executability(True, test_too_much)
348
        mangle_tree.apply()
349
1534.7.48 by Aaron Bentley
Ensured we can move/rename dangling inventory entries
350
    def test_move_dangling_ie(self):
1534.7.59 by Aaron Bentley
Simplified tests
351
        create_tree, root = self.get_transform()
352
        # prepare tree
1731.1.33 by Aaron Bentley
Revert no-special-root changes
353
        root = create_tree.root
1534.7.59 by Aaron Bentley
Simplified tests
354
        create_tree.new_file('name1', root, 'hello1', 'name1')
355
        create_tree.apply()
356
        delete_contents, root = self.get_transform()
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
357
        file = delete_contents.trans_id_tree_file_id('name1')
1534.7.59 by Aaron Bentley
Simplified tests
358
        delete_contents.delete_contents(file)
359
        delete_contents.apply()
360
        move_id, root = self.get_transform()
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
361
        name1 = move_id.trans_id_tree_file_id('name1')
1534.7.59 by Aaron Bentley
Simplified tests
362
        newdir = move_id.new_directory('dir', root, 'newdir')
363
        move_id.adjust_path('name2', newdir, name1)
364
        move_id.apply()
1534.7.48 by Aaron Bentley
Ensured we can move/rename dangling inventory entries
365
        
1534.7.50 by Aaron Bentley
Detect duplicate inventory ids
366
    def test_replace_dangling_ie(self):
1534.7.59 by Aaron Bentley
Simplified tests
367
        create_tree, root = self.get_transform()
368
        # prepare tree
1731.1.33 by Aaron Bentley
Revert no-special-root changes
369
        root = create_tree.root
1534.7.59 by Aaron Bentley
Simplified tests
370
        create_tree.new_file('name1', root, 'hello1', 'name1')
371
        create_tree.apply()
372
        delete_contents = TreeTransform(self.wt)
373
        self.addCleanup(delete_contents.finalize)
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
374
        file = delete_contents.trans_id_tree_file_id('name1')
1534.7.59 by Aaron Bentley
Simplified tests
375
        delete_contents.delete_contents(file)
376
        delete_contents.apply()
377
        delete_contents.finalize()
378
        replace = TreeTransform(self.wt)
379
        self.addCleanup(replace.finalize)
380
        name2 = replace.new_file('name2', root, 'hello2', 'name1')
381
        conflicts = replace.find_conflicts()
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
382
        name1 = replace.trans_id_tree_file_id('name1')
1534.7.59 by Aaron Bentley
Simplified tests
383
        self.assertEqual(conflicts, [('duplicate id', name1, name2)])
384
        resolve_conflicts(replace)
385
        replace.apply()
1534.7.48 by Aaron Bentley
Ensured we can move/rename dangling inventory entries
386
1534.7.43 by abentley
Fixed some Windows bugs, introduced a conflicts bug
387
    def test_symlinks(self):
1534.7.45 by Aaron Bentley
Skipped symlink test more correctly
388
        if not has_symlinks():
389
            raise TestSkipped('Symlinks are not supported on this platform')
1534.7.59 by Aaron Bentley
Simplified tests
390
        transform,root = self.get_transform()
391
        oz_id = transform.new_directory('oz', root, 'oz-id')
392
        wizard = transform.new_symlink('wizard', oz_id, 'wizard-target', 
393
                                       'wizard-id')
394
        wiz_id = transform.create_path('wizard2', oz_id)
395
        transform.create_symlink('behind_curtain', wiz_id)
396
        transform.version_file('wiz-id2', wiz_id)            
1534.7.71 by abentley
All tests pass under Windows
397
        transform.set_executability(True, wiz_id)
398
        self.assertEqual(transform.find_conflicts(), 
399
                         [('non-file executability', wiz_id)])
400
        transform.set_executability(None, wiz_id)
1534.7.59 by Aaron Bentley
Simplified tests
401
        transform.apply()
402
        self.assertEqual(self.wt.path2id('oz/wizard'), 'wizard-id')
1534.7.100 by Aaron Bentley
Fixed path-relative test cases
403
        self.assertEqual(file_kind(self.wt.abspath('oz/wizard')), 'symlink')
404
        self.assertEqual(os.readlink(self.wt.abspath('oz/wizard2')), 
405
                         'behind_curtain')
406
        self.assertEqual(os.readlink(self.wt.abspath('oz/wizard')),
407
                         'wizard-target')
1534.7.60 by Aaron Bentley
Tested existing conflict resolution functionality
408
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
409
    def get_conflicted(self):
1534.7.60 by Aaron Bentley
Tested existing conflict resolution functionality
410
        create,root = self.get_transform()
411
        create.new_file('dorothy', root, 'dorothy', 'dorothy-id')
1534.7.61 by Aaron Bentley
Handled parent loops, missing parents, unversioned parents
412
        oz = create.new_directory('oz', root, 'oz-id')
413
        create.new_directory('emeraldcity', oz, 'emerald-id')
1534.7.60 by Aaron Bentley
Tested existing conflict resolution functionality
414
        create.apply()
415
        conflicts,root = self.get_transform()
1534.7.65 by Aaron Bentley
Text cleaup/docs
416
        # set up duplicate entry, duplicate id
1534.7.60 by Aaron Bentley
Tested existing conflict resolution functionality
417
        new_dorothy = conflicts.new_file('dorothy', root, 'dorothy', 
418
                                         'dorothy-id')
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
419
        old_dorothy = conflicts.trans_id_tree_file_id('dorothy-id')
420
        oz = conflicts.trans_id_tree_file_id('oz-id')
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
421
        # set up DeletedParent parent conflict
1534.7.65 by Aaron Bentley
Text cleaup/docs
422
        conflicts.delete_versioned(oz)
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
423
        emerald = conflicts.trans_id_tree_file_id('emerald-id')
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
424
        # set up MissingParent conflict
425
        munchkincity = conflicts.trans_id_file_id('munchkincity-id')
426
        conflicts.adjust_path('munchkincity', root, munchkincity)
427
        conflicts.new_directory('auntem', munchkincity, 'auntem-id')
1534.7.65 by Aaron Bentley
Text cleaup/docs
428
        # set up parent loop
1534.7.61 by Aaron Bentley
Handled parent loops, missing parents, unversioned parents
429
        conflicts.adjust_path('emeraldcity', emerald, emerald)
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
430
        return conflicts, emerald, oz, old_dorothy, new_dorothy
431
432
    def test_conflict_resolution(self):
433
        conflicts, emerald, oz, old_dorothy, new_dorothy =\
434
            self.get_conflicted()
1534.7.60 by Aaron Bentley
Tested existing conflict resolution functionality
435
        resolve_conflicts(conflicts)
436
        self.assertEqual(conflicts.final_name(old_dorothy), 'dorothy.moved')
437
        self.assertIs(conflicts.final_file_id(old_dorothy), None)
438
        self.assertEqual(conflicts.final_name(new_dorothy), 'dorothy')
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
439
        self.assertEqual(conflicts.final_file_id(new_dorothy), 'dorothy-id')
1534.7.64 by Aaron Bentley
Extra testing
440
        self.assertEqual(conflicts.final_parent(emerald), oz)
1534.7.63 by Aaron Bentley
Ensure transform can be applied after resolution
441
        conflicts.apply()
1534.7.62 by Aaron Bentley
Fixed moving versioned directories
442
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
443
    def test_cook_conflicts(self):
444
        tt, emerald, oz, old_dorothy, new_dorothy = self.get_conflicted()
445
        raw_conflicts = resolve_conflicts(tt)
446
        cooked_conflicts = cook_conflicts(raw_conflicts, tt)
1534.10.20 by Aaron Bentley
Got all tests passing
447
        duplicate = DuplicateEntry('Moved existing file to', 'dorothy.moved', 
448
                                   'dorothy', None, 'dorothy-id')
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
449
        self.assertEqual(cooked_conflicts[0], duplicate)
1534.10.20 by Aaron Bentley
Got all tests passing
450
        duplicate_id = DuplicateID('Unversioned existing file', 
451
                                   'dorothy.moved', 'dorothy', None,
452
                                   'dorothy-id')
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
453
        self.assertEqual(cooked_conflicts[1], duplicate_id)
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
454
        missing_parent = MissingParent('Created directory', 'munchkincity',
455
                                       'munchkincity-id')
456
        deleted_parent = DeletingParent('Not deleting', 'oz', 'oz-id')
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
457
        self.assertEqual(cooked_conflicts[2], missing_parent)
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
458
        unversioned_parent = UnversionedParent('Versioned directory',
459
                                               'munchkincity',
460
                                               'munchkincity-id')
461
        unversioned_parent2 = UnversionedParent('Versioned directory', 'oz',
1534.10.20 by Aaron Bentley
Got all tests passing
462
                                               'oz-id')
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
463
        self.assertEqual(cooked_conflicts[3], unversioned_parent)
1534.10.20 by Aaron Bentley
Got all tests passing
464
        parent_loop = ParentLoop('Cancelled move', 'oz/emeraldcity', 
465
                                 'oz/emeraldcity', 'emerald-id', 'emerald-id')
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
466
        self.assertEqual(cooked_conflicts[4], deleted_parent)
467
        self.assertEqual(cooked_conflicts[5], unversioned_parent2)
468
        self.assertEqual(cooked_conflicts[6], parent_loop)
469
        self.assertEqual(len(cooked_conflicts), 7)
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
470
        tt.finalize()
471
472
    def test_string_conflicts(self):
473
        tt, emerald, oz, old_dorothy, new_dorothy = self.get_conflicted()
474
        raw_conflicts = resolve_conflicts(tt)
475
        cooked_conflicts = cook_conflicts(raw_conflicts, tt)
476
        tt.finalize()
1534.10.24 by Aaron Bentley
Eliminated conflicts_to_strings, made remove_files a ConflictList member
477
        conflicts_s = [str(c) for c in cooked_conflicts]
1534.7.171 by Aaron Bentley
Implemented stringifying filesystem conflicts
478
        self.assertEqual(len(cooked_conflicts), len(conflicts_s))
479
        self.assertEqual(conflicts_s[0], 'Conflict adding file dorothy.  '
480
                                         'Moved existing file to '
481
                                         'dorothy.moved.')
482
        self.assertEqual(conflicts_s[1], 'Conflict adding id to dorothy.  '
483
                                         'Unversioned existing file '
484
                                         'dorothy.moved.')
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
485
        self.assertEqual(conflicts_s[2], 'Conflict adding files to'
486
                                         ' munchkincity.  Created directory.')
487
        self.assertEqual(conflicts_s[3], 'Conflict because munchkincity is not'
488
                                         ' versioned, but has versioned'
489
                                         ' children.  Versioned directory.')
1551.8.23 by Aaron Bentley
Tweaked conflict message to be more understandable
490
        self.assertEqualDiff(conflicts_s[4], "Conflict: can't delete oz because it"
491
                                         " is not empty.  Not deleting.")
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
492
        self.assertEqual(conflicts_s[5], 'Conflict because oz is not'
493
                                         ' versioned, but has versioned'
494
                                         ' children.  Versioned directory.')
495
        self.assertEqual(conflicts_s[6], 'Conflict moving oz/emeraldcity into'
1534.7.171 by Aaron Bentley
Implemented stringifying filesystem conflicts
496
                                         ' oz/emeraldcity.  Cancelled move.')
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
497
1534.7.62 by Aaron Bentley
Fixed moving versioned directories
498
    def test_moving_versioned_directories(self):
499
        create, root = self.get_transform()
500
        kansas = create.new_directory('kansas', root, 'kansas-id')
501
        create.new_directory('house', kansas, 'house-id')
502
        create.new_directory('oz', root, 'oz-id')
503
        create.apply()
504
        cyclone, root = self.get_transform()
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
505
        oz = cyclone.trans_id_tree_file_id('oz-id')
506
        house = cyclone.trans_id_tree_file_id('house-id')
1534.7.62 by Aaron Bentley
Fixed moving versioned directories
507
        cyclone.adjust_path('house', oz, house)
508
        cyclone.apply()
1534.7.66 by Aaron Bentley
Ensured we don't accidentally move the root directory
509
510
    def test_moving_root(self):
1534.7.68 by Aaron Bentley
Got semi-reasonable root directory renaming working
511
        create, root = self.get_transform()
512
        fun = create.new_directory('fun', root, 'fun-id')
513
        create.new_directory('sun', root, 'sun-id')
514
        create.new_directory('moon', root, 'moon')
515
        create.apply()
1534.7.66 by Aaron Bentley
Ensured we don't accidentally move the root directory
516
        transform, root = self.get_transform()
1534.7.68 by Aaron Bentley
Got semi-reasonable root directory renaming working
517
        transform.adjust_root_path('oldroot', fun)
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
518
        new_root=transform.trans_id_tree_path('')
1534.7.69 by Aaron Bentley
Got real root moves working
519
        transform.version_file('new-root', new_root)
1534.7.68 by Aaron Bentley
Got semi-reasonable root directory renaming working
520
        transform.apply()
1534.7.93 by Aaron Bentley
Added text merge test
521
1534.7.114 by Aaron Bentley
Added file renaming test case
522
    def test_renames(self):
523
        create, root = self.get_transform()
524
        old = create.new_directory('old-parent', root, 'old-id')
525
        intermediate = create.new_directory('intermediate', old, 'im-id')
526
        myfile = create.new_file('myfile', intermediate, 'myfile-text',
527
                                 'myfile-id')
528
        create.apply()
529
        rename, root = self.get_transform()
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
530
        old = rename.trans_id_file_id('old-id')
1534.7.114 by Aaron Bentley
Added file renaming test case
531
        rename.adjust_path('new', root, old)
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
532
        myfile = rename.trans_id_file_id('myfile-id')
1534.7.114 by Aaron Bentley
Added file renaming test case
533
        rename.set_executability(True, myfile)
534
        rename.apply()
535
1534.7.123 by Aaron Bentley
Fixed handling of unversioned files
536
    def test_find_interesting(self):
537
        create, root = self.get_transform()
538
        wt = create._tree
539
        create.new_file('vfile', root, 'myfile-text', 'myfile-id')
540
        create.new_file('uvfile', root, 'othertext')
541
        create.apply()
2255.7.48 by Robert Collins
Deprecated and make work with DirState trees 'transform.find_interesting'.
542
        result = self.applyDeprecated(symbol_versioning.zero_fifteen,
543
            find_interesting, wt, wt, ['vfile'])
544
        self.assertEqual(result, set(['myfile-id']))
1534.7.123 by Aaron Bentley
Fixed handling of unversioned files
545
1740.2.4 by Aaron Bentley
Update transform tests and docs
546
    def test_set_executability_order(self):
547
        """Ensure that executability behaves the same, no matter what order.
548
        
549
        - create file and set executability simultaneously
550
        - create file and set executability afterward
551
        - unsetting the executability of a file whose executability has not been
552
        declared should throw an exception (this may happen when a
553
        merge attempts to create a file with a duplicate ID)
554
        """
555
        transform, root = self.get_transform()
556
        wt = transform._tree
557
        transform.new_file('set_on_creation', root, 'Set on creation', 'soc',
558
                           True)
2502.1.1 by Aaron Bentley
Ensure renames only root children are renamed when building trees
559
        sac = transform.new_file('set_after_creation', root,
560
                                 'Set after creation', 'sac')
1740.2.4 by Aaron Bentley
Update transform tests and docs
561
        transform.set_executability(True, sac)
2502.1.1 by Aaron Bentley
Ensure renames only root children are renamed when building trees
562
        uws = transform.new_file('unset_without_set', root, 'Unset badly',
563
                                 'uws')
1740.2.4 by Aaron Bentley
Update transform tests and docs
564
        self.assertRaises(KeyError, transform.set_executability, None, uws)
565
        transform.apply()
566
        self.assertTrue(wt.is_executable('soc'))
567
        self.assertTrue(wt.is_executable('sac'))
568
1534.12.2 by Aaron Bentley
Added test for preserving file mode
569
    def test_preserve_mode(self):
570
        """File mode is preserved when replacing content"""
571
        if sys.platform == 'win32':
572
            raise TestSkipped('chmod has no effect on win32')
573
        transform, root = self.get_transform()
574
        transform.new_file('file1', root, 'contents', 'file1-id', True)
575
        transform.apply()
576
        self.assertTrue(self.wt.is_executable('file1-id'))
577
        transform, root = self.get_transform()
578
        file1_id = transform.trans_id_tree_file_id('file1-id')
579
        transform.delete_contents(file1_id)
580
        transform.create_file('contents2', file1_id)
581
        transform.apply()
582
        self.assertTrue(self.wt.is_executable('file1-id'))
583
2027.1.1 by John Arbash Meinel
Fix bug #56549, and write a direct test that the right path is being statted
584
    def test__set_mode_stats_correctly(self):
585
        """_set_mode stats to determine file mode."""
586
        if sys.platform == 'win32':
587
            raise TestSkipped('chmod has no effect on win32')
588
589
        stat_paths = []
590
        real_stat = os.stat
591
        def instrumented_stat(path):
592
            stat_paths.append(path)
593
            return real_stat(path)
594
595
        transform, root = self.get_transform()
596
597
        bar1_id = transform.new_file('bar', root, 'bar contents 1\n',
598
                                     file_id='bar-id-1', executable=False)
599
        transform.apply()
600
601
        transform, root = self.get_transform()
602
        bar1_id = transform.trans_id_tree_path('bar')
603
        bar2_id = transform.trans_id_tree_path('bar2')
604
        try:
605
            os.stat = instrumented_stat
606
            transform.create_file('bar2 contents\n', bar2_id, mode_id=bar1_id)
607
        finally:
608
            os.stat = real_stat
609
            transform.finalize()
610
611
        bar1_abspath = self.wt.abspath('bar')
612
        self.assertEqual([bar1_abspath], stat_paths)
613
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
614
    def test_iter_changes(self):
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
615
        self.wt.set_root_id('eert_toor')
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
616
        transform, root = self.get_transform()
617
        transform.new_file('old', root, 'blah', 'id-1', True)
618
        transform.apply()
619
        transform, root = self.get_transform()
620
        try:
621
            self.assertEqual([], list(transform._iter_changes()))
622
            old = transform.trans_id_tree_file_id('id-1')
623
            transform.unversion_file(old)
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
624
            self.assertEqual([('id-1', ('old', None), False, (True, False),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
625
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
626
                (True, True))], list(transform._iter_changes()))
627
            transform.new_directory('new', root, 'id-1')
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
628
            self.assertEqual([('id-1', ('old', 'new'), True, (True, True),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
629
                ('eert_toor', 'eert_toor'), ('old', 'new'),
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
630
                ('file', 'directory'),
631
                (True, False))], list(transform._iter_changes()))
632
        finally:
633
            transform.finalize()
634
635
    def test_iter_changes_new(self):
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
636
        self.wt.set_root_id('eert_toor')
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
637
        transform, root = self.get_transform()
638
        transform.new_file('old', root, 'blah')
639
        transform.apply()
640
        transform, root = self.get_transform()
641
        try:
642
            old = transform.trans_id_tree_path('old')
643
            transform.version_file('id-1', old)
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
644
            self.assertEqual([('id-1', (None, 'old'), False, (False, True),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
645
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
646
                (False, False))], list(transform._iter_changes()))
647
        finally:
648
            transform.finalize()
649
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
650
    def test_iter_changes_modifications(self):
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
651
        self.wt.set_root_id('eert_toor')
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
652
        transform, root = self.get_transform()
653
        transform.new_file('old', root, 'blah', 'id-1')
654
        transform.new_file('new', root, 'blah')
655
        transform.new_directory('subdir', root, 'subdir-id')
656
        transform.apply()
657
        transform, root = self.get_transform()
658
        try:
659
            old = transform.trans_id_tree_path('old')
660
            subdir = transform.trans_id_tree_file_id('subdir-id')
661
            new = transform.trans_id_tree_path('new')
662
            self.assertEqual([], list(transform._iter_changes()))
663
664
            #content deletion
665
            transform.delete_contents(old)
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
666
            self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
667
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', None),
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
668
                (False, False))], list(transform._iter_changes()))
669
670
            #content change
671
            transform.create_file('blah', old)
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
672
            self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
673
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
674
                (False, False))], list(transform._iter_changes()))
675
            transform.cancel_deletion(old)
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
676
            self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
677
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
678
                (False, False))], list(transform._iter_changes()))
679
            transform.cancel_creation(old)
680
681
            # move file_id to a different file
682
            self.assertEqual([], list(transform._iter_changes()))
683
            transform.unversion_file(old)
684
            transform.version_file('id-1', new)
685
            transform.adjust_path('old', root, new)
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
686
            self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
687
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
688
                (False, False))], list(transform._iter_changes()))
689
            transform.cancel_versioning(new)
690
            transform._removed_id = set()
691
692
            #execute bit
693
            self.assertEqual([], list(transform._iter_changes()))
694
            transform.set_executability(True, old)
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
695
            self.assertEqual([('id-1', ('old', 'old'), False, (True, True),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
696
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
697
                (False, True))], list(transform._iter_changes()))
698
            transform.set_executability(None, old)
699
700
            # filename
701
            self.assertEqual([], list(transform._iter_changes()))
702
            transform.adjust_path('new', root, old)
703
            transform._new_parent = {}
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
704
            self.assertEqual([('id-1', ('old', 'new'), False, (True, True),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
705
                ('eert_toor', 'eert_toor'), ('old', 'new'), ('file', 'file'),
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
706
                (False, False))], list(transform._iter_changes()))
707
            transform._new_name = {}
708
709
            # parent directory
710
            self.assertEqual([], list(transform._iter_changes()))
711
            transform.adjust_path('new', subdir, old)
712
            transform._new_name = {}
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
713
            self.assertEqual([('id-1', ('old', 'subdir/old'), False,
2255.2.180 by Martin Pool
merge dirstate
714
                (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.
715
                ('file', 'file'), (False, False))],
716
                list(transform._iter_changes()))
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
717
            transform._new_path = {}
718
719
        finally:
720
            transform.finalize()
721
1551.11.7 by Aaron Bentley
Stop modified flag bleeding into later changes
722
    def test_iter_changes_modified_bleed(self):
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
723
        self.wt.set_root_id('eert_toor')
1551.11.7 by Aaron Bentley
Stop modified flag bleeding into later changes
724
        """Modified flag should not bleed from one change to another"""
725
        # unfortunately, we have no guarantee that file1 (which is modified)
726
        # will be applied before file2.  And if it's applied after file2, it
727
        # obviously can't bleed into file2's change output.  But for now, it
728
        # works.
729
        transform, root = self.get_transform()
730
        transform.new_file('file1', root, 'blah', 'id-1')
731
        transform.new_file('file2', root, 'blah', 'id-2')
732
        transform.apply()
733
        transform, root = self.get_transform()
734
        try:
735
            transform.delete_contents(transform.trans_id_file_id('id-1'))
736
            transform.set_executability(True,
737
            transform.trans_id_file_id('id-2'))
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
738
            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
739
                ('eert_toor', 'eert_toor'), ('file1', u'file1'),
1551.11.7 by Aaron Bentley
Stop modified flag bleeding into later changes
740
                ('file', None), (False, False)),
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
741
                ('id-2', (u'file2', u'file2'), False, (True, True),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
742
                ('eert_toor', 'eert_toor'), ('file2', u'file2'),
1551.11.7 by Aaron Bentley
Stop modified flag bleeding into later changes
743
                ('file', 'file'), (False, True))],
744
                list(transform._iter_changes()))
745
        finally:
746
            transform.finalize()
747
1551.10.37 by Aaron Bentley
recommit of TreeTransform._iter_changes fix with missing files
748
    def test_iter_changes_move_missing(self):
749
        """Test moving ids with no files around"""
750
        self.wt.set_root_id('toor_eert')
751
        # Need two steps because versioning a non-existant file is a conflict.
752
        transform, root = self.get_transform()
753
        transform.new_directory('floater', root, 'floater-id')
754
        transform.apply()
755
        transform, root = self.get_transform()
756
        transform.delete_contents(transform.trans_id_tree_path('floater'))
757
        transform.apply()
758
        transform, root = self.get_transform()
759
        floater = transform.trans_id_tree_path('floater')
760
        try:
761
            transform.adjust_path('flitter', root, floater)
762
            self.assertEqual([('floater-id', ('floater', 'flitter'), False,
763
            (True, True), ('toor_eert', 'toor_eert'), ('floater', 'flitter'),
764
            (None, None), (False, False))], list(transform._iter_changes()))
765
        finally:
766
            transform.finalize()
767
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
768
    def test_iter_changes_pointless(self):
769
        """Ensure that no-ops are not treated as modifications"""
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
770
        self.wt.set_root_id('eert_toor')
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
771
        transform, root = self.get_transform()
772
        transform.new_file('old', root, 'blah', 'id-1')
773
        transform.new_directory('subdir', root, 'subdir-id')
774
        transform.apply()
775
        transform, root = self.get_transform()
776
        try:
777
            old = transform.trans_id_tree_path('old')
778
            subdir = transform.trans_id_tree_file_id('subdir-id')
779
            self.assertEqual([], list(transform._iter_changes()))
780
            transform.delete_contents(subdir)
781
            transform.create_directory(subdir)
782
            transform.set_executability(False, old)
783
            transform.unversion_file(old)
784
            transform.version_file('id-1', old)
785
            transform.adjust_path('old', root, old)
786
            self.assertEqual([], list(transform._iter_changes()))
787
        finally:
788
            transform.finalize()
1534.7.93 by Aaron Bentley
Added text merge test
789
2502.1.1 by Aaron Bentley
Ensure renames only root children are renamed when building trees
790
    def test_rename_count(self):
791
        transform, root = self.get_transform()
2502.1.8 by Aaron Bentley
Updates from review comments
792
        transform.new_file('name1', root, 'contents')
2502.1.1 by Aaron Bentley
Ensure renames only root children are renamed when building trees
793
        self.assertEqual(transform.rename_count, 0)
794
        transform.apply()
795
        self.assertEqual(transform.rename_count, 1)
796
        transform2, root = self.get_transform()
2502.1.8 by Aaron Bentley
Updates from review comments
797
        transform2.adjust_path('name2', root,
798
                               transform2.trans_id_tree_path('name1'))
2502.1.1 by Aaron Bentley
Ensure renames only root children are renamed when building trees
799
        self.assertEqual(transform2.rename_count, 0)
800
        transform2.apply()
801
        self.assertEqual(transform2.rename_count, 2)
802
2502.1.2 by Aaron Bentley
Make the limited-renames functionality safer in the general case
803
    def test_change_parent(self):
2502.1.8 by Aaron Bentley
Updates from review comments
804
        """Ensure that after we change a parent, the results are still right.
805
806
        Renames and parent changes on pending transforms can happen as part
807
        of conflict resolution, and are explicitly permitted by the
808
        TreeTransform API.
809
810
        This test ensures they work correctly with the rename-avoidance
811
        optimization.
812
        """
2502.1.2 by Aaron Bentley
Make the limited-renames functionality safer in the general case
813
        transform, root = self.get_transform()
2502.1.8 by Aaron Bentley
Updates from review comments
814
        parent1 = transform.new_directory('parent1', root)
815
        child1 = transform.new_file('child1', parent1, 'contents')
816
        parent2 = transform.new_directory('parent2', root)
817
        transform.adjust_path('child1', parent2, child1)
2502.1.2 by Aaron Bentley
Make the limited-renames functionality safer in the general case
818
        transform.apply()
2502.1.8 by Aaron Bentley
Updates from review comments
819
        self.failIfExists(self.wt.abspath('parent1/child1'))
820
        self.failUnlessExists(self.wt.abspath('parent2/child1'))
821
        # rename limbo/new-1 => parent1, rename limbo/new-3 => parent2
822
        # 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
823
        self.failUnlessEqual(2, transform.rename_count)
824
825
    def test_cancel_parent(self):
826
        """Cancelling a parent doesn't cause deletion of a non-empty directory
827
828
        This is like the test_change_parent, except that we cancel the parent
829
        before adjusting the path.  The transform must detect that the
830
        directory is non-empty, and move children to safe locations.
831
        """
832
        transform, root = self.get_transform()
2502.1.8 by Aaron Bentley
Updates from review comments
833
        parent1 = transform.new_directory('parent1', root)
834
        child1 = transform.new_file('child1', parent1, 'contents')
835
        child2 = transform.new_file('child2', parent1, 'contents')
836
        try:
837
            transform.cancel_creation(parent1)
838
        except OSError:
839
            self.fail('Failed to move child1 before deleting parent1')
840
        transform.cancel_creation(child2)
841
        transform.create_directory(parent1)
842
        try:
843
            transform.cancel_creation(parent1)
844
        # If the transform incorrectly believes that child2 is still in
845
        # parent1's limbo directory, it will try to rename it and fail
846
        # because was already moved by the first cancel_creation.
847
        except OSError:
848
            self.fail('Transform still thinks child2 is a child of parent1')
849
        parent2 = transform.new_directory('parent2', root)
850
        transform.adjust_path('child1', parent2, child1)
2502.1.2 by Aaron Bentley
Make the limited-renames functionality safer in the general case
851
        transform.apply()
2502.1.8 by Aaron Bentley
Updates from review comments
852
        self.failIfExists(self.wt.abspath('parent1'))
853
        self.failUnlessExists(self.wt.abspath('parent2/child1'))
854
        # 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
855
        self.failUnlessEqual(2, transform.rename_count)
856
857
    def test_adjust_and_cancel(self):
2502.1.8 by Aaron Bentley
Updates from review comments
858
        """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
859
        transform, root = self.get_transform()
2502.1.8 by Aaron Bentley
Updates from review comments
860
        parent1 = transform.new_directory('parent1', root)
861
        child1 = transform.new_file('child1', parent1, 'contents')
862
        parent2 = transform.new_directory('parent2', root)
863
        transform.adjust_path('child1', parent2, child1)
864
        transform.cancel_creation(child1)
2502.1.2 by Aaron Bentley
Make the limited-renames functionality safer in the general case
865
        try:
2502.1.8 by Aaron Bentley
Updates from review comments
866
            transform.cancel_creation(parent1)
867
        # if the transform thinks child1 is still in parent1's limbo
868
        # 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
869
        except OSError:
2502.1.8 by Aaron Bentley
Updates from review comments
870
            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
871
        transform.finalize()
872
2502.1.3 by Aaron Bentley
Don't cause errors when creating contents for trans_ids with no parent/name
873
    def test_noname_contents(self):
2502.1.8 by Aaron Bentley
Updates from review comments
874
        """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
875
        transform, root = self.get_transform()
2502.1.8 by Aaron Bentley
Updates from review comments
876
        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
877
        try:
2502.1.8 by Aaron Bentley
Updates from review comments
878
            transform.create_directory(parent)
2502.1.3 by Aaron Bentley
Don't cause errors when creating contents for trans_ids with no parent/name
879
        except KeyError:
880
            self.fail("Can't handle contents with no name")
881
        transform.finalize()
882
2502.1.9 by Aaron Bentley
Add additional test for no-name contents
883
    def test_noname_contents_nested(self):
884
        """TreeTransform should permit deferring naming files."""
885
        transform, root = self.get_transform()
886
        parent = transform.trans_id_file_id('parent-id')
887
        try:
888
            transform.create_directory(parent)
889
        except KeyError:
890
            self.fail("Can't handle contents with no name")
891
        child = transform.new_directory('child', parent)
892
        transform.adjust_path('parent', root, parent)
893
        transform.apply()
894
        self.failUnlessExists(self.wt.abspath('parent/child'))
895
        self.assertEqual(1, transform.rename_count)
896
2502.1.4 by Aaron Bentley
Ensure we only reuse limbo names appropriately
897
    def test_reuse_name(self):
898
        """Avoid reusing the same limbo name for different files"""
899
        transform, root = self.get_transform()
2502.1.8 by Aaron Bentley
Updates from review comments
900
        parent = transform.new_directory('parent', root)
901
        child1 = transform.new_directory('child', parent)
2502.1.4 by Aaron Bentley
Ensure we only reuse limbo names appropriately
902
        try:
2502.1.8 by Aaron Bentley
Updates from review comments
903
            child2 = transform.new_directory('child', parent)
2502.1.4 by Aaron Bentley
Ensure we only reuse limbo names appropriately
904
        except OSError:
905
            self.fail('Tranform tried to use the same limbo name twice')
2502.1.8 by Aaron Bentley
Updates from review comments
906
        transform.adjust_path('child2', parent, child2)
2502.1.4 by Aaron Bentley
Ensure we only reuse limbo names appropriately
907
        transform.apply()
2502.1.8 by Aaron Bentley
Updates from review comments
908
        # limbo/new-1 => parent, limbo/new-3 => parent/child2
909
        # child2 is put into top-level limbo because child1 has already
910
        # claimed the direct limbo path when child2 is created.  There is no
911
        # advantage in renaming files once they're in top-level limbo, except
912
        # as part of apply.
2502.1.4 by Aaron Bentley
Ensure we only reuse limbo names appropriately
913
        self.assertEqual(2, transform.rename_count)
914
915
    def test_reuse_when_first_moved(self):
916
        """Don't avoid direct paths when it is safe to use them"""
917
        transform, root = self.get_transform()
2502.1.8 by Aaron Bentley
Updates from review comments
918
        parent = transform.new_directory('parent', root)
919
        child1 = transform.new_directory('child', parent)
920
        transform.adjust_path('child1', parent, child1)
921
        child2 = transform.new_directory('child', parent)
2502.1.4 by Aaron Bentley
Ensure we only reuse limbo names appropriately
922
        transform.apply()
2502.1.8 by Aaron Bentley
Updates from review comments
923
        # limbo/new-1 => parent
2502.1.4 by Aaron Bentley
Ensure we only reuse limbo names appropriately
924
        self.assertEqual(1, transform.rename_count)
925
926
    def test_reuse_after_cancel(self):
927
        """Don't avoid direct paths when it is safe to use them"""
928
        transform, root = self.get_transform()
2502.1.8 by Aaron Bentley
Updates from review comments
929
        parent2 = transform.new_directory('parent2', root)
930
        child1 = transform.new_directory('child1', parent2)
931
        transform.cancel_creation(parent2)
932
        transform.create_directory(parent2)
933
        child2 = transform.new_directory('child1', parent2)
934
        transform.adjust_path('child2', parent2, child1)
2502.1.4 by Aaron Bentley
Ensure we only reuse limbo names appropriately
935
        transform.apply()
2502.1.8 by Aaron Bentley
Updates from review comments
936
        # limbo/new-1 => parent2, limbo/new-2 => parent2/child1
2502.1.4 by Aaron Bentley
Ensure we only reuse limbo names appropriately
937
        self.assertEqual(2, transform.rename_count)
938
2502.1.7 by Aaron Bentley
Fix finalize deletion ordering
939
    def test_finalize_order(self):
2502.1.8 by Aaron Bentley
Updates from review comments
940
        """Finalize must be done in child-to-parent order"""
2502.1.7 by Aaron Bentley
Fix finalize deletion ordering
941
        transform, root = self.get_transform()
2502.1.8 by Aaron Bentley
Updates from review comments
942
        parent = transform.new_directory('parent', root)
943
        child = transform.new_directory('child', parent)
2502.1.7 by Aaron Bentley
Fix finalize deletion ordering
944
        try:
945
            transform.finalize()
946
        except OSError:
2502.1.8 by Aaron Bentley
Updates from review comments
947
            self.fail('Tried to remove parent before child1')
2502.1.7 by Aaron Bentley
Fix finalize deletion ordering
948
2502.1.13 by Aaron Bentley
Updates from review
949
    def test_cancel_with_cancelled_child_should_succeed(self):
2502.1.12 by Aaron Bentley
Avoid renaming children with no content
950
        transform, root = self.get_transform()
951
        parent = transform.new_directory('parent', root)
952
        child = transform.new_directory('child', parent)
953
        transform.cancel_creation(child)
2502.1.13 by Aaron Bentley
Updates from review
954
        transform.cancel_creation(parent)
2502.1.12 by Aaron Bentley
Avoid renaming children with no content
955
        transform.finalize()
956
2625.4.3 by Ian Clatworthy
Add a test to ensure the deprecation worked.
957
    def test_change_entry(self):
2687.2.2 by Martin Pool
Fix up other references to 0.19
958
        txt = 'bzrlib.transform.change_entry was deprecated in version 0.90.'
2625.4.3 by Ian Clatworthy
Add a test to ensure the deprecation worked.
959
        self.callDeprecated([txt], change_entry, None, None, None, None, None,
960
            None, None, None)
961
2502.1.3 by Aaron Bentley
Don't cause errors when creating contents for trans_ids with no parent/name
962
1534.7.93 by Aaron Bentley
Added text merge test
963
class TransformGroup(object):
1731.1.33 by Aaron Bentley
Revert no-special-root changes
964
    def __init__(self, dirname, root_id):
1534.7.101 by Aaron Bentley
Got conflicts on symlinks working properly
965
        self.name = dirname
1534.7.93 by Aaron Bentley
Added text merge test
966
        os.mkdir(dirname)
1558.1.3 by Aaron Bentley
Fixed deprecated op use in test suite
967
        self.wt = BzrDir.create_standalone_workingtree(dirname)
1731.1.33 by Aaron Bentley
Revert no-special-root changes
968
        self.wt.set_root_id(root_id)
1558.1.3 by Aaron Bentley
Fixed deprecated op use in test suite
969
        self.b = self.wt.branch
1534.7.93 by Aaron Bentley
Added text merge test
970
        self.tt = TreeTransform(self.wt)
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
971
        self.root = self.tt.trans_id_tree_file_id(self.wt.get_root_id())
1534.7.93 by Aaron Bentley
Added text merge test
972
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
973
1534.7.95 by Aaron Bentley
Added more text merge tests
974
def conflict_text(tree, merge):
975
    template = '%s TREE\n%s%s\n%s%s MERGE-SOURCE\n'
976
    return template % ('<' * 7, tree, '=' * 7, merge, '>' * 7)
977
1534.7.93 by Aaron Bentley
Added text merge test
978
979
class TestTransformMerge(TestCaseInTempDir):
980
    def test_text_merge(self):
2116.4.1 by John Arbash Meinel
Update file and revision id generators.
981
        root_id = generate_ids.gen_root_id()
1731.1.33 by Aaron Bentley
Revert no-special-root changes
982
        base = TransformGroup("base", root_id)
1534.7.93 by Aaron Bentley
Added text merge test
983
        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
984
        base.tt.new_file('b', base.root, 'b1', 'b')
985
        base.tt.new_file('c', base.root, 'c', 'c')
986
        base.tt.new_file('d', base.root, 'd', 'd')
987
        base.tt.new_file('e', base.root, 'e', 'e')
988
        base.tt.new_file('f', base.root, 'f', 'f')
1534.7.96 by Aaron Bentley
Tested with BASE as directory
989
        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
990
        base.tt.new_directory('h', base.root, 'h')
1534.7.93 by Aaron Bentley
Added text merge test
991
        base.tt.apply()
1731.1.33 by Aaron Bentley
Revert no-special-root changes
992
        other = TransformGroup("other", root_id)
1534.7.93 by Aaron Bentley
Added text merge test
993
        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
994
        other.tt.new_file('b', other.root, 'b2', 'b')
995
        other.tt.new_file('c', other.root, 'c2', 'c')
996
        other.tt.new_file('d', other.root, 'd', 'd')
997
        other.tt.new_file('e', other.root, 'e2', 'e')
998
        other.tt.new_file('f', other.root, 'f', 'f')
1534.7.96 by Aaron Bentley
Tested with BASE as directory
999
        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
1000
        other.tt.new_file('h', other.root, 'h\ni\nj\nk\n', 'h')
1534.7.99 by Aaron Bentley
Handle non-existent BASE properly
1001
        other.tt.new_file('i', other.root, 'h\ni\nj\nk\n', 'i')
1534.7.93 by Aaron Bentley
Added text merge test
1002
        other.tt.apply()
1731.1.33 by Aaron Bentley
Revert no-special-root changes
1003
        this = TransformGroup("this", root_id)
1534.7.93 by Aaron Bentley
Added text merge test
1004
        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
1005
        this.tt.new_file('b', this.root, 'b', 'b')
1006
        this.tt.new_file('c', this.root, 'c', 'c')
1007
        this.tt.new_file('d', this.root, 'd2', 'd')
1008
        this.tt.new_file('e', this.root, 'e2', 'e')
1009
        this.tt.new_file('f', this.root, 'f', 'f')
1534.7.96 by Aaron Bentley
Tested with BASE as directory
1010
        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
1011
        this.tt.new_file('h', this.root, '1\n2\n3\n4\n', 'h')
1534.7.99 by Aaron Bentley
Handle non-existent BASE properly
1012
        this.tt.new_file('i', this.root, '1\n2\n3\n4\n', 'i')
1534.7.93 by Aaron Bentley
Added text merge test
1013
        this.tt.apply()
1014
        Merge3Merger(this.wt, this.wt, base.wt, other.wt)
1534.7.95 by Aaron Bentley
Added more text merge tests
1015
        # textual merge
1534.7.93 by Aaron Bentley
Added text merge test
1016
        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
1017
        # three-way text conflict
1018
        self.assertEqual(this.wt.get_file('b').read(), 
1019
                         conflict_text('b', 'b2'))
1020
        # OTHER wins
1021
        self.assertEqual(this.wt.get_file('c').read(), 'c2')
1022
        # THIS wins
1023
        self.assertEqual(this.wt.get_file('d').read(), 'd2')
1024
        # Ambigious clean merge
1025
        self.assertEqual(this.wt.get_file('e').read(), 'e2')
1026
        # No change
1027
        self.assertEqual(this.wt.get_file('f').read(), 'f')
1534.7.97 by Aaron Bentley
Ensured foo.BASE is a directory if there's a conflict
1028
        # Correct correct results when THIS == OTHER 
1534.7.96 by Aaron Bentley
Tested with BASE as directory
1029
        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
1030
        # Text conflict when THIS & OTHER are text and BASE is dir
1031
        self.assertEqual(this.wt.get_file('h').read(), 
1032
                         conflict_text('1\n2\n3\n4\n', 'h\ni\nj\nk\n'))
1033
        self.assertEqual(this.wt.get_file_byname('h.THIS').read(),
1034
                         '1\n2\n3\n4\n')
1035
        self.assertEqual(this.wt.get_file_byname('h.OTHER').read(),
1036
                         'h\ni\nj\nk\n')
1037
        self.assertEqual(file_kind(this.wt.abspath('h.BASE')), 'directory')
1534.7.99 by Aaron Bentley
Handle non-existent BASE properly
1038
        self.assertEqual(this.wt.get_file('i').read(), 
1039
                         conflict_text('1\n2\n3\n4\n', 'h\ni\nj\nk\n'))
1040
        self.assertEqual(this.wt.get_file_byname('i.THIS').read(),
1041
                         '1\n2\n3\n4\n')
1042
        self.assertEqual(this.wt.get_file_byname('i.OTHER').read(),
1043
                         'h\ni\nj\nk\n')
1044
        self.assertEqual(os.path.exists(this.wt.abspath('i.BASE')), False)
1534.7.192 by Aaron Bentley
Record hashes produced by merges
1045
        modified = ['a', 'b', 'c', 'h', 'i']
1046
        merge_modified = this.wt.merge_modified()
1047
        self.assertSubset(merge_modified, modified)
1048
        self.assertEqual(len(merge_modified), len(modified))
1049
        file(this.wt.id2abspath('a'), 'wb').write('booga')
1050
        modified.pop(0)
1051
        merge_modified = this.wt.merge_modified()
1052
        self.assertSubset(merge_modified, modified)
1053
        self.assertEqual(len(merge_modified), len(modified))
1558.12.10 by Aaron Bentley
Be robust when merge_hash file_id not in inventory
1054
        this.wt.remove('b')
2796.1.4 by Aaron Bentley
Fix up various test cases
1055
        this.wt.revert()
1534.7.101 by Aaron Bentley
Got conflicts on symlinks working properly
1056
1057
    def test_file_merge(self):
1534.7.153 by Aaron Bentley
Handled test cases involving symlinks
1058
        if not has_symlinks():
1059
            raise TestSkipped('Symlinks are not supported on this platform')
2116.4.1 by John Arbash Meinel
Update file and revision id generators.
1060
        root_id = generate_ids.gen_root_id()
1731.1.33 by Aaron Bentley
Revert no-special-root changes
1061
        base = TransformGroup("BASE", root_id)
1062
        this = TransformGroup("THIS", root_id)
1063
        other = TransformGroup("OTHER", root_id)
1534.7.101 by Aaron Bentley
Got conflicts on symlinks working properly
1064
        for tg in this, base, other:
1065
            tg.tt.new_directory('a', tg.root, 'a')
1066
            tg.tt.new_symlink('b', tg.root, 'b', 'b')
1067
            tg.tt.new_file('c', tg.root, 'c', 'c')
1068
            tg.tt.new_symlink('d', tg.root, tg.name, 'd')
1534.7.104 by Aaron Bentley
Fixed set_versioned, enhanced conflict testing
1069
        targets = ((base, 'base-e', 'base-f', None, None), 
1070
                   (this, 'other-e', 'this-f', 'other-g', 'this-h'), 
1071
                   (other, 'other-e', None, 'other-g', 'other-h'))
1072
        for tg, e_target, f_target, g_target, h_target in targets:
1073
            for link, target in (('e', e_target), ('f', f_target), 
1074
                                 ('g', g_target), ('h', h_target)):
1075
                if target is not None:
1076
                    tg.tt.new_symlink(link, tg.root, target, link)
1534.7.102 by Aaron Bentley
Deleted old pre-conflict contents
1077
1078
        for tg in this, base, other:
1534.7.101 by Aaron Bentley
Got conflicts on symlinks working properly
1079
            tg.tt.apply()
1080
        Merge3Merger(this.wt, this.wt, base.wt, other.wt)
1081
        self.assertIs(os.path.isdir(this.wt.abspath('a')), True)
1082
        self.assertIs(os.path.islink(this.wt.abspath('b')), True)
1083
        self.assertIs(os.path.isfile(this.wt.abspath('c')), True)
1084
        for suffix in ('THIS', 'BASE', 'OTHER'):
1085
            self.assertEqual(os.readlink(this.wt.abspath('d.'+suffix)), suffix)
1534.7.102 by Aaron Bentley
Deleted old pre-conflict contents
1086
        self.assertIs(os.path.lexists(this.wt.abspath('d')), False)
1534.7.104 by Aaron Bentley
Fixed set_versioned, enhanced conflict testing
1087
        self.assertEqual(this.wt.id2path('d'), 'd.OTHER')
1088
        self.assertEqual(this.wt.id2path('f'), 'f.THIS')
1534.7.102 by Aaron Bentley
Deleted old pre-conflict contents
1089
        self.assertEqual(os.readlink(this.wt.abspath('e')), 'other-e')
1090
        self.assertIs(os.path.lexists(this.wt.abspath('e.THIS')), False)
1091
        self.assertIs(os.path.lexists(this.wt.abspath('e.OTHER')), False)
1092
        self.assertIs(os.path.lexists(this.wt.abspath('e.BASE')), False)
1534.7.104 by Aaron Bentley
Fixed set_versioned, enhanced conflict testing
1093
        self.assertIs(os.path.lexists(this.wt.abspath('g')), True)
1094
        self.assertIs(os.path.lexists(this.wt.abspath('g.BASE')), False)
1095
        self.assertIs(os.path.lexists(this.wt.abspath('h')), False)
1096
        self.assertIs(os.path.lexists(this.wt.abspath('h.BASE')), False)
1097
        self.assertIs(os.path.lexists(this.wt.abspath('h.THIS')), True)
1098
        self.assertIs(os.path.lexists(this.wt.abspath('h.OTHER')), True)
1534.7.105 by Aaron Bentley
Got merge with rename working
1099
1100
    def test_filename_merge(self):
2116.4.1 by John Arbash Meinel
Update file and revision id generators.
1101
        root_id = generate_ids.gen_root_id()
1731.1.33 by Aaron Bentley
Revert no-special-root changes
1102
        base = TransformGroup("BASE", root_id)
1103
        this = TransformGroup("THIS", root_id)
1104
        other = TransformGroup("OTHER", root_id)
1534.7.105 by Aaron Bentley
Got merge with rename working
1105
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a') 
1106
                                   for t in [base, this, other]]
1107
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b') 
1108
                                   for t in [base, this, other]]
1109
        base.tt.new_directory('c', base_a, 'c')
1110
        this.tt.new_directory('c1', this_a, 'c')
1111
        other.tt.new_directory('c', other_b, 'c')
1112
1113
        base.tt.new_directory('d', base_a, 'd')
1114
        this.tt.new_directory('d1', this_b, 'd')
1115
        other.tt.new_directory('d', other_a, 'd')
1116
1117
        base.tt.new_directory('e', base_a, 'e')
1118
        this.tt.new_directory('e', this_a, 'e')
1119
        other.tt.new_directory('e1', other_b, 'e')
1120
1121
        base.tt.new_directory('f', base_a, 'f')
1122
        this.tt.new_directory('f1', this_b, 'f')
1123
        other.tt.new_directory('f1', other_b, 'f')
1124
1125
        for tg in [this, base, other]:
1126
            tg.tt.apply()
1127
        Merge3Merger(this.wt, this.wt, base.wt, other.wt)
1534.7.176 by abentley
Fixed up tests for Windows
1128
        self.assertEqual(this.wt.id2path('c'), pathjoin('b/c1'))
1129
        self.assertEqual(this.wt.id2path('d'), pathjoin('b/d1'))
1130
        self.assertEqual(this.wt.id2path('e'), pathjoin('b/e1'))
1131
        self.assertEqual(this.wt.id2path('f'), pathjoin('b/f1'))
1534.7.105 by Aaron Bentley
Got merge with rename working
1132
1133
    def test_filename_merge_conflicts(self):
2116.4.1 by John Arbash Meinel
Update file and revision id generators.
1134
        root_id = generate_ids.gen_root_id()
1731.1.33 by Aaron Bentley
Revert no-special-root changes
1135
        base = TransformGroup("BASE", root_id)
1136
        this = TransformGroup("THIS", root_id)
1137
        other = TransformGroup("OTHER", root_id)
1534.7.105 by Aaron Bentley
Got merge with rename working
1138
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a') 
1139
                                   for t in [base, this, other]]
1140
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b') 
1141
                                   for t in [base, this, other]]
1142
1143
        base.tt.new_file('g', base_a, 'g', 'g')
1144
        other.tt.new_file('g1', other_b, 'g1', 'g')
1145
1146
        base.tt.new_file('h', base_a, 'h', 'h')
1147
        this.tt.new_file('h1', this_b, 'h1', 'h')
1148
1149
        base.tt.new_file('i', base.root, 'i', 'i')
1534.7.153 by Aaron Bentley
Handled test cases involving symlinks
1150
        other.tt.new_directory('i1', this_b, 'i')
1534.7.105 by Aaron Bentley
Got merge with rename working
1151
1152
        for tg in [this, base, other]:
1153
            tg.tt.apply()
1154
        Merge3Merger(this.wt, this.wt, base.wt, other.wt)
1155
1534.7.176 by abentley
Fixed up tests for Windows
1156
        self.assertEqual(this.wt.id2path('g'), pathjoin('b/g1.OTHER'))
1534.7.105 by Aaron Bentley
Got merge with rename working
1157
        self.assertIs(os.path.lexists(this.wt.abspath('b/g1.BASE')), True)
1158
        self.assertIs(os.path.lexists(this.wt.abspath('b/g1.THIS')), False)
1534.7.176 by abentley
Fixed up tests for Windows
1159
        self.assertEqual(this.wt.id2path('h'), pathjoin('b/h1.THIS'))
1534.7.105 by Aaron Bentley
Got merge with rename working
1160
        self.assertIs(os.path.lexists(this.wt.abspath('b/h1.BASE')), True)
1161
        self.assertIs(os.path.lexists(this.wt.abspath('b/h1.OTHER')), False)
1534.7.176 by abentley
Fixed up tests for Windows
1162
        self.assertEqual(this.wt.id2path('i'), pathjoin('b/i1.OTHER'))
1534.7.183 by Aaron Bentley
Fixed build_tree with symlinks
1163
2027.1.1 by John Arbash Meinel
Fix bug #56549, and write a direct test that the right path is being statted
1164
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1165
class TestBuildTree(tests.TestCaseWithTransport):
1166
1534.7.183 by Aaron Bentley
Fixed build_tree with symlinks
1167
    def test_build_tree(self):
1168
        if not has_symlinks():
1169
            raise TestSkipped('Test requires symlink support')
1170
        os.mkdir('a')
1171
        a = BzrDir.create_standalone_workingtree('a')
1172
        os.mkdir('a/foo')
1173
        file('a/foo/bar', 'wb').write('contents')
1174
        os.symlink('a/foo/bar', 'a/foo/baz')
1175
        a.add(['foo', 'foo/bar', 'foo/baz'])
1176
        a.commit('initial commit')
1177
        b = BzrDir.create_standalone_workingtree('b')
2255.2.194 by Robert Collins
[BROKEN] Many updates to stop using experimental formats in tests.
1178
        basis = a.basis_tree()
1179
        basis.lock_read()
1180
        self.addCleanup(basis.unlock)
1181
        build_tree(basis, b)
1534.7.183 by Aaron Bentley
Fixed build_tree with symlinks
1182
        self.assertIs(os.path.isdir('b/foo'), True)
1183
        self.assertEqual(file('b/foo/bar', 'rb').read(), "contents")
1184
        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
1185
2100.3.21 by Aaron Bentley
Work on checking out by-reference trees
1186
    def test_build_with_references(self):
2255.2.194 by Robert Collins
[BROKEN] Many updates to stop using experimental formats in tests.
1187
        tree = self.make_branch_and_tree('source',
1188
            format='dirstate-with-subtree')
1189
        subtree = self.make_branch_and_tree('source/subtree',
1190
            format='dirstate-with-subtree')
2100.3.21 by Aaron Bentley
Work on checking out by-reference trees
1191
        tree.add_reference(subtree)
1192
        tree.commit('a revision')
1193
        tree.branch.create_checkout('target')
1194
        self.failUnlessExists('target')
1195
        self.failUnlessExists('target/subtree')
1196
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1197
    def test_file_conflict_handling(self):
1198
        """Ensure that when building trees, conflict handling is done"""
1199
        source = self.make_branch_and_tree('source')
1200
        target = self.make_branch_and_tree('target')
1201
        self.build_tree(['source/file', 'target/file'])
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1202
        source.add('file', 'new-file')
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1203
        source.commit('added file')
1204
        build_tree(source.basis_tree(), target)
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1205
        self.assertEqual([DuplicateEntry('Moved existing file to',
1206
                          'file.moved', 'file', None, 'new-file')],
1207
                         target.conflicts())
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1208
        target2 = self.make_branch_and_tree('target2')
1209
        target_file = file('target2/file', 'wb')
1210
        try:
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1211
            source_file = file('source/file', 'rb')
1212
            try:
1213
                target_file.write(source_file.read())
1214
            finally:
1215
                source_file.close()
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1216
        finally:
1217
            target_file.close()
1218
        build_tree(source.basis_tree(), target2)
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1219
        self.assertEqual([], target2.conflicts())
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1220
1221
    def test_symlink_conflict_handling(self):
1222
        """Ensure that when building trees, conflict handling is done"""
1223
        if not has_symlinks():
1224
            raise TestSkipped('Test requires symlink support')
1225
        source = self.make_branch_and_tree('source')
1226
        os.symlink('foo', 'source/symlink')
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1227
        source.add('symlink', 'new-symlink')
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1228
        source.commit('added file')
1229
        target = self.make_branch_and_tree('target')
1230
        os.symlink('bar', 'target/symlink')
1231
        build_tree(source.basis_tree(), target)
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1232
        self.assertEqual([DuplicateEntry('Moved existing file to',
1233
            'symlink.moved', 'symlink', None, 'new-symlink')],
1234
            target.conflicts())
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1235
        target = self.make_branch_and_tree('target2')
1236
        os.symlink('foo', 'target2/symlink')
1237
        build_tree(source.basis_tree(), target)
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1238
        self.assertEqual([], target.conflicts())
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1239
        
1240
    def test_directory_conflict_handling(self):
1241
        """Ensure that when building trees, conflict handling is done"""
1242
        source = self.make_branch_and_tree('source')
1243
        target = self.make_branch_and_tree('target')
1244
        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
1245
        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
1246
        source.commit('added file')
1247
        build_tree(source.basis_tree(), target)
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1248
        self.assertEqual([], target.conflicts())
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1249
        self.failUnlessExists('target/dir1/file')
1250
1251
        # Ensure contents are merged
1252
        target = self.make_branch_and_tree('target2')
1253
        self.build_tree(['target2/dir1/', 'target2/dir1/file2'])
1254
        build_tree(source.basis_tree(), target)
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1255
        self.assertEqual([], target.conflicts())
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1256
        self.failUnlessExists('target2/dir1/file2')
1257
        self.failUnlessExists('target2/dir1/file')
1258
1259
        # 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
1260
        target = self.make_branch_and_tree('target3')
1261
        self.make_branch('target3/dir1')
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1262
        self.build_tree(['target3/dir1/file2'])
1263
        build_tree(source.basis_tree(), target)
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1264
        self.failIfExists('target3/dir1/file')
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1265
        self.failUnlessExists('target3/dir1/file2')
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1266
        self.failUnlessExists('target3/dir1.diverted/file')
1267
        self.assertEqual([DuplicateEntry('Diverted to',
1268
            'dir1.diverted', 'dir1', 'new-dir1', None)],
1269
            target.conflicts())
1270
1271
        target = self.make_branch_and_tree('target4')
1272
        self.build_tree(['target4/dir1/'])
1273
        self.make_branch('target4/dir1/file')
1274
        build_tree(source.basis_tree(), target)
1275
        self.failUnlessExists('target4/dir1/file')
1276
        self.assertEqual('directory', file_kind('target4/dir1/file'))
1277
        self.failUnlessExists('target4/dir1/file.diverted')
1278
        self.assertEqual([DuplicateEntry('Diverted to',
1279
            'dir1/file.diverted', 'dir1/file', 'new-file', None)],
1280
            target.conflicts())
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1281
1282
    def test_mixed_conflict_handling(self):
1283
        """Ensure that when building trees, conflict handling is done"""
1284
        source = self.make_branch_and_tree('source')
1285
        target = self.make_branch_and_tree('target')
1286
        self.build_tree(['source/name', 'target/name/'])
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1287
        source.add('name', 'new-name')
1288
        source.commit('added file')
1289
        build_tree(source.basis_tree(), target)
1290
        self.assertEqual([DuplicateEntry('Moved existing file to',
1291
            'name.moved', 'name', None, 'new-name')], target.conflicts())
1292
1293
    def test_raises_in_populated(self):
1294
        source = self.make_branch_and_tree('source')
1295
        self.build_tree(['source/name'])
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1296
        source.add('name')
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1297
        source.commit('added name')
1298
        target = self.make_branch_and_tree('target')
1299
        self.build_tree(['target/name'])
1300
        target.add('name')
2090.2.1 by Martin Pool
Fix some code which relies on assertions and breaks under python -O
1301
        self.assertRaises(errors.WorkingTreeAlreadyPopulated, 
1302
            build_tree, source.basis_tree(), target)
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1303
2502.1.1 by Aaron Bentley
Ensure renames only root children are renamed when building trees
1304
    def test_build_tree_rename_count(self):
1305
        source = self.make_branch_and_tree('source')
1306
        self.build_tree(['source/file1', 'source/dir1/'])
1307
        source.add(['file1', 'dir1'])
1308
        source.commit('add1')
1309
        target1 = self.make_branch_and_tree('target1')
2502.1.6 by Aaron Bentley
Update from review comments
1310
        transform_result = build_tree(source.basis_tree(), target1)
1311
        self.assertEqual(2, transform_result.rename_count)
2502.1.1 by Aaron Bentley
Ensure renames only root children are renamed when building trees
1312
1313
        self.build_tree(['source/dir1/file2'])
1314
        source.add(['dir1/file2'])
1315
        source.commit('add3')
1316
        target2 = self.make_branch_and_tree('target2')
2502.1.6 by Aaron Bentley
Update from review comments
1317
        transform_result = build_tree(source.basis_tree(), target2)
2502.1.1 by Aaron Bentley
Ensure renames only root children are renamed when building trees
1318
        # children of non-root directories should not be renamed
2502.1.6 by Aaron Bentley
Update from review comments
1319
        self.assertEqual(2, transform_result.rename_count)
2502.1.1 by Aaron Bentley
Ensure renames only root children are renamed when building trees
1320
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1321
1534.10.28 by Aaron Bentley
Use numbered backup files
1322
class MockTransform(object):
1323
1324
    def has_named_child(self, by_parent, parent_id, name):
1325
        for child_id in by_parent[parent_id]:
1326
            if child_id == '0':
1327
                if name == "name~":
1328
                    return True
1534.10.29 by Aaron Bentley
Fixed backup numbering to match GNU standard better
1329
            elif name == "name.~%s~" % child_id:
1534.10.28 by Aaron Bentley
Use numbered backup files
1330
                return True
1331
        return False
1332
2502.1.1 by Aaron Bentley
Ensure renames only root children are renamed when building trees
1333
1534.10.28 by Aaron Bentley
Use numbered backup files
1334
class MockEntry(object):
1335
    def __init__(self):
1336
        object.__init__(self)
1337
        self.name = "name"
1338
1339
class TestGetBackupName(TestCase):
1340
    def test_get_backup_name(self):
1341
        tt = MockTransform()
1342
        name = get_backup_name(MockEntry(), {'a':[]}, 'a', tt)
1534.10.29 by Aaron Bentley
Fixed backup numbering to match GNU standard better
1343
        self.assertEqual(name, 'name.~1~')
1344
        name = get_backup_name(MockEntry(), {'a':['1']}, 'a', tt)
1345
        self.assertEqual(name, 'name.~2~')
1534.10.28 by Aaron Bentley
Use numbered backup files
1346
        name = get_backup_name(MockEntry(), {'a':['2']}, 'a', tt)
1534.10.29 by Aaron Bentley
Fixed backup numbering to match GNU standard better
1347
        self.assertEqual(name, 'name.~1~')
1534.10.28 by Aaron Bentley
Use numbered backup files
1348
        name = get_backup_name(MockEntry(), {'a':['2'], 'b':[]}, 'b', tt)
1534.10.29 by Aaron Bentley
Fixed backup numbering to match GNU standard better
1349
        self.assertEqual(name, 'name.~1~')
1350
        name = get_backup_name(MockEntry(), {'a':['1', '2', '3']}, 'a', tt)
1351
        self.assertEqual(name, 'name.~4~')
2733.2.1 by Aaron Bentley
Implement FileMover, to support TreeTransform rollback
1352
1353
1354
class TestFileMover(tests.TestCaseWithTransport):
1355
1356
    def test_file_mover(self):
1357
        self.build_tree(['a/', 'a/b', 'c/', 'c/d'])
1358
        mover = _FileMover()
1359
        mover.rename('a', 'q')
1360
        self.failUnlessExists('q')
1361
        self.failIfExists('a')
2733.2.12 by Aaron Bentley
Updates from review
1362
        self.failUnlessExists('q/b')
1363
        self.failUnlessExists('c')
1364
        self.failUnlessExists('c/d')
2733.2.1 by Aaron Bentley
Implement FileMover, to support TreeTransform rollback
1365
2733.2.5 by Aaron Bentley
Implement FileMover.pre_delete and FileMover.apply_deletions
1366
    def test_pre_delete_rollback(self):
1367
        self.build_tree(['a/'])
1368
        mover = _FileMover()
1369
        mover.pre_delete('a', 'q')
1370
        self.failUnlessExists('q')
1371
        self.failIfExists('a')
1372
        mover.rollback()
1373
        self.failIfExists('q')
1374
        self.failUnlessExists('a')
1375
1376
    def test_apply_deletions(self):
2733.2.12 by Aaron Bentley
Updates from review
1377
        self.build_tree(['a/', 'b/'])
2733.2.5 by Aaron Bentley
Implement FileMover.pre_delete and FileMover.apply_deletions
1378
        mover = _FileMover()
1379
        mover.pre_delete('a', 'q')
2733.2.12 by Aaron Bentley
Updates from review
1380
        mover.pre_delete('b', 'r')
2733.2.5 by Aaron Bentley
Implement FileMover.pre_delete and FileMover.apply_deletions
1381
        self.failUnlessExists('q')
2733.2.12 by Aaron Bentley
Updates from review
1382
        self.failUnlessExists('r')
2733.2.5 by Aaron Bentley
Implement FileMover.pre_delete and FileMover.apply_deletions
1383
        self.failIfExists('a')
2733.2.12 by Aaron Bentley
Updates from review
1384
        self.failIfExists('b')
2733.2.5 by Aaron Bentley
Implement FileMover.pre_delete and FileMover.apply_deletions
1385
        mover.apply_deletions()
1386
        self.failIfExists('q')
2733.2.12 by Aaron Bentley
Updates from review
1387
        self.failIfExists('r')
2733.2.5 by Aaron Bentley
Implement FileMover.pre_delete and FileMover.apply_deletions
1388
        self.failIfExists('a')
2733.2.12 by Aaron Bentley
Updates from review
1389
        self.failIfExists('b')
2733.2.5 by Aaron Bentley
Implement FileMover.pre_delete and FileMover.apply_deletions
1390
2733.2.1 by Aaron Bentley
Implement FileMover, to support TreeTransform rollback
1391
    def test_file_mover_rollback(self):
1392
        self.build_tree(['a/', 'a/b', 'c/', 'c/d/', 'c/e/'])
1393
        mover = _FileMover()
1394
        mover.rename('c/d', 'c/f')
1395
        mover.rename('c/e', 'c/d')
1396
        try:
1397
            mover.rename('a', 'c')
1398
        except OSError, e:
1399
            mover.rollback()
1400
        self.failUnlessExists('a')
1401
        self.failUnlessExists('c/d')
2733.2.3 by Aaron Bentley
Test tranform rollback
1402
1403
1404
class Bogus(Exception):
1405
    pass
1406
1407
1408
class TestTransformRollback(tests.TestCaseWithTransport):
1409
1410
    class ExceptionFileMover(_FileMover):
1411
2733.2.4 by Aaron Bentley
Test transform rollback when renaming into place
1412
        def __init__(self, bad_source=None, bad_target=None):
1413
            _FileMover.__init__(self)
1414
            self.bad_source = bad_source
1415
            self.bad_target = bad_target
1416
2733.2.3 by Aaron Bentley
Test tranform rollback
1417
        def rename(self, source, target):
2733.2.4 by Aaron Bentley
Test transform rollback when renaming into place
1418
            if (self.bad_source is not None and
1419
                source.endswith(self.bad_source)):
1420
                raise Bogus
1421
            elif (self.bad_target is not None and
1422
                target.endswith(self.bad_target)):
2733.2.3 by Aaron Bentley
Test tranform rollback
1423
                raise Bogus
1424
            else:
1425
                _FileMover.rename(self, source, target)
1426
1427
    def test_rollback_rename(self):
1428
        tree = self.make_branch_and_tree('.')
1429
        self.build_tree(['a/', 'a/b'])
1430
        tt = TreeTransform(tree)
1431
        self.addCleanup(tt.finalize)
1432
        a_id = tt.trans_id_tree_path('a')
1433
        tt.adjust_path('c', tt.root, a_id)
1434
        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
1435
        self.assertRaises(Bogus, tt.apply,
1436
                          _mover=self.ExceptionFileMover(bad_source='a'))
1437
        self.failUnlessExists('a')
1438
        self.failUnlessExists('a/b')
1439
        tt.apply()
1440
        self.failUnlessExists('c')
1441
        self.failUnlessExists('c/d')
1442
1443
    def test_rollback_rename_into_place(self):
1444
        tree = self.make_branch_and_tree('.')
1445
        self.build_tree(['a/', 'a/b'])
1446
        tt = TreeTransform(tree)
1447
        self.addCleanup(tt.finalize)
1448
        a_id = tt.trans_id_tree_path('a')
1449
        tt.adjust_path('c', tt.root, a_id)
1450
        tt.adjust_path('d', a_id, tt.trans_id_tree_path('a/b'))
1451
        self.assertRaises(Bogus, tt.apply,
1452
                          _mover=self.ExceptionFileMover(bad_target='c/d'))
1453
        self.failUnlessExists('a')
1454
        self.failUnlessExists('a/b')
1455
        tt.apply()
1456
        self.failUnlessExists('c')
1457
        self.failUnlessExists('c/d')
2733.2.6 by Aaron Bentley
Make TreeTransform commits rollbackable
1458
1459
    def test_rollback_deletion(self):
1460
        tree = self.make_branch_and_tree('.')
1461
        self.build_tree(['a/', 'a/b'])
1462
        tt = TreeTransform(tree)
1463
        self.addCleanup(tt.finalize)
1464
        a_id = tt.trans_id_tree_path('a')
1465
        tt.delete_contents(a_id)
1466
        tt.adjust_path('d', tt.root, tt.trans_id_tree_path('a/b'))
1467
        self.assertRaises(Bogus, tt.apply,
1468
                          _mover=self.ExceptionFileMover(bad_target='d'))
1469
        self.failUnlessExists('a')
1470
        self.failUnlessExists('a/b')