def setUp(self):
        self.server = DeviceTestServer('', 0)
        self.server.start(timeout=0.1)

        host, port = self.server._sock.getsockname()

        self.client = katcp.DeviceClient(host, port)
        self.client.start(timeout=0.1)
Пример #2
0
    def setUp(self):
        self.server = DeviceTestServer('', 0)
        self.server.start(timeout=0.1)

        host, port = self.server._sock.getsockname()

        self.client = BlockingTestClient(self, host, port)
        self.client.start(timeout=0.1)
    def setUp(self):
        super(TestInspectingClientAsync, self).setUp()
        self.server = DeviceTestServer('', 0)
        start_thread_with_cleanup(self, self.server, start_timeout=1)
        self.host, self.port = self.server.bind_address

        self.client = InspectingClientAsync(self.host, self.port,
                                            ioloop=self.io_loop)
        self.io_loop.add_callback(self.client.connect)
Пример #4
0
    def setUp(self):
        self.server = DeviceTestServer('', 0)
        self.server.start(timeout=0.1)
        host, port = self.server._sock.getsockname()
        self.server_addr = (host, port)

        self.client = BlockingTestClient(self, host, port)
        self.client.start(timeout=0.1)
        self.assertTrue(self.client.wait_protocol(timeout=0.1))
Пример #5
0
    def setUp(self):
        self.addCleanup(self.stop_server_client)
        self.server = DeviceTestServer('', 0)
        self.server.start(timeout=0.1)

        host, port = self.server.bind_address

        self.client = katcp.CallbackClient(host, port)
        self.client.start(timeout=1)
        self.assertTrue(self.client.wait_protocol(timeout=1))
Пример #6
0
    def setUp(self):
        self.server = DeviceTestServer('', 0)
        start_thread_with_cleanup(self, self.server, start_timeout=1)

        host, port = self.server.bind_address

        self.client = katcp.DeviceClient(host, port)
        self.client.enable_thread_safety()
        start_thread_with_cleanup(self, self.client, start_timeout=1)
        self.client.wait_connected(timeout=1)
Пример #7
0
class test_DeviceServer(unittest.TestCase, TestUtilMixin):

    expected_connect_messages = (
        # Will have to be updated if the default KATCP protocol
        # spec version changes
        r'#version-connect katcp-protocol 5.0-IM',
        r'#version-connect katcp-library katcp-python-%s' % katcp_version,
        r'#version-connect katcp-device deviceapi-5.6 buildy-1.2g')

    def setUp(self):
        self.server = DeviceTestServer('', 0)

    def test_on_client_connect(self):
        fake_sock = mock.Mock()
        mock_conn = mock.Mock(
            spec=katcp.server.ClientConnection(self.server._server, fake_sock))
        self.server.BUILD_INFO = ('buildy', 1, 2, 'g')
        self.server.VERSION_INFO = ('deviceapi', 5, 6)
        # Hack around ioloop thread asserts
        self.server._server.ioloop_thread_id = thread.get_ident()
        # Test call
        self.server.on_client_connect(mock_conn)
        # we are expecting 3 inform messages
        no_msgs = 3
        self.assertEqual(mock_conn.inform.call_count, no_msgs)
        # Get all the messages sent to _send_message
        msgs = [str(call[0][0]) for call in mock_conn.inform.call_args_list]
        self._assert_msgs_equal(msgs, self.expected_connect_messages)

    def test_request_sensor_sampling_clear(self):
        self.server.clear_strategies = mock.Mock()
        start_thread_with_cleanup(self, self.server, start_timeout=1)
        client_connection = ClientConnectionTest()
        self.server.ioloop.make_current()
        tf = self.server.handle_message(
            client_connection, katcp.Message.request('sensor-sampling-clear'))
        # tf may be a tornado (not thread-safe) future, so we wrap it in a thread-safe
        # future object
        f = Future()
        self.server.ioloop.add_callback(gen.chain_future, tf, f)
        f.result(timeout=1)
        # Ensure that the tornado future has finished running its callbacks
        self.server.sync_with_ioloop()
        self._assert_msgs_equal(client_connection.messages,
                                ['!sensor-sampling-clear ok'])
        self.server.clear_strategies.assert_called_once_with(client_connection)

    def test_has_sensor(self):
        self.assertFalse(self.server.has_sensor('blaah'))
        self.server.add_sensor(katcp.Sensor.boolean('blaah', 'blaah sens'))
        self.assertTrue(self.server.has_sensor('blaah'))

    def test_excluded_default_handlers(self):
        """
        Test that default handers from higher KATCP versions are not included

        """
        # TODO NM 2017-04-18 This should probably be removed if we do official
        # KATCP v5.1 release and make it the default server version
        self.assertNotIn('request-timeout-hint', self.server._request_handlers)
Пример #8
0
    def setUp(self):
        super(test_AsyncClientIntegratedBase, self).setUp()
        self.server = DeviceTestServer('localhost', 0)
        self.server.set_ioloop(self.io_loop)
        self.server.set_concurrency_options(thread_safe=False,
                                            handler_thread=False)
        self.server.start()

        host, port = self.server.bind_address
        logger.info('host, port: {}:{}'.format(host, port))
        self.client = katcp.CallbackClient(host, port)
        self.client.set_ioloop(self.io_loop)
 def setUp(self):
     super(TestInspectingClientAsyncStateCallback, self).setUp()
     self.server = DeviceTestServer('', 0)
     start_thread_with_cleanup(self, self.server, start_timeout=1)
     self.host, self.port = self.server.bind_address
     self.state_cb_future = tornado.concurrent.Future()
     self.client = InspectingClientAsync(self.host, self.port,
                                         ioloop=self.io_loop)
     # Set a short initial_resync timeout to make resync tests quick
     self.client.initial_resync_timeout = 0.001
     self.client.set_state_callback(self._test_state_cb)
     self.done_state_cb_futures = []
     self.cnt_state_cb_futures = collections.defaultdict(tornado.concurrent.Future)
Пример #10
0
class test_DeviceServer(unittest.TestCase, TestUtilMixin):
    def setUp(self):
        self.server = DeviceTestServer('', 0)

    def test_on_client_connect(self):
        fake_sock = mock.Mock()
        mock_conn = mock.Mock(
            spec=katcp.server.ClientConnection(self.server._server, fake_sock))
        self.server.BUILD_INFO = ('buildy', 1, 2, 'g')
        self.server.VERSION_INFO = ('deviceapi', 5, 6)
        # Hack around ioloop thread asserts
        self.server._server.ioloop_thread_id = thread.get_ident()
        # Test call
        self.server.on_client_connect(mock_conn)
        # we are expecting 3 inform messages
        no_msgs = 3
        self.assertEqual(mock_conn.inform.call_count, no_msgs)
        # Get all the messages sent to _send_message
        msgs = [str(call[0][0]) for call in mock_conn.inform.call_args_list]
        katcp_version = __version__
        self._assert_msgs_equal(
            msgs,
            (
                r'#version-connect katcp-protocol 5.0-IM',
                # Will have to be updated for every library version bump
                r'#version-connect katcp-library katcp-python-%s' %
                katcp_version,
                r'#version-connect katcp-device deviceapi-5.6 buildy-1.2g'))

    def test_request_sensor_sampling_clear(self):
        self.server.clear_strategies = mock.Mock()
        start_thread_with_cleanup(self, self.server, start_timeout=1)
        client_connection = ClientConnectionTest()
        self.server.ioloop.make_current()
        tf = self.server.handle_message(
            client_connection, katcp.Message.request('sensor-sampling-clear'))
        # tf may be a tornado (not thread-safe) future, so we wrap it in a thread-safe
        # future object
        f = Future()
        self.server.ioloop.add_callback(gen.chain_future, tf, f)
        f.result(timeout=1)
        # Ensure that the tornado future has finished running its callbacks
        self.server.sync_with_ioloop()
        self._assert_msgs_equal(client_connection.messages,
                                ['!sensor-sampling-clear ok'])
        self.server.clear_strategies.assert_called_once_with(client_connection)

    def test_has_sensor(self):
        self.assertFalse(self.server.has_sensor('blaah'))
        self.server.add_sensor(katcp.Sensor.boolean('blaah', 'blaah sens'))
        self.assertTrue(self.server.has_sensor('blaah'))
Пример #11
0
    def setUp(self):
        self.server = DeviceTestServer('', 0)
        self.server.start(timeout=0.1)

        host, port = self.server._sock.getsockname()

        self.client = katcp.DeviceClient(host, port)
        self.client.start(timeout=0.1)
 def setUp(self):
     super(Test_InformHookDeviceClient, self).setUp()
     self.server = DeviceTestServer('', 0)
     start_thread_with_cleanup(self, self.server, start_timeout=1)
     self.host, self.port = self.server.bind_address
     self.client = katcp.inspecting_client._InformHookDeviceClient(
         self.host, self.port)
     self.client.set_ioloop(self.io_loop)
     self.io_loop.add_callback(self.client.start)
Пример #13
0
    def setUp(self):
        self.server = DeviceTestServer('', 0)
        start_thread_with_cleanup(self, self.server, start_timeout=1)

        host, port = self.server.bind_address

        self.client = katcp.BlockingClient(host, port)
        start_thread_with_cleanup(self, self.client, start_timeout=1)
        self.assertTrue(self.client.wait_protocol(timeout=1))
Пример #14
0
    def setUp(self):
        super(TestInspectingClientAsync, self).setUp()
        self.server = DeviceTestServer('', 0)
        start_thread_with_cleanup(self, self.server, start_timeout=1)
        self.host, self.port = self.server.bind_address

        self.client = InspectingClientAsync(self.host, self.port,
                                            ioloop=self.io_loop)
        self.io_loop.add_callback(self.client.connect)
Пример #15
0
    def setUp(self):
        self.server = DeviceTestServer('', 0)
        self.server.start(timeout=0.1)
        host, port = self.server._sock.getsockname()
        self.server_addr = (host, port)

        self.client = BlockingTestClient(self, host, port)
        self.client.start(timeout=0.1)
        self.assertTrue(self.client.wait_protocol(timeout=0.1))
Пример #16
0
 def setUp(self):
     super(test_KATCPClientResource_IntegratedTimewarp, self).setUp()
     self.server = DeviceTestServer('', 0)
     start_thread_with_cleanup(self, self.server)
     self.host, self.port = self.server.bind_address
     self.default_resource_spec = dict(
         name='thething',
         address=self.server.bind_address,
         controlled=True)
Пример #17
0
    def setUp(self):
        self.server = DeviceTestServer('', 0)
        start_thread_with_cleanup(self, self.server, start_timeout=1)

        host, port = self.server.bind_address

        self.client = katcp.DeviceClient(host, port)
        self.client.enable_thread_safety()
        start_thread_with_cleanup(self, self.client, start_timeout=1)
        self.client.wait_connected(timeout=1)
Пример #18
0
class test_DeviceServer(unittest.TestCase, TestUtilMixin):
    def setUp(self):
        self.server = DeviceTestServer('', 0)

    def test_on_client_connect(self):
        fake_sock = mock.Mock()
        mock_conn = mock.Mock(
            spec=katcp.server.ClientConnection(self.server._server, fake_sock))
        self.server.BUILD_INFO = ('buildy', 1, 2, 'g')
        self.server.VERSION_INFO = ('deviceapi', 5, 6)
        # Hack around ioloop thread asserts
        self.server._server.ioloop_thread_id = thread.get_ident()
        # Test call
        self.server.on_client_connect(mock_conn)
        # we are expecting 3 inform messages
        no_msgs = 3
        self.assertEqual(mock_conn.inform.call_count, no_msgs)
        # Get all the messages sent to _send_message
        msgs = [str(call[0][0]) for call in mock_conn.inform.call_args_list]
        katcp_version = __version__
        self._assert_msgs_equal(msgs, (
            r'#version-connect katcp-protocol 5.0-IM',
            # Will have to be updated for every library version bump
            r'#version-connect katcp-library katcp-python-%s' % katcp_version,
            r'#version-connect katcp-device deviceapi-5.6 buildy-1.2g'))

    def test_request_sensor_sampling_clear(self):
        self.server.clear_strategies = mock.Mock()
        start_thread_with_cleanup(self, self.server, start_timeout=1)
        client_connection = ClientConnectionTest()
        self.server.ioloop.make_current()
        tf = self.server.handle_message(
            client_connection, katcp.Message.request('sensor-sampling-clear'))
        # tf may be a tornado (not thread-safe) future, so we wrap it in a thread-safe
        # future object
        f = Future()
        self.server.ioloop.add_callback(gen.chain_future, tf, f)
        f.result(timeout=1)
        # Ensure that the tornado future has finished running its callbacks
        self.server.sync_with_ioloop()
        self._assert_msgs_equal(client_connection.messages, [
            '!sensor-sampling-clear ok'])
        self.server.clear_strategies.assert_called_once_with(client_connection)

    def test_has_sensor(self):
        self.assertFalse(self.server.has_sensor('blaah'))
        self.server.add_sensor(katcp.Sensor.boolean('blaah', 'blaah sens'))
        self.assertTrue(self.server.has_sensor('blaah'))
Пример #19
0
    def setUp(self):
        self.addCleanup(self.stop_server_client)
        self.server = DeviceTestServer('', 0)
        self.server.start(timeout=0.1)

        host, port = self.server._sock.getsockname()

        self.client = katcp.CallbackClient(host, port)
        self.client.start(timeout=0.1)
        self.assertTrue(self.client.wait_protocol(timeout=1))
Пример #20
0
class test_ThreadSafeKATCPClientResourceWrapper(unittest.TestCase):
    def setUp(self):
        self.server = DeviceTestServer('', 0)
        start_thread_with_cleanup(self, self.server)

        self.ioloop_manager = ioloop_manager.IOLoopManager(managed_default=True)
        self.io_loop = self.ioloop_manager.get_ioloop()
        self.host, self.port = self.server.bind_address
        self.default_resource_spec = dict(
            name='thething',
            address=self.server.bind_address,
            controlled=True)
        self.client_resource = resource_client.KATCPClientResource(
            self.default_resource_spec)
        self.client_resource.set_ioloop(self.io_loop)
        self.io_loop.add_callback(self.client_resource.start)

        self.ioloop_thread_wrapper = resource_client.IOLoopThreadWrapper(self.io_loop)
        start_thread_with_cleanup(self, self.ioloop_manager, start_timeout=1)
        self.ioloop_thread_wrapper.default_timeout = 1

        self.DUT = resource_client.ThreadSafeKATCPClientResourceWrapper(
            self.client_resource, self.ioloop_thread_wrapper)
        self.DUT.until_synced()

    def test_wrapped_timeout(self):
        self.assertEqual(self.client_resource.state, 'synced')
        # Test timeout
        self.ioloop_thread_wrapper.default_timeout = 0.001
        t0 = time.time()
        with self.assertRaises(TimeoutError):
            self.DUT.until_state('disconnected')
        self.assertLess(time.time() - t0, 0.2)
        # Now make sure we can actualy still wait on the state
        self.ioloop_thread_wrapper.default_timeout = 1
        self.server.stop()
        self.server.join()
        self.DUT.until_state('disconnected')
        self.assertEqual(self.client_resource.state, 'disconnected')
        self.server.start()
        self.DUT.until_state('synced')
        self.assertEqual(self.client_resource.state, 'synced')

    def test_request(self):
        reply = self.DUT.req.sensor_value('an.int')
        last_server_msg = self.server.messages[-1]
        self.assertTrue(reply.succeeded)
        self.assertEqual(str(last_server_msg),
                         '?sensor-value[{}] an.int'.format(reply.reply.mid))

    def test_sensor(self):
        server_sensor = self.server.get_sensor('an.int')
        reading = self.DUT.sensor.an_int.get_reading()
        self.assertEqual(reading.value, server_sensor.read().value)
        server_sensor.set_value(server_sensor.read().value + 5)
        reading = self.DUT.sensor.an_int.get_reading()
        self.assertEqual(reading.value, server_sensor.read().value)
Пример #21
0
    def setUp(self):
        super(test_AsyncClientIntegratedBase, self).setUp()
        self.server = DeviceTestServer('localhost', 0)
        self.server.set_ioloop(self.io_loop)
        self.server.set_concurrency_options(thread_safe=False, handler_thread=False)
        self.server.start()

        host, port = self.server.bind_address
        logger.info('host, port: {}:{}'.format(host, port))
        self.client = katcp.CallbackClient(host, port)
        self.client.set_ioloop(self.io_loop)
Пример #22
0
    def setUp(self):
        super(test_AsyncClientTimeoutsIntegrated, self).setUp()
        self.server = DeviceTestServer('', 0)
        self.server.set_ioloop(self.io_loop)
        self.server.set_concurrency_options(thread_safe=False, handler_thread=False)
        self.server.start()

        host, port = self.server.bind_address
        self.client = katcp.CallbackClient(host, port)
        self.client.set_ioloop(self.io_loop)
        self.client.start()
Пример #23
0
 def setUp(self):
     super(TestInspectingClientAsyncStateCallback, self).setUp()
     self.server = DeviceTestServer('', 0)
     start_thread_with_cleanup(self, self.server, start_timeout=1)
     self.host, self.port = self.server.bind_address
     self.state_cb_future = tornado.concurrent.Future()
     self.client = InspectingClientAsync(self.host, self.port,
                                         ioloop=self.io_loop)
     self.client.set_state_callback(self._test_state_cb)
     self.done_state_cb_futures = []
     self.cnt_state_cb_futures = collections.defaultdict(tornado.concurrent.Future)
Пример #24
0
class test_AsyncClientIntegrated(tornado.testing.AsyncTestCase, TestUtilMixin):
    def setUp(self):
        super(test_AsyncClientIntegrated, self).setUp()
        self.server = DeviceTestServer('', 0)
        self.server.set_ioloop(self.io_loop)
        self.server.set_concurrency_options(thread_safe=False, handler_thread=False)
        self.server.start()

        host, port = self.server.bind_address
        self.client = katcp.CallbackClient(host, port)
        self.client.set_ioloop(self.io_loop)
        self.client.start()

    @tornado.testing.gen_test
    def test_future_request_simple(self):
        yield self.client.until_connected()
        reply, informs = yield self.client.future_request(Message.request('watchdog'))
        self.assertEqual(len(informs), 0)
        self.assertEqual(reply.name, "watchdog")
        self.assertEqual(reply.arguments, ["ok"])

    @tornado.testing.gen_test
    def test_future_request_with_informs(self):
        yield self.client.until_connected()
        reply, informs = yield self.client.future_request(Message.request('help'))
        self.assertEqual(reply.name, "help")
        self.assertEqual(reply.arguments, ["ok", "%d" % NO_HELP_MESSAGES])
        self.assertEqual(len(informs), NO_HELP_MESSAGES)

    @tornado.testing.gen_test
    def test_disconnect_cleanup(self):
        yield self.client.until_protocol()
        mid = 55
        future_reply = self.client.future_request(Message.request(
            'slow-command', 1, mid=mid))
        # Force a disconnect
        self.client._disconnect()
        reply, informs = yield future_reply
        self.assertEqual(reply, Message.reply(
            'slow-command', 'fail', 'Connection closed before reply was received',
            mid=mid))

    @tornado.testing.gen_test
    def test_stop_cleanup(self):
        yield self.client.until_protocol()
        mid = 564
        future_reply = self.client.future_request(Message.request(
            'slow-command', 1, mid=mid))
        # Stop client
        self.client.stop()
        reply, informs = yield future_reply
        self.assertEqual(reply, Message.reply(
            'slow-command', 'fail', 'Client stopped before reply was received', mid=mid))
Пример #25
0
class test_AsyncClientTimeoutsIntegrated(TimewarpAsyncTestCase):
    def setUp(self):
        super(test_AsyncClientTimeoutsIntegrated, self).setUp()
        self.server = DeviceTestServer('', 0)
        self.server.set_ioloop(self.io_loop)
        self.server.set_concurrency_options(thread_safe=False, handler_thread=False)
        self.server.start()

        host, port = self.server.bind_address
        self.client = katcp.CallbackClient(host, port)
        self.client.set_ioloop(self.io_loop)
        self.client.start()


    @tornado.testing.gen_test(timeout=10)
    # We are using time-warping, so the timeout should be longer than the fake-duration
    def test_future_request_default_timeout(self):
        # Test the default timeout of 5s
        yield self._test_timeout(5)

    @tornado.testing.gen_test()
    def test_future_request_change_default_timeout(self):
        self.client._request_timeout = 3
        yield self._test_timeout(3)

    @tornado.testing.gen_test()
    def test_future_request_request_timeout(self):
        yield self._test_timeout(1, set_request_timeout=True)

    @gen.coroutine
    def _test_timeout(self, timeout, set_request_timeout=False):
        request_timeout = timeout if set_request_timeout else None
        yield self.client.until_connected()
        t0 = self.io_loop.time()
        reply_future = self.client.future_request(Message.request('slow-command', timeout + 1),
                                                  timeout=request_timeout)
        # Warp to just before the timeout expires and check that the future is not yet
        # resolved
        self.set_ioloop_time(t0 + timeout*0.9999)
        yield self.wake_ioloop()
        self.assertFalse(reply_future.done())
        # Warp to just after the timeout expires, and check that it gives us a timeout
        # error reply
        self.set_ioloop_time(t0 + timeout*1.0001)
        yield self.wake_ioloop()
        self.assertTrue(reply_future.done())
        reply, informs = reply_future.result()
        self.assertFalse(reply.reply_ok())
        self.assertRegexpMatches(
            reply.arguments[1],
            r"Request slow-command timed out after .* seconds.")
Пример #26
0
class test_DeviceServer(unittest.TestCase, TestUtilMixin):
    def setUp(self):
        self.server = DeviceTestServer('', 0)

    def test_on_client_connect(self):
        conn = katcp.server.ClientConnectionTCP(self.server, 'fake-sock')
        m_sm = self.server._send_message = mock.Mock()
        self.server.BUILD_INFO = ('buildy', 1, 2, 'g')
        self.server.VERSION_INFO = ('deviceapi', 5, 6)
        self.server.on_client_connect(conn)
        # we are expecting 3 inform messages
        no_msgs = 3
        self.assertEqual(m_sm.call_count, no_msgs)
        # Check that calls were syntactically valid
        self.assertEqual(m_sm.call_args_list,
                         [mock.call('fake-sock', mock.ANY)]*no_msgs)
        # Get all the messages sent to _send_message
        msgs = [str(call[0][1]) for call in m_sm.call_args_list]
        self._assert_msgs_equal(msgs, (
            r'#version-connect katcp-protocol 5.0-IM',
            # Will have to be updated for every library version bump
            r'#version-connect katcp-library katcp-python-0.5.4',
            r'#version-connect katcp-device deviceapi-5.6 buildy-1.2g') )

    def test_request_sensor_sampling_clear(self):
        self.server.clear_strategies = mock.Mock()
        client_connection = ClientConnectionTest()
        self.server.handle_message(
            client_connection, katcp.Message.request('sensor-sampling-clear'))
        self._assert_msgs_equal(client_connection.messages, [
            '!sensor-sampling-clear ok'])
        self.server.clear_strategies.assert_called_once_with(client_connection)

    def test_has_sensor(self):
        self.assertFalse(self.server.has_sensor('blaah'))
        self.server.add_sensor(katcp.Sensor.boolean('blaah', 'blaah sens'))
        self.assertTrue(self.server.has_sensor('blaah'))
Пример #27
0
class test_DeviceServer(unittest.TestCase, TestUtilMixin):
    def setUp(self):
        self.server = DeviceTestServer('', 0)

    def test_on_client_connect(self):
        conn = katcp.server.ClientConnectionTCP(self.server, 'fake-sock')
        m_sm = self.server._send_message = mock.Mock()
        self.server.BUILD_INFO = ('buildy', 1, 2, 'g')
        self.server.VERSION_INFO = ('deviceapi', 5, 6)
        self.server.on_client_connect(conn)
        # we are expecting 3 inform messages
        no_msgs = 3
        self.assertEqual(m_sm.call_count, no_msgs)
        # Check that calls were syntactically valid
        self.assertEqual(m_sm.call_args_list,
                         [mock.call('fake-sock', mock.ANY)]*no_msgs)
        # Get all the messages sent to _send_message
        msgs = [str(call[0][1]) for call in m_sm.call_args_list]
        self._assert_msgs_equal(msgs, (
            r'#version-connect katcp-protocol 5.0-IM',
            # Will have to be updated for every library version bump
            r'#version-connect katcp-library katcp-python-0.5.5a0',
            r'#version-connect katcp-device deviceapi-5.6 buildy-1.2g') )

    def test_request_sensor_sampling_clear(self):
        self.server.clear_strategies = mock.Mock()
        client_connection = ClientConnectionTest()
        self.server.handle_message(
            client_connection, katcp.Message.request('sensor-sampling-clear'))
        self._assert_msgs_equal(client_connection.messages, [
            '!sensor-sampling-clear ok'])
        self.server.clear_strategies.assert_called_once_with(client_connection)

    def test_has_sensor(self):
        self.assertFalse(self.server.has_sensor('blaah'))
        self.server.add_sensor(katcp.Sensor.boolean('blaah', 'blaah sens'))
        self.assertTrue(self.server.has_sensor('blaah'))
Пример #28
0
class TestBlockingClient(unittest.TestCase):
    def setUp(self):
        self.server = DeviceTestServer('', 0)
        self.server.start(timeout=0.1)

        host, port = self.server._sock.getsockname()

        self.client = katcp.BlockingClient(host, port)
        self.client.start(timeout=0.1)

    def tearDown(self):
        if self.client.running():
            self.client.stop()
            self.client.join()
        if self.server.running():
            self.server.stop()
            self.server.join()

    def test_blocking_request(self):
        """Test blocking_request."""
        reply, informs = self.client.blocking_request(
            katcp.Message.request("watchdog"))
        assert reply.name == "watchdog"
        assert reply.arguments == ["ok"]
        assert informs == []

        reply, informs = self.client.blocking_request(
            katcp.Message.request("help"))
        assert reply.name == "help"
        assert reply.arguments == ["ok", "13"]
        assert len(informs) == int(reply.arguments[1])

    def test_timeout(self):
        """Test calling blocking_request with a timeout."""
        try:
            self.client.blocking_request(
                katcp.Message.request("slow-command", "0.5"),
                timeout=0.001)
        except RuntimeError, e:
            self.assertEqual(str(e), "Request slow-command timed out after 0.001 seconds.")
        else:
Пример #29
0
    def setUp(self):
        self.server = DeviceTestServer('', 0)
        start_thread_with_cleanup(self, self.server)

        self.ioloop_manager = ioloop_manager.IOLoopManager(managed_default=True)
        self.io_loop = self.ioloop_manager.get_ioloop()
        self.host, self.port = self.server.bind_address
        self.default_resource_spec = dict(
            name='thething',
            address=self.server.bind_address,
            controlled=True)
        self.client_resource = resource_client.KATCPClientResource(
            self.default_resource_spec)
        self.client_resource.set_ioloop(self.io_loop)
        self.io_loop.add_callback(self.client_resource.start)

        self.ioloop_thread_wrapper = resource_client.IOLoopThreadWrapper(self.io_loop)
        start_thread_with_cleanup(self, self.ioloop_manager, start_timeout=1)
        self.ioloop_thread_wrapper.default_timeout = 1

        self.DUT = resource_client.ThreadSafeKATCPClientResourceWrapper(
            self.client_resource, self.ioloop_thread_wrapper)
        self.DUT.until_synced()
