~bzr-pqm/bzr/bzr.dev

1534.7.106 by Aaron Bentley
Cleaned up imports, added copyright statements
1
# Copyright (C) 2006 Canonical Ltd
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
2
#
1534.7.106 by Aaron Bentley
Cleaned up imports, added copyright statements
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
7
#
1534.7.106 by Aaron Bentley
Cleaned up imports, added copyright statements
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
12
#
1534.7.106 by Aaron Bentley
Cleaned up imports, added copyright statements
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
import os
2027.1.1 by John Arbash Meinel
Fix bug #56549, and write a direct test that the right path is being statted
18
import stat
19
import sys
1558.1.3 by Aaron Bentley
Fixed deprecated op use in test suite
20
2027.1.1 by John Arbash Meinel
Fix bug #56549, and write a direct test that the right path is being statted
21
from bzrlib import (
2090.2.1 by Martin Pool
Fix some code which relies on assertions and breaks under python -O
22
    errors,
2116.4.1 by John Arbash Meinel
Update file and revision id generators.
23
    generate_ids,
2255.7.48 by Robert Collins
Deprecated and make work with DirState trees 'transform.find_interesting'.
24
    symbol_versioning,
2027.1.1 by John Arbash Meinel
Fix bug #56549, and write a direct test that the right path is being statted
25
    tests,
26
    urlutils,
27
    )
1558.1.3 by Aaron Bentley
Fixed deprecated op use in test suite
28
from bzrlib.bzrdir import BzrDir
1534.10.24 by Aaron Bentley
Eliminated conflicts_to_strings, made remove_files a ConflictList member
29
from bzrlib.conflicts import (DuplicateEntry, DuplicateID, MissingParent,
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
30
                              UnversionedParent, ParentLoop, DeletingParent,)
1534.7.32 by Aaron Bentley
Got conflict handling working when conflicts involve existing files
31
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
1551.7.17 by Aaron Bentley
Switch to PathsNotVersioned, accept extra_trees
32
                           ReusingTransform, CantMoveRoot, 
33
                           PathsNotVersionedError, ExistingLimbo,
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
398
    def get_conflicted(self):
1534.7.60 by Aaron Bentley
Tested existing conflict resolution functionality
399
        create,root = self.get_transform()
400
        create.new_file('dorothy', root, 'dorothy', 'dorothy-id')
1534.7.61 by Aaron Bentley
Handled parent loops, missing parents, unversioned parents
401
        oz = create.new_directory('oz', root, 'oz-id')
402
        create.new_directory('emeraldcity', oz, 'emerald-id')
1534.7.60 by Aaron Bentley
Tested existing conflict resolution functionality
403
        create.apply()
404
        conflicts,root = self.get_transform()
1534.7.65 by Aaron Bentley
Text cleaup/docs
405
        # set up duplicate entry, duplicate id
1534.7.60 by Aaron Bentley
Tested existing conflict resolution functionality
406
        new_dorothy = conflicts.new_file('dorothy', root, 'dorothy', 
407
                                         'dorothy-id')
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
408
        old_dorothy = conflicts.trans_id_tree_file_id('dorothy-id')
409
        oz = conflicts.trans_id_tree_file_id('oz-id')
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
410
        # set up DeletedParent parent conflict
1534.7.65 by Aaron Bentley
Text cleaup/docs
411
        conflicts.delete_versioned(oz)
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
412
        emerald = conflicts.trans_id_tree_file_id('emerald-id')
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
413
        # set up MissingParent conflict
414
        munchkincity = conflicts.trans_id_file_id('munchkincity-id')
415
        conflicts.adjust_path('munchkincity', root, munchkincity)
416
        conflicts.new_directory('auntem', munchkincity, 'auntem-id')
1534.7.65 by Aaron Bentley
Text cleaup/docs
417
        # set up parent loop
1534.7.61 by Aaron Bentley
Handled parent loops, missing parents, unversioned parents
418
        conflicts.adjust_path('emeraldcity', emerald, emerald)
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
419
        return conflicts, emerald, oz, old_dorothy, new_dorothy
420
421
    def test_conflict_resolution(self):
422
        conflicts, emerald, oz, old_dorothy, new_dorothy =\
423
            self.get_conflicted()
1534.7.60 by Aaron Bentley
Tested existing conflict resolution functionality
424
        resolve_conflicts(conflicts)
425
        self.assertEqual(conflicts.final_name(old_dorothy), 'dorothy.moved')
426
        self.assertIs(conflicts.final_file_id(old_dorothy), None)
427
        self.assertEqual(conflicts.final_name(new_dorothy), 'dorothy')
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
428
        self.assertEqual(conflicts.final_file_id(new_dorothy), 'dorothy-id')
1534.7.64 by Aaron Bentley
Extra testing
429
        self.assertEqual(conflicts.final_parent(emerald), oz)
1534.7.63 by Aaron Bentley
Ensure transform can be applied after resolution
430
        conflicts.apply()
1534.7.62 by Aaron Bentley
Fixed moving versioned directories
431
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
432
    def test_cook_conflicts(self):
433
        tt, emerald, oz, old_dorothy, new_dorothy = self.get_conflicted()
434
        raw_conflicts = resolve_conflicts(tt)
435
        cooked_conflicts = cook_conflicts(raw_conflicts, tt)
1534.10.20 by Aaron Bentley
Got all tests passing
436
        duplicate = DuplicateEntry('Moved existing file to', 'dorothy.moved', 
437
                                   'dorothy', None, 'dorothy-id')
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
438
        self.assertEqual(cooked_conflicts[0], duplicate)
1534.10.20 by Aaron Bentley
Got all tests passing
439
        duplicate_id = DuplicateID('Unversioned existing file', 
440
                                   'dorothy.moved', 'dorothy', None,
441
                                   'dorothy-id')
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
442
        self.assertEqual(cooked_conflicts[1], duplicate_id)
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
443
        missing_parent = MissingParent('Created directory', 'munchkincity',
444
                                       'munchkincity-id')
