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