Пример #30
0
class TestDeviceServerClientIntegrated(unittest.TestCase, TestUtilMixin):

    BLACKLIST = ("version-connect", "version", "build-state")

    def setUp(self):
        self.server = DeviceTestServer('', 0)
        self.server.start(timeout=0.1)
        host, port = self.server._sock.getsockname()
        self.server_addr = (host, port)

        self.client = BlockingTestClient(self, host, port)
        self.client.start(timeout=0.1)
        self.assertTrue(self.client.wait_protocol(timeout=0.1))

    def tearDown(self):
        if self.client.running():
            self.client.stop()
            self.client.join()
        if self.server.running():
            self.server.stop()
            self.server.join()

    def test_log(self):
        get_msgs = self.client.message_recorder(
                blacklist=self.BLACKLIST,
                replies=True)

        with mock.patch('katcp.server.time.time') as m_time:
            m_time.return_value = 1234
            self.server.log.error('An error')
        get_msgs.wait_number(1)
        self._assert_msgs_equal(
            get_msgs(), [r"#log error 1234.000000 root An\_error"])

    def test_simple_connect(self):
        """Test a simple server setup and teardown with client connect."""
        get_msgs = self.client.message_recorder(
                blacklist=self.BLACKLIST,
                replies=True)
        # basic send
        self.client.request(katcp.Message.request("foo"), use_mid=False)

        # pipe-lined send
        self.client.raw_send("?bar-boom\r\n?baz\r")

        # broken up sends
        self.client.raw_send("?boo")
        self.client.raw_send("m arg1 arg2")
        self.client.raw_send("\n")

        time.sleep(0.1)

        self._assert_msgs_equal(get_msgs(), [
            r"!foo invalid Unknown\_request.",
            r"!bar-boom invalid Unknown\_request.",
            r"!baz invalid Unknown\_request.",
            r"!boom invalid Unknown\_request.",
        ])

    def test_bad_requests(self):
        """Test request failure paths in device server."""
        get_msgs = self.client.message_recorder(
                blacklist=self.BLACKLIST, replies=True)
        self.client.raw_send("bad msg\n")
        # wait for reply
        self.client.blocking_request(
            katcp.Message.request("watchdog"), use_mid=False)

        self._assert_msgs_like(get_msgs(), [
            (r"#log error", "KatcpSyntaxError:"
                            "\_Bad\_type\_character\_'b'.\\n"),
            (r"!watchdog ok", ""),
        ])

    def test_slow_client(self):
        # Test that server does not choke sending messages to slow clients
        self.server.send_timeout = 0.1    # Set a short sending timeout

        self.client.wait_protocol(1)
        slow_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        slow_sock.connect(self.server_addr)
        slow_sock.settimeout(0.1)
        # Send a bunch of request to the server, but don't read anything from
        # the server
        try:
            slow_sock.sendall('?help\n'*100000)
        except socket.timeout:
            pass

        t0 = time.time()
        # Request should not have taken a very long time.
        self.client.assert_request_succeeds('help', informs_count=NO_HELP_MESSAGES)
        self.assertTrue(time.time() - t0 < 1)


    def test_server_ignores_informs_and_replies(self):
        """Test server ignores informs and replies."""
        get_msgs = self.client.message_recorder(
                blacklist=self.BLACKLIST, replies=True)
        self.client.raw_send("#some inform\n")
        self.client.raw_send("!some reply\n")

        time.sleep(0.1)

        self.assertFalse(get_msgs())

    def test_standard_requests(self):
        """Test standard request and replies."""
        get_msgs = self.client.message_recorder(
                blacklist=self.BLACKLIST, replies=True)
        nomid_req = partial(self.client.request, use_mid=False)
        nomid_req(katcp.Message.request("watchdog"), use_mid=False)
        nomid_req(katcp.Message.request("restart"))
        nomid_req(katcp.Message.request("log-level"))
        nomid_req(katcp.Message.request("log-level", "trace"))
        nomid_req(katcp.Message.request("log-level", "unknown"))
        nomid_req(katcp.Message.request("help"))
        nomid_req(katcp.Message.request("help", "watchdog"))
        nomid_req(katcp.Message.request("help", "unknown-request"))
        nomid_req(katcp.Message.request("client-list"))
        nomid_req(katcp.Message.request("version-list"))
        nomid_req(katcp.Message.request("sensor-list"))
        nomid_req(katcp.Message.request("sensor-list", "an.int"))
        nomid_req(katcp.Message.request("sensor-list", "an.unknown"))
        nomid_req(katcp.Message.request("sensor-value"))
        nomid_req(katcp.Message.request("sensor-value", "an.int"))
        nomid_req(katcp.Message.request("sensor-value",
                                                  "an.unknown"))
        nomid_req(katcp.Message.request("sensor-sampling", "an.int"))
        nomid_req(katcp.Message.request("sensor-sampling", "an.int",
                                                  "differential", "2"))
        nomid_req(katcp.Message.request("sensor-sampling", "an.int",
                                                  "event-rate", "2", "3"))
        nomid_req(katcp.Message.request("sensor-sampling"))
        nomid_req(katcp.Message.request("sensor-sampling",
                                                  "an.unknown", "auto"))
        self.client.blocking_request(katcp.Message.request(
            "sensor-sampling", "an.int", "unknown"), use_mid=False)

        time.sleep(0.1)

        self.server.log.trace("trace-msg")
        self.server.log.debug("debug-msg")
        self.server.log.info("info-msg")
        self.server.log.warn("warn-msg")
        self.server.log.error("error-msg")
        self.server.log.fatal("fatal-msg")

        time.sleep(0.1)

        self.assertEqual(self.server.restart_queue.get_nowait(), self.server)
        self._assert_msgs_like(get_msgs(), [
            (r"!watchdog ok", ""),
            (r"!restart ok", ""),
            (r"!log-level ok warn", ""),
            (r"!log-level ok trace", ""),
            (r"!log-level fail Unknown\_logging\_level\_name\_'unknown'", ""),
            (r"#help cancel-slow-command Cancel\_slow\_command\_request,\_"
             "resulting\_in\_it\_replying\_immediately", ""),
            (r"#help client-list", ""),
            (r"#help halt", ""),
            (r"#help help", ""),
            (r"#help log-level", ""),
            (r"#help new-command", ""),
            (r"#help raise-exception", ""),
            (r"#help raise-fail", ""),
            (r"#help restart", ""),
            (r"#help sensor-list", ""),
            (r"#help sensor-sampling", ""),
            (r"#help sensor-sampling-clear", ""),
            (r"#help sensor-value", ""),
            (r"#help slow-command", ""),
            (r"#help version-list", ""),
            (r"#help watchdog", ""),
            (r"!help ok %d" % NO_HELP_MESSAGES, ""),
            (r"#help watchdog", ""),
            (r"!help ok 1", ""),
            (r"!help fail", ""),
            (r"#client-list", ""),
            (r"!client-list ok 1", ""),
            (r"#version-list katcp-protocol", ""),
            (r"#version-list katcp-library", ""),
            (r"#version-list katcp-device", ""),
            (r"!version-list ok 3", ""),
            (r"#sensor-list an.int An\_Integer. count integer -5 5", ""),
            (r"!sensor-list ok 1", ""),
            (r"#sensor-list an.int An\_Integer. count integer -5 5", ""),
            (r"!sensor-list ok 1", ""),
            (r"!sensor-list fail", ""),
            (r"#sensor-value 12345.000000 1 an.int nominal 3", ""),
            (r"!sensor-value ok 1", ""),
            (r"#sensor-value 12345.000000 1 an.int nominal 3", ""),
            (r"!sensor-value ok 1", ""),
            (r"!sensor-value fail", ""),
            (r"!sensor-sampling ok an.int none", ""),
            (r"#sensor-status 12345.000000 1 an.int nominal 3", ""),
            (r"!sensor-sampling ok an.int differential 2", ""),
            (r"#sensor-status 12345.000000 1 an.int nominal 3", ""),
            (r"!sensor-sampling ok an.int event-rate 2 3", ""),
            (r"!sensor-sampling fail No\_sensor\_name\_given.", ""),
            (r"!sensor-sampling fail Unknown\_sensor\_name:\_an.unknown.", ""),
            (r"!sensor-sampling fail Unknown\_strategy\_name:\_unknown.", ""),
            (r"#log trace", r"root trace-msg"),
            (r"#log debug", r"root debug-msg"),
            (r"#log info", r"root info-msg"),
            (r"#log warn", r"root warn-msg"),
            (r"#log error", r"root error-msg"),
            (r"#log fatal", r"root fatal-msg"),
        ])

    def test_standard_requests_with_ids(self):
        """Test standard request and replies with message ids."""
        get_msgs = self.client.message_recorder(
                blacklist=self.BLACKLIST, replies=True)

        current_id = [0]

        def mid():
            current_id[0] += 1
            return str(current_id[0])

        def mid_req(*args):
            return katcp.Message.request(*args, mid=mid())


        self.client.request(mid_req("watchdog"))
        self.client.request(mid_req("restart"))
        self.client.request(mid_req("log-level"))
        self.client.request(mid_req("log-level", "trace"))
        self.client.request(mid_req("log-level", "unknown"))
        self.client.request(mid_req("help"))
        self.client.request(mid_req("help", "watchdog"))
        self.client.request(mid_req("help", "unknown-request"))
        self.client.request(mid_req("client-list"))
        self.client.request(mid_req("version-list"))
        self.client.request(mid_req("sensor-list"))
        self.client.request(mid_req("sensor-list", "an.int"))
        self.client.request(mid_req("sensor-list", "an.unknown"))
        self.client.request(mid_req("sensor-value"))
        self.client.request(mid_req("sensor-value", "an.int"))
        self.client.request(mid_req("sensor-value", "an.unknown"))
        self.client._next_id = mid  # mock our mid generator for testing
        self.client.blocking_request(mid_req("sensor-sampling", "an.int"))
        self.client.blocking_request(mid_req(
            "sensor-sampling", "an.int", "differential", "2"))
        self.client.blocking_request(mid_req(
            "sensor-sampling", "an.int", "event-rate", "2", "3")),
        self.client.blocking_request(mid_req("sensor-sampling"))
        self.client.blocking_request(mid_req(
            "sensor-sampling", "an.unknown", "auto"))
        self.client.blocking_request(mid_req(
            "sensor-sampling", "an.int", "unknown"))

        self.server.log.trace("trace-msg")
        self.server.log.debug("debug-msg")
        self.server.log.info("info-msg")
        self.server.log.warn("warn-msg")
        self.server.log.error("error-msg")
        self.server.log.fatal("fatal-msg")

        time.sleep(0.1)

        self.assertEqual(self.server.restart_queue.get_nowait(), self.server)
        self._assert_msgs_like(get_msgs(), [
            (r"!watchdog[1] ok", ""),
            (r"!restart[2] ok", ""),
            (r"!log-level[3] ok warn", ""),
            (r"!log-level[4] ok trace", ""),
            (r"!log-level[5] fail Unknown\_logging\_level\_name\_'unknown'",
             ""),
            (r"#help[6] cancel-slow-command Cancel\_slow\_command\_request,\_"
             "resulting\_in\_it\_replying\_immediately", ""),
            (r"#help[6] client-list", ""),
            (r"#help[6] halt", ""),
            (r"#help[6] help", ""),
            (r"#help[6] log-level", ""),
            (r"#help[6] new-command", ""),
            (r"#help[6] raise-exception", ""),
            (r"#help[6] raise-fail", ""),
            (r"#help[6] restart", ""),
            (r"#help[6] sensor-list", ""),
            (r"#help[6] sensor-sampling", ""),
            (r"#help[6] sensor-sampling-clear", ""),
            (r"#help[6] sensor-value", ""),
            (r"#help[6] slow-command", ""),
            (r"#help[6] version-list", ""),
            (r"#help[6] watchdog", ""),
            (r"!help[6] ok %d" % NO_HELP_MESSAGES, ""),
            (r"#help[7] watchdog", ""),
            (r"!help[7] ok 1", ""),
            (r"!help[8] fail", ""),
            (r"#client-list[9]", ""),
            (r"!client-list[9] ok 1", ""),
            (r"#version-list[10] katcp-protocol", ""),
            (r"#version-list[10] katcp-library", ""),
            (r"#version-list[10] katcp-device", ""),
            (r"!version-list[10] ok 3", ""),
            (r"#sensor-list[11] an.int An\_Integer. count integer -5 5", ""),
            (r"!sensor-list[11] ok 1", ""),
            (r"#sensor-list[12] an.int An\_Integer. count integer -5 5", ""),
            (r"!sensor-list[12] ok 1", ""),
            (r"!sensor-list[13] fail", ""),
            (r"#sensor-value[14] 12345.000000 1 an.int nominal 3", ""),
            (r"!sensor-value[14] ok 1", ""),
            (r"#sensor-value[15] 12345.000000 1 an.int nominal 3", ""),
            (r"!sensor-value[15] ok 1", ""),
            (r"!sensor-value[16] fail", ""),
            (r"!sensor-sampling[17] ok an.int none", ""),
            (r"#sensor-status 12345.000000 1 an.int nominal 3", ""),
            (r"!sensor-sampling[18] ok an.int differential 2", ""),
            (r"#sensor-status 12345.000000 1 an.int nominal 3", ""),
            (r"!sensor-sampling[19] ok an.int event-rate 2 3", ""),
            (r"!sensor-sampling[20] fail No\_sensor\_name\_given.", ""),
            (r"!sensor-sampling[21] fail Unknown\_sensor\_name:\_an.unknown.", ""),
            (r"!sensor-sampling[22] fail Unknown\_strategy\_name:\_unknown.", ""),
            (r"#log trace", r"root trace-msg"),
            (r"#log debug", r"root debug-msg"),
            (r"#log info", r"root info-msg"),
            (r"#log warn", r"root warn-msg"),
            (r"#log error", r"root error-msg"),
            (r"#log fatal", r"root fatal-msg"),
        ])

    def test_sensor_list_regex(self):
        reply, informs = self.client.blocking_request(katcp.Message.request(
                "sensor-list", "/a.*/"), use_mid=False)
        self._assert_msgs_equal(informs + [reply], [
            r"#sensor-list an.int An\_Integer. count integer -5 5",
            r"!sensor-list ok 1",
        ])

        reply, informs = self.client.blocking_request(katcp.Message.request(
                "sensor-list", "//"), use_mid=False)
        self._assert_msgs_equal(informs + [reply], [
            r"#sensor-list an.int An\_Integer. count integer -5 5",
            r"!sensor-list ok 1",
        ])

        reply, informs = self.client.blocking_request(katcp.Message.request(
                "sensor-list", "/^int/"), use_mid=False)
        self._assert_msgs_equal(informs + [reply], [
            r"!sensor-list ok 0",
        ])

    def test_sensor_value_regex(self):
        reply, informs = self.client.blocking_request(katcp.Message.request(
                "sensor-value", "/a.*/"), use_mid=False)
        self._assert_msgs_equal(informs + [reply], [
            r"#sensor-value 12345.000000 1 an.int nominal 3",
            r"!sensor-value ok 1",
        ])

        reply, informs = self.client.blocking_request(katcp.Message.request(
                "sensor-value", "//"), use_mid=False)
        self._assert_msgs_equal(informs + [reply], [
            r"#sensor-value 12345.000000 1 an.int nominal 3",
            r"!sensor-value ok 1",
        ])

        reply, informs = self.client.blocking_request(katcp.Message.request(
                "sensor-value", "/^int/"), use_mid=False)
        self._assert_msgs_equal(informs + [reply], [
            r"!sensor-value ok 0",
        ])

    def test_client_list(self):
        reply, informs = self.client.blocking_request(
            katcp.Message.request('client-list'), use_mid=False)
        self.assertEqual(str(reply), '!client-list ok 1')
        self.assertEqual(len(informs), 1)
        inform = str(informs[0])
        self.assertTrue(inform.startswith('#client-list 127.0.0.1:'))
        _, addr = inform.split()
        host, port = addr.split(':')
        port = int(port)
        self.assertEqual((host, port), self.client._sock.getsockname())

    def test_halt_request(self):
        """Test halt request."""
        get_msgs = self.client.message_recorder(
                blacklist=self.BLACKLIST, replies=True)
        self.client.request(katcp.Message.request("halt"))
        # hack to hide re-connect exception
        self.client.connect = lambda: None
        self.server.join()
        time.sleep(0.1)

        self._assert_msgs_equal(get_msgs(), [
            r"!halt[1] ok",
            r"#disconnect Device\_server\_shutting\_down.",
        ])

    def test_bad_handlers(self):
        """Test that bad request and inform handlers are picked up."""
        try:

            class BadServer(katcp.DeviceServer):
                def request_baz(self, req, msg):
                    pass

        except AssertionError:
            pass
        else:
            self.fail("Server metaclass accepted missing request_ docstring.")

        try:

            class BadServer(katcp.DeviceServer):
                def inform_baz(self, req, msg):
                    pass

        except AssertionError:
            pass
        else:
            self.fail("Server metaclass accepted missing inform_ docstring.")

        class SortOfOkayServer(katcp.DeviceServer):
            request_bar = 1
            inform_baz = 2

        assert("bar" not in SortOfOkayServer._request_handlers)
        assert("baz" not in SortOfOkayServer._inform_handlers)

    def test_handler_exceptions(self):
        """Test handling of failure replies and other exceptions."""
        get_msgs = self.client.message_recorder(
                blacklist=self.BLACKLIST, replies=True)
        self.assertTrue(self.client.wait_protocol(timeout=1))

        self.client.request(katcp.Message.request("raise-exception"))
        self.client.request(katcp.Message.request("raise-fail"))

        time.sleep(0.1)

        self._assert_msgs_like(get_msgs(), [
            (r"!raise-exception[1] fail Traceback", ""),
            (r"!raise-fail[2] fail There\_was\_a\_problem\_with\_your\_request.",
             ""),
        ])

    def test_stop_and_restart(self):
        """Test stopping and restarting the device server."""
        self.server.stop(timeout=0.1)
        self.server.join(timeout=1.0)
        self.assertEqual(self.server._thread, None)
        self.assertFalse(self.server._running.isSet())
        self.server.start(timeout=1.0)

    def test_bad_client_socket(self):
        """Test what happens when select is called on a dead client socket."""
        # wait for client to arrive
        time.sleep(0.1)

        # close socket while the server isn't looking
        # then wait for the server to notice
        sock = self.server._socks[0]
        sock.close()
        time.sleep(0.75)

        # check that client was removed
        self.assertTrue(sock not in self.server._socks,
                        "Expected %r to not be in %r" %
                        (sock, self.server._socks))

    def test_bad_server_socket(self):
        """Test what happens when select is called on a dead server socket."""
        # wait for client to arrive
        time.sleep(0.1)

        # close socket while the server isn't looking
        # then wait for the server to notice
        sock = self.server._sock
        sockname = sock.getsockname()
        sock.close()
        time.sleep(0.75)

        # check that server restarted
        self.assertTrue(sock is not self.server._sock,
                        "Expected %r to not be %r" % (sock, self.server._sock))
        self.assertEqual(sockname, self.server._sock.getsockname())

    def test_daemon_value(self):
        """Test passing in a daemon value to server start method."""
        self.server.stop(timeout=0.1)
        self.server.join(timeout=1.0)

        self.server.start(timeout=0.1, daemon=True)
        self.assertTrue(self.server._thread.isDaemon())

    def test_excepthook(self):
        """Test passing in an excepthook to server start method."""
        exceptions = []
        except_event = threading.Event()

        def excepthook(etype, value, traceback):
            """Keep track of exceptions."""
            exceptions.append(etype)
            except_event.set()

        self.server.stop(timeout=0.1)
        self.server.join(timeout=1.5)

        self.server.start(timeout=0.1, excepthook=excepthook)
        # force exception by deleteing _running
        old_running = self.server._running
        try:
            del self.server._running
            except_event.wait(1.5)
            self.assertEqual(exceptions, [AttributeError])
        finally:
            self.server._running = old_running

        # close socket -- server didn't shut down correctly
        self.server._sock.close()
        self.server.stop(timeout=0.1)
        self.server.join(timeout=1.5)

        except_event.clear()
        del exceptions[:]
        self.server.start(timeout=0.1, excepthook=excepthook)
        # force exception in sample reactor and check that it makes
        # it back up
        reactor = self.server._reactor
        old_stop = reactor._stopEvent
        try:
            del reactor._stopEvent
            reactor._wakeEvent.set()
            except_event.wait(0.1)
            self.assertEqual(exceptions, [AttributeError])
        finally:
            reactor._stopEvent = old_stop

        # close socket -- server didn't shut down correctly
        self.server._sock.close()

    def test_sampling(self):
        """Test sensor sampling."""
        get_msgs = self.client.message_recorder(
                blacklist=self.BLACKLIST, replies=True)
        self.client.wait_protocol(timeout=1)
        self.client.request(katcp.Message.request(
            "sensor-sampling", "an.int", "period", 1/32.))

        # Wait for the request reply and for the sensor update messages to
        # arrive. We expect update one the moment the sensor-sampling request is
        # made, then four more over 4/32. of a second, resutling in 6
        # messages. Wait half a period longer just to be sure we get everything.

        self.assertTrue(get_msgs.wait_number(6, timeout=4.5/32.))
        self.client.assert_request_succeeds("sensor-sampling", "an.int", "none")
        # Wait for reply to above request
        get_msgs.wait_number(7)
        msgs = get_msgs()
        updates = [x for x in msgs if x.name == "sensor-status"]
        others = [x for x in msgs if x.name != "sensor-status"]
        self.assertTrue(abs(len(updates) - 5) < 2,
                        "Expected 5 informs, saw %d." % len(updates))

        self._assert_msgs_equal(others, [
            r"!sensor-sampling[1] ok an.int period %s" % (1/32.),
            r"!sensor-sampling[2] ok an.int none",
        ])

        self.assertEqual(updates[0].arguments[1:],
                         ["1", "an.int", "nominal", "3"])

        ## Now clear the strategies on this sensor
        # There should only be on connection to the server, so it should be
        # the test client
        client_conn = self.server._sock_connections.values()[0]
        self.server.clear_strategies(client_conn)
        self.client.assert_request_succeeds("sensor-sampling", "an.int",
                                            args_equal=["an.int", "none"])

        # Check that we did not accidentally clobber the strategy datastructure
        # in the proccess
        self.client.assert_request_succeeds(
            "sensor-sampling", "an.int", "period", 0.125)


    def test_add_remove_sensors(self):
        """Test adding and removing sensors from a running device."""
        an_int = self.server._sensors["an.int"]
        self.server.remove_sensor(an_int)
        self.server.add_sensor(an_int)
        self.test_sampling()
Пример #31
0
class TestInspectingClientAsync(tornado.testing.AsyncTestCase):

    def setUp(self):
        super(TestInspectingClientAsync, self).setUp()
        self.server = DeviceTestServer('', 0)
        start_thread_with_cleanup(self, self.server, start_timeout=1)
        self.host, self.port = self.server.bind_address

        self.client = InspectingClientAsync(self.host, self.port,
                                            ioloop=self.io_loop)
        self.io_loop.add_callback(self.client.connect)


    @tornado.testing.gen_test
    def test_simple_request(self):
        """Perform a basic request."""
        yield self.client.until_synced()
        reply, informs = yield self.client.simple_request('help', 'watchdog')
        self.assertIn('ok', str(reply))
        self.assertEquals(len(informs), 1)

    @tornado.testing.gen_test
    def test_sensor(self):
        """Access the sensor with the Async client."""
        yield self.client.until_synced()
        sensor_name = 'an.int'
        sensor = yield self.client.future_get_sensor(sensor_name)
        self.assertEquals(sensor.name, sensor_name)
        self.assertEquals(sensor.stype, 'integer')

        # Unknown sensor requests return a None.
        sensor_name = 'thing.unknown_sensor'
        sensor = yield self.client.future_get_sensor(sensor_name)
        self.assertIsNone(sensor)

    @tornado.testing.gen_test
    def test_request_access(self):
        """Test access to requests."""
        yield self.client.until_synced()

        request_name = 'watchdog'
        self.assertIn(request_name, self.client.requests)
        request = yield self.client.future_get_request(request_name)
        self.assertEqual(request.name, request_name)
        self.assertTrue(request.description,
                        'Expected an description: got nothing.')

        # Unknown request return a None.
        request_name = 'watchcat'
        self.assertNotIn(request_name, self.client.requests)
        request = yield self.client.future_get_request(request_name)
        self.assertIsNone(request)

    @tornado.testing.gen_test
    def test_sensor_add_remove(self):
        """Test a sensor being added and then remove it."""
        yield self.client.until_synced()

        sensor = DeviceTestSensor(Sensor.INTEGER, "another.int",
                                  "An Integer.",
                                  "count", [-5, 5], timestamp=time.time(),
                                  status=Sensor.NOMINAL, value=3)
        # Check that the sensor does not exist currently
        self.assertNotIn(sensor.name, self.client.sensors)

        # Add a sensor.
        self.server.add_sensor(sensor)
        self.server.mass_inform(Message.inform('interface-changed'))
        # Do a blocking request to ensure #interface-changed has been received
        yield self.client.simple_request('watchdog')
        yield self.client.until_synced()
        self.assertIn('another.int', self.client.sensors)

        # Remove a sensor.
        self.server.remove_sensor(sensor)
        self.server.mass_inform(Message.inform('interface-changed'))
        # Do a blocking request to ensure #interface-changed has been received
        yield self.client.simple_request('watchdog')

        yield self.client.until_synced()
        self.assertNotIn('another.int', self.client.sensors)


    @tornado.testing.gen_test
    def test_request_add_remove(self):
        """Test a request being added and then remove it."""
        yield self.client.until_synced()

        def request_sparkling_new(self, req, msg):
            """A new command."""
            return Message.reply(msg.name, "ok", "bling1", "bling2")

        # Check that the request did not exist before
        self.assertNotIn('sparkling-new', self.client.requests)

        # Add a request.
        self.server.request_sparkling_new = request_sparkling_new
        self.server._request_handlers['sparkling-new'] = request_sparkling_new
        self.server.mass_inform(Message.inform('interface-changed'))
        # Do a blocking request to ensure #interface-changed has been received
        yield self.client.simple_request('watchdog')

        yield self.client.until_synced()
        self.assertIn('sparkling-new', self.client.requests)
        req = yield self.client.future_get_request('sparkling-new')
        self.assertEqual(req.name, 'sparkling-new')

        # Remove a request.
        self.server.request_sparkling_new = None
        del(self.server._request_handlers['sparkling-new'])
        self.server.mass_inform(Message.inform('interface-changed'))
        # Do a blocking request to ensure #interface-changed has been received
        self.client.simple_request('watchdog')
        yield self.client.until_synced()
        self.assertNotIn('sparkling_new', self.client.requests)

    @tornado.testing.gen_test
    def test_send_request(self):
        """Very high level test.

        Calling methods to insure they do not raise exception.

        """
        client = InspectingClientAsync(self.host, self.port,
                                       ioloop=self.io_loop,
                                       initial_inspection=False)

        yield client.connect()
        yield client.until_connected()
        yield client.until_synced()
        self.assertEquals(len(client.sensors), 0)

        self.assertEquals(len(client.requests), 0)
        self.assertTrue(client.synced)
        self.assertTrue(client.is_connected())
        self.assertTrue(client.connected)
        yield client.simple_request('sensor-sampling', 'an.int', 'event')
        # Wait for sync and check if the sensor was automaticaly added.
        # Get the sensor object and see if it has data.
        sensor = yield client.future_get_sensor('an.int')
        self.assertEquals(len(client.sensors), 1)
        self.assertTrue(sensor.read())
        self.assertEquals(len(client.requests), 0)

    @tornado.testing.gen_test
    def test_handle_sensor_value(self):
        yield self.client.until_connected()
        # Test that #sensor-value informs are handles like #sensor-inform informs if
        # handle_sensor_value() is called.
        sens = yield self.client.future_get_sensor('an.int')
        test_val = 1911
        # Check that the sensor doesn't start out with our test value
        self.assertNotEqual(sens.read().value, test_val)
        # Now set the value of the sensor on the server
        server_sens = self.server.get_sensor('an.int')
        server_sens.set_value(test_val)
        # Do a request to ensure we are synced with the server
        yield self.client.simple_request('watchdog')
        # Test that the value has not yet been set on the client sensor (there are no
        # strategies set)
        self.assertNotEqual(sens.read().value, test_val)
        # Now do a ?sensor-value request, and check that our object is NOT YET updated,
        # since we have not called handle_sensor_value() yet
        yield self.client.simple_request('sensor-value', 'an.int')
        self.assertNotEqual(sens.read().value, test_val)

        # Test call
        self.client.handle_sensor_value()

        # Now do a ?sensor-value request, and check that our object is updated
        yield self.client.simple_request('sensor-value', 'an.int')
        self.assertEqual(sens.read().value, test_val)

    @tornado.testing.gen_test
    def test_factories(self):
        yield self.client.until_connected()
        # Test that the correct factories are used to construct sensor and request
        # objects, and that the factories are called with the correct parameters.
        sf = self.client.sensor_factory = mock.Mock()
        rf = self.client.request_factory = mock.Mock()

        sen = yield self.client.future_get_sensor('an.int')
        req = yield self.client.future_get_request('watchdog')

        self.assertIs(sen, sf.return_value)
        sf.assert_called_once_with(
            units=None, sensor_type=0, params=[-5, 5],
            description='An Integer.', name='an.int')
        self.assertIs(req, rf.return_value)
        rf.assert_called_once_with('watchdog', mock.ANY)