445
        deleted_parent = DeletingParent('Not deleting', 'oz', 'oz-id')
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
446
        self.assertEqual(cooked_conflicts[2], missing_parent)
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
447
        unversioned_parent = UnversionedParent('Versioned directory',
448
                                               'munchkincity',
449
                                               'munchkincity-id')
450
        unversioned_parent2 = UnversionedParent('Versioned directory', 'oz',
1534.10.20 by Aaron Bentley
Got all tests passing
451
                                               'oz-id')
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
452
        self.assertEqual(cooked_conflicts[3], unversioned_parent)
1534.10.20 by Aaron Bentley
Got all tests passing
453
        parent_loop = ParentLoop('Cancelled move', 'oz/emeraldcity', 
454
                                 'oz/emeraldcity', 'emerald-id', 'emerald-id')
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
455
        self.assertEqual(cooked_conflicts[4], deleted_parent)
456
        self.assertEqual(cooked_conflicts[5], unversioned_parent2)
457
        self.assertEqual(cooked_conflicts[6], parent_loop)
458
        self.assertEqual(len(cooked_conflicts), 7)
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
459
        tt.finalize()
460
461
    def test_string_conflicts(self):
462
        tt, emerald, oz, old_dorothy, new_dorothy = self.get_conflicted()
463
        raw_conflicts = resolve_conflicts(tt)
464
        cooked_conflicts = cook_conflicts(raw_conflicts, tt)
465
        tt.finalize()
1534.10.24 by Aaron Bentley
Eliminated conflicts_to_strings, made remove_files a ConflictList member
466
        conflicts_s = [str(c) for c in cooked_conflicts]
1534.7.171 by Aaron Bentley
Implemented stringifying filesystem conflicts
467
        self.assertEqual(len(cooked_conflicts), len(conflicts_s))
468
        self.assertEqual(conflicts_s[0], 'Conflict adding file dorothy.  '
469
                                         'Moved existing file to '
470
                                         'dorothy.moved.')
471
        self.assertEqual(conflicts_s[1], 'Conflict adding id to dorothy.  '
472
                                         'Unversioned existing file '
473
                                         'dorothy.moved.')
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
474
        self.assertEqual(conflicts_s[2], 'Conflict adding files to'
475
                                         ' munchkincity.  Created directory.')
476
        self.assertEqual(conflicts_s[3], 'Conflict because munchkincity is not'
477
                                         ' versioned, but has versioned'
478
                                         ' children.  Versioned directory.')
1551.8.23 by Aaron Bentley
Tweaked conflict message to be more understandable
479
        self.assertEqualDiff(conflicts_s[4], "Conflict: can't delete oz because it"
480
                                         " is not empty.  Not deleting.")
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
481
        self.assertEqual(conflicts_s[5], 'Conflict because oz is not'
482
                                         ' versioned, but has versioned'
483
                                         ' children.  Versioned directory.')
484
        self.assertEqual(conflicts_s[6], 'Conflict moving oz/emeraldcity into'
1534.7.171 by Aaron Bentley
Implemented stringifying filesystem conflicts
485
                                         ' oz/emeraldcity.  Cancelled move.')
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
486
1534.7.62 by Aaron Bentley
Fixed moving versioned directories
487
    def test_moving_versioned_directories(self):
488
        create, root = self.get_transform()
489
        kansas = create.new_directory('kansas', root, 'kansas-id')
490
        create.new_directory('house', kansas, 'house-id')
491
        create.new_directory('oz', root, 'oz-id')
492
        create.apply()
493
        cyclone, root = self.get_transform()
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
494
        oz = cyclone.trans_id_tree_file_id('oz-id')
495
        house = cyclone.trans_id_tree_file_id('house-id')
1534.7.62 by Aaron Bentley
Fixed moving versioned directories
496
        cyclone.adjust_path('house', oz, house)
497
        cyclone.apply()
1534.7.66 by Aaron Bentley
Ensured we don't accidentally move the root directory
498
499
    def test_moving_root(self):
1534.7.68 by Aaron Bentley
Got semi-reasonable root directory renaming working
500
        create, root = self.get_transform()
501
        fun = create.new_directory('fun', root, 'fun-id')
502
        create.new_directory('sun', root, 'sun-id')
503
        create.new_directory('moon', root, 'moon')
504
        create.apply()
1534.7.66 by Aaron Bentley
Ensured we don't accidentally move the root directory
505
        transform, root = self.get_transform()
1534.7.68 by Aaron Bentley
Got semi-reasonable root directory renaming working
506
        transform.adjust_root_path('oldroot', fun)
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
507
        new_root=transform.trans_id_tree_path('')
1534.7.69 by Aaron Bentley
Got real root moves working
508
        transform.version_file('new-root', new_root)
1534.7.68 by Aaron Bentley
Got semi-reasonable root directory renaming working
509
        transform.apply()
1534.7.93 by Aaron Bentley
Added text merge test
510
1534.7.114 by Aaron Bentley
Added file renaming test case
511
    def test_renames(self):
512
        create, root = self.get_transform()
513
        old = create.new_directory('old-parent', root, 'old-id')
514
        intermediate = create.new_directory('intermediate', old, 'im-id')
515
        myfile = create.new_file('myfile', intermediate, 'myfile-text',
516
                                 'myfile-id')
517
        create.apply()
518
        rename, root = self.get_transform()
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
519
        old = rename.trans_id_file_id('old-id')
1534.7.114 by Aaron Bentley
Added file renaming test case
520
        rename.adjust_path('new', root, old)
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
521
        myfile = rename.trans_id_file_id('myfile-id')
1534.7.114 by Aaron Bentley
Added file renaming test case
522
        rename.set_executability(True, myfile)
523
        rename.apply()
524
1534.7.123 by Aaron Bentley
Fixed handling of unversioned files
525
    def test_find_interesting(self):
526
        create, root = self.get_transform()
527
        wt = create._tree
528
        create.new_file('vfile', root, 'myfile-text', 'myfile-id')
529
        create.new_file('uvfile', root, 'othertext')
530
        create.apply()
