1
# Copyright (C) 2005-2011 Canonical Ltd
1
# (C) 2005 Canonical Ltd
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
11
# GNU General Public License for more details.
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
"""Tests for branch implementations - tests a branch format."""
20
branch as _mod_branch,
36
from bzrlib.tests import (
39
from bzrlib.tests.http_server import HttpServer
40
from bzrlib.transport import memory
43
class TestTestCaseWithBranch(per_branch.TestCaseWithBranch):
45
def test_branch_format_matches_bzrdir_branch_format(self):
46
bzrdir_branch_format = self.bzrdir_format.get_branch_format()
47
self.assertIs(self.branch_format.__class__,
48
bzrdir_branch_format.__class__)
50
def test_make_branch_gets_expected_format(self):
51
branch = self.make_branch('.')
52
self.assertIs(self.branch_format.__class__,
53
branch._format.__class__)
56
class TestBranch(per_branch.TestCaseWithBranch):
58
def test_create_tree_with_merge(self):
59
tree = self.create_tree_with_merge()
61
self.addCleanup(tree.unlock)
62
graph = tree.branch.repository.get_graph()
63
ancestry_graph = graph.get_parent_map(
64
tree.branch.repository.all_revision_ids())
65
self.assertEqual({'rev-1':('null:',),
67
'rev-1.1.1':('rev-1', ),
68
'rev-3':('rev-2', 'rev-1.1.1', ),
71
def test_revision_ids_are_utf8(self):
72
wt = self.make_branch_and_tree('tree')
73
wt.commit('f', rev_id='rev1')
74
wt.commit('f', rev_id='rev2')
75
wt.commit('f', rev_id='rev3')
77
br = self.get_branch()
79
br.generate_revision_history('rev3')
80
for revision_id in ['rev3', 'rev2', 'rev1']:
81
self.assertIsInstance(revision_id, str)
82
last = br.last_revision()
83
self.assertEqual('rev3', last)
84
self.assertIsInstance(last, str)
85
revno, last = br.last_revision_info()
86
self.assertEqual(3, revno)
87
self.assertEqual('rev3', last)
88
self.assertIsInstance(last, str)
90
def test_fetch_revisions(self):
91
"""Test fetch-revision operation."""
92
wt = self.make_branch_and_tree('b1')
94
self.build_tree_contents([('b1/foo', 'hello')])
95
wt.add(['foo'], ['foo-id'])
96
wt.commit('lala!', rev_id='revision-1', allow_pointless=False)
98
b2 = self.make_branch('b2')
101
rev = b2.repository.get_revision('revision-1')
102
tree = b2.repository.revision_tree('revision-1')
104
self.addCleanup(tree.unlock)
105
self.assertEqual(tree.get_file_text('foo-id'), 'hello')
107
def test_get_revision_delta(self):
108
tree_a = self.make_branch_and_tree('a')
109
self.build_tree(['a/foo'])
110
tree_a.add('foo', 'file1')
111
tree_a.commit('rev1', rev_id='rev1')
112
self.build_tree(['a/vla'])
113
tree_a.add('vla', 'file2')
114
tree_a.commit('rev2', rev_id='rev2')
116
delta = self.applyDeprecated(symbol_versioning.deprecated_in(
117
(2, 5, 0)), tree_a.branch.get_revision_delta, 1)
118
self.assertIsInstance(delta, _mod_delta.TreeDelta)
119
self.assertEqual([('foo', 'file1', 'file')], delta.added)
120
delta = self.applyDeprecated(symbol_versioning.deprecated_in(
121
(2, 5, 0)), tree_a.branch.get_revision_delta, 2)
122
self.assertIsInstance(delta, _mod_delta.TreeDelta)
123
self.assertEqual([('vla', 'file2', 'file')], delta.added)
125
def get_unbalanced_tree_pair(self):
126
"""Return two branches, a and b, with one file in a."""
127
tree_a = self.make_branch_and_tree('a')
128
self.build_tree_contents([('a/b', 'b')])
130
tree_a.commit("silly commit", rev_id='A')
132
tree_b = self.make_branch_and_tree('b')
133
return tree_a, tree_b
135
def get_balanced_branch_pair(self):
136
"""Returns br_a, br_b as with one commit in a, and b has a's stores."""
137
tree_a, tree_b = self.get_unbalanced_tree_pair()
138
tree_b.branch.repository.fetch(tree_a.branch.repository)
139
return tree_a, tree_b
141
def test_clone_partial(self):
142
"""Copy only part of the history of a branch."""
143
# TODO: RBC 20060208 test with a revision not on revision-history.
144
# what should that behaviour be ? Emailed the list.
145
# First, make a branch with two commits.
146
wt_a = self.make_branch_and_tree('a')
147
self.build_tree(['a/one'])
149
wt_a.commit('commit one', rev_id='1')
150
self.build_tree(['a/two'])
152
wt_a.commit('commit two', rev_id='2')
153
# Now make a copy of the repository.
154
repo_b = self.make_repository('b')
155
wt_a.branch.repository.copy_content_into(repo_b)
156
# wt_a might be a lightweight checkout, so get a hold of the actual
157
# branch (because you can't do a partial clone of a lightweight
159
branch = wt_a.branch.bzrdir.open_branch()
160
# Then make a branch where the new repository is, but specify a revision
161
# ID. The new branch's history will stop at the specified revision.
162
br_b = branch.clone(repo_b.bzrdir, revision_id='1')
163
self.assertEqual('1', br_b.last_revision())
165
def get_parented_branch(self):
166
wt_a = self.make_branch_and_tree('a')
167
self.build_tree(['a/one'])
169
wt_a.commit('commit one', rev_id='1')
171
branch_b = wt_a.branch.bzrdir.sprout('b', revision_id='1').open_branch()
172
self.assertEqual(wt_a.branch.base, branch_b.get_parent())
175
def test_clone_branch_nickname(self):
176
# test the nick name is preserved always
177
raise tests.TestSkipped('XXX branch cloning is not yet tested.')
179
def test_clone_branch_parent(self):
180
# test the parent is preserved always
181
branch_b = self.get_parented_branch()
182
repo_c = self.make_repository('c')
183
branch_b.repository.copy_content_into(repo_c)
184
branch_c = branch_b.clone(repo_c.bzrdir)
185
self.assertNotEqual(None, branch_c.get_parent())
186
self.assertEqual(branch_b.get_parent(), branch_c.get_parent())
188
# We can also set a specific parent, and it should be honored
189
random_parent = 'http://example.com/path/to/branch'
190
branch_b.set_parent(random_parent)
191
repo_d = self.make_repository('d')
192
branch_b.repository.copy_content_into(repo_d)
193
branch_d = branch_b.clone(repo_d.bzrdir)
194
self.assertEqual(random_parent, branch_d.get_parent())
196
def test_submit_branch(self):
197
"""Submit location can be queried and set"""
198
branch = self.make_branch('branch')
199
self.assertEqual(branch.get_submit_branch(), None)
200
branch.set_submit_branch('sftp://example.com')
201
self.assertEqual(branch.get_submit_branch(), 'sftp://example.com')
202
branch.set_submit_branch('sftp://example.net')
203
self.assertEqual(branch.get_submit_branch(), 'sftp://example.net')
205
def test_public_branch(self):
206
"""public location can be queried and set"""
207
branch = self.make_branch('branch')
208
self.assertEqual(branch.get_public_branch(), None)
209
branch.set_public_branch('sftp://example.com')
210
self.assertEqual(branch.get_public_branch(), 'sftp://example.com')
211
branch.set_public_branch('sftp://example.net')
212
self.assertEqual(branch.get_public_branch(), 'sftp://example.net')
213
branch.set_public_branch(None)
214
self.assertEqual(branch.get_public_branch(), None)
216
def test_record_initial_ghost(self):
217
"""Branches should support having ghosts."""
218
wt = self.make_branch_and_tree('.')
219
if not wt.branch.repository._format.supports_ghosts:
220
raise tests.TestNotApplicable("repository format does not "
222
wt.set_parent_ids(['non:existent@rev--ision--0--2'],
223
allow_leftmost_as_ghost=True)
224
self.assertEqual(['non:existent@rev--ision--0--2'],
226
rev_id = wt.commit('commit against a ghost first parent.')
227
rev = wt.branch.repository.get_revision(rev_id)
228
self.assertEqual(rev.parent_ids, ['non:existent@rev--ision--0--2'])
229
# parent_sha1s is not populated now, WTF. rbc 20051003
230
self.assertEqual(len(rev.parent_sha1s), 0)
232
def test_record_two_ghosts(self):
233
"""Recording with all ghosts works."""
234
wt = self.make_branch_and_tree('.')
235
if not wt.branch.repository._format.supports_ghosts:
236
raise tests.TestNotApplicable("repository format does not "
239
'foo@azkhazan-123123-abcabc',
240
'wibble@fofof--20050401--1928390812',
242
allow_leftmost_as_ghost=True)
243
rev_id = wt.commit("commit from ghost base with one merge")
244
# the revision should have been committed with two parents
245
rev = wt.branch.repository.get_revision(rev_id)
246
self.assertEqual(['foo@azkhazan-123123-abcabc',
247
'wibble@fofof--20050401--1928390812'],
250
def test_bad_revision(self):
251
self.assertRaises(errors.InvalidRevisionId,
252
self.get_branch().repository.get_revision,
255
def test_nicks_bzr(self):
256
"""Test the behaviour of branch nicks specific to bzr branches.
258
Nicknames are implicitly the name of the branch's directory, unless an
259
explicit nickname is set. That is, an explicit nickname always
260
overrides the implicit one.
263
t = self.get_transport()
264
branch = self.make_branch('bzr.dev')
265
if not isinstance(branch, _mod_branch.BzrBranch):
266
raise tests.TestNotApplicable("not a bzr branch format")
267
# The nick will be 'bzr.dev', because there is no explicit nick set.
268
self.assertEqual(branch.nick, 'bzr.dev')
269
# Move the branch to a different directory, 'bzr.ab'. Now that branch
270
# will report its nick as 'bzr.ab'.
271
t.move('bzr.dev', 'bzr.ab')
272
branch = _mod_branch.Branch.open(self.get_url('bzr.ab'))
273
self.assertEqual(branch.nick, 'bzr.ab')
274
# Set the branch nick explicitly. This will ensure there's a branch
275
# config file in the branch.
276
branch.nick = "Aaron's branch"
277
if not isinstance(branch, remote.RemoteBranch):
278
self.assertTrue(branch._transport.has("branch.conf"))
279
# Because the nick has been set explicitly, the nick is now always
280
# "Aaron's branch", regardless of directory name.
281
self.assertEqual(branch.nick, "Aaron's branch")
282
t.move('bzr.ab', 'integration')
283
branch = _mod_branch.Branch.open(self.get_url('integration'))
284
self.assertEqual(branch.nick, "Aaron's branch")
285
branch.nick = u"\u1234"
286
self.assertEqual(branch.nick, u"\u1234")
288
def test_nicks(self):
289
"""Test explicit and implicit branch nicknames.
291
A nickname is always available, whether set explicitly or not.
293
t = self.get_transport()
294
branch = self.make_branch('bzr.dev')
295
# An implicit nick name is set; what it is exactly depends on the
297
self.assertIsInstance(branch.nick, basestring)
298
# Set the branch nick explicitly.
299
branch.nick = "Aaron's branch"
300
# Because the nick has been set explicitly, the nick is now always
302
self.assertEqual(branch.nick, "Aaron's branch")
303
branch.nick = u"\u1234"
304
self.assertEqual(branch.nick, u"\u1234")
306
def test_commit_nicks(self):
307
"""Nicknames are committed to the revision"""
308
wt = self.make_branch_and_tree('bzr.dev')
310
branch.nick = "My happy branch"
311
wt.commit('My commit respect da nick.')
312
committed = branch.repository.get_revision(branch.last_revision())
313
self.assertEqual(committed.properties["branch-nick"],
316
def test_create_colocated(self):
318
repo = self.make_repository('.', shared=True)
319
except errors.IncompatibleFormat:
321
self.assertEquals(0, len(repo.bzrdir.list_branches()))
323
child_branch1 = self.branch_format.initialize(repo.bzrdir,
325
except (errors.UninitializableFormat, errors.NoColocatedBranchSupport):
326
# branch references are not default init'able and
327
# not all bzrdirs support colocated branches.
329
self.assertEquals(1, len(repo.bzrdir.list_branches()))
330
self.branch_format.initialize(repo.bzrdir, name='branch2')
331
self.assertEquals(2, len(repo.bzrdir.list_branches()))
333
def test_create_append_revisions_only(self):
335
repo = self.make_repository('.', shared=True)
336
except errors.IncompatibleFormat:
338
for val in (True, False):
340
branch = self.branch_format.initialize(repo.bzrdir,
341
append_revisions_only=True)
342
except (errors.UninitializableFormat, errors.UpgradeRequired):
343
# branch references are not default init'able and
344
# not all branches support append_revisions_only
346
self.assertEquals(True, branch.get_append_revisions_only())
347
repo.bzrdir.destroy_branch()
349
def test_get_set_append_revisions_only(self):
350
branch = self.make_branch('.')
351
if branch._format.supports_set_append_revisions_only():
352
branch.set_append_revisions_only(True)
353
self.assertTrue(branch.get_append_revisions_only())
354
branch.set_append_revisions_only(False)
355
self.assertFalse(branch.get_append_revisions_only())
357
self.assertRaises(errors.UpgradeRequired,
358
branch.set_append_revisions_only, True)
359
self.assertFalse(branch.get_append_revisions_only())
361
def test_create_open_branch_uses_repository(self):
363
repo = self.make_repository('.', shared=True)
364
except errors.IncompatibleFormat:
365
raise tests.TestNotApplicable("requires shared repository support")
366
child_transport = repo.bzrdir.root_transport.clone('child')
367
child_transport.mkdir('.')
369
child_dir = self.bzrdir_format.initialize_on_transport(child_transport)
370
except errors.UninitializableFormat:
371
raise tests.TestNotApplicable("control dir format not initializable")
373
child_branch = self.branch_format.initialize(child_dir)
374
except errors.UninitializableFormat:
375
# branch references are not default init'able.
377
self.assertEqual(repo.bzrdir.root_transport.base,
378
child_branch.repository.bzrdir.root_transport.base)
379
child_branch = _mod_branch.Branch.open(self.get_url('child'))
380
self.assertEqual(repo.bzrdir.root_transport.base,
381
child_branch.repository.bzrdir.root_transport.base)
383
def test_format_description(self):
384
tree = self.make_branch_and_tree('tree')
385
text = tree.branch._format.get_format_description()
386
self.assertTrue(len(text))
388
def test_get_commit_builder(self):
389
branch = self.make_branch(".")
391
builder = branch.get_commit_builder([])
392
self.assertIsInstance(builder, repository.CommitBuilder)
393
branch.repository.commit_write_group()
396
def test_generate_revision_history(self):
397
"""Create a fake revision history easily."""
398
tree = self.make_branch_and_tree('.')
399
rev1 = tree.commit('foo')
401
self.addCleanup(tree.unlock)
402
graph = tree.branch.repository.get_graph()
404
graph.iter_lefthand_ancestry(
405
tree.branch.last_revision(), [revision.NULL_REVISION]))
406
rev2 = tree.commit('bar', allow_pointless=True)
407
tree.branch.generate_revision_history(rev1)
408
self.assertEqual(orig_history, list(
409
graph.iter_lefthand_ancestry(
410
tree.branch.last_revision(), [revision.NULL_REVISION])))
412
def test_generate_revision_history_NULL_REVISION(self):
413
tree = self.make_branch_and_tree('.')
414
rev1 = tree.commit('foo')
416
self.addCleanup(tree.unlock)
417
tree.branch.generate_revision_history(revision.NULL_REVISION)
418
self.assertEqual(revision.NULL_REVISION, tree.branch.last_revision())
420
def test_create_checkout(self):
421
tree_a = self.make_branch_and_tree('a')
422
branch_a = tree_a.branch
423
checkout_b = branch_a.create_checkout('b')
424
self.assertEqual('null:', checkout_b.last_revision())
425
checkout_b.commit('rev1', rev_id='rev1')
426
self.assertEqual('rev1', branch_a.last_revision())
427
self.assertNotEqual(checkout_b.branch.base, branch_a.base)
429
checkout_c = branch_a.create_checkout('c', lightweight=True)
430
self.assertEqual('rev1', checkout_c.last_revision())
431
checkout_c.commit('rev2', rev_id='rev2')
432
self.assertEqual('rev2', branch_a.last_revision())
433
self.assertEqual(checkout_c.branch.base, branch_a.base)
435
checkout_d = branch_a.create_checkout('d', lightweight=True)
436
self.assertEqual('rev2', checkout_d.last_revision())
437
checkout_e = branch_a.create_checkout('e')
438
self.assertEqual('rev2', checkout_e.last_revision())
440
def test_create_anonymous_lightweight_checkout(self):
441
"""A lightweight checkout from a readonly branch should succeed."""
442
tree_a = self.make_branch_and_tree('a')
443
rev_id = tree_a.commit('put some content in the branch')
444
# open the branch via a readonly transport
445
url = self.get_readonly_url(urlutils.basename(tree_a.branch.base))
446
t = transport.get_transport_from_url(url)
447
if not tree_a.branch.bzrdir._format.supports_transport(t):
448
raise tests.TestNotApplicable("format does not support transport")
449
source_branch = _mod_branch.Branch.open(url)
450
# sanity check that the test will be valid
451
self.assertRaises((errors.LockError, errors.TransportNotPossible),
452
source_branch.lock_write)
453
checkout = source_branch.create_checkout('c', lightweight=True)
454
self.assertEqual(rev_id, checkout.last_revision())
456
def test_create_anonymous_heavyweight_checkout(self):
457
"""A regular checkout from a readonly branch should succeed."""
458
tree_a = self.make_branch_and_tree('a')
459
rev_id = tree_a.commit('put some content in the branch')
460
# open the branch via a readonly transport
461
url = self.get_readonly_url(
462
osutils.basename(tree_a.branch.base.rstrip('/')))
463
t = transport.get_transport_from_url(url)
464
if not tree_a.branch.bzrdir._format.supports_transport(t):
465
raise tests.TestNotApplicable("format does not support transport")
466
source_branch = _mod_branch.Branch.open(url)
467
# sanity check that the test will be valid
468
self.assertRaises((errors.LockError, errors.TransportNotPossible),
469
source_branch.lock_write)
470
checkout = source_branch.create_checkout('c')
471
self.assertEqual(rev_id, checkout.last_revision())
473
def test_set_revision_history(self):
474
tree = self.make_branch_and_tree('a')
475
tree.commit('a commit', rev_id='rev1')
477
self.applyDeprecated(symbol_versioning.deprecated_in((2, 4, 0)),
478
br.set_revision_history, ["rev1"])
479
self.assertEquals(br.last_revision(), "rev1")
480
self.applyDeprecated(symbol_versioning.deprecated_in((2, 4, 0)),
481
br.set_revision_history, [])
482
self.assertEquals(br.last_revision(), 'null:')
484
def test_heads_to_fetch(self):
485
# heads_to_fetch is a method that returns a collection of revids that
486
# need to be fetched to copy this branch into another repo. At a
487
# minimum this will include the tip.
488
# (In native formats, this is the tip + tags, but other formats may
489
# have other revs needed)
490
tree = self.make_branch_and_tree('a')
491
tree.commit('first commit', rev_id='rev1')
492
tree.commit('second commit', rev_id='rev2')
493
must_fetch, should_fetch = tree.branch.heads_to_fetch()
494
self.assertTrue('rev2' in must_fetch)
496
def test_heads_to_fetch_not_null_revision(self):
497
# NULL_REVISION does not appear in the result of heads_to_fetch, even
498
# for an empty branch.
499
tree = self.make_branch_and_tree('a')
500
must_fetch, should_fetch = tree.branch.heads_to_fetch()
501
self.assertFalse(revision.NULL_REVISION in must_fetch)
502
self.assertFalse(revision.NULL_REVISION in should_fetch)
505
class TestBranchFormat(per_branch.TestCaseWithBranch):
507
def test_branch_format_network_name(self):
508
br = self.make_branch('.')
510
network_name = format.network_name()
511
self.assertIsInstance(network_name, str)
512
# We want to test that the network_name matches the actual format on
513
# disk. For local branches that means that using network_name as a key
514
# in the registry gives back the same format. For remote branches we
515
# check that the network_name of the RemoteBranchFormat we have locally
516
# matches the actual format present on disk.
517
if isinstance(format, remote.RemoteBranchFormat):
519
real_branch = br._real_branch
520
self.assertEqual(real_branch._format.network_name(), network_name)
522
registry = _mod_branch.network_format_registry
523
looked_up_format = registry.get(network_name)
524
self.assertEqual(format.__class__, looked_up_format.__class__)
526
def get_get_config_calls(self):
527
# Smoke test that all branch succeed getting a config
528
br = self.make_branch('.')
530
br.get_config_stack()
533
class ChrootedTests(per_branch.TestCaseWithBranch):
534
"""A support class that provides readonly urls outside the local namespace.
536
This is done by checking if self.transport_server is a MemoryServer. if it
537
is then we are chrooted already, if it is not then an HttpServer is used
542
super(ChrootedTests, self).setUp()
543
if not self.vfs_transport_factory == memory.MemoryServer:
544
self.transport_readonly_server = HttpServer
546
def test_open_containing(self):
547
self.assertRaises(errors.NotBranchError,
548
_mod_branch.Branch.open_containing,
549
self.get_readonly_url(''))
550
self.assertRaises(errors.NotBranchError,
551
_mod_branch.Branch.open_containing,
552
self.get_readonly_url('g/p/q'))
553
branch = self.make_branch('.')
554
if not branch.bzrdir._format.supports_transport(
555
transport.get_transport_from_url(self.get_readonly_url('.'))):
556
raise tests.TestNotApplicable("format does not support transport")
557
branch, relpath = _mod_branch.Branch.open_containing(
558
self.get_readonly_url(''))
559
self.assertEqual('', relpath)
560
branch, relpath = _mod_branch.Branch.open_containing(
561
self.get_readonly_url('g/p/q'))
562
self.assertEqual('g/p/q', relpath)
565
class InstrumentedTransaction(object):
568
self.calls.append('finish')
574
class TestDecorator(object):
580
self._calls.append('lr')
582
def lock_write(self):
583
self._calls.append('lw')
586
self._calls.append('ul')
588
@_mod_branch.needs_read_lock
589
def do_with_read(self):
592
@_mod_branch.needs_read_lock
593
def except_with_read(self):
596
@_mod_branch.needs_write_lock
597
def do_with_write(self):
600
@_mod_branch.needs_write_lock
601
def except_with_write(self):
605
class TestDecorators(tests.TestCase):
607
def test_needs_read_lock(self):
608
branch = TestDecorator()
609
self.assertEqual(1, branch.do_with_read())
610
self.assertEqual(['lr', 'ul'], branch._calls)
612
def test_excepts_in_read_lock(self):
613
branch = TestDecorator()
614
self.assertRaises(RuntimeError, branch.except_with_read)
615
self.assertEqual(['lr', 'ul'], branch._calls)
617
def test_needs_write_lock(self):
618
branch = TestDecorator()
619
self.assertEqual(2, branch.do_with_write())
620
self.assertEqual(['lw', 'ul'], branch._calls)
622
def test_excepts_in_write_lock(self):
623
branch = TestDecorator()
624
self.assertRaises(RuntimeError, branch.except_with_write)
625
self.assertEqual(['lw', 'ul'], branch._calls)
628
class TestBranchPushLocations(per_branch.TestCaseWithBranch):
630
def test_get_push_location_unset(self):
631
self.assertEqual(None, self.get_branch().get_push_location())
633
def test_get_push_location_exact(self):
634
b = self.get_branch()
635
config.LocationConfig.from_string(
636
'[%s]\npush_location=foo\n' % (b.base,), b.base, save=True)
637
self.assertEqual("foo", self.get_branch().get_push_location())
639
def test_set_push_location(self):
640
branch = self.get_branch()
641
branch.set_push_location('foo')
642
self.assertEqual('foo', branch.get_push_location())
645
class TestChildSubmitFormats(per_branch.TestCaseWithBranch):
647
def test_get_child_submit_format_default(self):
648
submit_format = self.get_branch().get_child_submit_format()
649
self.assertTrue(submit_format is None or
650
isinstance(submit_format, str))
652
def test_get_child_submit_format(self):
653
branch = self.get_branch()
654
branch.get_config_stack().set('child_submit_format', '10')
655
branch = self.get_branch()
656
self.assertEqual('10', branch.get_child_submit_format())
659
class TestFormat(per_branch.TestCaseWithBranch):
660
"""Tests for the format itself."""
662
def test_get_reference(self):
663
"""get_reference on all regular branches should return None."""
664
if not self.branch_format.is_supported():
665
# unsupported formats are not loopback testable
666
# because the default open will not open them and
667
# they may not be initializable.
669
made_branch = self.make_branch('.')
670
self.assertEqual(None,
671
made_branch._format.get_reference(made_branch.bzrdir))
673
def test_set_reference(self):
674
"""set_reference on all regular branches should be callable."""
675
if not self.branch_format.is_supported():
676
# unsupported formats are not loopback testable
677
# because the default open will not open them and
678
# they may not be initializable.
680
this_branch = self.make_branch('this')
681
other_branch = self.make_branch('other')
683
this_branch._format.set_reference(this_branch.bzrdir, None,
685
except NotImplementedError:
689
ref = this_branch._format.get_reference(this_branch.bzrdir)
690
self.assertEqual(ref, other_branch.base)
692
def test_format_initialize_find_open(self):
693
# loopback test to check the current format initializes to itself.
694
if not self.branch_format.is_supported():
695
# unsupported formats are not loopback testable
696
# because the default open will not open them and
697
# they may not be initializable.
699
# supported formats must be able to init and open
700
t = self.get_transport()
701
readonly_t = transport.get_transport_from_url(self.get_readonly_url())
702
made_branch = self.make_branch('.')
703
self.assertIsInstance(made_branch, _mod_branch.Branch)
705
# find it via bzrdir opening:
706
opened_control = bzrdir.BzrDir.open(readonly_t.base)
707
direct_opened_branch = opened_control.open_branch()
708
self.assertEqual(direct_opened_branch.__class__, made_branch.__class__)
709
self.assertEqual(opened_control, direct_opened_branch.bzrdir)
710
self.assertIsInstance(direct_opened_branch._format,
711
self.branch_format.__class__)
713
# find it via Branch.open
714
opened_branch = _mod_branch.Branch.open(readonly_t.base)
715
self.assertIsInstance(opened_branch, made_branch.__class__)
716
self.assertEqual(made_branch._format.__class__,
717
opened_branch._format.__class__)
718
# if it has a unique id string, can we probe for it ?
720
self.branch_format.get_format_string()
721
except NotImplementedError:
723
self.assertEqual(self.branch_format,
724
opened_control.find_branch_format())
727
class TestBound(per_branch.TestCaseWithBranch):
729
def test_bind_unbind(self):
730
branch = self.make_branch('1')
731
branch2 = self.make_branch('2')
734
except errors.UpgradeRequired:
735
raise tests.TestNotApplicable('Format does not support binding')
736
self.assertTrue(branch.unbind())
737
self.assertFalse(branch.unbind())
738
self.assertIs(None, branch.get_bound_location())
740
def test_old_bound_location(self):
741
branch = self.make_branch('branch1')
743
self.assertIs(None, branch.get_old_bound_location())
744
except errors.UpgradeRequired:
745
raise tests.TestNotApplicable(
746
'Format does not store old bound locations')
747
branch2 = self.make_branch('branch2')
749
self.assertIs(None, branch.get_old_bound_location())
751
self.assertContainsRe(branch.get_old_bound_location(), '\/branch2\/$')
753
def test_bind_diverged(self):
754
tree_a = self.make_branch_and_tree('tree_a')
755
tree_a.commit('rev1a')
756
tree_b = tree_a.bzrdir.sprout('tree_b').open_workingtree()
757
tree_a.commit('rev2a')
758
tree_b.commit('rev2b')
760
tree_b.branch.bind(tree_a.branch)
761
except errors.UpgradeRequired:
762
raise tests.TestNotApplicable('Format does not support binding')
764
def test_unbind_clears_cached_master_branch(self):
765
"""b.unbind clears any cached value of b.get_master_branch."""
766
master = self.make_branch('master')
767
branch = self.make_branch('branch')
770
except errors.UpgradeRequired:
771
raise tests.TestNotApplicable('Format does not support binding')
772
self.addCleanup(branch.lock_write().unlock)
773
self.assertNotEqual(None, branch.get_master_branch())
775
self.assertEqual(None, branch.get_master_branch())
777
def test_unlocked_does_not_cache_master_branch(self):
778
"""Unlocked branches do not cache the result of get_master_branch."""
779
master = self.make_branch('master')
780
branch1 = self.make_branch('branch')
783
except errors.UpgradeRequired:
784
raise tests.TestNotApplicable('Format does not support binding')
786
branch2 = branch1.bzrdir.open_branch()
787
self.assertNotEqual(None, branch1.get_master_branch())
788
# Unbind the branch via branch2. branch1 isn't locked so will
789
# immediately return the new value for get_master_branch.
791
self.assertEqual(None, branch1.get_master_branch())
793
def test_bind_clears_cached_master_branch(self):
794
"""b.bind clears any cached value of b.get_master_branch."""
795
master1 = self.make_branch('master1')
796
master2 = self.make_branch('master2')
797
branch = self.make_branch('branch')
800
except errors.UpgradeRequired:
801
raise tests.TestNotApplicable('Format does not support binding')
802
self.addCleanup(branch.lock_write().unlock)
803
self.assertNotEqual(None, branch.get_master_branch())
805
self.assertEqual('.', urlutils.relative_url(self.get_url('master2'),
806
branch.get_master_branch().base))
808
def test_set_bound_location_clears_cached_master_branch(self):
809
"""b.set_bound_location clears any cached value of b.get_master_branch.
811
master1 = self.make_branch('master1')
812
master2 = self.make_branch('master2')
813
branch = self.make_branch('branch')
816
except errors.UpgradeRequired:
817
raise tests.TestNotApplicable('Format does not support binding')
818
self.addCleanup(branch.lock_write().unlock)
819
self.assertNotEqual(None, branch.get_master_branch())
820
branch.set_bound_location(self.get_url('master2'))
821
self.assertEqual('.', urlutils.relative_url(self.get_url('master2'),
822
branch.get_master_branch().base))
825
class TestStrict(per_branch.TestCaseWithBranch):
827
def test_strict_history(self):
828
tree1 = self.make_branch_and_tree('tree1')
830
tree1.branch.set_append_revisions_only(True)
831
except errors.UpgradeRequired:
832
raise tests.TestSkipped('Format does not support strict history')
833
tree1.commit('empty commit')
834
tree2 = tree1.bzrdir.sprout('tree2').open_workingtree()
835
tree2.commit('empty commit 2')
836
tree1.pull(tree2.branch)
837
tree1.commit('empty commit 3')
838
tree2.commit('empty commit 4')
839
self.assertRaises(errors.DivergedBranches, tree1.pull, tree2.branch)
840
tree2.merge_from_branch(tree1.branch)
841
tree2.commit('empty commit 5')
842
self.assertRaises(errors.AppendRevisionsOnlyViolation, tree1.pull,
844
tree3 = tree1.bzrdir.sprout('tree3').open_workingtree()
845
tree3.merge_from_branch(tree2.branch)
846
tree3.commit('empty commit 6')
847
tree2.pull(tree3.branch)
850
class TestIgnoreFallbacksParameter(per_branch.TestCaseWithBranch):
852
def make_branch_with_fallback(self):
853
fallback = self.make_branch('fallback')
854
if not fallback._format.supports_stacking():
855
raise tests.TestNotApplicable("format does not support stacking")
856
stacked = self.make_branch('stacked')
857
stacked.set_stacked_on_url(fallback.base)
860
def test_fallbacks_not_opened(self):
861
stacked = self.make_branch_with_fallback()
862
self.get_transport('').rename('fallback', 'moved')
863
reopened_dir = bzrdir.BzrDir.open(stacked.base)
864
reopened = reopened_dir.open_branch(ignore_fallbacks=True)
865
self.assertEqual([], reopened.repository._fallback_repositories)
867
def test_fallbacks_are_opened(self):
868
stacked = self.make_branch_with_fallback()
869
reopened_dir = bzrdir.BzrDir.open(stacked.base)
870
reopened = reopened_dir.open_branch(ignore_fallbacks=False)
871
self.assertLength(1, reopened.repository._fallback_repositories)
874
class TestReferenceLocation(per_branch.TestCaseWithBranch):
876
def test_reference_parent(self):
877
tree = self.make_branch_and_tree('tree')
878
subtree = self.make_branch_and_tree('tree/subtree')
879
subtree.set_root_id('subtree-id')
881
tree.add_reference(subtree)
882
except errors.UnsupportedOperation:
883
raise tests.TestNotApplicable('Tree cannot hold references.')
884
reference_parent = tree.branch.reference_parent(
886
urlutils.relative_url(tree.branch.user_url, subtree.branch.user_url))
887
self.assertEqual(subtree.branch.base, reference_parent.base)
889
def test_reference_parent_accepts_possible_transports(self):
890
tree = self.make_branch_and_tree('tree')
891
subtree = self.make_branch_and_tree('tree/subtree')
892
subtree.set_root_id('subtree-id')
894
tree.add_reference(subtree)
895
except errors.UnsupportedOperation:
896
raise tests.TestNotApplicable('Tree cannot hold references.')
897
reference_parent = tree.branch.reference_parent('subtree-id',
898
urlutils.relative_url(
899
tree.branch.user_url, subtree.branch.user_url),
900
possible_transports=[subtree.bzrdir.root_transport])
902
def test_get_reference_info(self):
903
branch = self.make_branch('branch')
905
path, loc = branch.get_reference_info('file-id')
906
except errors.UnsupportedOperation:
907
raise tests.TestNotApplicable('Branch cannot hold references.')
908
self.assertIs(None, path)
909
self.assertIs(None, loc)
911
def test_set_reference_info(self):
912
branch = self.make_branch('branch')
914
branch.set_reference_info('file-id', 'path/to/location',
916
except errors.UnsupportedOperation:
917
raise tests.TestNotApplicable('Branch cannot hold references.')
919
def test_set_get_reference_info(self):
920
branch = self.make_branch('branch')
922
branch.set_reference_info('file-id', 'path/to/file',
924
except errors.UnsupportedOperation:
925
raise tests.TestNotApplicable('Branch cannot hold references.')
926
# Create a new instance to ensure storage is permanent
927
branch = _mod_branch.Branch.open('branch')
928
tree_path, branch_location = branch.get_reference_info('file-id')
929
self.assertEqual('path/to/location', branch_location)
931
def test_set_null_reference_info(self):
932
branch = self.make_branch('branch')
934
branch.set_reference_info('file-id', 'path/to/file',
936
except errors.UnsupportedOperation:
937
raise tests.TestNotApplicable('Branch cannot hold references.')
938
branch.set_reference_info('file-id', None, None)
939
tree_path, branch_location = branch.get_reference_info('file-id')
940
self.assertIs(None, tree_path)
941
self.assertIs(None, branch_location)
943
def test_set_null_reference_info_when_null(self):
944
branch = self.make_branch('branch')
946
tree_path, branch_location = branch.get_reference_info('file-id')
947
except errors.UnsupportedOperation:
948
raise tests.TestNotApplicable('Branch cannot hold references.')
949
self.assertIs(None, tree_path)
950
self.assertIs(None, branch_location)
951
branch.set_reference_info('file-id', None, None)
953
def test_set_null_requires_two_nones(self):
954
branch = self.make_branch('branch')
956
e = self.assertRaises(ValueError, branch.set_reference_info,
957
'file-id', 'path', None)
958
except errors.UnsupportedOperation:
959
raise tests.TestNotApplicable('Branch cannot hold references.')
960
self.assertEqual('tree_path must be None when branch_location is'
962
e = self.assertRaises(ValueError, branch.set_reference_info,
963
'file-id', None, 'location')
964
self.assertEqual('branch_location must be None when tree_path is'
967
def make_branch_with_reference(self, location, reference_location,
969
branch = self.make_branch(location)
971
branch.set_reference_info(file_id, 'path/to/file',
973
except errors.UnsupportedOperation:
974
raise tests.TestNotApplicable('Branch cannot hold references.')
977
def test_reference_parent_from_reference_info_(self):
978
referenced_branch = self.make_branch('reference_branch')
979
branch = self.make_branch_with_reference('branch',
980
referenced_branch.base)
981
parent = branch.reference_parent('file-id', 'path/to/file')
982
self.assertEqual(parent.base, referenced_branch.base)
984
def test_branch_relative_reference_location(self):
985
branch = self.make_branch('branch')
987
branch.set_reference_info('file-id', 'path/to/file',
988
'../reference_branch')
989
except errors.UnsupportedOperation:
990
raise tests.TestNotApplicable('Branch cannot hold references.')
991
referenced_branch = self.make_branch('reference_branch')
992
parent = branch.reference_parent('file-id', 'path/to/file')
993
self.assertEqual(parent.base, referenced_branch.base)
995
def test_sprout_copies_reference_location(self):
996
branch = self.make_branch_with_reference('branch', '../reference')
997
new_branch = branch.bzrdir.sprout('new-branch').open_branch()
998
self.assertEqual('../reference',
999
new_branch.get_reference_info('file-id')[1])
1001
def test_clone_copies_reference_location(self):
1002
branch = self.make_branch_with_reference('branch', '../reference')
1003
new_branch = branch.bzrdir.clone('new-branch').open_branch()
1004
self.assertEqual('../reference',
1005
new_branch.get_reference_info('file-id')[1])
1007
def test_copied_locations_are_rebased(self):
1008
branch = self.make_branch_with_reference('branch', 'reference')
1009
new_branch = branch.bzrdir.sprout('branch/new-branch').open_branch()
1010
self.assertEqual('../reference',
1011
new_branch.get_reference_info('file-id')[1])
1013
def test_update_references_retains_old_references(self):
1014
branch = self.make_branch_with_reference('branch', 'reference')
1015
new_branch = self.make_branch_with_reference(
1016
'new_branch', 'reference', 'file-id2')
1017
new_branch.update_references(branch)
1018
self.assertEqual('reference',
1019
branch.get_reference_info('file-id')[1])
1021
def test_update_references_retains_known_references(self):
1022
branch = self.make_branch_with_reference('branch', 'reference')
1023
new_branch = self.make_branch_with_reference(
1024
'new_branch', 'reference2')
1025
new_branch.update_references(branch)
1026
self.assertEqual('reference',
1027
branch.get_reference_info('file-id')[1])
1029
def test_update_references_skips_known_references(self):
1030
branch = self.make_branch_with_reference('branch', 'reference')
1031
new_branch = branch.bzrdir.sprout('branch/new-branch').open_branch()
1032
new_branch.set_reference_info('file-id', '../foo', '../foo')
1033
new_branch.update_references(branch)
1034
self.assertEqual('reference',
1035
branch.get_reference_info('file-id')[1])
1037
def test_pull_updates_references(self):
1038
branch = self.make_branch_with_reference('branch', 'reference')
1039
new_branch = branch.bzrdir.sprout('branch/new-branch').open_branch()
1040
new_branch.set_reference_info('file-id2', '../foo', '../foo')
1041
branch.pull(new_branch)
1042
self.assertEqual('foo',
1043
branch.get_reference_info('file-id2')[1])
1045
def test_push_updates_references(self):
1046
branch = self.make_branch_with_reference('branch', 'reference')
1047
new_branch = branch.bzrdir.sprout('branch/new-branch').open_branch()
1048
new_branch.set_reference_info('file-id2', '../foo', '../foo')
1049
new_branch.push(branch)
1050
self.assertEqual('foo',
1051
branch.get_reference_info('file-id2')[1])
1053
def test_merge_updates_references(self):
1054
branch = self.make_branch_with_reference('branch', 'reference')
1055
tree = self.make_branch_and_tree('tree')
1057
branch.pull(tree.branch)
1058
checkout = branch.create_checkout('checkout', lightweight=True)
1059
checkout.commit('bar')
1061
self.addCleanup(tree.unlock)
1062
merger = merge.Merger.from_revision_ids(None, tree,
1063
branch.last_revision(),
1064
other_branch=branch)
1065
merger.merge_type = merge.Merge3Merger
1067
self.assertEqual('../branch/reference',
1068
tree.branch.get_reference_info('file-id')[1])
1071
class TestBranchControlComponent(per_branch.TestCaseWithBranch):
1072
"""Branch implementations adequately implement ControlComponent."""
1074
def test_urls(self):
1075
br = self.make_branch('branch')
1076
self.assertIsInstance(br.user_url, str)
1077
self.assertEqual(br.user_url, br.user_transport.base)
1078
# for all current bzrdir implementations the user dir must be
1079
# above the control dir but we might need to relax that?
1080
self.assertEqual(br.control_url.find(br.user_url), 0)
1081
self.assertEqual(br.control_url, br.control_transport.base)
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
from bzrlib.selftest import InTempDir
21
class TestAppendRevisions(InTempDir):
22
"""Test appending more than one revision"""
24
from bzrlib.branch import Branch
25
br = Branch(".", init=True)
26
br.append_revision("rev1")
27
self.assertEquals(br.revision_history(), ["rev1",])
28
br.append_revision("rev2", "rev3")
29
self.assertEquals(br.revision_history(), ["rev1", "rev2", "rev3"])