Пример #32
0
class TestDeviceServerClientIntegrated(unittest.TestCase, TestUtilMixin):

    BLACKLIST = ("version-connect", "version", "build-state")

    def setUp(self):
        super(TestDeviceServerClientIntegrated, self).setUp()
        self._setup_server()
        host, port = self.server.bind_address
        self.server_addr = (host, port)

        self.client = BlockingTestClient(self, host, port)
        start_thread_with_cleanup(self, self.client, start_timeout=1)
        self.assertTrue(self.client.wait_protocol(timeout=1))

    def _setup_server(self):
        self.server = DeviceTestServer('', 0)
        start_thread_with_cleanup(self, self.server, start_timeout=1)

    def test_log(self):
        get_msgs = self.client.message_recorder(blacklist=self.BLACKLIST,
                                                replies=True)

        def tst():
            with mock.patch('katcp.server.time.time') as m_time:
                m_time.return_value = 1234
                self.server.log.error('An error')

        self.server.ioloop.add_callback(tst)

        get_msgs.wait_number(1)
        self._assert_msgs_equal(get_msgs(),
                                [r"#log error 1234.000000 root An\_error"])

    def test_simple_connect(self):
        """Test a simple server setup and teardown with client connect."""
        get_msgs = self.client.message_recorder(blacklist=self.BLACKLIST,
                                                replies=True)
        # basic send
        self.client.request(katcp.Message.request("foo"), use_mid=False)

        # pipe-lined send
        self.client.raw_send("?bar-boom\r\n?baz\r")

        # broken up sends
        self.client.raw_send("?boo")
        self.client.raw_send("m arg1 arg2")
        self.client.raw_send("\n")

        self._assert_msgs_equal(get_msgs(min_number=4), [
            r"!foo invalid Unknown\_request.",
            r"!bar-boom invalid Unknown\_request.",
            r"!baz invalid Unknown\_request.",
            r"!boom invalid Unknown\_request.",
        ])

    def test_bad_requests(self):
        """Test request failure paths in device server."""
        get_msgs = self.client.message_recorder(blacklist=self.BLACKLIST,
                                                replies=True)
        self.client.raw_send("bad msg\n")
        # wait for reply
        self.client.blocking_request(katcp.Message.request("watchdog"),
                                     use_mid=False)

        self._assert_msgs_like(get_msgs(), [
            (r"#log error", "KatcpSyntaxError:"
             "\_Bad\_type\_character\_'b'.\\n"),
            (r"!watchdog ok", ""),
        ])

    def test_slow_client(self):
        # Test that server does not choke sending messages to slow clients

        # Set max server write buffer size smaller so that it gives up earlier to make the
        # test faster
        self.server._server.MAX_WRITE_BUFFER_SIZE = 16384

        self.client.wait_protocol(1)
        slow_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        slow_sock.connect(self.server_addr)
        slow_sock.settimeout(0.01)
        # Send a bunch of request to the server, but don't read anything from
        # the server
        try:
            slow_sock.sendall('?help\n' * 1000000)
        except (socket.error, socket.timeout):
            pass

        t0 = time.time()
        # Request should not have taken a very long time.
        self.client.assert_request_succeeds('help',
                                            informs_count=NO_HELP_MESSAGES)
        self.assertTrue(time.time() - t0 < 1)

    def test_server_ignores_informs_and_replies(self):
        """Test server ignores informs and replies."""
        get_msgs = self.client.message_recorder(blacklist=self.BLACKLIST,
                                                replies=True)
        self.client.raw_send("#some inform\n")
        self.client.raw_send("!some reply\n")

        time.sleep(0.1)

        self.assertFalse(get_msgs())

    def test_standard_requests(self):
        """Test standard request and replies."""
        get_msgs = self.client.message_recorder(blacklist=self.BLACKLIST,
                                                replies=True)
        nomid_req = partial(self.client.blocking_request, use_mid=False)
        nomid_req(katcp.Message.request("watchdog"))
        nomid_req(katcp.Message.request("restart"))
        nomid_req(katcp.Message.request("log-level"))
        nomid_req(katcp.Message.request("log-level", "trace"))
        nomid_req(katcp.Message.request("log-level", "unknown"))
        nomid_req(katcp.Message.request("help"))
        nomid_req(katcp.Message.request("help", "watchdog"))
        nomid_req(katcp.Message.request("help", "unknown-request"))
        nomid_req(katcp.Message.request("client-list"))
        nomid_req(katcp.Message.request("version-list"))
        nomid_req(katcp.Message.request("sensor-list"))
        nomid_req(katcp.Message.request("sensor-list", "an.int"))
        nomid_req(katcp.Message.request("sensor-list", "an.unknown"))
        nomid_req(katcp.Message.request("sensor-value"))
        nomid_req(katcp.Message.request("sensor-value", "an.int"))
        nomid_req(katcp.Message.request("sensor-value", "an.unknown"))
        nomid_req(katcp.Message.request("sensor-sampling", "an.int"))
        nomid_req(
            katcp.Message.request("sensor-sampling", "an.int", "differential",
                                  "2"))
        nomid_req(
            katcp.Message.request("sensor-sampling", "an.int", "event-rate",
                                  "2", "3"))
        nomid_req(katcp.Message.request("sensor-sampling"))
        nomid_req(
            katcp.Message.request("sensor-sampling", "an.unknown", "auto"))
        nomid_req(katcp.Message.request("sensor-sampling", "an.int",
                                        "unknown"))

        def tst():
            self.server.log.trace("trace-msg")
            self.server.log.debug("debug-msg")
            self.server.log.info("info-msg")
            self.server.log.warn("warn-msg")
            self.server.log.error("error-msg")
            self.server.log.fatal("fatal-msg")

        self.server.ioloop.add_callback(tst)

        self.assertEqual(self.server.restart_queue.get_nowait(), self.server)
        expected_msgs = [
            (r"!watchdog ok", ""),
            (r"!restart ok", ""),
            (r"!log-level ok warn", ""),
            (r"!log-level ok trace", ""),
            (r"!log-level fail Unknown\_logging\_level\_name\_'unknown'", ""),
            (r"#help cancel-slow-command Cancel\_slow\_command\_request,\_"
             "resulting\_in\_it\_replying\_immediately", ""),
            (r"#help client-list", ""),
            (r"#help halt", ""),
            (r"#help help", ""),
            (r"#help log-level", ""),
            (r"#help new-command", ""),
            (r"#help raise-exception", ""),
            (r"#help raise-fail", ""),
            (r"#help restart", ""),
            (r"#help sensor-list", ""),
            (r"#help sensor-sampling", ""),
            (r"#help sensor-sampling-clear", ""),
            (r"#help sensor-value", ""),
            (r"#help slow-command", ""),
            (r"#help version-list", ""),
            (r"#help watchdog", ""),
            (r"!help ok %d" % NO_HELP_MESSAGES, ""),
            (r"#help watchdog", ""),
            (r"!help ok 1", ""),
            (r"!help fail", ""),
            (r"#client-list", ""),
            (r"!client-list ok 1", ""),
            (r"#version-list katcp-protocol", ""),
            (r"#version-list katcp-library", ""),
            (r"#version-list katcp-device", ""),
            (r"!version-list ok 3", ""),
            (r"#sensor-list an.int An\_Integer. count integer -5 5", ""),
            (r"!sensor-list ok 1", ""),
            (r"#sensor-list an.int An\_Integer. count integer -5 5", ""),
            (r"!sensor-list ok 1", ""),
            (r"!sensor-list fail", ""),
            (r"#sensor-value 12345.000000 1 an.int nominal 3", ""),
            (r"!sensor-value ok 1", ""),
            (r"#sensor-value 12345.000000 1 an.int nominal 3", ""),
            (r"!sensor-value ok 1", ""),
            (r"!sensor-value fail", ""),
            (r"!sensor-sampling ok an.int none", ""),
            (r"#sensor-status 12345.000000 1 an.int nominal 3", ""),
            (r"!sensor-sampling ok an.int differential 2", ""),
            (r"#sensor-status 12345.000000 1 an.int nominal 3", ""),
            (r"!sensor-sampling ok an.int event-rate 2 3", ""),
            (r"!sensor-sampling fail No\_sensor\_name\_given.", ""),
            (r"!sensor-sampling fail Unknown\_sensor\_name:\_an.unknown.", ""),
            (r"!sensor-sampling fail Unknown\_strategy\_name:\_unknown.", ""),
            (r"#log trace", r"root trace-msg"),
            (r"#log debug", r"root debug-msg"),
            (r"#log info", r"root info-msg"),
            (r"#log warn", r"root warn-msg"),
            (r"#log error", r"root error-msg"),
            (r"#log fatal", r"root fatal-msg"),
        ]
        self._assert_msgs_like(get_msgs(min_number=len(expected_msgs)),
                               expected_msgs)

    def test_standard_requests_with_ids(self):
        """Test standard request and replies with message ids."""
        get_msgs = self.client.message_recorder(blacklist=self.BLACKLIST,
                                                replies=True)

        current_id = [0]

        def mid():
            current_id[0] += 1
            return str(current_id[0])

        def mid_req(*args):
            return katcp.Message.request(*args, mid=mid())

        self.client.request(mid_req("watchdog"))
        self.client._next_id = mid  # mock our mid generator for testing
        self.client.blocking_request(mid_req("restart"))
        self.client.request(mid_req("log-level"))
        self.client.request(mid_req("log-level", "trace"))
        self.client.request(mid_req("log-level", "unknown"))
        self.client.request(mid_req("help"))
        self.client.request(mid_req("help", "watchdog"))
        self.client.request(mid_req("help", "unknown-request"))
        self.client.request(mid_req("client-list"))
        self.client.request(mid_req("version-list"))
        self.client.request(mid_req("sensor-list"))
        self.client.request(mid_req("sensor-list", "an.int"))
        self.client.request(mid_req("sensor-list", "an.unknown"))
        self.client.request(mid_req("sensor-value"))
        self.client.request(mid_req("sensor-value", "an.int"))
        self.client.request(mid_req("sensor-value", "an.unknown"))
        self.client.blocking_request(mid_req("sensor-sampling", "an.int"))
        self.client.blocking_request(
            mid_req("sensor-sampling", "an.int", "differential", "2"))
        self.client.blocking_request(
            mid_req("sensor-sampling", "an.int", "event-rate", "2", "3")),
        self.client.blocking_request(mid_req("sensor-sampling"))
        self.client.blocking_request(
            mid_req("sensor-sampling", "an.unknown", "auto"))
        self.client.blocking_request(
            mid_req("sensor-sampling", "an.int", "unknown"))

        def tst():
            self.server.log.trace("trace-msg")
            self.server.log.debug("debug-msg")
            self.server.log.info("info-msg")
            self.server.log.warn("warn-msg")
            self.server.log.error("error-msg")
            self.server.log.fatal("fatal-msg")

        self.server.ioloop.add_callback(tst)

        expected_msgs = [
            (r"!watchdog[1] ok", ""),
            (r"!restart[2] ok", ""),
            (r"!log-level[3] ok warn", ""),
            (r"!log-level[4] ok trace", ""),
            (r"!log-level[5] fail Unknown\_logging\_level\_name\_'unknown'",
             ""),
            (r"#help[6] cancel-slow-command Cancel\_slow\_command\_request,\_"
             "resulting\_in\_it\_replying\_immediately", ""),
            (r"#help[6] client-list", ""),
            (r"#help[6] halt", ""),
            (r"#help[6] help", ""),
            (r"#help[6] log-level", ""),
            (r"#help[6] new-command", ""),
            (r"#help[6] raise-exception", ""),
            (r"#help[6] raise-fail", ""),
            (r"#help[6] restart", ""),
            (r"#help[6] sensor-list", ""),
            (r"#help[6] sensor-sampling", ""),
            (r"#help[6] sensor-sampling-clear", ""),
            (r"#help[6] sensor-value", ""),
            (r"#help[6] slow-command", ""),
            (r"#help[6] version-list", ""),
            (r"#help[6] watchdog", ""),
            (r"!help[6] ok %d" % NO_HELP_MESSAGES, ""),
            (r"#help[7] watchdog", ""),
            (r"!help[7] ok 1", ""),
            (r"!help[8] fail", ""),
            (r"#client-list[9]", ""),
            (r"!client-list[9] ok 1", ""),
            (r"#version-list[10] katcp-protocol", ""),
            (r"#version-list[10] katcp-library", ""),
            (r"#version-list[10] katcp-device", ""),
            (r"!version-list[10] ok 3", ""),
            (r"#sensor-list[11] an.int An\_Integer. count integer -5 5", ""),
            (r"!sensor-list[11] ok 1", ""),
            (r"#sensor-list[12] an.int An\_Integer. count integer -5 5", ""),
            (r"!sensor-list[12] ok 1", ""),
            (r"!sensor-list[13] fail", ""),
            (r"#sensor-value[14] 12345.000000 1 an.int nominal 3", ""),
            (r"!sensor-value[14] ok 1", ""),
            (r"#sensor-value[15] 12345.000000 1 an.int nominal 3", ""),
            (r"!sensor-value[15] ok 1", ""),
            (r"!sensor-value[16] fail", ""),
            (r"!sensor-sampling[17] ok an.int none", ""),
            (r"#sensor-status 12345.000000 1 an.int nominal 3", ""),
            (r"!sensor-sampling[18] ok an.int differential 2", ""),
            (r"#sensor-status 12345.000000 1 an.int nominal 3", ""),
            (r"!sensor-sampling[19] ok an.int event-rate 2 3", ""),
            (r"!sensor-sampling[20] fail No\_sensor\_name\_given.", ""),
            (r"!sensor-sampling[21] fail Unknown\_sensor\_name:\_an.unknown.",
             ""),
            (r"!sensor-sampling[22] fail Unknown\_strategy\_name:\_unknown.",
             ""),
            (r"#log trace", r"root trace-msg"),
            (r"#log debug", r"root debug-msg"),
            (r"#log info", r"root info-msg"),
            (r"#log warn", r"root warn-msg"),
            (r"#log error", r"root error-msg"),
            (r"#log fatal", r"root fatal-msg"),
        ]
        self.assertEqual(self.server.restart_queue.get_nowait(), self.server)
        self._assert_msgs_like(get_msgs(min_number=len(expected_msgs)),
                               expected_msgs)

    def test_sensor_list_regex(self):
        reply, informs = self.client.blocking_request(katcp.Message.request(
            "sensor-list", "/a.*/"),
                                                      use_mid=False)
        self._assert_msgs_equal(informs + [reply], [
            r"#sensor-list an.int An\_Integer. count integer -5 5",
            r"!sensor-list ok 1",
        ])

        reply, informs = self.client.blocking_request(katcp.Message.request(
            "sensor-list", "//"),
                                                      use_mid=False)
        self._assert_msgs_equal(informs + [reply], [
            r"#sensor-list an.int An\_Integer. count integer -5 5",
            r"!sensor-list ok 1",
        ])

        reply, informs = self.client.blocking_request(katcp.Message.request(
            "sensor-list", "/^int/"),
                                                      use_mid=False)
        self._assert_msgs_equal(informs + [reply], [
            r"!sensor-list ok 0",
        ])

    def test_sensor_value_regex(self):
        reply, informs = self.client.blocking_request(katcp.Message.request(
            "sensor-value", "/a.*/"),
                                                      use_mid=False)
        self._assert_msgs_equal(informs + [reply], [
            r"#sensor-value 12345.000000 1 an.int nominal 3",
            r"!sensor-value ok 1",
        ])

        reply, informs = self.client.blocking_request(katcp.Message.request(
            "sensor-value", "//"),
                                                      use_mid=False)
        self._assert_msgs_equal(informs + [reply], [
            r"#sensor-value 12345.000000 1 an.int nominal 3",
            r"!sensor-value ok 1",
        ])

        reply, informs = self.client.blocking_request(katcp.Message.request(
            "sensor-value", "/^int/"),
                                                      use_mid=False)
        self._assert_msgs_equal(informs + [reply], [
            r"!sensor-value ok 0",
        ])

    def test_client_list(self):
        reply, informs = self.client.blocking_request(
            katcp.Message.request('client-list'), use_mid=False)
        self.assertEqual(str(reply), '!client-list ok 1')
        self.assertEqual(len(informs), 1)
        inform = str(informs[0])
        self.assertTrue(inform.startswith('#client-list 127.0.0.1:'))
        _, addr = inform.split()
        host, port = addr.split(':')
        port = int(port)
        self.assertEqual((host, port), self.client.sockname)

    def test_halt_request(self):
        """Test halt request."""
        get_msgs = self.client.message_recorder(blacklist=self.BLACKLIST,
                                                replies=True)
        self.client.request(katcp.Message.request("halt"))
        # hack to hide re-connect exception
        self.client.connect = lambda: None
        self.server.join()

        self._assert_msgs_equal(get_msgs(min_number=2), [
            r"!halt[1] ok",
            r"#disconnect Device\_server\_shutting\_down.",
        ])

    def test_bad_handlers(self):
        """Test that bad request and inform handlers are picked up."""
        try:

            class BadServer(katcp.DeviceServer):
                def request_baz(self, req, msg):
                    pass

        except AssertionError:
            pass
        else:
            self.fail("Server metaclass accepted missing request_ docstring.")

        try:

            class BadServer(katcp.DeviceServer):
                def inform_baz(self, req, msg):
                    pass

        except AssertionError:
            pass
        else:
            self.fail("Server metaclass accepted missing inform_ docstring.")

        class SortOfOkayServer(katcp.DeviceServer):
            request_bar = 1
            inform_baz = 2

        assert ("bar" not in SortOfOkayServer._request_handlers)
        assert ("baz" not in SortOfOkayServer._inform_handlers)

    def test_handler_exceptions(self):
        """Test handling of failure replies and other exceptions."""
        get_msgs = self.client.message_recorder(blacklist=self.BLACKLIST,
                                                replies=True)
        self.assertTrue(self.client.wait_protocol(timeout=1))

        self.client.request(katcp.Message.request("raise-exception"))
        self.client.request(katcp.Message.request("raise-fail"))

        time.sleep(0.1)

        self._assert_msgs_like(get_msgs(), [
            (r"!raise-exception[1] fail Traceback", ""),
            (r"!raise-fail[2] fail There\_was\_a\_problem\_with\_your\_request.",
             ""),
        ])

    def test_stop_and_restart(self):
        """Test stopping and restarting the device server."""
        # So we can wait for the client to disconnect
        self.client.notify_connected = WaitingMock()
        self.server.stop(timeout=0.1)
        self.server.join(timeout=1.0)
        self.assertFalse(self.server.running())
        # Wait for client to be disconnected
        self.client.notify_connected.assert_wait_call_count(1)
        self.assertFalse(self.client.is_connected())
        self.server.start(timeout=1.0)

    def test_bad_client_socket(self):
        """Test what happens when select is called on a dead client socket."""
        # close client stream while the server isn't looking then wait for the server to
        # notice

        stream, client_conn = self.server._server._connections.items()[0]
        # Wait for the client to disconnect
        self.client.notify_connected = WaitingMock()
        self.server.ioloop.add_callback(stream.close)
        # Wait for the client to be disconnected, and to connect again
        self.client.notify_connected.assert_wait_call_count(2)
        self.server.sync_with_ioloop()

        # check that client stream was removed from the KATCPServer
        self.assertTrue(
            stream not in self.server._server._connections,
            "Expected %r to not be in %r" %
            (stream, self.server._server._connections))
        # And check that the ClientConnection object was removed from the DeviceServer
        self.assertTrue(
            client_conn not in self.server._client_conns,
            "Expected %r to not be in %r" %
            (client_conn, self.server._client_conns))

    def test_sampling(self):
        """Test sensor sampling."""
        get_msgs = self.client.message_recorder(blacklist=self.BLACKLIST,
                                                replies=True)
        self.client.wait_protocol(timeout=1)
        self.client.request(
            katcp.Message.request("sensor-sampling", "an.int", "period",
                                  1 / 32.))

        # Wait for the request reply and for the sensor update messages to
        # arrive. We expect update one the moment the sensor-sampling request is
        # made, then four more over 4/32. of a second, resutling in 6
        # messages. Wait 0.75 of a period longer just to be sure we get everything.

        self.assertTrue(get_msgs.wait_number(6, timeout=4.75 / 32.))
        self.client.assert_request_succeeds("sensor-sampling", "an.int",
                                            "none")
        # Wait for reply to above request
        get_msgs.wait_number(7)
        msgs = get_msgs()
        updates = [x for x in msgs if x.name == "sensor-status"]
        others = [x for x in msgs if x.name != "sensor-status"]
        self.assertTrue(
            abs(len(updates) - 5) < 2,
            "Expected 5 informs, saw %d." % len(updates))

        self._assert_msgs_equal(others, [
            r"!sensor-sampling[1] ok an.int period %s" % (1 / 32.),
            r"!sensor-sampling[2] ok an.int none",
        ])

        self.assertEqual(updates[0].arguments[1:],
                         ["1", "an.int", "nominal", "3"])

        ## Now clear the strategies on this sensor
        # There should only be on connection to the server, so it should be
        # the test client
        client_conn = list(self.server._client_conns)[0]
        self.server.ioloop.add_callback(self.server.clear_strategies,
                                        client_conn)
        self.server.sync_with_ioloop()
        self.client.assert_request_succeeds("sensor-sampling",
                                            "an.int",
                                            args_equal=["an.int", "none"])

        # Check that we did not accidentally clobber the strategy datastructure
        # in the proccess
        self.client.assert_request_succeeds("sensor-sampling", "an.int",
                                            "period", 0.125)

    def test_add_remove_sensors(self):
        """Test adding and removing sensors from a running device."""
        an_int = self.server._sensors["an.int"]
        self.server.remove_sensor(an_int)
        # TODO remove_sensor test that checks that everything is indeed gone
        self.server.add_sensor(an_int)
        self.test_sampling()

    def test_async_request_handler(self):
        """
        Request handlers allowing other requests to be handled before replying

        """
        # We use a coroutine running on the client's ioloop for this test
        @gen.coroutine
        def do_async_request_test(threadsafe_future, tornado_future):
            try:
                slow_wait_time = 20
                # Kick off a slow, async request
                slow_f = self.client.future_request(
                    katcp.Message.request('slow-command', slow_wait_time))
                t0 = time.time()
                # Do another normal request that should reply before the slow
                # request
                reply, _ = yield self.client.future_request(
                    katcp.Message.request('watchdog'))
                self.assertTrue(reply.reply_ok(),
                                'Normal request should succeed')
                # Normal request should reply quickly
                t1 = time.time()
                self.assertTrue(
                    t1 - t0 < 0.1 * slow_wait_time,
                    'The normal request should reply almost immediately')
                # Slow request should still be outstanding
                self.assertFalse(
                    slow_f.done(),
                    'slow async request should not be complete yet')
                # Now cancel the slow command
                reply, _ = yield self.client.future_request(
                    katcp.Message.request('cancel-slow-command'))
                self.assertTrue(reply.reply_ok(),
                                '?cancel-slow-command should succeed')
                slow_reply, _ = yield slow_f
                self.assertTrue(
                    slow_reply.reply_ok(),
                    '?slow-command should succeed after being cancelled')
                t2 = time.time()
                self.assertTrue(
                    t2 - t1 < 0.1 * slow_wait_time,
                    'Slow request should be cancelled almost immediately')
            except Exception as exc:
                tornado_future.set_exc_info(sys.exc_info())
                threadsafe_future.set_exception(exc)
            else:
                threadsafe_future.set_result(None)

        tornado_future = gen.Future()
        threadsafe_future = Future()
        self.client.ioloop.add_callback(do_async_request_test,
                                        threadsafe_future, tornado_future)
        try:
            threadsafe_future.result()
        except Exception:
            # Use the tornado future to get a usable traceback
            tornado_future.result()
Пример #33
0
class test_AsyncClientIntegrated(tornado.testing.AsyncTestCase, TestUtilMixin):
    def setUp(self):
        super(test_AsyncClientIntegrated, self).setUp()
        self.server = DeviceTestServer('', 0)
        self.server.set_ioloop(self.io_loop)
        self.server.set_concurrency_options(thread_safe=False,
                                            handler_thread=False)
        self.server.start()

        host, port = self.server.bind_address
        self.client = katcp.CallbackClient(host, port)
        self.client.set_ioloop(self.io_loop)
        self.client.start()

    @tornado.testing.gen_test
    def test_timeout_of_until_connected(self):
        # Test for timing out
        with self.assertRaises(tornado.gen.TimeoutError):
            yield self.client.until_connected(timeout=0.0001)
        # Test for NOT timing out
        host, port = self.server.bind_address
        client2 = katcp.CallbackClient(host, port)
        client2.set_ioloop(self.io_loop)
        client2.start()
        yield client2.until_connected(timeout=0.5)
        self.assertTrue(client2.is_connected)

    @tornado.testing.gen_test
    def test_timeout_of_until_protocol(self):
        # Test for timing out
        with self.assertRaises(tornado.gen.TimeoutError):
            yield self.client.until_protocol(timeout=0.0001)
        # Test for NOT timing out
        host, port = self.server.bind_address
        client2 = katcp.CallbackClient(host, port)
        client2.set_ioloop(self.io_loop)
        client2.start()
        yield client2.until_protocol(timeout=0.5)

    @tornado.testing.gen_test
    def test_future_request_simple(self):
        yield self.client.until_connected()
        reply, informs = yield self.client.future_request(
            Message.request('watchdog'))
        self.assertEqual(len(informs), 0)
        self.assertEqual(reply.name, "watchdog")
        self.assertEqual(reply.arguments, ["ok"])

    @tornado.testing.gen_test
    def test_future_request_with_informs(self):
        yield self.client.until_connected()
        reply, informs = yield self.client.future_request(
            Message.request('help'))
        self.assertEqual(reply.name, "help")
        self.assertEqual(reply.arguments, ["ok", "%d" % NO_HELP_MESSAGES])
        self.assertEqual(len(informs), NO_HELP_MESSAGES)

    @tornado.testing.gen_test
    def test_disconnect_cleanup(self):
        yield self.client.until_protocol()
        mid = 55
        future_reply = self.client.future_request(
            Message.request('slow-command', 1, mid=mid))
        # Force a disconnect
        self.client._disconnect()
        reply, informs = yield future_reply
        self.assertEqual(
            reply,
            Message.reply('slow-command',
                          'fail',
                          'Connection closed before reply was received',
                          mid=mid))

    @tornado.testing.gen_test
    def test_stop_cleanup(self):
        yield self.client.until_protocol()
        mid = 564
        future_reply = self.client.future_request(
            Message.request('slow-command', 1, mid=mid))
        # Stop client
        self.client.stop()
        reply, informs = yield future_reply
        self.assertEqual(
            reply,
            Message.reply('slow-command',
                          'fail',
                          'Client stopped before reply was received',
                          mid=mid))
