From 9131bdab3a9109823663babf3f79ec62d8b03015 Mon Sep 17 00:00:00 2001 From: Meet Patel Date: Mon, 13 Jul 2026 11:35:19 +0530 Subject: [PATCH 1/2] test(freertos): strengthen IDF additions WithCaps coverage Exercise functional use, OOM failure paths, heap restoration, and SPIRAM placement for WithCaps helpers beyond create-only smoke checks. --- .../freertos/misc/test_idf_additions.c | 133 ++++++++++++++++++ 1 file changed, 133 insertions(+) diff --git a/components/freertos/test_apps/freertos/misc/test_idf_additions.c b/components/freertos/test_apps/freertos/misc/test_idf_additions.c index 0e3e05145fc..e0a4cf1ccc0 100644 --- a/components/freertos/test_apps/freertos/misc/test_idf_additions.c +++ b/components/freertos/test_apps/freertos/misc/test_idf_additions.c @@ -15,6 +15,7 @@ #include "freertos/idf_additions.h" #include "freertos/freertos_debug.h" #include "esp_memory_utils.h" +#include "esp_heap_caps.h" #include "unity.h" #include "test_utils.h" #include @@ -30,6 +31,14 @@ Run only these cases from the Unity menu with the [idf_additions] tag filter. */ #define OBJECT_MEMORY_CAPS (MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT) +#define SPIRAM_OBJECT_MEMORY_CAPS (MALLOC_CAP_SPIRAM|MALLOC_CAP_8BIT) + +/* Sizes large enough to fail allocation on all supported targets */ +#define WITHCAPS_OOM_QUEUE_LEN (0x100000U) +#define WITHCAPS_OOM_QUEUE_ITEM_SIZE (1024U) +#define WITHCAPS_OOM_BUFFER_BYTES (0x40000000U) +#define WITHCAPS_OOM_TASK_STACK_BYTES (0x80000000U) +#define WITHCAPS_HEAP_LEAK_THRESHOLD (256) static void task_with_caps(void *arg) { @@ -99,6 +108,8 @@ TEST_CASE("IDF additions: Task creation with SPIRAM memory caps and self deletio TEST_ASSERT_EQUAL(pdPASS, xTaskCreateWithCaps(task_with_caps_self_delete, "task", 4096, NULL, UNITY_FREERTOS_PRIORITY, &task_handle[i], MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT)); // Get the task's memory TEST_ASSERT_EQUAL(pdTRUE, xTaskGetStaticBuffers(task_handle[i], &puxStackBuffer, &pxTaskBuffer)); + TEST_ASSERT(esp_ptr_external_ram(puxStackBuffer)); + TEST_ASSERT(esp_ptr_in_dram(pxTaskBuffer)); } for (int i = 0; i < TEST_NUM_TASKS; i++) { @@ -253,6 +264,128 @@ TEST_CASE("IDF additions: Event group creation with memory caps", "[freertos][id vEventGroupDeleteWithCaps(evt_group_handle); } +TEST_CASE("IDF additions: WithCaps objects functional smoke test", "[freertos][idf_additions]") +{ + QueueHandle_t queue_handle; + uint32_t send_val = 0xA5A5A5A5; + uint32_t recv_val = 0; + + queue_handle = xQueueCreateWithCaps(4, sizeof(uint32_t), OBJECT_MEMORY_CAPS); + TEST_ASSERT_NOT_NULL(queue_handle); + TEST_ASSERT_EQUAL(pdTRUE, xQueueSend(queue_handle, &send_val, 0)); + TEST_ASSERT_EQUAL(pdTRUE, xQueueReceive(queue_handle, &recv_val, 0)); + TEST_ASSERT_EQUAL(send_val, recv_val); + vQueueDeleteWithCaps(queue_handle); + + SemaphoreHandle_t sem_handle = xSemaphoreCreateBinaryWithCaps(OBJECT_MEMORY_CAPS); + TEST_ASSERT_NOT_NULL(sem_handle); + TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreGive(sem_handle)); + TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(sem_handle, 0)); + vSemaphoreDeleteWithCaps(sem_handle); + + StreamBufferHandle_t stream_buffer_handle; + uint8_t stream_tx[] = {0x01, 0x02, 0x03}; + uint8_t stream_rx[sizeof(stream_tx)] = {0}; + + stream_buffer_handle = xStreamBufferCreateWithCaps(32, 1, OBJECT_MEMORY_CAPS); + TEST_ASSERT_NOT_NULL(stream_buffer_handle); + TEST_ASSERT_EQUAL(sizeof(stream_tx), xStreamBufferSend(stream_buffer_handle, stream_tx, sizeof(stream_tx), 0)); + TEST_ASSERT_EQUAL(sizeof(stream_rx), xStreamBufferReceive(stream_buffer_handle, stream_rx, sizeof(stream_rx), 0)); + TEST_ASSERT_EQUAL_UINT8_ARRAY(stream_tx, stream_rx, sizeof(stream_tx)); + vStreamBufferDeleteWithCaps(stream_buffer_handle); + + MessageBufferHandle_t msg_buffer_handle; + + msg_buffer_handle = xMessageBufferCreateWithCaps(32, OBJECT_MEMORY_CAPS); + TEST_ASSERT_NOT_NULL(msg_buffer_handle); + TEST_ASSERT_EQUAL(sizeof(stream_tx), xMessageBufferSend(msg_buffer_handle, stream_tx, sizeof(stream_tx), 0)); + TEST_ASSERT_EQUAL(sizeof(stream_rx), xMessageBufferReceive(msg_buffer_handle, stream_rx, sizeof(stream_rx), 0)); + TEST_ASSERT_EQUAL_UINT8_ARRAY(stream_tx, stream_rx, sizeof(stream_tx)); + vMessageBufferDeleteWithCaps(msg_buffer_handle); + + EventGroupHandle_t evt_group_handle = xEventGroupCreateWithCaps(OBJECT_MEMORY_CAPS); + TEST_ASSERT_NOT_NULL(evt_group_handle); + TEST_ASSERT_BITS(BIT0, BIT0, xEventGroupSetBits(evt_group_handle, BIT0)); + TEST_ASSERT_BITS(BIT0, BIT0, xEventGroupWaitBits(evt_group_handle, BIT0, pdTRUE, pdFALSE, 0)); + vEventGroupDeleteWithCaps(evt_group_handle); +} + +TEST_CASE("IDF additions: WithCaps creation fails on out of memory", "[freertos][idf_additions]") +{ + TaskHandle_t task_handle = NULL; + + TEST_ASSERT_NULL(xQueueCreateWithCaps(WITHCAPS_OOM_QUEUE_LEN, WITHCAPS_OOM_QUEUE_ITEM_SIZE, OBJECT_MEMORY_CAPS)); + TEST_ASSERT_NULL(xStreamBufferCreateWithCaps(WITHCAPS_OOM_BUFFER_BYTES, 1, OBJECT_MEMORY_CAPS)); + TEST_ASSERT_NULL(xMessageBufferCreateWithCaps(WITHCAPS_OOM_BUFFER_BYTES, OBJECT_MEMORY_CAPS)); + TEST_ASSERT_EQUAL(pdFAIL, xTaskCreatePinnedToCoreWithCaps(task_with_caps, + "oom_task", + WITHCAPS_OOM_TASK_STACK_BYTES, + NULL, + UNITY_FREERTOS_PRIORITY, + &task_handle, + UNITY_FREERTOS_CPU, + OBJECT_MEMORY_CAPS)); + TEST_ASSERT_NULL(task_handle); +} + +TEST_CASE("IDF additions: WithCaps create delete restores heap", "[freertos][idf_additions]") +{ + const size_t before_free = heap_caps_get_free_size(OBJECT_MEMORY_CAPS); + + QueueHandle_t queue_handle = xQueueCreateWithCaps(4, sizeof(uint32_t), OBJECT_MEMORY_CAPS); + TEST_ASSERT_NOT_NULL(queue_handle); + vQueueDeleteWithCaps(queue_handle); + + SemaphoreHandle_t sem_handle = xSemaphoreCreateMutexWithCaps(OBJECT_MEMORY_CAPS); + TEST_ASSERT_NOT_NULL(sem_handle); + vSemaphoreDeleteWithCaps(sem_handle); + + StreamBufferHandle_t stream_buffer_handle = xStreamBufferCreateWithCaps(16, 1, OBJECT_MEMORY_CAPS); + TEST_ASSERT_NOT_NULL(stream_buffer_handle); + vStreamBufferDeleteWithCaps(stream_buffer_handle); + + MessageBufferHandle_t msg_buffer_handle = xMessageBufferCreateWithCaps(16, OBJECT_MEMORY_CAPS); + TEST_ASSERT_NOT_NULL(msg_buffer_handle); + vMessageBufferDeleteWithCaps(msg_buffer_handle); + + EventGroupHandle_t evt_group_handle = xEventGroupCreateWithCaps(OBJECT_MEMORY_CAPS); + TEST_ASSERT_NOT_NULL(evt_group_handle); + vEventGroupDeleteWithCaps(evt_group_handle); + + vTaskDelay(10); + const size_t after_free = heap_caps_get_free_size(OBJECT_MEMORY_CAPS); + TEST_ASSERT_INT_WITHIN(WITHCAPS_HEAP_LEAK_THRESHOLD, before_free, after_free); +} + +#if CONFIG_SPIRAM + +TEST_CASE("IDF additions: WithCaps objects allocate in SPIRAM when requested", "[freertos][idf_additions]") +{ + QueueHandle_t queue_handle; + uint8_t *queue_storage; + StaticQueue_t *queue_obj; + + queue_handle = xQueueCreateWithCaps(4, sizeof(uint32_t), SPIRAM_OBJECT_MEMORY_CAPS); + TEST_ASSERT_NOT_NULL(queue_handle); + TEST_ASSERT_EQUAL(pdTRUE, xQueueGetStaticBuffers(queue_handle, &queue_storage, &queue_obj)); + TEST_ASSERT(esp_ptr_external_ram(queue_storage)); + TEST_ASSERT(esp_ptr_external_ram(queue_obj)); + vQueueDeleteWithCaps(queue_handle); + + StreamBufferHandle_t stream_buffer_handle; + uint8_t *stream_buffer_storage; + StaticStreamBuffer_t *stream_buffer_obj; + + stream_buffer_handle = xStreamBufferCreateWithCaps(16, 1, SPIRAM_OBJECT_MEMORY_CAPS); + TEST_ASSERT_NOT_NULL(stream_buffer_handle); + TEST_ASSERT_EQUAL(pdTRUE, xStreamBufferGetStaticBuffers(stream_buffer_handle, &stream_buffer_storage, &stream_buffer_obj)); + TEST_ASSERT(esp_ptr_external_ram(stream_buffer_storage)); + TEST_ASSERT(esp_ptr_external_ram(stream_buffer_obj)); + vStreamBufferDeleteWithCaps(stream_buffer_handle); +} + +#endif /* CONFIG_SPIRAM */ + /* --------------------------------------------------------------------------------------------------------------------- * Task utility API tests (xTaskGetCoreID, xTaskGetStackStart, per-core handle helpers, etc.) * --------------------------------------------------------------------------------------------------------------------- */ From 6062cfd0a14e75decab9bdab6dd9259c4e60217d Mon Sep 17 00:00:00 2001 From: Meet Patel Date: Thu, 16 Jul 2026 10:39:25 +0530 Subject: [PATCH 2/2] test(freertos): harden WithCaps OOM sizing and leak checks Derive OOM sizes from heap totals to avoid size_t overflow, and rely on Unity setUp/tearDown for create/delete leak detection. --- .../freertos/misc/test_idf_additions.c | 30 ++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/components/freertos/test_apps/freertos/misc/test_idf_additions.c b/components/freertos/test_apps/freertos/misc/test_idf_additions.c index e0a4cf1ccc0..ea9fb734d89 100644 --- a/components/freertos/test_apps/freertos/misc/test_idf_additions.c +++ b/components/freertos/test_apps/freertos/misc/test_idf_additions.c @@ -33,13 +33,6 @@ Run only these cases from the Unity menu with the [idf_additions] tag filter. #define OBJECT_MEMORY_CAPS (MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT) #define SPIRAM_OBJECT_MEMORY_CAPS (MALLOC_CAP_SPIRAM|MALLOC_CAP_8BIT) -/* Sizes large enough to fail allocation on all supported targets */ -#define WITHCAPS_OOM_QUEUE_LEN (0x100000U) -#define WITHCAPS_OOM_QUEUE_ITEM_SIZE (1024U) -#define WITHCAPS_OOM_BUFFER_BYTES (0x40000000U) -#define WITHCAPS_OOM_TASK_STACK_BYTES (0x80000000U) -#define WITHCAPS_HEAP_LEAK_THRESHOLD (256) - static void task_with_caps(void *arg) { xTaskNotifyGive((TaskHandle_t)arg); @@ -312,14 +305,21 @@ TEST_CASE("IDF additions: WithCaps objects functional smoke test", "[freertos][i TEST_CASE("IDF additions: WithCaps creation fails on out of memory", "[freertos][idf_additions]") { + /* Size just past the total heap for these caps so allocation must fail on every target, + * without fixed constants that can overflow size_t (e.g. stack_words * sizeof(StackType_t)). */ + const size_t total_caps = heap_caps_get_total_size(OBJECT_MEMORY_CAPS); + const size_t oom_bytes = total_caps + 1024; + const UBaseType_t oom_queue_len = (UBaseType_t)((oom_bytes / sizeof(uint32_t)) + 1); + const configSTACK_DEPTH_TYPE oom_stack_words = + (configSTACK_DEPTH_TYPE)((oom_bytes / sizeof(StackType_t)) + 1); TaskHandle_t task_handle = NULL; - TEST_ASSERT_NULL(xQueueCreateWithCaps(WITHCAPS_OOM_QUEUE_LEN, WITHCAPS_OOM_QUEUE_ITEM_SIZE, OBJECT_MEMORY_CAPS)); - TEST_ASSERT_NULL(xStreamBufferCreateWithCaps(WITHCAPS_OOM_BUFFER_BYTES, 1, OBJECT_MEMORY_CAPS)); - TEST_ASSERT_NULL(xMessageBufferCreateWithCaps(WITHCAPS_OOM_BUFFER_BYTES, OBJECT_MEMORY_CAPS)); + TEST_ASSERT_NULL(xQueueCreateWithCaps(oom_queue_len, sizeof(uint32_t), OBJECT_MEMORY_CAPS)); + TEST_ASSERT_NULL(xStreamBufferCreateWithCaps(oom_bytes, 1, OBJECT_MEMORY_CAPS)); + TEST_ASSERT_NULL(xMessageBufferCreateWithCaps(oom_bytes, OBJECT_MEMORY_CAPS)); TEST_ASSERT_EQUAL(pdFAIL, xTaskCreatePinnedToCoreWithCaps(task_with_caps, "oom_task", - WITHCAPS_OOM_TASK_STACK_BYTES, + oom_stack_words, NULL, UNITY_FREERTOS_PRIORITY, &task_handle, @@ -328,10 +328,9 @@ TEST_CASE("IDF additions: WithCaps creation fails on out of memory", "[freertos] TEST_ASSERT_NULL(task_handle); } +/* Create/delete coverage for remaining WithCaps types; Unity setUp/tearDown leak check verifies heap restore. */ TEST_CASE("IDF additions: WithCaps create delete restores heap", "[freertos][idf_additions]") { - const size_t before_free = heap_caps_get_free_size(OBJECT_MEMORY_CAPS); - QueueHandle_t queue_handle = xQueueCreateWithCaps(4, sizeof(uint32_t), OBJECT_MEMORY_CAPS); TEST_ASSERT_NOT_NULL(queue_handle); vQueueDeleteWithCaps(queue_handle); @@ -351,16 +350,13 @@ TEST_CASE("IDF additions: WithCaps create delete restores heap", "[freertos][idf EventGroupHandle_t evt_group_handle = xEventGroupCreateWithCaps(OBJECT_MEMORY_CAPS); TEST_ASSERT_NOT_NULL(evt_group_handle); vEventGroupDeleteWithCaps(evt_group_handle); - - vTaskDelay(10); - const size_t after_free = heap_caps_get_free_size(OBJECT_MEMORY_CAPS); - TEST_ASSERT_INT_WITHIN(WITHCAPS_HEAP_LEAK_THRESHOLD, before_free, after_free); } #if CONFIG_SPIRAM TEST_CASE("IDF additions: WithCaps objects allocate in SPIRAM when requested", "[freertos][idf_additions]") { + /* Queue/stream Static* objects honor SPIRAM caps; task TCBs remain internal via pvPortMalloc. */ QueueHandle_t queue_handle; uint8_t *queue_storage; StaticQueue_t *queue_obj;