1
# Copyright (C) 2005-2012 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."""
22
branch as _mod_branch,
39
from bzrlib.tests import (
42
from bzrlib.tests.http_server import HttpServer
43
from bzrlib.transport import memory
46
class TestTestCaseWithBranch(per_branch.TestCaseWithBranch):
48
def test_branch_format_matches_bzrdir_branch_format(self):
49
bzrdir_branch_format = self.bzrdir_format.get_branch_format()
50
self.assertIs(self.branch_format.__class__,
51
bzrdir_branch_format.__class__)
53
def test_make_branch_gets_expected_format(self):
54
branch = self.make_branch('.')
55
self.assertIs(self.branch_format.__class__,
56
branch._format.__class__)
59
class TestBranch(per_branch.TestCaseWithBranch):
61
def test_create_tree_with_merge(self):
62
tree = self.create_tree_with_merge()
64
self.addCleanup(tree.unlock)
65
graph = tree.branch.repository.get_graph()
66
ancestry_graph = graph.get_parent_map(
67
tree.branch.repository.all_revision_ids())
68
self.assertEqual({'rev-1':('null:',),
70
'rev-1.1.1':('rev-1', ),
71
'rev-3':('rev-2', 'rev-1.1.1', ),
74
def test_revision_ids_are_utf8(self):
75
wt = self.make_branch_and_tree('tree')
76
wt.commit('f', rev_id='rev1')
77
wt.commit('f', rev_id='rev2')
78
wt.commit('f', rev_id='rev3')
80
br = self.get_branch()
82
br.generate_revision_history('rev3')
83
for revision_id in ['rev3', 'rev2', 'rev1']:
84
self.assertIsInstance(revision_id, str)
85
last = br.last_revision()
86
self.assertEqual('rev3', last)
87
self.assertIsInstance(last, str)
88
revno, last = br.last_revision_info()
89
self.assertEqual(3, revno)
90
self.assertEqual('rev3', last)
91
self.assertIsInstance(last, str)
93
def test_fetch_revisions(self):
94
"""Test fetch-revision operation."""
95
wt = self.make_branch_and_tree('b1')
97
self.build_tree_contents([('b1/foo', 'hello')])
98
wt.add(['foo'], ['foo-id'])
99
wt.commit('lala!', rev_id='revision-1', allow_pointless=False)
101
b2 = self.make_branch('b2')
104
rev = b2.repository.get_revision('revision-1')
105
tree = b2.repository.revision_tree('revision-1')
107
self.addCleanup(tree.unlock)
108
self.assertEqual(tree.get_file_text('foo-id'), 'hello')
110
def test_get_revision_delta(self):
111
tree_a = self.make_branch_and_tree('a')
112
self.build_tree(['a/foo'])
113
tree_a.add('foo', 'file1')
114
tree_a.commit('rev1', rev_id='rev1')
115
self.build_tree(['a/vla'])
116
tree_a.add('vla', 'file2')
117
tree_a.commit('rev2', rev_id='rev2')
119
delta = self.applyDeprecated(symbol_versioning.deprecated_in(
120
(2, 5, 0)), tree_a.branch.get_revision_delta, 1)
121
self.assertIsInstance(delta, _mod_delta.TreeDelta)
122
self.assertEqual([('foo', 'file1', 'file')], delta.added)
123
delta = self.applyDeprecated(symbol_versioning.deprecated_in(
124
(2, 5, 0)), tree_a.branch.get_revision_delta, 2)
125
self.assertIsInstance(delta, _mod_delta.TreeDelta)
126
self.assertEqual([('vla', 'file2', 'file')], delta.added)
128
def get_unbalanced_tree_pair(self):
129
"""Return two branches, a and b, with one file in a."""
130
tree_a = self.make_branch_and_tree('a')
131
self.build_tree_contents([('a/b', 'b')])
133
tree_a.commit("silly commit", rev_id='A')
135
tree_b = self.make_branch_and_tree('b')
136
return tree_a, tree_b
138
def get_balanced_branch_pair(self):
139
"""Returns br_a, br_b as with one commit in a, and b has a's stores."""
140
tree_a, tree_b = self.get_unbalanced_tree_pair()
141
tree_b.branch.repository.fetch(tree_a.branch.repository)
142
return tree_a, tree_b
144
def test_clone_partial(self):
145
"""Copy only part of the history of a branch."""
146
# TODO: RBC 20060208 test with a revision not on revision-history.
147
# what should that behaviour be ? Emailed the list.
148
# First, make a branch with two commits.
149
wt_a = self.make_branch_and_tree('a')
150
self.build_tree(['a/one'])
152
wt_a.commit('commit one', rev_id='1')
153
self.build_tree(['a/two'])
155
wt_a.commit('commit two', rev_id='2')
156
# Now make a copy of the repository.
157
repo_b = self.make_repository('b')
158
wt_a.branch.repository.copy_content_into(repo_b)
159
# wt_a might be a lightweight checkout, so get a hold of the actual
160
# branch (because you can't do a partial clone of a lightweight
162
branch = wt_a.branch.bzrdir.open_branch()
163
# Then make a branch where the new repository is, but specify a revision
164
# ID. The new branch's history will stop at the specified revision.
165
br_b = branch.clone(repo_b.bzrdir, revision_id='1')
166
self.assertEqual('1', br_b.last_revision())
168
def get_parented_branch(self):
169
wt_a = self.make_branch_and_tree('a')
170
self.build_tree(['a/one'])
172
wt_a.commit('commit one', rev_id='1')
174
branch_b = wt_a.branch.bzrdir.sprout('b', revision_id='1').open_branch()
175
self.assertEqual(wt_a.branch.base, branch_b.get_parent())
178
def test_clone_branch_nickname(self):
179
# test the nick name is preserved always
180
raise tests.TestSkipped('XXX branch cloning is not yet tested.')
182
def test_clone_branch_parent(self):
183
# test the parent is preserved always
184
branch_b = self.get_parented_branch()
185
repo_c = self.make_repository('c')
186
branch_b.repository.copy_content_into(repo_c)
187
branch_c = branch_b.clone(repo_c.bzrdir)
188
self.assertNotEqual(None, branch_c.get_parent())
189
self.assertEqual(branch_b.get_parent(), branch_c.get_parent())
191
# We can also set a specific parent, and it should be honored
192
random_parent = 'http://example.com/path/to/branch'
193
branch_b.set_parent(random_parent)
194
repo_d = self.make_repository('d')
195
branch_b.repository.copy_content_into(repo_d)
196
branch_d = branch_b.clone(repo_d.bzrdir)
197
self.assertEqual(random_parent, branch_d.get_parent())
199
def test_submit_branch(self):
200
"""Submit location can be queried and set"""
201
branch = self.make_branch('branch')
202
self.assertEqual(branch.get_submit_branch(), None)
203
branch.set_submit_branch('sftp://example.com')
204
self.assertEqual(branch.get_submit_branch(), 'sftp://example.com')
205
branch.set_submit_branch('sftp://example.net')
206
self.assertEqual(branch.get_submit_branch(), 'sftp://example.net')
208
def test_public_branch(self):
209
"""public location can be queried and set"""
210
branch = self.make_branch('branch')
211
self.assertEqual(branch.get_public_branch(), None)
212
branch.set_public_branch('sftp://example.com')
213
self.assertEqual(branch.get_public_branch(), 'sftp://example.com')
214
branch.set_public_branch('sftp://example.net')
215
self.assertEqual(branch.get_public_branch(), 'sftp://example.net')
216
branch.set_public_branch(None)
217
self.assertEqual(branch.get_public_branch(), None)
219
def test_record_initial_ghost(self):
220
"""Branches should support having ghosts."""
221
wt = self.make_branch_and_tree('.')
222
if not wt.branch.repository._format.supports_ghosts:
223
raise tests.TestNotApplicable("repository format does not "
225
wt.set_parent_ids(['non:existent@rev--ision--0--2'],
226
allow_leftmost_as_ghost=True)
227
self.assertEqual(['non:existent@rev--ision--0--2'],
229
rev_id = wt.commit('commit against a ghost first parent.')
230
rev = wt.branch.repository.get_revision(rev_id)
231
self.assertEqual(rev.parent_ids, ['non:existent@rev--ision--0--2'])
232
# parent_sha1s is not populated now, WTF. rbc 20051003
233
self.assertEqual(len(rev.parent_sha1s), 0)
235
def test_record_two_ghosts(self):
236
"""Recording with all ghosts works."""
237
wt = self.make_branch_and_tree('.')
238
if not wt.branch.repository._format.supports_ghosts:
239
raise tests.TestNotApplicable("repository format does not "
242
'foo@azkhazan-123123-abcabc',
243
'wibble@fofof--20050401--1928390812',
245
allow_leftmost_as_ghost=True)
246
rev_id = wt.commit("commit from ghost base with one merge")
247
# the revision should have been committed with two parents
248
rev = wt.branch.repository.get_revision(rev_id)
249
self.assertEqual(['foo@azkhazan-123123-abcabc',
250
'wibble@fofof--20050401--1928390812'],
253
def test_bad_revision(self):
254
self.assertRaises(errors.InvalidRevisionId,
255
self.get_branch().repository.get_revision,
258
def test_nicks_bzr(self):
259
"""Test the behaviour of branch nicks specific to bzr branches.
261
Nicknames are implicitly the name of the branch's directory, unless an
262
explicit nickname is set. That is, an explicit nickname always
263
overrides the implicit one.
266
t = self.get_transport()
267
branch = self.make_branch('bzr.dev')
268
if not isinstance(branch, _mod_branch.BzrBranch):
269
raise tests.TestNotApplicable("not a bzr branch format")
270
# The nick will be 'bzr.dev', because there is no explicit nick set.
271
self.assertEqual(branch.nick, 'bzr.dev')
272
# Move the branch to a different directory, 'bzr.ab'. Now that branch
273
# will report its nick as 'bzr.ab'.
274
t.move('bzr.dev', 'bzr.ab')
275
branch = _mod_branch.Branch.open(self.get_url('bzr.ab'))
276
self.assertEqual(branch.nick, 'bzr.ab')
277
# Set the branch nick explicitly. This will ensure there's a branch
278
# config file in the branch.
279
branch.nick = "Aaron's branch"
280
if not isinstance(branch, remote.RemoteBranch):
281
self.assertTrue(branch._transport.has("branch.conf"))
282
# Because the nick has been set explicitly, the nick is now always
283
# "Aaron's branch", regardless of directory name.
284
self.assertEqual(branch.nick, "Aaron's branch")
285
t.move('bzr.ab', 'integration')
286
branch = _mod_branch.Branch.open(self.get_url('integration'))
287
self.assertEqual(branch.nick, "Aaron's branch")
288
branch.nick = u"\u1234"
289
self.assertEqual(branch.nick, u"\u1234")
291
def test_nicks(self):
292
"""Test explicit and implicit branch nicknames.
294
A nickname is always available, whether set explicitly or not.
296
t = self.get_transport()
297
branch = self.make_branch('bzr.dev')
298
# An implicit nick name is set; what it is exactly depends on the
300
self.assertIsInstance(branch.nick, basestring)
301
# Set the branch nick explicitly.
302
branch.nick = "Aaron's branch"
303
# Because the nick has been set explicitly, the nick is now always
305
self.assertEqual(branch.nick, "Aaron's branch")
306
branch.nick = u"\u1234"
307
self.assertEqual(branch.nick, u"\u1234")
309
def test_commit_nicks(self):
310
"""Nicknames are committed to the revision"""
311
wt = self.make_branch_and_tree('bzr.dev')
313
branch.nick = "My happy branch"
314
wt.commit('My commit respect da nick.')
315
committed = branch.repository.get_revision(branch.last_revision())
316
self.assertEqual(committed.properties["branch-nick"],
319
def test_create_colocated(self):
321
repo = self.make_repository('.', shared=True)
322
except errors.IncompatibleFormat:
324
if repo.bzrdir._format.colocated_branches:
325
raise tests.TestNotApplicable(
326
"control dir does not support colocated branches")
327
self.assertEquals(0, len(repo.bzrdir.list_branches()))
328
if not self.bzrdir_format.colocated_branches:
329
raise tests.TestNotApplicable("control dir format does not support "
330
"colocated branches")
332
child_branch1 = self.branch_format.initialize(repo.bzrdir,
334
except errors.UninitializableFormat:
335
# branch references are not default init'able and
336
# not all bzrdirs support colocated branches.
338
self.assertEquals(1, len(repo.bzrdir.list_branches()))
339
self.branch_format.initialize(repo.bzrdir, name='branch2')
340
self.assertEquals(2, len(repo.bzrdir.list_branches()))
342
def test_create_append_revisions_only(self):
344
repo = self.make_repository('.', shared=True)
345
except errors.IncompatibleFormat:
347
for val in (True, False):
349
branch = self.branch_format.initialize(repo.bzrdir,
350
append_revisions_only=True)
351
except (errors.UninitializableFormat, errors.UpgradeRequired):
352
# branch references are not default init'able and
353
# not all branches support append_revisions_only
355
self.assertEquals(True, branch.get_append_revisions_only())
356
repo.bzrdir.destroy_branch()
358
def test_get_set_append_revisions_only(self):
359
branch = self.make_branch('.')
360
if branch._format.supports_set_append_revisions_only():
361
branch.set_append_revisions_only(True)
362
self.assertTrue(branch.get_append_revisions_only())
363
branch.set_append_revisions_only(False)
364
self.assertFalse(branch.get_append_revisions_only())
366
self.assertRaises(errors.UpgradeRequired,
367
branch.set_append_revisions_only, True)
368
self.assertFalse(branch.get_append_revisions_only())
370
def test_create_open_branch_uses_repository(self):
372
repo = self.make_repository('.', shared=True)
373
except errors.IncompatibleFormat:
374
raise tests.TestNotApplicable("requires shared repository support")
375
child_transport = repo.bzrdir.root_transport.clone('child')
376
child_transport.mkdir('.')
378
child_dir = self.bzrdir_format.initialize_on_transport(child_transport)
379
except errors.UninitializableFormat:
380
raise tests.TestNotApplicable("control dir format not initializable")
382
child_branch = self.branch_format.initialize(child_dir)
383
except errors.UninitializableFormat:
384
# branch references are not default init'able.
386
self.assertEqual(repo.bzrdir.root_transport.base,
387
child_branch.repository.bzrdir.root_transport.base)
388
child_branch = _mod_branch.Branch.open(self.get_url('child'))
389
self.assertEqual(repo.bzrdir.root_transport.base,
390
child_branch.repository.bzrdir.root_transport.base)
392
def test_format_description(self):
393
tree = self.make_branch_and_tree('tree')
394
text = tree.branch._format.get_format_description()
395
self.assertTrue(len(text))
397
def test_get_commit_builder(self):
398
branch = self.make_branch(".")
400
builder = branch.get_commit_builder([])
401
self.assertIsInstance(builder, repository.CommitBuilder)
402
branch.repository.commit_write_group()
405
def test_generate_revision_history(self):
406
"""Create a fake revision history easily."""
407
tree = self.make_branch_and_tree('.')
408
rev1 = tree.commit('foo')
410
self.addCleanup(tree.unlock)
411
graph = tree.branch.repository.get_graph()
413
graph.iter_lefthand_ancestry(
414
tree.branch.last_revision(), [revision.NULL_REVISION]))
415
rev2 = tree.commit('bar', allow_pointless=True)
416
tree.branch.generate_revision_history(rev1)
417
self.assertEqual(orig_history, list(
418
graph.iter_lefthand_ancestry(
419
tree.branch.last_revision(), [revision.NULL_REVISION])))
421
def test_generate_revision_history_NULL_REVISION(self):
422
tree = self.make_branch_and_tree('.')
423
rev1 = tree.commit('foo')
425
self.addCleanup(tree.unlock)
426
tree.branch.generate_revision_history(revision.NULL_REVISION)
427
self.assertEqual(revision.NULL_REVISION, tree.branch.last_revision())
429
def test_create_checkout(self):
430
tree_a = self.make_branch_and_tree('a')
431
branch_a = tree_a.branch
432
checkout_b = branch_a.create_checkout('b')
433
self.assertEqual('null:', checkout_b.last_revision())
434
checkout_b.commit('rev1', rev_id='rev1')
435
self.assertEqual('rev1', branch_a.last_revision())
436
self.assertNotEqual(checkout_b.branch.base, branch_a.base)
438
checkout_c = branch_a.create_checkout('c', lightweight=True)
439
self.assertEqual('rev1', checkout_c.last_revision())
440
checkout_c.commit('rev2', rev_id='rev2')
441
self.assertEqual('rev2', branch_a.last_revision())
442
self.assertEqual(checkout_c.branch.base, branch_a.base)
444
checkout_d = branch_a.create_checkout('d', lightweight=True)
445
self.assertEqual('rev2', checkout_d.last_revision())
446
checkout_e = branch_a.create_checkout('e')
447
self.assertEqual('rev2', checkout_e.last_revision())
449
def test_create_anonymous_lightweight_checkout(self):
450
"""A lightweight checkout from a readonly branch should succeed."""
451
tree_a = self.make_branch_and_tree('a')
452
rev_id = tree_a.commit('put some content in the branch')
453
# open the branch via a readonly transport
454
url = self.get_readonly_url(urlutils.basename(tree_a.branch.base))
455
t = transport.get_transport_from_url(url)
456
if not tree_a.branch.bzrdir._format.supports_transport(t):
457
raise tests.TestNotApplicable("format does not support transport")
458
source_branch = _mod_branch.Branch.open(url)
459
# sanity check that the test will be valid
460
self.assertRaises((errors.LockError, errors.TransportNotPossible),
461
source_branch.lock_write)
462
checkout = source_branch.create_checkout('c', lightweight=True)
463
self.assertEqual(rev_id, checkout.last_revision())
465
def test_create_anonymous_heavyweight_checkout(self):
466
"""A regular checkout from a readonly branch should succeed."""
467
tree_a = self.make_branch_and_tree('a')
468
rev_id = tree_a.commit('put some content in the branch')
469
# open the branch via a readonly transport
470
url = self.get_readonly_url(
471
osutils.basename(tree_a.branch.base.rstrip('/')))
472
t = transport.get_transport_from_url(url)
473
if not tree_a.branch.bzrdir._format.supports_transport(t):
474
raise tests.TestNotApplicable("format does not support transport")
475
source_branch = _mod_branch.Branch.open(url)
476
# sanity check that the test will be valid
477
self.assertRaises((errors.LockError, errors.TransportNotPossible),
478
source_branch.lock_write)
479
checkout = source_branch.create_checkout('c')
480
self.assertEqual(rev_id, checkout.last_revision())
482
def test_heads_to_fetch(self):
483
# heads_to_fetch is a method that returns a collection of revids that
484
# need to be fetched to copy this branch into another repo. At a
485
# minimum this will include the tip.
486
# (In native formats, this is the tip + tags, but other formats may
487
# have other revs needed)
488
tree = self.make_branch_and_tree('a')
489
tree.commit('first commit', rev_id='rev1')
490
tree.commit('second commit', rev_id='rev2')
491
must_fetch, should_fetch = tree.branch.heads_to_fetch()
492
self.assertTrue('rev2' in must_fetch)
494
def test_heads_to_fetch_not_null_revision(self):
495
# NULL_REVISION does not appear in the result of heads_to_fetch, even
496
# for an empty branch.
497
tree = self.make_branch_and_tree('a')
498
must_fetch, should_fetch = tree.branch.heads_to_fetch()
499
self.assertFalse(revision.NULL_REVISION in must_fetch)
500
self.assertFalse(revision.NULL_REVISION in should_fetch)
503
class TestBranchFormat(per_branch.TestCaseWithBranch):
505
def test_branch_format_network_name(self):
506
br = self.make_branch('.')
508
network_name = format.network_name()
509
self.assertIsInstance(network_name, str)
510
# We want to test that the network_name matches the actual format on
511
# disk. For local branches that means that using network_name as a key
512
# in the registry gives back the same format. For remote branches we
513
# check that the network_name of the RemoteBranchFormat we have locally
514
# matches the actual format present on disk.
515
if isinstance(format, remote.RemoteBranchFormat):
517
real_branch = br._real_branch
518
self.assertEqual(real_branch._format.network_name(), network_name)
520
registry = _mod_branch.network_format_registry
521
looked_up_format = registry.get(network_name)
522
self.assertEqual(format.__class__, looked_up_format.__class__)
524
def test_get_config_calls(self):
525
# Smoke test that all branch succeed getting a config
526
br = self.make_branch('.')
528
br.get_config_stack()
531
class ChrootedTests(per_branch.TestCaseWithBranch):
532
"""A support class that provides readonly urls outside the local namespace.
534
This is done by checking if self.transport_server is a MemoryServer. if it
535
is then we are chrooted already, if it is not then an HttpServer is used
540
super(ChrootedTests, self).setUp()
541
if not self.vfs_transport_factory == memory.MemoryServer:
542
self.transport_readonly_server = HttpServer
544
def test_open_containing(self):
545
self.assertRaises(errors.NotBranchError,
546
_mod_branch.Branch.open_containing,
547
self.get_readonly_url(''))
548
self.assertRaises(errors.NotBranchError,
549
_mod_branch.Branch.open_containing,
550
self.get_readonly_url('g/p/q'))
551
branch = self.make_branch('.')
552
if not branch.bzrdir._format.supports_transport(
553
transport.get_transport_from_url(self.get_readonly_url('.'))):
554
raise tests.TestNotApplicable("format does not support transport")
555
branch, relpath = _mod_branch.Branch.open_containing(
556
self.get_readonly_url(''))
557
self.assertEqual('', relpath)
558
branch, relpath = _mod_branch.Branch.open_containing(
559
self.get_readonly_url('g/p/q'))
560
self.assertEqual('g/p/q', relpath)
563
class InstrumentedTransaction(object):
566
self.calls.append('finish')
572
class TestDecorator(object):
578
self._calls.append('lr')
580
def lock_write(self):
581
self._calls.append('lw')
584
self._calls.append('ul')
586
@_mod_branch.needs_read_lock
587
def do_with_read(self):
590
@_mod_branch.needs_read_lock
591
def except_with_read(self):
594
@_mod_branch.needs_write_lock
595
def do_with_write(self):
598
@_mod_branch.needs_write_lock
599
def except_with_write(self):
603
class TestDecorators(tests.TestCase):
605
def test_needs_read_lock(self):
606
branch = TestDecorator()
607
self.assertEqual(1, branch.do_with_read())
608
self.assertEqual(['lr', 'ul'], branch._calls)
610
def test_excepts_in_read_lock(self):
611
branch = TestDecorator()
612
self.assertRaises(RuntimeError, branch.except_with_read)
613
self.assertEqual(['lr', 'ul'], branch._calls)
615
def test_needs_write_lock(self):
616
branch = TestDecorator()
617
self.assertEqual(2, branch.do_with_write())
618
self.assertEqual(['lw', 'ul'], branch._calls)
620
def test_excepts_in_write_lock(self):
621
branch = TestDecorator()
622
self.assertRaises(RuntimeError, branch.except_with_write)
623
self.assertEqual(['lw', 'ul'], branch._calls)
626
class TestBranchPushLocations(per_branch.TestCaseWithBranch):
628
def test_get_push_location_unset(self):
629
self.assertEqual(None, self.get_branch().get_push_location())
631
def test_get_push_location_exact(self):
632
b = self.get_branch()
633
config.LocationConfig.from_string(
634
'[%s]\npush_location=foo\n' % (b.base,), b.base, save=True)
635
self.assertEqual("foo", self.get_branch().get_push_location())
637
def test_set_push_location(self):
638
branch = self.get_branch()
639
branch.set_push_location('foo')
640
self.assertEqual('foo', branch.get_push_location())
643
class TestChildSubmitFormats(per_branch.TestCaseWithBranch):
645
def test_get_child_submit_format_default(self):
646
submit_format = self.get_branch().get_child_submit_format()
647
self.assertTrue(submit_format is None or
648
isinstance(submit_format, str))
650
def test_get_child_submit_format(self):
651
branch = self.get_branch()
652
branch.get_config_stack().set('child_submit_format', '10')
653
branch = self.get_branch()
654
self.assertEqual('10', branch.get_child_submit_format())
657
class TestFormat(per_branch.TestCaseWithBranch):
658
"""Tests for the format itself."""
660
def test_get_reference(self):
661
"""get_reference on all regular branches should return None."""
662
if not self.branch_format.is_supported():
663
# unsupported formats are not loopback testable
664
# because the default open will not open them and
665
# they may not be initializable.
667
made_branch = self.make_branch('.')
668
self.assertEqual(None,
669
made_branch._format.get_reference(made_branch.bzrdir))
671
def test_set_reference(self):
672
"""set_reference on all regular branches should be callable."""
673
if not self.branch_format.is_supported():
674
# unsupported formats are not loopback testable
675
# because the default open will not open them and
676
# they may not be initializable.
678
this_branch = self.make_branch('this')
679
other_branch = self.make_branch('other')
681
this_branch._format.set_reference(this_branch.bzrdir, None,
683
except NotImplementedError:
687
ref = this_branch._format.get_reference(this_branch.bzrdir)
688
self.assertEqual(ref, other_branch.base)
690
def test_format_initialize_find_open(self):
691
# loopback test to check the current format initializes to itself.
692
if not self.branch_format.is_supported():
693
# unsupported formats are not loopback testable
694
# because the default open will not open them and
695
# they may not be initializable.
697
# supported formats must be able to init and open
698
t = self.get_transport()
699
readonly_t = transport.get_transport_from_url(self.get_readonly_url())
700
made_branch = self.make_branch('.')
701
self.assertIsInstance(made_branch, _mod_branch.Branch)
703
# find it via bzrdir opening:
704
opened_control = controldir.ControlDir.open(readonly_t.base)
705
direct_opened_branch = opened_control.open_branch()
706
self.assertEqual(direct_opened_branch.__class__, made_branch.__class__)
707
self.assertEqual(opened_control, direct_opened_branch.bzrdir)
708
self.assertIsInstance(direct_opened_branch._format,
709
self.branch_format.__class__)
711
# find it via Branch.open
712
opened_branch = _mod_branch.Branch.open(readonly_t.base)
713
self.assertIsInstance(opened_branch, made_branch.__class__)
714
self.assertEqual(made_branch._format.__class__,
715
opened_branch._format.__class__)
716
# if it has a unique id string, can we probe for it ?
718
self.branch_format.get_format_string()
719
except NotImplementedError:
721
self.assertEqual(self.branch_format,
722
opened_control.find_branch_format())
725
class TestBound(per_branch.TestCaseWithBranch):
727
def test_bind_unbind(self):
728
branch = self.make_branch('1')
729
branch2 = self.make_branch('2')
732
except errors.UpgradeRequired:
733
raise tests.TestNotApplicable('Format does not support binding')
734
self.assertTrue(branch.unbind())
735
self.assertFalse(branch.unbind())
736
self.assertIs(None, branch.get_bound_location())
738
def test_old_bound_location(self):
739
branch = self.make_branch('branch1')
741
self.assertIs(None, branch.get_old_bound_location())
742
except errors.UpgradeRequired:
743
raise tests.TestNotApplicable(
744
'Format does not store old bound locations')
745
branch2 = self.make_branch('branch2')
747
self.assertIs(None, branch.get_old_bound_location())
749
self.assertContainsRe(branch.get_old_bound_location(), '\/branch2\/$')
751
def test_bind_diverged(self):
752
tree_a = self.make_branch_and_tree('tree_a')
753
tree_a.commit('rev1a')
754
tree_b = tree_a.bzrdir.sprout('tree_b').open_workingtree()
755
tree_a.commit('rev2a')
756
tree_b.commit('rev2b')
758
tree_b.branch.bind(tree_a.branch)
759
except errors.UpgradeRequired:
760
raise tests.TestNotApplicable('Format does not support binding')
762
def test_unbind_clears_cached_master_branch(self):
763
"""b.unbind clears any cached value of b.get_master_branch."""
764
master = self.make_branch('master')
765
branch = self.make_branch('branch')
768
except errors.UpgradeRequired:
769
raise tests.TestNotApplicable('Format does not support binding')
770
self.addCleanup(branch.lock_write().unlock)
771
self.assertNotEqual(None, branch.get_master_branch())
773
self.assertEqual(None, branch.get_master_branch())
775
def test_bind_clears_cached_master_branch(self):
776
"""b.bind clears any cached value of b.get_master_branch."""
777
master1 = self.make_branch('master1')
778
master2 = self.make_branch('master2')
779
branch = self.make_branch('branch')
782
except errors.UpgradeRequired:
783
raise tests.TestNotApplicable('Format does not support binding')
784
self.addCleanup(branch.lock_write().unlock)
785
self.assertNotEqual(None, branch.get_master_branch())
787
self.assertEqual('.', urlutils.relative_url(self.get_url('master2'),
788
branch.get_master_branch().base))
790
def test_set_bound_location_clears_cached_master_branch(self):
791
"""b.set_bound_location clears any cached value of b.get_master_branch.
793
master1 = self.make_branch('master1')
794
master2 = self.make_branch('master2')
795
branch = self.make_branch('branch')
798
except errors.UpgradeRequired:
799
raise tests.TestNotApplicable('Format does not support binding')
800
self.addCleanup(branch.lock_write().unlock)
801
self.assertNotEqual(None, branch.get_master_branch())
802
branch.set_bound_location(self.get_url('master2'))
803
self.assertEqual('.', urlutils.relative_url(self.get_url('master2'),
804
branch.get_master_branch().base))
807
class TestStrict(per_branch.TestCaseWithBranch):
809
def test_strict_history(self):
810
tree1 = self.make_branch_and_tree('tree1')
812
tree1.branch.set_append_revisions_only(True)
813
except errors.UpgradeRequired:
814
raise tests.TestSkipped('Format does not support strict history')
815
tree1.commit('empty commit')
816
tree2 = tree1.bzrdir.sprout('tree2').open_workingtree()
817
tree2.commit('empty commit 2')
818
tree1.pull(tree2.branch)
819
tree1.commit('empty commit 3')
820
tree2.commit('empty commit 4')
821
self.assertRaises(errors.DivergedBranches, tree1.pull, tree2.branch)
822
tree2.merge_from_branch(tree1.branch)
823
tree2.commit('empty commit 5')
824
self.assertRaises(errors.AppendRevisionsOnlyViolation, tree1.pull,
826
tree3 = tree1.bzrdir.sprout('tree3').open_workingtree()
827
tree3.merge_from_branch(tree2.branch)
828
tree3.commit('empty commit 6')
829
tree2.pull(tree3.branch)
832
class TestIgnoreFallbacksParameter(per_branch.TestCaseWithBranch):
834
def make_branch_with_fallback(self):
835
fallback = self.make_branch('fallback')
836
if not fallback._format.supports_stacking():
837
raise tests.TestNotApplicable("format does not support stacking")
838
stacked = self.make_branch('stacked')
839
stacked.set_stacked_on_url(fallback.base)
842
def test_fallbacks_not_opened(self):
843
stacked = self.make_branch_with_fallback()
844
self.get_transport('').rename('fallback', 'moved')
845
reopened_dir = controldir.ControlDir.open(stacked.base)
846
reopened = reopened_dir.open_branch(ignore_fallbacks=True)
847
self.assertEqual([], reopened.repository._fallback_repositories)
849
def test_fallbacks_are_opened(self):
850
stacked = self.make_branch_with_fallback()
851
reopened_dir = controldir.ControlDir.open(stacked.base)
852
reopened = reopened_dir.open_branch(ignore_fallbacks=False)
853
self.assertLength(1, reopened.repository._fallback_repositories)
856
class TestReferenceLocation(per_branch.TestCaseWithBranch):
858
def test_reference_parent(self):
859
tree = self.make_branch_and_tree('tree')
860
subtree = self.make_branch_and_tree('tree/subtree')
861
subtree.set_root_id('subtree-id')
863
tree.add_reference(subtree)
864
except errors.UnsupportedOperation:
865
raise tests.TestNotApplicable('Tree cannot hold references.')
866
reference_parent = tree.branch.reference_parent(
868
urlutils.relative_url(tree.branch.user_url, subtree.branch.user_url))
869
self.assertEqual(subtree.branch.base, reference_parent.base)
871
def test_reference_parent_accepts_possible_transports(self):
872
tree = self.make_branch_and_tree('tree')
873
subtree = self.make_branch_and_tree('tree/subtree')
874
subtree.set_root_id('subtree-id')
876
tree.add_reference(subtree)
877
except errors.UnsupportedOperation:
878
raise tests.TestNotApplicable('Tree cannot hold references.')
879
reference_parent = tree.branch.reference_parent('subtree-id',
880
urlutils.relative_url(
881
tree.branch.user_url, subtree.branch.user_url),
882
possible_transports=[subtree.bzrdir.root_transport])
884
def test_get_reference_info(self):
885
branch = self.make_branch('branch')
887
path, loc = branch.get_reference_info('file-id')
888
except errors.UnsupportedOperation:
889
raise tests.TestNotApplicable('Branch cannot hold references.')
890
self.assertIs(None, path)
891
self.assertIs(None, loc)
893
def test_set_reference_info(self):
894
branch = self.make_branch('branch')
896
branch.set_reference_info('file-id', 'path/to/location',
898
except errors.UnsupportedOperation:
899
raise tests.TestNotApplicable('Branch cannot hold references.')
901
def test_set_get_reference_info(self):
902
branch = self.make_branch('branch')
904
branch.set_reference_info('file-id', 'path/to/file',
906
except errors.UnsupportedOperation:
907
raise tests.TestNotApplicable('Branch cannot hold references.')
908
# Create a new instance to ensure storage is permanent
909
branch = _mod_branch.Branch.open('branch')
910
tree_path, branch_location = branch.get_reference_info('file-id')
911
self.assertEqual('path/to/location', branch_location)
913
def test_set_null_reference_info(self):
914
branch = self.make_branch('branch')
916
branch.set_reference_info('file-id', 'path/to/file',
918
except errors.UnsupportedOperation:
919
raise tests.TestNotApplicable('Branch cannot hold references.')
920
branch.set_reference_info('file-id', None, None)
921
tree_path, branch_location = branch.get_reference_info('file-id')
922
self.assertIs(None, tree_path)
923
self.assertIs(None, branch_location)
925
def test_set_null_reference_info_when_null(self):
926
branch = self.make_branch('branch')
928
tree_path, branch_location = branch.get_reference_info('file-id')
929
except errors.UnsupportedOperation:
930
raise tests.TestNotApplicable('Branch cannot hold references.')
931
self.assertIs(None, tree_path)
932
self.assertIs(None, branch_location)
933
branch.set_reference_info('file-id', None, None)
935
def test_set_null_requires_two_nones(self):
936
branch = self.make_branch('branch')
938
e = self.assertRaises(ValueError, branch.set_reference_info,
939
'file-id', 'path', None)
940
except errors.UnsupportedOperation:
941
raise tests.TestNotApplicable('Branch cannot hold references.')
942
self.assertEqual('tree_path must be None when branch_location is'
944
e = self.assertRaises(ValueError, branch.set_reference_info,
945
'file-id', None, 'location')
946
self.assertEqual('branch_location must be None when tree_path is'
949
def make_branch_with_reference(self, location, reference_location,
951
branch = self.make_branch(location)
953
branch.set_reference_info(file_id, 'path/to/file',
955
except errors.UnsupportedOperation:
956
raise tests.TestNotApplicable('Branch cannot hold references.')
959
def test_reference_parent_from_reference_info_(self):
960
referenced_branch = self.make_branch('reference_branch')
961
branch = self.make_branch_with_reference('branch',
962
referenced_branch.base)
963
parent = branch.reference_parent('file-id', 'path/to/file')
964
self.assertEqual(parent.base, referenced_branch.base)
966
def test_branch_relative_reference_location(self):
967
branch = self.make_branch('branch')
969
branch.set_reference_info('file-id', 'path/to/file',
970
'../reference_branch')
971
except errors.UnsupportedOperation:
972
raise tests.TestNotApplicable('Branch cannot hold references.')
973
referenced_branch = self.make_branch('reference_branch')
974
parent = branch.reference_parent('file-id', 'path/to/file')
975
self.assertEqual(parent.base, referenced_branch.base)
977
def test_sprout_copies_reference_location(self):
978
branch = self.make_branch_with_reference('branch', '../reference')
979
new_branch = branch.bzrdir.sprout('new-branch').open_branch()
980
self.assertEqual('../reference',
981
new_branch.get_reference_info('file-id')[1])
983
def test_clone_copies_reference_location(self):
984
branch = self.make_branch_with_reference('branch', '../reference')
985
new_branch = branch.bzrdir.clone('new-branch').open_branch()
986
self.assertEqual('../reference',
987
new_branch.get_reference_info('file-id')[1])
989
def test_copied_locations_are_rebased(self):
990
branch = self.make_branch_with_reference('branch', 'reference')
991
new_branch = branch.bzrdir.sprout('branch/new-branch').open_branch()
992
self.assertEqual('../reference',
993
new_branch.get_reference_info('file-id')[1])
995
def test_update_references_retains_old_references(self):
996
branch = self.make_branch_with_reference('branch', 'reference')
997
new_branch = self.make_branch_with_reference(
998
'new_branch', 'reference', 'file-id2')
999
new_branch.update_references(branch)
1000
self.assertEqual('reference',
1001
branch.get_reference_info('file-id')[1])
1003
def test_update_references_retains_known_references(self):
1004
branch = self.make_branch_with_reference('branch', 'reference')
1005
new_branch = self.make_branch_with_reference(
1006
'new_branch', 'reference2')
1007
new_branch.update_references(branch)
1008
self.assertEqual('reference',
1009
branch.get_reference_info('file-id')[1])
1011
def test_update_references_skips_known_references(self):
1012
branch = self.make_branch_with_reference('branch', 'reference')
1013
new_branch = branch.bzrdir.sprout('branch/new-branch').open_branch()
1014
new_branch.set_reference_info('file-id', '../foo', '../foo')
1015
new_branch.update_references(branch)
1016
self.assertEqual('reference',
1017
branch.get_reference_info('file-id')[1])
1019
def test_pull_updates_references(self):
1020
branch = self.make_branch_with_reference('branch', 'reference')
1021
new_branch = branch.bzrdir.sprout('branch/new-branch').open_branch()
1022
new_branch.set_reference_info('file-id2', '../foo', '../foo')
1023
branch.pull(new_branch)
1024
self.assertEqual('foo',
1025
branch.get_reference_info('file-id2')[1])
1027
def test_push_updates_references(self):
1028
branch = self.make_branch_with_reference('branch', 'reference')
1029
new_branch = branch.bzrdir.sprout('branch/new-branch').open_branch()
1030
new_branch.set_reference_info('file-id2', '../foo', '../foo')
1031
new_branch.push(branch)
1032
self.assertEqual('foo',
1033
branch.get_reference_info('file-id2')[1])
1035
def test_merge_updates_references(self):
1036
branch = self.make_branch_with_reference('branch', 'reference')
1037
tree = self.make_branch_and_tree('tree')
1039
branch.pull(tree.branch)
1040
checkout = branch.create_checkout('checkout', lightweight=True)
1041
checkout.commit('bar')
1043
self.addCleanup(tree.unlock)
1044
merger = merge.Merger.from_revision_ids(None, tree,
1045
branch.last_revision(),
1046
other_branch=branch)
1047
merger.merge_type = merge.Merge3Merger
1049
self.assertEqual('../branch/reference',
1050
tree.branch.get_reference_info('file-id')[1])
1053
class TestBranchControlComponent(per_branch.TestCaseWithBranch):
1054
"""Branch implementations adequately implement ControlComponent."""
1056
def test_urls(self):
1057
br = self.make_branch('branch')
1058
self.assertIsInstance(br.user_url, str)
1059
self.assertEqual(br.user_url, br.user_transport.base)
1060
# for all current bzrdir implementations the user dir must be
1061
# above the control dir but we might need to relax that?
1062
self.assertEqual(br.control_url.find(br.user_url), 0)
1063
self.assertEqual(br.control_url, br.control_transport.base)
1066
class FakeShelfCreator(object):
1068
def __init__(self, branch):
1069
self.branch = branch
1071
def write_shelf(self, shelf_file, message=None):
1072
tree = self.branch.repository.revision_tree(revision.NULL_REVISION)
1073
with transform.TransformPreview(tree) as tt:
1074
shelf.ShelfCreator._write_shelf(
1075
shelf_file, tt, revision.NULL_REVISION)
1078
@contextlib.contextmanager
1079
def skip_if_storing_uncommitted_unsupported():
1082
except errors.StoringUncommittedNotSupported:
1083
raise tests.TestNotApplicable('Cannot store uncommitted changes.')
1086
class TestUncommittedChanges(per_branch.TestCaseWithBranch):
1088
def bind(self, branch, master):
1091
except errors.UpgradeRequired:
1092
raise tests.TestNotApplicable('Branch cannot be bound.')
1094
def test_store_uncommitted(self):
1095
tree = self.make_branch_and_tree('b')
1096
branch = tree.branch
1097
creator = FakeShelfCreator(branch)
1098
with skip_if_storing_uncommitted_unsupported():
1099
self.assertIs(None, branch.get_unshelver(tree))
1100
branch.store_uncommitted(creator)
1101
self.assertIsNot(None, branch.get_unshelver(tree))
1103
def test_store_uncommitted_bound(self):
1104
tree = self.make_branch_and_tree('b')
1105
branch = tree.branch
1106
master = self.make_branch('master')
1107
self.bind(branch, master)
1108
creator = FakeShelfCreator(tree.branch)
1109
self.assertIs(None, tree.branch.get_unshelver(tree))
1110
self.assertIs(None, master.get_unshelver(tree))
1111
tree.branch.store_uncommitted(creator)
1112
self.assertIsNot(None, master.get_unshelver(tree))
1114
def test_store_uncommitted_already_stored(self):
1115
branch = self.make_branch('b')
1116
with skip_if_storing_uncommitted_unsupported():
1117
branch.store_uncommitted(FakeShelfCreator(branch))
1118
self.assertRaises(errors.ChangesAlreadyStored,
1119
branch.store_uncommitted, FakeShelfCreator(branch))
1121
def test_store_uncommitted_none(self):
1122
branch = self.make_branch('b')
1123
with skip_if_storing_uncommitted_unsupported():
1124
branch.store_uncommitted(FakeShelfCreator(branch))
1125
branch.store_uncommitted(None)
1126
self.assertIs(None, branch.get_unshelver(None))
1128
def test_get_unshelver(self):
1129
tree = self.make_branch_and_tree('tree')
1131
self.build_tree_contents([('tree/file', 'contents1')])
1133
with skip_if_storing_uncommitted_unsupported():
1134
tree.store_uncommitted()
1135
unshelver = tree.branch.get_unshelver(tree)
1136
self.assertIsNot(None, unshelver)
1138
def test_get_unshelver_bound(self):
1139
tree = self.make_branch_and_tree('tree')
1141
self.build_tree_contents([('tree/file', 'contents1')])
1143
with skip_if_storing_uncommitted_unsupported():
1144
tree.store_uncommitted()
1145
branch = self.make_branch('branch')
1146
self.bind(branch, tree.branch)
1147
unshelver = branch.get_unshelver(tree)
1148
self.assertIsNot(None, unshelver)
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
from bzrlib.selftest import TestCaseInTempDir
20
class TestAppendRevisions(TestCaseInTempDir):
21
"""Test appending more than one revision"""
22
def test_append_revisions(self):
23
from bzrlib.branch import Branch
24
br = Branch(".", init=True)
25
br.append_revision("rev1")
26
self.assertEquals(br.revision_history(), ["rev1",])
27
br.append_revision("rev2", "rev3")
28
self.assertEquals(br.revision_history(), ["rev1", "rev2", "rev3"])
31
# TODO: rewrite this as a regular unittest, without relying on the displayed output
32
# >>> from bzrlib.commit import commit
33
# >>> bzrlib.trace.silent = True
34
# >>> br1 = ScratchBranch(files=['foo', 'bar'])
37
# >>> commit(br1, "lala!", rev_id="REVISION-ID-1", verbose=False)
38
# >>> br2 = ScratchBranch()
39
# >>> br2.update_revisions(br1)
41
# Added 1 inventories.
43
# >>> br2.revision_history()
45
# >>> br2.update_revisions(br1)
47
# >>> br1.text_store.total_size() == br2.text_store.total_size()