808
808
self.server = smart.SmartTCPServer(self.backing_transport)
809
809
self.server.start_background_thread()
810
810
self.transport = smart.SmartTCPTransport(self.server.get_url())
811
self.addCleanup(self.tearDownServer)
813
def tearDownServer(self):
813
814
if getattr(self, 'transport', None):
814
815
self.transport.disconnect()
815
817
if getattr(self, 'server', None):
816
818
self.server.stop_background_thread()
817
super(SmartTCPTests, self).tearDown()
822
class TestServerSocketUsage(SmartTCPTests):
824
def test_server_closes_listening_sock_on_shutdown(self):
825
"""The server should close its listening socket when its stopped."""
827
# clean up the server and initial transport (which wont have connected)
829
# force a connection, which uses the listening socket to synchronise
830
# with the server thread, so that when we shut it down it has already
831
# executed the 'self._should_terminate = False' line in the server
833
self.transport.has('.')
834
self.tearDownServer()
835
# make a new connection to break out the inner loop in the server.
836
transport = smart.SmartTCPTransport(server.get_url())
837
# force the connection
840
transport.disconnect()
842
while server._server_thread.isAlive():
843
# this is fugly: we should have an event for the server we can
845
import time; time.sleep(0.001)
846
# if the listening socket has closed, we should get a BADFD error
847
# when connecting, rather than a hang.
848
transport = smart.SmartTCPTransport(server.get_url())
849
self.assertRaises(errors.ConnectionError, transport.has, '.')
820
852
class WritableEndToEndTests(SmartTCPTests):
821
853
"""Client to server tests that require a writable transport."""
901
933
self.setUpServer(readonly=True)
902
934
self.assertRaises(errors.TransportNotPossible, self.transport.mkdir,
938
class TestServerHooks(SmartTCPTests):
940
def capture_server_call(self, backing_url, public_url):
941
"""Record a server_started|stopped hook firing."""
942
self.hook_calls.append((backing_url, public_url))
944
def test_server_started_hook(self):
945
"""The server_started hook fires when the server is started."""
947
smart.SmartTCPServer.hooks.install_hook('server_started',
948
self.capture_server_call)
950
# at this point, the server will be starting a thread up.
951
# there is no indicator at the moment, so bodge it by doing a request.
952
self.transport.has('.')
953
self.assertEqual([(self.backing_transport.base, self.transport.base)],
956
def test_server_stopped_hook_simple(self):
957
"""The server_stopped hook fires when the server is stopped."""
959
smart.SmartTCPServer.hooks.install_hook('server_stopped',
960
self.capture_server_call)
962
result = [(self.backing_transport.base, self.transport.base)]
963
# check the stopping message isn't emitted up front, this also
964
# has the effect of synchronising with the server, so that
965
# when we shut it down it has already executed the
966
# 'self._should_terminate = False' line in the server method.
967
self.transport.has('.')
968
self.assertEqual([], self.hook_calls)
969
# clean up the server
971
self.tearDownServer()
972
# make a new connection to break out the inner loop in the server.
973
transport = smart.SmartTCPTransport(result[0][1])
975
transport.disconnect()
977
while server._server_thread.isAlive():
978
# this is fugly: we should have an event for the server we can
980
import time; time.sleep(0.001)
981
self.assertEqual(result, self.hook_calls)
983
# TODO: test that when the server suffers an exception that it calls the
984
# server-stopped hook.
906
987
class SmartServerRequestHandlerTests(tests.TestCaseWithTransport):
907
988
"""Test that call directly into the handler logic, bypassing the network."""