1 # Copyright 2015 The Chromium OS Authors. All rights reserved. 2 # Use of this source code is governed by a BSD-style license that can be 3 # found in the LICENSE file. 4 5 import array 6 import logging 7 import unittest 8 9 import common 10 from autotest_lib.client.cros.cellular.mbim_compliance import mbim_constants 11 from autotest_lib.client.cros.cellular.mbim_compliance import \ 12 mbim_command_message 13 from autotest_lib.client.cros.cellular.mbim_compliance import mbim_errors 14 from autotest_lib.client.cros.cellular.mbim_compliance import mbim_message 15 from autotest_lib.client.cros.cellular.mbim_compliance import \ 16 mbim_message_request 17 from autotest_lib.client.cros.cellular.mbim_compliance import \ 18 mbim_message_response 19 20 21 class TestMessage(mbim_message.MBIMControlMessage): 22 """ MBIMMessage for unit testing. """ 23 _FIELDS = (('I', 'message_type', mbim_message.FIELD_TYPE_PAYLOAD_ID), 24 ('I', 'message_length', ''), 25 ('I', 'transaction_id', '')) 26 _DEFAULTS = {'message_length': 0, 'transaction_id': 0} 27 28 29 class MBIMMessageTestCase(unittest.TestCase): 30 """ Test cases for verifying MBIMMessage classes and MBIMMessageParser. """ 31 32 33 def test_fields_not_defined(self): 34 """ 35 Verifies that an excepion is raised when constructing an MBIMMessage 36 subclass that does not define a _FIELDS attribute. 37 """ 38 with self.assertRaisesRegexp( 39 mbim_errors.MBIMComplianceControlMessageError, 40 'message must have some fields defined$'): 41 class MBIMMessageFieldsNotDefined(mbim_message.MBIMControlMessage): 42 """ MBIMMessage without _FIELDS attribute. """ 43 pass 44 45 46 def test_message_missing_field_values(self): 47 """ 48 Verifies that an exception is raised when constructing an MBIMMessage 49 subclass object without providing values for all of the fields either 50 in _DEFAULTS or in the constructor. 51 """ 52 with self.assertRaisesRegexp( 53 mbim_errors.MBIMComplianceControlMessageError, 54 '^Missing field value'): 55 message = TestMessage() 56 57 58 def test_argument_mismatch(self): 59 """ 60 Verifies that an exception is raised when there is any argument which is 61 not defined in the control message class. 62 """ 63 with self.assertRaisesRegexp( 64 mbim_errors.MBIMComplianceControlMessageError, 65 '^Unexpected fields'): 66 message = TestMessage(message_type=4, fake=5) 67 68 69 def test_message_default_value_set(self): 70 """ 71 Verifies that the values for fields not provided in MBIMMessage 72 constructor is taken from the _DEFAULTS attribute of the class. 73 """ 74 message = TestMessage(message_type=3) 75 self.assertEqual(message.message_length, 0) 76 self.assertEqual(message.transaction_id, 0) 77 self.assertEqual(message.message_type, 3) 78 79 80 def test_message_default_value_override(self): 81 """ 82 Verifies that the values for fields provided in MBIMMessage 83 constructor overrides the values from the _DEFAULTS attribute of the 84 class. 85 """ 86 message = TestMessage(message_type=3, transaction_id=4) 87 self.assertEqual(message.message_length, 0) 88 self.assertEqual(message.transaction_id, 4) 89 self.assertEqual(message.message_type, 3) 90 91 92 def test_message_data_less_than_total_size_of_fields(self): 93 """ 94 Verifies that an exception is raised when constructing a MBIMMessage 95 subclass from raw message data of length less than the total size of 96 fields specified by the _FIELDS attribute. 97 """ 98 with self.assertRaisesRegexp( 99 mbim_errors.MBIMComplianceControlMessageError, 100 '^Length of Data'): 101 message_data = array.array('B', [0x02, 0xAA]) 102 message = TestMessage(raw_data=message_data) 103 104 105 def test_message_data_more_than_total_size_of_fields(self): 106 """ 107 Verifies that it is OK to construct a MBIMMessage subclass from raw 108 message data of length more than the total size of fields specified 109 by the _FIELDS attribute. The additional data is put into 110 |payload_buffer| field. 111 """ 112 message_data = array.array('B', [0x02, 0xAA, 0xAA, 0XCC, 0xED, 0x98, 113 0x80, 0x80, 0xAA, 0xED, 0x45, 0x45, 114 0x50, 0x40]) 115 message = TestMessage(raw_data=message_data) 116 self.assertEqual(message.payload_buffer, array.array('B', [0x50, 0x40])) 117 118 119 def test_parse_mbim_open_done(self): 120 """ 121 Verifies the packets of |MBIM_OPEN_DONE| type are parsed correctly. 122 """ 123 packets = [array.array('B', [0x01, 0x00, 0x00, 0x80, 0x10, 0x00, 0x00, 124 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 125 0x00, 0x00])] 126 message = mbim_message_response.parse_response_packets(packets) 127 self.assertEqual(True, isinstance(message, 128 mbim_message_response.MBIMOpenDone)) 129 self.assertEqual(message.message_type, mbim_constants.MBIM_OPEN_DONE) 130 self.assertEqual(message.message_length, 16) 131 self.assertEqual(message.transaction_id, 1) 132 self.assertEqual(message.status_codes, 133 mbim_constants.MBIM_STATUS_SUCCESS) 134 135 136 def test_parse_mbim_close_done(self): 137 """ 138 Verifies the packets of |MBIM_OPEN_DONE| type are parsed correctly. 139 """ 140 packets = [array.array('B', [0x02, 0x00, 0x00, 0x80, 0x10, 0x00, 0x00, 141 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 142 0x00, 0x00])] 143 message = mbim_message_response.parse_response_packets(packets) 144 self.assertEqual(True, isinstance(message, 145 mbim_message_response.MBIMCloseDone)) 146 self.assertEqual(message.message_type, mbim_constants.MBIM_CLOSE_DONE) 147 self.assertEqual(message.message_length, 16) 148 self.assertEqual(message.transaction_id, 1) 149 self.assertEqual(message.status_codes, 150 mbim_constants.MBIM_STATUS_SUCCESS) 151 152 153 def test_parse_mbim_function_error_msg(self): 154 """ 155 Verifies the |MBIM_FUNCTION_ERROR_MSG| packets are parsed correctly. 156 """ 157 packets = [array.array('B', [0x04, 0x00, 0x00, 0x80, 0x10, 0x00, 0x00, 158 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 159 0x00, 0x00])] 160 message = mbim_message_response.parse_response_packets(packets) 161 self.assertEqual(True, isinstance(message, 162 mbim_message_response.MBIMFunctionError)) 163 self.assertEqual(message.message_type, 164 mbim_constants.MBIM_FUNCTION_ERROR_MSG) 165 self.assertEqual(message.message_length, 16) 166 self.assertEqual(message.transaction_id, 1) 167 self.assertEqual(message.error_status_code, 168 mbim_constants.MBIM_ERROR_UNKNOWN) 169 170 171 def test_parse_mbim_command_done(self): 172 """ 173 Verifies the packets of |MBIM_COMMAND_DONE| type are parsed correctly. 174 This tests both the fragmentation reassembly and message parsing 175 functionality. 176 """ 177 packets = [array.array('B', [0x03, 0x00, 0x00, 0x80, 0x34, 0x00, 0x00, 178 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 179 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 180 0x00, 0x06, 0xEE, 0x00, 0x00, 0x00, 0x00, 181 0x80, 0x40, 0x20, 0x10, 0x00, 0xAA, 0xBB, 182 0xCC, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 183 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 184 0x01, 0x01, 0x01]), 185 array.array('B', [0x03, 0x00, 0x00, 0x80, 0x18, 0x00, 0x00, 186 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 187 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 188 0x01, 0x01, 0x01])] 189 message = mbim_message_response.parse_response_packets(packets) 190 is_instance = isinstance(message, 191 mbim_message_response.MBIMCommandDone) 192 self.assertEqual(is_instance, True) 193 self.assertEqual(message.message_type, mbim_constants.MBIM_COMMAND_DONE) 194 self.assertEqual(message.message_length, 56) 195 self.assertEqual(message.transaction_id, 1) 196 self.assertEqual(message.total_fragments, 2) 197 self.assertEqual(message.current_fragment, 0) 198 self.assertEqual(message.device_service_id, 199 '\x02\x00\x06\xEE\x00\x00\x00\x00\x80\x40\x20\x10' 200 '\x00\xAA\xBB\xCC') 201 self.assertEqual(message.cid, 1) 202 self.assertEqual(message.status_codes, 203 mbim_constants.MBIM_STATUS_SUCCESS) 204 self.assertEqual(message.information_buffer_length, 8) 205 self.assertEqual(message.payload_buffer, 206 array.array('B', [0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 207 0x01, 0x01])) 208 209 210 def test_parse_mbim_get_device_caps(self): 211 """ 212 Verifies the packets of |MBIM_COMMAND_DONE| type for a GetDeviceCaps 213 CID query are parsed correctly. 214 This tests both the fragmentation reassembly and message parsing 215 functionality. 216 """ 217 packets = [array.array('B', [0x03, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 218 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 219 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA2, 220 0x89, 0xCC, 0x33, 0xBC, 0xBB, 0x8B, 0x4F, 221 0xB6, 0xB0, 0x13, 0x3E, 0xC2, 0xAA, 0xE6, 222 0xDF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 223 0x00, 0x00, 0xA0, 0x00, 0x00, 0x00, 0x01, 224 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 225 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 226 0x0]), 227 array.array('B', [0x03, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 228 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 229 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1F, 230 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x00, 231 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 232 0x00, 0x40, 0x00, 0x00, 0x00, 0x0A, 0x00, 233 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x1E, 234 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00, 235 0x1E, 0x00, 0x00, 0x00, 0x8C, 0x00, 0x00, 236 0x00]), 237 array.array('B', [0x03, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 238 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 239 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x12, 240 0x00, 0x00, 0x00, 0x48, 0x00, 0x53, 0x00, 241 0x50, 0x00, 0x41, 0x00, 0x2B, 0x00, 0x00, 242 0x00, 0x33, 0x00, 0x35, 0x00, 0x31, 0x00, 243 0x38, 0x00, 0x35, 0x00, 0x31, 0x00, 0x30, 244 0x00, 0x36, 0x00, 0x30, 0x00, 0x30, 0x00, 245 0x30, 0x00, 0x30, 0x00, 0x37, 0x00, 0x38, 246 0x00]), 247 array.array('B', [0x03, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 248 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 249 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x34, 250 0x00, 0x00, 0x00, 0x31, 0x00, 0x31, 0x00, 251 0x2E, 0x00, 0x33, 0x00, 0x35, 0x00, 0x30, 252 0x00, 0x2E, 0x00, 0x31, 0x00, 0x36, 0x00, 253 0x2E, 0x00, 0x30, 0x00, 0x34, 0x00, 0x2E, 254 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00, 255 0x4D, 0x00, 0x4C, 0x00, 0x31, 0x00, 0x4D, 256 0x0]), 257 array.array('B', [0x03, 0x00, 0x00, 0x80, 0x20, 0x00, 0x00, 258 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 259 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x45, 260 0x00, 0x39, 0x00, 0x33, 0x00, 0x36, 0x00, 261 0x4D, 0x00, 0x00, 0x00])] 262 message = mbim_message_response.parse_response_packets(packets) 263 is_instance = isinstance(message, 264 mbim_command_message.MBIMDeviceCapsInfo) 265 self.assertEqual(is_instance, True) 266 self.assertEqual(message.message_type, mbim_constants.MBIM_COMMAND_DONE) 267 self.assertEqual(message.message_length, 208) 268 self.assertEqual(message.transaction_id, 1) 269 self.assertEqual(message.total_fragments, 5) 270 self.assertEqual(message.current_fragment, 0) 271 self.assertEqual(message.device_service_id, 272 '\xA2\x89\xCC3\xBC\xBB\x8BO\xB6\xB0\x13>\xC2\xAA\xE6' 273 '\xDF') 274 self.assertEqual(message.cid, 1) 275 self.assertEqual(message.status_codes, 276 mbim_constants.MBIM_STATUS_SUCCESS) 277 self.assertEqual(message.information_buffer_length, 160) 278 self.assertEqual(message.device_type, 1) 279 self.assertEqual(message.cellular_class, 1) 280 self.assertEqual(message.voice_class, 1) 281 self.assertEqual(message.sim_class, 2) 282 self.assertEqual(message.data_class, 2147483679) 283 self.assertEqual(message.sms_caps, 3) 284 self.assertEqual(message.control_caps, 3) 285 self.assertEqual(message.max_sessions, 8) 286 self.assertEqual(message.custom_data_class_offset, 64) 287 self.assertEqual(message.custom_data_class_size, 10) 288 self.assertEqual(message.device_id_offset, 76) 289 self.assertEqual(message.device_id_size, 30) 290 self.assertEqual(message.firmware_info_offset, 108) 291 self.assertEqual(message.firmware_info_size, 30) 292 self.assertEqual(message.hardware_info_offset, 140) 293 self.assertEqual(message.hardware_info_size, 18) 294 295 296 def test_generate_mbim_open(self): 297 """ 298 Verifies the raw packet of |MBIM_OPEN| type is generated correctly. 299 """ 300 message = mbim_message_request.MBIMOpen(max_control_transfer=40) 301 packets = mbim_message_request.generate_request_packets(message, 64) 302 self.assertEqual(packets, [array.array('B', [0x01, 0x00, 0x00, 0x00, 303 0x10, 0x00, 0x00, 0x00, 304 0x02, 0x00, 0x00, 0x00, 305 0x28, 0x00, 0x00, 0x00])]) 306 307 308 def test_generate_mbim_command_packets(self): 309 """ 310 Verifies the raw packets of |MBIM_COMMAND| type are generated correctly. 311 This verifies the fragmentation logic in the generate_request_packets. 312 """ 313 payload_buffer=array.array('B', [0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 314 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 315 0x28, 0x00, 0x00, 0x00, 0x04, 0x05, 316 0x06, 0x10, 0x87, 0xDE, 0xED, 0xAC, 317 0x45, 0x35, 0x50, 0x60, 0x90, 0xED, 318 0xAB]) 319 message = mbim_message_request.MBIMCommand( 320 device_service_id=mbim_constants.UUID_BASIC_CONNECT.bytes, 321 cid=mbim_constants.MBIM_CID_DEVICE_CAPS, 322 command_type=mbim_constants.COMMAND_TYPE_QUERY, 323 information_buffer_length=len(payload_buffer), 324 payload_buffer=payload_buffer) 325 packets = mbim_message_request.generate_request_packets(message, 64) 326 self.assertEqual(packets, [array.array('B', [0x03, 0x00, 0x00, 0x00, 327 0x40, 0x00, 0x00, 0x00, 328 0x01, 0x00, 0x00, 0x00, 329 0x02, 0x00, 0x00, 0x00, 330 0x00, 0x00, 0x00, 0x00, 331 0xA2, 0x89, 0xCC, 0x33, 332 0xBC, 0xBB, 0x8B, 0x4F, 333 0xB6, 0xB0, 0x13, 0x3E, 334 0xC2, 0xAA, 0xE6, 0xDF, 335 0x01, 0x00, 0x00, 0x00, 336 0x00, 0x00, 0x00, 0x00, 337 0x1F, 0x00, 0x00, 0x00, 338 0x01, 0x00, 0x00, 0x00, 339 0x10, 0x00, 0x00, 0x00, 340 0x01, 0x00, 0x00, 0x00, 341 0x28, 0x00, 0x00, 0x00]), 342 array.array('B', [0x03, 0x00, 0x00, 0x00, 343 0x23, 0x00, 0x00, 0x00, 344 0x01, 0x00, 0x00, 0x00, 345 0x02, 0x00, 0x00, 0x00, 346 0x01, 0x00, 0x00, 0x00, 347 0x04, 0x05, 0x06, 0x10, 348 0x87, 0xDE, 0xED, 0xAC, 349 0x45, 0x35, 0x50, 0x60, 350 0x90, 0xED, 0xAB])]) 351 352 353 if __name__ == '__main__': 354 logging.basicConfig(level=logging.DEBUG) 355 unittest.main() 356