18
18
"""Tests of the bzr serve command."""
28
26
from bzrlib import (
33
31
revision as _mod_revision,
37
36
from bzrlib.branch import Branch
38
from bzrlib.bzrdir import BzrDir
37
from bzrlib.controldir import ControlDir
39
38
from bzrlib.smart import client, medium
40
from bzrlib.smart.server import BzrServerFactory, SmartTCPServer
39
from bzrlib.smart.server import (
41
43
from bzrlib.tests import (
42
44
TestCaseWithMemoryTransport,
43
45
TestCaseWithTransport,
46
from bzrlib.trace import mutter
47
47
from bzrlib.transport import remote
53
53
*func_args, **func_kwargs):
54
54
"""Run 'bzr serve', and run the given func in a thread once the server
57
57
When 'func' terminates, the server will be terminated too.
59
59
Returns stdout and stderr.
62
def on_server_start(backing_urls, tcp_server):
64
target=on_server_start_thread, args=(tcp_server,))
66
61
def on_server_start_thread(tcp_server):
62
"""This runs concurrently with the server thread.
64
The server is interrupted as soon as ``func`` finishes, even if an
65
exception is encountered.
69
69
self.tcp_server = tcp_server
72
72
func(*func_args, **func_kwargs)
73
73
except Exception, e:
74
74
# Log errors to make some test failures a little less
76
mutter('func broke: %r', e)
76
trace.mutter('func broke: %r', e)
78
78
# Then stop the server
79
mutter('interrupting...')
79
trace.mutter('interrupting...')
80
80
thread.interrupt_main()
81
# When the hook is fired, it just starts ``on_server_start_thread`` and
83
def on_server_start(backing_urls, tcp_server):
85
target=on_server_start_thread, args=(tcp_server,))
81
88
SmartTCPServer.hooks.install_named_hook(
82
89
'server_started_ex', on_server_start,
83
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
94
self.overrideAttr(SmartTCPServer, '_ACCEPT_TIMEOUT', 0.1)
84
95
# start a TCP server
86
out, err = self.run_bzr(['serve'] + list(serve_args))
97
out, err = self.run_bzr(['serve'] + list(serve_args),
87
99
except KeyboardInterrupt, e:
95
107
super(TestBzrServe, self).setUp()
96
108
self.disable_missing_extensions_warning()
110
def test_server_exception_with_hook(self):
111
"""Catch exception from the server in the server_exception hook.
113
We use ``run_bzr_serve_then_func`` without a ``func`` so the server
114
will receive a KeyboardInterrupt exception we want to catch.
117
if exception[0] is KeyboardInterrupt:
118
sys.stderr.write('catching KeyboardInterrupt\n')
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)
129
def test_server_exception_no_hook(self):
130
"""test exception without hook returns error"""
132
out, err = self.run_bzr_serve_then_func(args, retcode=3)
98
134
def assertInetServerShutsdownCleanly(self, process):
99
135
"""Shutdown the server process looking for errors."""
100
136
# Shutdown the server: the server should shut down when it cannot read
164
200
url = 'bzr://localhost:%d/' % port
165
201
self.permit_url(url)
166
202
return process, url
168
204
def test_bzr_serve_quiet(self):
169
205
self.make_branch('.')
170
args = ['--port', 'localhost:0', '--quiet']
206
args = ['--listen', 'localhost', '--port', '0', '--quiet']
171
207
out, err = self.run_bzr_serve_then_func(args, retcode=3)
172
208
self.assertEqual('', out)
173
209
self.assertEqual('', err)
225
261
# -Dhpss, and does drop some hpss logging to the file.
226
262
self.make_branch('.')
227
263
log_fname = os.getcwd() + '/server.log'
228
self._captureVar('BZR_LOG', log_fname)
264
self.overrideEnv('BZR_LOG', log_fname)
229
265
process, transport = self.start_server_inet(['-Dhpss'])
230
branch = BzrDir.open_from_transport(transport).open_branch()
266
branch = ControlDir.open_from_transport(transport).open_branch()
231
267
self.make_read_requests(branch)
232
268
self.assertInetServerShutsdownCleanly(process)
233
269
f = open(log_fname, 'rb')
236
272
self.assertContainsRe(content, r'hpss request: \[[0-9-]+\]')
274
def test_bzr_serve_supports_configurable_timeout(self):
275
gs = config.GlobalStack()
276
gs.set('serve.client_timeout', 0.2)
277
process, url = self.start_server_port()
278
self.build_tree_contents([('a_file', 'contents\n')])
279
# We can connect and issue a request
280
t = transport.get_transport_from_url(url)
281
self.assertEqual('contents\n', t.get_bytes('a_file'))
282
# However, if we just wait for more content from the server, it will
283
# eventually disconnect us.
284
# TODO: Use something like signal.alarm() so that if the server doesn't
285
# properly handle the timeout, we end up failing the test instead
286
# of hanging forever.
287
m = t.get_smart_medium()
289
# Now, we wait for timeout to trigger
290
err = process.stderr.readline()
292
'Connection Timeout: disconnecting client after 0.2 seconds\n',
294
self.assertServerFinishesCleanly(process)
296
def test_bzr_serve_supports_client_timeout(self):
297
process, url = self.start_server_port(['--client-timeout=0.1'])
298
self.build_tree_contents([('a_file', 'contents\n')])
299
# We can connect and issue a request
300
t = transport.get_transport_from_url(url)
301
self.assertEqual('contents\n', t.get_bytes('a_file'))
302
# However, if we just wait for more content from the server, it will
303
# eventually disconnect us.
304
# TODO: Use something like signal.alarm() so that if the server doesn't
305
# properly handle the timeout, we end up failing the test instead
306
# of hanging forever.
307
m = t.get_smart_medium()
309
# Now, we wait for timeout to trigger
310
err = process.stderr.readline()
312
'Connection Timeout: disconnecting client after 0.1 seconds\n',
314
self.assertServerFinishesCleanly(process)
316
def test_bzr_serve_graceful_shutdown(self):
317
big_contents = 'a'*64*1024
318
self.build_tree_contents([('bigfile', big_contents)])
319
process, url = self.start_server_port(['--client-timeout=1.0'])
320
t = transport.get_transport_from_url(url)
321
m = t.get_smart_medium()
322
c = client._SmartClient(m)
323
# Start, but don't finish a response
324
resp, response_handler = c.call_expecting_body('get', 'bigfile')
325
self.assertEqual(('ok',), resp)
326
# Note: process.send_signal is a Python 2.6ism
327
process.send_signal(signal.SIGHUP)
328
# Wait for the server to notice the signal, and then read the actual
329
# body of the response. That way we know that it is waiting for the
331
self.assertEqual('Requested to stop gracefully\n',
332
process.stderr.readline())
333
self.assertEqual('Waiting for 1 client(s) to finish\n',
334
process.stderr.readline())
335
body = response_handler.read_body_bytes()
336
if body != big_contents:
337
self.fail('Failed to properly read the contents of "bigfile"')
338
# Now that our request is finished, the medium should notice it has
340
self.assertEqual('', m.read_bytes(1))
341
# And the server should be stopping
342
self.assertEqual(0, process.wait())
239
345
class TestCmdServeChrooting(TestBzrServeBase):