diff --git a/components/freertos_kernel/CMakeLists.txt b/components/freertos_kernel/CMakeLists.txt index 1474e2db..c18d1292 100644 --- a/components/freertos_kernel/CMakeLists.txt +++ b/components/freertos_kernel/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright 2023-2024 Arm Limited and/or its affiliates +# Copyright 2023-2025 Arm Limited and/or its affiliates # # SPDX-License-Identifier: MIT @@ -29,6 +29,9 @@ else() $<$:${IOT_REFERENCE_ARM_CORSTONE3XX_SOURCE_DIR}/bsp/corstone310/include> $<$:${IOT_REFERENCE_ARM_CORSTONE3XX_SOURCE_DIR}/bsp/corstone315/include> $<$:${IOT_REFERENCE_ARM_CORSTONE3XX_SOURCE_DIR}/bsp/corstone320/include> + ${CMAKE_CURRENT_LIST_DIR}/integration/inc ) add_subdirectory(library) endif() + +add_subdirectory(integration) diff --git a/components/freertos_kernel/integration/CMakeLists.txt b/components/freertos_kernel/integration/CMakeLists.txt new file mode 100644 index 00000000..8fceb14e --- /dev/null +++ b/components/freertos_kernel/integration/CMakeLists.txt @@ -0,0 +1,19 @@ +# Copyright 2025 Arm Limited and/or its affiliates +# +# SPDX-License-Identifier: MIT + +cmake_minimum_required(VERSION 3.21.0 FATAL_ERROR) + +add_library(heap-management + STATIC + src/heap_management.c +) + +target_include_directories(heap-management + PRIVATE + inc +) + +if(BUILD_TESTING AND NOT CMAKE_CROSSCOMPILING) + add_subdirectory(tests) +endif() diff --git a/components/freertos_kernel/integration/inc/heap_management.h b/components/freertos_kernel/integration/inc/heap_management.h new file mode 100644 index 00000000..8b36ba83 --- /dev/null +++ b/components/freertos_kernel/integration/inc/heap_management.h @@ -0,0 +1,67 @@ +/* Copyright 2025 Arm Limited and/or its affiliates + * + * SPDX-License-Identifier: MIT + */ + +/** + * @file heap_management.h + * @brief Thin wrappers around the C library used by the integration code. + */ +#ifndef HEAP_MANAGEMENT_H + #define HEAP_MANAGEMENT_H + + #include + + #ifdef __cplusplus + extern "C" { + #endif + +/** + * @brief Wrapper for the C libraries malloc + * @param xWantedSize The number of bytes to be allocated + * @return Either a non-NULL pointer to a block of memory on success, or NULL on failure + */ + void * pvPortMalloc( size_t xWantedSize ); + +/** + * @brief Wrapper for the C libraries free function + * @param pv Pointer to a memory location to be freed, or NULL + */ + void vPortFree( void * pv ); + +/** + * @brief Wrapper for the C libraries calloc + * @param xNum The number of elements + * @param xSize The size of each element in bytes + * @return Either a non-NULL pointer to a block of memory on success, or NULL on failure + */ + void * pvPortCalloc( size_t xNum, + size_t xSize ); + +/** + * @brief A dummy implementation as C standard library does not provide + * functions to get the statistics of heap memory. + * @return Always 0 in the integration build + * @note These dummy implementation are needed + * as this API is used as part of FreeRTOS Plus TCP code which is unused in the + * FRI code (removed by the linker) but ARMClang linker requires all the compiled + * symbols to be defined. + */ + size_t xPortGetFreeHeapSize( void ); + +/** + * @brief A dummy implementation as C standard library does not provide + * functions to get the statistics of heap memory. + * @return Always 0 in the integration build + * @note These dummy implementation are needed + * as this API is used as part of FreeRTOS Plus TCP code which is unused in the + * FRI code (removed by the linker) but ARMClang linker requires all the compiled + * symbols to be defined. + */ + size_t xPortGetMinimumEverFreeHeapSize( void ); + + #ifdef __cplusplus + } + #endif + +#endif /* HEAP_MANAGEMENT_H */ diff --git a/components/freertos_kernel/integration/src/heap_management.c b/components/freertos_kernel/integration/src/heap_management.c index 7c486701..b9de73d5 100644 --- a/components/freertos_kernel/integration/src/heap_management.c +++ b/components/freertos_kernel/integration/src/heap_management.c @@ -1,8 +1,9 @@ -/* Copyright 2024 Arm Limited and/or its affiliates +/* Copyright 2024-2025 Arm Limited and/or its affiliates * * SPDX-License-Identifier: MIT */ +#include "heap_management.h" #include void * pvPortMalloc( size_t xWantedSize ) @@ -21,11 +22,6 @@ void * pvPortCalloc( size_t xNum, return calloc( xNum, xSize ); } -/* These are dummy implementations as C standard library does not provide - * functions to get the statistics of heap memory. These dummy implementation are needed - * as these APIs are used as part of FreeRTOS Plus TCP code which is unused in the FRI code (removed by the linker) - * but ARMClang linker requires all the compiled symbols to be defined. - */ size_t xPortGetFreeHeapSize( void ) { return 0; diff --git a/components/freertos_kernel/integration/tests/CMakeLists.txt b/components/freertos_kernel/integration/tests/CMakeLists.txt new file mode 100644 index 00000000..f28de75e --- /dev/null +++ b/components/freertos_kernel/integration/tests/CMakeLists.txt @@ -0,0 +1,31 @@ +# Copyright 2025 Arm Limited and/or its affiliates +# +# SPDX-License-Identifier: MIT + +cmake_minimum_required(VERSION 3.21.0 FATAL_ERROR) + +add_executable(heap-management-test + test_heap_management.cpp +) + +target_include_directories(heap-management-test + PRIVATE + ../inc + ../../library_mocks/inc +) + +target_link_libraries(heap-management-test + PRIVATE + fff + freertos-kernel-mock + heap-management +) + +target_link_options(heap-management-test + PRIVATE + -Wl,--wrap=malloc + -Wl,--wrap=calloc + -Wl,--wrap=free +) + +iot_reference_arm_corstone3xx_add_test(heap-management-test) diff --git a/components/freertos_kernel/integration/tests/test_heap_management.cpp b/components/freertos_kernel/integration/tests/test_heap_management.cpp new file mode 100644 index 00000000..abe6d082 --- /dev/null +++ b/components/freertos_kernel/integration/tests/test_heap_management.cpp @@ -0,0 +1,190 @@ +/* Copyright 2025 Arm Limited and/or its affiliates + * + * SPDX-License-Identifier: MIT + */ + +#include "gtest/gtest.h" +#include + +extern "C" { +#include "heap_management.h" +#include "alloc_fakes.h" +} + +class TestHeapManagement : public ::testing::Test { +public: + TestHeapManagement() + { + RESET_FAKE( test_malloc ); + RESET_FAKE( test_calloc ); + RESET_FAKE( test_free ); + } +}; + +TEST_F( TestHeapManagement, calls_malloc_and_returns_same_pointer ) +{ + static uint8_t dummy[ 32 ]; + + test_malloc_fake.return_val = dummy; + void * allocated_ptr = pvPortMalloc( 32 ); + ASSERT_EQ( allocated_ptr, static_cast( dummy ) ); + + EXPECT_EQ( test_malloc_fake.call_count, 1 ); + EXPECT_EQ( test_malloc_fake.arg0_val, static_cast( 32 ) ); +} + +TEST_F( TestHeapManagement, calling_malloc_with_size_zero_may_return_null ) +{ + test_malloc_fake.return_val = nullptr; + void * allocated_ptr = pvPortMalloc( 0 ); + ASSERT_EQ( allocated_ptr, nullptr ); + + EXPECT_EQ( test_malloc_fake.call_count, 1 ); + EXPECT_EQ( test_malloc_fake.arg0_val, static_cast( 0 ) ); +} + +TEST_F( TestHeapManagement, calling_malloc_with_size_zero_may_return_pointer ) +{ + static uint8_t dummy[ 1 ]; + + test_malloc_fake.return_val = dummy; + void * allocated_ptr = pvPortMalloc( 0 ); + ASSERT_EQ( allocated_ptr, static_cast( dummy ) ); + + EXPECT_EQ( test_malloc_fake.call_count, 1 ); + EXPECT_EQ( test_malloc_fake.arg0_val, static_cast( 0 ) ); +} + +TEST_F( TestHeapManagement, returns_null_when_malloc_fails ) +{ + test_malloc_fake.return_val = nullptr; + void * allocated_ptr = pvPortMalloc( 64 ); + ASSERT_EQ( allocated_ptr, nullptr ); + + EXPECT_EQ( test_malloc_fake.call_count, 1 ); + EXPECT_EQ( test_malloc_fake.arg0_val, static_cast( 64 ) ); +} + +TEST_F( TestHeapManagement, calls_free_with_same_pointer ) +{ + static uint8_t dummy[ 16 ]; + void * allocated_ptr = static_cast( dummy ); + + vPortFree( allocated_ptr ); + + EXPECT_EQ( test_free_fake.call_count, 1 ); + EXPECT_EQ( test_free_fake.arg0_val, allocated_ptr ); +} + +TEST_F( TestHeapManagement, calls_free_with_null ) +{ + vPortFree( nullptr ); + EXPECT_EQ( test_free_fake.call_count, 1 ); + EXPECT_EQ( test_free_fake.arg0_val, nullptr ); +} + +TEST_F( TestHeapManagement, calls_calloc_and_returns_same_pointer ) +{ + static uint64_t dummy[ 4 ]; + + test_calloc_fake.return_val = dummy; + void * allocated_ptr = pvPortCalloc( 4, 8 ); + ASSERT_EQ( allocated_ptr, static_cast( dummy ) ); + + EXPECT_EQ( test_calloc_fake.call_count, 1 ); + EXPECT_EQ( test_calloc_fake.arg0_val, 4 ); + EXPECT_EQ( test_calloc_fake.arg1_val, 8 ); +} + +TEST_F( TestHeapManagement, calling_calloc_with_num_zero_may_return_null ) +{ + test_calloc_fake.return_val = nullptr; + void * allocated_ptr = pvPortCalloc( 0, 8 ); + ASSERT_EQ( allocated_ptr, nullptr ); + + EXPECT_EQ( test_calloc_fake.call_count, 1 ); + EXPECT_EQ( test_calloc_fake.arg0_val, 0 ); + EXPECT_EQ( test_calloc_fake.arg1_val, 8 ); +} + +TEST_F( TestHeapManagement, calling_calloc_with_num_zero_may_return_pointer ) +{ + static uint8_t dummy[ 1 ]; + + test_calloc_fake.return_val = dummy; + void * allocated_ptr = pvPortCalloc( 0, 8 ); + ASSERT_EQ( allocated_ptr, static_cast( dummy ) ); + + EXPECT_EQ( test_calloc_fake.call_count, 1 ); + EXPECT_EQ( test_calloc_fake.arg0_val, 0 ); + EXPECT_EQ( test_calloc_fake.arg1_val, 8 ); +} + +TEST_F( TestHeapManagement, calling_calloc_with_size_zero_may_return_null ) +{ + test_calloc_fake.return_val = nullptr; + void * allocated_ptr = pvPortCalloc( 8, 0 ); + ASSERT_EQ( allocated_ptr, nullptr ); + + EXPECT_EQ( test_calloc_fake.call_count, 1 ); + EXPECT_EQ( test_calloc_fake.arg0_val, 8 ); + EXPECT_EQ( test_calloc_fake.arg1_val, 0 ); +} + +TEST_F( TestHeapManagement, calling_calloc_with_size_zero_may_return_pointer ) +{ + static uint8_t dummy[ 1 ]; + + test_calloc_fake.return_val = dummy; + void * allocated_ptr = pvPortCalloc( 8, 0 ); + ASSERT_EQ( allocated_ptr, static_cast( dummy ) ); + + EXPECT_EQ( test_calloc_fake.call_count, 1 ); + EXPECT_EQ( test_calloc_fake.arg0_val, 8 ); + EXPECT_EQ( test_calloc_fake.arg1_val, 0 ); +} + +TEST_F( TestHeapManagement, calling_calloc_zero_zero_may_return_null ) +{ + test_calloc_fake.return_val = nullptr; + void * allocated_ptr = pvPortCalloc( 0, 0 ); + ASSERT_EQ( allocated_ptr, nullptr ); + + EXPECT_EQ( test_calloc_fake.call_count, 1 ); + EXPECT_EQ( test_calloc_fake.arg0_val, 0 ); + EXPECT_EQ( test_calloc_fake.arg1_val, 0 ); +} + +TEST_F( TestHeapManagement, calling_calloc_zero_zero_may_return_pointer ) +{ + static uint8_t dummy[ 1 ]; + + test_calloc_fake.return_val = dummy; + void * allocated_ptr = pvPortCalloc( 0, 0 ); + ASSERT_EQ( allocated_ptr, static_cast( dummy ) ); + + EXPECT_EQ( test_calloc_fake.call_count, 1 ); + EXPECT_EQ( test_calloc_fake.arg0_val, 0 ); + EXPECT_EQ( test_calloc_fake.arg1_val, 0 ); +} + +TEST_F( TestHeapManagement, returns_null_when_calloc_fails ) +{ + test_calloc_fake.return_val = nullptr; + void * allocated_ptr = pvPortCalloc( 16, 8 ); + ASSERT_EQ( allocated_ptr, nullptr ); + + EXPECT_EQ( test_calloc_fake.call_count, 1 ); + EXPECT_EQ( test_calloc_fake.arg0_val, 16 ); + EXPECT_EQ( test_calloc_fake.arg1_val, 8 ); +} + +TEST_F( TestHeapManagement, xPortGetFreeHeapSize_returns_zero ) +{ + ASSERT_EQ( xPortGetFreeHeapSize(), 0 ); +} + +TEST_F( TestHeapManagement, xPortGetMinimumEverFreeHeapSize_returns_zero ) +{ + ASSERT_EQ( xPortGetMinimumEverFreeHeapSize(), 0 ); +} diff --git a/components/freertos_kernel/library_mocks/CMakeLists.txt b/components/freertos_kernel/library_mocks/CMakeLists.txt index ef117e58..281e5646 100644 --- a/components/freertos_kernel/library_mocks/CMakeLists.txt +++ b/components/freertos_kernel/library_mocks/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright 2023-2024 Arm Limited and/or its affiliates +# Copyright 2023-2025 Arm Limited and/or its affiliates # # SPDX-License-Identifier: MIT @@ -7,6 +7,7 @@ add_library(freertos-kernel-mock src/semphr.c src/tasks.c src/queue.c + src/alloc_fakes.c ) target_include_directories(freertos-kernel-mock diff --git a/components/freertos_kernel/library_mocks/inc/alloc_fakes.h b/components/freertos_kernel/library_mocks/inc/alloc_fakes.h new file mode 100644 index 00000000..c315243e --- /dev/null +++ b/components/freertos_kernel/library_mocks/inc/alloc_fakes.h @@ -0,0 +1,16 @@ +/* Copyright 2025 Arm Limited and/or its affiliates + * + * SPDX-License-Identifier: MIT + */ + +#ifndef ALLOC_FAKES_H +#define ALLOC_FAKES_H + +#include +#include "fff.h" + +DECLARE_FAKE_VALUE_FUNC( void *, test_malloc, size_t ); +DECLARE_FAKE_VALUE_FUNC( void *, test_calloc, size_t, size_t ); +DECLARE_FAKE_VOID_FUNC( test_free, void * ); + +#endif /* ALLOC_FAKES_H*/ diff --git a/components/freertos_kernel/library_mocks/src/alloc_fakes.c b/components/freertos_kernel/library_mocks/src/alloc_fakes.c new file mode 100644 index 00000000..eaad592e --- /dev/null +++ b/components/freertos_kernel/library_mocks/src/alloc_fakes.c @@ -0,0 +1,26 @@ +/* Copyright 2025 Arm Limited and/or its affiliates + * + * SPDX-License-Identifier: MIT + */ + +#include "alloc_fakes.h" + +DEFINE_FFF_GLOBALS; + +DEFINE_FAKE_VALUE_FUNC( void *, test_malloc, size_t ); +DEFINE_FAKE_VALUE_FUNC( void *, test_calloc, size_t, size_t ); +DEFINE_FAKE_VOID_FUNC( test_free, void * ); + +void * __wrap_malloc( size_t size ) +{ + return test_malloc( size ); +} +void * __wrap_calloc( size_t num, + size_t size ) +{ + return test_calloc( num, size ); +} +void __wrap_free( void * ptr ) +{ + test_free( ptr ); +} diff --git a/release_changes/202508121214.change.md b/release_changes/202508121214.change.md new file mode 100644 index 00000000..bdd612e9 --- /dev/null +++ b/release_changes/202508121214.change.md @@ -0,0 +1 @@ +unit-test: Add unit tests for heap management module