diff --git a/components/bt/common/ble_log/test_apps/.build-test-rules.yml b/components/bt/common/ble_log/test_apps/.build-test-rules.yml new file mode 100644 index 00000000000..4e7c9339041 --- /dev/null +++ b/components/bt/common/ble_log/test_apps/.build-test-rules.yml @@ -0,0 +1,17 @@ +# Documentation: .gitlab/ci/README.md#manifest-file-to-control-the-buildtest-apps + +components/bt/common/ble_log/test_apps/ble_log_perf_test: + disable: + - if: IDF_TARGET != "none" + temporary: true + reason: No BLE Log test runners are available yet + depends_components: + - bt + +components/bt/common/ble_log/test_apps/ble_log_test: + disable: + - if: IDF_TARGET != "none" + temporary: true + reason: No BLE Log test runners are available yet + depends_components: + - bt diff --git a/components/bt/common/ble_log/test_apps/ble_log_perf_test/.build-test-rules.yml b/components/bt/common/ble_log/test_apps/ble_log_perf_test/.build-test-rules.yml deleted file mode 100644 index 323e59a5d46..00000000000 --- a/components/bt/common/ble_log/test_apps/ble_log_perf_test/.build-test-rules.yml +++ /dev/null @@ -1,9 +0,0 @@ -# Documentation: .gitlab/ci/README.md#manifest-file-to-control-the-buildtest-apps - -components/bt/common/ble_log/test_apps: - disable: - - if: IDF_TARGET != "none" - temporary: true - reason: No BLE Log test runners are available yet - depends_components: - - bt diff --git a/components/bt/common/ble_log/test_apps/ble_log_test/CMakeLists.txt b/components/bt/common/ble_log/test_apps/ble_log_test/CMakeLists.txt new file mode 100644 index 00000000000..01f592eaf39 --- /dev/null +++ b/components/bt/common/ble_log/test_apps/ble_log_test/CMakeLists.txt @@ -0,0 +1,13 @@ +# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: CC0-1.0 + +cmake_minimum_required(VERSION 3.22) + +list(PREPEND SDKCONFIG_DEFAULTS + "$ENV{IDF_PATH}/tools/test_apps/configs/sdkconfig.debug_helpers" + "sdkconfig.defaults") +set(COMPONENTS main) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(ble_log_test) diff --git a/components/bt/common/ble_log/test_apps/ble_log_test/README.md b/components/bt/common/ble_log/test_apps/ble_log_test/README.md new file mode 100644 index 00000000000..316cb36a6ce --- /dev/null +++ b/components/bt/common/ble_log/test_apps/ble_log_test/README.md @@ -0,0 +1,18 @@ +# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: Apache-2.0 + +# BLE Log Functional Test + +| Supported Targets | +| ----------------- | + +This test app verifies the BLE Log runtime behaviour on target, using the +in-memory test peripheral (`CONFIG_BLE_LOG_PRPH_TEST=y`) to capture the +transport stream written by the runtime task hook. + +Currently covered: + +- `BLE_LOG_INT_SRC_VERSION_INFO` frame: BLE Log version, ESP-IDF build commit, + controller lib commit, btdm_common lib commit, BLE Mesh and BLE Audio lib + commits, chip model and chip revision diff --git a/components/bt/common/ble_log/test_apps/ble_log_test/main/CMakeLists.txt b/components/bt/common/ble_log/test_apps/ble_log_test/main/CMakeLists.txt new file mode 100644 index 00000000000..c5ef9f8951f --- /dev/null +++ b/components/bt/common/ble_log/test_apps/ble_log_test/main/CMakeLists.txt @@ -0,0 +1,17 @@ +# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: Apache-2.0 + +idf_component_register( + SRCS "test_ble_log_main.c" "test_ble_log_rt.c" + INCLUDE_DIRS "." + PRIV_REQUIRES unity bt esp_hw_support esp_timer + WHOLE_ARCHIVE +) + +idf_component_get_property(bt_dir bt COMPONENT_DIR) +target_include_directories(${COMPONENT_LIB} PRIVATE + "${bt_dir}/common/ble_log/include" + "${bt_dir}/common/ble_log/src/internal_include" + "${bt_dir}/common/ble_log/src/internal_include/prph" +) diff --git a/components/bt/common/ble_log/test_apps/ble_log_test/main/test_ble_log_main.c b/components/bt/common/ble_log/test_apps/ble_log_test/main/test_ble_log_main.c new file mode 100644 index 00000000000..3437fed856c --- /dev/null +++ b/components/bt/common/ble_log/test_apps/ble_log_test/main/test_ble_log_main.c @@ -0,0 +1,65 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "unity.h" +#include "unity_test_runner.h" + +#include "ble_log.h" +#include "ble_log_lbm.h" +#include "test_ble_log_main.h" + +bool test_ble_log_walk_frames(const uint8_t *data, size_t len, + test_ble_log_frame_observer_t observer, void *ctx) +{ + size_t offset = 0; + while (len - offset >= BLE_LOG_FRAME_OVERHEAD) { + ble_log_frame_head_t head; + memcpy(&head, data + offset, sizeof(head)); + + size_t frame_len = BLE_LOG_FRAME_OVERHEAD + head.length; + if (frame_len > len - offset) { + return false; + } + + uint32_t checksum; + memcpy(&checksum, data + offset + BLE_LOG_FRAME_HEAD_LEN + head.length, + sizeof(checksum)); + if (checksum != ble_log_fast_checksum(data + offset, + BLE_LOG_FRAME_HEAD_LEN + head.length)) { + return false; + } + + if (observer) { + test_ble_log_frame_t frame = { + .src = head.frame_meta & 0xff, + .sn = head.frame_meta >> 8, + .payload = data + offset + BLE_LOG_FRAME_HEAD_LEN, + .payload_len = head.length, + }; + observer(&frame, ctx); + } + offset += frame_len; + } + return offset == len; +} + +void setUp(void) +{ +} + +void tearDown(void) +{ +} + +void app_main(void) +{ + /* The BLE Log module has no automatic system init on this branch; the + * controller normally calls ble_log_init(). Initialize it explicitly. */ + TEST_ASSERT_TRUE_MESSAGE(ble_log_init(), "BLE Log init failed"); + unity_run_menu(); +} diff --git a/components/bt/common/ble_log/test_apps/ble_log_test/main/test_ble_log_main.h b/components/bt/common/ble_log/test_apps/ble_log_test/main/test_ble_log_main.h new file mode 100644 index 00000000000..1f82cf73b33 --- /dev/null +++ b/components/bt/common/ble_log/test_apps/ble_log_test/main/test_ble_log_main.h @@ -0,0 +1,27 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include + +#include "ble_log.h" + +typedef struct { + ble_log_src_t src; + uint32_t sn; + const uint8_t *payload; + size_t payload_len; +} test_ble_log_frame_t; + +typedef void (*test_ble_log_frame_observer_t)(const test_ble_log_frame_t *frame, void *ctx); + +/* Walks a captured transport buffer, validating frame headers and checksums. + * Returns true when the whole buffer consists of valid frames. */ +bool test_ble_log_walk_frames(const uint8_t *data, size_t len, + test_ble_log_frame_observer_t observer, void *ctx); diff --git a/components/bt/common/ble_log/test_apps/ble_log_test/main/test_ble_log_rt.c b/components/bt/common/ble_log/test_apps/ble_log_test/main/test_ble_log_rt.c new file mode 100644 index 00000000000..1ff0c6fd5cb --- /dev/null +++ b/components/bt/common/ble_log/test_apps/ble_log_test/main/test_ble_log_rt.c @@ -0,0 +1,188 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#include "esp_chip_info.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "freertos/task.h" +#include "unity.h" + +#include "ble_log.h" +#include "ble_log_prph_test.h" +#include "ble_log_rt.h" +#include "test_ble_log_main.h" + +#if !CONFIG_BLE_LOG_PRPH_TEST +#error "BLE Log test app requires CONFIG_BLE_LOG_PRPH_TEST" +#endif + +/* The runtime task hook is throttled to one pass per + * BLE_LOG_TS_TRIGGER_TIMEOUT_MS; let the window elapse between write bursts + * so a hook pass is guaranteed to run after the settle delay. */ +#define TEST_HOOK_SETTLE_MS (BLE_LOG_TS_TRIGGER_TIMEOUT_MS + 100) +#define TEST_READ_TIMEOUT_MS (50) +#define TEST_MAX_ROUNDS (3) +#define TEST_WRITES_PER_ROUND (64) +#define TEST_PAYLOAD_LEN (64) +#define TEST_READ_BUF_SIZE (4096) +#define TEST_READER_STACK_SIZE (3072) +#define TEST_READER_PRIO (2) + +typedef struct { + size_t version_info_count; + ble_log_version_info_t version_info; +} version_capture_t; + +typedef struct { + version_capture_t capture; + bool malformed; + volatile bool stop; + SemaphoreHandle_t done; +} reader_ctx_t; + +static uint8_t s_read_buf[TEST_READ_BUF_SIZE]; + +/* A commit field is hex characters, zero-padded after a shorter value; + * anything else (garbage, non-hex, zeros after data) is invalid. */ +static bool commit_is_valid(const uint8_t *commit, size_t len) +{ + bool padding = false; + for (size_t i = 0; i < len; i++) { + if (commit[i] == 0) { + padding = true; + continue; + } + if (padding || + !((commit[i] >= '0' && commit[i] <= '9') || + (commit[i] >= 'a' && commit[i] <= 'f') || + (commit[i] >= 'A' && commit[i] <= 'F'))) { + return false; + } + } + return true; +} + +static bool commit_is_zero(const uint8_t *commit, size_t len) +{ + for (size_t i = 0; i < len; i++) { + if (commit[i] != 0) { + return false; + } + } + return true; +} + +static void capture_version_info_frame(const test_ble_log_frame_t *frame, void *ctx) +{ + version_capture_t *capture = ctx; + /* Every frame payload starts with a 4-byte timestamp prefix */ + const uint8_t *record = frame->payload + sizeof(uint32_t); + size_t record_len = frame->payload_len - sizeof(uint32_t); + + if (frame->src == BLE_LOG_SRC_INTERNAL && + record_len == sizeof(ble_log_version_info_t) && + record[0] == BLE_LOG_INT_SRC_VERSION_INFO) { + memcpy(&capture->version_info, record, sizeof(capture->version_info)); + capture->version_info_count++; + } +} + +/* Consumes pending test transports concurrently with the writer: transports + * are recycled only once read, and the LBM has a small pool of them. */ +static void test_reader_task(void *arg) +{ + reader_ctx_t *ctx = arg; + while (!ctx->stop) { + size_t len = ble_log_prph_test_read(s_read_buf, sizeof(s_read_buf), + pdMS_TO_TICKS(TEST_READ_TIMEOUT_MS), 0); + if (len > 0 && + !test_ble_log_walk_frames(s_read_buf, len, capture_version_info_frame, + &ctx->capture)) { + ctx->malformed = true; + } + } + xSemaphoreGive(ctx->done); + vTaskDelete(NULL); +} + +TEST_CASE("BLE Log runtime hook reports build and chip versions", "[ble_log]") +{ + static const uint8_t payload[TEST_PAYLOAD_LEN] = {0}; + + reader_ctx_t ctx = {0}; + ctx.done = xSemaphoreCreateBinary(); + TEST_ASSERT_NOT_NULL(ctx.done); + + TaskHandle_t reader; + TEST_ASSERT_EQUAL(pdTRUE, xTaskCreate(test_reader_task, "ble_log_rd", + TEST_READER_STACK_SIZE, &ctx, + TEST_READER_PRIO, &reader)); + TEST_ASSERT_TRUE(ble_log_enable(true)); + + /* Transports are auto-submitted once full, which wakes the runtime task; + * after the throttle window elapses, a hook pass writes the version frame + * into the LBM and a later transport carries it out. ble_log_flush() + * cannot be used here: it disables the module while waiting for the + * transports to drain, so the hook frame written during the flush window + * would be dropped. */ + for (int round = 0; round < TEST_MAX_ROUNDS && ctx.capture.version_info_count == 0; round++) { + vTaskDelay(pdMS_TO_TICKS(TEST_HOOK_SETTLE_MS)); + for (int i = 0; i < TEST_WRITES_PER_ROUND; i++) { + ble_log_write_hex(BLE_LOG_SRC_CUSTOM, payload, sizeof(payload)); + } + vTaskDelay(pdMS_TO_TICKS(200)); + } + + ctx.stop = true; + TEST_ASSERT_TRUE(xSemaphoreTake(ctx.done, pdMS_TO_TICKS(1000))); + + TEST_ASSERT_FALSE(ctx.malformed); + TEST_ASSERT_GREATER_OR_EQUAL_size_t(1, ctx.capture.version_info_count); + + const ble_log_version_info_t *vi = &ctx.capture.version_info; + TEST_ASSERT_EQUAL_UINT8(BLE_LOG_INT_SRC_VERSION_INFO, vi->int_src_code); + TEST_ASSERT_EQUAL_UINT8(BLE_LOG_VERSION, vi->version); + TEST_ASSERT_TRUE(commit_is_valid(vi->idf_commit, BLE_LOG_IDF_COMMIT_LEN)); + /* Built from an IDF git checkout; the build commit must be injected */ + TEST_ASSERT_FALSE(commit_is_zero(vi->idf_commit, BLE_LOG_IDF_COMMIT_LEN)); + TEST_ASSERT_TRUE(commit_is_valid(vi->controller_commit, BLE_LOG_LIB_COMMIT_LEN)); + TEST_ASSERT_TRUE(commit_is_valid(vi->btdm_common_commit, BLE_LOG_LIB_COMMIT_LEN)); + TEST_ASSERT_TRUE(commit_is_valid(vi->mesh_commit, BLE_LOG_LIB_COMMIT_LEN)); + TEST_ASSERT_TRUE(commit_is_valid(vi->audio_commit, BLE_LOG_LIB_COMMIT_LEN)); + /* A commit field is non-zero only when the matching lib is linked in */ +#if CONFIG_BT_CONTROLLER_ENABLED && (!CONFIG_BT_DUAL_MODE_ARCH || CONFIG_BT_CTRL_BLE_ENABLE) + TEST_ASSERT_FALSE(commit_is_zero(vi->controller_commit, BLE_LOG_LIB_COMMIT_LEN)); +#else + TEST_ASSERT_TRUE(commit_is_zero(vi->controller_commit, BLE_LOG_LIB_COMMIT_LEN)); +#endif +#if CONFIG_BT_CONTROLLER_ENABLED && CONFIG_BT_DUAL_MODE_ARCH + TEST_ASSERT_FALSE(commit_is_zero(vi->btdm_common_commit, BLE_LOG_LIB_COMMIT_LEN)); +#else + TEST_ASSERT_TRUE(commit_is_zero(vi->btdm_common_commit, BLE_LOG_LIB_COMMIT_LEN)); +#endif +#if CONFIG_BLE_MESH && CONFIG_BLE_MESH_V11_SUPPORT + TEST_ASSERT_FALSE(commit_is_zero(vi->mesh_commit, BLE_LOG_LIB_COMMIT_LEN)); +#else + TEST_ASSERT_TRUE(commit_is_zero(vi->mesh_commit, BLE_LOG_LIB_COMMIT_LEN)); +#endif +#if CONFIG_BT_AUDIO && CONFIG_SOC_BLE_AUDIO_SUPPORTED + TEST_ASSERT_FALSE(commit_is_zero(vi->audio_commit, BLE_LOG_LIB_COMMIT_LEN)); +#else + TEST_ASSERT_TRUE(commit_is_zero(vi->audio_commit, BLE_LOG_LIB_COMMIT_LEN)); +#endif + + esp_chip_info_t chip_info; + esp_chip_info(&chip_info); + TEST_ASSERT_EQUAL_UINT16((uint16_t)chip_info.model, vi->chip_model); + TEST_ASSERT_EQUAL_UINT16(chip_info.revision, vi->chip_revision); + + vSemaphoreDelete(ctx.done); +} diff --git a/components/bt/common/ble_log/test_apps/ble_log_test/sdkconfig.defaults b/components/bt/common/ble_log/test_apps/ble_log_test/sdkconfig.defaults new file mode 100644 index 00000000000..7bbad451380 --- /dev/null +++ b/components/bt/common/ble_log/test_apps/ble_log_test/sdkconfig.defaults @@ -0,0 +1,6 @@ +CONFIG_BT_ENABLED=y +CONFIG_BLE_LOG_ENABLED=y +CONFIG_BLE_LOG_PRPH_TEST=y +CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0=n +CONFIG_UNITY_ENABLE_64BIT=y +CONFIG_BLE_MESH=y