# Copyright 2015 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import array
import logging
import unittest
import common
from autotest_lib.client.cros.cellular.mbim_compliance import mbim_constants
from autotest_lib.client.cros.cellular.mbim_compliance import \
mbim_command_message
from autotest_lib.client.cros.cellular.mbim_compliance import mbim_errors
from autotest_lib.client.cros.cellular.mbim_compliance import mbim_message
from autotest_lib.client.cros.cellular.mbim_compliance import \
mbim_message_request
from autotest_lib.client.cros.cellular.mbim_compliance import \
mbim_message_response
class TestMessage(mbim_message.MBIMControlMessage):
""" MBIMMessage for unit testing. """
_FIELDS = (('I', 'message_type', mbim_message.FIELD_TYPE_PAYLOAD_ID),
('I', 'message_length', ''),
('I', 'transaction_id', ''))
_DEFAULTS = {'message_length': 0, 'transaction_id': 0}
class MBIMMessageTestCase(unittest.TestCase):
""" Test cases for verifying MBIMMessage classes and MBIMMessageParser. """
def test_fields_not_defined(self):
"""
Verifies that an excepion is raised when constructing an MBIMMessage
subclass that does not define a _FIELDS attribute.
"""
with self.assertRaisesRegexp(
mbim_errors.MBIMComplianceControlMessageError,
'message must have some fields defined$'):
class MBIMMessageFieldsNotDefined(mbim_message.MBIMControlMessage):
""" MBIMMessage without _FIELDS attribute. """
pass
def test_message_missing_field_values(self):
"""
Verifies that an exception is raised when constructing an MBIMMessage
subclass object without providing values for all of the fields either
in _DEFAULTS or in the constructor.
"""
with self.assertRaisesRegexp(
mbim_errors.MBIMComplianceControlMessageError,
'^Missing field value'):
message = TestMessage()
def test_argument_mismatch(self):
"""
Verifies that an exception is raised when there is any argument which is
not defined in the control message class.
"""
with self.assertRaisesRegexp(
mbim_errors.MBIMComplianceControlMessageError,
'^Unexpected fields'):
message = TestMessage(message_type=4, fake=5)
def test_message_default_value_set(self):
"""
Verifies that the values for fields not provided in MBIMMessage
constructor is taken from the _DEFAULTS attribute of the class.
"""
message = TestMessage(message_type=3)
self.assertEqual(message.message_length, 0)
self.assertEqual(message.transaction_id, 0)
self.assertEqual(message.message_type, 3)
def test_message_default_value_override(self):
"""
Verifies that the values for fields provided in MBIMMessage
constructor overrides the values from the _DEFAULTS attribute of the
class.
"""
message = TestMessage(message_type=3, transaction_id=4)
self.assertEqual(message.message_length, 0)
self.assertEqual(message.transaction_id, 4)
self.assertEqual(message.message_type, 3)
def test_message_data_less_than_total_size_of_fields(self):
"""
Verifies that an exception is raised when constructing a MBIMMessage
subclass from raw message data of length less than the total size of
fields specified by the _FIELDS attribute.
"""
with self.assertRaisesRegexp(
mbim_errors.MBIMComplianceControlMessageError,
'^Length of Data'):
message_data = array.array('B', [0x02, 0xAA])
message = TestMessage(raw_data=message_data)
def test_message_data_more_than_total_size_of_fields(self):
"""
Verifies that it is OK to construct a MBIMMessage subclass from raw
message data of length more than the total size of fields specified
by the _FIELDS attribute. The additional data is put into
|payload_buffer| field.
"""
message_data = array.array('B', [0x02, 0xAA, 0xAA, 0XCC, 0xED, 0x98,
0x80, 0x80, 0xAA, 0xED, 0x45, 0x45,
0x50, 0x40])
message = TestMessage(raw_data=message_data)
self.assertEqual(message.payload_buffer, array.array('B', [0x50, 0x40]))
def test_parse_mbim_open_done(self):
"""
Verifies the packets of |MBIM_OPEN_DONE| type are parsed correctly.
"""
packets = [array.array('B', [0x01, 0x00, 0x00, 0x80, 0x10, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00])]
message = mbim_message_response.parse_response_packets(packets)
self.assertEqual(True, isinstance(message,
mbim_message_response.MBIMOpenDone))
self.assertEqual(message.message_type, mbim_constants.MBIM_OPEN_DONE)
self.assertEqual(message.message_length, 16)
self.assertEqual(message.transaction_id, 1)
self.assertEqual(message.status_codes,
mbim_constants.MBIM_STATUS_SUCCESS)
def test_parse_mbim_close_done(self):
"""
Verifies the packets of |MBIM_OPEN_DONE| type are parsed correctly.
"""
packets = [array.array('B', [0x02, 0x00, 0x00, 0x80, 0x10, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00])]
message = mbim_message_response.parse_response_packets(packets)
self.assertEqual(True, isinstance(message,
mbim_message_response.MBIMCloseDone))
self.assertEqual(message.message_type, mbim_constants.MBIM_CLOSE_DONE)
self.assertEqual(message.message_length, 16)
self.assertEqual(message.transaction_id, 1)
self.assertEqual(message.status_codes,
mbim_constants.MBIM_STATUS_SUCCESS)
def test_parse_mbim_function_error_msg(self):
"""
Verifies the |MBIM_FUNCTION_ERROR_MSG| packets are parsed correctly.
"""
packets = [array.array('B', [0x04, 0x00, 0x00, 0x80, 0x10, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00,
0x00, 0x00])]
message = mbim_message_response.parse_response_packets(packets)
self.assertEqual(True, isinstance(message,
mbim_message_response.MBIMFunctionError))
self.assertEqual(message.message_type,
mbim_constants.MBIM_FUNCTION_ERROR_MSG)
self.assertEqual(message.message_length, 16)
self.assertEqual(message.transaction_id, 1)
self.assertEqual(message.error_status_code,
mbim_constants.MBIM_ERROR_UNKNOWN)
def test_parse_mbim_command_done(self):
"""
Verifies the packets of |MBIM_COMMAND_DONE| type are parsed correctly.
This tests both the fragmentation reassembly and message parsing
functionality.
"""
packets = [array.array('B', [0x03, 0x00, 0x00, 0x80, 0x34, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
0x00, 0x06, 0xEE, 0x00, 0x00, 0x00, 0x00,
0x80, 0x40, 0x20, 0x10, 0x00, 0xAA, 0xBB,
0xCC, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01,
0x01, 0x01, 0x01]),
array.array('B', [0x03, 0x00, 0x00, 0x80, 0x18, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
0x01, 0x01, 0x01])]
message = mbim_message_response.parse_response_packets(packets)
is_instance = isinstance(message,
mbim_message_response.MBIMCommandDone)
self.assertEqual(is_instance, True)
self.assertEqual(message.message_type, mbim_constants.MBIM_COMMAND_DONE)
self.assertEqual(message.message_length, 56)
self.assertEqual(message.transaction_id, 1)
self.assertEqual(message.total_fragments, 2)
self.assertEqual(message.current_fragment, 0)
self.assertEqual(message.device_service_id,
'\x02\x00\x06\xEE\x00\x00\x00\x00\x80\x40\x20\x10'
'\x00\xAA\xBB\xCC')
self.assertEqual(message.cid, 1)
self.assertEqual(message.status_codes,
mbim_constants.MBIM_STATUS_SUCCESS)
self.assertEqual(message.information_buffer_length, 8)
self.assertEqual(message.payload_buffer,
array.array('B', [0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01]))
def test_parse_mbim_get_device_caps(self):
"""
Verifies the packets of |MBIM_COMMAND_DONE| type for a GetDeviceCaps
CID query are parsed correctly.
This tests both the fragmentation reassembly and message parsing
functionality.
"""
packets = [array.array('B', [0x03, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA2,
0x89, 0xCC, 0x33, 0xBC, 0xBB, 0x8B, 0x4F,
0xB6, 0xB0, 0x13, 0x3E, 0xC2, 0xAA, 0xE6,
0xDF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xA0, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
0x0]),
array.array('B', [0x03, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1F,
0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
0x00, 0x40, 0x00, 0x00, 0x00, 0x0A, 0x00,
0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x1E,
0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00,
0x1E, 0x00, 0x00, 0x00, 0x8C, 0x00, 0x00,
0x00]),
array.array('B', [0x03, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00,
0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x12,
0x00, 0x00, 0x00, 0x48, 0x00, 0x53, 0x00,
0x50, 0x00, 0x41, 0x00, 0x2B, 0x00, 0x00,
0x00, 0x33, 0x00, 0x35, 0x00, 0x31, 0x00,
0x38, 0x00, 0x35, 0x00, 0x31, 0x00, 0x30,
0x00, 0x36, 0x00, 0x30, 0x00, 0x30, 0x00,
0x30, 0x00, 0x30, 0x00, 0x37, 0x00, 0x38,
0x00]),
array.array('B', [0x03, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00,
0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x34,
0x00, 0x00, 0x00, 0x31, 0x00, 0x31, 0x00,
0x2E, 0x00, 0x33, 0x00, 0x35, 0x00, 0x30,
0x00, 0x2E, 0x00, 0x31, 0x00, 0x36, 0x00,
0x2E, 0x00, 0x30, 0x00, 0x34, 0x00, 0x2E,
0x00, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00,
0x4D, 0x00, 0x4C, 0x00, 0x31, 0x00, 0x4D,
0x0]),
array.array('B', [0x03, 0x00, 0x00, 0x80, 0x20, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00,
0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x45,
0x00, 0x39, 0x00, 0x33, 0x00, 0x36, 0x00,
0x4D, 0x00, 0x00, 0x00])]
message = mbim_message_response.parse_response_packets(packets)
is_instance = isinstance(message,
mbim_command_message.MBIMDeviceCapsInfo)
self.assertEqual(is_instance, True)
self.assertEqual(message.message_type, mbim_constants.MBIM_COMMAND_DONE)
self.assertEqual(message.message_length, 208)
self.assertEqual(message.transaction_id, 1)
self.assertEqual(message.total_fragments, 5)
self.assertEqual(message.current_fragment, 0)
self.assertEqual(message.device_service_id,
'\xA2\x89\xCC3\xBC\xBB\x8BO\xB6\xB0\x13>\xC2\xAA\xE6'
'\xDF')
self.assertEqual(message.cid, 1)
self.assertEqual(message.status_codes,
mbim_constants.MBIM_STATUS_SUCCESS)
self.assertEqual(message.information_buffer_length, 160)
self.assertEqual(message.device_type, 1)
self.assertEqual(message.cellular_class, 1)
self.assertEqual(message.voice_class, 1)
self.assertEqual(message.sim_class, 2)
self.assertEqual(message.data_class, 2147483679)
self.assertEqual(message.sms_caps, 3)
self.assertEqual(message.control_caps, 3)
self.assertEqual(message.max_sessions, 8)
self.assertEqual(message.custom_data_class_offset, 64)
self.assertEqual(message.custom_data_class_size, 10)
self.assertEqual(message.device_id_offset, 76)
self.assertEqual(message.device_id_size, 30)
self.assertEqual(message.firmware_info_offset, 108)
self.assertEqual(message.firmware_info_size, 30)
self.assertEqual(message.hardware_info_offset, 140)
self.assertEqual(message.hardware_info_size, 18)
def test_generate_mbim_open(self):
"""
Verifies the raw packet of |MBIM_OPEN| type is generated correctly.
"""
message = mbim_message_request.MBIMOpen(max_control_transfer=40)
packets = mbim_message_request.generate_request_packets(message, 64)
self.assertEqual(packets, [array.array('B', [0x01, 0x00, 0x00, 0x00,
0x10, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00,
0x28, 0x00, 0x00, 0x00])])
def test_generate_mbim_command_packets(self):
"""
Verifies the raw packets of |MBIM_COMMAND| type are generated correctly.
This verifies the fragmentation logic in the generate_request_packets.
"""
payload_buffer=array.array('B', [0x01, 0x00, 0x00, 0x00, 0x10, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x28, 0x00, 0x00, 0x00, 0x04, 0x05,
0x06, 0x10, 0x87, 0xDE, 0xED, 0xAC,
0x45, 0x35, 0x50, 0x60, 0x90, 0xED,
0xAB])
message = mbim_message_request.MBIMCommand(
device_service_id=mbim_constants.UUID_BASIC_CONNECT.bytes,
cid=mbim_constants.MBIM_CID_DEVICE_CAPS,
command_type=mbim_constants.COMMAND_TYPE_QUERY,
information_buffer_length=len(payload_buffer),
payload_buffer=payload_buffer)
packets = mbim_message_request.generate_request_packets(message, 64)
self.assertEqual(packets, [array.array('B', [0x03, 0x00, 0x00, 0x00,
0x40, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0xA2, 0x89, 0xCC, 0x33,
0xBC, 0xBB, 0x8B, 0x4F,
0xB6, 0xB0, 0x13, 0x3E,
0xC2, 0xAA, 0xE6, 0xDF,
0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x1F, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00,
0x10, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00,
0x28, 0x00, 0x00, 0x00]),
array.array('B', [0x03, 0x00, 0x00, 0x00,
0x23, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00,
0x04, 0x05, 0x06, 0x10,
0x87, 0xDE, 0xED, 0xAC,
0x45, 0x35, 0x50, 0x60,
0x90, 0xED, 0xAB])])
if __name__ == '__main__':
logging.basicConfig(level=logging.DEBUG)
unittest.main()