Пример #34
0
class TestCallbackClient(unittest.TestCase, TestUtilMixin):
    def setUp(self):
        self.addCleanup(self.stop_server_client)
        self.server = DeviceTestServer('', 0)
        self.server.start(timeout=0.1)

        host, port = self.server.bind_address

        self.client = katcp.CallbackClient(host, port)
        self.client.start(timeout=1)
        self.assertTrue(self.client.wait_protocol(timeout=1))

    def stop_server_client(self):
        if hasattr(self, 'client') and self.client.running():
            self.client.stop()
            self.client.join()
        if hasattr(self, 'server') and self.server.running():
            self.server.stop()
            self.server.join()

    def test_callback_request(self):
        """Test callback request."""

        watchdog_replies = []
        watchdog_replied = threading.Event()

        def watchdog_reply(reply):
            self.assertEqual(reply.name, "watchdog")
            self.assertEqual(reply.arguments, ["ok"])
            watchdog_replies.append(reply)
            watchdog_replied.set()

        self.assertTrue(self.client.wait_protocol(0.5))
        self.client.callback_request(
            Message.request("watchdog"),
            reply_cb=watchdog_reply,
        )

        watchdog_replied.wait(0.5)
        self.assertTrue(watchdog_replies)

        help_replies = []
        help_informs = []
        help_replied = threading.Event()

        def help_reply(reply):
            self.assertEqual(reply.name, "help")
            self.assertEqual(reply.arguments, ["ok", "%d" % NO_HELP_MESSAGES])
            self.assertEqual(len(help_informs), int(reply.arguments[1]))
            help_replies.append(reply)
            help_replied.set()

        def help_inform(inform):
            self.assertEqual(inform.name, "help")
            self.assertEqual(len(inform.arguments), 2)
            help_informs.append(inform)

        self.client.callback_request(
            Message.request("help"),
            reply_cb=help_reply,
            inform_cb=help_inform,
        )

        help_replied.wait(1)
        self.assertTrue(help_replied.isSet())
        help_replied.clear()
        help_replied.wait(0.05)  # Check if (unwanted) late help replies arrive
        self.assertFalse(help_replied.isSet())
        self.assertEqual(len(help_replies), 1)
        self.assertEqual(len(help_informs), NO_HELP_MESSAGES)

    def test_callback_request_mid(self):
        ## Test that the client does the right thing with message identifiers

        # Wait for the client to detect the server protocol. Server should
        # support message identifiers
        self.assertTrue(self.client.wait_protocol(0.2))
        # Replace send_message so that we can check the message
        self.client.send_message = mock.Mock(wraps=self.client.send_message)

        # By default message identifiers should be enabled, and should start
        # counting at 1
        cb = counting_callback(number_of_calls=3)(lambda *x: x)
        self.client.callback_request(Message.request('watchdog'), reply_cb=cb)
        self.client.callback_request(Message.request('watchdog'), reply_cb=cb)
        self.client.callback_request(Message.request('watchdog'), reply_cb=cb)
        cb.assert_wait()
        # Extract Message object .mid attributes from the mock calls to
        # send_message
        mids = [
            args[0].mid  # args[0] should be the Message() object
            for args, kwargs in self.client.send_message.call_args_list
        ]
        self.assertEqual(mids, ['1', '2', '3'])
        self.client.send_message.reset_mock()

        # Explicitly ask for no mid to be used
        cb = counting_callback(number_of_calls=1)(lambda *x: x)
        self.client.callback_request(Message.request('watchdog'),
                                     use_mid=False,
                                     reply_cb=cb)
        cb.assert_wait()
        mid = self.client.send_message.call_args[0][0].mid
        self.assertEqual(mid, None)

        # Ask for a specific mid to be used
        self.client.send_message.reset_mock()
        cb = counting_callback(number_of_calls=1)(lambda *x: x)
        self.client.callback_request(Message.request('watchdog', mid=42),
                                     reply_cb=cb)
        cb.assert_wait()
        mid = self.client.send_message.call_args[0][0].mid
        self.assertEqual(mid, '42')

        ## Check situation for a katcpv4 server
        self.client._server_supports_ids = False

        # Should fail if an mid is passed
        reply = [None]

        @counting_callback()
        def cb(msg):
            reply[0] = msg

        self.client.callback_request(Message.request('watchdog', mid=42),
                                     reply_cb=cb)
        cb.assert_wait()
        self.assertFalse(reply[0].reply_ok())

        # Should fail if an mid is requested
        reply = [None]
        cb.reset()
        self.client.callback_request(Message.request('watchdog'),
                                     use_mid=True,
                                     reply_cb=cb)
        cb.assert_wait()
        self.assertFalse(reply[0].reply_ok())

        # Should use no mid by default
        self.client.send_message.reset_mock()
        cb = counting_callback(number_of_calls=1)(lambda *x: x)
        self.client.callback_request(Message.request('watchdog'), reply_cb=cb)
        cb.assert_wait(timeout=1)
        mid = self.client.send_message.call_args[0][0].mid
        self.assertEqual(mid, None)

    def test_no_callback(self):
        """Test request without callback."""

        help_messages = []
        help_completed = threading.Event()

        def handle_help_message(client, msg):
            help_messages.append(msg)
            if msg.mtype == msg.REPLY:
                help_completed.set()

        self.client._inform_handlers["help"] = handle_help_message
        self.client._reply_handlers["help"] = handle_help_message
        # Set client._last_msg_id so we know that the ID is. Should be
        # _last_msg_id + 1
        self.client._last_msg_id = 0
        self.assertTrue(self.client.wait_protocol(0.2))
        self.client.callback_request(Message.request("help"))
        help_completed.wait(1)
        self.assertTrue(help_completed.isSet())

        self._assert_msgs_like(help_messages,
                               [("#help[1] ", "")] * NO_HELP_MESSAGES +
                               [("!help[1] ok %d" % NO_HELP_MESSAGES, "")])

    def test_timeout(self):
        self._test_timeout()

    def test_timeout_nomid(self, use_mid=False):
        self._test_timeout(use_mid=False)

    def _test_timeout(self, use_mid=None):
        """Test requests that timeout."""

        replies = []
        replied = threading.Event()
        informs = []
        timeout = 0.001

        @counting_callback()
        def reply_cb(msg):
            replies.append(msg)
            replied.set()

        def inform_cb(msg):
            informs.append(msg)

        self.assertTrue(self.client.wait_protocol(0.2))
        self.client.callback_request(
            Message.request("slow-command", "0.1"),
            use_mid=use_mid,
            reply_cb=reply_cb,
            inform_cb=inform_cb,
            timeout=timeout,
        )

        reply_cb.assert_wait(1)
        self.client.request(Message.request('cancel-slow-command'))
        msg = replies[0]
        self.assertEqual(msg.name, "slow-command")
        self.assertFalse(msg.reply_ok())
        self.assertRegexpMatches(
            msg.arguments[1],
            r"Request slow-command timed out after 0\..* seconds.")
        self.assertEqual(len(remove_version_connect(informs)), 0)
        self.assertEqual(len(replies), 1)

        del replies[:]
        del informs[:]
        reply_cb.reset()

        # test next request succeeds
        self.client.callback_request(
            Message.request("slow-command", "0.05"),
            reply_cb=reply_cb,
            inform_cb=inform_cb,
        )

        reply_cb.assert_wait()
        self.assertEqual(len(replies), 1)
        self.assertEqual(len(informs), 0)
        self.assertEqual([msg.name for msg in replies + informs],
                         ["slow-command"] * len(replies + informs))
        self.assertEqual([msg.arguments for msg in replies], [["ok"]])

    def test_timeout_nocb(self):
        """Test requests that timeout with no callbacks."""
        # Included to test https://katfs.kat.ac.za/mantis/view.php?id=1722
        # Situation can occur during a race between the timeout handler and the
        # receipt of a reply -- the reply can arrive after the timeout timer has
        # expired but before the request has been popped off the stack with
        # client._pop_async_request(). The normal request handler then pops off
        # the request first, resulting in the timeout handler getting a bunch of
        # None's. It should handle this gracefully.

        # Running the handler with a fake msg_id should have the same result as
        # running it after a real request has already been popped. The expected
        # result is that no assertions are raised.

        # NM 2014-09-26: This is probably no longer an issue with the tornado-based client
        # implementatin, but leaving the test for good measure
        f = Future()

        @gen.coroutine
        def cb():
            self.client._handle_timeout('fake_msg_id', time.time())

        self.client.ioloop.add_callback(lambda: gen.chain_future(cb(), f))
        f.result(timeout=1)

    def test_user_data(self):
        """Test callbacks with user data."""
        help_replies = []
        help_informs = []
        done = threading.Event()

        def help_reply(reply, x, y):
            self.assertEqual(reply.name, "help")
            self.assertEqual(x, 5)
            self.assertEqual(y, "foo")
            help_replies.append(reply)
            done.set()

        def help_inform(inform, x, y):
            self.assertEqual(inform.name, "help")
            self.assertEqual(x, 5)
            self.assertEqual(y, "foo")
            help_informs.append(inform)

        self.client.callback_request(Message.request("help"),
                                     reply_cb=help_reply,
                                     inform_cb=help_inform,
                                     user_data=(5, "foo"))

        done.wait(1)
        # Wait a bit longer to see if spurious replies arrive
        time.sleep(0.01)
        self.assertEqual(len(help_replies), 1)
        self.assertEqual(len(remove_version_connect(help_informs)),
                         NO_HELP_MESSAGES)

    def test_fifty_thread_mayhem(self):
        """Test using callbacks from fifty threads simultaneously."""
        num_threads = 50
        # map from thread_id -> (replies, informs)
        results = {}
        # list of thread objects
        threads = []

        def reply_cb(reply, thread_id):
            results[thread_id][0].append(reply)
            results[thread_id][2].set()

        def inform_cb(inform, thread_id):
            results[thread_id][1].append(inform)

        def worker(thread_id, request):
            self.client.callback_request(
                request.copy(),
                reply_cb=reply_cb,
                inform_cb=inform_cb,
                user_data=(thread_id, ),
            )

        request = Message.request("help")

        for thread_id in range(num_threads):
            results[thread_id] = ([], [], threading.Event())

        for thread_id in range(num_threads):
            thread = threading.Thread(target=worker, args=(thread_id, request))
            threads.append(thread)

        for thread in threads:
            thread.start()

        for thread in threads:
            thread.join()

        for thread_id in range(num_threads):
            replies, informs, done = results[thread_id]
            done.wait(5.0)
            self.assertTrue(done.isSet())
            self.assertEqual(len(replies), 1)
            self.assertEqual(replies[0].arguments[0], "ok")
            informs = remove_version_connect(informs)
            if len(informs) != NO_HELP_MESSAGES:
                print thread_id, len(informs)
                print[x.arguments[0] for x in informs]
            self.assertEqual(len(informs), NO_HELP_MESSAGES)

    def test_blocking_request(self):
        """Test the callback client's blocking request."""
        reply, informs = self.client.blocking_request(
            Message.request("help"), )

        self.assertEqual(reply.name, "help")
        self.assertEqual(reply.arguments, ["ok", "%d" % NO_HELP_MESSAGES])
        self.assertEqual(len(remove_version_connect(informs)),
                         NO_HELP_MESSAGES)

        reply, informs = self.client.blocking_request(Message.request(
            "slow-command", "0.5"),
                                                      timeout=0.001)

        self.assertEqual(reply.name, "slow-command")
        self.assertEqual(reply.arguments[0], "fail")
        self.assertRegexpMatches(
            reply.arguments[1],
            r"Request slow-command timed out after 0\..* seconds.")

    def test_blocking_request_mid(self):
        ## Test that the blocking client does the right thing with message
        ## identifiers

        # Wait for the client to detect the server protocol. Server should
        # support message identifiers
        self.assertTrue(self.client.wait_protocol(1))
        # Replace send_message so that we can check the message
        self.client.send_message = mock.Mock()

        # By default message identifiers should be enabled, and should start
        # counting at 1
        self.client.blocking_request(Message.request('watchdog'), timeout=0)
        self.client.blocking_request(Message.request('watchdog'), timeout=0)
        self.client.blocking_request(Message.request('watchdog'), timeout=0)
        # Extract Message object .mid attributes from the mock calls to
        # send_message
        mids = [
            args[0].mid  # arg[0] should be the Message() object
            for args, kwargs in self.client.send_message.call_args_list
        ]
        self.assertEqual(mids, ['1', '2', '3'])
        self.client.send_message.reset_mock()

        # Explicitly ask for no mid to be used
        self.client.blocking_request(Message.request('watchdog'),
                                     use_mid=False,
                                     timeout=0)
        mid = self.client.send_message.call_args[0][0].mid
        self.assertEqual(mid, None)

        # Ask for a specific mid to be used
        self.client.send_message.reset_mock()
        self.client.blocking_request(Message.request('watchdog', mid=42),
                                     timeout=0)
        mid = self.client.send_message.call_args[0][0].mid
        self.assertEqual(mid, '42')

        ## Check situation for a katcpv4 server
        self.client._server_supports_ids = False

        # Should fail if an mid is passed
        reply, inform = self.client.blocking_request(Message.request(
            'watchdog', mid=42),
                                                     timeout=0)
        self.assertFalse(reply.reply_ok())

        # Should fail if an mid is requested
        reply, inform = self.client.blocking_request(
            Message.request('watchdog'), use_mid=True, timeout=0)
        self.assertFalse(reply.reply_ok())

        # Should use no mid by default
        self.client.send_message.reset_mock()
        self.client.blocking_request(Message.request('watchdog'), timeout=0)
        mid = self.client.send_message.call_args[0][0].mid
        self.assertEqual(mid, None)

    def test_request_fail_on_raise(self):
        """Test that the callback is called even if send_message raises
           KatcpClientError."""
        def raise_error(msg, timeout=None):
            raise katcp.KatcpClientError("Error %s" % msg.name)

        self.client.send_message = raise_error

        replies = []

        @counting_callback()
        def reply_cb(msg):
            replies.append(msg)

        self.client.callback_request(
            Message.request("foo"),
            reply_cb=reply_cb,
        )

        reply_cb.assert_wait()
        self.assertEqual(len(replies), 1)
        self.assertEqual(replies[0].name, "foo")
        self.assertEqual(replies[0].arguments, ["fail", "Error foo"])

    def test_stop_cleanup(self):
        self.client.wait_protocol(timeout=1)
        mid = 56
        future_reply = Future()
        self.client.ioloop.add_callback(lambda: gen.chain_future(
            self.client.future_request(
                Message.request('slow-command', 1, mid=mid)), future_reply))
        # Force a disconnect
        self.client.stop()
        reply, informs = future_reply.result(timeout=1)
        self.assertEqual(
            reply,
            Message.reply('slow-command',
                          'fail',
                          'Client stopped before reply was received',
                          mid=mid))
Пример #35
0
 def _setup_server(self):
     self.server = DeviceTestServer('', 0)
     self.server.set_concurrency_options(thread_safe=False,
                                         handler_thread=False)
     start_thread_with_cleanup(self, self.server, start_timeout=1)
Пример #36
0
import threading
import signal

import tornado
import IPython

from katcp.testutils import DeviceTestServer

from katcp import resource_client, inspecting_client


log = logging.getLogger(__name__)

ioloop = tornado.ioloop.IOLoop.current()

d = DeviceTestServer("", 0)
d.set_concurrency_options(False, False)
d.set_ioloop(ioloop)
ioloop.add_callback(d.start)


def setup_resource_client():
    global rc
    print d.bind_address
    rc = resource_client.KATCPClientResource(dict(name="thething", address=d.bind_address, controlled=True))
    rc.start()


def printy(*args):
    print args
class TestInspectingClientAsyncStateCallback(tornado.testing.AsyncTestCase):
    longMessage = True
    maxDiff = None

    def setUp(self):
        super(TestInspectingClientAsyncStateCallback, self).setUp()
        self.server = DeviceTestServer('', 0)
        start_thread_with_cleanup(self, self.server, start_timeout=1)
        self.host, self.port = self.server.bind_address
        self.state_cb_future = tornado.concurrent.Future()
        self.client = InspectingClientAsync(self.host, self.port,
                                            ioloop=self.io_loop)
        # Set a short initial_resync timeout to make resync tests quick
        self.client.initial_resync_timeout = 0.001
        self.client.set_state_callback(self._test_state_cb)
        self.done_state_cb_futures = []
        self.cnt_state_cb_futures = collections.defaultdict(tornado.concurrent.Future)

    def _test_state_cb(self, state, model_changes):
        f = self.state_cb_future
        self.state_cb_future = tornado.concurrent.Future()
        self.done_state_cb_futures.append(f)
        num_calls = len(self.done_state_cb_futures)
        f.set_result((state, model_changes))
        self.cnt_state_cb_futures[num_calls].set_result(None)

    @tornado.gen.coroutine
    def _check_cb_count(self, expected_count):
        """Let the ioloop run and assert that the callback has been called
        the expected number of times"""
        yield tornado.gen.moment
        self.assertEqual(len(self.done_state_cb_futures), expected_count)

    @tornado.testing.gen_test(timeout=1)
    def test_from_connect(self):
        # Hold back #version-connect informs
        num_calls_before = len(self.done_state_cb_futures)
        logger.debug('before starting client, num_calls_before:{}'.format(num_calls_before))

        self.server.proceed_on_client_connect.clear()
        self.client.connect()

        state, model_changes = yield self.state_cb_future
        self.assertEqual(state, inspecting_client.InspectingClientStateType(
            connected=False, synced=False, model_changed=False, data_synced=False))

        state, model_changes = yield self.state_cb_future
        self.assertEqual(state, inspecting_client.InspectingClientStateType(
            connected=True, synced=False, model_changed=False, data_synced=False))
        self.assertIs(model_changes, None)
        # Due to the structure of the state loop the initial state may be sent
        # twice before we get here, and was the case for the initial
        # implementation. Changes made on 2015-01-26 caused it to happen only
        # once, hence + 1. If the implementation changes having + 2 would also
        # be OK. As, indeed, changes made on 2016-11-03 caused again :)
        yield self._check_cb_count(num_calls_before + 2)

        # Now let the server send #version-connect informs
        num_calls_before = len(self.done_state_cb_futures)
        self.server.ioloop.add_callback(self.server.proceed_on_client_connect.set)
        # We're expecting two calls hard on each other's heels, so lets wait for them
        yield self.cnt_state_cb_futures[num_calls_before + 2]
        # We expected two status callbacks, and no more after
        yield self._check_cb_count(num_calls_before + 2)
        state, model_changes = yield self.done_state_cb_futures[-2]
        state2, model_changes2 = yield self.done_state_cb_futures[-1]
        self.assertEqual(state, inspecting_client.InspectingClientStateType(
            connected=True, synced=False, model_changed=True, data_synced=True))
        # Check that the expected model changes came from the callback
        self._test_expected_model_changes(model_changes)
        self.assertEqual(state2, inspecting_client.InspectingClientStateType(
            connected=True, synced=True, model_changed=False, data_synced=True))
        self.assertEqual(model_changes2, None)

    def _test_expected_model_changes(self, model_changes):
        # Check that the model_changes reflect the sensors and requests of the
        # test sever (self.server)
        server_sensors = self.server._sensors.keys()
        server_requests = self.server._request_handlers.keys()
        self.assertEqual(model_changes, dict(
            sensors=dict(added=set(server_sensors), removed=set()),
            requests=dict(added=set(server_requests), removed=set())))


    @tornado.testing.gen_test(timeout=1)
    def test_reconnect(self):
        self.client.connect()
        yield self.client.until_synced()
        yield tornado.gen.moment   # Make sure the ioloop is 'caught up'

        # cause a disconnection and check that the callback is called
        num_calls_before = len(self.done_state_cb_futures)
        self.server.stop()
        self.server.join()
        state, model_changes = yield self.state_cb_future
        self.assertEqual(state, inspecting_client.InspectingClientStateType(
            connected=False, synced=False, model_changed=False, data_synced=False))
        self.assertIs(model_changes, None)
        yield self._check_cb_count(num_calls_before + 1)

    @tornado.gen.coroutine
    def _test_inspection_error(self, break_var, break_message):
        # Test that the client retries if there is an error in the inspection
        # process
        setattr(self.server, break_var, break_message)

        self.client.connect()
        # Wait for the client to be connected
        yield self.client.until_connected()
        # Wait for the state loop to send another update or 2
        yield self.state_cb_future
        state, _ = yield self.state_cb_future
        # Check that data is still not synced
        self.assertFalse(state.synced)
        self.assertFalse(state.data_synced)

        # Now fix the inspection request, client should sync up.
        setattr(self.server, break_var, False)
        # Check that the server's sensors and request are reflected in the model
        # changes.
        #
        changes_state = inspecting_client.InspectingClientStateType(
            connected=True, synced=False, model_changed=True, data_synced=True)
        yield self.client.until_state(changes_state)
        state, model_changes = self.done_state_cb_futures[-1].result()
        assert state == changes_state
        self._test_expected_model_changes(model_changes)
        yield self.client.until_synced()
        self.assertTrue(self.client.synced)



    @tornado.testing.gen_test(timeout=1)
    def test_help_inspection_error(self):
        yield self._test_inspection_error('break_help', 'Help is broken')

    @tornado.testing.gen_test(timeout=1)
    def test_sensor_list_inspection_error(self):
        yield self._test_inspection_error(
            'break_sensor_list', 'Sensor-list is broken')
class TestInspectingClientAsync(tornado.testing.AsyncTestCase):

    def setUp(self):
        super(TestInspectingClientAsync, self).setUp()
        self.server = DeviceTestServer('', 0)
        start_thread_with_cleanup(self, self.server, start_timeout=1)
        self.host, self.port = self.server.bind_address

        self.client = InspectingClientAsync(self.host, self.port,
                                            ioloop=self.io_loop)
        self.io_loop.add_callback(self.client.connect)

    @tornado.testing.gen_test
    def test_timeout_of_until_synced(self):
        # Test for timing out
        with self.assertRaises(tornado.gen.TimeoutError):
            yield self.client.until_synced(timeout=0.00001)
        # Test for NOT timing out
        yield self.client.until_synced(timeout=0.5)

    @tornado.testing.gen_test
    def test_simple_request(self):
        """Perform a basic request."""
        yield self.client.until_synced()
        reply, informs = yield self.client.simple_request('help', 'watchdog')
        self.assertIn('ok', str(reply))
        self.assertEquals(len(informs), 1)

    @tornado.testing.gen_test
    def test_sensor(self):
        """Access the sensor with the Async client."""
        yield self.client.until_synced()
        sensor_name = 'an.int'
        sensor = yield self.client.future_get_sensor(sensor_name)
        self.assertEquals(sensor.name, sensor_name)
        self.assertEquals(sensor.stype, 'integer')

        # Unknown sensor requests return a None.
        sensor_name = 'thing.unknown_sensor'
        sensor = yield self.client.future_get_sensor(sensor_name)
        self.assertIsNone(sensor)

    @tornado.testing.gen_test
    def test_request_access(self):
        """Test access to requests."""
        yield self.client.until_synced()

        request_name = 'watchdog'
        self.assertIn(request_name, self.client.requests)
        request = yield self.client.future_get_request(request_name)
        self.assertEqual(request.name, request_name)
        self.assertTrue(request.description,
                        'Expected an description: got nothing.')

        # Unknown request return a None.
        request_name = 'watchcat'
        self.assertNotIn(request_name, self.client.requests)
        request = yield self.client.future_get_request(request_name)
        self.assertIsNone(request)

    @tornado.testing.gen_test
    def test_sensor_add_remove(self):
        """Test a sensor being added and then remove it."""
        yield self.client.until_synced()

        sensor = DeviceTestSensor(Sensor.INTEGER, "another.int",
                                  "An Integer.",
                                  "count", [-5, 5], timestamp=time.time(),
                                  status=Sensor.NOMINAL, value=3)
        # Check that the sensor does not exist currently
        self.assertNotIn(sensor.name, self.client.sensors)

        # Add a sensor.
        self.server.add_sensor(sensor)
        self.server.mass_inform(Message.inform('interface-changed'))
        # Do a blocking request to ensure #interface-changed has been received
        yield self.client.simple_request('watchdog')
        yield self.client.until_synced()
        self.assertIn('another.int', self.client.sensors)

        # Remove a sensor.
        self.server.remove_sensor(sensor)
        self.server.mass_inform(Message.inform('interface-changed'))
        # Do a blocking request to ensure #interface-changed has been received
        yield self.client.simple_request('watchdog')

        yield self.client.until_synced()
        self.assertNotIn('another.int', self.client.sensors)


    @tornado.testing.gen_test
    def test_request_add_remove(self):
        """Test a request being added and then remove it."""
        yield self.client.until_synced()

        def request_sparkling_new(self, req, msg):
            """A new command."""
            return Message.reply(msg.name, "ok", "bling1", "bling2")

        # Check that the request did not exist before
        self.assertNotIn('sparkling-new', self.client.requests)

        # Add a request.
        self.server.request_sparkling_new = request_sparkling_new
        self.server._request_handlers['sparkling-new'] = request_sparkling_new
        self.server.mass_inform(Message.inform('interface-changed'))
        # Do a blocking request to ensure #interface-changed has been received
        yield self.client.simple_request('watchdog')

        yield self.client.until_synced()
        self.assertIn('sparkling-new', self.client.requests)
        req = yield self.client.future_get_request('sparkling-new')
        self.assertEqual(req.name, 'sparkling-new')

        # Remove a request.
        self.server.request_sparkling_new = None
        del(self.server._request_handlers['sparkling-new'])
        self.server.mass_inform(Message.inform('interface-changed'))
        # Do a blocking request to ensure #interface-changed has been received
        self.client.simple_request('watchdog')
        yield self.client.until_synced()
        self.assertNotIn('sparkling_new', self.client.requests)

    @tornado.testing.gen_test
    def test_send_request(self):
        """Very high level test.

        Calling methods to insure they do not raise exception.

        """
        client = InspectingClientAsync(self.host, self.port,
                                       ioloop=self.io_loop,
                                       initial_inspection=False)

        yield client.connect()
        yield client.until_connected()
        yield client.until_synced()
        self.assertEquals(len(client.sensors), 0)

        self.assertEquals(len(client.requests), 0)
        self.assertTrue(client.synced)
        self.assertTrue(client.is_connected())
        self.assertTrue(client.connected)
        yield client.simple_request('sensor-sampling', 'an.int', 'event')
        # Wait for sync and check if the sensor was automaticaly added.
        # Get the sensor object and see if it has data.
        sensor = yield client.future_get_sensor('an.int')
        self.assertEquals(len(client.sensors), 1)
        self.assertTrue(sensor.read())
        self.assertEquals(len(client.requests), 0)

    @tornado.testing.gen_test
    def test_handle_sensor_value(self):
        yield self.client.until_connected()
        # Test that #sensor-value informs are handles like #sensor-inform informs if
        # handle_sensor_value() is called.
        sens = yield self.client.future_get_sensor('an.int')
        test_val = 1911
        # Check that the sensor doesn't start out with our test value
        self.assertNotEqual(sens.read().value, test_val)
        # Now set the value of the sensor on the server
        server_sens = self.server.get_sensor('an.int')
        server_sens.set_value(test_val)
        # Do a request to ensure we are synced with the server
        yield self.client.simple_request('watchdog')
        # Test that the value has not yet been set on the client sensor (there are no
        # strategies set)
        self.assertNotEqual(sens.read().value, test_val)
        # Now do a ?sensor-value request, and check that our object is NOT YET updated,
        # since we have not called handle_sensor_value() yet
        yield self.client.simple_request('sensor-value', 'an.int')
        self.assertNotEqual(sens.read().value, test_val)

        # Test call
        self.client.handle_sensor_value()

        # Now do a ?sensor-value request, and check that our object is updated
        yield self.client.simple_request('sensor-value', 'an.int')
        self.assertEqual(sens.read().value, test_val)

    @tornado.testing.gen_test
    def test_factories(self):
        yield self.client.until_connected()
        # Test that the correct factories are used to construct sensor and request
        # objects, and that the factories are called with the correct parameters.
        sf = self.client.sensor_factory = mock.Mock()
        rf = self.client.request_factory = mock.Mock()

        sen = yield self.client.future_get_sensor('an.int')
        req = yield self.client.future_get_request('watchdog')
        self.assertIs(sen, sf.return_value)
        sf.assert_called_once_with(
            units='count', sensor_type=0, params=[-5, 5],
            description='An Integer.', name='an.int')
        self.assertIs(req, rf.return_value)
        rf.assert_called_once_with(
            name='watchdog', description=mock.ANY, timeout_hint=None)
