Пример #1
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)
Пример #2
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'))
Пример #3
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'))
Пример #4
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'))
Пример #5
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'))
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)
Пример #7
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)
Пример #8
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()
Пример #9
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)
Пример #10
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)
Пример #11
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()
Пример #12
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()
Пример #13
0
class TestDeviceServer(unittest.TestCase, TestUtilMixin):
    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 tearDown(self):
        if self.client.running():
            self.client.stop()
            self.client.join()
        if self.server.running():
            self.server.stop()
            self.server.join()

    def test_simple_connect(self):
        """Test a simple server setup and teardown with client connect."""
        # basic send
        self.client.request(katcp.Message.request("foo"))

        # 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)

        msgs = self.client.messages()
        self._assert_msgs_equal(msgs, [
            r"#version device_stub-0.1",
            r"#build-state name-0.1",
            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."""
        self.client.raw_send("bad msg\n")
        # wait for reply
        self.client.blocking_request(katcp.Message.request("watchdog"))

        msgs = self.client.messages()
        self._assert_msgs_like(msgs, [
            (r"#version device_stub-0.1", ""),
            (r"#build-state name-0.1", ""),
            (r"#log error", "KatcpSyntaxError:\_Bad\_type\_character\_'b'.\\n"),
            (r"!watchdog ok", ""),
        ])

    def test_server_ignores_informs_and_replies(self):
        """Test server ignores informs and replies."""
        self.client.raw_send("#some inform\n")
        self.client.raw_send("!some reply\n")

        time.sleep(0.1)

        msgs = self.client.messages()
        self._assert_msgs_like(msgs, [
            (r"#version device_stub-0.1", ""),
            (r"#build-state name-0.1", ""),
        ])

    def test_standard_requests(self):
        """Test standard request and replies."""
        self.client.request(katcp.Message.request("watchdog"))
        self.client.request(katcp.Message.request("restart"))
        self.client.request(katcp.Message.request("log-level"))
        self.client.request(katcp.Message.request("log-level", "trace"))
        self.client.request(katcp.Message.request("log-level", "unknown"))
        self.client.request(katcp.Message.request("help"))
        self.client.request(katcp.Message.request("help", "watchdog"))
        self.client.request(katcp.Message.request("help", "unknown-request"))
        self.client.request(katcp.Message.request("client-list"))
        self.client.request(katcp.Message.request("sensor-list"))
        self.client.request(katcp.Message.request("sensor-list", "an.int"))
        self.client.request(katcp.Message.request("sensor-list", "an.unknown"))
        self.client.request(katcp.Message.request("sensor-value"))
        self.client.request(katcp.Message.request("sensor-value", "an.int"))
        self.client.request(katcp.Message.request("sensor-value", "an.unknown"))
        self.client.request(katcp.Message.request("sensor-sampling", "an.int"))
        self.client.request(katcp.Message.request("sensor-sampling", "an.int", "differential", "2"))
        self.client.request(katcp.Message.request("sensor-sampling"))
        self.client.request(katcp.Message.request("sensor-sampling", "an.unknown", "auto"))
        self.client.blocking_request(katcp.Message.request("sensor-sampling", "an.int", "unknown"))

        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)

        msgs = self.client.messages()
        self.assertEqual(self.server.restart_queue.get_nowait(), self.server)
        self._assert_msgs_like(msgs, [
            (r"#version device_stub-0.1", ""),
            (r"#build-state name-0.1", ""),
            (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 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-value", ""),
            (r"#help slow-command", ""),
            (r"#help watchdog", ""),
            (r"!help ok 13", ""),
            (r"#help watchdog", ""),
            (r"!help ok 1", ""),
            (r"!help fail", ""),
            (r"#client-list", ""),
            (r"!client-list ok 1", ""),
            (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 12345000 1 an.int nominal 3", ""),
            (r"!sensor-value ok 1", ""),
            (r"#sensor-value 12345000 1 an.int nominal 3", ""),
            (r"!sensor-value ok 1", ""),
            (r"!sensor-value fail", ""),
            (r"!sensor-sampling ok an.int none", ""),
            (r"#sensor-status 12345000 1 an.int nominal 3", ""),
            (r"!sensor-sampling ok an.int differential 2", ""),
            (r"!sensor-sampling fail No\_sensor\_name\_given.", ""),
            (r"!sensor-sampling fail Unknown\_sensor\_name.", ""),
            (r"!sensor-sampling fail Unknown\_strategy\_name.", ""),
            (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."""
        current_id = [0]
        def mid():
            current_id[0] += 1
            return current_id[0]

        self.client.request(katcp.Message.request("watchdog", mid=mid()))
        self.client.request(katcp.Message.request("restart", mid=mid()))
        self.client.request(katcp.Message.request("log-level", mid=mid()))
        self.client.request(katcp.Message.request("log-level", "trace", mid=mid()))
        self.client.request(katcp.Message.request("log-level", "unknown", mid=mid()))
        self.client.request(katcp.Message.request("help", mid=mid()))
        self.client.request(katcp.Message.request("help", "watchdog", mid=mid()))
        self.client.request(katcp.Message.request("help", "unknown-request", mid=mid()))
        self.client.request(katcp.Message.request("client-list", mid=mid()))
        self.client.request(katcp.Message.request("sensor-list", mid=mid()))
        self.client.request(katcp.Message.request("sensor-list", "an.int", mid=mid()))
        self.client.request(katcp.Message.request("sensor-list", "an.unknown", mid=mid()))
        self.client.request(katcp.Message.request("sensor-value", mid=mid()))
        self.client.request(katcp.Message.request("sensor-value", "an.int", mid=mid()))
        self.client.request(katcp.Message.request("sensor-value", "an.unknown", mid=mid()))
        self.client.blocking_request(katcp.Message.request("sensor-sampling", "an.int", mid=mid()))
        self.client.blocking_request(katcp.Message.request("sensor-sampling", "an.int", "differential", "2", mid=mid()))
        self.client.blocking_request(katcp.Message.request("sensor-sampling", mid=mid()))
        self.client.blocking_request(katcp.Message.request("sensor-sampling", "an.unknown", "auto", mid=mid()))
        self.client.blocking_request(katcp.Message.request("sensor-sampling", "an.int", "unknown", mid=mid()))

        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)

        msgs = self.client.messages()
        self.assertEqual(self.server.restart_queue.get_nowait(), self.server)
        self._assert_msgs_like(msgs, [
            (r"#version device_stub-0.1", ""),
            (r"#build-state name-0.1", ""),
            (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] 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-value", ""),
            (r"#help[6] slow-command", ""),
            (r"#help[6] watchdog", ""),
            (r"!help[6] ok 13", ""),
            (r"#help[7] watchdog", ""),
            (r"!help[7] ok 1", ""),
            (r"!help[8] fail", ""),
            (r"#client-list[9]", ""),
            (r"!client-list[9] ok 1", ""),
            (r"#sensor-list[10] an.int An\_Integer. count integer -5 5", ""),
            (r"!sensor-list[10] ok 1", ""),
            (r"#sensor-list[11] an.int An\_Integer. count integer -5 5", ""),
            (r"!sensor-list[11] ok 1", ""),
            (r"!sensor-list[12] fail", ""),
            (r"#sensor-value[13] 12345000 1 an.int nominal 3", ""),
            (r"!sensor-value[13] ok 1", ""),
            (r"#sensor-value[14] 12345000 1 an.int nominal 3", ""),
            (r"!sensor-value[14] ok 1", ""),
            (r"!sensor-value[15] fail", ""),
            (r"!sensor-sampling[16] ok an.int none", ""),
            (r"#sensor-status 12345000 1 an.int nominal 3", ""),
            (r"!sensor-sampling[17] ok an.int differential 2", ""),
            (r"!sensor-sampling[18] fail No\_sensor\_name\_given.", ""),
            (r"!sensor-sampling[19] fail Unknown\_sensor\_name.", ""),
            (r"!sensor-sampling[20] fail Unknown\_strategy\_name.", ""),
            (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.*/"))
        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", "//"))
        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/"))
        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.*/"))
        self._assert_msgs_equal(informs + [reply], [
            r"#sensor-value 12345000 1 an.int nominal 3",
            r"!sensor-value ok 1",
        ])

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

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


    def test_halt_request(self):
        """Test halt request."""
        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)

        msgs = self.client.messages()
        self._assert_msgs_equal(msgs, [
            r"#version device_stub-0.1",
            r"#build-state name-0.1",
            r"!halt 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, sock, msg):
                    pass
        except AssertionError:
            pass
        else:
            self.fail("Server metaclass accepted missing request_ docstring.")

        try:
            class BadServer(katcp.DeviceServer):
                def inform_baz(self, sock, 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."""

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

        time.sleep(0.1)

        msgs = self.client.messages()
        self._assert_msgs_like(msgs, [
            (r"#version device_stub-0.1", ""),
            (r"#build-state name-0.1", ""),
            (r"!raise-exception fail Traceback", ""),
            (r"!raise-fail 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."""
        self.client.request(katcp.Message.request("sensor-sampling", "an.int", "period", 100))
        time.sleep(1.0)
        self.client.request(katcp.Message.request("sensor-sampling", "an.int", "none"))
        time.sleep(0.5)

        msgs = self.client.messages()
        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) - 12) < 2, "Expected 12 informs, saw %d." % len(updates))

        self._assert_msgs_equal(others, [
            r"#version device_stub-0.1",
            r"#build-state name-0.1",
            r"!sensor-sampling ok an.int period 100",
            r"!sensor-sampling ok an.int none",
        ])

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

    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()