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