396 lines
13 KiB
C++
396 lines
13 KiB
C++
/******************************************************************************
|
|
*
|
|
* Copyright (C) 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);
|
|
}
|