/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/tests/blackbox/test_serve.py

  • Committer: Andrew Bennetts
  • Date: 2009-11-11 06:50:40 UTC
  • mto: This revision was merged to the branch mainline in revision 4793.
  • Revision ID: andrew.bennetts@canonical.com-20091111065040-ibq6r3cnza7ekyzs
Show real branch/repo format description in 'info -v' over HPSS.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2006 Canonical Ltd
 
2
#
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
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
 
16
 
 
17
 
 
18
"""Tests of the bzr serve command."""
 
19
 
 
20
import os
 
21
import os.path
 
22
import signal
 
23
import subprocess
 
24
import sys
 
25
import thread
 
26
import threading
 
27
 
 
28
from bzrlib import (
 
29
    builtins,
 
30
    errors,
 
31
    osutils,
 
32
    revision as _mod_revision,
 
33
    )
 
34
from bzrlib.branch import Branch
 
35
from bzrlib.bzrdir import BzrDir
 
36
from bzrlib.smart import client, medium
 
37
from bzrlib.smart.server import BzrServerFactory, SmartTCPServer
 
38
from bzrlib.tests import (
 
39
    ParamikoFeature,
 
40
    TestCaseWithMemoryTransport,
 
41
    TestCaseWithTransport,
 
42
    TestSkipped,
 
43
    )
 
44
from bzrlib.trace import mutter
 
45
from bzrlib.transport import get_transport, remote
 
46
 
 
47
 
 
48
class TestBzrServe(TestCaseWithTransport):
 
49
 
 
50
    def setUp(self):
 
51
        super(TestBzrServe, self).setUp()
 
52
        self.disable_missing_extensions_warning()
 
53
 
 
54
    def assertInetServerShutsdownCleanly(self, process):
 
55
        """Shutdown the server process looking for errors."""
 
56
        # Shutdown the server: the server should shut down when it cannot read
 
57
        # from stdin anymore.
 
58
        process.stdin.close()
 
59
        # Hide stdin from the subprocess module, so it won't fail to close it.
 
60
        process.stdin = None
 
61
        result = self.finish_bzr_subprocess(process)
 
62
        self.assertEqual('', result[0])
 
63
        self.assertEqual('', result[1])
 
64
 
 
65
    def assertServerFinishesCleanly(self, process):
 
66
        """Shutdown the bzr serve instance process looking for errors."""
 
67
        # Shutdown the server
 
68
        result = self.finish_bzr_subprocess(process, retcode=3,
 
69
                                            send_signal=signal.SIGINT)
 
70
        self.assertEqual('', result[0])
 
71
        self.assertEqual('bzr: interrupted\n', result[1])
 
72
 
 
73
    def make_read_requests(self, branch):
 
74
        """Do some read only requests."""
 
75
        branch.lock_read()
 
76
        try:
 
77
            branch.repository.all_revision_ids()
 
78
            self.assertEqual(_mod_revision.NULL_REVISION,
 
79
                             _mod_revision.ensure_null(branch.last_revision()))
 
80
        finally:
 
81
            branch.unlock()
 
82
 
 
83
    def start_server_inet(self, extra_options=()):
 
84
        """Start a bzr server subprocess using the --inet option.
 
85
 
 
86
        :param extra_options: extra options to give the server.
 
87
        :return: a tuple with the bzr process handle for passing to
 
88
            finish_bzr_subprocess, a client for the server, and a transport.
 
89
        """
 
90
        # Serve from the current directory
 
91
        process = self.start_bzr_subprocess(['serve', '--inet'])
 
92
 
 
93
        # Connect to the server
 
94
        # We use this url because while this is no valid URL to connect to this
 
95
        # server instance, the transport needs a URL.
 
96
        url = 'bzr://localhost/'
 
97
        self.permit_url(url)
 
98
        client_medium = medium.SmartSimplePipesClientMedium(
 
99
            process.stdout, process.stdin, url)
 
100
        transport = remote.RemoteTransport(url, medium=client_medium)
 
101
        return process, transport
 
102
 
 
103
    def start_server_port(self, extra_options=()):
 
104
        """Start a bzr server subprocess.
 
105
 
 
106
        :param extra_options: extra options to give the server.
 
107
        :return: a tuple with the bzr process handle for passing to
 
108
            finish_bzr_subprocess, and the base url for the server.
 
109
        """
 
110
        # Serve from the current directory
 
111
        args = ['serve', '--port', 'localhost:0']
 