2255.7.48 by Robert Collins
Deprecated and make work with DirState trees 'transform.find_interesting'.
531
        result = self.applyDeprecated(symbol_versioning.zero_fifteen,
532
            find_interesting, wt, wt, ['vfile'])
533
        self.assertEqual(result, set(['myfile-id']))
1534.7.123 by Aaron Bentley
Fixed handling of unversioned files
534
1740.2.4 by Aaron Bentley
Update transform tests and docs
535
    def test_set_executability_order(self):
536
        """Ensure that executability behaves the same, no matter what order.
537
        
538
        - create file and set executability simultaneously
539
        - create file and set executability afterward
540
        - unsetting the executability of a file whose executability has not been
541
        declared should throw an exception (this may happen when a
542
        merge attempts to create a file with a duplicate ID)
543
        """
544
        transform, root = self.get_transform()
545
        wt = transform._tree
546
        transform.new_file('set_on_creation', root, 'Set on creation', 'soc',
547
                           True)
548
        sac = transform.new_file('set_after_creation', root, 'Set after creation', 'sac')
549
        transform.set_executability(True, sac)
550
        uws = transform.new_file('unset_without_set', root, 'Unset badly', 'uws')
551
        self.assertRaises(KeyError, transform.set_executability, None, uws)
552
        transform.apply()
553
        self.assertTrue(wt.is_executable('soc'))
554
        self.assertTrue(wt.is_executable('sac'))
555
1534.12.2 by Aaron Bentley
Added test for preserving file mode
556
    def test_preserve_mode(self):
557
        """File mode is preserved when replacing content"""
558
        if sys.platform == 'win32':
559
            raise TestSkipped('chmod has no effect on win32')
560
        transform, root = self.get_transform()
561
        transform.new_file('file1', root, 'contents', 'file1-id', True)
562
        transform.apply()
563
        self.assertTrue(self.wt.is_executable('file1-id'))
564
        transform, root = self.get_transform()
565
        file1_id = transform.trans_id_tree_file_id('file1-id')
566
        transform.delete_contents(file1_id)
567
        transform.create_file('contents2', file1_id)
568
        transform.apply()
569
        self.assertTrue(self.wt.is_executable('file1-id'))
570
2027.1.1 by John Arbash Meinel
Fix bug #56549, and write a direct test that the right path is being statted
571
    def test__set_mode_stats_correctly(self):
572
        """_set_mode stats to determine file mode."""
573
        if sys.platform == 'win32':
574
            raise TestSkipped('chmod has no effect on win32')
575
576
        stat_paths = []
577
        real_stat = os.stat
578
        def instrumented_stat(path):
579
            stat_paths.append(path)
580
            return real_stat(path)
581
582
        transform, root = self.get_transform()
583
584
        bar1_id = transform.new_file('bar', root, 'bar contents 1\n',
585
                                     file_id='bar-id-1', executable=False)
586
        transform.apply()
587
588
        transform, root = self.get_transform()
589
        bar1_id = transform.trans_id_tree_path('bar')
590
        bar2_id = transform.trans_id_tree_path('bar2')
591
        try:
592
            os.stat = instrumented_stat
593
            transform.create_file('bar2 contents\n', bar2_id, mode_id=bar1_id)
594
        finally:
595
            os.stat = real_stat
596
            transform.finalize()
597
598
        bar1_abspath = self.wt.abspath('bar')
599
        self.assertEqual([bar1_abspath], stat_paths)
600
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
601
    def test_iter_changes(self):
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
602
        self.wt.set_root_id('eert_toor')
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
603
        transform, root = self.get_transform()
604
        transform.new_file('old', root, 'blah', 'id-1', True)
605
        transform.apply()
606
        transform, root = self.get_transform()
607
        try:
608
            self.assertEqual([], list(transform._iter_changes()))
609
            old = transform.trans_id_tree_file_id('id-1')
610
            transform.unversion_file(old)
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
611
            self.assertEqual([('id-1', ('old', None), False, (True, False),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
612
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
613
                (True, True))], list(transform._iter_changes()))
614
            transform.new_directory('new', root, 'id-1')
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
615
            self.assertEqual([('id-1', ('old', 'new'), True, (True, True),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
616
                ('eert_toor', 'eert_toor'), ('old', 'new'),
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
617
                ('file', 'directory'),
618
                (True, False))], list(transform._iter_changes()))
619
        finally:
620
            transform.finalize()
621
622
    def test_iter_changes_new(self):
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
623
        self.wt.set_root_id('eert_toor')
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
624
        transform, root = self.get_transform()
625
        transform.new_file('old', root, 'blah')
626
        transform.apply()
627
        transform, root = self.get_transform()
628
        try:
629
            old = transform.trans_id_tree_path('old')
630
            transform.version_file('id-1', old)
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
631
            self.assertEqual([('id-1', (None, 'old'), False, (False, True),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
632
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
633
                (False, False))], list(transform._iter_changes()))
634
        finally:
635
            transform.finalize()
636
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
637
    def test_iter_changes_modifications(self):
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
638
        self.wt.set_root_id('eert_toor')
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
639
        transform, root = self.get_transform()
640
        transform.new_file('old', root, 'blah', 'id-1')
641
        transform.new_file('new', root, 'blah')
642
        transform.new_directory('subdir', root, 'subdir-id')
643
        transform.apply()
644
        transform, root = self.get_transform()
645
        try:
646
            old = transform.trans_id_tree_path('old')
647
            subdir = transform.trans_id_tree_file_id('subdir-id')
648
            new = transform.trans_id_tree_path('new')
649
            self.assertEqual([], list(transform._iter_changes()))
650
651
            #content deletion
652
            transform.delete_contents(old)
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
653
            self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
654
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', None),
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
655
                (False, False))], list(transform._iter_changes()))
656
657
            #content change
658
            transform.create_file('blah', old)
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
659
            self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
660
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
661
                (False, False))], list(transform._iter_changes()))
662
            transform.cancel_deletion(old)
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
663
            self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
