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