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,
35
from bzrlib.tests import (
38
from bzrlib.tests.http_server import HttpServer
39
from bzrlib.transport import memory
42
class TestTestCaseWithBranch(per_branch.TestCaseWithBranch):
44
def test_branch_format_matches_bzrdir_branch_format(self):
45
bzrdir_branch_format = self.bzrdir_format.get_branch_format()
46
self.assertIs(self.branch_format.__class__,
47
bzrdir_branch_format.__class__)
49
def test_make_branch_gets_expected_format(self):
50
branch = self.make_branch('.')
51
self.assertIs(self.branch_format.__class__,
52
branch._format.__class__)
55
class TestBranch(per_branch.TestCaseWithBranch):
57
def test_create_tree_with_merge(self):
58
tree = self.create_tree_with_merge()
60
self.addCleanup(tree.unlock)
61
graph = tree.branch.repository.get_graph()
62
ancestry_graph = graph.get_parent_map(
63
tree.branch.repository.all_revision_ids())
64
self.assertEqual({'rev-1':('null:',),
66
'rev-1.1.1':('rev-1', ),
67
'rev-3':('rev-2', 'rev-1.1.1', ),
70
def test_revision_ids_are_utf8(self):
71
wt = self.make_branch_and_tree('tree')
72
wt.commit('f', rev_id='rev1')
73
wt.commit('f', rev_id='rev2')
74
wt.commit('f', rev_id='rev3')
76
br = self.get_branch()
78
br.generate_revision_history('rev3')
79
rh = br.revision_history()
80
self.assertEqual(['rev1', 'rev2', 'rev3'], rh)
81
for revision_id in rh:
82
self.assertIsInstance(revision_id, str)
83
last = br.last_revision()
84
self.assertEqual('rev3', last)
85
self.assertIsInstance(last, str)
86
revno, last = br.last_revision_info()
87
self.assertEqual(3, revno)
88
self.assertEqual('rev3', last)
89
self.assertIsInstance(last, str)
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
from bzrlib.selftest import TestCaseInTempDir
21
from bzrlib.branch import Branch
22
from bzrlib.trace import mutter
25
class TestAppendRevisions(TestCaseInTempDir):
26
"""Test appending more than one revision"""
27
def test_append_revisions(self):
28
from bzrlib.branch import Branch
29
br = Branch(".", init=True)
30
br.append_revision("rev1")
31
self.assertEquals(br.revision_history(), ["rev1",])
32
br.append_revision("rev2", "rev3")
33
self.assertEquals(br.revision_history(), ["rev1", "rev2", "rev3"])
37
class TestFetch(TestCaseInTempDir):
91
38
def test_fetch_revisions(self):
92
39
"""Test fetch-revision operation."""
93
wt = self.make_branch_and_tree('b1')
95
self.build_tree_contents([('b1/foo', 'hello')])
96
wt.add(['foo'], ['foo-id'])
97
wt.commit('lala!', rev_id='revision-1', allow_pointless=False)
99
b2 = self.make_branch('b2')
102
rev = b2.repository.get_revision('revision-1')
103
tree = b2.repository.revision_tree('revision-1')
105
self.addCleanup(tree.unlock)
106
self.assertEqual(tree.get_file_text('foo-id'), 'hello')
108
def test_get_revision_delta(self):
109
tree_a = self.make_branch_and_tree('a')
110
self.build_tree(['a/foo'])
111
tree_a.add('foo', 'file1')
112
tree_a.commit('rev1', rev_id='rev1')
113
self.build_tree(['a/vla'])
114
tree_a.add('vla', 'file2')
115
tree_a.commit('rev2', rev_id='rev2')
117
delta = tree_a.branch.get_revision_delta(1)
118
self.assertIsInstance(delta, _mod_delta.TreeDelta)
119
self.assertEqual([('foo', 'file1', 'file')], delta.added)
120
delta = tree_a.branch.get_revision_delta(2)
121
self.assertIsInstance(delta, _mod_delta.TreeDelta)
122
self.assertEqual([('vla', 'file2', 'file')], delta.added)
124
def get_unbalanced_tree_pair(self):
125
"""Return two branches, a and b, with one file in a."""
126
tree_a = self.make_branch_and_tree('a')
127
self.build_tree_contents([('a/b', 'b')])
129
tree_a.commit("silly commit", rev_id='A')
131
tree_b = self.make_branch_and_tree('b')
132
return tree_a, tree_b
134
def get_balanced_branch_pair(self):
135
"""Returns br_a, br_b as with one commit in a, and b has a's stores."""
136
tree_a, tree_b = self.get_unbalanced_tree_pair()
137
tree_b.branch.repository.fetch(tree_a.branch.repository)
138
return tree_a, tree_b
140
def test_clone_partial(self):
141
"""Copy only part of the history of a branch."""
142
# TODO: RBC 20060208 test with a revision not on revision-history.
143
# what should that behaviour be ? Emailed the list.
144
# First, make a branch with two commits.
145
wt_a = self.make_branch_and_tree('a')
146
self.build_tree(['a/one'])
148
wt_a.commit('commit one', rev_id='1')
149
self.build_tree(['a/two'])
151
wt_a.commit('commit two', rev_id='2')
152
# Now make a copy of the repository.
153
repo_b = self.make_repository('b')
154
wt_a.branch.repository.copy_content_into(repo_b)
155
# wt_a might be a lightweight checkout, so get a hold of the actual
156
# branch (because you can't do a partial clone of a lightweight
158
branch = wt_a.branch.bzrdir.open_branch()
159
# Then make a branch where the new repository is, but specify a revision
160
# ID. The new branch's history will stop at the specified revision.
161
br_b = branch.clone(repo_b.bzrdir, revision_id='1')
162
self.assertEqual('1', br_b.last_revision())
164
def get_parented_branch(self):
165
wt_a = self.make_branch_and_tree('a')
166
self.build_tree(['a/one'])
168
wt_a.commit('commit one', rev_id='1')
170
branch_b = wt_a.branch.bzrdir.sprout('b', revision_id='1').open_branch()
171
self.assertEqual(wt_a.branch.base, branch_b.get_parent())
174
def test_clone_branch_nickname(self):
175
# test the nick name is preserved always
176
raise tests.TestSkipped('XXX branch cloning is not yet tested.')
178
def test_clone_branch_parent(self):
179
# test the parent is preserved always
180
branch_b = self.get_parented_branch()
181
repo_c = self.make_repository('c')
182
branch_b.repository.copy_content_into(repo_c)
183
branch_c = branch_b.clone(repo_c.bzrdir)
184
self.assertNotEqual(None, branch_c.get_parent())
185
self.assertEqual(branch_b.get_parent(), branch_c.get_parent())
187
# We can also set a specific parent, and it should be honored
188
random_parent = 'http://example.com/path/to/branch'
189
branch_b.set_parent(random_parent)
190
repo_d = self.make_repository('d')
191
branch_b.repository.copy_content_into(repo_d)
192
branch_d = branch_b.clone(repo_d.bzrdir)
193
self.assertEqual(random_parent, branch_d.get_parent())
195
def test_submit_branch(self):
196
"""Submit location can be queried and set"""
197
branch = self.make_branch('branch')
198
self.assertEqual(branch.get_submit_branch(), None)
199
branch.set_submit_branch('sftp://example.com')
200
self.assertEqual(branch.get_submit_branch(), 'sftp://example.com')
201
branch.set_submit_branch('sftp://example.net')
202
self.assertEqual(branch.get_submit_branch(), 'sftp://example.net')
204
def test_public_branch(self):
205
"""public location can be queried and set"""
206
branch = self.make_branch('branch')
207
self.assertEqual(branch.get_public_branch(), None)
208
branch.set_public_branch('sftp://example.com')
209
self.assertEqual(branch.get_public_branch(), 'sftp://example.com')
210
branch.set_public_branch('sftp://example.net')
211
self.assertEqual(branch.get_public_branch(), 'sftp://example.net')
212
branch.set_public_branch(None)
213
self.assertEqual(branch.get_public_branch(), None)
215
def test_record_initial_ghost(self):
216
"""Branches should support having ghosts."""
217
wt = self.make_branch_and_tree('.')
218
wt.set_parent_ids(['non:existent@rev--ision--0--2'],
219
allow_leftmost_as_ghost=True)
220
self.assertEqual(['non:existent@rev--ision--0--2'],
222
rev_id = wt.commit('commit against a ghost first parent.')
223
rev = wt.branch.repository.get_revision(rev_id)
224
self.assertEqual(rev.parent_ids, ['non:existent@rev--ision--0--2'])
225
# parent_sha1s is not populated now, WTF. rbc 20051003
226
self.assertEqual(len(rev.parent_sha1s), 0)
228
def test_record_two_ghosts(self):
229
"""Recording with all ghosts works."""
230
wt = self.make_branch_and_tree('.')
232
'foo@azkhazan-123123-abcabc',
233
'wibble@fofof--20050401--1928390812',
235
allow_leftmost_as_ghost=True)
236
rev_id = wt.commit("commit from ghost base with one merge")
237
# the revision should have been committed with two parents
238
rev = wt.branch.repository.get_revision(rev_id)
239
self.assertEqual(['foo@azkhazan-123123-abcabc',
240
'wibble@fofof--20050401--1928390812'],
243
def test_bad_revision(self):
244
self.assertRaises(errors.InvalidRevisionId,
245
self.get_branch().repository.get_revision,
249
# compare the gpg-to-sign info for a commit with a ghost and
250
# an identical tree without a ghost
251
# fetch missing should rewrite the TOC of weaves to list newly available parents.
253
def test_sign_existing_revision(self):
254
wt = self.make_branch_and_tree('.')
256
wt.commit("base", allow_pointless=True, rev_id='A')
257
from bzrlib.testament import Testament
258
strategy = gpg.LoopbackGPGStrategy(None)
259
branch.repository.lock_write()
260
branch.repository.start_write_group()
261
branch.repository.sign_revision('A', strategy)
262
branch.repository.commit_write_group()
263
branch.repository.unlock()
264
self.assertEqual('-----BEGIN PSEUDO-SIGNED CONTENT-----\n' +
265
Testament.from_revision(branch.repository,
266
'A').as_short_text() +
267
'-----END PSEUDO-SIGNED CONTENT-----\n',
268
branch.repository.get_signature_text('A'))
270
def test_store_signature(self):
271
wt = self.make_branch_and_tree('.')
275
branch.repository.start_write_group()
277
branch.repository.store_revision_signature(
278
gpg.LoopbackGPGStrategy(None), 'FOO', 'A')
280
branch.repository.abort_write_group()
283
branch.repository.commit_write_group()
286
# A signature without a revision should not be accessible.
287
self.assertRaises(errors.NoSuchRevision,
288
branch.repository.has_signature_for_revision_id,
290
wt.commit("base", allow_pointless=True, rev_id='A')
291
self.assertEqual('-----BEGIN PSEUDO-SIGNED CONTENT-----\n'
292
'FOO-----END PSEUDO-SIGNED CONTENT-----\n',
293
branch.repository.get_signature_text('A'))
295
def test_branch_keeps_signatures(self):
296
wt = self.make_branch_and_tree('source')
297
wt.commit('A', allow_pointless=True, rev_id='A')
298
repo = wt.branch.repository
300
repo.start_write_group()
301
repo.sign_revision('A', gpg.LoopbackGPGStrategy(None))
302
repo.commit_write_group()
304
#FIXME: clone should work to urls,
305
# wt.clone should work to disks.
306
self.build_tree(['target/'])
307
d2 = repo.bzrdir.clone(urlutils.local_path_to_url('target'))
308
self.assertEqual(repo.get_signature_text('A'),
309
d2.open_repository().get_signature_text('A'))
311
def test_nicks(self):
312
"""Test explicit and implicit branch nicknames.
314
Nicknames are implicitly the name of the branch's directory, unless an
315
explicit nickname is set. That is, an explicit nickname always
316
overrides the implicit one.
318
t = self.get_transport()
319
branch = self.make_branch('bzr.dev')
320
# The nick will be 'bzr.dev', because there is no explicit nick set.
321
self.assertEqual(branch.nick, 'bzr.dev')
322
# Move the branch to a different directory, 'bzr.ab'. Now that branch
323
# will report its nick as 'bzr.ab'.
324
t.move('bzr.dev', 'bzr.ab')
325
branch = _mod_branch.Branch.open(self.get_url('bzr.ab'))
326
self.assertEqual(branch.nick, 'bzr.ab')
327
# Set the branch nick explicitly. This will ensure there's a branch
328
# config file in the branch.
329
branch.nick = "Aaron's branch"
330
if not isinstance(branch, remote.RemoteBranch):
331
self.assertTrue(branch._transport.has("branch.conf"))
332
# Because the nick has been set explicitly, the nick is now always
333
# "Aaron's branch", regardless of directory name.
334
self.assertEqual(branch.nick, "Aaron's branch")
335
t.move('bzr.ab', 'integration')
336
branch = _mod_branch.Branch.open(self.get_url('integration'))
337
self.assertEqual(branch.nick, "Aaron's branch")
338
branch.nick = u"\u1234"
339
self.assertEqual(branch.nick, u"\u1234")
341
def test_commit_nicks(self):
342
"""Nicknames are committed to the revision"""
343
wt = self.make_branch_and_tree('bzr.dev')
345
branch.nick = "My happy branch"
346
wt.commit('My commit respect da nick.')
347
committed = branch.repository.get_revision(branch.last_revision())
348
self.assertEqual(committed.properties["branch-nick"],
351
def test_create_colocated(self):
353
repo = self.make_repository('.', shared=True)
354
except errors.IncompatibleFormat:
356
self.assertEquals(0, len(repo.bzrdir.list_branches()))
358
child_branch1 = self.branch_format.initialize(repo.bzrdir,
360
except (errors.UninitializableFormat, errors.NoColocatedBranchSupport):
361
# branch references are not default init'able and
362
# not all bzrdirs support colocated branches.
364
self.assertEquals(1, len(repo.bzrdir.list_branches()))
365
self.branch_format.initialize(repo.bzrdir, name='branch2')
366
self.assertEquals(2, len(repo.bzrdir.list_branches()))
368
def test_create_append_revisions_only(self):
370
repo = self.make_repository('.', shared=True)
371
except errors.IncompatibleFormat:
373
for val in (True, False):
375
branch = self.branch_format.initialize(repo.bzrdir,
376
append_revisions_only=True)
377
except (errors.UninitializableFormat, errors.UpgradeRequired):
378
# branch references are not default init'able and
379
# not all branches support append_revisions_only
381
self.assertEquals(True, branch.get_append_revisions_only())
382
repo.bzrdir.destroy_branch()
384
def test_get_set_append_revisions_only(self):
385
branch = self.make_branch('.')
386
if branch._format.supports_set_append_revisions_only():
387
branch.set_append_revisions_only(True)
388
self.assertTrue(branch.get_append_revisions_only())
389
branch.set_append_revisions_only(False)
390
self.assertFalse(branch.get_append_revisions_only())
392
self.assertRaises(errors.UpgradeRequired,
393
branch.set_append_revisions_only, True)
394
self.assertFalse(branch.get_append_revisions_only())
396
def test_create_open_branch_uses_repository(self):
398
repo = self.make_repository('.', shared=True)
399
except errors.IncompatibleFormat:
401
child_transport = repo.bzrdir.root_transport.clone('child')
402
child_transport.mkdir('.')
403
child_dir = self.bzrdir_format.initialize_on_transport(child_transport)
405
child_branch = self.branch_format.initialize(child_dir)
406
except errors.UninitializableFormat:
407
# branch references are not default init'able.
409
self.assertEqual(repo.bzrdir.root_transport.base,
410
child_branch.repository.bzrdir.root_transport.base)
411
child_branch = _mod_branch.Branch.open(self.get_url('child'))
412
self.assertEqual(repo.bzrdir.root_transport.base,
413
child_branch.repository.bzrdir.root_transport.base)
415
def test_format_description(self):
416
tree = self.make_branch_and_tree('tree')
417
text = tree.branch._format.get_format_description()
418
self.assertTrue(len(text))
420
def test_get_commit_builder(self):
421
branch = self.make_branch(".")
423
builder = branch.get_commit_builder([])
424
self.assertIsInstance(builder, repository.CommitBuilder)
425
branch.repository.commit_write_group()
428
def test_generate_revision_history(self):
429
"""Create a fake revision history easily."""
430
tree = self.make_branch_and_tree('.')
431
rev1 = tree.commit('foo')
432
orig_history = tree.branch.revision_history()
433
rev2 = tree.commit('bar', allow_pointless=True)
434
tree.branch.generate_revision_history(rev1)
435
self.assertEqual(orig_history, tree.branch.revision_history())
437
def test_generate_revision_history_NULL_REVISION(self):
438
tree = self.make_branch_and_tree('.')
439
rev1 = tree.commit('foo')
440
tree.branch.generate_revision_history(revision.NULL_REVISION)
441
self.assertEqual([], tree.branch.revision_history())
443
def test_create_checkout(self):
444
tree_a = self.make_branch_and_tree('a')
445
branch_a = tree_a.branch
446
checkout_b = branch_a.create_checkout('b')
447
self.assertEqual('null:', checkout_b.last_revision())
448
checkout_b.commit('rev1', rev_id='rev1')
449
self.assertEqual('rev1', branch_a.last_revision())
450
self.assertNotEqual(checkout_b.branch.base, branch_a.base)
452
checkout_c = branch_a.create_checkout('c', lightweight=True)
453
self.assertEqual('rev1', checkout_c.last_revision())
454
checkout_c.commit('rev2', rev_id='rev2')
455
self.assertEqual('rev2', branch_a.last_revision())
456
self.assertEqual(checkout_c.branch.base, branch_a.base)
458
checkout_d = branch_a.create_checkout('d', lightweight=True)
459
self.assertEqual('rev2', checkout_d.last_revision())
460
checkout_e = branch_a.create_checkout('e')
461
self.assertEqual('rev2', checkout_e.last_revision())
463
def test_create_anonymous_lightweight_checkout(self):
464
"""A lightweight checkout from a readonly branch should succeed."""
465
tree_a = self.make_branch_and_tree('a')
466
rev_id = tree_a.commit('put some content in the branch')
467
# open the branch via a readonly transport
468
source_branch = _mod_branch.Branch.open(self.get_readonly_url('a'))
469
# sanity check that the test will be valid
470
self.assertRaises((errors.LockError, errors.TransportNotPossible),
471
source_branch.lock_write)
472
checkout = source_branch.create_checkout('c', lightweight=True)
473
self.assertEqual(rev_id, checkout.last_revision())
475
def test_create_anonymous_heavyweight_checkout(self):
476
"""A regular checkout from a readonly branch should succeed."""
477
tree_a = self.make_branch_and_tree('a')
478
rev_id = tree_a.commit('put some content in the branch')
479
# open the branch via a readonly transport
480
source_branch = _mod_branch.Branch.open(self.get_readonly_url('a'))
481
# sanity check that the test will be valid
482
self.assertRaises((errors.LockError, errors.TransportNotPossible),
483
source_branch.lock_write)
484
checkout = source_branch.create_checkout('c')
485
self.assertEqual(rev_id, checkout.last_revision())
487
def test_set_revision_history(self):
488
tree = self.make_branch_and_tree('a')
489
tree.commit('a commit', rev_id='rev1')
491
self.applyDeprecated(symbol_versioning.deprecated_in((2, 4, 0)),
492
br.set_revision_history, ["rev1"])
493
self.assertEquals(br.revision_history(), ["rev1"])
494
self.applyDeprecated(symbol_versioning.deprecated_in((2, 4, 0)),
495
br.set_revision_history, [])
496
self.assertEquals(br.revision_history(), [])
498
def test_heads_to_fetch(self):
499
# heads_to_fetch is a method that returns a collection of revids that
500
# need to be fetched to copy this branch into another repo. At a
501
# minimum this will include the tip.
502
# (In native formats, this is the tip + tags, but other formats may
503
# have other revs needed)
504
tree = self.make_branch_and_tree('a')
505
tree.commit('first commit', rev_id='rev1')
506
tree.commit('second commit', rev_id='rev2')
507
must_fetch, should_fetch = tree.branch.heads_to_fetch()
508
self.assertTrue('rev2' in must_fetch)
510
def test_heads_to_fetch_not_null_revision(self):
511
# NULL_REVISION does not appear in the result of heads_to_fetch, even
512
# for an empty branch.
513
tree = self.make_branch_and_tree('a')
514
must_fetch, should_fetch = tree.branch.heads_to_fetch()
515
self.assertFalse(revision.NULL_REVISION in must_fetch)
516
self.assertFalse(revision.NULL_REVISION in should_fetch)
519
class TestBranchFormat(per_branch.TestCaseWithBranch):
521
def test_branch_format_network_name(self):
522
br = self.make_branch('.')
524
network_name = format.network_name()
525
self.assertIsInstance(network_name, str)
526
# We want to test that the network_name matches the actual format on
527
# disk. For local branches that means that using network_name as a key
528
# in the registry gives back the same format. For remote branches we
529
# check that the network_name of the RemoteBranchFormat we have locally
530
# matches the actual format present on disk.
531
if isinstance(format, remote.RemoteBranchFormat):
533
real_branch = br._real_branch
534
self.assertEqual(real_branch._format.network_name(), network_name)
536
registry = _mod_branch.network_format_registry
537
looked_up_format = registry.get(network_name)
538
self.assertEqual(format.__class__, looked_up_format.__class__)
540
def get_get_config_calls(self):
541
# Smoke test that all branch succeed getting a config
542
br = self.make_branch('.')
544
br.get_config_stack()
547
class ChrootedTests(per_branch.TestCaseWithBranch):
548
"""A support class that provides readonly urls outside the local namespace.
550
This is done by checking if self.transport_server is a MemoryServer. if it
551
is then we are chrooted already, if it is not then an HttpServer is used
556
super(ChrootedTests, self).setUp()
557
if not self.vfs_transport_factory == memory.MemoryServer:
558
self.transport_readonly_server = HttpServer
560
def test_open_containing(self):
561
self.assertRaises(errors.NotBranchError,
562
_mod_branch.Branch.open_containing,
563
self.get_readonly_url(''))
564
self.assertRaises(errors.NotBranchError,
565
_mod_branch.Branch.open_containing,
566
self.get_readonly_url('g/p/q'))
567
branch = self.make_branch('.')
568
branch, relpath = _mod_branch.Branch.open_containing(
569
self.get_readonly_url(''))
570
self.assertEqual('', relpath)
571
branch, relpath = _mod_branch.Branch.open_containing(
572
self.get_readonly_url('g/p/q'))
573
self.assertEqual('g/p/q', relpath)
576
class InstrumentedTransaction(object):
579
self.calls.append('finish')
585
class TestDecorator(object):
591
self._calls.append('lr')
593
def lock_write(self):
594
self._calls.append('lw')
597
self._calls.append('ul')
599
@_mod_branch.needs_read_lock
600
def do_with_read(self):
603
@_mod_branch.needs_read_lock
604
def except_with_read(self):
607
@_mod_branch.needs_write_lock
608
def do_with_write(self):
611
@_mod_branch.needs_write_lock
612
def except_with_write(self):
616
class TestDecorators(tests.TestCase):
618
def test_needs_read_lock(self):
619
branch = TestDecorator()
620
self.assertEqual(1, branch.do_with_read())
621
self.assertEqual(['lr', 'ul'], branch._calls)
623
def test_excepts_in_read_lock(self):
624
branch = TestDecorator()
625
self.assertRaises(RuntimeError, branch.except_with_read)
626
self.assertEqual(['lr', 'ul'], branch._calls)
628
def test_needs_write_lock(self):
629
branch = TestDecorator()
630
self.assertEqual(2, branch.do_with_write())
631
self.assertEqual(['lw', 'ul'], branch._calls)
633
def test_excepts_in_write_lock(self):
634
branch = TestDecorator()
635
self.assertRaises(RuntimeError, branch.except_with_write)
636
self.assertEqual(['lw', 'ul'], branch._calls)
639
class TestBranchPushLocations(per_branch.TestCaseWithBranch):
641
def test_get_push_location_unset(self):
642
self.assertEqual(None, self.get_branch().get_push_location())
644
def test_get_push_location_exact(self):
645
b = self.get_branch()
646
config.LocationConfig.from_string(
647
'[%s]\npush_location=foo\n' % (b.base,), b.base, save=True)
648
self.assertEqual("foo", self.get_branch().get_push_location())
650
def test_set_push_location(self):
651
branch = self.get_branch()
652
branch.set_push_location('foo')
653
self.assertEqual('foo', branch.get_push_location())
656
class TestChildSubmitFormats(per_branch.TestCaseWithBranch):
658
def test_get_child_submit_format_default(self):
659
submit_format = self.get_branch().get_child_submit_format()
660
self.assertTrue(submit_format is None or
661
isinstance(submit_format, str))
663
def test_get_child_submit_format(self):
664
branch = self.get_branch()
665
branch.get_config().set_user_option('child_submit_format', '10')
666
branch = self.get_branch()
667
self.assertEqual('10', branch.get_child_submit_format())
670
class TestFormat(per_branch.TestCaseWithBranch):
671
"""Tests for the format itself."""
673
def test_get_reference(self):
674
"""get_reference on all regular branches should return None."""
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
made_branch = self.make_branch('.')
681
self.assertEqual(None,
682
made_branch._format.get_reference(made_branch.bzrdir))
684
def test_set_reference(self):
685
"""set_reference on all regular branches should be callable."""
686
if not self.branch_format.is_supported():
687
# unsupported formats are not loopback testable
688
# because the default open will not open them and
689
# they may not be initializable.
691
this_branch = self.make_branch('this')
692
other_branch = self.make_branch('other')
694
this_branch._format.set_reference(this_branch.bzrdir, None,
696
except NotImplementedError:
700
ref = this_branch._format.get_reference(this_branch.bzrdir)
701
self.assertEqual(ref, other_branch.base)
703
def test_format_initialize_find_open(self):
704
# loopback test to check the current format initializes to itself.
705
if not self.branch_format.is_supported():
706
# unsupported formats are not loopback testable
707
# because the default open will not open them and
708
# they may not be initializable.
710
# supported formats must be able to init and open
711
t = self.get_transport()
712
readonly_t = transport.get_transport_from_url(self.get_readonly_url())
713
made_branch = self.make_branch('.')
714
self.assertIsInstance(made_branch, _mod_branch.Branch)
716
# find it via bzrdir opening:
717
opened_control = bzrdir.BzrDir.open(readonly_t.base)
718
direct_opened_branch = opened_control.open_branch()
719
self.assertEqual(direct_opened_branch.__class__, made_branch.__class__)
720
self.assertEqual(opened_control, direct_opened_branch.bzrdir)
721
self.assertIsInstance(direct_opened_branch._format,
722
self.branch_format.__class__)
724
# find it via Branch.open
725
opened_branch = _mod_branch.Branch.open(readonly_t.base)
726
self.assertIsInstance(opened_branch, made_branch.__class__)
727
self.assertEqual(made_branch._format.__class__,
728
opened_branch._format.__class__)
729
# if it has a unique id string, can we probe for it ?
731
self.branch_format.get_format_string()
732
except NotImplementedError:
734
self.assertEqual(self.branch_format,
735
opened_control.find_branch_format())
738
class TestBound(per_branch.TestCaseWithBranch):
740
def test_bind_unbind(self):
741
branch = self.make_branch('1')
742
branch2 = self.make_branch('2')
745
except errors.UpgradeRequired:
746
raise tests.TestNotApplicable('Format does not support binding')
747
self.assertTrue(branch.unbind())
748
self.assertFalse(branch.unbind())
749
self.assertIs(None, branch.get_bound_location())
751
def test_old_bound_location(self):
752
branch = self.make_branch('branch1')
754
self.assertIs(None, branch.get_old_bound_location())
755
except errors.UpgradeRequired:
756
raise tests.TestNotApplicable(
757
'Format does not store old bound locations')
758
branch2 = self.make_branch('branch2')
760
self.assertIs(None, branch.get_old_bound_location())
762
self.assertContainsRe(branch.get_old_bound_location(), '\/branch2\/$')
764
def test_bind_diverged(self):
765
tree_a = self.make_branch_and_tree('tree_a')
766
tree_a.commit('rev1a')
767
tree_b = tree_a.bzrdir.sprout('tree_b').open_workingtree()
768
tree_a.commit('rev2a')
769
tree_b.commit('rev2b')
771
tree_b.branch.bind(tree_a.branch)
772
except errors.UpgradeRequired:
773
raise tests.TestNotApplicable('Format does not support binding')
775
def test_unbind_clears_cached_master_branch(self):
776
"""b.unbind clears any cached value of b.get_master_branch."""
777
master = self.make_branch('master')
778
branch = self.make_branch('branch')
781
except errors.UpgradeRequired:
782
raise tests.TestNotApplicable('Format does not support binding')
783
self.addCleanup(branch.lock_write().unlock)
784
self.assertNotEqual(None, branch.get_master_branch())
786
self.assertEqual(None, branch.get_master_branch())
788
def test_unlocked_does_not_cache_master_branch(self):
789
"""Unlocked branches do not cache the result of get_master_branch."""
790
master = self.make_branch('master')
791
branch1 = self.make_branch('branch')
794
except errors.UpgradeRequired:
795
raise tests.TestNotApplicable('Format does not support binding')
797
branch2 = branch1.bzrdir.open_branch()
798
self.assertNotEqual(None, branch1.get_master_branch())
799
# Unbind the branch via branch2. branch1 isn't locked so will
800
# immediately return the new value for get_master_branch.
802
self.assertEqual(None, branch1.get_master_branch())
804
def test_bind_clears_cached_master_branch(self):
805
"""b.bind clears any cached value of b.get_master_branch."""
806
master1 = self.make_branch('master1')
807
master2 = self.make_branch('master2')
808
branch = self.make_branch('branch')
811
except errors.UpgradeRequired:
812
raise tests.TestNotApplicable('Format does not support binding')
813
self.addCleanup(branch.lock_write().unlock)
814
self.assertNotEqual(None, branch.get_master_branch())
816
self.assertEqual('.', urlutils.relative_url(self.get_url('master2'),
817
branch.get_master_branch().base))
819
def test_set_bound_location_clears_cached_master_branch(self):
820
"""b.set_bound_location clears any cached value of b.get_master_branch.
822
master1 = self.make_branch('master1')
823
master2 = self.make_branch('master2')
824
branch = self.make_branch('branch')
827
except errors.UpgradeRequired:
828
raise tests.TestNotApplicable('Format does not support binding')
829
self.addCleanup(branch.lock_write().unlock)
830
self.assertNotEqual(None, branch.get_master_branch())
831
branch.set_bound_location(self.get_url('master2'))
832
self.assertEqual('.', urlutils.relative_url(self.get_url('master2'),
833
branch.get_master_branch().base))
836
class TestStrict(per_branch.TestCaseWithBranch):
838
def test_strict_history(self):
839
tree1 = self.make_branch_and_tree('tree1')
841
tree1.branch.set_append_revisions_only(True)
842
except errors.UpgradeRequired:
843
raise tests.TestSkipped('Format does not support strict history')
844
tree1.commit('empty commit')
845
tree2 = tree1.bzrdir.sprout('tree2').open_workingtree()
846
tree2.commit('empty commit 2')
847
tree1.pull(tree2.branch)
848
tree1.commit('empty commit 3')
849
tree2.commit('empty commit 4')
850
self.assertRaises(errors.DivergedBranches, tree1.pull, tree2.branch)
851
tree2.merge_from_branch(tree1.branch)
852
tree2.commit('empty commit 5')
853
self.assertRaises(errors.AppendRevisionsOnlyViolation, tree1.pull,
855
tree3 = tree1.bzrdir.sprout('tree3').open_workingtree()
856
tree3.merge_from_branch(tree2.branch)
857
tree3.commit('empty commit 6')
858
tree2.pull(tree3.branch)
861
class TestIgnoreFallbacksParameter(per_branch.TestCaseWithBranch):
863
def make_branch_with_fallback(self):
864
fallback = self.make_branch('fallback')
865
if not fallback._format.supports_stacking():
866
raise tests.TestNotApplicable("format does not support stacking")
867
stacked = self.make_branch('stacked')
868
stacked.set_stacked_on_url(fallback.base)
871
def test_fallbacks_not_opened(self):
872
stacked = self.make_branch_with_fallback()
873
self.get_transport('').rename('fallback', 'moved')
874
reopened = stacked.bzrdir.open_branch(ignore_fallbacks=True)
875
self.assertEqual([], reopened.repository._fallback_repositories)
877
def test_fallbacks_are_opened(self):
878
stacked = self.make_branch_with_fallback()
879
reopened = stacked.bzrdir.open_branch(ignore_fallbacks=False)
880
self.assertLength(1, reopened.repository._fallback_repositories)
883
class TestReferenceLocation(per_branch.TestCaseWithBranch):
885
def test_reference_parent(self):
886
tree = self.make_branch_and_tree('tree')
887
subtree = self.make_branch_and_tree('tree/subtree')
888
subtree.set_root_id('subtree-id')
890
tree.add_reference(subtree)
891
except errors.UnsupportedOperation:
892
raise tests.TestNotApplicable('Tree cannot hold references.')
893
reference_parent = tree.branch.reference_parent('subtree-id',
895
self.assertEqual(subtree.branch.base, reference_parent.base)
897
def test_reference_parent_accepts_possible_transports(self):
898
tree = self.make_branch_and_tree('tree')
899
subtree = self.make_branch_and_tree('tree/subtree')
900
subtree.set_root_id('subtree-id')
902
tree.add_reference(subtree)
903
except errors.UnsupportedOperation:
904
raise tests.TestNotApplicable('Tree cannot hold references.')
905
reference_parent = tree.branch.reference_parent('subtree-id',
906
'subtree', possible_transports=[subtree.bzrdir.root_transport])
908
def test_get_reference_info(self):
909
branch = self.make_branch('branch')
911
path, loc = branch.get_reference_info('file-id')
912
except errors.UnsupportedOperation:
913
raise tests.TestNotApplicable('Branch cannot hold references.')
914
self.assertIs(None, path)
915
self.assertIs(None, loc)
917
def test_set_reference_info(self):
918
branch = self.make_branch('branch')
920
branch.set_reference_info('file-id', 'path/to/location',
922
except errors.UnsupportedOperation:
923
raise tests.TestNotApplicable('Branch cannot hold references.')
925
def test_set_get_reference_info(self):
926
branch = self.make_branch('branch')
928
branch.set_reference_info('file-id', 'path/to/file',
930
except errors.UnsupportedOperation:
931
raise tests.TestNotApplicable('Branch cannot hold references.')
932
# Create a new instance to ensure storage is permanent
933
branch = _mod_branch.Branch.open('branch')
934
tree_path, branch_location = branch.get_reference_info('file-id')
935
self.assertEqual('path/to/location', branch_location)
937
def test_set_null_reference_info(self):
938
branch = self.make_branch('branch')
940
branch.set_reference_info('file-id', 'path/to/file',
942
except errors.UnsupportedOperation:
943
raise tests.TestNotApplicable('Branch cannot hold references.')
944
branch.set_reference_info('file-id', None, None)
945
tree_path, branch_location = branch.get_reference_info('file-id')
946
self.assertIs(None, tree_path)
947
self.assertIs(None, branch_location)
949
def test_set_null_reference_info_when_null(self):
950
branch = self.make_branch('branch')
952
tree_path, branch_location = branch.get_reference_info('file-id')
953
except errors.UnsupportedOperation:
954
raise tests.TestNotApplicable('Branch cannot hold references.')
955
self.assertIs(None, tree_path)
956
self.assertIs(None, branch_location)
957
branch.set_reference_info('file-id', None, None)
959
def test_set_null_requires_two_nones(self):
960
branch = self.make_branch('branch')
962
e = self.assertRaises(ValueError, branch.set_reference_info,
963
'file-id', 'path', None)
964
except errors.UnsupportedOperation:
965
raise tests.TestNotApplicable('Branch cannot hold references.')
966
self.assertEqual('tree_path must be None when branch_location is'
968
e = self.assertRaises(ValueError, branch.set_reference_info,
969
'file-id', None, 'location')
970
self.assertEqual('branch_location must be None when tree_path is'
973
def make_branch_with_reference(self, location, reference_location,
975
branch = self.make_branch(location)
977
branch.set_reference_info(file_id, 'path/to/file',
979
except errors.UnsupportedOperation:
980
raise tests.TestNotApplicable('Branch cannot hold references.')
983
def test_reference_parent_from_reference_info_(self):
984
referenced_branch = self.make_branch('reference_branch')
985
branch = self.make_branch_with_reference('branch',
986
referenced_branch.base)
987
parent = branch.reference_parent('file-id', 'path/to/file')
988
self.assertEqual(parent.base, referenced_branch.base)
990
def test_branch_relative_reference_location(self):
991
branch = self.make_branch('branch')
993
branch.set_reference_info('file-id', 'path/to/file',
994
'../reference_branch')
995
except errors.UnsupportedOperation:
996
raise tests.TestNotApplicable('Branch cannot hold references.')
997
referenced_branch = self.make_branch('reference_branch')
998
parent = branch.reference_parent('file-id', 'path/to/file')
999
self.assertEqual(parent.base, referenced_branch.base)
1001
def test_sprout_copies_reference_location(self):
1002
branch = self.make_branch_with_reference('branch', '../reference')
1003
new_branch = branch.bzrdir.sprout('new-branch').open_branch()
1004
self.assertEqual('../reference',
1005
new_branch.get_reference_info('file-id')[1])
1007
def test_clone_copies_reference_location(self):
1008
branch = self.make_branch_with_reference('branch', '../reference')
1009
new_branch = branch.bzrdir.clone('new-branch').open_branch()
1010
self.assertEqual('../reference',
1011
new_branch.get_reference_info('file-id')[1])
1013
def test_copied_locations_are_rebased(self):
1014
branch = self.make_branch_with_reference('branch', 'reference')
1015
new_branch = branch.bzrdir.sprout('branch/new-branch').open_branch()
1016
self.assertEqual('../reference',
1017
new_branch.get_reference_info('file-id')[1])
1019
def test_update_references_retains_old_references(self):
1020
branch = self.make_branch_with_reference('branch', 'reference')
1021
new_branch = self.make_branch_with_reference(
1022
'new_branch', 'reference', 'file-id2')
1023
new_branch.update_references(branch)
1024
self.assertEqual('reference',
1025
branch.get_reference_info('file-id')[1])
1027
def test_update_references_retains_known_references(self):
1028
branch = self.make_branch_with_reference('branch', 'reference')
1029
new_branch = self.make_branch_with_reference(
1030
'new_branch', 'reference2')
1031
new_branch.update_references(branch)
1032
self.assertEqual('reference',
1033
branch.get_reference_info('file-id')[1])
1035
def test_update_references_skips_known_references(self):
1036
branch = self.make_branch_with_reference('branch', 'reference')
1037
new_branch = branch.bzrdir.sprout('branch/new-branch').open_branch()
1038
new_branch.set_reference_info('file-id', '../foo', '../foo')
1039
new_branch.update_references(branch)
1040
self.assertEqual('reference',
1041
branch.get_reference_info('file-id')[1])
1043
def test_pull_updates_references(self):
1044
branch = self.make_branch_with_reference('branch', 'reference')
1045
new_branch = branch.bzrdir.sprout('branch/new-branch').open_branch()
1046
new_branch.set_reference_info('file-id2', '../foo', '../foo')
1047
branch.pull(new_branch)
1048
self.assertEqual('foo',
1049
branch.get_reference_info('file-id2')[1])
1051
def test_push_updates_references(self):
1052
branch = self.make_branch_with_reference('branch', 'reference')
1053
new_branch = branch.bzrdir.sprout('branch/new-branch').open_branch()
1054
new_branch.set_reference_info('file-id2', '../foo', '../foo')
1055
new_branch.push(branch)
1056
self.assertEqual('foo',
1057
branch.get_reference_info('file-id2')[1])
1059
def test_merge_updates_references(self):
1060
branch = self.make_branch_with_reference('branch', 'reference')
1061
tree = self.make_branch_and_tree('tree')
1063
branch.pull(tree.branch)
1064
checkout = branch.create_checkout('checkout', lightweight=True)
1065
checkout.commit('bar')
1067
self.addCleanup(tree.unlock)
1068
merger = merge.Merger.from_revision_ids(None, tree,
1069
branch.last_revision(),
1070
other_branch=branch)
1071
merger.merge_type = merge.Merge3Merger
1073
self.assertEqual('../branch/reference',
1074
tree.branch.get_reference_info('file-id')[1])
1077
class TestBranchControlComponent(per_branch.TestCaseWithBranch):
1078
"""Branch implementations adequately implement ControlComponent."""
1080
def test_urls(self):
1081
br = self.make_branch('branch')
1082
self.assertIsInstance(br.user_url, str)
1083
self.assertEqual(br.user_url, br.user_transport.base)
1084
# for all current bzrdir implementations the user dir must be
1085
# above the control dir but we might need to relax that?
1086
self.assertEqual(br.control_url.find(br.user_url), 0)
1087
self.assertEqual(br.control_url, br.control_transport.base)
40
from bzrlib.fetch import Fetcher
43
b1 = Branch('b1', init=True)
44
b2 = Branch('b2', init=True)
45
file(os.sep.join(['b1', 'foo']), 'w').write('hello')
46
b1.add(['foo'], ['foo-id'])
47
b1.commit('lala!', rev_id='revision-1', allow_pointless=False)
50
f = Fetcher(from_branch=b1, to_branch=b2)
51
eq = self.assertEquals
53
eq(f.last_revision, 'revision-1')
55
rev = b2.get_revision('revision-1')
56
tree = b2.revision_tree('revision-1')
57
eq(tree.get_file_text('foo-id'), 'hello')
60
# TODO: rewrite this as a regular unittest, without relying on the displayed output
61
# >>> from bzrlib.commit import commit
62
# >>> bzrlib.trace.silent = True
63
# >>> br1 = ScratchBranch(files=['foo', 'bar'])
66
# >>> commit(br1, "lala!", rev_id="REVISION-ID-1", verbose=False)
67
# >>> br2 = ScratchBranch()
68
# >>> br2.update_revisions(br1)
70
# Added 1 inventories.
72
# >>> br2.revision_history()
74
# >>> br2.update_revisions(br1)
76
# >>> br1.text_store.total_size() == br2.text_store.total_size()