Rev 2162: Add a Transport.is_readonly remote call, let {Branch, Repository}.lock_write remote call return UnlockableTransport, and miscellaneous test fixes. in sftp://bazaar.launchpad.net/%7Ebzr/bzr/hpss/
Andrew Bennetts
andrew.bennetts at canonical.com
Tue Mar 13 05:57:53 GMT 2007
At sftp://bazaar.launchpad.net/%7Ebzr/bzr/hpss/
------------------------------------------------------------
revno: 2162
revision-id: andrew.bennetts at canonical.com-20070313055201-ivxd3uhcvhqk995p
parent: andrew.bennetts at canonical.com-20070306133649-2371mkrfnrxcqjnn
committer: Andrew Bennetts <andrew.bennetts at canonical.com>
branch nick: hpss
timestamp: Tue 2007-03-13 16:52:01 +1100
message:
Add a Transport.is_readonly remote call, let {Branch,Repository}.lock_write remote call return UnlockableTransport, and miscellaneous test fixes.
modified:
bzrlib/remote.py remote.py-20060720103555-yeeg2x51vn0rbtdp-1
bzrlib/smart/branch.py branch.py-20061124031907-mzh3pla28r83r97f-1
bzrlib/smart/repository.py repository.py-20061128022038-vr5wy5bubyb8xttk-1
bzrlib/smart/request.py request.py-20061108095550-gunadhxmzkdjfeek-1
bzrlib/smart/server.py server.py-20061110062051-chzu10y32vx8gvur-1
bzrlib/tests/branch_implementations/test_branch.py testbranch.py-20050711070244-121d632bc37d7253
bzrlib/tests/test_remote.py test_remote.py-20060720103555-yeeg2x51vn0rbtdp-2
bzrlib/tests/test_smart.py test_smart.py-20061122024551-ol0l0o0oofsu9b3t-2
bzrlib/transport/memory.py memory.py-20051016101338-cd008dbdf69f04fc
bzrlib/transport/readonly.py readonly.py-20060120032407-66d3166c39ffdc79
bzrlib/transport/remote.py ssh.py-20060608202016-c25gvf1ob7ypbus6-1
=== modified file 'bzrlib/remote.py'
--- a/bzrlib/remote.py 2007-03-06 13:36:49 +0000
+++ b/bzrlib/remote.py 2007-03-13 05:52:01 +0000
@@ -315,6 +315,8 @@
return token
elif response[0] == 'LockContention':
raise errors.LockContention('(remote lock)')
+ elif response[0] == 'UnlockableTransport':
+ raise errors.UnlockableTransport(self.bzrdir.root_transport)
else:
assert False, 'unexpected response code %s' % (response,)
@@ -674,6 +676,8 @@
raise errors.LockContention('(remote lock)')
elif response[0] == 'TokenMismatch':
raise errors.TokenMismatch(tokens, '(remote tokens)')
+ elif response[0] == 'UnlockableTransport':
+ raise errors.UnlockableTransport(self.bzrdir.root_transport)
else:
assert False, 'unexpected response code %r' % (response,)
@@ -844,6 +848,12 @@
self._ensure_real()
return self._real_branch.set_last_revision_info(revno, revision_id)
+ def generate_revision_history(self, revision_id, last_rev=None,
+ other_branch=None):
+ self._ensure_real()
+ return self._real_branch.generate_revision_history(
+ revision_id, last_rev=last_rev, other_branch=other_branch)
+
class RemoteWorkingTree(object):
=== modified file 'bzrlib/smart/branch.py'
--- a/bzrlib/smart/branch.py 2007-03-05 05:03:14 +0000
+++ b/bzrlib/smart/branch.py 2007-03-13 05:52:01 +0000
@@ -127,6 +127,8 @@
return SmartServerResponse(('LockContention',))
except errors.TokenMismatch:
return SmartServerResponse(('TokenMismatch',))
+ except errors.UnlockableTransport:
+ return SmartServerResponse(('UnlockableTransport',))
branch.repository.leave_lock_in_place()
branch.leave_lock_in_place()
branch.unlock()
=== modified file 'bzrlib/smart/repository.py'
--- a/bzrlib/smart/repository.py 2007-03-05 05:03:14 +0000
+++ b/bzrlib/smart/repository.py 2007-03-13 05:52:01 +0000
@@ -153,6 +153,8 @@
token = repository.lock_write(token=token)
except errors.LockContention, e:
return SmartServerResponse(('LockContention',))
+ except errors.UnlockableTransport:
+ return SmartServerResponse(('UnlockableTransport',))
repository.leave_lock_in_place()
repository.unlock()
if token is None:
=== modified file 'bzrlib/smart/request.py'
--- a/bzrlib/smart/request.py 2007-02-21 05:55:24 +0000
+++ b/bzrlib/smart/request.py 2007-03-13 05:52:01 +0000
@@ -230,6 +230,17 @@
return SmartServerResponse((answer,))
+class SmartServerIsReadonly(SmartServerRequest):
+ # XXX: this request method belongs somewhere else.
+
+ def do(self):
+ if self._backing_transport.is_readonly():
+ answer = 'yes'
+ else:
+ answer = 'no'
+ return SmartServerResponse((answer,))
+
+
request_handlers = registry.Registry()
request_handlers.register_lazy(
'append', 'bzrlib.smart.vfs', 'AppendRequest')
@@ -295,4 +306,6 @@
request_handlers.register_lazy(
'stat', 'bzrlib.smart.vfs', 'StatRequest')
request_handlers.register_lazy(
+ 'Transport.is_readonly', 'bzrlib.smart.request', 'SmartServerIsReadonly')
+request_handlers.register_lazy(
'probe_dont_use', 'bzrlib.smart.request', 'ProbeDontUseRequest')
=== modified file 'bzrlib/smart/server.py'
--- a/bzrlib/smart/server.py 2007-02-01 06:55:05 +0000
+++ b/bzrlib/smart/server.py 2007-03-13 05:52:01 +0000
@@ -143,3 +143,4 @@
"""Get a backing transport from a server we are decorating."""
url = 'chroot+readonly+' + backing_transport_server.get_url()
return transport.get_transport(url)
+
=== modified file 'bzrlib/tests/branch_implementations/test_branch.py'
--- a/bzrlib/tests/branch_implementations/test_branch.py 2007-03-06 13:36:49 +0000
+++ b/bzrlib/tests/branch_implementations/test_branch.py 2007-03-13 05:52:01 +0000
@@ -118,12 +118,10 @@
def test_fetch_revisions(self):
"""Test fetch-revision operation."""
- get_transport(self.get_url()).mkdir('b1')
- get_transport(self.get_url()).mkdir('b2')
wt = self.make_branch_and_tree('b1')
b1 = wt.branch
b2 = self.make_branch('b2')
- file('b1/foo', 'w').write('hello')
+ wt.bzrdir.root_transport.put_bytes('foo', 'hello')
wt.add(['foo'], ['foo-id'])
wt.commit('lala!', rev_id='revision-1', allow_pointless=False)
@@ -136,10 +134,11 @@
def test_get_revision_delta(self):
tree_a = self.make_branch_and_tree('a')
- self.build_tree(['a/foo'])
+ transport = tree_a.bzrdir.root_transport
+ self.build_tree(['foo'], transport=transport)
tree_a.add('foo', 'file1')
tree_a.commit('rev1', rev_id='rev1')
- self.build_tree(['a/vla'])
+ self.build_tree(['vla'], transport=transport)
tree_a.add('vla', 'file2')
tree_a.commit('rev2', rev_id='rev2')
@@ -337,23 +336,37 @@
d2.open_repository().get_signature_text('A'))
def test_nicks(self):
- """Branch nicknames"""
+ """Test explicit and implicit branch nicknames.
+
+ Nicknames are implicitly the name of the branch's directory, unless an
+ explicit nickname is set. That is, an explicit nickname always
+ overrides the implicit one.
+ """
+ # Make a branch in a directory called 'bzr.dev'
t = get_transport(self.get_url())
t.mkdir('bzr.dev')
branch = self.make_branch('bzr.dev')
+ # The nick will be 'bzr.dev', because there is no explicit nick set.
self.assertEqual(branch.nick, 'bzr.dev')
+ # Move the branch to a different directory, 'bzr.ab'. Now that branch
+ # will report its nick as 'bzr.ab'.
t.move('bzr.dev', 'bzr.ab')
branch = Branch.open(self.get_url('bzr.ab'))
self.assertEqual(branch.nick, 'bzr.ab')
- branch.nick = "Aaron's branch"
- branch.nick = "Aaron's branch"
- self.failUnless(
- t.has(
- t.relpath(
- branch.control_files.controlfilename("branch.conf")
- )
- )
- )
+ # Set the branch nick explicitly. This will ensure there's a branch
+ # config file in the branch.
+ branch.nick = "Aaron's branch"
+ branch.nick = "Aaron's branch"
+ try:
+ controlfilename = branch.control_files.controlfilename
+ except AttributeError:
+ # remote branches don't have control_files
+ pass
+ else:
+ self.failUnless(
+ t.has(t.relpath(controlfilename("branch.conf"))))
+ # Because the nick has been set explicitly, the nick is now always
+ # "Aaron's branch", regardless of directory name.
self.assertEqual(branch.nick, "Aaron's branch")
t.move('bzr.ab', 'integration')
branch = Branch.open(self.get_url('integration'))
@@ -377,8 +390,9 @@
repo = self.make_repository('.', shared=True)
except errors.IncompatibleFormat:
return
- repo.bzrdir.root_transport.mkdir('child')
- child_dir = self.bzrdir_format.initialize('child')
+ child_transport = repo.bzrdir.root_transport.clone('child')
+ child_transport.mkdir('.')
+ child_dir = self.bzrdir_format.initialize_on_transport(child_transport)
try:
child_branch = self.branch_format.initialize(child_dir)
except errors.UninitializableFormat:
@@ -448,8 +462,8 @@
"""A lightweight checkout from a readonly branch should succeed."""
tree_a = self.make_branch_and_tree('a')
rev_id = tree_a.commit('put some content in the branch')
- source_branch = bzrlib.branch.Branch.open(
- 'readonly+' + tree_a.bzrdir.root_transport.base)
+ # open the branch via a readonly transport
+ source_branch = bzrlib.branch.Branch.open(self.get_readonly_url('a'))
# sanity check that the test will be valid
self.assertRaises((errors.LockError, errors.TransportNotPossible),
source_branch.lock_write)
@@ -460,8 +474,8 @@
"""A regular checkout from a readonly branch should succeed."""
tree_a = self.make_branch_and_tree('a')
rev_id = tree_a.commit('put some content in the branch')
- source_branch = bzrlib.branch.Branch.open(
- 'readonly+' + tree_a.branch.bzrdir.root_transport.base)
+ # open the branch via a readonly transport
+ source_branch = bzrlib.branch.Branch.open(self.get_readonly_url('a'))
# sanity check that the test will be valid
self.assertRaises((errors.LockError, errors.TransportNotPossible),
source_branch.lock_write)
=== modified file 'bzrlib/tests/test_remote.py'
--- a/bzrlib/tests/test_remote.py 2007-02-28 07:08:25 +0000
+++ b/bzrlib/tests/test_remote.py 2007-03-13 05:52:01 +0000
@@ -47,13 +47,19 @@
class BasicRemoteObjectTests(tests.TestCaseWithTransport):
def setUp(self):
+ self.transport_server = server.SmartTCPServer_for_testing
super(BasicRemoteObjectTests, self).setUp()
- self.transport_server = server.SmartTCPServer_for_testing
self.transport = self.get_transport()
self.client = self.transport.get_smart_client()
# make a branch that can be opened over the smart transport
self.local_wt = BzrDir.create_standalone_workingtree('.')
+ def test_is_readonly(self):
+ # XXX: this is a poor way to test RemoteTransport, but currently there's
+ # no easy way to substitute in a fake client on a transport like we can
+ # with RemoteBzrDir/Branch/Repository.
+ self.assertEqual(self.transport.is_readonly(), False)
+
def test_create_remote_bzrdir(self):
b = remote.RemoteBzrDir(self.transport)
self.assertIsInstance(b, BzrDir)
@@ -90,6 +96,20 @@
self.assertIsInstance(d, BzrDir)
+class ReadonlyRemoteTransportTests(tests.TestCaseWithTransport):
+
+ def setUp(self):
+ self.transport_server = server.ReadonlySmartTCPServer_for_testing
+ super(ReadonlyRemoteTransportTests, self).setUp()
+
+ def test_is_readonly_yes(self):
+ # XXX: this is a poor way to test RemoteTransport, but currently there's
+ # no easy way to substitute in a fake client on a transport like we can
+ # with RemoteBzrDir/Branch/Repository.
+ transport = self.get_readonly_transport()
+ self.assertEqual(transport.is_readonly(), True)
+
+
class FakeProtocol(object):
"""Lookalike SmartClientRequestProtocolOne allowing body reading tests."""
@@ -298,6 +318,22 @@
self.assertEqual('config file body', result.read())
+class TestBranchLockWrite(tests.TestCase):
+
+ def test_lock_write_unlockable(self):
+ client = FakeClient([(('UnlockableTransport', ), '')])
+ transport = MemoryTransport()
+ transport.mkdir('quack')
+ transport = transport.clone('quack')
+ # we do not want bzrdir to make any remote calls
+ bzrdir = RemoteBzrDir(transport, _client=False)
+ branch = RemoteBranch(bzrdir, None, _client=client)
+ self.assertRaises(errors.UnlockableTransport, branch.lock_write)
+ self.assertEqual(
+ [('call', 'Branch.lock_write', ('///quack/', '', ''))],
+ client._calls)
+
+
class TestRemoteRepository(tests.TestCase):
def setup_fake_client_and_repository(self, responses, transport_path):
@@ -470,7 +506,7 @@
responses, transport_path)
result = repo.lock_write()
self.assertEqual(
- [('call', 'Repository.lock_write', ('///quack/',))],
+ [('call', 'Repository.lock_write', ('///quack/', ''))],
client._calls)
self.assertEqual('a token', result)
@@ -481,7 +517,17 @@
responses, transport_path)
self.assertRaises(errors.LockContention, repo.lock_write)
self.assertEqual(
- [('call', 'Repository.lock_write', ('///quack/',))],
+ [('call', 'Repository.lock_write', ('///quack/', ''))],
+ client._calls)
+
+ def test_lock_write_unlockable(self):
+ responses = [(('UnlockableTransport', ), '')]
+ transport_path = 'quack'
+ repo, client = self.setup_fake_client_and_repository(
+ responses, transport_path)
+ self.assertRaises(errors.UnlockableTransport, repo.lock_write)
+ self.assertEqual(
+ [('call', 'Repository.lock_write', ('///quack/', ''))],
client._calls)
@@ -496,7 +542,7 @@
repo.lock_write()
repo.unlock()
self.assertEqual(
- [('call', 'Repository.lock_write', ('///quack/',)),
+ [('call', 'Repository.lock_write', ('///quack/', '')),
('call', 'Repository.unlock', ('///quack/', 'a token'))],
client._calls)
@@ -526,4 +572,3 @@
# The remote repo shouldn't be accessed.
self.assertEqual([], client._calls)
-
=== modified file 'bzrlib/tests/test_smart.py'
--- a/bzrlib/tests/test_smart.py 2007-03-05 05:03:14 +0000
+++ b/bzrlib/tests/test_smart.py 2007-03-13 05:52:01 +0000
@@ -395,6 +395,14 @@
self.assertEqual(
SmartServerResponse(('LockContention',)), response)
+ def test_lock_write_on_readonly_transport(self):
+ backing = self.get_readonly_transport()
+ request = smart.branch.SmartServerBranchRequestLockWrite(backing)
+ branch = self.make_branch('.')
+ response = request.execute('')
+ self.assertEqual(
+ SmartServerResponse(('UnlockableTransport',)), response)
+
class TestSmartServerBranchRequestUnlock(tests.TestCaseWithTransport):
@@ -650,6 +658,14 @@
self.assertEqual(
SmartServerResponse(('LockContention',)), response)
+ def test_lock_write_on_readonly_transport(self):
+ backing = self.get_readonly_transport()
+ request = smart.repository.SmartServerRepositoryLockWrite(backing)
+ repository = self.make_repository('.')
+ response = request.execute('')
+ self.assertEqual(
+ SmartServerResponse(('UnlockableTransport',)), response)
+
class TestSmartServerRepositoryUnlock(tests.TestCaseWithTransport):
@@ -682,6 +698,23 @@
SmartServerResponse(('TokenMismatch',)), response)
+class TestSmartServerIsReadonly(tests.TestCaseWithTransport):
+
+ def test_is_readonly_no(self):
+ backing = self.get_transport()
+ request = smart.request.SmartServerIsReadonly(backing)
+ response = request.execute()
+ self.assertEqual(
+ SmartServerResponse(('no',)), response)
+
+ def test_is_readonly_yes(self):
+ backing = self.get_readonly_transport()
+ request = smart.request.SmartServerIsReadonly(backing)
+ response = request.execute()
+ self.assertEqual(
+ SmartServerResponse(('yes',)), response)
+
+
class TestHandlers(tests.TestCase):
"""Tests for the request.request_handlers object."""
@@ -732,3 +765,6 @@
self.assertEqual(
smart.request.request_handlers.get('Repository.unlock'),
smart.repository.SmartServerRepositoryUnlock)
+ self.assertEqual(
+ smart.request.request_handlers.get('Transport.is_readonly'),
+ smart.request.SmartServerIsReadonly)
=== modified file 'bzrlib/transport/memory.py'
--- a/bzrlib/transport/memory.py 2006-11-21 08:16:46 +0000
+++ b/bzrlib/transport/memory.py 2007-03-13 05:52:01 +0000
@@ -192,11 +192,11 @@
if _abspath in self._files:
self._translate_error(IOError(errno.ENOTDIR, relpath), relpath)
for path in self._files:
- if path.startswith(_abspath):
+ if path.startswith(_abspath + '/'):
self._translate_error(IOError(errno.ENOTEMPTY, relpath),
relpath)
for path in self._dirs:
- if path.startswith(_abspath) and path != _abspath:
+ if path.startswith(_abspath + '/') and path != _abspath:
self._translate_error(IOError(errno.ENOTEMPTY, relpath), relpath)
if not _abspath in self._dirs:
raise NoSuchFile(relpath)
=== modified file 'bzrlib/transport/readonly.py'
--- a/bzrlib/transport/readonly.py 2006-09-05 21:20:24 +0000
+++ b/bzrlib/transport/readonly.py 2007-03-13 05:52:01 +0000
@@ -16,7 +16,7 @@
"""Implementation of Transport that adapts another transport to be readonly."""
-from bzrlib.errors import TransportNotPossible
+from bzrlib.errors import TransportNotPossible, NoSmartServer, NoSmartMedium
from bzrlib.transport.decorator import TransportDecorator, DecoratorServer
@@ -71,6 +71,13 @@
"""See Transport.lock_write."""
raise TransportNotPossible('readonly transport')
+ def get_smart_client(self):
+ raise NoSmartServer(self.base)
+
+ def get_smart_medium(self):
+ raise NoSmartMedium(self)
+
+
class ReadonlyServer(DecoratorServer):
"""Server for the ReadonlyTransportDecorator for testing with."""
=== modified file 'bzrlib/transport/remote.py'
--- a/bzrlib/transport/remote.py 2007-03-06 13:36:49 +0000
+++ b/bzrlib/transport/remote.py 2007-03-13 05:52:01 +0000
@@ -33,8 +33,7 @@
from bzrlib.smart import client, medium, protocol
# must do this otherwise urllib can't parse the urls properly :(
-for scheme in ['ssh', 'bzr', 'bzr+loopback', 'bzr+ssh', 'bzr+http',
- 'readonly+bzr']:
+for scheme in ['ssh', 'bzr', 'bzr+loopback', 'bzr+ssh', 'bzr+http']:
transport.register_urlparse_netloc_protocol(scheme)
del scheme
@@ -802,8 +801,15 @@
def is_readonly(self):
"""Smart server transport can do read/write file operations."""
- return False
-
+ resp = self._call2('Transport.is_readonly')
+ if resp == ('yes', ):
+ return True
+ elif resp == ('no', ):
+ return False
+ else:
+ self._translate_error(resp)
+ assert False, 'weird response %r' % (resp,)
+
def get_smart_client(self):
return self._medium
More information about the bazaar-commits
mailing list