Пример #39
0
class TestDeviceClientIntegrated(unittest.TestCase, TestUtilMixin):

    def setUp(self):
        self.server = DeviceTestServer('', 0)
        start_thread_with_cleanup(self, self.server, start_timeout=1)

        host, port = self.server.bind_address

        self.client = katcp.DeviceClient(host, port)
        self.client.enable_thread_safety()
        start_thread_with_cleanup(self, self.client, start_timeout=1)
        self.client.wait_connected(timeout=1)

    def test_versions(self):
        """Test that the versions parameter is populated."""
        preamble_done = self.client.handle_reply = WaitingMock()
        # Do a request and wait for it to be done so that we can be sure we received the
        # full connection-header before testing
        self.client.request(Message.request('watchdog'))
        preamble_done.assert_wait_call_count(1, timeout=1)
        versions = self.client.versions
        self.assertIn('katcp', ' '.join(versions['katcp-library']))
        self.assertIn('device', ' '.join(versions['katcp-device']))
        self.assertTrue(' '.join(versions['katcp-protocol']))

    def test_request(self):
        """Test request method."""
        self.assertTrue(self.client.wait_protocol(1))
        self.client.send_request(Message.request("watchdog"))
        self.client._server_supports_ids = False
        with self.assertRaises(katcp.core.KatcpVersionError):
            self.client.send_request(Message.request("watchdog", mid=56))
        self.client._server_supports_ids = True
        self.client.send_request(Message.request("watchdog", mid=55))

        msgs = self.server.until_messages(2).result(timeout=1)
        self._assert_msgs_equal(msgs, [
            r"?watchdog",
            r"?watchdog[55]",
        ])

    def test_send_message(self):
        """Test send_message method."""
        self.client.send_message(Message.inform("random-inform"))

        msgs = self.server.until_messages(1).result(timeout=1)
        self._assert_msgs_equal(msgs, [
            r"#random-inform",
        ])

    def test_stop_and_restart(self):
        """Test stopping and then restarting a client."""
        self.client.wait_running(timeout=1)
        before_threads = threading.enumerate()
        self.client.stop(timeout=1)
        self.client.join(timeout=1)
        # Test that we have fewer threads than before
        self.assertLess(len(threading.enumerate()), len(before_threads))
        self.assertFalse(self.client._running.isSet())
        self.client.start(timeout=1)
        self.client.wait_running(timeout=1)
        # Now we should have the original number of threads again
        self.assertEqual(len(threading.enumerate()), len(before_threads))

    def test_is_connected(self):
        """Test is_connected method."""
        self.assertTrue(self.client.is_connected())
        # Use client.notify_connected to synchronise to the disconnection
        disconnected = threading.Event()
        self.client.notify_connected = (
            lambda connected: disconnected.set() if not connected else None)
        self.server.stop(timeout=1.)
        # Restart server during cleanup to keep teardown happy
        self.addCleanup(self.server.start)
        self.server.join(timeout=1.)
        # Wait for the client to be disconnected
        disconnected.wait(1.5)
        self.assertFalse(self.client.is_connected())

    def test_wait_connected(self):
        """Test wait_connected method."""
        start = time.time()
        # Ensure that we are connected at the start of the test
        self.assertTrue(self.client.wait_connected(1.0))
        # Check that we did not wait more than the timeout.
        self.assertTrue(time.time() - start < 1.0)
        # Now we will cause the client to disconnect by stopping the server, and then
        # checking that wait_connected returns fails, and waits approximately the right
        # amount of time
        # Use client.notify_connected to synchronise to the disconnection
        disconnected = threading.Event()
        self.client.notify_connected = (
            lambda connected: disconnected.set() if not connected else None)
        self.server.stop(timeout=0.1)
        self.server.join(timeout=1)
        # Restart server during cleanup to keep teardown happy
        self.addCleanup(self.server.start)
        # Wait for the client to be disconnected
        disconnected.wait(1)
        # Now check that wait_connected returns false
        start = time.time()
        self.assertFalse(self.client.wait_connected(0.1))
        # And waited more or less the timeout.
        self.assertTrue(0.05 < time.time() - start <= 0.15)

    def test_bad_socket(self):
        """Test what happens when select is called on a dead socket."""
        # wait for client to connect
        self.client.wait_connected(timeout=1)
        f = Future()
        def notify_connected(connected):
            if connected:
                f.set_result(connected)
        self.client.notify_connected = notify_connected

        # close stream while the client isn't looking
        # then wait for the client to notice
        stream = self.client._stream
        sockname = stream.socket.getpeername()
        self.client.ioloop.add_callback(stream.close)
        f.result(timeout=1.25)

        # check that client reconnected
        self.assertTrue(stream is not self.client._stream,
                        "Expected %r to not be %r" % (stream, self.client._stream))
        self.assertEqual(sockname, self.client._stream.socket.getpeername())
Пример #40
0
class TestInspectingClientAsyncStateCallback(tornado.testing.AsyncTestCase):
    longMessage = True
    maxDiff = None

    def setUp(self):
        super(TestInspectingClientAsyncStateCallback, self).setUp()
        self.server = DeviceTestServer('', 0)
        start_thread_with_cleanup(self, self.server, start_timeout=1)
        self.host, self.port = self.server.bind_address
        self.state_cb_future = tornado.concurrent.Future()
        self.client = InspectingClientAsync(self.host, self.port,
                                            ioloop=self.io_loop)
        # Set a short initial_resync timeout to make resync tests quick
        self.client.initial_resync_timeout = 0.001
        self.client.set_state_callback(self._test_state_cb)
        self.done_state_cb_futures = []
        self.cnt_state_cb_futures = collections.defaultdict(tornado.concurrent.Future)

    def _test_state_cb(self, state, model_changes):
        f = self.state_cb_future
        self.state_cb_future = tornado.concurrent.Future()
        self.done_state_cb_futures.append(f)
        num_calls = len(self.done_state_cb_futures)
        f.set_result((state, model_changes))
        self.cnt_state_cb_futures[num_calls].set_result(None)

    @tornado.gen.coroutine
    def _check_cb_count(self, expected_count):
        """Let the ioloop run and assert that the callback has been called
        the expected number of times"""
        yield tornado.gen.moment
        self.assertEqual(len(self.done_state_cb_futures), expected_count)

    @tornado.testing.gen_test(timeout=1)
    def test_from_connect(self):
        # Hold back #version-connect informs
        num_calls_before = len(self.done_state_cb_futures)
        logger.debug('before starting client, num_calls_before:{}'.format(num_calls_before))

        self.server.proceed_on_client_connect.clear()
        self.client.connect()

        state, model_changes = yield self.state_cb_future
        self.assertEqual(state, inspecting_client.InspectingClientStateType(
            connected=False, synced=False, model_changed=False, data_synced=False))

        state, model_changes = yield self.state_cb_future
        self.assertEqual(state, inspecting_client.InspectingClientStateType(
            connected=True, synced=False, model_changed=False, data_synced=False))
        self.assertIs(model_changes, None)
        # Due to the structure of the state loop the initial state may be sent
        # twice before we get here, and was the case for the initial
        # implementation. Changes made on 2015-01-26 caused it to happen only
        # once, hence + 1. If the implementation changes having + 2 would also
        # be OK. As, indeed, changes made on 2016-11-03 caused again :)
        yield self._check_cb_count(num_calls_before + 2)

        # Now let the server send #version-connect informs
        num_calls_before = len(self.done_state_cb_futures)
        self.server.ioloop.add_callback(self.server.proceed_on_client_connect.set)
        # We're expecting two calls hard on each other's heels, so lets wait for them
        yield self.cnt_state_cb_futures[num_calls_before + 2]
        # We expected two status callbacks, and no more after
        yield self._check_cb_count(num_calls_before + 2)
        state, model_changes = yield self.done_state_cb_futures[-2]
        state2, model_changes2 = yield self.done_state_cb_futures[-1]
        self.assertEqual(state, inspecting_client.InspectingClientStateType(
            connected=True, synced=False, model_changed=True, data_synced=True))
        # Check that the expected model changes came from the callback
        self._test_expected_model_changes(model_changes)
        self.assertEqual(state2, inspecting_client.InspectingClientStateType(
            connected=True, synced=True, model_changed=False, data_synced=True))
        self.assertEqual(model_changes2, None)

    def _test_expected_model_changes(self, model_changes):
        # Check that the model_changes reflect the sensors and requests of the
        # test sever (self.server)
        server_sensors = self.server._sensors.keys()
        server_requests = self.server._request_handlers.keys()
        self.assertEqual(model_changes, dict(
            sensors=dict(added=set(server_sensors), removed=set()),
            requests=dict(added=set(server_requests), removed=set())))


    @tornado.testing.gen_test(timeout=1)
    def test_reconnect(self):
        self.client.connect()
        yield self.client.until_synced()
        yield tornado.gen.moment   # Make sure the ioloop is 'caught up'

        # cause a disconnection and check that the callback is called
        num_calls_before = len(self.done_state_cb_futures)
        self.server.stop()
        self.server.join()
        state, model_changes = yield self.state_cb_future
        self.assertEqual(state, inspecting_client.InspectingClientStateType(
            connected=False, synced=False, model_changed=False, data_synced=False))
        self.assertIs(model_changes, None)
        yield self._check_cb_count(num_calls_before + 1)

    @tornado.gen.coroutine
    def _test_inspection_error(self, break_var, break_message):
        # Test that the client retries if there is an error in the inspection
        # process
        setattr(self.server, break_var, break_message)

        self.client.connect()
        # Wait for the client to be connected
        yield self.client.until_connected()
        # Wait for the state loop to send another update or 2
        yield self.state_cb_future
        state, _ = yield self.state_cb_future
        # Check that data is still not synced
        self.assertFalse(state.synced)
        self.assertFalse(state.data_synced)

        # Now fix the inspection request, client should sync up.
        setattr(self.server, break_var, False)
        # Check that the server's sensors and request are reflected in the model
        # changes.
        #
        changes_state = inspecting_client.InspectingClientStateType(
            connected=True, synced=False, model_changed=True, data_synced=True)
        yield self.client.until_state(changes_state)
        state, model_changes = self.done_state_cb_futures[-1].result()
        assert state == changes_state
        self._test_expected_model_changes(model_changes)
        yield self.client.until_synced()
        self.assertTrue(self.client.synced)



    @tornado.testing.gen_test(timeout=1)
    def test_help_inspection_error(self):
        yield self._test_inspection_error('break_help', 'Help is broken')

    @tornado.testing.gen_test(timeout=1)
    def test_sensor_list_inspection_error(self):
        yield self._test_inspection_error(
            'break_sensor_list', 'Sensor-list is broken')
Пример #41
0
 def setUp(self):
     super(TestDeviceClientMemoryLeaks, self).setUp()
     self.server = DeviceTestServer('', 0)
     start_thread_with_cleanup(self, self.server, start_timeout=0.1)
     self.host, self.port = self.server.bind_address
Пример #42
0
class TestDeviceClientMemoryLeaks(tornado.testing.AsyncTestCase):
    def setUp(self):
        super(TestDeviceClientMemoryLeaks, self).setUp()
        self.server = DeviceTestServer('', 0)
        start_thread_with_cleanup(self, self.server, start_timeout=0.1)
        self.host, self.port = self.server.bind_address

    def use_on_managed_ioloop(self, client, timeout=0.1):
        client.start(timeout=timeout)
        client.wait_protocol(timeout=timeout)
        self.assertTrue(client.protocol_flags)
        client.stop(timeout=timeout)
        client.join(timeout=timeout)

    @gen.coroutine
    def use_on_unmanaged_ioloop(self, client, timeout=0.1):
        client.set_ioloop(self.io_loop)
        client.start()  # no timeout if starting from ioloop thread
        yield client.until_protocol(timeout=timeout)
        self.assertTrue(client.protocol_flags)
        client.stop(timeout=timeout)
        yield client.until_stopped(timeout=timeout)

    def test_no_memory_leak_managed_ioloop(self):
        client = katcp.DeviceClient(self.host, self.port)
        wr = weakref.ref(client)

        self.use_on_managed_ioloop(client)

        # clear strong reference and check if object can be garbage collected
        client = None
        gc.collect()
        self.assertIsNone(wr())

    @tornado.testing.gen_test
    def test_no_memory_leak_unmanaged_ioloop(self):
        client = katcp.DeviceClient(self.host, self.port)
        wr = weakref.ref(client)

        # use test's ioloop, so client does not create its own (i.e., unmanaged)
        yield self.use_on_unmanaged_ioloop(client)

        # clear strong reference and check if object can be garbage collected
        client = None
        gc.collect()
        self.assertIsNone(wr())

    @tornado.testing.gen_test
    def test_no_memory_leak_change_ioloop(self):
        client = katcp.DeviceClient(self.host, self.port)
        wr = weakref.ref(client)

        # start and stop client with managed ioloop
        self.use_on_managed_ioloop(client)

        # repeat with managed ioloop (new ioloop instance created)
        self.use_on_managed_ioloop(client)

        # change to unmanaged ioloop
        yield self.use_on_unmanaged_ioloop(client)

        # clear strong reference and check if object can be garbage collected
        client = None
        gc.collect()
        self.assertIsNone(wr())

    def test_no_memory_leak_stop_current_ioloop(self):
        # The DeviceClient may use the current ioloop during initialisation.
        # Similarly, the DeviceTestServer created in setUp() may have references to
        # the current ioloop.  We make a new instance to replace the current
        # ioloop, which we can stop without affecting the server.  This test aims
        # to verify that the client is not dependent on the ioloop that was
        # current at the time the client was initialised.  This is only for
        # the "managed" ioloop case.
        ioloop = tornado.ioloop.IOLoop()
        tornado.ioloop.IOLoop.make_current(ioloop)

        client = katcp.DeviceClient(self.host, self.port)
        wr = weakref.ref(client)
        # close ioloop that was current when client created
        ioloop.close()
        self.use_on_managed_ioloop(client)

        # clear strong reference and check if object can be garbage collected
        client = None
        gc.collect()
        self.assertIsNone(wr())

    def test_no_memory_leak_async_client(self):
        client = katcp.AsyncClient(self.host, self.port)
        wr = weakref.ref(client)

        self.use_on_managed_ioloop(client)

        # clear strong reference and check if object can be garbage collected
        client = None
        gc.collect()
        self.assertIsNone(wr())

    def test_no_memory_leak_callback_client(self):
        client = katcp.CallbackClient(self.host, self.port)
        wr = weakref.ref(client)

        self.use_on_managed_ioloop(client)

        # clear strong reference and check if object can be garbage collected
        client = None
        gc.collect()
        self.assertIsNone(wr())

    def test_no_memory_leak_stopped_server(self):
        client = katcp.DeviceClient(self.host, self.port)
        wr = weakref.ref(client)

        client.auto_reconnect_delay = 0.01
        client.start(timeout=0.1)
        client.wait_protocol(timeout=0.1)
        self.assertTrue(client.protocol_flags)
        # stop server before client
        self.server.stop()
        client.wait_disconnected(timeout=0.1)
        self.assertFalse(client.is_connected())
        client.stop(timeout=0.1)
        client.join(timeout=0.1)

        # clear strong reference and check if object can be garbage collected
        client = None
        gc.collect()
        self.assertIsNone(wr())

    def test_no_memory_leak_invalid_server_no_reconnect(self):
        bad_port = 0
        client = katcp.DeviceClient(self.host, bad_port, auto_reconnect=False)
        wr = weakref.ref(client)

        client.start(timeout=0.1)
        client.wait_connected(timeout=0.1)
        self.assertFalse(client.is_connected())
        client.stop(timeout=0.1)
        client.join(timeout=0.1)

        # clear strong reference and check if object can be garbage collected
        client = None
        gc.collect()
        self.assertIsNone(wr())

    def test_no_memory_leak_invalid_server_auto_reconnect(self):
        bad_port = 0
        client = katcp.DeviceClient(self.host, bad_port, auto_reconnect=True)
        wr = weakref.ref(client)

        client.auto_reconnect_delay = 0.01  # speed up test
        client.start(timeout=0.1)
        client.wait_connected(timeout=2 * client.auto_reconnect_delay)
        self.assertFalse(client.is_connected())
        client.stop(timeout=0.1)
        client.join(timeout=0.1)

        # clear strong reference and check if object can be garbage collected
        client = None
        gc.collect()
        self.assertIsNone(wr())
Пример #43
0
class TestInspectingClientAsyncStateCallback(tornado.testing.AsyncTestCase):

    def setUp(self):
        super(TestInspectingClientAsyncStateCallback, self).setUp()
        self.server = DeviceTestServer('', 0)
        start_thread_with_cleanup(self, self.server, start_timeout=1)
        self.host, self.port = self.server.bind_address
        self.state_cb_future = tornado.concurrent.Future()
        self.client = InspectingClientAsync(self.host, self.port,
                                            ioloop=self.io_loop)
        self.client.set_state_callback(self._test_state_cb)
        self.done_state_cb_futures = []
        self.cnt_state_cb_futures = collections.defaultdict(tornado.concurrent.Future)

    def _test_state_cb(self, state, model_changes):
        f = self.state_cb_future
        self.state_cb_future = tornado.concurrent.Future()
        self.done_state_cb_futures.append(f)
        num_calls = len(self.done_state_cb_futures)
        f.set_result((state, model_changes))
        self.cnt_state_cb_futures[num_calls].set_result(None)

    @tornado.gen.coroutine
    def _check_no_cb(self, no_expected):
        """Let the ioloop run and assert that the callback was not called"""
        yield tornado.gen.moment
        self.assertEqual(len(self.done_state_cb_futures), no_expected)

    @tornado.testing.gen_test(timeout=1)
    def test_from_connect(self):
        # Hold back #version-connect informs
        num_calls_before = len(self.done_state_cb_futures)
        logger.debug('before starting client, num_calls_before:{}'.format(num_calls_before))

        self.server.proceed_on_client_connect.clear()
        self.client.connect()

        state, model_changes = yield self.state_cb_future
        self.assertEqual(state, inspecting_client.InspectingClientStateType(
            connected=True, synced=False, model_changed=False, data_synced=False))
        self.assertIs(model_changes, None)
        # Due to the structure of the state loop the initial state may be sent twice
        # before we get her, and was the case for the initial implementation. Changes made
        # on 2015-01-26 caused it to happy only once, hence + 1. If the implementation
        # changes having + 2 would also be OK.
        yield self._check_no_cb(num_calls_before + 1)

        # Now let the server send #version-connect informs
        num_calls_before = len(self.done_state_cb_futures)
        self.server.ioloop.add_callback(self.server.proceed_on_client_connect.set)
        # We're expecting two calls hard on each other's heels, so lets wait for them
        yield self.cnt_state_cb_futures[num_calls_before + 2]
        # We expected two status callbacks, and no more after
        yield self._check_no_cb(num_calls_before + 2)
        state, model_changes = yield self.done_state_cb_futures[-2]
        state2, model_changes2 = yield self.done_state_cb_futures[-1]
        self.assertEqual(state, inspecting_client.InspectingClientStateType(
            connected=True, synced=False, model_changed=True, data_synced=True))
        server_sensors = self.server._sensors.keys()
        server_requests = self.server._request_handlers.keys()
        self.assertEqual(model_changes, dict(
            sensors=dict(added=set(server_sensors), removed=set()),
            requests=dict(added=set(server_requests), removed=set())))
        self.assertEqual(state2, inspecting_client.InspectingClientStateType(
            connected=True, synced=True, model_changed=False, data_synced=True))
        self.assertEqual(model_changes2, None)

    @tornado.testing.gen_test(timeout=1)
    def test_reconnect(self):
        self.client.connect()
        yield self.client.until_synced()
        yield tornado.gen.moment   # Make sure the ioloop is 'caught up'

        # cause a disconnection and check that the callback is called
        num_calls_before = len(self.done_state_cb_futures)
        self.server.stop()
        self.server.join()
        state, model_changes = yield self.state_cb_future
        self.assertEqual(state, inspecting_client.InspectingClientStateType(
            connected=False, synced=False, model_changed=False, data_synced=False))
        self.assertIs(model_changes, None)
        yield self._check_no_cb(num_calls_before + 1)
Пример #44
0
 def _setup_server(self):
     self.server = DeviceTestServer('', 0)
     start_thread_with_cleanup(self, self.server, start_timeout=1)
Пример #45
0
class test_KATCPClientResource_IntegratedTimewarp(TimewarpAsyncTestCase):
    def setUp(self):
        super(test_KATCPClientResource_IntegratedTimewarp, self).setUp()
        self.server = DeviceTestServer('', 0)
        start_thread_with_cleanup(self, self.server)
        self.host, self.port = self.server.bind_address
        self.default_resource_spec = dict(
            name='thething',
            address=self.server.bind_address,
            controlled=True)

    @tornado.gen.coroutine
    def _get_DUT_and_sync(self, resource_spec):
        DUT = resource_client.KATCPClientResource(self.default_resource_spec)
        DUT.start()
        yield DUT.until_state('synced')
        raise tornado.gen.Return(DUT)

    @tornado.testing.gen_test
    def test_disconnect(self):
        # Test that a device disconnect / reconnect is correctly handled
        DUT = yield self._get_DUT_and_sync(self.default_resource_spec)
        initial_reqs = set(DUT.req)
        initial_sensors = set(DUT.sensor)
        self.server.stop()
        self.server.join(timeout=1)
        yield DUT.until_state('disconnected')

        # Test that requests fail
        rep = yield DUT.req.watchdog()
        self.assertFalse(rep.succeeded)

        # Restart device so that we can reconnect
        self.server.start()
        # timewarp beyond reconect delay
        self.set_ioloop_time(self.ioloop_time + 1)
        yield DUT.until_state('syncing')
        yield DUT.until_state('synced')
        # check that sensors / requests are unchanged
        self.assertEqual(set(DUT.req), initial_reqs)
        self.assertEqual(set(DUT.sensor), initial_sensors)

        # Now disconnect and change the device, to check that it is properly resynced.
        self.server.stop()
        self.server.join(timeout=1)
        yield DUT.until_state('disconnected')

        # Add a new request to the server
        def request_sparkling_new(self, req, msg):
            """A new command."""
            return Message.reply(msg.name, "ok", "bling1", "bling2")
        self.server._request_handlers['sparkling-new'] = request_sparkling_new
        # Check that the request does not exist currently
        self.assertNotIn('sparkling_new', initial_reqs)

        # Add a new sensor to the server
        sensor = DeviceTestSensor(DeviceTestSensor.INTEGER, "another.int",
                                  "An Integer.",
                                  "count", [-5, 5], timestamp=self.io_loop.time(),
                                  status=DeviceTestSensor.NOMINAL, value=3)
        self.server.add_sensor(sensor)
        # Check that the sensor does not exist currently
        escaped_new_sensor = resource.escape_name(sensor.name)
        self.assertNotIn(resource.escape_name(sensor.name), initial_sensors)

        # Restart device so that we can reconnect
        self.server.start()
        # timewarp beyond reconect delay
        self.set_ioloop_time(self.ioloop_time + 1)
        yield DUT.until_state('syncing')
        yield DUT.until_state('synced')
        # check that sensors / requests are correctly updated
        self.assertEqual(set(DUT.req), initial_reqs | set(['sparkling_new']))
        self.assertEqual(set(DUT.sensor), initial_sensors | set([escaped_new_sensor]))

    @tornado.testing.gen_test(timeout=1000)
    def test_set_sensor_sampling(self):
        self.server.stop()
        self.server.join()
        DUT = resource_client.KATCPClientResource(self.default_resource_spec)
        DUT.start()
        yield tornado.gen.moment
        test_strategy = ('period', '2.5')
        yield DUT.set_sensor_strategy('an_int', test_strategy)
        # Double-check that the sensor does not yet exist
        self.assertNotIn('an_int', DUT.sensor)
        self.server.start()
        self.server.wait_running(timeout=1)
        advancer = TimewarpAsyncTestCaseTimeAdvancer(self, quantum=0.55)
        advancer.start()
        yield DUT.until_synced()
        self.assertEqual(DUT.sensor.an_int.sampling_strategy, test_strategy)

        # Now call set_sensor_strategy with a different strategy and check that it is
        # applied to the real sensor
        new_test_strategy = ('event',)
        yield DUT.set_sensor_strategy('an_int', new_test_strategy)
        self.assertEqual(DUT.sensor.an_int.sampling_strategy, new_test_strategy)

    @tornado.testing.gen_test(timeout=1000)
    def test_set_sensor_listener(self):
        self.server.stop()
        self.server.join()
        resource_spec = self.default_resource_spec
        DUT = resource_client.KATCPClientResource(resource_spec)
        DUT.start()
        yield tornado.gen.moment
        test_listener1 = lambda *x : None
        test_listener2 = lambda *y : None
        DUT.set_sensor_listener('an_int', test_listener1)
        # Double-check that the sensor does not yet exist
        self.assertNotIn('an_int', DUT.sensor)
        self.server.start()
        self.server.wait_running(timeout=1)
        advancer = TimewarpAsyncTestCaseTimeAdvancer(self, quantum=0.55)
        advancer.start()
        yield DUT.until_synced()
        self.assertTrue(DUT.sensor.an_int.is_listener, test_listener1)

        # Now call set_sensor_lister with a different listener and check that it is
        # also subscribed
        DUT.set_sensor_listener('an_int', test_listener2)
        self.assertTrue(DUT.sensor.an_int.is_listener, test_listener2)
