1# Copyright 2018 Google Inc. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14"""Unit tests for controller manager.""" 15 16import unittest 17from unittest import mock 18 19from mobly import controller_manager 20from mobly import signals 21from tests.lib import mock_controller 22 23 24class ControllerManagerTest(unittest.TestCase): 25 """Unit tests for Mobly's ControllerManager.""" 26 27 def test_verify_controller_module(self): 28 controller_manager.verify_controller_module(mock_controller) 29 30 def test_verify_controller_module_null_attr(self): 31 try: 32 tmp = mock_controller.MOBLY_CONTROLLER_CONFIG_NAME 33 mock_controller.MOBLY_CONTROLLER_CONFIG_NAME = None 34 msg = 'Controller interface .* in .* cannot be null.' 35 with self.assertRaisesRegex(signals.ControllerError, msg): 36 controller_manager.verify_controller_module(mock_controller) 37 finally: 38 mock_controller.MOBLY_CONTROLLER_CONFIG_NAME = tmp 39 40 def test_verify_controller_module_missing_attr(self): 41 try: 42 tmp = mock_controller.MOBLY_CONTROLLER_CONFIG_NAME 43 delattr(mock_controller, 'MOBLY_CONTROLLER_CONFIG_NAME') 44 msg = 'Module .* missing required controller module attribute' 45 with self.assertRaisesRegex(signals.ControllerError, msg): 46 controller_manager.verify_controller_module(mock_controller) 47 finally: 48 setattr(mock_controller, 'MOBLY_CONTROLLER_CONFIG_NAME', tmp) 49 50 def test_register_controller_no_config(self): 51 c_manager = controller_manager.ControllerManager('SomeClass', {}) 52 with self.assertRaisesRegex( 53 signals.ControllerError, 'No corresponding config found for' 54 ): 55 c_manager.register_controller(mock_controller) 56 57 def test_register_controller_no_config_for_not_required(self): 58 c_manager = controller_manager.ControllerManager('SomeClass', {}) 59 self.assertIsNone( 60 c_manager.register_controller(mock_controller, required=False) 61 ) 62 63 def test_register_controller_dup_register(self): 64 """Verifies correctness of registration, internal tally of controllers 65 objects, and the right error happen when a controller module is 66 registered twice. 67 """ 68 mock_ctrlr_config_name = mock_controller.MOBLY_CONTROLLER_CONFIG_NAME 69 controller_configs = {mock_ctrlr_config_name: ['magic1', 'magic2']} 70 c_manager = controller_manager.ControllerManager( 71 'SomeClass', controller_configs 72 ) 73 c_manager.register_controller(mock_controller) 74 registered_name = 'mock_controller' 75 self.assertTrue(registered_name in c_manager._controller_objects) 76 mock_ctrlrs = c_manager._controller_objects[registered_name] 77 self.assertEqual(mock_ctrlrs[0].magic, 'magic1') 78 self.assertEqual(mock_ctrlrs[1].magic, 'magic2') 79 self.assertTrue(c_manager._controller_modules[registered_name]) 80 expected_msg = 'Controller module .* has already been registered.' 81 with self.assertRaisesRegex(signals.ControllerError, expected_msg): 82 c_manager.register_controller(mock_controller) 83 84 def test_register_controller_return_value(self): 85 mock_ctrlr_config_name = mock_controller.MOBLY_CONTROLLER_CONFIG_NAME 86 controller_configs = {mock_ctrlr_config_name: ['magic1', 'magic2']} 87 c_manager = controller_manager.ControllerManager( 88 'SomeClass', controller_configs 89 ) 90 magic_devices = c_manager.register_controller(mock_controller) 91 self.assertEqual(magic_devices[0].magic, 'magic1') 92 self.assertEqual(magic_devices[1].magic, 'magic2') 93 94 def test_register_controller_change_return_value(self): 95 mock_ctrlr_config_name = mock_controller.MOBLY_CONTROLLER_CONFIG_NAME 96 controller_configs = {mock_ctrlr_config_name: ['magic1', 'magic2']} 97 c_manager = controller_manager.ControllerManager( 98 'SomeClass', controller_configs 99 ) 100 magic_devices = c_manager.register_controller(mock_controller) 101 magic1 = magic_devices.pop(0) 102 self.assertIs(magic1, c_manager._controller_objects['mock_controller'][0]) 103 self.assertEqual(len(c_manager._controller_objects['mock_controller']), 2) 104 105 def test_register_controller_less_than_min_number(self): 106 mock_ctrlr_config_name = mock_controller.MOBLY_CONTROLLER_CONFIG_NAME 107 controller_configs = {mock_ctrlr_config_name: ['magic1', 'magic2']} 108 c_manager = controller_manager.ControllerManager( 109 'SomeClass', controller_configs 110 ) 111 expected_msg = 'Expected to get at least 3 controller objects, got 2.' 112 with self.assertRaisesRegex(signals.ControllerError, expected_msg): 113 c_manager.register_controller(mock_controller, min_number=3) 114 115 @mock.patch('yaml.dump', side_effect=TypeError('ha')) 116 def test_get_controller_info_record_not_serializable(self, _): 117 mock_ctrlr_config_name = mock_controller.MOBLY_CONTROLLER_CONFIG_NAME 118 controller_configs = {mock_ctrlr_config_name: ['magic1', 'magic2']} 119 c_manager = controller_manager.ControllerManager( 120 'SomeClass', controller_configs 121 ) 122 c_manager.register_controller(mock_controller) 123 record = c_manager.get_controller_info_records()[0] 124 actual_controller_info = record.controller_info 125 self.assertEqual( 126 actual_controller_info, "[{'MyMagic': 'magic1'}, {'MyMagic': 'magic2'}]" 127 ) 128 129 def test_controller_record_exists_without_get_info(self): 130 mock_ctrlr_config_name = mock_controller.MOBLY_CONTROLLER_CONFIG_NAME 131 controller_configs = {mock_ctrlr_config_name: ['magic1', 'magic2']} 132 c_manager = controller_manager.ControllerManager( 133 'SomeClass', controller_configs 134 ) 135 get_info = getattr(mock_controller, 'get_info') 136 delattr(mock_controller, 'get_info') 137 try: 138 c_manager.register_controller(mock_controller) 139 record = c_manager.get_controller_info_records()[0] 140 self.assertIsNone(record.controller_info) 141 self.assertEqual(record.test_class, 'SomeClass') 142 self.assertEqual(record.controller_name, 'MagicDevice') 143 finally: 144 setattr(mock_controller, 'get_info', get_info) 145 146 @mock.patch('tests.lib.mock_controller.get_info') 147 def test_get_controller_info_records_empty(self, mock_get_info_func): 148 mock_get_info_func.return_value = None 149 mock_ctrlr_config_name = mock_controller.MOBLY_CONTROLLER_CONFIG_NAME 150 controller_configs = {mock_ctrlr_config_name: ['magic1', 'magic2']} 151 c_manager = controller_manager.ControllerManager( 152 'SomeClass', controller_configs 153 ) 154 c_manager.register_controller(mock_controller) 155 record = c_manager.get_controller_info_records()[0] 156 self.assertIsNone(record.controller_info) 157 self.assertEqual(record.test_class, 'SomeClass') 158 self.assertEqual(record.controller_name, 'MagicDevice') 159 160 @mock.patch('mobly.expects._ExpectErrorRecorder.add_error') 161 @mock.patch('tests.lib.mock_controller.get_info') 162 def test_get_controller_info_records_error( 163 self, mock_get_info_func, mock_add_error 164 ): 165 mock_get_info_func.side_effect = Exception('Record info failed.') 166 mock_ctrlr_config_name = mock_controller.MOBLY_CONTROLLER_CONFIG_NAME 167 controller_configs = {mock_ctrlr_config_name: ['magic1', 'magic2']} 168 c_manager = controller_manager.ControllerManager( 169 'SomeClass', controller_configs 170 ) 171 c_manager.register_controller(mock_controller) 172 self.assertFalse(c_manager.get_controller_info_records()) 173 mock_add_error.assert_called_once() 174 error_record = mock_add_error.call_args[0][0] 175 self.assertIn('Record info failed.', error_record.stacktrace) 176 177 def test_get_controller_info_records(self): 178 mock_ctrlr_config_name = mock_controller.MOBLY_CONTROLLER_CONFIG_NAME 179 controller_configs = {mock_ctrlr_config_name: ['magic1', 'magic2']} 180 c_manager = controller_manager.ControllerManager( 181 'SomeClass', controller_configs 182 ) 183 c_manager.register_controller(mock_controller) 184 record = c_manager.get_controller_info_records()[0] 185 record_dict = record.to_dict() 186 record_dict.pop('Timestamp') 187 self.assertEqual( 188 record_dict, 189 { 190 'Controller Info': [{'MyMagic': 'magic1'}, {'MyMagic': 'magic2'}], 191 'Controller Name': 'MagicDevice', 192 'Test Class': 'SomeClass', 193 }, 194 ) 195 196 def test_get_controller_info_without_registration(self): 197 mock_ctrlr_config_name = mock_controller.MOBLY_CONTROLLER_CONFIG_NAME 198 controller_configs = {mock_ctrlr_config_name: ['magic1', 'magic2']} 199 c_manager = controller_manager.ControllerManager( 200 'SomeClass', controller_configs 201 ) 202 self.assertFalse(c_manager.get_controller_info_records()) 203 204 @mock.patch('tests.lib.mock_controller.destroy') 205 def test_unregister_controller(self, mock_destroy_func): 206 mock_ctrlr_config_name = mock_controller.MOBLY_CONTROLLER_CONFIG_NAME 207 controller_configs = {mock_ctrlr_config_name: ['magic1', 'magic2']} 208 c_manager = controller_manager.ControllerManager( 209 'SomeClass', controller_configs 210 ) 211 objects = c_manager.register_controller(mock_controller) 212 c_manager.unregister_controllers() 213 mock_destroy_func.assert_called_once_with(objects) 214 self.assertFalse(c_manager._controller_objects) 215 self.assertFalse(c_manager._controller_modules) 216 217 @mock.patch('mobly.expects._ExpectErrorRecorder.add_error') 218 @mock.patch('tests.lib.mock_controller.destroy') 219 def test_unregister_controller_error(self, mock_destroy_func, mock_add_error): 220 mock_ctrlr_config_name = mock_controller.MOBLY_CONTROLLER_CONFIG_NAME 221 controller_configs = {mock_ctrlr_config_name: ['magic1', 'magic2']} 222 c_manager = controller_manager.ControllerManager( 223 'SomeClass', controller_configs 224 ) 225 c_manager.register_controller(mock_controller) 226 mock_destroy_func.side_effect = Exception('Failed in destroy.') 227 c_manager.unregister_controllers() 228 mock_add_error.assert_called_once() 229 error_record = mock_add_error.call_args[0][0] 230 self.assertIn('Failed in destroy.', error_record.stacktrace) 231 self.assertFalse(c_manager._controller_objects) 232 self.assertFalse(c_manager._controller_modules) 233 234 @mock.patch('tests.lib.mock_controller.destroy') 235 def test_unregister_controller_without_registration(self, mock_destroy_func): 236 mock_ctrlr_config_name = mock_controller.MOBLY_CONTROLLER_CONFIG_NAME 237 controller_configs = {mock_ctrlr_config_name: ['magic1', 'magic2']} 238 c_manager = controller_manager.ControllerManager( 239 'SomeClass', controller_configs 240 ) 241 c_manager.unregister_controllers() 242 mock_destroy_func.assert_not_called() 243 self.assertFalse(c_manager._controller_objects) 244 self.assertFalse(c_manager._controller_modules) 245 246 247if __name__ == '__main__': 248 unittest.main() 249