~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

(vila) Revise legal option names to be less drastic. (Vincent Ladeuil)

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
 
20
20
import os
21
21
import signal
 
22
import sys
22
23
import thread
23
24
import threading
24
25
 
25
26
from bzrlib import (
26
27
    builtins,
 
28
    config,
27
29
    errors,
28
30
    osutils,
29
31
    revision as _mod_revision,
 
32
    trace,
30
33
    transport,
31
34
    urlutils,
32
35
    )
33
36
from bzrlib.branch import Branch
34
 
from bzrlib.bzrdir import BzrDir
 
37
from bzrlib.controldir import ControlDir
35
38
from bzrlib.smart import client, medium
36
39
from bzrlib.smart.server import (
37
40
    BzrServerFactory,
41
44
    TestCaseWithMemoryTransport,
42
45
    TestCaseWithTransport,
43
46
    )
44
 
from bzrlib.trace import mutter
45
47
from bzrlib.transport import remote
46
48
 
47
49
 
56
58
 
57
59
        Returns stdout and stderr.
58
60
        """
59
 
        # install hook
60
 
        def on_server_start(backing_urls, tcp_server):
61
 
            t = threading.Thread(
62
 
                target=on_server_start_thread, args=(tcp_server,))
63
 
            t.start()
64
61
        def on_server_start_thread(tcp_server):
 
62
            """This runs concurrently with the server thread.
 
63
 
 
64
            The server is interrupted as soon as ``func`` finishes, even if an
 
65
            exception is encountered.
 
66
            """
65
67
            try:
66
68
                # Run func if set
67
69
                self.tcp_server = tcp_server
68
 
                if not func is None:
 
70
                if func is not None:
69
71
                    try:
70
72
                        func(*func_args, **func_kwargs)
71
73
                    except Exception, e:
72
74
                        # Log errors to make some test failures a little less
73
75
                        # mysterious.
74
 
                        mutter('func broke: %r', e)
 
76
                        trace.mutter('func broke: %r', e)
75
77
            finally:
76
78
                # Then stop the server
77
 
                mutter('interrupting...')
 
79
                trace.mutter('interrupting...')
78
80
                thread.interrupt_main()
 
81
        # When the hook is fired, it just starts ``on_server_start_thread`` and
 
82
        # return
 
83
        def on_server_start(backing_urls, tcp_server):
 
84
            t = threading.Thread(
 
85
                target=on_server_start_thread, args=(tcp_server,))
 
86
            t.start()
 
87
        # install hook
79
88
        SmartTCPServer.hooks.install_named_hook(
80
89
            'server_started_ex', on_server_start,
81
90
            'run_bzr_serve_then_func hook')
 
91
        # It seesm thread.interrupt_main() will not raise KeyboardInterrupt
 
92
        # until after socket.accept returns. So we set the timeout low to make
 
93
        # the test faster.
 
94
        self.overrideAttr(SmartTCPServer, '_ACCEPT_TIMEOUT', 0.1)
82
95
        # start a TCP server
83
96
        try:
84
 
            out, err = self.run_bzr(['serve'] + list(serve_args))
 
97
            out, err = self.run_bzr(['serve'] + list(serve_args),
 
98
                                    retcode=retcode)
85
99
        except KeyboardInterrupt, e:
86
100
            out, err = e.args
87
101
        return out, err
93
107
        super(TestBzrServe, self).setUp()
94
108
        self.disable_missing_extensions_warning()
95
109
 
 
110
    def test_server_exception_with_hook(self):
 
111
        """Catch exception from the server in the server_exception hook.
 
112
 
 
113
        We use ``run_bzr_serve_then_func`` without a ``func`` so the server
 
114
        will receive a KeyboardInterrupt exception we want to catch.
 
115
        """
 
116
        def hook(exception):
 
117
            if exception[0] is KeyboardInterrupt:
 
118
                sys.stderr.write('catching KeyboardInterrupt\n')
 
119
                return True
 
120
            else:
 
121
                return False
 
122
        SmartTCPServer.hooks.install_named_hook(
 
123
            'server_exception', hook,
 
124
            'test_server_except_hook hook')
 
125
        args = ['--listen', 'localhost', '--port', '0', '--quiet']
 
126
        out, err = self.run_bzr_serve_then_func(args, retcode=0)
 
127
        self.assertEqual('catching KeyboardInterrupt\n', err)
 
128
 
 
129
    def test_server_exception_no_hook(self):
 
130
        """test exception without hook returns error"""
 
131
        args = []
 
132
        out, err = self.run_bzr_serve_then_func(args, retcode=3)
 
133
 
96
134
    def assertInetServerShutsdownCleanly(self, process):
97
135
        """Shutdown the server process looking for errors."""
98
136
        # Shutdown the server: the server should shut down when it cannot read
152
190
            finish_bzr_subprocess, and the base url for the server.
153
191
        """
