diff --git a/test/Makefile b/test/Makefile index b4f366d91..e0b55d420 100644 --- a/test/Makefile +++ b/test/Makefile @@ -22,6 +22,7 @@ SUBDIRS = \ gatt_service \ hfp \ hid_parser \ + l2cap-le \ le_device_db_tlv \ linked_list \ map_test \ @@ -58,6 +59,7 @@ SUBDIRS_BLE = \ gatt_server \ gatt_service \ hid_parser \ + l2cap-le \ le_device_db_tlv \ linked_list \ ring_buffer \ diff --git a/test/l2cap-le/CMakeLists.txt b/test/l2cap-le/CMakeLists.txt new file mode 100644 index 000000000..22d5653dd --- /dev/null +++ b/test/l2cap-le/CMakeLists.txt @@ -0,0 +1,52 @@ +cmake_minimum_required (VERSION 3.5) +project(gatt-client-test) + +# add CppUTest +include_directories("/usr/local/include") +link_directories("/usr/local/lib") +link_libraries( CppUTest ) +link_libraries( CppUTestExt ) + +# set include paths +include_directories(.) +include_directories(../../src) +include_directories(../mock) +include_directories(../../platform/embedded) +include_directories(../../platform/posix) +include_directories( ${CMAKE_CURRENT_BINARY_DIR}) + +add_compile_options(--coverage) +add_link_options( --coverage) +add_definitions( -DCOVERAGE) +add_definitions( -DHAVE_ASSERT) + +# common files +set(SOURCES + ../../src/btstack_linked_list.c + ../../src/btstack_util.c + ../../src/hci.c + ../../src/hci_cmd.c + ../../src/ad_parser.c + ../../src/l2cap.c + ../../src/l2cap_signaling.c + ../../src/btstack_memory.c + ../../src/btstack_run_loop.c + ../../src/hci_dump.c + ../../platform/posix/hci_dump_posix_stdout.c + ../../platform/embedded/btstack_run_loop_embedded.c +) + +# create static lib +add_library(btstack STATIC ${SOURCES}) + +# create targets +file(GLOB TEST_FILES_CPP "*_test.cpp") +foreach(TEST_FILE ${TEST_FILES_CPP}) + # Use C++ Compiler + # set_source_files_properties(${TEST_FILE} PROPERTIES LANGUAGE CXX ) + set (SOURCE_FILES ${TEST_FILE}) + get_filename_component(TEST_NAME ${TEST_FILE} NAME_WE) + message("- " ${TEST_NAME}) + add_executable(${TEST_NAME} ${SOURCE_FILES} ) + target_link_libraries(${TEST_NAME} btstack) +endforeach(TEST_FILE) diff --git a/test/l2cap-le/Makefile b/test/l2cap-le/Makefile new file mode 100644 index 000000000..26ceb6285 --- /dev/null +++ b/test/l2cap-le/Makefile @@ -0,0 +1,78 @@ +CC = gcc +CXX = g++ + +# Requirements: cpputest.github.io + +BTSTACK_ROOT = ../.. + +CFLAGS = -DUNIT_TEST -g -Wall -Wnarrowing -Wconversion-null -Ibuild-coverage -I./ +CFLAGS += -I${BTSTACK_ROOT}/src +CFLAGS += -I${BTSTACK_ROOT}/src/ble +CFLAGS += -I${BTSTACK_ROOT}/platform/posix +CFLAGS += -I${BTSTACK_ROOT}/platform/embedded +# CFLAGS += -D ENABLE_TESTING_SUPPORT + +VPATH += ${BTSTACK_ROOT}/src +VPATH += ${BTSTACK_ROOT}/src/ble +VPATH += ${BTSTACK_ROOT}/platform/embedded +VPATH += ${BTSTACK_ROOT}/platform/posix + +COMMON = \ + btstack_linked_list.c \ + btstack_util.c \ + hci.c \ + hci_cmd.c \ + ad_parser.c \ + l2cap.c \ + l2cap_signaling.c \ + btstack_memory.c \ + btstack_run_loop.c \ + btstack_run_loop_embedded.c \ + hci_dump.c \ + hci_dump_posix_stdout.c \ + +CFLAGS_COVERAGE = ${CFLAGS} -fprofile-arcs -ftest-coverage +CFLAGS_ASAN = ${CFLAGS} -fsanitize=address -DHAVE_ASSERT + +LDFLAGS += -lCppUTest -lCppUTestExt +LDFLAGS_COVERAGE = ${LDFLAGS} -fprofile-arcs -ftest-coverage +LDFLAGS_ASAN = ${LDFLAGS} -fsanitize=address + +COMMON_OBJ_COVERAGE = $(addprefix build-coverage/,$(COMMON:.c=.o)) +COMMON_OBJ_ASAN = $(addprefix build-asan/, $(COMMON:.c=.o)) + + +all: \ + build-coverage/le_data_channel_test build-asan/le_data_channel_test + +build-%: + mkdir -p $@ + +build-coverage/%.o: %.c | build-coverage + ${CC} -c $(CFLAGS_COVERAGE) $< -o $@ + +build-coverage/%.o: %.cpp | build-coverage + ${CXX} -c $(CFLAGS_COVERAGE) $< -o $@ + +build-asan/%.o: %.c | build-asan + ${CC} -c $(CFLAGS_ASAN) $< -o $@ + +build-asan/%.o: %.cpp | build-asan + ${CXX} -c $(CFLAGS_ASAN) $< -o $@ + +build-coverage/le_data_channel_test: ${COMMON_OBJ_COVERAGE} build-coverage/le_data_channel_test.o | build-coverage + ${CXX} $^ ${LDFLAGS_COVERAGE} -o $@ + +build-asan/le_data_channel_test: ${COMMON_OBJ_ASAN} build-asan/le_data_channel_test.o | build-asan + ${CXX} $^ ${LDFLAGS_ASAN} -o $@ + +test: all + build-asan/le_data_channel_test + +coverage: all + rm -f build-coverage/*.gcda + build-coverage/le_data_channel_test + +clean: + rm -rf build-coverage build-asan + diff --git a/test/l2cap-le/btstack_config.h b/test/l2cap-le/btstack_config.h new file mode 100644 index 000000000..7e7d8e07f --- /dev/null +++ b/test/l2cap-le/btstack_config.h @@ -0,0 +1,31 @@ +// +// btstack_config.h for most tests +// + +#ifndef BTSTACK_CONFIG_H +#define BTSTACK_CONFIG_H + +// Port related features +#define HAVE_BTSTACK_STDIN +#define HAVE_MALLOC +#define HAVE_POSIX_FILE_IO +#define HAVE_POSIX_TIME + +// BTstack features that can be enabled +#define ENABLE_BLE +#define ENABLE_LOG_ERROR +#define ENABLE_LOG_INFO +#define ENABLE_PRINTF_HEXDUMP + +#define ENABLE_LE_CENTRAL +#define ENABLE_LE_PERIPHERAL +#define ENABLE_LE_DATA_CHANNELS + +// for ready-to-use hci channels +#define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + +// BTstack configuration. buffers, sizes, ... +#define HCI_ACL_PAYLOAD_SIZE 52 +#define HCI_INCOMING_PRE_BUFFER_SIZE 4 + +#endif diff --git a/test/l2cap-le/le_data_channel_test.cpp b/test/l2cap-le/le_data_channel_test.cpp new file mode 100644 index 000000000..ff1a7e364 --- /dev/null +++ b/test/l2cap-le/le_data_channel_test.cpp @@ -0,0 +1,280 @@ + +// hal_cpu +#include "hal_cpu.h" +void hal_cpu_disable_irqs(void){} +void hal_cpu_enable_irqs(void){} +void hal_cpu_enable_irqs_and_sleep(void){} + +// mock_sm.c +#include "ble/sm.h" +void sm_add_event_handler(btstack_packet_callback_registration_t * callback_handler){} +void sm_request_pairing(hci_con_handle_t con_handle){} + +// mock_hci_transport.h +#include "hci_transport.h" +void mock_hci_transport_receive_packet(uint8_t packet_type, uint8_t * packet, uint16_t size); +const hci_transport_t * mock_hci_transport_mock_get_instance(void); + +// mock_hci_transport.c +#include +static uint8_t mock_hci_transport_outgoing_packet_buffer[HCI_ACL_PAYLOAD_SIZE]; +static uint16_t mock_hci_transport_outgoing_packet_size; +static uint8_t mock_hci_transport_outgoing_packet_type; + +static void (*mock_hci_transport_packet_handler)(uint8_t packet_type, uint8_t * packet, uint16_t size); +static void mock_hci_transport_register_packet_handler(void (*packet_handler)(uint8_t packet_type, uint8_t * packet, uint16_t size)){ + mock_hci_transport_packet_handler = packet_handler; +} +static int mock_hci_transport_send_packet(uint8_t packet_type, uint8_t *packet, int size){ + mock_hci_transport_outgoing_packet_type = packet_type; + mock_hci_transport_outgoing_packet_size = size; + memcpy(mock_hci_transport_outgoing_packet_buffer, packet, size); + return 0; +} +const hci_transport_t * mock_hci_transport_mock_get_instance(void){ + static hci_transport_t mock_hci_transport = { + /* .transport.name = */ "mock", + /* .transport.init = */ NULL, + /* .transport.open = */ NULL, + /* .transport.close = */ NULL, + /* .transport.register_packet_handler = */ &mock_hci_transport_register_packet_handler, + /* .transport.can_send_packet_now = */ NULL, + /* .transport.send_packet = */ &mock_hci_transport_send_packet, + /* .transport.set_baudrate = */ NULL, + }; + return &mock_hci_transport; +} +void mock_hci_transport_receive_packet(uint8_t packet_type, const uint8_t * packet, uint16_t size){ + (*mock_hci_transport_packet_handler)(packet_type, (uint8_t *) packet, size); +} + +// + +#include +#include +#include +#include + +#include "CppUTest/TestHarness.h" +#include "CppUTest/CommandLineTestRunner.h" +#include "CppUTestExt/MockSupport.h" + +#include "hci_dump.h" +#include "btstack_debug.h" +#include "l2cap.h" +#include "btstack_memory.h" +#include "btstack_run_loop_embedded.h" +#include "hci_dump_posix_stdout.h" +#include "btstack_event.h" + +#define TEST_PACKET_SIZE 100 +#define HCI_CON_HANDLE_TEST 0x0005 +#define TEST_PSM 0x1001 + +static bool l2cap_channel_accept_incoming; +static uint16_t initial_credits = L2CAP_LE_AUTOMATIC_CREDITS; +static uint8_t data_channel_buffer[TEST_PACKET_SIZE]; +static uint16_t l2cap_cid; +static bool l2cap_channel_opened; + +const uint8_t le_data_channel_conn_request_1[] = { + 0x05, 0x20, 0x12, 0x00, 0x0e, 0x00, 0x05, 0x00, 0x14, 0x01, 0x0a, 0x00, 0x01, 0x10, 0x41, 0x00, + 0x64, 0x00, 0x30, 0x00, 0xff, 0xff +}; + +const uint8_t le_data_channel_invalid_pdu[] = { + 0x05, 0x20, 0x12, 0x00, 0x0e, 0x00, 0x05, 0x00, 0x11, 0x01, 0x0a, 0x00, 0x01, 0x10, 0x41, 0x00, + 0x64, 0x00, 0x30, 0x00, 0xff, 0xff +}; + +const uint8_t le_data_channel_conn_response_1[] = { + 0x05, 0x20, 0x12, 0x00, 0x0e, 0x00, 0x05, 0x00, 0x15, 0x01, 0x0a, 0x00, 0x41, 0x00, 0x64, 0x00, + 0x30, 0x00, 0xff, 0xff, 0x00, 0x00 +}; + +const uint8_t le_data_channel_data_1[] = { + 0x05, 0x20, 0x04, 0x00, 0x00, 0x00, 0x41, 0x00 +}; + +static void fix_boundary_flags(uint8_t * packet, uint16_t size){ + uint8_t acl_flags = packet[1] >> 4; + if (acl_flags == 0){ + acl_flags = 2; // first fragment + } + packet[1] = (packet[1] & 0x0f) | (acl_flags << 4); +} + +static void print_acl(const char * name, const uint8_t * packet, uint16_t size){ + printf("const uint8_t %s[] = {", name); + uint16_t i; + for (i=0;i 0); + bool can_send_now = l2cap_le_can_send_now(l2cap_cid); + CHECK(can_send_now); + l2cap_le_provide_credits(l2cap_cid, 1); + l2cap_le_request_can_send_now_event(l2cap_cid); + CHECK(hci_number_free_acl_slots_for_handle(HCI_CON_HANDLE_TEST) > 0); + l2cap_le_send_data(l2cap_cid, (uint8_t *) "hallo", 5); + // CHECK(hci_number_free_acl_slots_for_handle(HCI_CON_HANDLE_TEST) > 0); + // fix_boundary_flags(mock_hci_transport_outgoing_packet_buffer, mock_hci_transport_outgoing_packet_size); + // print_acl("le_data_channel_data_1", mock_hci_transport_outgoing_packet_buffer, mock_hci_transport_outgoing_packet_size); + // simulate data + mock_hci_transport_receive_packet(HCI_ACL_DATA_PACKET, le_data_channel_data_1, sizeof(le_data_channel_data_1)); + l2cap_le_disconnect(l2cap_cid); +} + +TEST(L2CAP_CHANNELS, incoming_1){ + hci_setup_test_connections_fuzz(); + l2cap_le_register_service(&l2cap_channel_packet_handler, TEST_PSM, LEVEL_0); + // simulate conn request + l2cap_channel_accept_incoming = true; + mock_hci_transport_receive_packet(HCI_ACL_DATA_PACKET, le_data_channel_conn_request_1, sizeof(le_data_channel_conn_request_1)); + // fix_boundary_flags(mock_hci_transport_outgoing_packet_buffer, mock_hci_transport_outgoing_packet_size); + // print_acl("le_data_channel_conn_response_1", mock_hci_transport_outgoing_packet_buffer, mock_hci_transport_outgoing_packet_size); + // TODO: verify data + l2cap_le_unregister_service(TEST_PSM); +} + +TEST(L2CAP_CHANNELS, incoming_2){ + hci_setup_test_connections_fuzz(); + l2cap_le_register_service(&l2cap_channel_packet_handler, TEST_PSM, LEVEL_2); + // simulate conn request + l2cap_channel_accept_incoming = true; + mock_hci_transport_receive_packet(HCI_ACL_DATA_PACKET, le_data_channel_conn_request_1, sizeof(le_data_channel_conn_request_1)); +} + +TEST(L2CAP_CHANNELS, incoming_3){ + hci_setup_test_connections_fuzz(); + l2cap_le_register_service(&l2cap_channel_packet_handler, TEST_PSM, LEVEL_0); + // simulate conn request + l2cap_channel_accept_incoming = true; + mock_hci_transport_receive_packet(HCI_ACL_DATA_PACKET, le_data_channel_invalid_pdu, sizeof(le_data_channel_invalid_pdu)); +} + +TEST(L2CAP_CHANNELS, incoming_decline){ + hci_setup_test_connections_fuzz(); + l2cap_le_register_service(&l2cap_channel_packet_handler, TEST_PSM, LEVEL_0); + // simulate conn request + l2cap_channel_accept_incoming = false; + mock_hci_transport_receive_packet(HCI_ACL_DATA_PACKET, le_data_channel_conn_request_1, sizeof(le_data_channel_conn_request_1)); + // fix_boundary_flags(mock_hci_transport_outgoing_packet_buffer, mock_hci_transport_outgoing_packet_size); + // print_acl("le_data_channel_conn_response_1", mock_hci_transport_outgoing_packet_buffer, mock_hci_transport_outgoing_packet_size); + // TODO: verify data +} + +int main (int argc, const char * argv[]){ + return CommandLineTestRunner::RunAllTests(argc, argv); +}