Пример #46
0
class TestDeviceClientIntegrated(unittest.TestCase, TestUtilMixin):
    def setUp(self):
        self.server = DeviceTestServer('', 0)
        self.server.start(timeout=0.1)

        host, port = self.server._sock.getsockname()

        self.client = katcp.DeviceClient(host, port)
        self.client.start(timeout=0.1)

    def tearDown(self):
        if self.client.running():
            self.client.stop()
            self.client.join()
        if self.server.running():
            self.server.stop()
            self.server.join()

    def test_request(self):
        """Test request method."""
        self.assertTrue(self.client.wait_protocol(1))
        self.client.send_request(katcp.Message.request("watchdog"))
        self.client.send_request(katcp.Message.request("watchdog", mid=55))
        self.client._server_supports_ids = False
        with self.assertRaises(katcp.core.KatcpVersionError):
            self.client.send_request(katcp.Message.request("watchdog", mid=56))

        time.sleep(0.1)

        msgs = self.server.messages()
        self._assert_msgs_equal(msgs, [
            r"?watchdog",
            r"?watchdog[55]",
        ])

    def test_send_message(self):
        """Test send_message method."""
        self.client.send_message(katcp.Message.inform("random-inform"))

        time.sleep(0.1)

        msgs = self.server.messages()
        self._assert_msgs_equal(msgs, [
            r"#random-inform",
        ])

    def test_stop_and_restart(self):
        """Test stopping and then restarting a client."""
        self.client.stop(timeout=0.1)
        # timeout needs to be longer than select sleep.
        self.client.join(timeout=1.5)
        self.assertEqual(self.client._thread, None)
        self.assertFalse(self.client._running.isSet())
        self.client.start(timeout=0.1)

    def test_is_connected(self):
        """Test is_connected method."""
        self.assertTrue(self.client.is_connected())
        self.server.stop(timeout=0.1)
        # timeout needs to be longer than select sleep.
        self.server.join(timeout=1.5)
        self.assertFalse(self.client.is_connected())

    def test_wait_connected(self):
        """Test wait_connected method."""
        start = time.time()
        self.assertTrue(self.client.wait_connected(1.0))
        self.assertTrue(time.time() - start < 1.0)
        self.server.stop(timeout=0.1)
        # timeout needs to be longer than select sleep.
        self.server.join(timeout=1.5)
        start = time.time()
        self.assertFalse(self.client.wait_connected(0.2))
        self.assertTrue(0.15 < time.time() - start < 0.25)

    def test_bad_socket(self):
        """Test what happens when select is called on a dead socket."""
        # wait for client to connect
        time.sleep(0.1)

        # close socket while the client isn't looking
        # then wait for the client to notice
        sock = self.client._sock
        sockname = sock.getpeername()
        sock.close()
        time.sleep(1.25)

        # check that client reconnected
        self.assertTrue(sock is not self.client._sock,
                        "Expected %r to not be %r" % (sock, self.client._sock))
        self.assertEqual(sockname, self.client._sock.getpeername())

    def test_daemon_value(self):
        """Test passing in a daemon value to client start method."""
        self.client.stop(timeout=0.1)
        # timeout needs to be longer than select sleep.
        self.client.join(timeout=1.5)

        self.client.start(timeout=0.1, daemon=True)
        self.assertTrue(self.client._thread.isDaemon())

    def test_excepthook(self):
        """Test passing in an excepthook to client start method."""
        exceptions = []
        except_event = threading.Event()

        def excepthook(etype, value, traceback):
            """Keep track of exceptions."""
            exceptions.append(etype)
            except_event.set()

        self.client.stop(timeout=0.1)
        # timeout needs to be longer than select sleep.
        self.client.join(timeout=1.5)

        self.client.start(timeout=0.1, excepthook=excepthook)
        # force exception by deleteing _running
        old_running = self.client._running
        try:
            del self.client._running
            except_event.wait(1.5)
            self.assertEqual(exceptions, [AttributeError])
        finally:
            self.client._running = old_running
Пример #47
0
class TestDeviceServerClientIntegrated(unittest.TestCase, TestUtilMixin):

    BLACKLIST = ("version-connect", "version", "build-state")

    def setUp(self):
        self.server = DeviceTestServer('', 0)
        self.server.start(timeout=0.1)
        host, port = self.server._sock.getsockname()
        self.server_addr = (host, port)

        self.client = BlockingTestClient(self, host, port)
        self.client.start(timeout=0.1)
        self.assertTrue(self.client.wait_protocol(timeout=0.1))

    def tearDown(self):
        if self.client.running():
            self.client.stop()
            self.client.join()
        if self.server.running():
            self.server.stop()
            self.server.join()

    def test_log(self):
        get_msgs = self.client.message_recorder(
                blacklist=self.BLACKLIST,
                replies=True)

        with mock.patch('katcp.server.time.time') as m_time:
            m_time.return_value = 1234
            self.server.log.error('An error')
        get_msgs.wait_number(1)
        self._assert_msgs_equal(
            get_msgs(), [r"#log error 1234.000000 root An\_error"])

    def test_simple_connect(self):
        """Test a simple server setup and teardown with client connect."""
        get_msgs = self.client.message_recorder(
                blacklist=self.BLACKLIST,
                replies=True)
        # basic send
        self.client.request(katcp.Message.request("foo"), use_mid=False)

        # pipe-lined send
        self.client.raw_send("?bar-boom\r\n?baz\r")

        # broken up sends
        self.client.raw_send("?boo")
        self.client.raw_send("m arg1 arg2")
        self.client.raw_send("\n")

        time.sleep(0.1)

        self._assert_msgs_equal(get_msgs(), [
            r"!foo invalid Unknown\_request.",
            r"!bar-boom invalid Unknown\_request.",
            r"!baz invalid Unknown\_request.",
            r"!boom invalid Unknown\_request.",
        ])

    def test_bad_requests(self):
        """Test request failure paths in device server."""
        get_msgs = self.client.message_recorder(
                blacklist=self.BLACKLIST, replies=True)
        self.client.raw_send("bad msg\n")
        # wait for reply
        self.client.blocking_request(
            katcp.Message.request("watchdog"), use_mid=False)

        self._assert_msgs_like(get_msgs(), [
            (r"#log error", "KatcpSyntaxError:"
                            "\_Bad\_type\_character\_'b'.\\n"),
            (r"!watchdog ok", ""),
        ])

    def test_slow_client(self):
        # Test that server does not choke sending messages to slow clients
        self.server.send_timeout = 0.1    # Set a short sending timeout

        self.client.wait_protocol(1)
        slow_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        slow_sock.connect(self.server_addr)
        slow_sock.settimeout(0.1)
        # Send a bunch of request to the server, but don't read anything from
        # the server
        try:
            slow_sock.sendall('?help\n'*100000)
        except socket.timeout:
            pass

        t0 = time.time()
        # Request should not have taken a very long time.
        self.client.assert_request_succeeds('help', informs_count=NO_HELP_MESSAGES)
        self.assertTrue(time.time() - t0 < 1)


    def test_server_ignores_informs_and_replies(self):
        """Test server ignores informs and replies."""
        get_msgs = self.client.message_recorder(
                blacklist=self.BLACKLIST, replies=True)
        self.client.raw_send("#some inform\n")
        self.client.raw_send("!some reply\n")

        time.sleep(0.1)

        self.assertFalse(get_msgs())

    def test_standard_requests(self):
        """Test standard request and replies."""
        get_msgs = self.client.message_recorder(
                blacklist=self.BLACKLIST, replies=True)
        nomid_req = partial(self.client.request, use_mid=False)
        nomid_req(katcp.Message.request("watchdog"), use_mid=False)
        nomid_req(katcp.Message.request("restart"))
        nomid_req(katcp.Message.request("log-level"))
        nomid_req(katcp.Message.request("log-level", "trace"))
        nomid_req(katcp.Message.request("log-level", "unknown"))
        nomid_req(katcp.Message.request("help"))
        nomid_req(katcp.Message.request("help", "watchdog"))
        nomid_req(katcp.Message.request("help", "unknown-request"))
        nomid_req(katcp.Message.request("client-list"))
        nomid_req(katcp.Message.request("version-list"))
        nomid_req(katcp.Message.request("sensor-list"))
        nomid_req(katcp.Message.request("sensor-list", "an.int"))
        nomid_req(katcp.Message.request("sensor-list", "an.unknown"))
        nomid_req(katcp.Message.request("sensor-value"))
        nomid_req(katcp.Message.request("sensor-value", "an.int"))
        nomid_req(katcp.Message.request("sensor-value",
                                                  "an.unknown"))
        nomid_req(katcp.Message.request("sensor-sampling", "an.int"))
        nomid_req(katcp.Message.request("sensor-sampling", "an.int",
                                                  "differential", "2"))
        nomid_req(katcp.Message.request("sensor-sampling", "an.int",
                                                  "event-rate", "2", "3"))
        nomid_req(katcp.Message.request("sensor-sampling"))
        nomid_req(katcp.Message.request("sensor-sampling",
                                                  "an.unknown", "auto"))
        self.client.blocking_request(katcp.Message.request(
            "sensor-sampling", "an.int", "unknown"), use_mid=False)

        time.sleep(0.1)

        self.server.log.trace("trace-msg")
        self.server.log.debug("debug-msg")
        self.server.log.info("info-msg")
        self.server.log.warn("warn-msg")
        self.server.log.error("error-msg")
        self.server.log.fatal("fatal-msg")

        time.sleep(0.1)

        self.assertEqual(self.server.restart_queue.get_nowait(), self.server)
        self._assert_msgs_like(get_msgs(), [
            (r"!watchdog ok", ""),
            (r"!restart ok", ""),
            (r"!log-level ok warn", ""),
            (r"!log-level ok trace", ""),
            (r"!log-level fail Unknown\_logging\_level\_name\_'unknown'", ""),
            (r"#help cancel-slow-command Cancel\_slow\_command\_request,\_"
             "resulting\_in\_it\_replying\_immediately", ""),
            (r"#help client-list", ""),
            (r"#help halt", ""),
            (r"#help help", ""),
            (r"#help log-level", ""),
            (r"#help new-command", ""),
            (r"#help raise-exception", ""),
            (r"#help raise-fail", ""),
            (r"#help restart", ""),
            (r"#help sensor-list", ""),
            (r"#help sensor-sampling", ""),
            (r"#help sensor-sampling-clear", ""),
            (r"#help sensor-value", ""),
            (r"#help slow-command", ""),
            (r"#help version-list", ""),
            (r"#help watchdog", ""),
            (r"!help ok %d" % NO_HELP_MESSAGES, ""),
            (r"#help watchdog", ""),
            (r"!help ok 1", ""),
            (r"!help fail", ""),
            (r"#client-list", ""),
            (r"!client-list ok 1", ""),
            (r"#version-list katcp-protocol", ""),
            (r"#version-list katcp-library", ""),
            (r"#version-list katcp-device", ""),
            (r"!version-list ok 3", ""),
            (r"#sensor-list an.int An\_Integer. count integer -5 5", ""),
            (r"!sensor-list ok 1", ""),
            (r"#sensor-list an.int An\_Integer. count integer -5 5", ""),
            (r"!sensor-list ok 1", ""),
            (r"!sensor-list fail", ""),
            (r"#sensor-value 12345.000000 1 an.int nominal 3", ""),
            (r"!sensor-value ok 1", ""),
            (r"#sensor-value 12345.000000 1 an.int nominal 3", ""),
            (r"!sensor-value ok 1", ""),
            (r"!sensor-value fail", ""),
            (r"!sensor-sampling ok an.int none", ""),
            (r"#sensor-status 12345.000000 1 an.int nominal 3", ""),
            (r"!sensor-sampling ok an.int differential 2", ""),
            (r"#sensor-status 12345.000000 1 an.int nominal 3", ""),
            (r"!sensor-sampling ok an.int event-rate 2 3", ""),
            (r"!sensor-sampling fail No\_sensor\_name\_given.", ""),
            (r"!sensor-sampling fail Unknown\_sensor\_name:\_an.unknown.", ""),
            (r"!sensor-sampling fail Unknown\_strategy\_name:\_unknown.", ""),
            (r"#log trace", r"root trace-msg"),
            (r"#log debug", r"root debug-msg"),
            (r"#log info", r"root info-msg"),
            (r"#log warn", r"root warn-msg"),
            (r"#log error", r"root error-msg"),
            (r"#log fatal", r"root fatal-msg"),
        ])

    def test_standard_requests_with_ids(self):
        """Test standard request and replies with message ids."""
        get_msgs = self.client.message_recorder(
                blacklist=self.BLACKLIST, replies=True)

        current_id = [0]

        def mid():
            current_id[0] += 1
            return str(current_id[0])

        def mid_req(*args):
            return katcp.Message.request(*args, mid=mid())


        self.client.request(mid_req("watchdog"))
        self.client.request(mid_req("restart"))
        self.client.request(mid_req("log-level"))
        self.client.request(mid_req("log-level", "trace"))
        self.client.request(mid_req("log-level", "unknown"))
        self.client.request(mid_req("help"))
        self.client.request(mid_req("help", "watchdog"))
        self.client.request(mid_req("help", "unknown-request"))
        self.client.request(mid_req("client-list"))
        self.client.request(mid_req("version-list"))
        self.client.request(mid_req("sensor-list"))
        self.client.request(mid_req("sensor-list", "an.int"))
        self.client.request(mid_req("sensor-list", "an.unknown"))
        self.client.request(mid_req("sensor-value"))
        self.client.request(mid_req("sensor-value", "an.int"))
        self.client.request(mid_req("sensor-value", "an.unknown"))
        self.client._next_id = mid  # mock our mid generator for testing
        self.client.blocking_request(mid_req("sensor-sampling", "an.int"))
        self.client.blocking_request(mid_req(
            "sensor-sampling", "an.int", "differential", "2"))
        self.client.blocking_request(mid_req(
            "sensor-sampling", "an.int", "event-rate", "2", "3")),
        self.client.blocking_request(mid_req("sensor-sampling"))
        self.client.blocking_request(mid_req(
            "sensor-sampling", "an.unknown", "auto"))
        self.client.blocking_request(mid_req(
            "sensor-sampling", "an.int", "unknown"))

        self.server.log.trace("trace-msg")
        self.server.log.debug("debug-msg")
        self.server.log.info("info-msg")
        self.server.log.warn("warn-msg")
        self.server.log.error("error-msg")
        self.server.log.fatal("fatal-msg")

        time.sleep(0.1)

        self.assertEqual(self.server.restart_queue.get_nowait(), self.server)
        self._assert_msgs_like(get_msgs(), [
            (r"!watchdog[1] ok", ""),
            (r"!restart[2] ok", ""),
            (r"!log-level[3] ok warn", ""),
            (r"!log-level[4] ok trace", ""),
            (r"!log-level[5] fail Unknown\_logging\_level\_name\_'unknown'",
             ""),
            (r"#help[6] cancel-slow-command Cancel\_slow\_command\_request,\_"
             "resulting\_in\_it\_replying\_immediately", ""),
            (r"#help[6] client-list", ""),
            (r"#help[6] halt", ""),
            (r"#help[6] help", ""),
            (r"#help[6] log-level", ""),
            (r"#help[6] new-command", ""),
            (r"#help[6] raise-exception", ""),
            (r"#help[6] raise-fail", ""),
            (r"#help[6] restart", ""),
            (r"#help[6] sensor-list", ""),
            (r"#help[6] sensor-sampling", ""),
            (r"#help[6] sensor-sampling-clear", ""),
            (r"#help[6] sensor-value", ""),
            (r"#help[6] slow-command", ""),
            (r"#help[6] version-list", ""),
            (r"#help[6] watchdog", ""),
            (r"!help[6] ok %d" % NO_HELP_MESSAGES, ""),
            (r"#help[7] watchdog", ""),
            (r"!help[7] ok 1", ""),
            (r"!help[8] fail", ""),
            (r"#client-list[9]", ""),
            (r"!client-list[9] ok 1", ""),
            (r"#version-list[10] katcp-protocol", ""),
            (r"#version-list[10] katcp-library", ""),
            (r"#version-list[10] katcp-device", ""),
            (r"!version-list[10] ok 3", ""),
            (r"#sensor-list[11] an.int An\_Integer. count integer -5 5", ""),
            (r"!sensor-list[11] ok 1", ""),
            (r"#sensor-list[12] an.int An\_Integer. count integer -5 5", ""),
            (r"!sensor-list[12] ok 1", ""),
            (r"!sensor-list[13] fail", ""),
            (r"#sensor-value[14] 12345.000000 1 an.int nominal 3", ""),
            (r"!sensor-value[14] ok 1", ""),
            (r"#sensor-value[15] 12345.000000 1 an.int nominal 3", ""),
            (r"!sensor-value[15] ok 1", ""),
            (r"!sensor-value[16] fail", ""),
            (r"!sensor-sampling[17] ok an.int none", ""),
            (r"#sensor-status 12345.000000 1 an.int nominal 3", ""),
            (r"!sensor-sampling[18] ok an.int differential 2", ""),
            (r"#sensor-status 12345.000000 1 an.int nominal 3", ""),
            (r"!sensor-sampling[19] ok an.int event-rate 2 3", ""),
            (r"!sensor-sampling[20] fail No\_sensor\_name\_given.", ""),
            (r"!sensor-sampling[21] fail Unknown\_sensor\_name:\_an.unknown.", ""),
            (r"!sensor-sampling[22] fail Unknown\_strategy\_name:\_unknown.", ""),
            (r"#log trace", r"root trace-msg"),
            (r"#log debug", r"root debug-msg"),
            (r"#log info", r"root info-msg"),
            (r"#log warn", r"root warn-msg"),
            (r"#log error", r"root error-msg"),
            (r"#log fatal", r"root fatal-msg"),
        ])

    def test_sensor_list_regex(self):
        reply, informs = self.client.blocking_request(katcp.Message.request(
                "sensor-list", "/a.*/"), use_mid=False)
        self._assert_msgs_equal(informs + [reply], [
            r"#sensor-list an.int An\_Integer. count integer -5 5",
            r"!sensor-list ok 1",
        ])

        reply, informs = self.client.blocking_request(katcp.Message.request(
                "sensor-list", "//"), use_mid=False)
        self._assert_msgs_equal(informs + [reply], [
            r"#sensor-list an.int An\_Integer. count integer -5 5",
            r"!sensor-list ok 1",
        ])

        reply, informs = self.client.blocking_request(katcp.Message.request(
                "sensor-list", "/^int/"), use_mid=False)
        self._assert_msgs_equal(informs + [reply], [
            r"!sensor-list ok 0",
        ])

    def test_sensor_value_regex(self):
        reply, informs = self.client.blocking_request(katcp.Message.request(
                "sensor-value", "/a.*/"), use_mid=False)
        self._assert_msgs_equal(informs + [reply], [
            r"#sensor-value 12345.000000 1 an.int nominal 3",
            r"!sensor-value ok 1",
        ])

        reply, informs = self.client.blocking_request(katcp.Message.request(
                "sensor-value", "//"), use_mid=False)
        self._assert_msgs_equal(informs + [reply], [
            r"#sensor-value 12345.000000 1 an.int nominal 3",
            r"!sensor-value ok 1",
        ])

        reply, informs = self.client.blocking_request(katcp.Message.request(
                "sensor-value", "/^int/"), use_mid=False)
        self._assert_msgs_equal(informs + [reply], [
            r"!sensor-value ok 0",
        ])

    def test_client_list(self):
        reply, informs = self.client.blocking_request(
            katcp.Message.request('client-list'), use_mid=False)
        self.assertEqual(str(reply), '!client-list ok 1')
        self.assertEqual(len(informs), 1)
        inform = str(informs[0])
        self.assertTrue(inform.startswith('#client-list 127.0.0.1:'))
        _, addr = inform.split()
        host, port = addr.split(':')
        port = int(port)
        self.assertEqual((host, port), self.client._sock.getsockname())

    def test_halt_request(self):
        """Test halt request."""
        get_msgs = self.client.message_recorder(
                blacklist=self.BLACKLIST, replies=True)
        self.client.request(katcp.Message.request("halt"))
        # hack to hide re-connect exception
        self.client.connect = lambda: None
        self.server.join()
        time.sleep(0.1)

        self._assert_msgs_equal(get_msgs(), [
            r"!halt[1] ok",
            r"#disconnect Device\_server\_shutting\_down.",
        ])

    def test_bad_handlers(self):
        """Test that bad request and inform handlers are picked up."""
        try:

            class BadServer(katcp.DeviceServer):
                def request_baz(self, req, msg):
                    pass

        except AssertionError:
            pass
        else:
            self.fail("Server metaclass accepted missing request_ docstring.")

        try:

            class BadServer(katcp.DeviceServer):
                def inform_baz(self, req, msg):
                    pass

        except AssertionError:
            pass
        else:
            self.fail("Server metaclass accepted missing inform_ docstring.")

        class SortOfOkayServer(katcp.DeviceServer):
            request_bar = 1
            inform_baz = 2

        assert("bar" not in SortOfOkayServer._request_handlers)
        assert("baz" not in SortOfOkayServer._inform_handlers)

    def test_handler_exceptions(self):
        """Test handling of failure replies and other exceptions."""
        get_msgs = self.client.message_recorder(
                blacklist=self.BLACKLIST, replies=True)
        self.assertTrue(self.client.wait_protocol(timeout=1))

        self.client.request(katcp.Message.request("raise-exception"))
        self.client.request(katcp.Message.request("raise-fail"))

        time.sleep(0.1)

        self._assert_msgs_like(get_msgs(), [
            (r"!raise-exception[1] fail Traceback", ""),
            (r"!raise-fail[2] fail There\_was\_a\_problem\_with\_your\_request.",
             ""),
        ])

    def test_stop_and_restart(self):
        """Test stopping and restarting the device server."""
        self.server.stop(timeout=0.1)
        self.server.join(timeout=1.0)
        self.assertEqual(self.server._thread, None)
        self.assertFalse(self.server._running.isSet())
        self.server.start(timeout=1.0)

    def test_bad_client_socket(self):
        """Test what happens when select is called on a dead client socket."""
        # wait for client to arrive
        time.sleep(0.1)

        # close socket while the server isn't looking
        # then wait for the server to notice
        sock = self.server._socks[0]
        sock.close()
        time.sleep(0.75)

        # check that client was removed
        self.assertTrue(sock not in self.server._socks,
                        "Expected %r to not be in %r" %
                        (sock, self.server._socks))

    def test_bad_server_socket(self):
        """Test what happens when select is called on a dead server socket."""
        # wait for client to arrive
        time.sleep(0.1)

        # close socket while the server isn't looking
        # then wait for the server to notice
        sock = self.server._sock
        sockname = sock.getsockname()
        sock.close()
        time.sleep(0.75)

        # check that server restarted
        self.assertTrue(sock is not self.server._sock,
                        "Expected %r to not be %r" % (sock, self.server._sock))
        self.assertEqual(sockname, self.server._sock.getsockname())

    def test_daemon_value(self):
        """Test passing in a daemon value to server start method."""
        self.server.stop(timeout=0.1)
        self.server.join(timeout=1.0)

        self.server.start(timeout=0.1, daemon=True)
        self.assertTrue(self.server._thread.isDaemon())

    def test_excepthook(self):
        """Test passing in an excepthook to server start method."""
        exceptions = []
        except_event = threading.Event()

        def excepthook(etype, value, traceback):
            """Keep track of exceptions."""
            exceptions.append(etype)
            except_event.set()

        self.server.stop(timeout=0.1)
        self.server.join(timeout=1.5)

        self.server.start(timeout=0.1, excepthook=excepthook)
        # force exception by deleteing _running
        old_running = self.server._running
        try:
            del self.server._running
            except_event.wait(1.5)
            self.assertEqual(exceptions, [AttributeError])
        finally:
            self.server._running = old_running

        # close socket -- server didn't shut down correctly
        self.server._sock.close()
        self.server.stop(timeout=0.1)
        self.server.join(timeout=1.5)

        except_event.clear()
        del exceptions[:]
        self.server.start(timeout=0.1, excepthook=excepthook)
        # force exception in sample reactor and check that it makes
        # it back up
        reactor = self.server._reactor
        old_stop = reactor._stopEvent
        try:
            del reactor._stopEvent
            reactor._wakeEvent.set()
            except_event.wait(0.1)
            self.assertEqual(exceptions, [AttributeError])
        finally:
            reactor._stopEvent = old_stop

        # close socket -- server didn't shut down correctly
        self.server._sock.close()

    def test_sampling(self):
        """Test sensor sampling."""
        get_msgs = self.client.message_recorder(
                blacklist=self.BLACKLIST, replies=True)
        self.client.wait_protocol(timeout=1)
        self.client.request(katcp.Message.request(
            "sensor-sampling", "an.int", "period", 1/32.))

        # Wait for the request reply and for the sensor update messages to
        # arrive. We expect update one the moment the sensor-sampling request is
        # made, then four more over 4/32. of a second, resutling in 6
        # messages. Wait half a period longer just to be sure we get everything.

        self.assertTrue(get_msgs.wait_number(6, timeout=4.5/32.))
        self.client.assert_request_succeeds("sensor-sampling", "an.int", "none")
        # Wait for reply to above request
        get_msgs.wait_number(7)
        msgs = get_msgs()
        updates = [x for x in msgs if x.name == "sensor-status"]
        others = [x for x in msgs if x.name != "sensor-status"]
        self.assertTrue(abs(len(updates) - 5) < 2,
                        "Expected 5 informs, saw %d." % len(updates))

        self._assert_msgs_equal(others, [
            r"!sensor-sampling[1] ok an.int period %s" % (1/32.),
            r"!sensor-sampling[2] ok an.int none",
        ])

        self.assertEqual(updates[0].arguments[1:],
                         ["1", "an.int", "nominal", "3"])

        ## Now clear the strategies on this sensor
        # There should only be on connection to the server, so it should be
        # the test client
        client_conn = self.server._sock_connections.values()[0]
        self.server.clear_strategies(client_conn)
        self.client.assert_request_succeeds("sensor-sampling", "an.int",
                                            args_equal=["an.int", "none"])

        # Check that we did not accidentally clobber the strategy datastructure
        # in the proccess
        self.client.assert_request_succeeds(
            "sensor-sampling", "an.int", "period", 0.125)


    def test_add_remove_sensors(self):
        """Test adding and removing sensors from a running device."""
        an_int = self.server._sensors["an.int"]
        self.server.remove_sensor(an_int)
        self.server.add_sensor(an_int)
        self.test_sampling()
