/brz/remove-bazaar

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