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