Пример #48
0
class TestCallbackClient(unittest.TestCase, TestUtilMixin):

    def setUp(self):
        self.addCleanup(self.stop_server_client)
        self.server = DeviceTestServer('', 0)
        self.server.start(timeout=0.1)

        host, port = self.server.bind_address

        self.client = katcp.CallbackClient(host, port)
        self.client.start(timeout=1)
        self.assertTrue(self.client.wait_protocol(timeout=1))

    def stop_server_client(self):
        if hasattr(self, 'client') and self.client.running():
            self.client.stop()
            self.client.join()
        if hasattr(self, 'server') and self.server.running():
            self.server.stop()
            self.server.join()

    def test_callback_request(self):
        """Test callback request."""

        watchdog_replies = []
        watchdog_replied = threading.Event()

        def watchdog_reply(reply):
            self.assertEqual(reply.name, "watchdog")
            self.assertEqual(reply.arguments, ["ok"])
            watchdog_replies.append(reply)
            watchdog_replied.set()

        self.assertTrue(self.client.wait_protocol(0.5))
        self.client.callback_request(
            Message.request("watchdog"),
            reply_cb=watchdog_reply,
        )

        watchdog_replied.wait(0.5)
        self.assertTrue(watchdog_replies)

        help_replies = []
        help_informs = []
        help_replied = threading.Event()

        def help_reply(reply):
            self.assertEqual(reply.name, "help")
            self.assertEqual(reply.arguments, ["ok", "%d" % NO_HELP_MESSAGES])
            self.assertEqual(len(help_informs), int(reply.arguments[1]))
            help_replies.append(reply)
            help_replied.set()

        def help_inform(inform):
            self.assertEqual(inform.name, "help")
            self.assertEqual(len(inform.arguments), 2)
            help_informs.append(inform)


        self.client.callback_request(
            Message.request("help"),
            reply_cb=help_reply,
            inform_cb=help_inform,
        )

        help_replied.wait(1)
        self.assertTrue(help_replied.isSet())
        help_replied.clear()
        help_replied.wait(0.05)   # Check if (unwanted) late help replies arrive
        self.assertFalse(help_replied.isSet())
        self.assertEqual(len(help_replies), 1)
        self.assertEqual(len(help_informs), NO_HELP_MESSAGES)

    def test_callback_request_mid(self):
        ## Test that the client does the right thing with message identifiers

        # Wait for the client to detect the server protocol. Server should
        # support message identifiers
        self.assertTrue(self.client.wait_protocol(0.2))
        # Replace send_message so that we can check the message
        self.client.send_message = mock.Mock(wraps=self.client.send_message)

        # By default message identifiers should be enabled, and should start
        # counting at 1
        cb = counting_callback(number_of_calls=3)(lambda *x: x)
        self.client.callback_request(Message.request('watchdog'), reply_cb=cb)
        self.client.callback_request(Message.request('watchdog'), reply_cb=cb)
        self.client.callback_request(Message.request('watchdog'), reply_cb=cb)
        cb.assert_wait()
        # Extract Message object .mid attributes from the mock calls to
        # send_message
        mids = [args[0].mid              # args[0] should be the Message() object
                for args, kwargs in self.client.send_message.call_args_list]
        self.assertEqual(mids, ['1','2','3'])
        self.client.send_message.reset_mock()

        # Explicitly ask for no mid to be used
        cb = counting_callback(number_of_calls=1)(lambda *x: x)
        self.client.callback_request(
            Message.request('watchdog'), use_mid=False, reply_cb=cb)
        cb.assert_wait()
        mid = self.client.send_message.call_args[0][0].mid
        self.assertEqual(mid, None)

        # Ask for a specific mid to be used
        self.client.send_message.reset_mock()
        cb = counting_callback(number_of_calls=1)(lambda *x: x)
        self.client.callback_request(
            Message.request('watchdog', mid=42), reply_cb=cb)
        cb.assert_wait()
        mid = self.client.send_message.call_args[0][0].mid
        self.assertEqual(mid, '42')

        ## Check situation for a katcpv4 server
        self.client._server_supports_ids = False

        # Should fail if an mid is passed
        reply = [None]
        @counting_callback()
        def cb(msg):
            reply[0] = msg
        self.client.callback_request(
            Message.request('watchdog', mid=42), reply_cb=cb)
        cb.assert_wait()
        self.assertFalse(reply[0].reply_ok())

        # Should fail if an mid is requested
        reply = [None]
        cb.reset()
        self.client.callback_request(
            Message.request('watchdog'), use_mid=True, reply_cb=cb)
        cb.assert_wait()
        self.assertFalse(reply[0].reply_ok())

        # Should use no mid by default
        self.client.send_message.reset_mock()
        cb = counting_callback(number_of_calls=1)(lambda *x: x)
        self.client.callback_request(Message.request('watchdog'), reply_cb=cb)
        cb.assert_wait(timeout=1)
        mid = self.client.send_message.call_args[0][0].mid
        self.assertEqual(mid, None)

    def test_no_callback(self):
        """Test request without callback."""

        help_messages = []
        help_completed = threading.Event()

        def handle_help_message(client, msg):
            help_messages.append(msg)
            if msg.mtype == msg.REPLY:
                help_completed.set()

        self.client._inform_handlers["help"] = handle_help_message
        self.client._reply_handlers["help"] = handle_help_message
        # Set client._last_msg_id so we know that the ID is. Should be
        # _last_msg_id + 1
        self.client._last_msg_id = 0
        self.assertTrue(self.client.wait_protocol(0.2))
        self.client.callback_request(Message.request("help"))
        help_completed.wait(1)
        self.assertTrue(help_completed.isSet())

        self._assert_msgs_like(help_messages,
            [("#help[1] ", "")] * NO_HELP_MESSAGES +
            [("!help[1] ok %d" % NO_HELP_MESSAGES, "")])

    def test_timeout(self):
        self._test_timeout()

    def test_timeout_nomid(self, use_mid=False):
        self._test_timeout(use_mid=False)

    def _test_timeout(self, use_mid=None):
        """Test requests that timeout."""

        replies = []
        replied = threading.Event()
        informs = []
        timeout = 0.001

        @counting_callback()
        def reply_cb(msg):
            replies.append(msg)
            replied.set()

        def inform_cb(msg):
            informs.append(msg)

        self.assertTrue(self.client.wait_protocol(0.2))
        self.client.callback_request(
            Message.request("slow-command", "0.1"),
            use_mid=use_mid,
            reply_cb=reply_cb,
            inform_cb=inform_cb,
            timeout=timeout,
        )

        reply_cb.assert_wait(1)
        self.client.request(Message.request('cancel-slow-command'))
        msg = replies[0]
        self.assertEqual(msg.name, "slow-command")
        self.assertFalse(msg.reply_ok())
        self.assertRegexpMatches(msg.arguments[1],
                                 r"Request slow-command timed out after 0\..* seconds.")
        self.assertEqual(len(remove_version_connect(informs)), 0)
        self.assertEqual(len(replies), 1)

        del replies[:]
        del informs[:]
        reply_cb.reset()

        # test next request succeeds
        self.client.callback_request(
            Message.request("slow-command", "0.05"),
            reply_cb=reply_cb,
            inform_cb=inform_cb,
        )

        reply_cb.assert_wait()
        self.assertEqual(len(replies), 1)
        self.assertEqual(len(informs), 0)
        self.assertEqual([msg.name for msg in replies + informs],
                         ["slow-command"] * len(replies + informs))
        self.assertEqual([msg.arguments for msg in replies], [["ok"]])

    def test_timeout_nocb(self):
        """Test requests that timeout with no callbacks."""
        # Included to test https://katfs.kat.ac.za/mantis/view.php?id=1722
        # Situation can occur during a race between the timeout handler and the
        # receipt of a reply -- the reply can arrive after the timeout timer has
        # expired but before the request has been popped off the stack with
        # client._pop_async_request(). The normal request handler then pops off
        # the request first, resulting in the timeout handler getting a bunch of
        # None's. It should handle this gracefully.

        # Running the handler with a fake msg_id should have the same result as
        # running it after a real request has already been popped. The expected
        # result is that no assertions are raised.

        # NM 2014-09-26: This is probably no longer an issue with the tornado-based client
        # implementatin, but leaving the test for good measure
        f = Future()
        @gen.coroutine
        def cb():
            self.client._handle_timeout('fake_msg_id', time.time())
        self.client.ioloop.add_callback(lambda : gen.chain_future(cb(), f))
        f.result(timeout=1)


    def test_user_data(self):
        """Test callbacks with user data."""
        help_replies = []
        help_informs = []
        done = threading.Event()

        def help_reply(reply, x, y):
            self.assertEqual(reply.name, "help")
            self.assertEqual(x, 5)
            self.assertEqual(y, "foo")
            help_replies.append(reply)
            done.set()

        def help_inform(inform, x, y):
            self.assertEqual(inform.name, "help")
            self.assertEqual(x, 5)
            self.assertEqual(y, "foo")
            help_informs.append(inform)

        self.client.callback_request(
            Message.request("help"),
            reply_cb=help_reply,
            inform_cb=help_inform,
            user_data=(5, "foo"))

        done.wait(1)
        # Wait a bit longer to see if spurious replies arrive
        time.sleep(0.01)
        self.assertEqual(len(help_replies), 1)
        self.assertEqual(len(remove_version_connect(help_informs)),
                         NO_HELP_MESSAGES)

    def test_fifty_thread_mayhem(self):
        """Test using callbacks from fifty threads simultaneously."""
        num_threads = 50
        # map from thread_id -> (replies, informs)
        results = {}
        # list of thread objects
        threads = []

        def reply_cb(reply, thread_id):
            results[thread_id][0].append(reply)
            results[thread_id][2].set()

        def inform_cb(inform, thread_id):
            results[thread_id][1].append(inform)

        def worker(thread_id, request):
            self.client.callback_request(
                request.copy(),
                reply_cb=reply_cb,
                inform_cb=inform_cb,
                user_data=(thread_id,),
            )

        request = Message.request("help")

        for thread_id in range(num_threads):
            results[thread_id] = ([], [], threading.Event())

        for thread_id in range(num_threads):
            thread = threading.Thread(target=worker, args=(thread_id, request))
            threads.append(thread)

        for thread in threads:
            thread.start()

        for thread in threads:
            thread.join()

        for thread_id in range(num_threads):
            replies, informs, done = results[thread_id]
            done.wait(5.0)
            self.assertTrue(done.isSet())
            self.assertEqual(len(replies), 1)
            self.assertEqual(replies[0].arguments[0], "ok")
            informs = remove_version_connect(informs)
            if len(informs) != NO_HELP_MESSAGES:
                print thread_id, len(informs)
                print [x.arguments[0] for x in informs]
            self.assertEqual(len(informs), NO_HELP_MESSAGES)

    def test_blocking_request(self):
        """Test the callback client's blocking request."""
        reply, informs = self.client.blocking_request(
            Message.request("help"),
        )

        self.assertEqual(reply.name, "help")
        self.assertEqual(reply.arguments, ["ok", "%d" % NO_HELP_MESSAGES])
        self.assertEqual(len(remove_version_connect(informs)), NO_HELP_MESSAGES)

        reply, informs = self.client.blocking_request(
            Message.request("slow-command", "0.5"),
            timeout=0.001)

        self.assertEqual(reply.name, "slow-command")
        self.assertEqual(reply.arguments[0], "fail")
        self.assertRegexpMatches(
            reply.arguments[1],
            r"Request slow-command timed out after 0\..* seconds.")

    def test_blocking_request_mid(self):
        ## Test that the blocking client does the right thing with message
        ## identifiers

        # Wait for the client to detect the server protocol. Server should
        # support message identifiers
        self.assertTrue(self.client.wait_protocol(1))
        # Replace send_message so that we can check the message
        self.client.send_message = mock.Mock()

        # By default message identifiers should be enabled, and should start
        # counting at 1
        self.client.blocking_request(Message.request('watchdog'), timeout=0)
        self.client.blocking_request(Message.request('watchdog'), timeout=0)
        self.client.blocking_request(Message.request('watchdog'), timeout=0)
        # Extract Message object .mid attributes from the mock calls to
        # send_message
        mids = [args[0].mid              # arg[0] should be the Message() object
                for args, kwargs in self.client.send_message.call_args_list]
        self.assertEqual(mids, ['1','2','3'])
        self.client.send_message.reset_mock()

        # Explicitly ask for no mid to be used
        self.client.blocking_request(Message.request('watchdog'),
                                     use_mid=False, timeout=0)
        mid = self.client.send_message.call_args[0][0].mid
        self.assertEqual(mid, None)

        # Ask for a specific mid to be used
        self.client.send_message.reset_mock()
        self.client.blocking_request(Message.request(
            'watchdog', mid=42), timeout=0)
        mid = self.client.send_message.call_args[0][0].mid
        self.assertEqual(mid, '42')

        ## Check situation for a katcpv4 server
        self.client._server_supports_ids = False

        # Should fail if an mid is passed
        reply, inform = self.client.blocking_request(Message.request(
                'watchdog', mid=42), timeout=0)
        self.assertFalse(reply.reply_ok())

        # Should fail if an mid is requested
        reply, inform = self.client.blocking_request(
            Message.request('watchdog'), use_mid=True, timeout=0)
        self.assertFalse(reply.reply_ok())

        # Should use no mid by default
        self.client.send_message.reset_mock()
        self.client.blocking_request(Message.request(
            'watchdog'), timeout=0)
        mid = self.client.send_message.call_args[0][0].mid
        self.assertEqual(mid, None)

    def test_request_fail_on_raise(self):
        """Test that the callback is called even if send_message raises
           KatcpClientError."""
        def raise_error(msg, timeout=None):
            raise katcp.KatcpClientError("Error %s" % msg.name)
        self.client.send_message = raise_error

        replies = []

        @counting_callback()
        def reply_cb(msg):
            replies.append(msg)

        self.client.callback_request(Message.request("foo"),
            reply_cb=reply_cb,
        )

        reply_cb.assert_wait()
        self.assertEqual(len(replies), 1)
        self.assertEqual(replies[0].name, "foo")
        self.assertEqual(replies[0].arguments, ["fail", "Error foo"])

    def test_stop_cleanup(self):
        self.client.wait_protocol(timeout=1)
        mid = 56
        future_reply = Future()
        self.client.ioloop.add_callback(
            lambda : gen.chain_future(self.client.future_request(Message.request(
                'slow-command', 1, mid=mid)), future_reply))
        # Force a disconnect
        self.client.stop()
        reply, informs = future_reply.result(timeout=1)
        self.assertEqual(reply, Message.reply(
            'slow-command', 'fail', 'Client stopped before reply was received', mid=mid))
Пример #49
0
 def setUp(self):
     self.server = DeviceTestServer('', 0)
Пример #50
0
class test_KATCPClientResource_Integrated(tornado.testing.AsyncTestCase):
    def setUp(self):
        super(test_KATCPClientResource_Integrated, self).setUp()
        self.server = DeviceTestServer('', 0)
        start_thread_with_cleanup(self, self.server)
        self.host, self.port = self.server.bind_address
        self.default_resource_spec = dict(
            name='thething',
            address=self.server.bind_address,
            controlled=True)

    @tornado.gen.coroutine
    def _get_DUT_and_sync(self, resource_spec):
        DUT = resource_client.KATCPClientResource(self.default_resource_spec)
        DUT.start()
        yield DUT.until_state('synced')
        raise tornado.gen.Return(DUT)

    @tornado.testing.gen_test(timeout=1)
    def test_requests(self):
        DUT = yield self._get_DUT_and_sync(self.default_resource_spec)
        # Check that all the test-device requests are listed
        self.assertEqual(sorted(DUT.req),
                         sorted(n.replace('-', '_')
                                for n in self.server.request_names))

    @tornado.testing.gen_test(timeout=1)
    def test_active(self):
        DUT = yield self._get_DUT_and_sync(self.default_resource_spec)
        self.assertTrue(DUT.is_active(), 'Expect DUT to be active initialy')
        reply = yield DUT.req.new_command()
        self.assertTrue(reply.succeeded, 'Expect request to be succesful in active state')

        # Set DUT to 'inactive'
        DUT.set_active(False)
        with self.assertRaises(resource.KATCPResourceInactive):
            # Should raise if we attempt to do the request when inactive
            yield DUT.req.new_command()

        # Set DUT to back to 'active'
        DUT.set_active(True)
        reply = yield DUT.req.new_command()
        self.assertTrue(reply.succeeded, 'Expect request to be succesful in active state')


    @tornado.testing.gen_test(timeout=1)
    def test_sensors(self):
       DUT = yield self._get_DUT_and_sync(self.default_resource_spec)
       # Check that all the test-device sensors are listed
       self.assertEqual(sorted(DUT.sensor),
                        sorted(n.replace('-', '_').replace('.', '_')
                               for n in self.server.sensor_names))

    @tornado.testing.gen_test(timeout=1)
    def test_interface_change(self):
        DUT = yield self._get_DUT_and_sync(self.default_resource_spec)
        sensors_before = set(DUT.sensor)
        reqs_before = set(DUT.req)

        # Add a new sensor to the server
        sensor = DeviceTestSensor(DeviceTestSensor.INTEGER, "another.int",
                                  "An Integer.",
                                  "count", [-5, 5], timestamp=self.io_loop.time(),
                                  status=DeviceTestSensor.NOMINAL, value=3)
        self.server.add_sensor(sensor)
        # Check that the sensor does not exist currently
        self.assertNotIn(resource.escape_name(sensor.name), sensors_before)

        # Add a new request to the server
        def request_sparkling_new(self, req, msg):
            """A new command."""
            return Message.reply(msg.name, "ok", "bling1", "bling2")
        self.server._request_handlers['sparkling-new'] = request_sparkling_new
        # Check that the request did not exist before
        self.assertNotIn('sparkling-new', reqs_before)

        # Issue #interface-changed
        self.server.mass_inform(Message.inform('interface-changed'))
        yield DUT.until_state('syncing')
        yield DUT.until_state('synced')

        # Check if sensor/request was added
        self.assertEqual(set(DUT.sensor) - sensors_before, set(['another_int']))
        self.assertEqual(set(DUT.req) - reqs_before, set(['sparkling_new']))

        # And now remove them again
        self.server._request_handlers.pop('sparkling-new')
        self.server.remove_sensor('another.int')

        # Issue #interface-changed
        self.server.mass_inform(Message.inform('interface-changed'))
        yield DUT.until_state('syncing')
        yield DUT.until_state('synced')

        # Check if sensor/request was removed
        self.assertEqual(set(DUT.sensor), sensors_before)
        self.assertEqual(set(DUT.req), reqs_before)
Пример #51
0
class TestDeviceClientIntegrated(unittest.TestCase, TestUtilMixin):
    def setUp(self):
        self.server = DeviceTestServer('', 0)
        start_thread_with_cleanup(self, self.server, start_timeout=1)

        host, port = self.server.bind_address

        self.client = katcp.DeviceClient(host, port)
        self.client.enable_thread_safety()
        start_thread_with_cleanup(self, self.client, start_timeout=1)
        self.client.wait_connected(timeout=1)

    def test_versions(self):
        """Test that the versions parameter is populated."""
        preamble_done = self.client.handle_reply = WaitingMock()
        # Do a request and wait for it to be done so that we can be sure we received the
        # full connection-header before testing
        self.client.request(Message.request('watchdog'))
        preamble_done.assert_wait_call_count(1, timeout=1)
        versions = self.client.versions
        self.assertIn('katcp', ' '.join(versions['katcp-library']))
        self.assertIn('device', ' '.join(versions['katcp-device']))
        self.assertTrue(' '.join(versions['katcp-protocol']))

    def test_request(self):
        """Test request method."""
        self.assertTrue(self.client.wait_protocol(1))
        self.client.send_request(Message.request("watchdog"))
        self.client._server_supports_ids = False
        with self.assertRaises(katcp.core.KatcpVersionError):
            self.client.send_request(Message.request("watchdog", mid=56))
        self.client._server_supports_ids = True
        self.client.send_request(Message.request("watchdog", mid=55))

        msgs = self.server.until_messages(2).result(timeout=1)
        self._assert_msgs_equal(msgs, [
            r"?watchdog",
            r"?watchdog[55]",
        ])

    def test_send_message(self):
        """Test send_message method."""
        self.client.send_message(Message.inform("random-inform"))

        msgs = self.server.until_messages(1).result(timeout=1)
        self._assert_msgs_equal(msgs, [
            r"#random-inform",
        ])

    def test_stop_and_restart(self):
        """Test stopping and then restarting a client."""
        self.client.wait_running(timeout=1)
        before_threads = threading.enumerate()
        self.client.stop(timeout=1)
        self.client.join(timeout=1)
        # Test that we have fewer threads than before
        self.assertLess(len(threading.enumerate()), len(before_threads))
        self.assertFalse(self.client._running.isSet())
        self.client.start(timeout=1)
        self.client.wait_running(timeout=1)
        # Now we should have the original number of threads again
        self.assertEqual(len(threading.enumerate()), len(before_threads))

    def test_is_connected(self):
        """Test is_connected method."""
        self.assertTrue(self.client.is_connected())
        # Use client.notify_connected to synchronise to the disconnection
        disconnected = threading.Event()
        self.client.notify_connected = (lambda connected: disconnected.set()
                                        if not connected else None)
        self.server.stop(timeout=1.)
        # Restart server during cleanup to keep teardown happy
        self.addCleanup(self.server.start)
        self.server.join(timeout=1.)
        # Wait for the client to be disconnected
        disconnected.wait(1.5)
        self.assertFalse(self.client.is_connected())

    def test_wait_connected(self):
        """Test wait_connected method."""
        start = time.time()
        # Ensure that we are connected at the start of the test
        self.assertTrue(self.client.wait_connected(1.0))
        # Check that we did not wait more than the timeout.
        self.assertTrue(time.time() - start < 1.0)
        # Now we will cause the client to disconnect by stopping the server, and then
        # checking that wait_connected returns fails, and waits approximately the right
        # amount of time
        # Use client.notify_connected to synchronise to the disconnection
        disconnected = threading.Event()
        self.client.notify_connected = (lambda connected: disconnected.set()
                                        if not connected else None)
        self.server.stop(timeout=0.1)
        self.server.join(timeout=1)
        # Restart server during cleanup to keep teardown happy
        self.addCleanup(self.server.start)
        # Wait for the client to be disconnected
        disconnected.wait(1)
        # Now check that wait_connected returns false
        start = time.time()
        self.assertFalse(self.client.wait_connected(0.1))
        # And waited more or less the timeout.
        self.assertTrue(0.05 < time.time() - start <= 0.15)

    def test_bad_socket(self):
        """Test what happens when select is called on a dead socket."""
        # wait for client to connect
        self.client.wait_connected(timeout=1)
        f = Future()

        def notify_connected(connected):
            if connected:
                f.set_result(connected)

        self.client.notify_connected = notify_connected

        # close stream while the client isn't looking
        # then wait for the client to notice
        stream = self.client._stream
        sockname = stream.socket.getpeername()
        self.client.ioloop.add_callback(stream.close)
        f.result(timeout=1.25)

        # check that client reconnected
        self.assertTrue(
            stream is not self.client._stream,
            "Expected %r to not be %r" % (stream, self.client._stream))
        self.assertEqual(sockname, self.client._stream.socket.getpeername())
Пример #52
0
class TestDeviceClientIntegrated(unittest.TestCase, TestUtilMixin):
    def setUp(self):
        self.server = DeviceTestServer('', 0)
        self.server.start(timeout=0.1)

        host, port = self.server._sock.getsockname()

        self.client = katcp.DeviceClient(host, port)
        self.client.start(timeout=0.1)

    def tearDown(self):
        if self.client.running():
            self.client.stop()
            self.client.join()
        if self.server.running():
            self.server.stop()
            self.server.join()

    def test_request(self):
        """Test request method."""
        self.assertTrue(self.client.wait_protocol(1))
        self.client.send_request(katcp.Message.request("watchdog"))
        self.client.send_request(katcp.Message.request("watchdog", mid=55))
        self.client._server_supports_ids = False
        with self.assertRaises(katcp.core.KatcpVersionError):
            self.client.send_request(katcp.Message.request("watchdog", mid=56))

        time.sleep(0.1)

        msgs = self.server.messages()
        self._assert_msgs_equal(msgs, [
            r"?watchdog",
            r"?watchdog[55]",
        ])

    def test_send_message(self):
        """Test send_message method."""
        self.client.send_message(katcp.Message.inform("random-inform"))

        time.sleep(0.1)

        msgs = self.server.messages()
        self._assert_msgs_equal(msgs, [
            r"#random-inform",
        ])

    def test_stop_and_restart(self):
        """Test stopping and then restarting a client."""
        self.client.stop(timeout=0.1)
        # timeout needs to be longer than select sleep.
        self.client.join(timeout=1.5)
        self.assertEqual(self.client._thread, None)
        self.assertFalse(self.client._running.isSet())
        self.client.start(timeout=0.1)

    def test_is_connected(self):
        """Test is_connected method."""
        self.assertTrue(self.client.is_connected())
        self.server.stop(timeout=0.1)
        # timeout needs to be longer than select sleep.
        self.server.join(timeout=1.5)
        self.assertFalse(self.client.is_connected())

    def test_wait_connected(self):
        """Test wait_connected method."""
        start = time.time()
        self.assertTrue(self.client.wait_connected(1.0))
        self.assertTrue(time.time() - start < 1.0)
        self.server.stop(timeout=0.1)
        # timeout needs to be longer than select sleep.
        self.server.join(timeout=1.5)
        start = time.time()
        self.assertFalse(self.client.wait_connected(0.2))
        self.assertTrue(0.15 < time.time() - start < 0.25)

    def test_bad_socket(self):
        """Test what happens when select is called on a dead socket."""
        # wait for client to connect
        time.sleep(0.1)

        # close socket while the client isn't looking
        # then wait for the client to notice
        sock = self.client._sock
        sockname = sock.getpeername()
        sock.close()
        time.sleep(1.25)

        # check that client reconnected
        self.assertTrue(sock is not self.client._sock,
                        "Expected %r to not be %r" % (sock, self.client._sock))
        self.assertEqual(sockname, self.client._sock.getpeername())

    def test_daemon_value(self):
        """Test passing in a daemon value to client start method."""
        self.client.stop(timeout=0.1)
        # timeout needs to be longer than select sleep.
        self.client.join(timeout=1.5)

        self.client.start(timeout=0.1, daemon=True)
        self.assertTrue(self.client._thread.isDaemon())

    def test_excepthook(self):
        """Test passing in an excepthook to client start method."""
        exceptions = []
        except_event = threading.Event()

        def excepthook(etype, value, traceback):
            """Keep track of exceptions."""
            exceptions.append(etype)
            except_event.set()

        self.client.stop(timeout=0.1)
        # timeout needs to be longer than select sleep.
        self.client.join(timeout=1.5)

        self.client.start(timeout=0.1, excepthook=excepthook)
        # force exception by deleteing _running
        old_running = self.client._running
        try:
            del self.client._running
            except_event.wait(1.5)
            self.assertEqual(exceptions, [AttributeError])
        finally:
            self.client._running = old_running
Пример #53
0
from tornado.util import ObjectDict
from tornado.concurrent import Future as tornado_Future
from katcp import Sensor

from katcp.testutils import DeviceTestServer
from katcp.client import *

logging.basicConfig(
    format="%(asctime)s %(name)s %(levelname)s %(funcName)s(%(filename)s:%(lineno)d)%(message)s",
    level=logging.DEBUG
)

def cb(*args):
    print args

try:
    d = DeviceTestServer('', 0)
    d.start(timeout=1)
    logging.info('Server started at port {0}'.format(d.bind_address[1]))
    c = AsyncClient('127.0.0.1', d.bind_address[1])
    c.enable_thread_safety()
    c.start(timeout=1)

#     rm = Message.request

#     #time.sleep(10000000)
    import IPython ; IPython.embed()
finally:
    d.stop()
    c.stop()
import threading
import signal

import tornado
import IPython

from katcp.testutils import DeviceTestServer

from katcp import resource_client, inspecting_client


log = logging.getLogger(__name__)

ioloop = tornado.ioloop.IOLoop.current()

d = DeviceTestServer('', 0)
d.set_concurrency_options(False, False)
d.set_ioloop(ioloop)
ioloop.add_callback(d.start)

def setup_resource_client():
    global rc
    print d.bind_address
    rc = resource_client.KATCPClientResource(dict(
        name='thething',
        address=d.bind_address,
        controlled=True
    ))
    rc.start()