112
        args.extend(extra_options)
 
113
        process = self.start_bzr_subprocess(args, skip_if_plan_to_signal=True)
 
114
        port_line = process.stderr.readline()
 
115
        prefix = 'listening on port: '
 
116
        self.assertStartsWith(port_line, prefix)
 
117
        port = int(port_line[len(prefix):])
 
118
        url = 'bzr://localhost:%d/' % port
 
119
        self.permit_url(url)
 
120
        return process, url
 
121
 
 
122
    def test_bzr_serve_inet_readonly(self):
 
123
        """bzr server should provide a read only filesystem by default."""
 
124
        process, transport = self.start_server_inet()
 
125
        self.assertRaises(errors.TransportNotPossible, transport.mkdir, 'adir')
 
126
        self.assertInetServerShutsdownCleanly(process)
 
127
 
 
128
    def test_bzr_serve_inet_readwrite(self):
 
129
        # Make a branch
 
130
        self.make_branch('.')
 
131
 
 
132
        process, transport = self.start_server_inet(['--allow-writes'])
 
133
 
 
134
        # We get a working branch
 
135
        branch = BzrDir.open_from_transport(transport).open_branch()
 
136
        self.make_read_requests(branch)
 
137
        self.assertInetServerShutsdownCleanly(process)
 
138
 
 
139
    def test_bzr_serve_port_readonly(self):
 
140
        """bzr server should provide a read only filesystem by default."""
 
141
        process, url = self.start_server_port()
 
142
        transport = get_transport(url)
 
143
        self.assertRaises(errors.TransportNotPossible, transport.mkdir, 'adir')
 
144
        self.assertServerFinishesCleanly(process)
 
145
 
 
146
    def test_bzr_serve_port_readwrite(self):
 
147
        # Make a branch
 
148
        self.make_branch('.')
 
149
 
 
150
        process, url = self.start_server_port(['--allow-writes'])
 
151
 
 
152
        # Connect to the server
 
153
        branch = Branch.open(url)
 
154
        self.make_read_requests(branch)
 
155
        self.assertServerFinishesCleanly(process)
 
156
 
 
157
    def test_bzr_serve_supports_protocol(self):
 
158
        # Make a branch
 
159
        self.make_branch('.')
 
160
 
 
161
        process, url = self.start_server_port(['--allow-writes',
 
162
                                               '--protocol=bzr'])
 
163
 
 
164
        # Connect to the server
 
165
        branch = Branch.open(url)
 
166
        self.make_read_requests(branch)
 
167
        self.assertServerFinishesCleanly(process)
 
168
 
 
169
 
 
170
class TestCmdServeChrooting(TestCaseWithTransport):
 
171
 
 
172
    def test_serve_tcp(self):
 
173
        """'bzr serve' wraps the given --directory in a ChrootServer.
 
174
 
 
175
        So requests that search up through the parent directories (like
 
176
        find_repositoryV3) will give "not found" responses, rather than
 
177
        InvalidURLJoin or jail break errors.
 
178
        """
 
179
        t = self.get_transport()
 
180
        t.mkdir('server-root')
 
181
        self.run_bzr_serve_then_func(
 
182
            ['--port', '127.0.0.1:0',
 
183
             '--directory', t.local_abspath('server-root'),
 
184
             '--allow-writes'],
 
185
            self.when_server_started)
 
186
        # The when_server_started method issued a find_repositoryV3 that should
 
187
        # fail with 'norepository' because there are no repositories inside the
 
188
        # --directory.
 
189
        self.assertEqual(('norepository',), self.client_resp)
 
190
 
 
191
    def run_bzr_serve_then_func(self, serve_args, func, *func_args,
 
192
            **func_kwargs):
 
193
        """Run 'bzr serve', and run the given func in a thread once the server
 
194
        has started.
 
195
        
 
196
        When 'func' terminates, the server will be terminated too.
 
197
        """
 
198
        # install hook
 
199
        def on_server_start(backing_urls, tcp_server):
 
200
            t = threading.Thread(
 
201
                target=on_server_start_thread, args=(tcp_server,))
 
202
            t.start()
 
203
        def on_server_start_thread(tcp_server):
 
204
            try:
 
205
                # Run func
 
206
                self.tcp_server = tcp_server
 
207
                try:
 
208
                    func(*func_args, **func_kwargs)
 
209
                except Exception, e:
 
210
                    # Log errors to make some test failures a little less
 
211
                    # mysterious.
 