664
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
665
                (False, False))], list(transform._iter_changes()))
666
            transform.cancel_creation(old)
667
668
            # move file_id to a different file
669
            self.assertEqual([], list(transform._iter_changes()))
670
            transform.unversion_file(old)
671
            transform.version_file('id-1', new)
672
            transform.adjust_path('old', root, new)
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
673
            self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
674
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
675
                (False, False))], list(transform._iter_changes()))
676
            transform.cancel_versioning(new)
677
            transform._removed_id = set()
678
679
            #execute bit
680
            self.assertEqual([], list(transform._iter_changes()))
681
            transform.set_executability(True, old)
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
682
            self.assertEqual([('id-1', ('old', 'old'), False, (True, True),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
683
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
684
                (False, True))], list(transform._iter_changes()))
685
            transform.set_executability(None, old)
686
687
            # filename
688
            self.assertEqual([], list(transform._iter_changes()))
689
            transform.adjust_path('new', root, old)
690
            transform._new_parent = {}
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
691
            self.assertEqual([('id-1', ('old', 'new'), False, (True, True),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
692
                ('eert_toor', 'eert_toor'), ('old', 'new'), ('file', 'file'),
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
693
                (False, False))], list(transform._iter_changes()))
694
            transform._new_name = {}
695
696
            # parent directory
697
            self.assertEqual([], list(transform._iter_changes()))
698
            transform.adjust_path('new', subdir, old)
699
            transform._new_name = {}
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
700
            self.assertEqual([('id-1', ('old', 'subdir/old'), False,
2255.2.180 by Martin Pool
merge dirstate
701
                (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.
702
                ('file', 'file'), (False, False))],
703
                list(transform._iter_changes()))
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
704
            transform._new_path = {}
705
706
        finally:
707
            transform.finalize()
708
1551.11.7 by Aaron Bentley
Stop modified flag bleeding into later changes
709
    def test_iter_changes_modified_bleed(self):
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
710
        self.wt.set_root_id('eert_toor')
1551.11.7 by Aaron Bentley
Stop modified flag bleeding into later changes
711
        """Modified flag should not bleed from one change to another"""
712
        # unfortunately, we have no guarantee that file1 (which is modified)
713
        # will be applied before file2.  And if it's applied after file2, it
714
        # obviously can't bleed into file2's change output.  But for now, it
715
        # works.
716
        transform, root = self.get_transform()
717
        transform.new_file('file1', root, 'blah', 'id-1')
718
        transform.new_file('file2', root, 'blah', 'id-2')
719
        transform.apply()
720
        transform, root = self.get_transform()
721
        try:
722
            transform.delete_contents(transform.trans_id_file_id('id-1'))
723
            transform.set_executability(True,
724
            transform.trans_id_file_id('id-2'))
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
725
            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
726
                ('eert_toor', 'eert_toor'), ('file1', u'file1'),
1551.11.7 by Aaron Bentley
Stop modified flag bleeding into later changes
727
                ('file', None), (False, False)),
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
728
                ('id-2', (u'file2', u'file2'), False, (True, True),
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
729
                ('eert_toor', 'eert_toor'), ('file2', u'file2'),
1551.11.7 by Aaron Bentley
Stop modified flag bleeding into later changes
730
                ('file', 'file'), (False, True))],
731
                list(transform._iter_changes()))
732
        finally:
733
            transform.finalize()
734
1551.10.37 by Aaron Bentley
recommit of TreeTransform._iter_changes fix with missing files
735
    def test_iter_changes_move_missing(self):
736
        """Test moving ids with no files around"""
737
        self.wt.set_root_id('toor_eert')
738
        # Need two steps because versioning a non-existant file is a conflict.
739
        transform, root = self.get_transform()
740
        transform.new_directory('floater', root, 'floater-id')
741
        transform.apply()
742
        transform, root = self.get_transform()
743
        transform.delete_contents(transform.trans_id_tree_path('floater'))
744
        transform.apply()
745
        transform, root = self.get_transform()
746
        floater = transform.trans_id_tree_path('floater')
747
        try:
748
            transform.adjust_path('flitter', root, floater)
749
            self.assertEqual([('floater-id', ('floater', 'flitter'), False,
750
            (True, True), ('toor_eert', 'toor_eert'), ('floater', 'flitter'),
751
            (None, None), (False, False))], list(transform._iter_changes()))
752
        finally:
753
            transform.finalize()
754
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
755
    def test_iter_changes_pointless(self):
756
        """Ensure that no-ops are not treated as modifications"""
2100.3.33 by Aaron Bentley
Handle unique roots in tests for TreeTransform.iter_changes
757
        self.wt.set_root_id('eert_toor')
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
758
        transform, root = self.get_transform()
759
        transform.new_file('old', root, 'blah', 'id-1')
760
        transform.new_directory('subdir', root, 'subdir-id')
761
        transform.apply()
762
        transform, root = self.get_transform()
763
        try:
764
            old = transform.trans_id_tree_path('old')
765
            subdir = transform.trans_id_tree_file_id('subdir-id')
766
            self.assertEqual([], list(transform._iter_changes()))
767
            transform.delete_contents(subdir)
768
            transform.create_directory(subdir)
769
            transform.set_executability(False, old)
770
            transform.unversion_file(old)
771
            transform.version_file('id-1', old)
772
            transform.adjust_path('old', root, old)
773
            self.assertEqual([], list(transform._iter_changes()))
774
        finally:
775
            transform.finalize()
1534.7.93 by Aaron Bentley
Added text merge test
776
777
class TransformGroup(object):
1731.1.33 by Aaron Bentley
Revert no-special-root changes
778
    def __init__(self, dirname, root_id):
1534.7.101 by Aaron Bentley
Got conflicts on symlinks working properly
779
        self.name = dirname
1534.7.93 by Aaron Bentley
Added text merge test
780
        os.mkdir(dirname)
1558.1.3 by Aaron Bentley
Fixed deprecated op use in test suite
781
        self.wt = BzrDir.create_standalone_workingtree(dirname)
