18
18
"""Tests of the bzr serve command."""
27
28
from bzrlib import (
32
33
revision as _mod_revision,
37
36
from bzrlib.branch import Branch
38
37
from bzrlib.bzrdir import BzrDir
39
38
from bzrlib.smart import client, medium
40
from bzrlib.smart.server import (
39
from bzrlib.smart.server import BzrServerFactory, SmartTCPServer
44
40
from bzrlib.tests import (
45
41
TestCaseWithMemoryTransport,
46
42
TestCaseWithTransport,
48
from bzrlib.transport import remote
45
from bzrlib.trace import mutter
46
from bzrlib.transport import get_transport, remote
51
49
class TestBzrServeBase(TestCaseWithTransport):
54
52
*func_args, **func_kwargs):
55
53
"""Run 'bzr serve', and run the given func in a thread once the server
58
56
When 'func' terminates, the server will be terminated too.
60
58
Returns stdout and stderr.
61
def on_server_start(backing_urls, tcp_server):
63
target=on_server_start_thread, args=(tcp_server,))
62
65
def on_server_start_thread(tcp_server):
63
"""This runs concurrently with the server thread.
65
The server is interrupted as soon as ``func`` finishes, even if an
66
exception is encountered.
70
68
self.tcp_server = tcp_server
73
71
func(*func_args, **func_kwargs)
74
72
except Exception, e:
75
73
# Log errors to make some test failures a little less
77
trace.mutter('func broke: %r', e)
75
mutter('func broke: %r', e)
79
77
# Then stop the server
80
trace.mutter('interrupting...')
78
mutter('interrupting...')
81
79
thread.interrupt_main()
82
# When the hook is fired, it just starts ``on_server_start_thread`` and
84
def on_server_start(backing_urls, tcp_server):
86
target=on_server_start_thread, args=(tcp_server,))
89
80
SmartTCPServer.hooks.install_named_hook(
90
81
'server_started_ex', on_server_start,
91
82
'run_bzr_serve_then_func hook')
92
# It seesm thread.interrupt_main() will not raise KeyboardInterrupt
93
# until after socket.accept returns. So we set the timeout low to make
95
self.overrideAttr(SmartTCPServer, '_ACCEPT_TIMEOUT', 0.1)
96
83
# start a TCP server
98
out, err = self.run_bzr(['serve'] + list(serve_args),
85
out, err = self.run_bzr(['serve'] + list(serve_args))
100
86
except KeyboardInterrupt, e:
108
94
super(TestBzrServe, self).setUp()
109
95
self.disable_missing_extensions_warning()
111
def test_server_exception_with_hook(self):
112
"""Catch exception from the server in the server_exception hook.
114
We use ``run_bzr_serve_then_func`` without a ``func`` so the server
115
will receive a KeyboardInterrupt exception we want to catch.
118
if exception[0] is KeyboardInterrupt:
119
sys.stderr.write('catching KeyboardInterrupt\n')
123
SmartTCPServer.hooks.install_named_hook(
124
'server_exception', hook,
125
'test_server_except_hook hook')
126
args = ['--port', 'localhost:0', '--quiet']
127
out, err = self.run_bzr_serve_then_func(args, retcode=0)
128
self.assertEqual('catching KeyboardInterrupt\n', err)
130
def test_server_exception_no_hook(self):
131
"""test exception without hook returns error"""
133
out, err = self.run_bzr_serve_then_func(args, retcode=3)
135
97
def assertInetServerShutsdownCleanly(self, process):
136
98
"""Shutdown the server process looking for errors."""
137
99
# Shutdown the server: the server should shut down when it cannot read
230
192
def test_bzr_serve_port_readonly(self):
231
193
"""bzr server should provide a read only filesystem by default."""
232
194
process, url = self.start_server_port()
233
t = transport.get_transport_from_url(url)
234
self.assertRaises(errors.TransportNotPossible, t.mkdir, 'adir')
195
transport = get_transport(url)
196
self.assertRaises(errors.TransportNotPossible, transport.mkdir, 'adir')
235
197
self.assertServerFinishesCleanly(process)
237
199
def test_bzr_serve_port_readwrite(self):
273
235
self.assertContainsRe(content, r'hpss request: \[[0-9-]+\]')
275
def test_bzr_serve_supports_configurable_timeout(self):
276
gs = config.GlobalStack()
277
gs.set('serve.client_timeout', 0.2)
278
process, url = self.start_server_port()
279
self.build_tree_contents([('a_file', 'contents\n')])
280
# We can connect and issue a request
281
t = transport.get_transport_from_url(url)
282
self.assertEqual('contents\n', t.get_bytes('a_file'))
283
# However, if we just wait for more content from the server, it will
284
# eventually disconnect us.
285
# TODO: Use something like signal.alarm() so that if the server doesn't
286
# properly handle the timeout, we end up failing the test instead
287
# of hanging forever.
288
m = t.get_smart_medium()
290
# Now, we wait for timeout to trigger
291
err = process.stderr.readline()
293
'Connection Timeout: disconnecting client after 0.2 seconds\n',
295
self.assertServerFinishesCleanly(process)
297
def test_bzr_serve_supports_client_timeout(self):
298
process, url = self.start_server_port(['--client-timeout=0.1'])
299
self.build_tree_contents([('a_file', 'contents\n')])
300
# We can connect and issue a request
301
t = transport.get_transport_from_url(url)
302
self.assertEqual('contents\n', t.get_bytes('a_file'))
303
# However, if we just wait for more content from the server, it will
304
# eventually disconnect us.
305
# TODO: Use something like signal.alarm() so that if the server doesn't
306
# properly handle the timeout, we end up failing the test instead
307
# of hanging forever.
308
m = t.get_smart_medium()
310
# Now, we wait for timeout to trigger
311
err = process.stderr.readline()
313
'Connection Timeout: disconnecting client after 0.1 seconds\n',
315
self.assertServerFinishesCleanly(process)
317
def test_bzr_serve_graceful_shutdown(self):
318
big_contents = 'a'*64*1024
319
self.build_tree_contents([('bigfile', big_contents)])
320
process, url = self.start_server_port(['--client-timeout=1.0'])
321
t = transport.get_transport_from_url(url)
322
m = t.get_smart_medium()
323
c = client._SmartClient(m)
324
# Start, but don't finish a response
325
resp, response_handler = c.call_expecting_body('get', 'bigfile')
326
self.assertEqual(('ok',), resp)
327
# Note: process.send_signal is a Python 2.6ism
328
process.send_signal(signal.SIGHUP)
329
# Wait for the server to notice the signal, and then read the actual
330
# body of the response. That way we know that it is waiting for the
332
self.assertEqual('Requested to stop gracefully\n',
333
process.stderr.readline())
334
self.assertEqual('Waiting for 1 client(s) to finish\n',
335
process.stderr.readline())
336
body = response_handler.read_body_bytes()
337
if body != big_contents:
338
self.fail('Failed to properly read the contents of "bigfile"')
339
# Now that our request is finished, the medium should notice it has
341
self.assertEqual('', m.read_bytes(1))
342
# And the server should be stopping
343
self.assertEqual(0, process.wait())
346
238
class TestCmdServeChrooting(TestBzrServeBase):