154
192
        # Serve from the current directory
155
 
        args = ['serve', '--port', 'localhost:0']
 
193
        args = ['serve', '--listen', 'localhost', '--port', '0']
156
194
        args.extend(extra_options)
157
195
        process = self.start_bzr_subprocess(args, skip_if_plan_to_signal=True)
158
196
        port_line = process.stderr.readline()
165
203
 
166
204
    def test_bzr_serve_quiet(self):
167
205
        self.make_branch('.')
168
 
        args = ['--port', 'localhost:0', '--quiet']
 
206
        args = ['--listen', 'localhost', '--port', '0', '--quiet']
169
207
        out, err = self.run_bzr_serve_then_func(args, retcode=3)
170
208
        self.assertEqual('', out)
171
209
        self.assertEqual('', err)
183
221
        process, transport = self.start_server_inet(['--allow-writes'])
184
222
 
185
223
        # We get a working branch, and can create a directory
186
 
        branch = BzrDir.open_from_transport(transport).open_branch()
 
224
        branch = ControlDir.open_from_transport(transport).open_branch()
187
225
        self.make_read_requests(branch)
188
226
        transport.mkdir('adir')
189
227
        self.assertInetServerShutsdownCleanly(process)
191
229
    def test_bzr_serve_port_readonly(self):
192
230
        """bzr server should provide a read only filesystem by default."""
193
231
        process, url = self.start_server_port()
194
 
        t = transport.get_transport(url)
 
232
        t = transport.get_transport_from_url(url)
195
233
        self.assertRaises(errors.TransportNotPossible, t.mkdir, 'adir')
196
234
        self.assertServerFinishesCleanly(process)
197
235
 
225
263
        log_fname = os.getcwd() + '/server.log'
226
264
        self.overrideEnv('BZR_LOG', log_fname)
227
265
        process, transport = self.start_server_inet(['-Dhpss'])
228
 
        branch = BzrDir.open_from_transport(transport).open_branch()
 
266
        branch = ControlDir.open_from_transport(transport).open_branch()
229
267
        self.make_read_requests(branch)
230
268
        self.assertInetServerShutsdownCleanly(process)
231
269
        f = open(log_fname, 'rb')
233
271
        f.close()
234
272
        self.assertContainsRe(content, r'hpss request: \[[0-9-]+\]')
235
273
 
 
274
    def test_bzr_serve_supports_configurable_timeout(self):
 
275
        gs = config.GlobalStack()
 
276
        gs.set('serve.client_timeout', 0.2)
 
277
        # Save the config as the subprocess will use it
 
278
        gs.store.save()
 
279
        process, url = self.start_server_port()
 
280
        self.build_tree_contents([('a_file', 'contents\n')])
 
281
        # We can connect and issue a request
 
282
        t = transport.get_transport_from_url(url)
 
283
        self.assertEqual('contents\n', t.get_bytes('a_file'))
 
284
        # However, if we just wait for more content from the server, it will
 
285
        # eventually disconnect us.
 
286
        m = t.get_smart_medium()
 
287
        m.read_bytes(1)
 
288
        # Now, we wait for timeout to trigger
 
289
        err = process.stderr.readline()
 
290
        self.assertEqual(
 
291
            'Connection Timeout: disconnecting client after 0.2 seconds\n',
 
292
            err)
 
293
        self.assertServerFinishesCleanly(process)
 
294
 
 
295
    def test_bzr_serve_supports_client_timeout(self):
 
296
        process, url = self.start_server_port(['--client-timeout=0.1'])
 
297
        self.build_tree_contents([('a_file', 'contents\n')])
 
298
        # We can connect and issue a request
 
299
        t = transport.get_transport_from_url(url)
 
300
        self.assertEqual('contents\n', t.get_bytes('a_file'))
 
301
        # However, if we just wait for more content from the server, it will
 
302
        # eventually disconnect us.
 
303
        # TODO: Use something like signal.alarm() so that if the server doesn't
 
