| /****************************************************************************** |
| * |
| * Copyright 2014 Google, Inc. |
| * |
| * 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. |
| * |
| ******************************************************************************/ |
| |
| #include <gtest/gtest.h> |
| |
| #include "AllocationTestHarness.h" |
| |
| #include <stdint.h> |
| |
| #include "device/include/controller.h" |
| #include "hci_internals.h" |
| #include "osi/include/allocator.h" |
| #include "osi/include/osi.h" |
| #include "packet_fragmenter.h" |
| #include "test_stubs.h" |
| |
| DECLARE_TEST_MODES(init, set_data_sizes, no_fragmentation, fragmentation, |
| ble_no_fragmentation, ble_fragmentation, |
| non_acl_passthrough_fragmentation, no_reassembly, reassembly, |
| non_acl_passthrough_reassembly); |
| |
| #define LOCAL_BLE_CONTROLLER_ID 1 |
| |
| static const char* sample_data = |
| "At this point they came in sight of thirty forty windmills that there are " |
| "on plain, and " |
| "as soon as Don Quixote saw them he said to his squire, \"Fortune is " |
| "arranging matters " |
| "for us better than we could have shaped our desires ourselves, for look " |
| "there, friend " |
| "Sancho Panza, where thirty or more monstrous giants present themselves, " |
| "all of whom I " |
| "mean to engage in battle and slay, and with whose spoils we shall begin " |
| "to make our " |
| "fortunes; for this is righteous warfare, and it is God's good service to " |
| "sweep so evil " |
| "a breed from off the face of the earth.\""; |
| |
| static const char* small_sample_data = "\"What giants?\" said Sancho Panza."; |
| static const uint16_t test_handle_start = (0x1992 & 0xCFFF) | 0x2000; |
| static const uint16_t test_handle_continuation = (0x1992 & 0xCFFF) | 0x1000; |
| static int packet_index; |
| static unsigned int data_size_sum; |
| |
| static const packet_fragmenter_t* fragmenter; |
| |
| static BT_HDR* manufacture_packet_for_fragmentation(uint16_t event, |
| const char* data) { |
| uint16_t data_length = strlen(data); |
| uint16_t size = data_length; |
| if (event == MSG_STACK_TO_HC_HCI_ACL) { |
| size += 4; // 2 for the handle, 2 for the length; |
| } |
| |
| BT_HDR* packet = (BT_HDR*)osi_malloc(size + sizeof(BT_HDR)); |
| packet->len = size; |
| packet->offset = 0; |
| packet->event = event; |
| packet->layer_specific = 0; |
| uint8_t* packet_data = packet->data; |
| |
| if (event == MSG_STACK_TO_HC_HCI_ACL) { |
| UINT16_TO_STREAM(packet_data, test_handle_start); |
| UINT16_TO_STREAM(packet_data, data_length); |
| } |
| |
| memcpy(packet_data, data, data_length); |
| return packet; |
| } |
| |
| static void expect_packet_fragmented(uint16_t event, int max_acl_data_size, |
| BT_HDR* packet, const char* expected_data, |
| bool send_complete) { |
| uint8_t* data = packet->data + packet->offset; |
| int expected_data_offset; |
| int length_to_check; |
| |
| if (event == MSG_STACK_TO_HC_HCI_ACL) { |
| uint16_t handle; |
| uint16_t length; |
| STREAM_TO_UINT16(handle, data); |
| STREAM_TO_UINT16(length, data); |
| |
| if (packet_index == 0) |
| EXPECT_EQ(test_handle_start, handle); |
| else |
| EXPECT_EQ(test_handle_continuation, handle); |
| |
| int length_remaining = strlen(expected_data) - data_size_sum; |
| int packet_data_length = packet->len - HCI_ACL_PREAMBLE_SIZE; |
| EXPECT_EQ(packet_data_length, length); |
| |
| if (length_remaining > max_acl_data_size) |
| EXPECT_EQ(max_acl_data_size, packet_data_length); |
| |
| length_to_check = packet_data_length; |
| expected_data_offset = packet_index * max_acl_data_size; |
| packet_index++; |
| } else { |
| length_to_check = strlen(expected_data); |
| expected_data_offset = 0; |
| } |
| |
| for (int i = 0; i < length_to_check; i++) { |
| EXPECT_EQ(expected_data[expected_data_offset + i], data[i]); |
| data_size_sum++; |
| } |
| |
| if (event == MSG_STACK_TO_HC_HCI_ACL) |
| EXPECT_TRUE(send_complete == (data_size_sum == strlen(expected_data))); |
| |
| if (send_complete) osi_free(packet); |
| } |
| |
| static void manufacture_packet_and_then_reassemble(uint16_t event, |
| uint16_t acl_size, |
| const char* data) { |
| uint16_t data_length = strlen(data); |
| |
| if (event == MSG_HC_TO_STACK_HCI_ACL) { |
| uint16_t total_length = data_length + 2; // 2 for l2cap length; |
| uint16_t length_sent = 0; |
| uint16_t l2cap_length = data_length - 2; // l2cap length field, 2 for the |
| // pretend channel id borrowed |
| // from the data |
| |
| do { |
| int length_to_send = (length_sent + (acl_size - 4) < total_length) |
| ? (acl_size - 4) |
| : (total_length - length_sent); |
| BT_HDR* packet = (BT_HDR*)osi_malloc(length_to_send + 4 + sizeof(BT_HDR)); |
| packet->len = length_to_send + 4; |
| packet->offset = 0; |
| packet->event = event; |
| packet->layer_specific = 0; |
| |
| uint8_t* packet_data = packet->data; |
| if (length_sent == 0) { // first packet |
| UINT16_TO_STREAM(packet_data, test_handle_start); |
| UINT16_TO_STREAM(packet_data, length_to_send); |
| UINT16_TO_STREAM(packet_data, l2cap_length); |
| memcpy(packet_data, data, length_to_send - 2); |
| } else { |
| UINT16_TO_STREAM(packet_data, test_handle_continuation); |
| UINT16_TO_STREAM(packet_data, length_to_send); |
| memcpy(packet_data, data + length_sent - 2, length_to_send); |
| } |
| |
| length_sent += length_to_send; |
| fragmenter->reassemble_and_dispatch(packet); |
| } while (length_sent < total_length); |
| } else { |
| BT_HDR* packet = (BT_HDR*)osi_malloc(data_length + sizeof(BT_HDR)); |
| packet->len = data_length; |
| packet->offset = 0; |
| packet->event = event; |
| packet->layer_specific = 0; |
| memcpy(packet->data, data, data_length); |
| |
| fragmenter->reassemble_and_dispatch(packet); |
| } |
| } |
| |
| static void expect_packet_reassembled(uint16_t event, BT_HDR* packet, |
| const char* expected_data) { |
| uint16_t expected_data_length = strlen(expected_data); |
| uint8_t* data = packet->data + packet->offset; |
| |
| if (event == MSG_HC_TO_STACK_HCI_ACL) { |
| uint16_t handle; |
| uint16_t length; |
| uint16_t l2cap_length; |
| STREAM_TO_UINT16(handle, data); |
| STREAM_TO_UINT16(length, data); |
| STREAM_TO_UINT16(l2cap_length, data); |
| |
| EXPECT_EQ(test_handle_start, handle); |
| EXPECT_EQ(expected_data_length + 2, length); |
| EXPECT_EQ(expected_data_length - 2, |
| l2cap_length); // -2 for the pretend channel id |
| } |
| |
| for (int i = 0; i < expected_data_length; i++) { |
| EXPECT_EQ(expected_data[i], data[i]); |
| data_size_sum++; |
| } |
| |
| osi_free(packet); |
| } |
| |
| STUB_FUNCTION(void, fragmented_callback, (BT_HDR * packet, bool send_complete)) |
| DURING(no_fragmentation) AT_CALL(0) { |
| expect_packet_fragmented(MSG_STACK_TO_HC_HCI_ACL, 42, packet, |
| small_sample_data, send_complete); |
| return; |
| } |
| |
| DURING(fragmentation) { |
| expect_packet_fragmented(MSG_STACK_TO_HC_HCI_ACL, 10, packet, sample_data, |
| send_complete); |
| return; |
| } |
| |
| DURING(ble_no_fragmentation) AT_CALL(0) { |
| expect_packet_fragmented(MSG_STACK_TO_HC_HCI_ACL, 42, packet, |
| small_sample_data, send_complete); |
| return; |
| } |
| |
| DURING(ble_fragmentation) { |
| expect_packet_fragmented(MSG_STACK_TO_HC_HCI_ACL, 10, packet, sample_data, |
| send_complete); |
| return; |
| } |
| |
| DURING(non_acl_passthrough_fragmentation) AT_CALL(0) { |
| expect_packet_fragmented(MSG_STACK_TO_HC_HCI_CMD, 10, packet, sample_data, |
| send_complete); |
| return; |
| } |
| |
| UNEXPECTED_CALL; |
| } |
| |
| STUB_FUNCTION(void, reassembled_callback, (BT_HDR * packet)) |
| DURING(no_reassembly) AT_CALL(0) { |
| expect_packet_reassembled(MSG_HC_TO_STACK_HCI_ACL, packet, small_sample_data); |
| return; |
| } |
| |
| DURING(reassembly) AT_CALL(0) { |
| expect_packet_reassembled(MSG_HC_TO_STACK_HCI_ACL, packet, sample_data); |
| return; |
| } |
| |
| DURING(non_acl_passthrough_reassembly) AT_CALL(0) { |
| expect_packet_reassembled(MSG_HC_TO_STACK_HCI_EVT, packet, sample_data); |
| return; |
| } |
| |
| UNEXPECTED_CALL; |
| } |
| |
| STUB_FUNCTION(void, transmit_finished_callback, |
| (UNUSED_ATTR BT_HDR * packet, |
| UNUSED_ATTR bool sent_all_fragments)) |
| UNEXPECTED_CALL; |
| } |
| |
| STUB_FUNCTION(uint16_t, get_acl_data_size_classic, (void)) |
| DURING(no_fragmentation, non_acl_passthrough_fragmentation, no_reassembly) |
| return 42; |
| DURING(fragmentation) return 10; |
| DURING(no_reassembly) return 1337; |
| |
| UNEXPECTED_CALL; |
| return 0; |
| } |
| |
| STUB_FUNCTION(uint16_t, get_acl_data_size_ble, (void)) |
| DURING(ble_no_fragmentation) return 42; |
| DURING(ble_fragmentation) return 10; |
| |
| UNEXPECTED_CALL; |
| return 0; |
| } |
| |
| static void reset_for(TEST_MODES_T next) { |
| RESET_CALL_COUNT(fragmented_callback); |
| RESET_CALL_COUNT(reassembled_callback); |
| RESET_CALL_COUNT(transmit_finished_callback); |
| RESET_CALL_COUNT(get_acl_data_size_classic); |
| RESET_CALL_COUNT(get_acl_data_size_ble); |
| CURRENT_TEST_MODE = next; |
| } |
| |
| class PacketFragmenterTest : public AllocationTestHarness { |
| protected: |
| virtual void SetUp() { |
| AllocationTestHarness::SetUp(); |
| fragmenter = |
| packet_fragmenter_get_test_interface(&controller, &allocator_malloc); |
| |
| packet_index = 0; |
| data_size_sum = 0; |
| |
| callbacks.fragmented = fragmented_callback; |
| callbacks.reassembled = reassembled_callback; |
| callbacks.transmit_finished = transmit_finished_callback; |
| controller.get_acl_data_size_classic = get_acl_data_size_classic; |
| controller.get_acl_data_size_ble = get_acl_data_size_ble; |
| |
| reset_for(init); |
| fragmenter->init(&callbacks); |
| } |
| |
| virtual void TearDown() { |
| fragmenter->cleanup(); |
| AllocationTestHarness::TearDown(); |
| } |
| |
| controller_t controller; |
| packet_fragmenter_callbacks_t callbacks; |
| }; |
| |
| TEST_F(PacketFragmenterTest, test_no_fragment_necessary) { |
| reset_for(no_fragmentation); |
| BT_HDR* packet = manufacture_packet_for_fragmentation(MSG_STACK_TO_HC_HCI_ACL, |
| small_sample_data); |
| fragmenter->fragment_and_dispatch(packet); |
| |
| EXPECT_EQ(strlen(small_sample_data), data_size_sum); |
| EXPECT_CALL_COUNT(fragmented_callback, 1); |
| } |
| |
| TEST_F(PacketFragmenterTest, test_fragment_necessary) { |
| reset_for(fragmentation); |
| BT_HDR* packet = manufacture_packet_for_fragmentation(MSG_STACK_TO_HC_HCI_ACL, |
| sample_data); |
| fragmenter->fragment_and_dispatch(packet); |
| |
| EXPECT_EQ(strlen(sample_data), data_size_sum); |
| } |
| |
| TEST_F(PacketFragmenterTest, test_ble_no_fragment_necessary) { |
| reset_for(ble_no_fragmentation); |
| BT_HDR* packet = manufacture_packet_for_fragmentation(MSG_STACK_TO_HC_HCI_ACL, |
| small_sample_data); |
| packet->event |= LOCAL_BLE_CONTROLLER_ID; |
| fragmenter->fragment_and_dispatch(packet); |
| |
| EXPECT_EQ(strlen(small_sample_data), data_size_sum); |
| EXPECT_CALL_COUNT(fragmented_callback, 1); |
| } |
| |
| TEST_F(PacketFragmenterTest, test_ble_fragment_necessary) { |
| reset_for(ble_fragmentation); |
| BT_HDR* packet = manufacture_packet_for_fragmentation(MSG_STACK_TO_HC_HCI_ACL, |
| sample_data); |
| packet->event |= LOCAL_BLE_CONTROLLER_ID; |
| fragmenter->fragment_and_dispatch(packet); |
| |
| EXPECT_EQ(strlen(sample_data), data_size_sum); |
| } |
| |
| TEST_F(PacketFragmenterTest, test_non_acl_passthrough_fragmentation) { |
| reset_for(non_acl_passthrough_fragmentation); |
| BT_HDR* packet = manufacture_packet_for_fragmentation(MSG_STACK_TO_HC_HCI_CMD, |
| sample_data); |
| fragmenter->fragment_and_dispatch(packet); |
| |
| EXPECT_EQ(strlen(sample_data), data_size_sum); |
| EXPECT_CALL_COUNT(fragmented_callback, 1); |
| } |
| |
| TEST_F(PacketFragmenterTest, test_no_reassembly_necessary) { |
| reset_for(no_reassembly); |
| manufacture_packet_and_then_reassemble(MSG_HC_TO_STACK_HCI_ACL, 1337, |
| small_sample_data); |
| |
| EXPECT_EQ(strlen(small_sample_data), data_size_sum); |
| EXPECT_CALL_COUNT(reassembled_callback, 1); |
| } |
| |
| TEST_F(PacketFragmenterTest, test_reassembly_necessary) { |
| reset_for(reassembly); |
| manufacture_packet_and_then_reassemble(MSG_HC_TO_STACK_HCI_ACL, 42, |
| sample_data); |
| |
| EXPECT_EQ(strlen(sample_data), data_size_sum); |
| EXPECT_CALL_COUNT(reassembled_callback, 1); |
| } |
| |
| TEST_F(PacketFragmenterTest, test_non_acl_passthrough_reasseembly) { |
| reset_for(non_acl_passthrough_reassembly); |
| manufacture_packet_and_then_reassemble(MSG_HC_TO_STACK_HCI_EVT, 42, |
| sample_data); |
| |
| EXPECT_EQ(strlen(sample_data), data_size_sum); |
| EXPECT_CALL_COUNT(reassembled_callback, 1); |
| } |