Rev 2126: Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil). in file:///home/robertc/source/baz/hpss/
Robert Collins
robertc at robertcollins.net
Fri Feb 2 19:25:32 GMT 2007
------------------------------------------------------------
revno: 2126
revision-id: robertc at robertcollins.net-20070202192520-3fecwesz5o3hb0fo
parent: larstiq at larstiq.dyndns.org-20070202180451-ug4361fboqj0qfcc
committer: Robert Collins <robertc at robertcollins.net>
branch nick: hpss
timestamp: Sat 2007-02-03 06:25:20 +1100
message:
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
modified:
bzrlib/branch.py branch.py-20050309040759-e4baf4e0d046576e
bzrlib/config.py config.py-20051011043216-070c74f4e9e338e8
bzrlib/remote.py remote.py-20060720103555-yeeg2x51vn0rbtdp-1
bzrlib/smart/branch.py branch.py-20061124031907-mzh3pla28r83r97f-1
bzrlib/smart/request.py request.py-20061108095550-gunadhxmzkdjfeek-1
bzrlib/tests/test_lockable_files.py test_lockable_files.py-20051225183927-365c7fd99591caf1
bzrlib/tests/test_remote.py test_remote.py-20060720103555-yeeg2x51vn0rbtdp-2
bzrlib/tests/test_smart.py test_smart.py-20061122024551-ol0l0o0oofsu9b3t-2
bzrlib/tests/test_smart_transport.py test_ssh_transport.py-20060608202016-c25gvf1ob7ypbus6-2
=== modified file 'bzrlib/branch.py'
--- a/bzrlib/branch.py 2007-02-01 15:22:42 +0000
+++ b/bzrlib/branch.py 2007-02-02 19:25:20 +0000
@@ -442,7 +442,8 @@
def get_push_location(self):
"""Return the None or the location to push this branch to."""
- raise NotImplementedError(self.get_push_location)
+ push_loc = self.get_config().get_user_option('push_location')
+ return push_loc
def set_push_location(self, location):
"""Set a new push location for this branch."""
@@ -1296,11 +1297,6 @@
raise errors.InaccessibleParent(parent, self.base)
return None
- def get_push_location(self):
- """See Branch.get_push_location."""
- push_loc = self.get_config().get_user_option('push_location')
- return push_loc
-
def set_push_location(self, location):
"""See Branch.set_push_location."""
self.get_config().set_user_option(
=== modified file 'bzrlib/config.py'
--- a/bzrlib/config.py 2006-12-07 16:21:39 +0000
+++ b/bzrlib/config.py 2007-02-02 19:25:20 +0000
@@ -829,7 +829,7 @@
def _get_config(self):
try:
- obj = ConfigObj(self.branch.control_files.get('branch.conf'),
+ obj = ConfigObj(self.branch.control_files.get('branch.conf'),
encoding='utf-8')
except errors.NoSuchFile:
obj = ConfigObj(encoding='utf=8')
=== modified file 'bzrlib/remote.py'
--- a/bzrlib/remote.py 2007-02-02 17:34:08 +0000
+++ b/bzrlib/remote.py 2007-02-02 19:25:20 +0000
@@ -17,6 +17,7 @@
# TODO: At some point, handle upgrades by just passing the whole request
# across to run on the server.
+from cStringIO import StringIO
from urlparse import urlparse
from bzrlib import branch, errors, repository
@@ -209,6 +210,30 @@
return response[0] == 'yes'
+class RemoteBranchLockableFiles(object):
+ """A 'LockableFiles' implementation that talks to a smart server.
+
+ This is not a public interface class.
+ """
+
+ def __init__(self, bzrdir, _client):
+ self.bzrdir = bzrdir
+ self._client = _client
+
+ def get(self, path):
+ """'get' a remote path as per the LockableFiles interface.
+
+ :param path: the file to 'get'. If this is 'branch.conf', we do not
+ just retrieve a file, instead we ask the smart server to generate
+ a configuration for us - which is retrieved as an INI file.
+ """
+ assert path == 'branch.conf'
+ path = self.bzrdir._path_for_remote_call(self._client)
+ response = self._client.call2('Branch.get_config_file', path)
+ assert response[0][0] == 'ok', 'unexpected response code %s' % (response[0],)
+ return StringIO(response[1].read_body_bytes())
+
+
class RemoteBranchFormat(branch.BranchFormat):
def open(self, a_bzrdir):
@@ -242,8 +267,10 @@
self.repository = remote_repository
if real_branch is not None:
self._real_branch = real_branch
+ # Fill out expected attributes of branch for bzrlib api users.
self._format = RemoteBranchFormat()
self.base = self.bzrdir.root_transport.base
+ self.control_files = RemoteBranchLockableFiles(self.bzrdir, self._client)
def lock_read(self):
return self._real_branch.lock_read()
=== modified file 'bzrlib/smart/branch.py'
--- a/bzrlib/smart/branch.py 2007-02-01 16:09:44 +0000
+++ b/bzrlib/smart/branch.py 2007-02-02 19:25:20 +0000
@@ -39,6 +39,20 @@
return self.do_with_branch(branch)
+class SmartServerBranchGetConfigFile(SmartServerBranchRequest):
+
+ def do_with_branch(self, branch):
+ """Return the content of branch.control_files.get('branch.conf').
+
+ The body is not utf8 decoded - its the literal bytestream from disk.
+ """
+ try:
+ content = branch.control_files.get('branch.conf').read()
+ except errors.NoSuchFile:
+ content = ''
+ return SmartServerResponse( ('ok', ), content)
+
+
class SmartServerRequestRevisionHistory(SmartServerBranchRequest):
def do_with_branch(self, branch):
=== modified file 'bzrlib/smart/request.py'
--- a/bzrlib/smart/request.py 2007-02-02 17:34:08 +0000
+++ b/bzrlib/smart/request.py 2007-02-02 19:25:20 +0000
@@ -234,6 +234,8 @@
request_handlers.register_lazy(
'append', 'bzrlib.smart.vfs', 'AppendRequest')
request_handlers.register_lazy(
+ 'Branch.get_config_file', 'bzrlib.smart.branch', 'SmartServerBranchGetConfigFile')
+request_handlers.register_lazy(
'Branch.last_revision_info', 'bzrlib.smart.branch', 'SmartServerBranchRequestLastRevisionInfo')
request_handlers.register_lazy(
'Branch.revision_history', 'bzrlib.smart.branch', 'SmartServerRequestRevisionHistory')
=== modified file 'bzrlib/tests/test_lockable_files.py'
--- a/bzrlib/tests/test_lockable_files.py 2006-10-11 23:08:27 +0000
+++ b/bzrlib/tests/test_lockable_files.py 2007-02-02 19:25:20 +0000
@@ -23,6 +23,7 @@
from bzrlib.lockable_files import LockableFiles, TransportLock
from bzrlib.lockdir import LockDir
from bzrlib.tests import TestCaseInTempDir
+from bzrlib.tests.test_smart import TestCaseWithSmartMedium
from bzrlib.tests.test_transactions import DummyWeave
from bzrlib.transactions import (PassThroughTransaction,
ReadOnlyTransaction,
@@ -175,3 +176,24 @@
# TODO: Test the lockdir inherits the right file and directory permissions
# from the LockableFiles.
+
+
+class TestLockableFiles_RemoteLockDir(TestCaseWithSmartMedium,
+ _TestLockableFiles_mixin):
+ """LockableFile tests run with RemoteLockDir on a branch."""
+
+ def setUp(self):
+ super(TestLockableFiles_RemoteLockDir, self).setUp()
+ # can only get a RemoteLockDir with some RemoteObject...
+ # use a branch as thats what we want. These mixin tests test the end
+ # to end behaviour, so stubbing out the backend and simulating would
+ # defeat the purpose. We test the protocol implementation separately
+ # in test_remote and test_smart as usual.
+ self.make_branch('foo')
+ self.transport = get_transport('.')
+ self.lockable = self.get_lockable()
+
+ def get_lockable(self):
+ # getting a new lockable involves opening a new instance of the branch
+ branch = bzrlib.branch.Branch.open(self.get_url('foo'))
+ return branch.control_files
=== modified file 'bzrlib/tests/test_remote.py'
--- a/bzrlib/tests/test_remote.py 2007-02-02 17:34:08 +0000
+++ b/bzrlib/tests/test_remote.py 2007-02-02 19:25:20 +0000
@@ -21,6 +21,8 @@
the object given a transport that supports smartserver rpc operations.
"""
+from cStringIO import StringIO
+
from bzrlib import bzrdir, remote, tests
from bzrlib.branch import Branch
from bzrlib.bzrdir import BzrDir, BzrDirFormat
@@ -83,24 +85,44 @@
self.assertIsInstance(d, BzrDir)
+class FakeProtocol(object):
+ """Lookalike SmartClientRequestProtocolOne allowing body reading tests."""
+
+ def __init__(self, body):
+ self._body_buffer = StringIO(body)
+
+ def read_body_bytes(self, count=-1):
+ return self._body_buffer.read(count)
+
+
class FakeClient(SmartClient):
"""Lookalike for SmartClient allowing testing."""
def __init__(self, responses):
# We don't call the super init because there is no medium.
+ """create a FakeClient.
+
+ :param respones: A list of response-tuple, body-data pairs to be sent
+ back to callers.
+ """
self.responses = responses
self._calls = []
def call(self, method, *args):
self._calls.append(('call', method, args))
- return self.responses.pop(0)
+ return self.responses.pop(0)[0]
+
+ def call2(self, method, *args):
+ self._calls.append(('call2', method, args))
+ result = self.responses.pop(0)
+ return result[0], FakeProtocol(result[1])
class TestBranchLastRevisionInfo(tests.TestCase):
def test_empty_branch(self):
# in an empty branch we decode the response properly
- client = FakeClient([('ok', '0', '')])
+ client = FakeClient([(('ok', '0', ''), )])
transport = MemoryTransport()
transport.mkdir('quack')
transport = transport.clone('quack')
@@ -117,7 +139,7 @@
def test_non_empty_branch(self):
# in a non-empty branch we also decode the response properly
- client = FakeClient([('ok', '2', u'\xc8'.encode('utf8'))])
+ client = FakeClient([(('ok', '2', u'\xc8'.encode('utf8')), )])
transport = MemoryTransport()
transport.mkdir('kwaak')
transport = transport.clone('kwaak')
@@ -132,6 +154,31 @@
self.assertEqual((2, u'\xc8'), result)
+class TestBranchControlGetBranchConf(tests.TestCase):
+ """Test branch.control_files api munging...
+
+ we special case RemoteBranch.control_files.get('branch.conf') to
+ call a specific API so that RemoteBranch's can intercept configuration
+ file reading, allowing them to signal to the client about things like
+ 'email is configured for commits'.
+ """
+
+ def test_get_branch_conf(self):
+ # in an empty branch we decode the response properly
+ client = FakeClient([(('ok', ), 'config file body')])
+ 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)
+ result = branch.control_files.get('branch.conf')
+ self.assertEqual(
+ [('call2', 'Branch.get_config_file', ('///quack/',))],
+ client._calls)
+ self.assertEqual('config file body', result.read())
+
+
class TestRepositoryIsShared(tests.TestCase):
def setup_fake_client_and_repository(self, responses, transport_path):
@@ -147,7 +194,7 @@
def test_is_shared(self):
# ('yes', ) for Repository.is_shared -> 'True'.
- responses = [('yes', )]
+ responses = [(('yes', ), )]
transport_path = 'quack'
repo, client = self.setup_fake_client_and_repository(
responses, transport_path)
@@ -159,7 +206,7 @@
def test_is_not_shared(self):
# ('no', ) for Repository.is_shared -> 'False'.
- responses = [('no', )]
+ responses = [(('no', ), )]
transport_path = 'qwack'
repo, client = self.setup_fake_client_and_repository(
responses, transport_path)
=== modified file 'bzrlib/tests/test_smart.py'
--- a/bzrlib/tests/test_smart.py 2007-02-02 18:04:51 +0000
+++ b/bzrlib/tests/test_smart.py 2007-02-02 19:25:20 +0000
@@ -23,6 +23,21 @@
import bzrlib.smart.repository
+class TestCaseWithSmartMedium(tests.TestCaseWithTransport):
+
+ def setUp(self):
+ super(TestCaseWithSmartMedium, self).setUp()
+ # We're allowed to set the transport class here, so that we don't use
+ # the default or a parameterized class, but rather use the
+ # TestCaseWithTransport infrastructure to set up a smart server and
+ # transport.
+ self.transport_server = smart.server.SmartTCPServer_for_testing
+
+ def get_smart_medium(self):
+ """Get a smart medium to use in tests."""
+ return self.get_transport().get_smart_medium()
+
+
class TestSmartServerResponse(tests.TestCase):
def test__eq__(self):
@@ -209,6 +224,30 @@
request.execute(backing.local_abspath('')))
+class TestSmartServerBranchGetConfigFile(tests.TestCaseWithTransport):
+
+ def test_default(self):
+ """With no file, we get empty content."""
+ backing = self.get_transport()
+ request = smart.branch.SmartServerBranchGetConfigFile(backing)
+ branch = self.make_branch('.')
+ # there should be no file by default
+ content = ''
+ self.assertEqual(SmartServerResponse(('ok', ), content),
+ request.execute(backing.local_abspath('')))
+
+ def test_with_content(self):
+ # SmartServerBranchGetConfigFile should return the content from
+ # branch.control_files.get('branch.conf') for now - in the future it may
+ # perform more complex processing.
+ backing = self.get_transport()
+ request = smart.branch.SmartServerBranchGetConfigFile(backing)
+ branch = self.make_branch('.')
+ branch.control_files.put_utf8('branch.conf', 'foo bar baz')
+ self.assertEqual(SmartServerResponse(('ok', ), 'foo bar baz'),
+ request.execute(backing.local_abspath('')))
+
+
class TestSmartServerRepositoryRequest(tests.TestCaseWithTransport):
def test_no_repository(self):
@@ -275,6 +314,9 @@
def test_registered_methods(self):
"""Test that known methods are registered to the correct object."""
self.assertEqual(
+ smart.request.request_handlers.get('Branch.get_config_file'),
+ smart.branch.SmartServerBranchGetConfigFile)
+ self.assertEqual(
smart.request.request_handlers.get('Branch.last_revision_info'),
smart.branch.SmartServerBranchRequestLastRevisionInfo)
self.assertEqual(
=== modified file 'bzrlib/tests/test_smart_transport.py'
--- a/bzrlib/tests/test_smart_transport.py 2006-11-29 03:43:35 +0000
+++ b/bzrlib/tests/test_smart_transport.py 2007-02-02 19:25:20 +0000
@@ -43,6 +43,7 @@
HTTPServerWithSmarts,
SmartRequestHandler,
)
+from bzrlib.tests.test_smart import TestCaseWithSmartMedium
from bzrlib.transport import (
get_transport,
local,
@@ -521,15 +522,7 @@
self.assertRaises(errors.ReadingCompleted, request.read_bytes, None)
-class RemoteTransportTests(tests.TestCaseWithTransport):
-
- def setUp(self):
- super(RemoteTransportTests, self).setUp()
- # We're allowed to set the transport class here, so that we don't use
- # the default or a parameterized class, but rather use the
- # TestCaseWithTransport infrastructure to set up a smart server and
- # transport.
- self.transport_server = server.SmartTCPServer_for_testing
+class RemoteTransportTests(TestCaseWithSmartMedium):
def test_plausible_url(self):
self.assert_(self.get_url().startswith('bzr://'))
More information about the bazaar-commits
mailing list