blob: 3113780885c2155a10ca2d6a25d3fc0755c91123 [file] [log] [blame]
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Unit tests for translation layer."""
import collections
import unittest
from unittest import mock
from local_agent import ams_client
from local_agent import errors as agent_errors
from local_agent.translation_layer import gdm_manager
from local_agent.translation_layer import translation_layer
####################### Fake data for unit test #############################
_FAKE_RPC_ID = 'fake-rpc-id'
_FAKE_DEVICE_TYPE = 'fake-device-type'
_FAKE_DEVICE_ID = 'fake-device-id'
_FAKE_CAPABILITY = 'fake-device-capability'
_SET_ON = 'setOn'
_SET_LOCK = 'setLock'
##############################################################################
def rpc_request(method, params):
"""Simple wrapper for json rpc request."""
return {'jsonrpc': '2.0', 'id': 0, 'method': method, 'params': params}
class TranslationLayerTest(unittest.TestCase):
"""Unit tests for local agent translation layer."""
def setUp(self):
super().setUp()
self.mock_client = mock.Mock()
self.translator = translation_layer.TranslationLayer(
client=self.mock_client)
@mock.patch.object(gdm_manager.GdmManager, 'create_devices')
def test_01_create_devices(self, mock_create):
"""Verifies create_devices method on success."""
fake_dut_ids = [_FAKE_DEVICE_ID]
fake_suite_dir = ''
self.translator.create_devices(fake_dut_ids, fake_suite_dir)
mock_create.assert_called_once_with(fake_dut_ids, fake_suite_dir)
@mock.patch.object(gdm_manager.GdmManager, 'close_open_devices')
def test_02_close_devices(self, mock_close):
"""Verifies close_devices method on success."""
self.translator.close_devices()
mock_close.assert_called_once()
@mock.patch.object(gdm_manager.GdmManager, 'detect_devices')
def test_03_detect_devices(self, mock_detect):
"""Verifies detect_devices on success."""
self.translator.detect_devices()
self.assertEqual(1, mock_detect.call_count)
@mock.patch.object(translation_layer.TranslationLayer, '_get_cmd_handler')
@mock.patch.object(gdm_manager.GdmManager, 'check_device_connected')
def test_04_dispatch_to_cmd_handler_on_success(self, mock_check, mock_get):
"""Verifies dispatch_to_cmd_handler on success."""
self.translator._busy_devices.clear()
set_on = rpc_request(_SET_ON, {'dutDeviceId': _FAKE_DEVICE_ID})
self.translator.dispatch_to_cmd_handler(set_on)
self.assertEqual(1, mock_check.call_count)
self.assertEqual(1, mock_get.call_count)
def test_04_dispatch_to_cmd_handler_on_failure_no_device(self):
"""Verifies dispatch_to_cmd_handler on failure with no device params."""
invalid_rpc_request = rpc_request(_SET_ON, {})
error_msg = 'no dutDeviceId in params'
with self.assertRaisesRegex(agent_errors.InvalidRPCError, error_msg):
self.translator.dispatch_to_cmd_handler(invalid_rpc_request)
@mock.patch.object(translation_layer, 'issubclass', return_value=True)
def test_05_validate_handlers_cls_map_on_success(self, mock_isusbclass):
"""Verifies validate_handlers_cls_map on success."""
fake_handlers = [mock.Mock(SUPPORTED_METHODS={_SET_ON,}),
mock.Mock(SUPPORTED_METHODS={_SET_LOCK,})]
translation_layer.validate_handlers_cls_map(fake_handlers)
self.assertEqual(2, mock_isusbclass.call_count)
@mock.patch.object(translation_layer, 'issubclass', return_value=False)
def test_05_validate_handlers_cls_map_on_failure_not_subclass(
self, mock_issubclass):
"""Verifies validate_handlers_cls_map invalid handler."""
error_msg = 'is not a subclass of BaseCommandHandler.'
with self.assertRaisesRegex(agent_errors.HandlerInvalidError, error_msg):
translation_layer.validate_handlers_cls_map([mock.Mock(__name__='')])
@mock.patch.object(translation_layer, 'issubclass', return_value=True)
def test_05_validate_handlers_cls_map_on_failure_collision(
self, mock_issubclass):
"""Verifies validate_handlers_cls_map collision."""
fake_handlers = [mock.Mock(SUPPORTED_METHODS={_SET_ON,}, __name__='1'),
mock.Mock(SUPPORTED_METHODS={_SET_ON,}, __name__='2')]
error_msg = f'Handlers 1 and 2 have duplicate methods: {_SET_ON}.'
with self.assertRaisesRegex(agent_errors.HandlersCollisionError, error_msg):
translation_layer.validate_handlers_cls_map(fake_handlers)
self.assertEqual(2, mock_issubclass.call_count)
def test_06_get_cmd_handler_on_success_already_exists(self):
"""Verifies get_cmd_handler on success with existing handler."""
method = _SET_ON
self.translator._cmd_handlers[_FAKE_DEVICE_ID] = (
mock.Mock(SUPPORTED_METHODS={method,}))
handler = self.translator._get_cmd_handler(_FAKE_DEVICE_ID, method)
self.assertIn(method, handler.SUPPORTED_METHODS)
def test_06_get_cmd_handler_on_failure_method_not_found(self):
"""Verifies get_cmd_handler on failure method not found."""
self.translator._cmd_handlers[_FAKE_DEVICE_ID] = (
mock.Mock(SUPPORTED_METHODS={_SET_LOCK,}))
error_msg = 'No matching command handler'
with self.assertRaisesRegex(agent_errors.HandlerNotFoundError, error_msg):
self.translator._get_cmd_handler(_FAKE_DEVICE_ID, _SET_ON)
@mock.patch.object(gdm_manager.GdmManager, 'get_device_instance')
@mock.patch.object(gdm_manager.GdmManager, 'get_device_type')
def test_06_get_cmd_handler_on_success_not_exists(
self, mock_get_type, mock_get_inst):
"""Verifies get_cmd_handler on success without existing handler."""
method = _SET_ON
self.translator._cmd_handlers.clear()
self.translator._handlers_cls_map[_FAKE_DEVICE_TYPE] = (
{method: mock.Mock()})
mock_get_type.return_value = _FAKE_DEVICE_TYPE
handler = self.translator._get_cmd_handler(_FAKE_DEVICE_ID, method)
self.assertEqual(
self.translator._cmd_handlers[_FAKE_DEVICE_ID], handler)
self.assertEqual(1, mock_get_type.call_count)
self.assertEqual(1, mock_get_inst.call_count)
@mock.patch.object(gdm_manager.GdmManager, 'get_device_type')
def test_06_get_cmd_handler_on_failure_invalid_device_type(self, mock_get_type):
"""Verifies get_cmd_handler on failure with invalid device type."""
fake_not_exist_type = 'not-exists-type'
mock_get_type.return_value = fake_not_exist_type
error_msg = f'device type: {fake_not_exist_type} is not implemented'
with self.assertRaisesRegex(agent_errors.HandlerNotFoundError, error_msg):
self.translator._get_cmd_handler(_FAKE_DEVICE_TYPE, '')
self.assertEqual(1, mock_get_type.call_count)
@mock.patch.object(gdm_manager.GdmManager, 'get_device_type')
def test_06_get_cmd_handler_on_failure_invalid_method(self, mock_get_type):
"""Verifies get_cmd_handler on failure with invalid method."""
self.translator._cmd_handlers.clear()
self.translator._handlers_cls_map[_FAKE_DEVICE_TYPE] = (
{_SET_ON: mock.Mock()})
mock_get_type.return_value = _FAKE_DEVICE_TYPE
error_msg = 'method: setLock is not implemented'
with self.assertRaisesRegex(agent_errors.HandlerNotFoundError, error_msg):
self.translator._get_cmd_handler(_FAKE_DEVICE_ID, 'setLock')
self.assertEqual(1, mock_get_type.call_count)
def test_07_device_operation_handler_on_failure_is_busy(self):
"""Verifies device_operation_handler raises when device is still busy"""
self.translator._busy_devices = {_FAKE_DEVICE_ID,}
error_msg = f'{_FAKE_DEVICE_ID} is still busy'
with self.assertRaisesRegex(agent_errors.InvalidRPCError, error_msg):
with self.translator.device_operation_handler(
_FAKE_DEVICE_ID, _FAKE_RPC_ID):
pass
def test_07_device_operation_handler_on_failure_duplicate_rpc(self):
"""Verifies device_operation_handler raises when duplicate RPC"""
self.translator._rpc_execution_start_time[_FAKE_RPC_ID] = 0
error_msg = f'RPC {_FAKE_RPC_ID} is already executing.'
with self.assertRaisesRegex(agent_errors.InvalidRPCError, error_msg):
with self.translator.device_operation_handler(
_FAKE_DEVICE_ID, _FAKE_RPC_ID):
pass
@mock.patch.object(translation_layer, 'validate_handlers_cls_map')
def test_08_update_handlers_cls_map_on_success_creation(
self, mock_validate):
"""Verifies _update_handlers_cls_map creation on success."""
fake_handler = mock.Mock(SUPPORTED_METHODS={_SET_ON,})
fake_capabilities_map = {
_FAKE_CAPABILITY: [fake_handler]}
translation_layer.GDM_CAPABILITIES_TO_COMMAND_HANDLERS = (
fake_capabilities_map)
self.translator.update_handlers_cls_map(
_FAKE_DEVICE_TYPE, [_FAKE_CAPABILITY])
handler = self.translator._handlers_cls_map[_FAKE_DEVICE_TYPE][_SET_ON]
self.assertEqual(fake_handler, handler)
self.assertEqual(1, mock_validate.call_count)
@mock.patch.object(translation_layer, 'validate_handlers_cls_map')
def test_08_update_handlers_cls_map_does_nothing(self, mock_validate):
"""Verifies the _update_handlers_cls_map does nothing."""
self.translator._handlers_cls_map[_FAKE_DEVICE_TYPE][_SET_ON] = None
self.translator.update_handlers_cls_map(
_FAKE_DEVICE_TYPE, [_FAKE_CAPABILITY])
self.assertEqual(0, mock_validate.call_count)
def test_09_check_rpc_timeout(self):
"""Verifies _check_rpc_timeout catches timeout RPCs."""
fake_terminiation_event = mock.Mock()
fake_terminiation_event.wait.side_effect = [False, True]
self.translator._termination_event = fake_terminiation_event
self.translator._timeout_rpc.clear()
self.translator._rpc_execution_start_time = {_FAKE_DEVICE_ID: 0}
self.translator._check_rpc_timeout()
self.mock_client.send_rpc_response.assert_called_once()
self.assertTrue(self.translator.is_rpc_timeout(_FAKE_DEVICE_ID))
if __name__ == '__main__':
unittest.main(failfast=True)