18
18
"""Tests of the bzr serve command."""
28
26
from bzrlib import (
33
31
revision as _mod_revision,
36
36
from bzrlib.branch import Branch
37
from bzrlib.bzrdir import BzrDir
37
from bzrlib.controldir import ControlDir
38
38
from bzrlib.smart import client, medium
39
from bzrlib.smart.server import BzrServerFactory, SmartTCPServer
39
from bzrlib.smart.server import (
40
43
from bzrlib.tests import (
41
44
TestCaseWithMemoryTransport,
42
45
TestCaseWithTransport,
45
from bzrlib.trace import mutter
46
from bzrlib.transport import get_transport, remote
47
from bzrlib.transport import remote
49
50
class TestBzrServeBase(TestCaseWithTransport):
52
53
*func_args, **func_kwargs):
53
54
"""Run 'bzr serve', and run the given func in a thread once the server
56
57
When 'func' terminates, the server will be terminated too.
58
59
Returns stdout and stderr.
61
def on_server_start(backing_urls, tcp_server):
63
target=on_server_start_thread, args=(tcp_server,))
65
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.
68
69
self.tcp_server = tcp_server
71
72
func(*func_args, **func_kwargs)
72
73
except Exception, e:
73
74
# Log errors to make some test failures a little less
75
mutter('func broke: %r', e)
76
trace.mutter('func broke: %r', e)
77
78
# Then stop the server
78
mutter('interrupting...')
79
trace.mutter('interrupting...')
79
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,))
80
88
SmartTCPServer.hooks.install_named_hook(
81
89
'server_started_ex', on_server_start,
82
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)
83
95
# start a TCP server
85
out, err = self.run_bzr(['serve'] + list(serve_args))
97
out, err = self.run_bzr(['serve'] + list(serve_args),
86
99
except KeyboardInterrupt, e:
94
107
super(TestBzrServe, self).setUp()
95
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)
97
134
def assertInetServerShutsdownCleanly(self, process):
98
135
"""Shutdown the server process looking for errors."""
99
136
# Shutdown the server: the server should shut down when it cannot read
163
200
url = 'bzr://localhost:%d/' % port
164
201
self.permit_url(url)
165
202
return process, url
167
204
def test_bzr_serve_quiet(self):
168
205
self.make_branch('.')
169
args = ['--port', 'localhost:0', '--quiet']
206
args = ['--listen', 'localhost', '--port', '0', '--quiet']
170
207
out, err = self.run_bzr_serve_then_func(args, retcode=3)
171
208
self.assertEqual('', out)
172
209
self.assertEqual('', err)
192
229
def test_bzr_serve_port_readonly(self):
193
230
"""bzr server should provide a read only filesystem by default."""
194
231
process, url = self.start_server_port()
195
transport = get_transport(url)
196
self.assertRaises(errors.TransportNotPossible, transport.mkdir, 'adir')
232
t = transport.get_transport_from_url(url)
233
self.assertRaises(errors.TransportNotPossible, t.mkdir, 'adir')
197
234
self.assertServerFinishesCleanly(process)
199
236
def test_bzr_serve_port_readwrite(self):
224
261
# -Dhpss, and does drop some hpss logging to the file.
225
262
self.make_branch('.')
226
263
log_fname = os.getcwd() + '/server.log'
227
self._captureVar('BZR_LOG', log_fname)
264
self.overrideEnv('BZR_LOG', log_fname)
228
265
process, transport = self.start_server_inet(['-Dhpss'])
229
branch = BzrDir.open_from_transport(transport).open_branch()
266
branch = ControlDir.open_from_transport(transport).open_branch()
230
267
self.make_read_requests(branch)
231
268
self.assertInetServerShutsdownCleanly(process)
232
269
f = open(log_fname, 'rb')
235
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())
238
345
class TestCmdServeChrooting(TestBzrServeBase):