1731.1.33 by Aaron Bentley
Revert no-special-root changes
782
        self.wt.set_root_id(root_id)
1558.1.3 by Aaron Bentley
Fixed deprecated op use in test suite
783
        self.b = self.wt.branch
1534.7.93 by Aaron Bentley
Added text merge test
784
        self.tt = TreeTransform(self.wt)
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
785
        self.root = self.tt.trans_id_tree_file_id(self.wt.get_root_id())
1534.7.93 by Aaron Bentley
Added text merge test
786
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
787
1534.7.95 by Aaron Bentley
Added more text merge tests
788
def conflict_text(tree, merge):
789
    template = '%s TREE\n%s%s\n%s%s MERGE-SOURCE\n'
790
    return template % ('<' * 7, tree, '=' * 7, merge, '>' * 7)
791
1534.7.93 by Aaron Bentley
Added text merge test
792
793
class TestTransformMerge(TestCaseInTempDir):
794
    def test_text_merge(self):
2116.4.1 by John Arbash Meinel
Update file and revision id generators.
795
        root_id = generate_ids.gen_root_id()
1731.1.33 by Aaron Bentley
Revert no-special-root changes
796
        base = TransformGroup("base", root_id)
1534.7.93 by Aaron Bentley
Added text merge test
797
        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
798
        base.tt.new_file('b', base.root, 'b1', 'b')
799
        base.tt.new_file('c', base.root, 'c', 'c')
800
        base.tt.new_file('d', base.root, 'd', 'd')
801
        base.tt.new_file('e', base.root, 'e', 'e')
802
        base.tt.new_file('f', base.root, 'f', 'f')
1534.7.96 by Aaron Bentley
Tested with BASE as directory
803
        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
804
        base.tt.new_directory('h', base.root, 'h')
1534.7.93 by Aaron Bentley
Added text merge test
805
        base.tt.apply()
1731.1.33 by Aaron Bentley
Revert no-special-root changes
806
        other = TransformGroup("other", root_id)
1534.7.93 by Aaron Bentley
Added text merge test
807
        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
808
        other.tt.new_file('b', other.root, 'b2', 'b')
809
        other.tt.new_file('c', other.root, 'c2', 'c')
810
        other.tt.new_file('d', other.root, 'd', 'd')
811
        other.tt.new_file('e', other.root, 'e2', 'e')
812
        other.tt.new_file('f', other.root, 'f', 'f')
1534.7.96 by Aaron Bentley
Tested with BASE as directory
813
        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
814
        other.tt.new_file('h', other.root, 'h\ni\nj\nk\n', 'h')
1534.7.99 by Aaron Bentley
Handle non-existent BASE properly
815
        other.tt.new_file('i', other.root, 'h\ni\nj\nk\n', 'i')
1534.7.93 by Aaron Bentley
Added text merge test
816
        other.tt.apply()
1731.1.33 by Aaron Bentley
Revert no-special-root changes
817
        this = TransformGroup("this", root_id)
1534.7.93 by Aaron Bentley
Added text merge test
818
        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
819
        this.tt.new_file('b', this.root, 'b', 'b')
820
        this.tt.new_file('c', this.root, 'c', 'c')
821
        this.tt.new_file('d', this.root, 'd2', 'd')
822
        this.tt.new_file('e', this.root, 'e2', 'e')
823
        this.tt.new_file('f', this.root, 'f', 'f')
1534.7.96 by Aaron Bentley
Tested with BASE as directory
824
        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
825
        this.tt.new_file('h', this.root, '1\n2\n3\n4\n', 'h')
1534.7.99 by Aaron Bentley
Handle non-existent BASE properly
826
        this.tt.new_file('i', this.root, '1\n2\n3\n4\n', 'i')
1534.7.93 by Aaron Bentley
Added text merge test
827
        this.tt.apply()
828
        Merge3Merger(this.wt, this.wt, base.wt, other.wt)
1534.7.95 by Aaron Bentley
Added more text merge tests
829
        # textual merge
1534.7.93 by Aaron Bentley
Added text merge test
830
        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
831
        # three-way text conflict
832
        self.assertEqual(this.wt.get_file('b').read(), 
833
                         conflict_text('b', 'b2'))
834
        # OTHER wins
835
        self.assertEqual(this.wt.get_file('c').read(), 'c2')
836
        # THIS wins
837
        self.assertEqual(this.wt.get_file('d').read(), 'd2')
838
        # Ambigious clean merge
839
        self.assertEqual(this.wt.get_file('e').read(), 'e2')
840
        # No change
841
        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
842
        # Correct correct results when THIS == OTHER 
1534.7.96 by Aaron Bentley
Tested with BASE as directory
843
        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
844
        # Text conflict when THIS & OTHER are text and BASE is dir
845
        self.assertEqual(this.wt.get_file('h').read(), 
846
                         conflict_text('1\n2\n3\n4\n', 'h\ni\nj\nk\n'))
847
        self.assertEqual(this.wt.get_file_byname('h.THIS').read(),
848
                         '1\n2\n3\n4\n')
849
        self.assertEqual(this.wt.get_file_byname('h.OTHER').read(),
850
                         'h\ni\nj\nk\n')
851
        self.assertEqual(file_kind(this.wt.abspath('h.BASE')), 'directory')
1534.7.99 by Aaron Bentley
Handle non-existent BASE properly
852
        self.assertEqual(this.wt.get_file('i').read(), 
853
                         conflict_text('1\n2\n3\n4\n', 'h\ni\nj\nk\n'))
854
        self.assertEqual(this.wt.get_file_byname('i.THIS').read(),
855
                         '1\n2\n3\n4\n')
856
        self.assertEqual(this.wt.get_file_byname('i.OTHER').read(),
857
                         'h\ni\nj\nk\n')
858
        self.assertEqual(os.path.exists(this.wt.abspath('i.BASE')), False)
1534.7.192 by Aaron Bentley
Record hashes produced by merges
859
        modified = ['a', 'b', 'c', 'h', 'i']
860
        merge_modified = this.wt.merge_modified()
