1
# Copyright (C) 2005-2010 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,
35
from bzrlib.symbol_versioning import deprecated_in
36
from bzrlib.tests import (
40
from bzrlib.tests.http_server import HttpServer
41
from bzrlib.transport import memory
44
class TestTestCaseWithBranch(per_branch.TestCaseWithBranch):
46
def test_branch_format_matches_bzrdir_branch_format(self):
47
bzrdir_branch_format = self.bzrdir_format.get_branch_format()
48
self.assertIs(self.branch_format.__class__,
49
bzrdir_branch_format.__class__)
51
def test_make_branch_gets_expected_format(self):
52
branch = self.make_branch('.')
53
self.assertIs(self.branch_format.__class__,
54
branch._format.__class__)
57
class TestBranch(per_branch.TestCaseWithBranch):
59
def test_create_tree_with_merge(self):
60
tree = self.create_tree_with_merge()
62
self.addCleanup(tree.unlock)
63
graph = tree.branch.repository.get_graph()
64
ancestry_graph = graph.get_parent_map(
65
tree.branch.repository.all_revision_ids())
66
self.assertEqual({'rev-1':('null:',),
68
'rev-1.1.1':('rev-1', ),
69
'rev-3':('rev-2', 'rev-1.1.1', ),
72
def test_revision_ids_are_utf8(self):
73
wt = self.make_branch_and_tree('tree')
74
wt.commit('f', rev_id='rev1')
75
wt.commit('f', rev_id='rev2')
76
wt.commit('f', rev_id='rev3')
78
br = self.get_branch()
80
br.set_revision_history(['rev1', 'rev2', 'rev3'])
81
rh = br.revision_history()
82
self.assertEqual(['rev1', 'rev2', 'rev3'], rh)
83
for revision_id in rh:
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 = tree_a.branch.get_revision_delta(1)
120
self.assertIsInstance(delta, _mod_delta.TreeDelta)
121
self.assertEqual([('foo', 'file1', 'file')], delta.added)
122
delta = tree_a.branch.get_revision_delta(2)
123
self.assertIsInstance(delta, _mod_delta.TreeDelta)
124
self.assertEqual([('vla', 'file2', 'file')], delta.added)
126
def get_unbalanced_tree_pair(self):
127
"""Return two branches, a and b, with one file in a."""
128
tree_a = self.make_branch_and_tree('a')
129
self.build_tree_contents([('a/b', 'b')])
131
tree_a.commit("silly commit", rev_id='A')
133
tree_b = self.make_branch_and_tree('b')
134
return tree_a, tree_b
136
def get_balanced_branch_pair(self):
137
"""Returns br_a, br_b as with one commit in a, and b has a's stores."""
138
tree_a, tree_b = self.get_unbalanced_tree_pair()
139
tree_b.branch.repository.fetch(tree_a.branch.repository)
140
return tree_a, tree_b
142
def test_clone_partial(self):
143
"""Copy only part of the history of a branch."""
144
# TODO: RBC 20060208 test with a revision not on revision-history.
145
# what should that behaviour be ? Emailed the list.
146
# First, make a branch with two commits.
147
wt_a = self.make_branch_and_tree('a')
148
self.build_tree(['a/one'])
150
wt_a.commit('commit one', rev_id='1')
151
self.build_tree(['a/two'])
153
wt_a.commit('commit two', rev_id='2')
154
# Now make a copy of the repository.
155
repo_b = self.make_repository('b')
156
wt_a.branch.repository.copy_content_into(repo_b)
157
# wt_a might be a lightweight checkout, so get a hold of the actual
158
# branch (because you can't do a partial clone of a lightweight
160
branch = wt_a.branch.bzrdir.open_branch()
161
# Then make a branch where the new repository is, but specify a revision
162
# ID. The new branch's history will stop at the specified revision.
163
br_b = branch.clone(repo_b.bzrdir, revision_id='1')
164
self.assertEqual('1', br_b.last_revision())
166
def get_parented_branch(self):
167
wt_a = self.make_branch_and_tree('a')
168
self.build_tree(['a/one'])
170
wt_a.commit('commit one', rev_id='1')
172
branch_b = wt_a.branch.bzrdir.sprout('b', revision_id='1').open_branch()
173
self.assertEqual(wt_a.branch.base, branch_b.get_parent())
176
def test_clone_branch_nickname(self):
177
# test the nick name is preserved always
178
raise tests.TestSkipped('XXX branch cloning is not yet tested.')
180
def test_clone_branch_parent(self):
181
# test the parent is preserved always
182
branch_b = self.get_parented_branch()
183
repo_c = self.make_repository('c')
184
branch_b.repository.copy_content_into(repo_c)
185
branch_c = branch_b.clone(repo_c.bzrdir)
186
self.assertNotEqual(None, branch_c.get_parent())
187
self.assertEqual(branch_b.get_parent(), branch_c.get_parent())
189
# We can also set a specific parent, and it should be honored
190
random_parent = 'http://bazaar-vcs.org/path/to/branch'
191
branch_b.set_parent(random_parent)
192
repo_d = self.make_repository('d')
193
branch_b.repository.copy_content_into(repo_d)
194
branch_d = branch_b.clone(repo_d.bzrdir)
195
self.assertEqual(random_parent, branch_d.get_parent())
197
def test_submit_branch(self):
198
"""Submit location can be queried and set"""
199
branch = self.make_branch('branch')
200
self.assertEqual(branch.get_submit_branch(), None)
201
branch.set_submit_branch('sftp://example.com')
202
self.assertEqual(branch.get_submit_branch(), 'sftp://example.com')
203
branch.set_submit_branch('sftp://example.net')
204
self.assertEqual(branch.get_submit_branch(), 'sftp://example.net')
206
def test_public_branch(self):
207
"""public location can be queried and set"""
208
branch = self.make_branch('branch')
209
self.assertEqual(branch.get_public_branch(), None)
210
branch.set_public_branch('sftp://example.com')
211
self.assertEqual(branch.get_public_branch(), 'sftp://example.com')
212
branch.set_public_branch('sftp://example.net')
213
self.assertEqual(branch.get_public_branch(), 'sftp://example.net')
214
branch.set_public_branch(None)
215
self.assertEqual(branch.get_public_branch(), None)
217
def test_record_initial_ghost(self):
218
"""Branches should support having ghosts."""
219
wt = self.make_branch_and_tree('.')
220
wt.set_parent_ids(['non:existent@rev--ision--0--2'],
221
allow_leftmost_as_ghost=True)
222
self.assertEqual(['non:existent@rev--ision--0--2'],
224
rev_id = wt.commit('commit against a ghost first parent.')
225
rev = wt.branch.repository.get_revision(rev_id)
226
self.assertEqual(rev.parent_ids, ['non:existent@rev--ision--0--2'])
227
# parent_sha1s is not populated now, WTF. rbc 20051003
228
self.assertEqual(len(rev.parent_sha1s), 0)
230
def test_record_two_ghosts(self):
231
"""Recording with all ghosts works."""
232
wt = self.make_branch_and_tree('.')
234
'foo@azkhazan-123123-abcabc',
235
'wibble@fofof--20050401--1928390812',
237
allow_leftmost_as_ghost=True)
238
rev_id = wt.commit("commit from ghost base with one merge")
239
# the revision should have been committed with two parents
240
rev = wt.branch.repository.get_revision(rev_id)
241
self.assertEqual(['foo@azkhazan-123123-abcabc',
242
'wibble@fofof--20050401--1928390812'],
245
def test_bad_revision(self):
246
self.assertRaises(errors.InvalidRevisionId,
247
self.get_branch().repository.get_revision,
251
# compare the gpg-to-sign info for a commit with a ghost and
252
# an identical tree without a ghost
253
# fetch missing should rewrite the TOC of weaves to list newly available parents.
255
def test_sign_existing_revision(self):
256
wt = self.make_branch_and_tree('.')
258
wt.commit("base", allow_pointless=True, rev_id='A')
259
from bzrlib.testament import Testament
260
strategy = gpg.LoopbackGPGStrategy(None)
261
branch.repository.lock_write()
262
branch.repository.start_write_group()
263
branch.repository.sign_revision('A', strategy)
264
branch.repository.commit_write_group()
265
branch.repository.unlock()
266
self.assertEqual('-----BEGIN PSEUDO-SIGNED CONTENT-----\n' +
267
Testament.from_revision(branch.repository,
268
'A').as_short_text() +
269
'-----END PSEUDO-SIGNED CONTENT-----\n',
270
branch.repository.get_signature_text('A'))
272
def test_store_signature(self):
273
wt = self.make_branch_and_tree('.')
277
branch.repository.start_write_group()
279
branch.repository.store_revision_signature(
280
gpg.LoopbackGPGStrategy(None), 'FOO', 'A')
282
branch.repository.abort_write_group()
285
branch.repository.commit_write_group()
288
# A signature without a revision should not be accessible.
289
self.assertRaises(errors.NoSuchRevision,
290
branch.repository.has_signature_for_revision_id,
292
wt.commit("base", allow_pointless=True, rev_id='A')
293
self.assertEqual('-----BEGIN PSEUDO-SIGNED CONTENT-----\n'
294
'FOO-----END PSEUDO-SIGNED CONTENT-----\n',
295
branch.repository.get_signature_text('A'))
297
def test_branch_keeps_signatures(self):
298
wt = self.make_branch_and_tree('source')
299
wt.commit('A', allow_pointless=True, rev_id='A')
300
repo = wt.branch.repository
302
repo.start_write_group()
303
repo.sign_revision('A', gpg.LoopbackGPGStrategy(None))
304
repo.commit_write_group()
306
#FIXME: clone should work to urls,
307
# wt.clone should work to disks.
308
self.build_tree(['target/'])
309
d2 = repo.bzrdir.clone(urlutils.local_path_to_url('target'))
310
self.assertEqual(repo.get_signature_text('A'),
311
d2.open_repository().get_signature_text('A'))
313
def test_missing_revisions(self):
314
t1 = self.make_branch_and_tree('b1')
315
rev1 = t1.commit('one')
316
t2 = t1.bzrdir.sprout('b2').open_workingtree()
317
rev2 = t1.commit('two')
318
rev3 = t1.commit('three')
320
self.assertEqual([rev2, rev3],
321
self.applyDeprecated(deprecated_in((1, 6, 0)),
322
t2.branch.missing_revisions, t1.branch))
325
self.applyDeprecated(deprecated_in((1, 6, 0)),
326
t2.branch.missing_revisions, t1.branch, stop_revision=1))
327
self.assertEqual([rev2],
328
self.applyDeprecated(deprecated_in((1, 6, 0)),
329
t2.branch.missing_revisions, t1.branch, stop_revision=2))
330
self.assertEqual([rev2, rev3],
331
self.applyDeprecated(deprecated_in((1, 6, 0)),
332
t2.branch.missing_revisions, t1.branch, stop_revision=3))
334
self.assertRaises(errors.NoSuchRevision,
335
self.applyDeprecated, deprecated_in((1, 6, 0)),
336
t2.branch.missing_revisions, t1.branch, stop_revision=4)
338
rev4 = t2.commit('four')
339
self.assertRaises(errors.DivergedBranches,
340
self.applyDeprecated, deprecated_in((1, 6, 0)),
341
t2.branch.missing_revisions, t1.branch)
343
def test_nicks(self):
344
"""Test explicit and implicit branch nicknames.
346
Nicknames are implicitly the name of the branch's directory, unless an
347
explicit nickname is set. That is, an explicit nickname always
348
overrides the implicit one.
350
t = transport.get_transport(self.get_url())
351
branch = self.make_branch('bzr.dev')
352
# The nick will be 'bzr.dev', because there is no explicit nick set.
353
self.assertEqual(branch.nick, 'bzr.dev')
354
# Move the branch to a different directory, 'bzr.ab'. Now that branch
355
# will report its nick as 'bzr.ab'.
356
t.move('bzr.dev', 'bzr.ab')
357
branch = _mod_branch.Branch.open(self.get_url('bzr.ab'))
358
self.assertEqual(branch.nick, 'bzr.ab')
359
# Set the branch nick explicitly. This will ensure there's a branch
360
# config file in the branch.
361
branch.nick = "Aaron's branch"
362
if not isinstance(branch, remote.RemoteBranch):
363
self.failUnless(branch._transport.has("branch.conf"))
364
# Because the nick has been set explicitly, the nick is now always
365
# "Aaron's branch", regardless of directory name.
366
self.assertEqual(branch.nick, "Aaron's branch")
367
t.move('bzr.ab', 'integration')
368
branch = _mod_branch.Branch.open(self.get_url('integration'))
369
self.assertEqual(branch.nick, "Aaron's branch")
370
branch.nick = u"\u1234"
371
self.assertEqual(branch.nick, u"\u1234")
373
def test_commit_nicks(self):
374
"""Nicknames are committed to the revision"""
375
wt = self.make_branch_and_tree('bzr.dev')
377
branch.nick = "My happy branch"
378
wt.commit('My commit respect da nick.')
379
committed = branch.repository.get_revision(branch.last_revision())
380
self.assertEqual(committed.properties["branch-nick"],
383
def test_create_colocated(self):
385
repo = self.make_repository('.', shared=True)
386
except errors.IncompatibleFormat:
388
self.assertEquals(0, len(repo.bzrdir.list_branches()))
390
child_branch1 = self.branch_format.initialize(repo.bzrdir,
392
except (errors.UninitializableFormat, errors.NoColocatedBranchSupport):
393
# branch references are not default init'able and
394
# not all bzrdirs support colocated branches.
396
self.assertEquals(1, len(repo.bzrdir.list_branches()))
397
self.branch_format.initialize(repo.bzrdir, name='branch2')
398
self.assertEquals(2, len(repo.bzrdir.list_branches()))
400
def test_create_open_branch_uses_repository(self):
402
repo = self.make_repository('.', shared=True)
403
except errors.IncompatibleFormat:
405
child_transport = repo.bzrdir.root_transport.clone('child')
406
child_transport.mkdir('.')
407
child_dir = self.bzrdir_format.initialize_on_transport(child_transport)
409
child_branch = self.branch_format.initialize(child_dir)
410
except errors.UninitializableFormat:
411
# branch references are not default init'able.
413
self.assertEqual(repo.bzrdir.root_transport.base,
414
child_branch.repository.bzrdir.root_transport.base)
415
child_branch = _mod_branch.Branch.open(self.get_url('child'))
416
self.assertEqual(repo.bzrdir.root_transport.base,
417
child_branch.repository.bzrdir.root_transport.base)
419
def test_format_description(self):
420
tree = self.make_branch_and_tree('tree')
421
text = tree.branch._format.get_format_description()
422
self.failUnless(len(text))
424
def test_get_commit_builder(self):
425
branch = self.make_branch(".")
427
builder = branch.get_commit_builder([])
428
self.assertIsInstance(builder, repository.CommitBuilder)
429
branch.repository.commit_write_group()
432
def test_generate_revision_history(self):
433
"""Create a fake revision history easily."""
434
tree = self.make_branch_and_tree('.')
435
rev1 = tree.commit('foo')
436
orig_history = tree.branch.revision_history()
437
rev2 = tree.commit('bar', allow_pointless=True)
438
tree.branch.generate_revision_history(rev1)
439
self.assertEqual(orig_history, tree.branch.revision_history())
441
def test_generate_revision_history_NULL_REVISION(self):
442
tree = self.make_branch_and_tree('.')
443
rev1 = tree.commit('foo')
444
tree.branch.generate_revision_history(revision.NULL_REVISION)
445
self.assertEqual([], tree.branch.revision_history())
447
def test_create_checkout(self):
448
tree_a = self.make_branch_and_tree('a')
449
branch_a = tree_a.branch
450
checkout_b = branch_a.create_checkout('b')
451
self.assertEqual('null:', checkout_b.last_revision())
452
checkout_b.commit('rev1', rev_id='rev1')
453
self.assertEqual('rev1', branch_a.last_revision())
454
self.assertNotEqual(checkout_b.branch.base, branch_a.base)
456
checkout_c = branch_a.create_checkout('c', lightweight=True)
457
self.assertEqual('rev1', checkout_c.last_revision())
458
checkout_c.commit('rev2', rev_id='rev2')
459
self.assertEqual('rev2', branch_a.last_revision())
460
self.assertEqual(checkout_c.branch.base, branch_a.base)
462
checkout_d = branch_a.create_checkout('d', lightweight=True)
463
self.assertEqual('rev2', checkout_d.last_revision())
464
checkout_e = branch_a.create_checkout('e')
465
self.assertEqual('rev2', checkout_e.last_revision())
467
def test_create_anonymous_lightweight_checkout(self):
468
"""A lightweight checkout from a readonly branch should succeed."""
469
tree_a = self.make_branch_and_tree('a')
470
rev_id = tree_a.commit('put some content in the branch')
471
# open the branch via a readonly transport
472
source_branch = _mod_branch.Branch.open(self.get_readonly_url('a'))
473
# sanity check that the test will be valid
474
self.assertRaises((errors.LockError, errors.TransportNotPossible),
475
source_branch.lock_write)
476
checkout = source_branch.create_checkout('c', lightweight=True)
477
self.assertEqual(rev_id, checkout.last_revision())
479
def test_create_anonymous_heavyweight_checkout(self):
480
"""A regular checkout from a readonly branch should succeed."""
481
tree_a = self.make_branch_and_tree('a')
482
rev_id = tree_a.commit('put some content in the branch')
483
# open the branch via a readonly transport
484
source_branch = _mod_branch.Branch.open(self.get_readonly_url('a'))
485
# sanity check that the test will be valid
486
self.assertRaises((errors.LockError, errors.TransportNotPossible),
487
source_branch.lock_write)
488
checkout = source_branch.create_checkout('c')
489
self.assertEqual(rev_id, checkout.last_revision())
491
def test_set_revision_history(self):
492
tree = self.make_branch_and_tree('a')
493
tree.commit('a commit', rev_id='rev1')
495
br.set_revision_history(["rev1"])
496
self.assertEquals(br.revision_history(), ["rev1"])
497
br.set_revision_history([])
498
self.assertEquals(br.revision_history(), [])
501
class TestBranchFormat(per_branch.TestCaseWithBranch):
503
def test_branch_format_network_name(self):
504
br = self.make_branch('.')
506
network_name = format.network_name()
507
self.assertIsInstance(network_name, str)
508
# We want to test that the network_name matches the actual format on
509
# disk. For local branches that means that using network_name as a key
510
# in the registry gives back the same format. For remote branches we
511
# check that the network_name of the RemoteBranchFormat we have locally
512
# matches the actual format present on disk.
513
if isinstance(format, remote.RemoteBranchFormat):
515
real_branch = br._real_branch
516
self.assertEqual(real_branch._format.network_name(), network_name)
518
registry = _mod_branch.network_format_registry
519
looked_up_format = registry.get(network_name)
520
self.assertEqual(format.__class__, looked_up_format.__class__)
523
class ChrootedTests(per_branch.TestCaseWithBranch):
524
"""A support class that provides readonly urls outside the local namespace.
526
This is done by checking if self.transport_server is a MemoryServer. if it
527
is then we are chrooted already, if it is not then an HttpServer is used
532
super(ChrootedTests, self).setUp()
533
if not self.vfs_transport_factory == memory.MemoryServer:
534
self.transport_readonly_server = HttpServer
536
def test_open_containing(self):
537
self.assertRaises(errors.NotBranchError,
538
_mod_branch.Branch.open_containing,
539
self.get_readonly_url(''))
540
self.assertRaises(errors.NotBranchError,
541
_mod_branch.Branch.open_containing,
542
self.get_readonly_url('g/p/q'))
543
branch = self.make_branch('.')
544
branch, relpath = _mod_branch.Branch.open_containing(
545
self.get_readonly_url(''))
546
self.assertEqual('', relpath)
547
branch, relpath = _mod_branch.Branch.open_containing(
548
self.get_readonly_url('g/p/q'))
549
self.assertEqual('g/p/q', relpath)
552
class InstrumentedTransaction(object):
555
self.calls.append('finish')
561
class TestDecorator(object):
567
self._calls.append('lr')
569
def lock_write(self):
570
self._calls.append('lw')
573
self._calls.append('ul')
575
@_mod_branch.needs_read_lock
576
def do_with_read(self):
579
@_mod_branch.needs_read_lock
580
def except_with_read(self):
583
@_mod_branch.needs_write_lock
584
def do_with_write(self):
587
@_mod_branch.needs_write_lock
588
def except_with_write(self):
592
class TestDecorators(tests.TestCase):
594
def test_needs_read_lock(self):
595
branch = TestDecorator()
596
self.assertEqual(1, branch.do_with_read())
597
self.assertEqual(['lr', 'ul'], branch._calls)
599
def test_excepts_in_read_lock(self):
600
branch = TestDecorator()
601
self.assertRaises(RuntimeError, branch.except_with_read)
602
self.assertEqual(['lr', 'ul'], branch._calls)
604
def test_needs_write_lock(self):
605
branch = TestDecorator()
606
self.assertEqual(2, branch.do_with_write())
607
self.assertEqual(['lw', 'ul'], branch._calls)
609
def test_excepts_in_write_lock(self):
610
branch = TestDecorator()
611
self.assertRaises(RuntimeError, branch.except_with_write)
612
self.assertEqual(['lw', 'ul'], branch._calls)
615
class TestBranchPushLocations(per_branch.TestCaseWithBranch):
617
def test_get_push_location_unset(self):
618
self.assertEqual(None, self.get_branch().get_push_location())
620
def test_get_push_location_exact(self):
621
b = self.get_branch()
622
config.LocationConfig.from_string(
623
'[%s]\npush_location=foo\n' % (b.base,), b.base, save=True)
624
self.assertEqual("foo", self.get_branch().get_push_location())
626
def test_set_push_location(self):
627
branch = self.get_branch()
628
branch.set_push_location('foo')
629
self.assertEqual('foo', branch.get_push_location())
632
class TestChildSubmitFormats(per_branch.TestCaseWithBranch):
634
def test_get_child_submit_format_default(self):
635
self.assertEqual(None, self.get_branch().get_child_submit_format())
637
def test_get_child_submit_format(self):
638
branch = self.get_branch()
639
branch.get_config().set_user_option('child_submit_format', '10')
640
branch = self.get_branch()
641
self.assertEqual('10', branch.get_child_submit_format())
644
class TestFormat(per_branch.TestCaseWithBranch):
645
"""Tests for the format itself."""
647
def test_get_reference(self):
648
"""get_reference on all regular branches should return None."""
649
if not self.branch_format.is_supported():
650
# unsupported formats are not loopback testable
651
# because the default open will not open them and
652
# they may not be initializable.
654
made_branch = self.make_branch('.')
655
self.assertEqual(None,
656
made_branch._format.get_reference(made_branch.bzrdir))
658
def test_set_reference(self):
659
"""set_reference on all regular branches should be callable."""
660
if not self.branch_format.is_supported():
661
# unsupported formats are not loopback testable
662
# because the default open will not open them and
663
# they may not be initializable.
665
this_branch = self.make_branch('this')
666
other_branch = self.make_branch('other')
668
this_branch._format.set_reference(this_branch.bzrdir, None,
670
except NotImplementedError:
674
ref = this_branch._format.get_reference(this_branch.bzrdir)
675
self.assertEqual(ref, other_branch.base)
677
def test_format_initialize_find_open(self):
678
# loopback test to check the current format initializes to itself.
679
if not self.branch_format.is_supported():
680
# unsupported formats are not loopback testable
681
# because the default open will not open them and
682
# they may not be initializable.
684
# supported formats must be able to init and open
685
t = transport.get_transport(self.get_url())
686
readonly_t = transport.get_transport(self.get_readonly_url())
687
made_branch = self.make_branch('.')
688
self.failUnless(isinstance(made_branch, _mod_branch.Branch))
690
# find it via bzrdir opening:
691
opened_control = bzrdir.BzrDir.open(readonly_t.base)
692
direct_opened_branch = opened_control.open_branch()
693
self.assertEqual(direct_opened_branch.__class__, made_branch.__class__)
694
self.assertEqual(opened_control, direct_opened_branch.bzrdir)
695
self.failUnless(isinstance(direct_opened_branch._format,
696
self.branch_format.__class__))
698
# find it via Branch.open
699
opened_branch = _mod_branch.Branch.open(readonly_t.base)
700
self.failUnless(isinstance(opened_branch, made_branch.__class__))
701
self.assertEqual(made_branch._format.__class__,
702
opened_branch._format.__class__)
703
# if it has a unique id string, can we probe for it ?
705
self.branch_format.get_format_string()
706
except NotImplementedError:
708
self.assertEqual(self.branch_format,
709
opened_control.find_branch_format())
712
class TestBound(per_branch.TestCaseWithBranch):
714
def test_bind_unbind(self):
715
branch = self.make_branch('1')
716
branch2 = self.make_branch('2')
719
except errors.UpgradeRequired:
720
raise tests.TestNotApplicable('Format does not support binding')
721
self.assertTrue(branch.unbind())
722
self.assertFalse(branch.unbind())
723
self.assertIs(None, branch.get_bound_location())
725
def test_old_bound_location(self):
726
branch = self.make_branch('branch1')
728
self.assertIs(None, branch.get_old_bound_location())
729
except errors.UpgradeRequired:
730
raise tests.TestNotApplicable(
731
'Format does not store old bound locations')
732
branch2 = self.make_branch('branch2')
734
self.assertIs(None, branch.get_old_bound_location())
736
self.assertContainsRe(branch.get_old_bound_location(), '\/branch2\/$')
738
def test_bind_diverged(self):
739
tree_a = self.make_branch_and_tree('tree_a')
740
tree_a.commit('rev1a')
741
tree_b = tree_a.bzrdir.sprout('tree_b').open_workingtree()
742
tree_a.commit('rev2a')
743
tree_b.commit('rev2b')
745
tree_b.branch.bind(tree_a.branch)
746
except errors.UpgradeRequired:
747
raise tests.TestNotApplicable('Format does not support binding')
750
class TestStrict(per_branch.TestCaseWithBranch):
752
def test_strict_history(self):
753
tree1 = self.make_branch_and_tree('tree1')
755
tree1.branch.set_append_revisions_only(True)
756
except errors.UpgradeRequired:
757
raise tests.TestSkipped('Format does not support strict history')
758
tree1.commit('empty commit')
759
tree2 = tree1.bzrdir.sprout('tree2').open_workingtree()
760
tree2.commit('empty commit 2')
761
tree1.pull(tree2.branch)
762
tree1.commit('empty commit 3')
763
tree2.commit('empty commit 4')
764
self.assertRaises(errors.DivergedBranches, tree1.pull, tree2.branch)
765
tree2.merge_from_branch(tree1.branch)
766
tree2.commit('empty commit 5')
767
self.assertRaises(errors.AppendRevisionsOnlyViolation, tree1.pull,
769
tree3 = tree1.bzrdir.sprout('tree3').open_workingtree()
770
tree3.merge_from_branch(tree2.branch)
771
tree3.commit('empty commit 6')
772
tree2.pull(tree3.branch)
775
class TestIgnoreFallbacksParameter(per_branch.TestCaseWithBranch):
777
def make_branch_with_fallback(self):
778
fallback = self.make_branch('fallback')
779
if not fallback._format.supports_stacking():
780
raise tests.TestNotApplicable("format does not support stacking")
781
stacked = self.make_branch('stacked')
782
stacked.set_stacked_on_url(fallback.base)
785
def test_fallbacks_not_opened(self):
786
stacked = self.make_branch_with_fallback()
787
self.get_transport('').rename('fallback', 'moved')
788
reopened = stacked.bzrdir.open_branch(ignore_fallbacks=True)
789
self.assertEqual([], reopened.repository._fallback_repositories)
791
def test_fallbacks_are_opened(self):
792
stacked = self.make_branch_with_fallback()
793
reopened = stacked.bzrdir.open_branch(ignore_fallbacks=False)
794
self.assertLength(1, reopened.repository._fallback_repositories)
797
class TestReferenceLocation(per_branch.TestCaseWithBranch):
799
def test_reference_parent(self):
800
tree = self.make_branch_and_tree('tree')
801
subtree = self.make_branch_and_tree('tree/subtree')
802
subtree.set_root_id('subtree-id')
804
tree.add_reference(subtree)
805
except errors.UnsupportedOperation:
806
raise tests.TestNotApplicable('Tree cannot hold references.')
807
reference_parent = tree.branch.reference_parent('subtree-id',
809
self.assertEqual(subtree.branch.base, reference_parent.base)
811
def test_reference_parent_accepts_possible_transports(self):
812
tree = self.make_branch_and_tree('tree')
813
subtree = self.make_branch_and_tree('tree/subtree')
814
subtree.set_root_id('subtree-id')
816
tree.add_reference(subtree)
817
except errors.UnsupportedOperation:
818
raise tests.TestNotApplicable('Tree cannot hold references.')
819
reference_parent = tree.branch.reference_parent('subtree-id',
820
'subtree', possible_transports=[subtree.bzrdir.root_transport])
822
def test_get_reference_info(self):
823
branch = self.make_branch('branch')
825
path, loc = branch.get_reference_info('file-id')
826
except errors.UnsupportedOperation:
827
raise tests.TestNotApplicable('Branch cannot hold references.')
828
self.assertIs(None, path)
829
self.assertIs(None, loc)
831
def test_set_reference_info(self):
832
branch = self.make_branch('branch')
834
branch.set_reference_info('file-id', 'path/to/location',
836
except errors.UnsupportedOperation:
837
raise tests.TestNotApplicable('Branch cannot hold references.')
839
def test_set_get_reference_info(self):
840
branch = self.make_branch('branch')
842
branch.set_reference_info('file-id', 'path/to/file',
844
except errors.UnsupportedOperation:
845
raise tests.TestNotApplicable('Branch cannot hold references.')
846
# Create a new instance to ensure storage is permanent
847
branch = _mod_branch.Branch.open('branch')
848
tree_path, branch_location = branch.get_reference_info('file-id')
849
self.assertEqual('path/to/location', branch_location)
851
def test_set_null_reference_info(self):
852
branch = self.make_branch('branch')
854
branch.set_reference_info('file-id', 'path/to/file',
856
except errors.UnsupportedOperation:
857
raise tests.TestNotApplicable('Branch cannot hold references.')
858
branch.set_reference_info('file-id', None, None)
859
tree_path, branch_location = branch.get_reference_info('file-id')
860
self.assertIs(None, tree_path)
861
self.assertIs(None, branch_location)
863
def test_set_null_reference_info_when_null(self):
864
branch = self.make_branch('branch')
866
tree_path, branch_location = branch.get_reference_info('file-id')
867
except errors.UnsupportedOperation:
868
raise tests.TestNotApplicable('Branch cannot hold references.')
869
self.assertIs(None, tree_path)
870
self.assertIs(None, branch_location)
871
branch.set_reference_info('file-id', None, None)
873
def test_set_null_requires_two_nones(self):
874
branch = self.make_branch('branch')
876
e = self.assertRaises(ValueError, branch.set_reference_info,
877
'file-id', 'path', None)
878
except errors.UnsupportedOperation:
879
raise tests.TestNotApplicable('Branch cannot hold references.')
880
self.assertEqual('tree_path must be None when branch_location is'
882
e = self.assertRaises(ValueError, branch.set_reference_info,
883
'file-id', None, 'location')
884
self.assertEqual('branch_location must be None when tree_path is'
887
def make_branch_with_reference(self, location, reference_location,
889
branch = self.make_branch(location)
891
branch.set_reference_info(file_id, 'path/to/file',
893
except errors.UnsupportedOperation:
894
raise tests.TestNotApplicable('Branch cannot hold references.')
897
def test_reference_parent_from_reference_info_(self):
898
referenced_branch = self.make_branch('reference_branch')
899
branch = self.make_branch_with_reference('branch',
900
referenced_branch.base)
901
parent = branch.reference_parent('file-id', 'path/to/file')
902
self.assertEqual(parent.base, referenced_branch.base)
904
def test_branch_relative_reference_location(self):
905
branch = self.make_branch('branch')
907
branch.set_reference_info('file-id', 'path/to/file',
908
'../reference_branch')
909
except errors.UnsupportedOperation:
910
raise tests.TestNotApplicable('Branch cannot hold references.')
911
referenced_branch = self.make_branch('reference_branch')
912
parent = branch.reference_parent('file-id', 'path/to/file')
913
self.assertEqual(parent.base, referenced_branch.base)
915
def test_sprout_copies_reference_location(self):
916
branch = self.make_branch_with_reference('branch', '../reference')
917
new_branch = branch.bzrdir.sprout('new-branch').open_branch()
918
self.assertEqual('../reference',
919
new_branch.get_reference_info('file-id')[1])
921
def test_clone_copies_reference_location(self):
922
branch = self.make_branch_with_reference('branch', '../reference')
923
new_branch = branch.bzrdir.clone('new-branch').open_branch()
924
self.assertEqual('../reference',
925
new_branch.get_reference_info('file-id')[1])
927
def test_copied_locations_are_rebased(self):
928
branch = self.make_branch_with_reference('branch', 'reference')
929
new_branch = branch.bzrdir.sprout('branch/new-branch').open_branch()
930
self.assertEqual('../reference',
931
new_branch.get_reference_info('file-id')[1])
933
def test_update_references_retains_old_references(self):
934
branch = self.make_branch_with_reference('branch', 'reference')
935
new_branch = self.make_branch_with_reference(
936
'new_branch', 'reference', 'file-id2')
937
new_branch.update_references(branch)
938
self.assertEqual('reference',
939
branch.get_reference_info('file-id')[1])
941
def test_update_references_retains_known_references(self):
942
branch = self.make_branch_with_reference('branch', 'reference')
943
new_branch = self.make_branch_with_reference(
944
'new_branch', 'reference2')
945
new_branch.update_references(branch)
946
self.assertEqual('reference',
947
branch.get_reference_info('file-id')[1])
949
def test_update_references_skips_known_references(self):
950
branch = self.make_branch_with_reference('branch', 'reference')
951
new_branch = branch.bzrdir.sprout('branch/new-branch').open_branch()
952
new_branch.set_reference_info('file-id', '../foo', '../foo')
953
new_branch.update_references(branch)
954
self.assertEqual('reference',
955
branch.get_reference_info('file-id')[1])
957
def test_pull_updates_references(self):
958
branch = self.make_branch_with_reference('branch', 'reference')
959
new_branch = branch.bzrdir.sprout('branch/new-branch').open_branch()
960
new_branch.set_reference_info('file-id2', '../foo', '../foo')
961
branch.pull(new_branch)
962
self.assertEqual('foo',
963
branch.get_reference_info('file-id2')[1])
965
def test_push_updates_references(self):
966
branch = self.make_branch_with_reference('branch', 'reference')
967
new_branch = branch.bzrdir.sprout('branch/new-branch').open_branch()
968
new_branch.set_reference_info('file-id2', '../foo', '../foo')
969
new_branch.push(branch)
970
self.assertEqual('foo',
971
branch.get_reference_info('file-id2')[1])
973
def test_merge_updates_references(self):
974
branch = self.make_branch_with_reference('branch', 'reference')
975
tree = self.make_branch_and_tree('tree')
977
branch.pull(tree.branch)
978
checkout = branch.create_checkout('checkout', lightweight=True)
979
checkout.commit('bar')
981
self.addCleanup(tree.unlock)
982
merger = merge.Merger.from_revision_ids(None, tree,
983
branch.last_revision(),
985
merger.merge_type = merge.Merge3Merger
987
self.assertEqual('../branch/reference',
988
tree.branch.get_reference_info('file-id')[1])
991
class TestBranchControlComponent(per_branch.TestCaseWithBranch):
992
"""Branch implementations adequately implement ControlComponent."""
995
br = self.make_branch('branch')
996
self.assertIsInstance(br.user_url, str)
997
self.assertEqual(br.user_url, br.user_transport.base)
998
# for all current bzrdir implementations the user dir must be
999
# above the control dir but we might need to relax that?
1000
self.assertEqual(br.control_url.find(br.user_url), 0)
1001
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"])