def printy(*args):
Пример #55
0
class TestCallbackClient(unittest.TestCase, TestUtilMixin):

    def setUp(self):
        self.addCleanup(self.stop_server_client)
        self.server = DeviceTestServer('', 0)
        self.server.start(timeout=0.1)

        host, port = self.server._sock.getsockname()

        self.client = katcp.CallbackClient(host, port)
        self.client.start(timeout=0.1)
        self.assertTrue(self.client.wait_protocol(timeout=1))

    def stop_server_client(self):
        if hasattr(self, 'client') and self.client.running():
            self.client.stop()
            self.client.join()
        if hasattr(self, 'server') and self.server.running():
            self.server.stop()
            self.server.join()

    def test_callback_request(self):
        """Test callback request."""

        watchdog_replies = []
        watchdog_replied = threading.Event()

        def watchdog_reply(reply):
            self.assertEqual(reply.name, "watchdog")
            self.assertEqual(reply.arguments, ["ok"])
            watchdog_replies.append(reply)
            watchdog_replied.set()

        self.assertTrue(self.client.wait_protocol(0.2))
        self.client.callback_request(
            katcp.Message.request("watchdog"),
            reply_cb=watchdog_reply,
        )

        watchdog_replied.wait(0.2)
        self.assertTrue(watchdog_replies)

        help_replies = []
        help_informs = []
        help_replied = threading.Event()

        def help_reply(reply):
            self.assertEqual(reply.name, "help")
            self.assertEqual(reply.arguments, ["ok", "%d" % NO_HELP_MESSAGES])
            self.assertEqual(len(help_informs), int(reply.arguments[1]))
            help_replies.append(reply)
            help_replied.set()

        def help_inform(inform):
            self.assertEqual(inform.name, "help")
            self.assertEqual(len(inform.arguments), 2)
            help_informs.append(inform)

        self.client.callback_request(
            katcp.Message.request("help"),
            reply_cb=help_reply,
            inform_cb=help_inform,
        )

        help_replied.wait(1)
        self.assertTrue(help_replied.isSet())
        help_replied.clear()
        help_replied.wait(0.05)   # Check if (unwanted) late help replies arrive
        self.assertFalse(help_replied.isSet())
        self.assertEqual(len(help_replies), 1)
        self.assertEqual(len(help_informs), NO_HELP_MESSAGES)

    def test_callback_request_mid(self):
        ## Test that the client does the right thing with message identifiers

        # Wait for the client to detect the server protocol. Server should
        # support message identifiers
        self.assertTrue(self.client.wait_protocol(0.2))
        # Replace send_message so that we can check the message
        self.client.send_message = mock.Mock()

        # By default message identifiers should be enabled, and should start
        # counting at 1
        self.client.callback_request(katcp.Message.request('watchdog'))
        self.client.callback_request(katcp.Message.request('watchdog'))
        self.client.callback_request(katcp.Message.request('watchdog'))
        # Extract katcp.Message object .mid attributes from the mock calls to
        # send_message
        mids = [args[0].mid              # arg[0] should be the Message() object
                for args, kwargs in self.client.send_message.call_args_list]
        self.assertEqual(mids, ['1','2','3'])
        self.client.send_message.reset_mock()

        # Explicitly ask for no mid to be used
        self.client.callback_request(
            katcp.Message.request('watchdog'), use_mid=False)
        mid = self.client.send_message.call_args[0][0].mid
        self.assertEqual(mid, None)

        # Ask for a specific mid to be used
        self.client.send_message.reset_mock()
        self.client.callback_request(katcp.Message.request('watchdog', mid=42))
        mid = self.client.send_message.call_args[0][0].mid
        self.assertEqual(mid, '42')

        ## Check situation for a katcpv4 server
        self.client._server_supports_ids = False

        # Should fail if an mid is passed
        with self.assertRaises(katcp.core.KatcpVersionError):
            self.client.callback_request(
                katcp.Message.request('watchdog', mid=42))

        # Should fail if an mid is requested
        with self.assertRaises(katcp.core.KatcpVersionError):
            self.client.callback_request(
                katcp.Message.request('watchdog'), use_mid=True)

        # Should use no mid by default
        self.client.send_message.reset_mock()
        self.client.callback_request(katcp.Message.request('watchdog'))
        mid = self.client.send_message.call_args[0][0].mid
        self.assertEqual(mid, None)

    def test_no_callback(self):
        """Test request without callback."""

        help_messages = []
        help_completed = threading.Event()

        def handle_help_message(client, msg):
            help_messages.append(msg)
            if msg.mtype == msg.REPLY:
                help_completed.set()

        self.client._inform_handlers["help"] = handle_help_message
        self.client._reply_handlers["help"] = handle_help_message
        # Set client._last_msg_id so we know that the ID is. Should be
        # _last_msg_id + 1
        self.client._last_msg_id = 0
        self.assertTrue(self.client.wait_protocol(0.2))
        self.client.callback_request(katcp.Message.request("help"))
        help_completed.wait(1)
        self.assertTrue(help_completed.isSet())

        self._assert_msgs_like(help_messages,
            [("#help[1] ", "")] * NO_HELP_MESSAGES +
            [("!help[1] ok %d" % NO_HELP_MESSAGES, "")])

    def test_no_timeout(self):
        self.client._request_timeout = None
        replies = []
        informs = []
        replied = threading.Event()
        def reply_handler(reply):
            replies.append(reply)
            replied.set()
        def inform_handler(reply):
            informs.append(reply)

        with mock.patch('katcp.client.threading.Timer') as MockTimer:
            self.client.callback_request(katcp.Message.request("help"),
                                reply_cb=reply_handler,
                                inform_cb=inform_handler)
        replied.wait(1)
        # With no timeout no Timer object should have been instantiated
        self.assertEqual(MockTimer.call_count, 0)
        self.assertEqual(len(replies), 1)
        self.assertEqual(len(remove_version_connect(informs)), NO_HELP_MESSAGES)

    def test_timeout(self):
        self._test_timeout()

    def test_timeout_nomid(self, use_mid=False):
        self._test_timeout(use_mid=False)

    def _test_timeout(self, use_mid=None):
        """Test requests that timeout."""

        replies = []
        replied = threading.Event()
        informs = []
        timeout = 0.001

        @counting_callback()
        def reply_cb(msg):
            replies.append(msg)
            replied.set()

        def inform_cb(msg):
            informs.append(msg)

        self.assertTrue(self.client.wait_protocol(0.2))
        self.client.callback_request(
            katcp.Message.request("slow-command", "0.1"),
            use_mid=use_mid,
            reply_cb=reply_cb,
            inform_cb=inform_cb,
            timeout=timeout,
        )

        reply_cb.assert_wait(1)
        self.assertEqual(len(replies), 1)
        self.assertEqual([msg.name for msg in replies], ["slow-command"])
        self.assertEqual([msg.arguments for msg in replies], [
                ["fail", "Timed out after %f seconds" % timeout]])
        self.assertEqual(len(remove_version_connect(informs)), 0)

        del replies[:]
        del informs[:]
        reply_cb.reset()

        # test next request succeeds
        self.client.callback_request(
            katcp.Message.request("slow-command", "0.05"),
            reply_cb=reply_cb,
            inform_cb=inform_cb,
        )

        reply_cb.assert_wait(1)
        self.assertEqual(len(replies), 1)
        self.assertEqual(len(informs), 0)
        self.assertEqual([msg.name for msg in replies + informs],
                         ["slow-command"] * len(replies + informs))
        self.assertEqual([msg.arguments for msg in replies], [["ok"]])

    def test_timeout_nocb(self):
        """Test requests that timeout with no callbacks."""
        # Included to test https://katfs.kat.ac.za/mantis/view.php?id=1722
        # Situation can occur during a race between the timeout handler and the
        # receipt of a reply -- the reply can arrive after the timeout timer has
        # expired but before the request has been popped off the stack with
        # client._pop_async_request(). The normal request handler then pops off
        # the request first, resulting in the timeout handler getting a bunch of
        # None's. It should handle this gracefully.

        # Running the handler with a fake msg_id should have the same result as
        # running it after a real request has already been popped. The expected
        # result is that no assertions are raised.
        self.client._handle_timeout('fake_msg_id')

    def test_user_data(self):
        """Test callbacks with user data."""
        help_replies = []
        help_informs = []
        done = threading.Event()
        
        def help_reply(reply, x, y):
            self.assertEqual(reply.name, "help")
            self.assertEqual(x, 5)
            self.assertEqual(y, "foo")
            help_replies.append(reply)
            done.set()

        def help_inform(inform, x, y):
            self.assertEqual(inform.name, "help")
            self.assertEqual(x, 5)
            self.assertEqual(y, "foo")
            help_informs.append(inform)

        self.client.callback_request(
            katcp.Message.request("help"),
            reply_cb=help_reply,
            inform_cb=help_inform,
            user_data=(5, "foo"))

        done.wait(1)
        # Wait a bit longer to see if spurious replies arrive
        time.sleep(0.01)
        self.assertEqual(len(help_replies), 1)
        self.assertEqual(len(remove_version_connect(help_informs)),
                         NO_HELP_MESSAGES)

    def test_fifty_thread_mayhem(self):
        """Test using callbacks from fifty threads simultaneously."""
        num_threads = 50
        # map from thread_id -> (replies, informs)
        results = {}
        # list of thread objects
        threads = []

        def reply_cb(reply, thread_id):
            results[thread_id][0].append(reply)
            results[thread_id][2].set()

        def inform_cb(inform, thread_id):
            results[thread_id][1].append(inform)

        def worker(thread_id, request):
            self.client.callback_request(
                request.copy(),
                reply_cb=reply_cb,
                inform_cb=inform_cb,
                user_data=(thread_id,),
            )

        request = katcp.Message.request("help")

        for thread_id in range(num_threads):
            results[thread_id] = ([], [], threading.Event())

        for thread_id in range(num_threads):
            thread = threading.Thread(target=worker, args=(thread_id, request))
            threads.append(thread)

        for thread in threads:
            thread.start()

        for thread in threads:
            thread.join()

        for thread_id in range(num_threads):
            replies, informs, done = results[thread_id]
            done.wait(5.0)
            self.assertTrue(done.isSet())
            self.assertEqual(len(replies), 1)
            self.assertEqual(replies[0].arguments[0], "ok")
            informs = remove_version_connect(informs)
            if len(informs) != NO_HELP_MESSAGES:
                print thread_id, len(informs)
                print [x.arguments[0] for x in informs]
            self.assertEqual(len(informs), NO_HELP_MESSAGES)

    def test_blocking_request(self):
        """Test the callback client's blocking request."""
        reply, informs = self.client.blocking_request(
            katcp.Message.request("help"),
        )

        self.assertEqual(reply.name, "help")
        self.assertEqual(reply.arguments, ["ok", "%d" % NO_HELP_MESSAGES])
        self.assertEqual(len(remove_version_connect(informs)), NO_HELP_MESSAGES)

        reply, informs = self.client.blocking_request(
            katcp.Message.request("slow-command", "0.5"),
            timeout=0.001)

        self.assertEqual(reply.name, "slow-command")
        self.assertEqual(reply.arguments[0], "fail")
        self.assertTrue(reply.arguments[1].startswith("Timed out after"))

    def test_blocking_request_mid(self):
        ## Test that the blocking client does the right thing with message
        ## identifiers

        # Wait for the client to detect the server protocol. Server should
        # support message identifiers
        self.assertTrue(self.client.wait_protocol(0.2))
        # Replace send_message so that we can check the message
        self.client.send_message = mock.Mock()

        # By default message identifiers should be enabled, and should start
        # counting at 1
        self.client.blocking_request(
            katcp.Message.request('watchdog'), timeout=0)
        self.client.blocking_request(
            katcp.Message.request('watchdog'), timeout=0)
        self.client.blocking_request(
            katcp.Message.request('watchdog'), timeout=0)
        # Extract katcp.Message object .mid attributes from the mock calls to
        # send_message
        mids = [args[0].mid              # arg[0] should be the Message() object
                for args, kwargs in self.client.send_message.call_args_list]
        self.assertEqual(mids, ['1','2','3'])
        self.client.send_message.reset_mock()

        # Explicitly ask for no mid to be used
        self.client.blocking_request(katcp.Message.request('watchdog'),
                                     use_mid=False, timeout=0)
        mid = self.client.send_message.call_args[0][0].mid
        self.assertEqual(mid, None)

        # Ask for a specific mid to be used
        self.client.send_message.reset_mock()
        self.client.blocking_request(katcp.Message.request(
            'watchdog', mid=42), timeout=0)
        mid = self.client.send_message.call_args[0][0].mid
        self.assertEqual(mid, '42')

        ## Check situation for a katcpv4 server
        self.client._server_supports_ids = False

        # Should fail if an mid is passed
        with self.assertRaises(katcp.core.KatcpVersionError):
            self.client.blocking_request(katcp.Message.request(
                'watchdog', mid=42), timeout=0)

        # Should fail if an mid is requested
        with self.assertRaises(katcp.core.KatcpVersionError):
            self.client.blocking_request(
                katcp.Message.request('watchdog'), use_mid=True, timeout=0)

        # Should use no mid by default
        self.client.send_message.reset_mock()
        self.client.blocking_request(katcp.Message.request(
            'watchdog'), timeout=0)
        mid = self.client.send_message.call_args[0][0].mid
        self.assertEqual(mid, None)

    def test_request_fail_on_raise(self):
        """Test that the callback is called even if send_message raises
           KatcpClientError."""
        def raise_error(msg, timeout=None):
            raise katcp.KatcpClientError("Error %s" % msg.name)
        self.client.send_message = raise_error

        replies = []

        def reply_cb(msg):
            replies.append(msg)

        self.client.callback_request(katcp.Message.request("foo"),
            reply_cb=reply_cb,
        )

        self.assertEqual(len(replies), 1)
        self.assertEqual(replies[0].name, "foo")
        self.assertEqual(replies[0].arguments, ["fail", "Error foo"])

    def test_stop_join(self):
        # Set up a slow command to ensure that there is something in
        # the async queue
        with mock.patch('katcp.client.threading.Timer') as MockTimer:
            instance = MockTimer.return_value
            self.client.callback_request(
                    katcp.Message.request("slow-command", "10000"),
                    timeout=10000.1)
        # Exactly one instance of MockTimer should have been instantiated
        self.assertEqual(MockTimer.call_count, 1)
        self.client.stop(timeout=0.5)
        # Check that the timer's cancel() method has been called
        instance.cancel.assert_called_once_with()
        self.client.join(timeout=0.45)
        instance.join.assert_called_once_with(timeout=0.45)
        # It's OK not to have this in teardown since the client will
        # itself cancel the slow_command when it is stop()ed
        self.client.blocking_request(katcp.Message.request("cancel-slow-command"))
Пример #56
0
from katcp import Sensor
from katcp.client import *
from katcp.testutils import DeviceTestServer

logging.basicConfig(
    format=
    "%(asctime)s %(name)s %(levelname)s %(funcName)s(%(filename)s:%(lineno)d)%(message)s",
    level=logging.DEBUG)


def cb(*args):
    print(args)


try:
    d = DeviceTestServer('', 0)
    d.start(timeout=1)
    logging.info('Server started at port {0}'.format(d.bind_address[1]))
    c = AsyncClient('127.0.0.1', d.bind_address[1])
    c.enable_thread_safety()
    c.start(timeout=1)

    #     rm = Message.request

    #     #time.sleep(10000000)
    import IPython
    IPython.embed()
finally:
    d.stop()
    c.stop()
Пример #57
0
class TestCallbackClient(unittest.TestCase, TestUtilMixin):
    def setUp(self):
        self.addCleanup(self.stop_server_client)
        self.server = DeviceTestServer('', 0)
        self.server.start(timeout=0.1)

        host, port = self.server._sock.getsockname()

        self.client = katcp.CallbackClient(host, port)
        self.client.start(timeout=0.1)
        self.assertTrue(self.client.wait_protocol(timeout=1))

    def stop_server_client(self):
        if hasattr(self, 'client') and self.client.running():
            self.client.stop()
            self.client.join()
        if hasattr(self, 'server') and self.server.running():
            self.server.stop()
            self.server.join()

    def test_callback_request(self):
        """Test callback request."""

        watchdog_replies = []
        watchdog_replied = threading.Event()

        def watchdog_reply(reply):
            self.assertEqual(reply.name, "watchdog")
            self.assertEqual(reply.arguments, ["ok"])
            watchdog_replies.append(reply)
            watchdog_replied.set()

        self.assertTrue(self.client.wait_protocol(0.2))
        self.client.callback_request(
            katcp.Message.request("watchdog"),
            reply_cb=watchdog_reply,
        )

        watchdog_replied.wait(0.2)
        self.assertTrue(watchdog_replies)

        help_replies = []
        help_informs = []
        help_replied = threading.Event()

        def help_reply(reply):
            self.assertEqual(reply.name, "help")
            self.assertEqual(reply.arguments, ["ok", "%d" % NO_HELP_MESSAGES])
            self.assertEqual(len(help_informs), int(reply.arguments[1]))
            help_replies.append(reply)
            help_replied.set()

        def help_inform(inform):
            self.assertEqual(inform.name, "help")
            self.assertEqual(len(inform.arguments), 2)
            help_informs.append(inform)

        self.client.callback_request(
            katcp.Message.request("help"),
            reply_cb=help_reply,
            inform_cb=help_inform,
        )

        help_replied.wait(1)
        self.assertTrue(help_replied.isSet())
        help_replied.clear()
        help_replied.wait(0.05)  # Check if (unwanted) late help replies arrive
        self.assertFalse(help_replied.isSet())
        self.assertEqual(len(help_replies), 1)
        self.assertEqual(len(help_informs), NO_HELP_MESSAGES)

    def test_callback_request_mid(self):
        ## Test that the client does the right thing with message identifiers

        # Wait for the client to detect the server protocol. Server should
        # support message identifiers
        self.assertTrue(self.client.wait_protocol(0.2))
        # Replace send_message so that we can check the message
        self.client.send_message = mock.Mock()

        # By default message identifiers should be enabled, and should start
        # counting at 1
        self.client.callback_request(katcp.Message.request('watchdog'))
        self.client.callback_request(katcp.Message.request('watchdog'))
        self.client.callback_request(katcp.Message.request('watchdog'))
        # Extract katcp.Message object .mid attributes from the mock calls to
        # send_message
        mids = [
            args[0].mid  # arg[0] should be the Message() object
            for args, kwargs in self.client.send_message.call_args_list
        ]
        self.assertEqual(mids, ['1', '2', '3'])
        self.client.send_message.reset_mock()

        # Explicitly ask for no mid to be used
        self.client.callback_request(katcp.Message.request('watchdog'),
                                     use_mid=False)
        mid = self.client.send_message.call_args[0][0].mid
        self.assertEqual(mid, None)

        # Ask for a specific mid to be used
        self.client.send_message.reset_mock()
        self.client.callback_request(katcp.Message.request('watchdog', mid=42))
        mid = self.client.send_message.call_args[0][0].mid
        self.assertEqual(mid, '42')

        ## Check situation for a katcpv4 server
        self.client._server_supports_ids = False

        # Should fail if an mid is passed
        with self.assertRaises(katcp.core.KatcpVersionError):
            self.client.callback_request(
                katcp.Message.request('watchdog', mid=42))

        # Should fail if an mid is requested
        with self.assertRaises(katcp.core.KatcpVersionError):
            self.client.callback_request(katcp.Message.request('watchdog'),
                                         use_mid=True)

        # Should use no mid by default
        self.client.send_message.reset_mock()
        self.client.callback_request(katcp.Message.request('watchdog'))
        mid = self.client.send_message.call_args[0][0].mid
        self.assertEqual(mid, None)

    def test_no_callback(self):
        """Test request without callback."""

        help_messages = []
        help_completed = threading.Event()

        def handle_help_message(client, msg):
            help_messages.append(msg)
            if msg.mtype == msg.REPLY:
                help_completed.set()

        self.client._inform_handlers["help"] = handle_help_message
        self.client._reply_handlers["help"] = handle_help_message
        # Set client._last_msg_id so we know that the ID is. Should be
        # _last_msg_id + 1
        self.client._last_msg_id = 0
        self.assertTrue(self.client.wait_protocol(0.2))
        self.client.callback_request(katcp.Message.request("help"))
        help_completed.wait(1)
        self.assertTrue(help_completed.isSet())

        self._assert_msgs_like(help_messages,
                               [("#help[1] ", "")] * NO_HELP_MESSAGES +
                               [("!help[1] ok %d" % NO_HELP_MESSAGES, "")])

    def test_no_timeout(self):
        self.client._request_timeout = None
        replies = []
        informs = []
        replied = threading.Event()

        def reply_handler(reply):
            replies.append(reply)
            replied.set()

        def inform_handler(reply):
            informs.append(reply)

        with mock.patch('katcp.client.threading.Timer') as MockTimer:
            self.client.callback_request(katcp.Message.request("help"),
                                         reply_cb=reply_handler,
                                         inform_cb=inform_handler)
        replied.wait(1)
        # With no timeout no Timer object should have been instantiated
        self.assertEqual(MockTimer.call_count, 0)
        self.assertEqual(len(replies), 1)
        self.assertEqual(len(remove_version_connect(informs)),
                         NO_HELP_MESSAGES)

    def test_timeout(self):
        self._test_timeout()

    def test_timeout_nomid(self, use_mid=False):
        self._test_timeout(use_mid=False)

    def _test_timeout(self, use_mid=None):
        """Test requests that timeout."""

        replies = []
        replied = threading.Event()
        informs = []
        timeout = 0.001

        @counting_callback()
        def reply_cb(msg):
            replies.append(msg)
            replied.set()

        def inform_cb(msg):
            informs.append(msg)

        self.assertTrue(self.client.wait_protocol(0.2))
        self.client.callback_request(
            katcp.Message.request("slow-command", "0.1"),
            use_mid=use_mid,
            reply_cb=reply_cb,
            inform_cb=inform_cb,
            timeout=timeout,
        )

        reply_cb.assert_wait(1)
        self.assertEqual(len(replies), 1)
        self.assertEqual([msg.name for msg in replies], ["slow-command"])
        self.assertEqual(
            [msg.arguments for msg in replies],
            [["fail", "Timed out after %f seconds" % timeout]])
        self.assertEqual(len(remove_version_connect(informs)), 0)

        del replies[:]
        del informs[:]
        reply_cb.reset()

        # test next request succeeds
        self.client.callback_request(
            katcp.Message.request("slow-command", "0.05"),
            reply_cb=reply_cb,
            inform_cb=inform_cb,
        )

        reply_cb.assert_wait(1)
        self.assertEqual(len(replies), 1)
        self.assertEqual(len(informs), 0)
        self.assertEqual([msg.name for msg in replies + informs],
                         ["slow-command"] * len(replies + informs))
        self.assertEqual([msg.arguments for msg in replies], [["ok"]])

    def test_timeout_nocb(self):
        """Test requests that timeout with no callbacks."""
        # Included to test https://katfs.kat.ac.za/mantis/view.php?id=1722
        # Situation can occur during a race between the timeout handler and the
        # receipt of a reply -- the reply can arrive after the timeout timer has
        # expired but before the request has been popped off the stack with
        # client._pop_async_request(). The normal request handler then pops off
        # the request first, resulting in the timeout handler getting a bunch of
        # None's. It should handle this gracefully.

        # Running the handler with a fake msg_id should have the same result as
        # running it after a real request has already been popped. The expected
        # result is that no assertions are raised.
        self.client._handle_timeout('fake_msg_id')

    def test_user_data(self):
        """Test callbacks with user data."""
        help_replies = []
        help_informs = []
        done = threading.Event()

        def help_reply(reply, x, y):
            self.assertEqual(reply.name, "help")
            self.assertEqual(x, 5)
            self.assertEqual(y, "foo")
            help_replies.append(reply)
            done.set()

        def help_inform(inform, x, y):
            self.assertEqual(inform.name, "help")
            self.assertEqual(x, 5)
            self.assertEqual(y, "foo")
            help_informs.append(inform)

        self.client.callback_request(katcp.Message.request("help"),
                                     reply_cb=help_reply,
                                     inform_cb=help_inform,
                                     user_data=(5, "foo"))

        done.wait(1)
        # Wait a bit longer to see if spurious replies arrive
        time.sleep(0.01)
        self.assertEqual(len(help_replies), 1)
        self.assertEqual(len(remove_version_connect(help_informs)),
                         NO_HELP_MESSAGES)

    def test_fifty_thread_mayhem(self):
        """Test using callbacks from fifty threads simultaneously."""
        num_threads = 50
        # map from thread_id -> (replies, informs)
        results = {}
        # list of thread objects
        threads = []

        def reply_cb(reply, thread_id):
            results[thread_id][0].append(reply)
            results[thread_id][2].set()

        def inform_cb(inform, thread_id):
            results[thread_id][1].append(inform)

        def worker(thread_id, request):
            self.client.callback_request(
                request.copy(),
                reply_cb=reply_cb,
                inform_cb=inform_cb,
                user_data=(thread_id, ),
            )

        request = katcp.Message.request("help")

        for thread_id in range(num_threads):
            results[thread_id] = ([], [], threading.Event())

        for thread_id in range(num_threads):
            thread = threading.Thread(target=worker, args=(thread_id, request))
            threads.append(thread)

        for thread in threads:
            thread.start()

        for thread in threads:
            thread.join()

        for thread_id in range(num_threads):
            replies, informs, done = results[thread_id]
            done.wait(5.0)
            self.assertTrue(done.isSet())
            self.assertEqual(len(replies), 1)
            self.assertEqual(replies[0].arguments[0], "ok")
            informs = remove_version_connect(informs)
            if len(informs) != NO_HELP_MESSAGES:
                print thread_id, len(informs)
                print[x.arguments[0] for x in informs]
            self.assertEqual(len(informs), NO_HELP_MESSAGES)

    def test_blocking_request(self):
        """Test the callback client's blocking request."""
        reply, informs = self.client.blocking_request(
            katcp.Message.request("help"), )

        self.assertEqual(reply.name, "help")
        self.assertEqual(reply.arguments, ["ok", "%d" % NO_HELP_MESSAGES])
        self.assertEqual(len(remove_version_connect(informs)),
                         NO_HELP_MESSAGES)

        reply, informs = self.client.blocking_request(katcp.Message.request(
            "slow-command", "0.5"),
                                                      timeout=0.001)

        self.assertEqual(reply.name, "slow-command")
        self.assertEqual(reply.arguments[0], "fail")
        self.assertTrue(reply.arguments[1].startswith("Timed out after"))

    def test_blocking_request_mid(self):
        ## Test that the blocking client does the right thing with message
        ## identifiers

        # Wait for the client to detect the server protocol. Server should
        # support message identifiers
        self.assertTrue(self.client.wait_protocol(0.2))
        # Replace send_message so that we can check the message
        self.client.send_message = mock.Mock()

        # By default message identifiers should be enabled, and should start
        # counting at 1
        self.client.blocking_request(katcp.Message.request('watchdog'),
                                     timeout=0)
        self.client.blocking_request(katcp.Message.request('watchdog'),
                                     timeout=0)
        self.client.blocking_request(katcp.Message.request('watchdog'),
                                     timeout=0)
        # Extract katcp.Message object .mid attributes from the mock calls to
        # send_message
        mids = [
            args[0].mid  # arg[0] should be the Message() object
            for args, kwargs in self.client.send_message.call_args_list
        ]
        self.assertEqual(mids, ['1', '2', '3'])
        self.client.send_message.reset_mock()

        # Explicitly ask for no mid to be used
        self.client.blocking_request(katcp.Message.request('watchdog'),
                                     use_mid=False,
                                     timeout=0)
        mid = self.client.send_message.call_args[0][0].mid
        self.assertEqual(mid, None)

        # Ask for a specific mid to be used
        self.client.send_message.reset_mock()
        self.client.blocking_request(katcp.Message.request('watchdog', mid=42),
                                     timeout=0)
        mid = self.client.send_message.call_args[0][0].mid
        self.assertEqual(mid, '42')

        ## Check situation for a katcpv4 server
        self.client._server_supports_ids = False

        # Should fail if an mid is passed
        with self.assertRaises(katcp.core.KatcpVersionError):
            self.client.blocking_request(katcp.Message.request('watchdog',
                                                               mid=42),
                                         timeout=0)

        # Should fail if an mid is requested
        with self.assertRaises(katcp.core.KatcpVersionError):
            self.client.blocking_request(katcp.Message.request('watchdog'),
                                         use_mid=True,
                                         timeout=0)

        # Should use no mid by default
        self.client.send_message.reset_mock()
        self.client.blocking_request(katcp.Message.request('watchdog'),
                                     timeout=0)
        mid = self.client.send_message.call_args[0][0].mid
        self.assertEqual(mid, None)

    def test_request_fail_on_raise(self):
        """Test that the callback is called even if send_message raises
           KatcpClientError."""
        def raise_error(msg, timeout=None):
            raise katcp.KatcpClientError("Error %s" % msg.name)

        self.client.send_message = raise_error

        replies = []

        def reply_cb(msg):
            replies.append(msg)

        self.client.callback_request(
            katcp.Message.request("foo"),
            reply_cb=reply_cb,
        )

        self.assertEqual(len(replies), 1)
        self.assertEqual(replies[0].name, "foo")
        self.assertEqual(replies[0].arguments, ["fail", "Error foo"])

    def test_stop_join(self):
        # Set up a slow command to ensure that there is something in
        # the async queue
        with mock.patch('katcp.client.threading.Timer') as MockTimer:
            instance = MockTimer.return_value
            self.client.callback_request(katcp.Message.request(
                "slow-command", "10000"),
                                         timeout=10000.1)
        # Exactly one instance of MockTimer should have been instantiated
        self.assertEqual(MockTimer.call_count, 1)
        self.client.stop(timeout=0.5)
        # Check that the timer's cancel() method has been called
        instance.cancel.assert_called_once_with()
        self.client.join(timeout=0.45)
        instance.join.assert_called_once_with(timeout=0.45)
        # It's OK not to have this in teardown since the client will
        # itself cancel the slow_command when it is stop()ed
        self.client.blocking_request(
            katcp.Message.request("cancel-slow-command"))
Пример #58
0
 def setUp(self):
     self.server = DeviceTestServer('', 0)