861
        self.assertSubset(merge_modified, modified)
862
        self.assertEqual(len(merge_modified), len(modified))
863
        file(this.wt.id2abspath('a'), 'wb').write('booga')
864
        modified.pop(0)
865
        merge_modified = this.wt.merge_modified()
866
        self.assertSubset(merge_modified, modified)
867
        self.assertEqual(len(merge_modified), len(modified))
1558.12.10 by Aaron Bentley
Be robust when merge_hash file_id not in inventory
868
        this.wt.remove('b')
869
        this.wt.revert([])
1534.7.101 by Aaron Bentley
Got conflicts on symlinks working properly
870
871
    def test_file_merge(self):
1534.7.153 by Aaron Bentley
Handled test cases involving symlinks
872
        if not has_symlinks():
873
            raise TestSkipped('Symlinks are not supported on this platform')
2116.4.1 by John Arbash Meinel
Update file and revision id generators.
874
        root_id = generate_ids.gen_root_id()
1731.1.33 by Aaron Bentley
Revert no-special-root changes
875
        base = TransformGroup("BASE", root_id)
876
        this = TransformGroup("THIS", root_id)
877
        other = TransformGroup("OTHER", root_id)
1534.7.101 by Aaron Bentley
Got conflicts on symlinks working properly
878
        for tg in this, base, other:
879
            tg.tt.new_directory('a', tg.root, 'a')
880
            tg.tt.new_symlink('b', tg.root, 'b', 'b')
881
            tg.tt.new_file('c', tg.root, 'c', 'c')
882
            tg.tt.new_symlink('d', tg.root, tg.name, 'd')
1534.7.104 by Aaron Bentley
Fixed set_versioned, enhanced conflict testing
883
        targets = ((base, 'base-e', 'base-f', None, None), 
884
                   (this, 'other-e', 'this-f', 'other-g', 'this-h'), 
885
                   (other, 'other-e', None, 'other-g', 'other-h'))
886
        for tg, e_target, f_target, g_target, h_target in targets:
887
            for link, target in (('e', e_target), ('f', f_target), 
888
                                 ('g', g_target), ('h', h_target)):
889
                if target is not None:
890
                    tg.tt.new_symlink(link, tg.root, target, link)
1534.7.102 by Aaron Bentley
Deleted old pre-conflict contents
891
892
        for tg in this, base, other:
1534.7.101 by Aaron Bentley
Got conflicts on symlinks working properly
893
            tg.tt.apply()
894
        Merge3Merger(this.wt, this.wt, base.wt, other.wt)
895
        self.assertIs(os.path.isdir(this.wt.abspath('a')), True)
896
        self.assertIs(os.path.islink(this.wt.abspath('b')), True)
897
        self.assertIs(os.path.isfile(this.wt.abspath('c')), True)
898
        for suffix in ('THIS', 'BASE', 'OTHER'):
899
            self.assertEqual(os.readlink(this.wt.abspath('d.'+suffix)), suffix)
1534.7.102 by Aaron Bentley
Deleted old pre-conflict contents
900
        self.assertIs(os.path.lexists(this.wt.abspath('d')), False)
1534.7.104 by Aaron Bentley
Fixed set_versioned, enhanced conflict testing
901
        self.assertEqual(this.wt.id2path('d'), 'd.OTHER')
902
        self.assertEqual(this.wt.id2path('f'), 'f.THIS')
1534.7.102 by Aaron Bentley
Deleted old pre-conflict contents
903
        self.assertEqual(os.readlink(this.wt.abspath('e')), 'other-e')
904
        self.assertIs(os.path.lexists(this.wt.abspath('e.THIS')), False)
905
        self.assertIs(os.path.lexists(this.wt.abspath('e.OTHER')), False)
906
        self.assertIs(os.path.lexists(this.wt.abspath('e.BASE')), False)
1534.7.104 by Aaron Bentley
Fixed set_versioned, enhanced conflict testing
907
        self.assertIs(os.path.lexists(this.wt.abspath('g')), True)
908
        self.assertIs(os.path.lexists(this.wt.abspath('g.BASE')), False)
909
        self.assertIs(os.path.lexists(this.wt.abspath('h')), False)
910
        self.assertIs(os.path.lexists(this.wt.abspath('h.BASE')), False)
911
        self.assertIs(os.path.lexists(this.wt.abspath('h.THIS')), True)
912
        self.assertIs(os.path.lexists(this.wt.abspath('h.OTHER')), True)
1534.7.105 by Aaron Bentley
Got merge with rename working
913
914
    def test_filename_merge(self):
2116.4.1 by John Arbash Meinel
Update file and revision id generators.
915
        root_id = generate_ids.gen_root_id()
1731.1.33 by Aaron Bentley
Revert no-special-root changes
916
        base = TransformGroup("BASE", root_id)
917
        this = TransformGroup("THIS", root_id)
918
        other = TransformGroup("OTHER", root_id)
1534.7.105 by Aaron Bentley
Got merge with rename working
919
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a') 
920
                                   for t in [base, this, other]]
921
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b') 
922
                                   for t in [base, this, other]]
923
        base.tt.new_directory('c', base_a, 'c')
924
        this.tt.new_directory('c1', this_a, 'c')
925
        other.tt.new_directory('c', other_b, 'c')
926
927
        base.tt.new_directory('d', base_a, 'd')
928
        this.tt.new_directory('d1', this_b, 'd')
929
        other.tt.new_directory('d', other_a, 'd')
930
931
        base.tt.new_directory('e', base_a, 'e')
932
        this.tt.new_directory('e', this_a, 'e')
933
        other.tt.new_directory('e1', other_b, 'e')
934
935
        base.tt.new_directory('f', base_a, 'f')
936
        this.tt.new_directory('f1', this_b, 'f')
937
        other.tt.new_directory('f1', other_b, 'f')
938
939
        for tg in [this, base, other]:
940
            tg.tt.apply()
941
        Merge3Merger(this.wt, this.wt, base.wt, other.wt)
