14
14
# You should have received a copy of the GNU General Public License
15
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
from cStringIO import StringIO
22
from bzrlib.branch import Branch
23
import bzrlib.bzrdir as bzrdir
24
from bzrlib.bzrdir import BzrDir
25
from bzrlib.conflicts import *
26
import bzrlib.errors as errors
27
from bzrlib.errors import NotBranchError, NotVersionedError
28
from bzrlib.lockdir import LockDir
29
from bzrlib.osutils import pathjoin, getcwd, has_symlinks
30
from bzrlib.tests import TestCaseWithTransport, TestSkipped
31
from bzrlib.trace import mutter
32
from bzrlib.transport import get_transport
33
import bzrlib.workingtree as workingtree
34
from bzrlib.workingtree import (TreeEntry, TreeDirectory, TreeFile, TreeLink,
16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
26
workingtree as bzrworkingtree,
30
from ..lock import write_locked
31
from ..lockdir import LockDir
32
from . import TestCase, TestCaseWithTransport, TestSkipped
37
41
class TestTreeDirectory(TestCaseWithTransport):
61
65
class TestDefaultFormat(TestCaseWithTransport):
63
67
def test_get_set_default_format(self):
64
old_format = workingtree.WorkingTreeFormat.get_default_format()
66
self.assertTrue(isinstance(old_format, workingtree.WorkingTreeFormat3))
67
workingtree.WorkingTreeFormat.set_default_format(SampleTreeFormat())
69
# the default branch format is used by the meta dir format
70
# which is not the default bzrdir format at this point
71
dir = bzrdir.BzrDirMetaFormat1().initialize('.')
72
dir.create_repository()
74
result = dir.create_workingtree()
75
self.assertEqual(result, 'A tree')
77
workingtree.WorkingTreeFormat.set_default_format(old_format)
78
self.assertEqual(old_format, workingtree.WorkingTreeFormat.get_default_format())
81
class SampleTreeFormat(workingtree.WorkingTreeFormat):
68
old_format = workingtree.format_registry.get_default()
70
self.assertTrue(isinstance(
71
old_format, workingtree_4.WorkingTreeFormat6))
72
workingtree.format_registry.set_default(SampleTreeFormat())
74
# the default branch format is used by the meta dir format
75
# which is not the default bzrdir format at this point
76
dir = bzrdir.BzrDirMetaFormat1().initialize('.')
77
dir.create_repository()
79
result = dir.create_workingtree()
80
self.assertEqual(result, 'A tree')
82
workingtree.format_registry.set_default(old_format)
83
self.assertEqual(old_format, workingtree.format_registry.get_default())
85
def test_from_string(self):
86
self.assertIsInstance(
87
SampleTreeFormat.from_string(b"Sample tree format."),
90
AssertionError, SampleTreeFormat.from_string,
91
b"Different format string.")
93
def test_get_set_default_format_by_key(self):
94
old_format = workingtree.format_registry.get_default()
96
format = SampleTreeFormat()
97
workingtree.format_registry.register(format)
98
self.addCleanup(workingtree.format_registry.remove, format)
99
self.assertTrue(isinstance(
100
old_format, workingtree_4.WorkingTreeFormat6))
101
workingtree.format_registry.set_default_key(format.get_format_string())
103
# the default branch format is used by the meta dir format
104
# which is not the default bzrdir format at this point
105
dir = bzrdir.BzrDirMetaFormat1().initialize('.')
106
dir.create_repository()
108
result = dir.create_workingtree()
109
self.assertEqual(result, 'A tree')
111
workingtree.format_registry.set_default_key(
112
old_format.get_format_string())
113
self.assertEqual(old_format, workingtree.format_registry.get_default())
116
tree = self.make_branch_and_tree('.')
117
open_direct = workingtree.WorkingTree.open('.')
118
self.assertEqual(tree.basedir, open_direct.basedir)
119
open_no_args = workingtree.WorkingTree.open()
120
self.assertEqual(tree.basedir, open_no_args.basedir)
122
def test_open_containing(self):
123
tree = self.make_branch_and_tree('.')
124
open_direct, relpath = workingtree.WorkingTree.open_containing('.')
125
self.assertEqual(tree.basedir, open_direct.basedir)
126
self.assertEqual('', relpath)
127
open_no_args, relpath = workingtree.WorkingTree.open_containing()
128
self.assertEqual(tree.basedir, open_no_args.basedir)
129
self.assertEqual('', relpath)
130
open_subdir, relpath = workingtree.WorkingTree.open_containing(
132
self.assertEqual(tree.basedir, open_subdir.basedir)
133
self.assertEqual('subdir', relpath)
136
class SampleTreeFormat(bzrworkingtree.WorkingTreeFormatMetaDir):
82
137
"""A sample format
84
this format is initializable, unsupported to aid in testing the
139
this format is initializable, unsupported to aid in testing the
85
140
open and open_downlevel routines.
88
def get_format_string(self):
144
def get_format_string(cls):
89
145
"""See WorkingTreeFormat.get_format_string()."""
90
return "Sample tree format."
146
return b"Sample tree format."
92
def initialize(self, a_bzrdir, revision_id=None):
148
def initialize(self, a_controldir, revision_id=None, from_branch=None,
149
accelerator_tree=None, hardlink=False):
93
150
"""Sample branches cannot be created."""
94
t = a_bzrdir.get_workingtree_transport(self)
95
t.put('format', StringIO(self.get_format_string()))
151
t = a_controldir.get_workingtree_transport(self)
152
t.put_bytes('format', self.get_format_string())
98
155
def is_supported(self):
102
159
return "opened tree."
162
class SampleExtraTreeFormat(workingtree.WorkingTreeFormat):
163
"""A sample format that does not support use in a metadir.
167
def get_format_string(self):
168
# Not usable in a metadir, so no format string
171
def initialize(self, a_controldir, revision_id=None, from_branch=None,
172
accelerator_tree=None, hardlink=False):
173
raise NotImplementedError(self.initialize)
175
def is_supported(self):
178
def open(self, transport, _found=False):
179
raise NotImplementedError(self.open)
105
182
class TestWorkingTreeFormat(TestCaseWithTransport):
106
183
"""Tests for the WorkingTreeFormat facility."""
185
def test_find_format_string(self):
186
# is the right format object found for a working tree?
187
branch = self.make_branch('branch')
189
errors.NoWorkingTree,
190
bzrworkingtree.WorkingTreeFormatMetaDir.find_format_string,
192
transport = branch.controldir.get_workingtree_transport(None)
194
transport.put_bytes("format", b"some format name")
195
# The format does not have to be known by Bazaar,
196
# find_format_string just retrieves the name
199
bzrworkingtree.WorkingTreeFormatMetaDir.find_format_string(
108
202
def test_find_format(self):
109
203
# is the right format object found for a working tree?
110
204
# create a branch with a few known format objects.
111
205
self.build_tree(["foo/", "bar/"])
112
207
def check_format(format, url):
113
dir = format._matchingbzrdir.initialize(url)
208
dir = format._matchingcontroldir.initialize(url)
114
209
dir.create_repository()
115
210
dir.create_branch()
116
211
format.initialize(dir)
117
t = get_transport(url)
118
found_format = workingtree.WorkingTreeFormat.find_format(dir)
119
self.failUnless(isinstance(found_format, format.__class__))
120
check_format(workingtree.WorkingTreeFormat3(), "bar")
212
found_format = bzrworkingtree.WorkingTreeFormatMetaDir.find_format(
214
self.assertIsInstance(found_format, format.__class__)
215
check_format(workingtree_3.WorkingTreeFormat3(), "bar")
122
217
def test_find_format_no_tree(self):
123
218
dir = bzrdir.BzrDirMetaFormat1().initialize('.')
124
219
self.assertRaises(errors.NoWorkingTree,
125
workingtree.WorkingTreeFormat.find_format,
220
bzrworkingtree.WorkingTreeFormatMetaDir.find_format,
128
223
def test_find_format_unknown_format(self):
131
226
dir.create_branch()
132
227
SampleTreeFormat().initialize(dir)
133
228
self.assertRaises(errors.UnknownFormatError,
134
workingtree.WorkingTreeFormat.find_format,
229
bzrworkingtree.WorkingTreeFormatMetaDir.find_format,
232
def test_find_format_with_features(self):
233
tree = self.make_branch_and_tree('.', format='2a')
234
tree.update_feature_flags({b"name": b"necessity"})
235
found_format = bzrworkingtree.WorkingTreeFormatMetaDir.find_format(
237
self.assertIsInstance(found_format, workingtree.WorkingTreeFormat)
238
self.assertEqual(found_format.features.get(b"name"), b"necessity")
240
bzrdir.MissingFeature, found_format.check_support_status, True)
242
bzrworkingtree.WorkingTreeFormatMetaDir.unregister_feature,
244
bzrworkingtree.WorkingTreeFormatMetaDir.register_feature(b"name")
245
found_format.check_support_status(True)
248
class TestWorkingTreeIterEntriesByDir_wSubtrees(TestCaseWithTransport):
250
def make_simple_tree(self):
251
tree = self.make_branch_and_tree('tree', format='development-subtree')
252
self.build_tree(['tree/a/', 'tree/a/b/', 'tree/a/b/c'])
253
tree.set_root_id(b'root-id')
254
tree.add(['a', 'a/b', 'a/b/c'], [b'a-id', b'b-id', b'c-id'])
255
tree.commit('initial')
258
def test_just_directory(self):
259
tree = self.make_simple_tree()
260
self.assertEqual([('directory', b'root-id'),
261
('directory', b'a-id'),
262
('directory', b'b-id'),
264
[(ie.kind, ie.file_id)
265
for path, ie in tree.iter_entries_by_dir()])
266
self.make_branch_and_tree('tree/a/b')
267
self.assertEqual([('tree-reference', b'b-id')],
268
[(ie.kind, ie.file_id)
269
for path, ie in tree.iter_entries_by_dir(
270
specific_files=['a/b'])])
272
def test_direct_subtree(self):
273
tree = self.make_simple_tree()
274
self.make_branch_and_tree('tree/a/b')
275
self.assertEqual([('directory', b'root-id'),
276
('directory', b'a-id'),
277
('tree-reference', b'b-id')],
278
[(ie.kind, ie.file_id)
279
for path, ie in tree.iter_entries_by_dir()])
281
def test_indirect_subtree(self):
282
tree = self.make_simple_tree()
283
self.make_branch_and_tree('tree/a')
284
self.assertEqual([('directory', b'root-id'),
285
('tree-reference', b'a-id')],
286
[(ie.kind, ie.file_id)
287
for path, ie in tree.iter_entries_by_dir()])
290
class TestWorkingTreeFormatRegistry(TestCase):
293
super(TestWorkingTreeFormatRegistry, self).setUp()
294
self.registry = workingtree.WorkingTreeFormatRegistry()
137
296
def test_register_unregister_format(self):
138
297
format = SampleTreeFormat()
140
dir = bzrdir.BzrDirMetaFormat1().initialize('.')
141
dir.create_repository()
144
format.initialize(dir)
145
# register a format for it.
146
workingtree.WorkingTreeFormat.register_format(format)
147
# which branch.Open will refuse (not supported)
148
self.assertRaises(errors.UnsupportedFormatError, workingtree.WorkingTree.open, '.')
149
# but open_downlevel will work
150
self.assertEqual(format.open(dir), workingtree.WorkingTree.open_downlevel('.'))
151
# unregister the format
152
workingtree.WorkingTreeFormat.unregister_format(format)
298
self.registry.register(format)
299
self.assertEqual(format, self.registry.get(b"Sample tree format."))
300
self.registry.remove(format)
301
self.assertRaises(KeyError, self.registry.get, b"Sample tree format.")
303
def test_get_all(self):
304
format = SampleTreeFormat()
305
self.assertEqual([], self.registry._get_all())
306
self.registry.register(format)
307
self.assertEqual([format], self.registry._get_all())
309
def test_register_extra(self):
310
format = SampleExtraTreeFormat()
311
self.assertEqual([], self.registry._get_all())
312
self.registry.register_extra(format)
313
self.assertEqual([format], self.registry._get_all())
315
def test_register_extra_lazy(self):
316
self.assertEqual([], self.registry._get_all())
317
self.registry.register_extra_lazy("breezy.tests.test_workingtree",
318
"SampleExtraTreeFormat")
319
formats = self.registry._get_all()
320
self.assertEqual(1, len(formats))
321
self.assertIsInstance(formats[0], SampleExtraTreeFormat)
155
324
class TestWorkingTreeFormat3(TestCaseWithTransport):
167
336
# stat-cache = ??
168
337
# no inventory.basis yet
169
338
t = control.get_workingtree_transport(None)
170
self.assertEqualDiff('Bazaar-NG Working Tree format 3',
339
self.assertEqualDiff(b'Bazaar-NG Working Tree format 3',
171
340
t.get('format').read())
172
self.assertEqualDiff('<inventory format="5">\n'
174
t.get('inventory').read())
175
self.assertEqualDiff('### bzr hashcache v5\n',
341
self.assertEqualDiff(t.get('inventory').read(),
342
b'<inventory format="5">\n'
345
self.assertEqualDiff(b'### bzr hashcache v5\n',
176
346
t.get('stat-cache').read())
177
347
self.assertFalse(t.has('inventory.basis'))
178
348
# no last-revision file means 'None' or 'NULLREVISION'
179
349
self.assertFalse(t.has('last-revision'))
180
# TODO RBC 20060210 do a commit, check the inventory.basis is created
350
# TODO RBC 20060210 do a commit, check the inventory.basis is created
181
351
# correctly and last-revision file becomes present.
183
353
def test_uses_lockdir(self):
184
354
"""WorkingTreeFormat3 uses its own LockDir:
186
356
- lock is a directory
187
357
- when the WorkingTree is locked, LockDir can see that
189
359
t = self.get_transport()
190
360
url = self.get_url()
191
361
dir = bzrdir.BzrDirMetaFormat1().initialize(url)
192
repo = dir.create_repository()
193
branch = dir.create_branch()
362
dir.create_repository()
195
tree = workingtree.WorkingTreeFormat3().initialize(dir)
365
tree = workingtree_3.WorkingTreeFormat3().initialize(dir)
196
366
except errors.NotLocalUrl:
197
367
raise TestSkipped('Not a local URL')
198
368
self.assertIsDirectory('.bzr', t)
199
369
self.assertIsDirectory('.bzr/checkout', t)
200
370
self.assertIsDirectory('.bzr/checkout/lock', t)
201
371
our_lock = LockDir(t, '.bzr/checkout/lock')
202
self.assertEquals(our_lock.peek(), None)
204
self.assertTrue(our_lock.peek())
206
self.assertEquals(our_lock.peek(), None)
208
def create_format2_tree(self, url):
209
return BzrDir.create_standalone_workingtree(url)
211
def test_conflicts_format2(self):
212
# test backwards compatability
213
tree = self.create_format2_tree('.')
214
self.assertRaises(errors.UnsupportedOperation, tree.set_conflicts,
216
file('lala.BASE', 'wb').write('labase')
217
expected = ContentsConflict('lala')
218
self.assertEqual(list(tree.conflicts()), [expected])
219
file('lala', 'wb').write('la')
220
tree.add('lala', 'lala-id')
221
expected = ContentsConflict('lala', file_id='lala-id')
222
self.assertEqual(list(tree.conflicts()), [expected])
223
file('lala.THIS', 'wb').write('lathis')
224
file('lala.OTHER', 'wb').write('laother')
225
# When "text conflict"s happen, stem, THIS and OTHER are text
226
expected = TextConflict('lala', file_id='lala-id')
227
self.assertEqual(list(tree.conflicts()), [expected])
228
os.unlink('lala.OTHER')
229
os.mkdir('lala.OTHER')
230
expected = ContentsConflict('lala', file_id='lala-id')
231
self.assertEqual(list(tree.conflicts()), [expected])
372
self.assertEqual(our_lock.peek(), None)
373
with tree.lock_write():
374
self.assertTrue(our_lock.peek())
375
self.assertEqual(our_lock.peek(), None)
377
def test_missing_pending_merges(self):
378
control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
379
control.create_repository()
380
control.create_branch()
381
tree = workingtree_3.WorkingTreeFormat3().initialize(control)
382
tree._transport.delete("pending-merges")
383
self.assertEqual([], tree.get_parent_ids())
386
class TestRevert(TestCaseWithTransport):
388
def test_revert_conflicts_recursive(self):
389
this_tree = self.make_branch_and_tree('this-tree')
390
self.build_tree_contents([('this-tree/foo/',),
391
('this-tree/foo/bar', b'bar')])
392
this_tree.add(['foo', 'foo/bar'])
393
this_tree.commit('created foo/bar')
394
other_tree = this_tree.controldir.sprout(
395
'other-tree').open_workingtree()
396
self.build_tree_contents([('other-tree/foo/bar', b'baz')])
397
other_tree.commit('changed bar')
398
self.build_tree_contents([('this-tree/foo/bar', b'qux')])
399
this_tree.commit('changed qux')
400
this_tree.merge_from_branch(other_tree.branch)
401
self.assertEqual(1, len(this_tree.conflicts()))
402
this_tree.revert(['foo'])
403
self.assertEqual(0, len(this_tree.conflicts()))
406
class TestAutoResolve(TestCaseWithTransport):
408
def test_auto_resolve(self):
409
base = self.make_branch_and_tree('base')
410
self.build_tree_contents([('base/hello', b'Hello')])
411
base.add('hello', b'hello_id')
413
other = base.controldir.sprout('other').open_workingtree()
414
self.build_tree_contents([('other/hello', b'hELLO')])
415
other.commit('Case switch')
416
this = base.controldir.sprout('this').open_workingtree()
417
self.assertPathExists('this/hello')
418
self.build_tree_contents([('this/hello', b'Hello World')])
419
this.commit('Add World')
420
this.merge_from_branch(other.branch)
421
self.assertEqual([conflicts.TextConflict('hello', b'hello_id')],
424
self.assertEqual([conflicts.TextConflict('hello', b'hello_id')],
426
self.build_tree_contents([('this/hello', b'<<<<<<<')])
428
self.assertEqual([conflicts.TextConflict('hello', b'hello_id')],
430
self.build_tree_contents([('this/hello', b'=======')])
432
self.assertEqual([conflicts.TextConflict('hello', b'hello_id')],
434
self.build_tree_contents([('this/hello', b'\n>>>>>>>')])
435
remaining, resolved = this.auto_resolve()
436
self.assertEqual([conflicts.TextConflict('hello', b'hello_id')],
438
self.assertEqual([], resolved)
439
self.build_tree_contents([('this/hello', b'hELLO wORLD')])
440
remaining, resolved = this.auto_resolve()
441
self.assertEqual([], this.conflicts())
442
self.assertEqual([conflicts.TextConflict('hello', b'hello_id')],
444
self.assertPathDoesNotExist('this/hello.BASE')
446
def test_auto_resolve_dir(self):
447
tree = self.make_branch_and_tree('tree')
448
self.build_tree(['tree/hello/'])
449
tree.add('hello', b'hello-id')
450
file_conflict = conflicts.TextConflict('file', b'hello-id')
451
tree.set_conflicts(conflicts.ConflictList([file_conflict]))
455
class TestFindTrees(TestCaseWithTransport):
457
def test_find_trees(self):
458
self.make_branch_and_tree('foo')
459
self.make_branch_and_tree('foo/bar')
460
# Sticking a tree inside a control dir is heinous, so let's skip it
461
self.make_branch_and_tree('foo/.bzr/baz')
462
self.make_branch('qux')
463
trees = workingtree.WorkingTree.find_trees('.')
464
self.assertEqual(2, len(list(trees)))
467
class TestStoredUncommitted(TestCaseWithTransport):
469
def store_uncommitted(self):
470
tree = self.make_branch_and_tree('tree')
471
tree.commit('get root in there')
472
self.build_tree_contents([('tree/file', b'content')])
473
tree.add('file', b'file-id')
474
tree.store_uncommitted()
477
def test_store_uncommitted(self):
478
self.store_uncommitted()
479
self.assertPathDoesNotExist('tree/file')
481
def test_store_uncommitted_no_change(self):
482
tree = self.make_branch_and_tree('tree')
483
tree.commit('get root in there')
484
tree.store_uncommitted()
485
self.assertIs(None, tree.branch.get_unshelver(tree))
487
def test_restore_uncommitted(self):
488
with write_locked(self.store_uncommitted()) as tree:
489
tree.restore_uncommitted()
490
self.assertPathExists('tree/file')
491
self.assertIs(None, tree.branch.get_unshelver(tree))
493
def test_restore_uncommitted_none(self):
494
tree = self.make_branch_and_tree('tree')
495
tree.restore_uncommitted()