/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: Canonical.com Patch Queue Manager
  • Date: 2009-11-26 04:58:09 UTC
  • mfrom: (4798.4.3 fix-348025)
  • Revision ID: pqm@pqm.ubuntu.com-20091126045809-fzd0z4hll8fsgosd
(robertc) Doc improvements for bzr help urlspec. (Neil
        Martinsen-Burrell)

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