1534.7.176 by abentley
Fixed up tests for Windows
942
        self.assertEqual(this.wt.id2path('c'), pathjoin('b/c1'))
943
        self.assertEqual(this.wt.id2path('d'), pathjoin('b/d1'))
944
        self.assertEqual(this.wt.id2path('e'), pathjoin('b/e1'))
945
        self.assertEqual(this.wt.id2path('f'), pathjoin('b/f1'))
1534.7.105 by Aaron Bentley
Got merge with rename working
946
947
    def test_filename_merge_conflicts(self):
2116.4.1 by John Arbash Meinel
Update file and revision id generators.
948
        root_id = generate_ids.gen_root_id()
1731.1.33 by Aaron Bentley
Revert no-special-root changes
949
        base = TransformGroup("BASE", root_id)
950
        this = TransformGroup("THIS", root_id)
951
        other = TransformGroup("OTHER", root_id)
1534.7.105 by Aaron Bentley
Got merge with rename working
952
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a') 
953
                                   for t in [base, this, other]]
954
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b') 
955
                                   for t in [base, this, other]]
956
957
        base.tt.new_file('g', base_a, 'g', 'g')
958
        other.tt.new_file('g1', other_b, 'g1', 'g')
959
960
        base.tt.new_file('h', base_a, 'h', 'h')
961
        this.tt.new_file('h1', this_b, 'h1', 'h')
962
963
        base.tt.new_file('i', base.root, 'i', 'i')
1534.7.153 by Aaron Bentley
Handled test cases involving symlinks
964
        other.tt.new_directory('i1', this_b, 'i')
1534.7.105 by Aaron Bentley
Got merge with rename working
965
966
        for tg in [this, base, other]:
967
            tg.tt.apply()
968
        Merge3Merger(this.wt, this.wt, base.wt, other.wt)
969
1534.7.176 by abentley
Fixed up tests for Windows
970
        self.assertEqual(this.wt.id2path('g'), pathjoin('b/g1.OTHER'))
1534.7.105 by Aaron Bentley
Got merge with rename working
971
        self.assertIs(os.path.lexists(this.wt.abspath('b/g1.BASE')), True)
972
        self.assertIs(os.path.lexists(this.wt.abspath('b/g1.THIS')), False)
1534.7.176 by abentley
Fixed up tests for Windows
973
        self.assertEqual(this.wt.id2path('h'), pathjoin('b/h1.THIS'))
1534.7.105 by Aaron Bentley
Got merge with rename working
974
        self.assertIs(os.path.lexists(this.wt.abspath('b/h1.BASE')), True)
975
        self.assertIs(os.path.lexists(this.wt.abspath('b/h1.OTHER')), False)
1534.7.176 by abentley
Fixed up tests for Windows
976
        self.assertEqual(this.wt.id2path('i'), pathjoin('b/i1.OTHER'))
1534.7.183 by Aaron Bentley
Fixed build_tree with symlinks
977
2027.1.1 by John Arbash Meinel
Fix bug #56549, and write a direct test that the right path is being statted
978
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
979
class TestBuildTree(tests.TestCaseWithTransport):
980
1534.7.183 by Aaron Bentley
Fixed build_tree with symlinks
981
    def test_build_tree(self):
982
        if not has_symlinks():
983
            raise TestSkipped('Test requires symlink support')
984
        os.mkdir('a')
985
        a = BzrDir.create_standalone_workingtree('a')
986
        os.mkdir('a/foo')
987
        file('a/foo/bar', 'wb').write('contents')
988
        os.symlink('a/foo/bar', 'a/foo/baz')
989
        a.add(['foo', 'foo/bar', 'foo/baz'])
990
        a.commit('initial commit')
991
        b = BzrDir.create_standalone_workingtree('b')
2255.2.194 by Robert Collins
[BROKEN] Many updates to stop using experimental formats in tests.
992
        basis = a.basis_tree()
993
        basis.lock_read()
994
        self.addCleanup(basis.unlock)
995
        build_tree(basis, b)
1534.7.183 by Aaron Bentley
Fixed build_tree with symlinks
996
        self.assertIs(os.path.isdir('b/foo'), True)
997
        self.assertEqual(file('b/foo/bar', 'rb').read(), "contents")
998
        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
999
2100.3.21 by Aaron Bentley
Work on checking out by-reference trees
1000
    def test_build_with_references(self):
2255.2.194 by Robert Collins
[BROKEN] Many updates to stop using experimental formats in tests.
1001
        tree = self.make_branch_and_tree('source',
1002
            format='dirstate-with-subtree')
1003
        subtree = self.make_branch_and_tree('source/subtree',
1004
            format='dirstate-with-subtree')
2100.3.21 by Aaron Bentley
Work on checking out by-reference trees
1005
        tree.add_reference(subtree)
1006
        tree.commit('a revision')
1007
        tree.branch.create_checkout('target')
1008
        self.failUnlessExists('target')
1009
        self.failUnlessExists('target/subtree')
1010
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1011
    def test_file_conflict_handling(self):
1012
        """Ensure that when building trees, conflict handling is done"""
1013
        source = self.make_branch_and_tree('source')
1014
        target = self.make_branch_and_tree('target')
1015
        self.build_tree(['source/file', 'target/file'])
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1016
        source.add('file', 'new-file')
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1017
        source.commit('added file')
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
                          'file.moved', 'file', None, 'new-file')],
1021
                         target.conflicts())
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1022
        target2 = self.make_branch_and_tree('target2')
1023
        target_file = file('target2/file', 'wb')
1024
        try:
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1025
            source_file = file('source/file', 'rb')
1026
            try:
1027
                target_file.write(source_file.read())
1028
            finally:
1029
                source_file.close()
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1030
        finally:
1031
            target_file.close()
1032
        build_tree(source.basis_tree(), target2)
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1033
        self.assertEqual([], target2.conflicts())
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1034
1035
    def test_symlink_conflict_handling(self):
1036
        """Ensure that when building trees, conflict handling is done"""
1037
        if not has_symlinks():
1038
            raise TestSkipped('Test requires symlink support')
