18
18
"""Tests of the bzr serve command."""
28
27
from bzrlib import (
33
32
revision as _mod_revision,
36
37
from bzrlib.branch import Branch
37
38
from bzrlib.bzrdir import BzrDir
38
39
from bzrlib.smart import client, medium
39
from bzrlib.smart.server import BzrServerFactory, SmartTCPServer
40
from bzrlib.smart.server import (
40
44
from bzrlib.tests import (
41
45
TestCaseWithMemoryTransport,
42
46
TestCaseWithTransport,
45
from bzrlib.trace import mutter
46
from bzrlib.transport import get_transport, remote
48
from bzrlib.transport import remote
49
51
class TestBzrServeBase(TestCaseWithTransport):
52
54
*func_args, **func_kwargs):
53
55
"""Run 'bzr serve', and run the given func in a thread once the server
56
58
When 'func' terminates, the server will be terminated too.
58
60
Returns stdout and stderr.
61
def on_server_start(backing_urls, tcp_server):
63
target=on_server_start_thread, args=(tcp_server,))
65
62
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.
68
70
self.tcp_server = tcp_server
71
73
func(*func_args, **func_kwargs)
72
74
except Exception, e:
73
75
# Log errors to make some test failures a little less
75
mutter('func broke: %r', e)
77
trace.mutter('func broke: %r', e)
77
79
# Then stop the server
78
mutter('interrupting...')
80
trace.mutter('interrupting...')
79
81
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,))
80
89
SmartTCPServer.hooks.install_named_hook(
81
90
'server_started_ex', on_server_start,
82
91
'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)
83
96
# start a TCP server
85
out, err = self.run_bzr(['serve'] + list(serve_args))
98
out, err = self.run_bzr(['serve'] + list(serve_args),
86
100
except KeyboardInterrupt, e:
94
108
super(TestBzrServe, self).setUp()
95
109
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)
97
135
def assertInetServerShutsdownCleanly(self, process):
98
136
"""Shutdown the server process looking for errors."""
99
137
# Shutdown the server: the server should shut down when it cannot read
192
230
def test_bzr_serve_port_readonly(self):
193
231
"""bzr server should provide a read only filesystem by default."""
194
232
process, url = self.start_server_port()
195
transport = get_transport(url)
196
self.assertRaises(errors.TransportNotPossible, transport.mkdir, 'adir')
233
t = transport.get_transport_from_url(url)
234
self.assertRaises(errors.TransportNotPossible, t.mkdir, 'adir')
197
235
self.assertServerFinishesCleanly(process)
199
237
def test_bzr_serve_port_readwrite(self):
224
262
# -Dhpss, and does drop some hpss logging to the file.
225
263
self.make_branch('.')
226
264
log_fname = os.getcwd() + '/server.log'
227
self._captureVar('BZR_LOG', log_fname)
265
self.overrideEnv('BZR_LOG', log_fname)
228
266
process, transport = self.start_server_inet(['-Dhpss'])
229
267
branch = BzrDir.open_from_transport(transport).open_branch()
230
268
self.make_read_requests(branch)
235
273
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
self.build_tree_contents([('a_file', 'contents\n')])
319
process, url = self.start_server_port(['--client-timeout=1.0'])
320
# TODO: I would like to test this by having a large data set that we
321
# want to finish reading before exiting. So the server should be
322
# blocked but not accepting new connections after SIGHUP.
323
t = transport.get_transport_from_url(url)
324
self.assertEqual('contents\n', t.get_bytes('a_file'))
325
# Note: process.send_signal is a Python 2.6ism
326
process.send_signal(signal.SIGHUP)
327
m = t.get_smart_medium()
328
self.assertEqual('', m.read_bytes(1))
329
err = process.stderr.readline()
330
self.assertEqual('Requested to stop gracefully\n', err)
238
333
class TestCmdServeChrooting(TestBzrServeBase):
294
390
self.fake_expanduser, lambda t: base_path)
295
391
mem_transport = self.get_transport()
296
392
mem_transport.mkdir_multi(['home', 'home/user'])
297
bzr_server.set_up(mem_transport, None, None, inet=True)
393
bzr_server.set_up(mem_transport, None, None, inet=True, timeout=4.0)
298
394
self.addCleanup(bzr_server.tear_down)
299
395
return bzr_server
316
412
base_url = urlutils.local_path_to_url(base_dir) + '/'
317
413
# Define a fake 'protocol' to capture the transport that cmd_serve
318
414
# passes to serve_bzr.
319
def capture_transport(transport, host, port, inet):
415
def capture_transport(transport, host, port, inet, timeout):
320
416
self.bzr_serve_transport = transport
321
417
cmd = builtins.cmd_serve()