212
                    mutter('func broke: %r', e)
 
213
            finally:
 
214
                # Then stop the server
 
215
                mutter('interrupting...')
 
216
                thread.interrupt_main()
 
217
        SmartTCPServer.hooks.install_named_hook(
 
218
            'server_started_ex', on_server_start,
 
219
            'run_bzr_serve_then_func hook')
 
220
        # start a TCP server
 
221
        try:
 
222
            self.run_bzr(['serve'] + list(serve_args))
 
223
        except KeyboardInterrupt:
 
224
            pass
 
225
 
 
226
    def when_server_started(self):
 
227
        # Connect to the TCP server and issue some requests and see what comes
 
228
        # back.
 
229
        client_medium = medium.SmartTCPClientMedium(
 
230
            '127.0.0.1', self.tcp_server.port,
 
231
            'bzr://localhost:%d/' % (self.tcp_server.port,))
 
232
        smart_client = client._SmartClient(client_medium)
 
233
        resp = smart_client.call('mkdir', 'foo', '')
 
234
        resp = smart_client.call('BzrDirFormat.initialize', 'foo/')
 
235
        try:
 
236
            resp = smart_client.call('BzrDir.find_repositoryV3', 'foo/')
 
237
        except errors.ErrorFromSmartServer, e:
 
238
            resp = e.error_tuple
 
239
        self.client_resp = resp
 
240
        client_medium.disconnect()
 
241
 
 
242
 
 
243
class TestUserdirExpansion(TestCaseWithMemoryTransport):
 
244
 
 
245
    def fake_expanduser(self, path):
 
246
        """A simple, environment-independent, function for the duration of this
 
247
        test.
 
248
 
 
249
        Paths starting with a path segment of '~user' will expand to start with
 
250
        '/home/user/'.  Every other path will be unchanged.
 
251
        """
 
252
        if path.split('/', 1)[0] == '~user':
 
253
            return '/home/user' + path[len('~user'):]
 
254
        return path
 
255
 
 
256
    def make_test_server(self, base_path='/'):
 
257
        """Make and setUp a BzrServerFactory, backed by a memory transport, and
 
258
        creat '/home/user' in that transport.
 
259
        """
 
260
        bzr_server = BzrServerFactory(
 
261
            self.fake_expanduser, lambda t: base_path)
 
262
        mem_transport = self.get_transport()
 
263
        mem_transport.mkdir_multi(['home', 'home/user'])
 
264
        bzr_server.set_up(mem_transport, None, None, inet=True)
 
265
        self.addCleanup(bzr_server.tear_down)
 
266
        return bzr_server
 
267
 
 
268
    def test_bzr_serve_expands_userdir(self):
 
269
        bzr_server = self.make_test_server()
 
270
        self.assertTrue(bzr_server.smart_server.backing_transport.has('~user'))
 
271
 
 
272
    def test_bzr_serve_does_not_expand_userdir_outside_base(self):
 
273
        bzr_server = self.make_test_server('/foo')
 
274
        self.assertFalse(bzr_server.smart_server.backing_transport.has('~user'))
 
275
 
 
276
    def test_get_base_path(self):
 
277
        """cmd_serve will turn the --directory option into a LocalTransport
 
278
        (optionally decorated with 'readonly+').  BzrServerFactory can
 
279
        determine the original --directory from that transport.
 
280
        """
 
281
        # Define a fake 'protocol' to capture the transport that cmd_serve
 
282
        # passes to serve_bzr.
 
283
        def capture_transport(transport, host, port, inet):
 
284
            self.bzr_serve_transport = transport
 
285
        cmd = builtins.cmd_serve()
 
286
        # Read-only
 
287
        cmd.run(directory='/a/b/c', protocol=capture_transport)
 
288
        server_maker = BzrServerFactory()
 
289
        self.assertEqual(
 
290
            'readonly+file:///a/b/c/', self.bzr_serve_transport.base)
 
291
        self.assertEqual(
 
292
            u'/a/b/c/', server_maker.get_base_path(self.bzr_serve_transport))
 
293
        # Read-write
 
294
        cmd.run(directory='/a/b/c', protocol=capture_transport,
 
295
            allow_writes=True)
 
296
        server_maker = BzrServerFactory()
 
297
        self.assertEqual('file:///a/b/c/', self.bzr_serve_transport.base)
 
298
        self.assertEqual(
 
299
            u'/a/b/c/', server_maker.get_base_path(self.bzr_serve_transport))
 
300