1039
        source = self.make_branch_and_tree('source')
1040
        os.symlink('foo', 'source/symlink')
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1041
        source.add('symlink', 'new-symlink')
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1042
        source.commit('added file')
1043
        target = self.make_branch_and_tree('target')
1044
        os.symlink('bar', 'target/symlink')
1045
        build_tree(source.basis_tree(), target)
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1046
        self.assertEqual([DuplicateEntry('Moved existing file to',
1047
            'symlink.moved', 'symlink', None, 'new-symlink')],
1048
            target.conflicts())
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1049
        target = self.make_branch_and_tree('target2')
1050
        os.symlink('foo', 'target2/symlink')
1051
        build_tree(source.basis_tree(), target)
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1052
        self.assertEqual([], target.conflicts())
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1053
        
1054
    def test_directory_conflict_handling(self):
1055
        """Ensure that when building trees, conflict handling is done"""
1056
        source = self.make_branch_and_tree('source')
1057
        target = self.make_branch_and_tree('target')
1058
        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
1059
        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
1060
        source.commit('added file')
1061
        build_tree(source.basis_tree(), target)
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1062
        self.assertEqual([], target.conflicts())
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1063
        self.failUnlessExists('target/dir1/file')
1064
1065
        # Ensure contents are merged
1066
        target = self.make_branch_and_tree('target2')
1067
        self.build_tree(['target2/dir1/', 'target2/dir1/file2'])
1068
        build_tree(source.basis_tree(), target)
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1069
        self.assertEqual([], target.conflicts())
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1070
        self.failUnlessExists('target2/dir1/file2')
1071
        self.failUnlessExists('target2/dir1/file')
1072
1073
        # 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
1074
        target = self.make_branch_and_tree('target3')
1075
        self.make_branch('target3/dir1')
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1076
        self.build_tree(['target3/dir1/file2'])
1077
        build_tree(source.basis_tree(), target)
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1078
        self.failIfExists('target3/dir1/file')
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1079
        self.failUnlessExists('target3/dir1/file2')
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1080
        self.failUnlessExists('target3/dir1.diverted/file')
1081
        self.assertEqual([DuplicateEntry('Diverted to',
1082
            'dir1.diverted', 'dir1', 'new-dir1', None)],
1083
            target.conflicts())
1084
1085
        target = self.make_branch_and_tree('target4')
1086
        self.build_tree(['target4/dir1/'])
1087
        self.make_branch('target4/dir1/file')
1088
        build_tree(source.basis_tree(), target)
1089
        self.failUnlessExists('target4/dir1/file')
1090
        self.assertEqual('directory', file_kind('target4/dir1/file'))
1091
        self.failUnlessExists('target4/dir1/file.diverted')
1092
        self.assertEqual([DuplicateEntry('Diverted to',
1093
            'dir1/file.diverted', 'dir1/file', 'new-file', None)],
1094
            target.conflicts())
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1095
1096
    def test_mixed_conflict_handling(self):
1097
        """Ensure that when building trees, conflict handling is done"""
1098
        source = self.make_branch_and_tree('source')
1099
        target = self.make_branch_and_tree('target')
1100
        self.build_tree(['source/name', 'target/name/'])
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1101
        source.add('name', 'new-name')
1102
        source.commit('added file')
1103
        build_tree(source.basis_tree(), target)
1104
        self.assertEqual([DuplicateEntry('Moved existing file to',
1105
            'name.moved', 'name', None, 'new-name')], target.conflicts())
1106
1107
    def test_raises_in_populated(self):
1108
        source = self.make_branch_and_tree('source')
1109
        self.build_tree(['source/name'])
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1110
        source.add('name')
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1111
        source.commit('added name')
1112
        target = self.make_branch_and_tree('target')
1113
        self.build_tree(['target/name'])
1114
        target.add('name')
2090.2.1 by Martin Pool
Fix some code which relies on assertions and breaks under python -O
1115
        self.assertRaises(errors.WorkingTreeAlreadyPopulated, 
1116
            build_tree, source.basis_tree(), target)
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1117
1118
1534.10.28 by Aaron Bentley
Use numbered backup files
1119
class MockTransform(object):
1120
1121
    def has_named_child(self, by_parent, parent_id, name):
1122
        for child_id in by_parent[parent_id]:
1123
            if child_id == '0':
1124
                if name == "name~":
1125
                    return True
1534.10.29 by Aaron Bentley
Fixed backup numbering to match GNU standard better
1126
            elif name == "name.~%s~" % child_id:
1534.10.28 by Aaron Bentley
Use numbered backup files
1127
                return True
1128
        return False
1129
1130
class MockEntry(object):
1131
    def __init__(self):
1132
        object.__init__(self)
1133
        self.name = "name"
1134
1135
class TestGetBackupName(TestCase):
1136
    def test_get_backup_name(self):
1137
        tt = MockTransform()
1138
        name = get_backup_name(MockEntry(), {'a':[]}, 'a', tt)
1534.10.29 by Aaron Bentley
Fixed backup numbering to match GNU standard better
1139
        self.assertEqual(name, 'name.~1~')
1140
        name = get_backup_name(MockEntry(), {'a':['1']}, 'a', tt)
1141
        self.assertEqual(name, 'name.~2~')
1534.10.28 by Aaron Bentley
Use numbered backup files
1142
        name = get_backup_name(MockEntry(), {'a':['2']}, 'a', tt)
1534.10.29 by Aaron Bentley
Fixed backup numbering to match GNU standard better
1143
        self.assertEqual(name, 'name.~1~')
1534.10.28 by Aaron Bentley
Use numbered backup files
1144
        name = get_backup_name(MockEntry(), {'a':['2'], 'b':[]}, 'b', tt)
1534.10.29 by Aaron Bentley
Fixed backup numbering to match GNU standard better
1145
        self.assertEqual(name, 'name.~1~')
1146
        name = get_backup_name(MockEntry(), {'a':['1', '2', '3']}, 'a', tt)
1147
        self.assertEqual(name, 'name.~4~')