304
        #       properly handle the timeout, we end up failing the test instead
 
305
        #       of hanging forever.
 
306
        m = t.get_smart_medium()
 
307
        m.read_bytes(1)
 
308
        # Now, we wait for timeout to trigger
 
309
        err = process.stderr.readline()
 
310
        self.assertEqual(
 
311
            'Connection Timeout: disconnecting client after 0.1 seconds\n',
 
312
            err)
 
313
        self.assertServerFinishesCleanly(process)
 
314
 
 
315
    def test_bzr_serve_graceful_shutdown(self):
 
316
        big_contents = 'a'*64*1024
 
317
        self.build_tree_contents([('bigfile', big_contents)])
 
318
        process, url = self.start_server_port(['--client-timeout=1.0'])
 
319
        t = transport.get_transport_from_url(url)
 
320
        m = t.get_smart_medium()
 
321
        c = client._SmartClient(m)
 
322
        # Start, but don't finish a response
 
323
        resp, response_handler = c.call_expecting_body('get', 'bigfile')
 
324
        self.assertEqual(('ok',), resp)
 
325
        # Note: process.send_signal is a Python 2.6ism
 
326
        process.send_signal(signal.SIGHUP)
 
327
        # Wait for the server to notice the signal, and then read the actual
 
328
        # body of the response. That way we know that it is waiting for the
 
329
        # request to finish
 
330
        self.assertEqual('Requested to stop gracefully\n',
 
331
                         process.stderr.readline())
 
332
        self.assertEqual('Waiting for 1 client(s) to finish\n',
 
333
                         process.stderr.readline())
 
334
        body = response_handler.read_body_bytes()
 
335
        if body != big_contents:
 
336
            self.fail('Failed to properly read the contents of "bigfile"')
 
337
        # Now that our request is finished, the medium should notice it has
 
338
        # been disconnected.
 
339
        self.assertEqual('', m.read_bytes(1))
 
340
        # And the server should be stopping
 
341
        self.assertEqual(0, process.wait())
 
342
 
236
343
 
237
344
class TestCmdServeChrooting(TestBzrServeBase):
238
345
 
246
353
        t = self.get_transport()
247
354
        t.mkdir('server-root')
248
355
        self.run_bzr_serve_then_func(
249
 
            ['--port', '127.0.0.1:0',
 
356
            ['--listen', '127.0.0.1', '--port', '0',
250
357
             '--directory', t.local_abspath('server-root'),
251
358
             '--allow-writes'],
252
359
            func=self.when_server_started)
274
381
 
275
382
class TestUserdirExpansion(TestCaseWithMemoryTransport):
276
383
 
277
 
    def fake_expanduser(self, path):
 
384
    @staticmethod
 
385
    def fake_expanduser(path):
278
386
        """A simple, environment-independent, function for the duration of this
279
387
        test.
280
388
 
293
401
            self.fake_expanduser, lambda t: base_path)
294
402
        mem_transport = self.get_transport()
295
403
        mem_transport.mkdir_multi(['home', 'home/user'])
296
 
        bzr_server.set_up(mem_transport, None, None, inet=True)
 
404
        bzr_server.set_up(mem_transport, None, None, inet=True, timeout=4.0)
297
405
        self.addCleanup(bzr_server.tear_down)
298
406
        return bzr_server
299
407
 
315
423
        base_url = urlutils.local_path_to_url(base_dir) + '/'
316
424
        # Define a fake 'protocol' to capture the transport that cmd_serve
317
425
        # passes to serve_bzr.
318
 
        def capture_transport(transport, host, port, inet):
 
426
        def capture_transport(transport, host, port, inet, timeout):
319
427
            self.bzr_serve_transport = transport
320
428
        cmd = builtins.cmd_serve()
321
429
        # Read-only
332
440
        self.assertEqual(base_url, self.bzr_serve_transport.base)
333
441
        self.assertEqual(base_dir,
334
442
            server_maker.get_base_path(self.bzr_serve_transport))
 
443
        # Read-only, from a URL
 
444
        cmd.run(directory=base_url, protocol=capture_transport)
 
445
        server_maker = BzrServerFactory()
 
446
        self.assertEqual(
 
447
            'readonly+%s' % base_url, self.bzr_serve_transport.base)
 
448
        self.assertEqual(
 
449
            base_dir, server_maker.get_base_path(self.bzr_serve_transport))