diff --git a/components/cxx/test_apps/exception/CMakeLists.txt b/components/cxx/test_apps/exception/CMakeLists.txt index 236f0449e6..b29acb36de 100644 --- a/components/cxx/test_apps/exception/CMakeLists.txt +++ b/components/cxx/test_apps/exception/CMakeLists.txt @@ -1,7 +1,6 @@ # This is the project CMakeLists.txt file for the test subproject cmake_minimum_required(VERSION 3.5) -set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components") set(COMPONENTS main) include($ENV{IDF_PATH}/tools/cmake/project.cmake) diff --git a/components/cxx/test_apps/exception/main/CMakeLists.txt b/components/cxx/test_apps/exception/main/CMakeLists.txt index dd12b0365f..526e9b5439 100644 --- a/components/cxx/test_apps/exception/main/CMakeLists.txt +++ b/components/cxx/test_apps/exception/main/CMakeLists.txt @@ -1,3 +1,2 @@ idf_component_register(SRCS "test_exception.cpp" - REQUIRES test_utils PRIV_REQUIRES unity driver) diff --git a/components/cxx/test_apps/exception/main/test_exception.cpp b/components/cxx/test_apps/exception/main/test_exception.cpp index 69f7102f07..ee77e0afcb 100644 --- a/components/cxx/test_apps/exception/main/test_exception.cpp +++ b/components/cxx/test_apps/exception/main/test_exception.cpp @@ -5,7 +5,7 @@ */ #include #include "unity.h" -#include "memory_checks.h" +#include "unity_test_utils.h" /* Note: When first exception (in system) is thrown this test produces memory leaks report (~300 bytes): - 8 bytes are allocated by __cxa_get_globals() to keep __cxa_eh_globals @@ -25,12 +25,12 @@ extern "C" void setUp() { - test_utils_record_free_mem(); + unity_utils_record_free_mem(); } extern "C" void tearDown() { - test_utils_finish_and_evaluate_leaks(LEAKS, LEAKS); + unity_utils_evaluate_leaks_direct(LEAKS); } TEST_CASE("c++ exceptions work", "[cxx] [exceptions]") diff --git a/components/cxx/test_apps/exception_no_except/CMakeLists.txt b/components/cxx/test_apps/exception_no_except/CMakeLists.txt index 236f0449e6..b29acb36de 100644 --- a/components/cxx/test_apps/exception_no_except/CMakeLists.txt +++ b/components/cxx/test_apps/exception_no_except/CMakeLists.txt @@ -1,7 +1,6 @@ # This is the project CMakeLists.txt file for the test subproject cmake_minimum_required(VERSION 3.5) -set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components") set(COMPONENTS main) include($ENV{IDF_PATH}/tools/cmake/project.cmake) diff --git a/components/cxx/test_apps/exception_no_except/main/CMakeLists.txt b/components/cxx/test_apps/exception_no_except/main/CMakeLists.txt index aa767eafcd..fd5da60266 100644 --- a/components/cxx/test_apps/exception_no_except/main/CMakeLists.txt +++ b/components/cxx/test_apps/exception_no_except/main/CMakeLists.txt @@ -1,3 +1,2 @@ idf_component_register(SRCS "test_exception_no_except.cpp" - REQUIRES test_utils PRIV_REQUIRES unity driver) diff --git a/components/cxx/test_apps/exception_no_except/main/test_exception_no_except.cpp b/components/cxx/test_apps/exception_no_except/main/test_exception_no_except.cpp index a3942613e6..e89e685455 100644 --- a/components/cxx/test_apps/exception_no_except/main/test_exception_no_except.cpp +++ b/components/cxx/test_apps/exception_no_except/main/test_exception_no_except.cpp @@ -7,7 +7,6 @@ #include #include #include "unity.h" -#include "memory_checks.h" extern "C" void setUp() { diff --git a/components/cxx/test_apps/general/CMakeLists.txt b/components/cxx/test_apps/general/CMakeLists.txt index d37fad99f0..5d1506c5e8 100644 --- a/components/cxx/test_apps/general/CMakeLists.txt +++ b/components/cxx/test_apps/general/CMakeLists.txt @@ -1,7 +1,6 @@ # This is the project CMakeLists.txt file for the test subproject cmake_minimum_required(VERSION 3.5) -set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components") set(COMPONENTS main) include($ENV{IDF_PATH}/tools/cmake/project.cmake) diff --git a/components/cxx/test_apps/general/main/CMakeLists.txt b/components/cxx/test_apps/general/main/CMakeLists.txt index 271a3dd092..d66ec8e1b3 100644 --- a/components/cxx/test_apps/general/main/CMakeLists.txt +++ b/components/cxx/test_apps/general/main/CMakeLists.txt @@ -1,2 +1,2 @@ idf_component_register(SRCS "test_cxx_general.cpp" - PRIV_REQUIRES unity test_utils driver) + PRIV_REQUIRES unity driver) diff --git a/components/cxx/test_apps/general/main/test_cxx_general.cpp b/components/cxx/test_apps/general/main/test_cxx_general.cpp index 006ea56a46..6fb8f8fb13 100644 --- a/components/cxx/test_apps/general/main/test_cxx_general.cpp +++ b/components/cxx/test_apps/general/main/test_cxx_general.cpp @@ -11,18 +11,17 @@ #include "freertos/semphr.h" #include "esp_log.h" #include "unity.h" -#include "memory_checks.h" +#include "unity_test_utils.h" extern "C" void setUp() { - test_utils_set_leak_level(0, ESP_LEAK_TYPE_CRITICAL, ESP_COMP_LEAK_GENERAL); - test_utils_record_free_mem(); + unity_utils_set_leak_level(0); + unity_utils_record_free_mem(); } extern "C" void tearDown() { - size_t leak_level = test_utils_get_leak_level(ESP_LEAK_TYPE_CRITICAL, ESP_COMP_LEAK_GENERAL); - test_utils_finish_and_evaluate_leaks(leak_level, leak_level); + unity_utils_evaluate_leaks(); } static const char* TAG = "cxx"; @@ -45,7 +44,7 @@ static int non_pod_test_helper(int new_val) // Will fail if run twice TEST_CASE("can use static initializers for non-POD types", "[restart_init]") { - test_utils_set_leak_level(300, ESP_LEAK_TYPE_CRITICAL, ESP_COMP_LEAK_GENERAL); + unity_utils_set_leak_level(300); TEST_ASSERT_EQUAL(42, non_pod_test_helper(1)); TEST_ASSERT_EQUAL(1, non_pod_test_helper(0)); } @@ -99,7 +98,7 @@ static int start_slow_init_task(int id, int affinity) TEST_CASE("static initialization guards work as expected", "[misc]") { - test_utils_set_leak_level(300, ESP_LEAK_TYPE_CRITICAL, ESP_COMP_LEAK_GENERAL); + unity_utils_set_leak_level(300); s_slow_init_sem = xSemaphoreCreateCounting(10, 0); TEST_ASSERT_NOT_NULL(s_slow_init_sem); int task_count = 0; diff --git a/components/cxx/test_apps/rtti/CMakeLists.txt b/components/cxx/test_apps/rtti/CMakeLists.txt index f8b0144caf..289b1c9e21 100644 --- a/components/cxx/test_apps/rtti/CMakeLists.txt +++ b/components/cxx/test_apps/rtti/CMakeLists.txt @@ -1,7 +1,6 @@ # This is the project CMakeLists.txt file for the test subproject cmake_minimum_required(VERSION 3.5) -set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components") set(COMPONENTS main) include($ENV{IDF_PATH}/tools/cmake/project.cmake) diff --git a/components/cxx/test_apps/rtti/main/CMakeLists.txt b/components/cxx/test_apps/rtti/main/CMakeLists.txt index 74d9dc9108..fef050f7a7 100644 --- a/components/cxx/test_apps/rtti/main/CMakeLists.txt +++ b/components/cxx/test_apps/rtti/main/CMakeLists.txt @@ -1,3 +1,2 @@ idf_component_register(SRCS "test_rtti.cpp" - REQUIRES test_utils PRIV_REQUIRES unity driver) diff --git a/components/cxx/test_apps/rtti/main/test_rtti.cpp b/components/cxx/test_apps/rtti/main/test_rtti.cpp index 1490b8dc6c..06ae473376 100644 --- a/components/cxx/test_apps/rtti/main/test_rtti.cpp +++ b/components/cxx/test_apps/rtti/main/test_rtti.cpp @@ -6,7 +6,7 @@ #include #include #include "unity.h" -#include "memory_checks.h" +#include "unity_test_utils.h" /* Note: When first exception (in system) is thrown this test produces memory leaks report (~300 bytes): - 8 bytes are allocated by __cxa_get_globals() to keep __cxa_eh_globals @@ -26,14 +26,13 @@ extern "C" void setUp() { - test_utils_set_leak_level(0, ESP_LEAK_TYPE_CRITICAL, ESP_COMP_LEAK_GENERAL); - test_utils_record_free_mem(); + unity_utils_set_leak_level(0); + unity_utils_record_free_mem(); } extern "C" void tearDown() { - size_t leak_level = test_utils_get_leak_level(ESP_LEAK_TYPE_CRITICAL, ESP_COMP_LEAK_GENERAL); - test_utils_finish_and_evaluate_leaks(leak_level, leak_level); + unity_utils_evaluate_leaks(); } using namespace std; @@ -97,7 +96,7 @@ TEST_CASE("typeid of function works", "[cxx]") TEST_CASE("unsuccessful dynamic cast on reference throws exception", "[cxx]") { - test_utils_set_leak_level(LEAKS, ESP_LEAK_TYPE_CRITICAL, ESP_COMP_LEAK_GENERAL); + unity_utils_set_leak_level(LEAKS); bool thrown = false; DerivedA derived_a; Base &base = derived_a; diff --git a/components/unity/CMakeLists.txt b/components/unity/CMakeLists.txt index 14a33632cd..bc8818b32e 100644 --- a/components/unity/CMakeLists.txt +++ b/components/unity/CMakeLists.txt @@ -15,7 +15,10 @@ endif() if(CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER) list(APPEND srcs "unity_runner.c") + # Note the following files are not compatible with the Linux target. + # On Linux, these are masked because we also don't use the IDF test runner there list(APPEND srcs "unity_utils_freertos.c") + list(APPEND requires "freertos") endif() if(CONFIG_UNITY_ENABLE_FIXTURE) @@ -23,8 +26,13 @@ if(CONFIG_UNITY_ENABLE_FIXTURE) list(APPEND includes "unity/extras/fixture/src") endif() +list(APPEND srcs "unity_utils_memory.c") + if(NOT "${target}" STREQUAL "linux") list(APPEND srcs "unity_port_esp32.c") + list(APPEND srcs "port/esp/unity_utils_memory_esp.c") +else() + list(APPEND srcs "port/linux/unity_utils_memory_linux.c") endif() idf_component_register(SRCS "${srcs}" diff --git a/components/unity/include/unity_test_utils.h b/components/unity/include/unity_test_utils.h index f508ecaa5f..c9f38613e7 100644 --- a/components/unity/include/unity_test_utils.h +++ b/components/unity/include/unity_test_utils.h @@ -9,6 +9,7 @@ #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" +#include "unity_test_utils_memory.h" #ifdef __cplusplus extern "C" { diff --git a/components/unity/include/unity_test_utils_memory.h b/components/unity/include/unity_test_utils_memory.h new file mode 100644 index 0000000000..5327523d28 --- /dev/null +++ b/components/unity/include/unity_test_utils_memory.h @@ -0,0 +1,80 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Adjust the allowed memory leak thresholds for unit tests. + * + * Usually, unit tests will check if memory is leaked. Some functionality used by unit tests may unavoidably + * leak memory. This function allows to adjust that memory leak threshold. + * + * @param leak_level Maximum allowed memory leak which will not trigger a unit test failure. + */ +void unity_utils_set_leak_level(size_t leak_level); + +/** + * @brief Start/Restart memory leak checking. + * + * Records the current free memory values at time of calling. After the test case, it may be checked with + * \c unity_utils_finish_and_evaluate_leaks. + * + * If this function is called repeatedly, only the free memory values at the last time of calling will prevail + * as reference. + */ +void unity_utils_record_free_mem(void); + +/** + * @brief Calculate leaks and check they are below against a threshold + * + * This function is for internal use, users shouldn't have a reason to call this. + * + * Calculates the leak from \c before_free and \c after_free and checks that the difference does not exceed + * \c threshold. It uses a unity assert to to the check and report in case of failure. + * A summary of the leaked data will be printed in all cases. + */ +void unity_utils_check_leak(unsigned int before_free, + unsigned int after_free, + const char *type, + unsigned int threshold); + +/** + * @brief Evaluate memory leak checking according to the provided thresholds. + * + * If the current memory leak level (counted from the last time calling \c unity_utils_record_free_mem() ) exceeds + * \c threshold, a unit test failure will be triggered. + */ +void unity_utils_evaluate_leaks_direct(size_t threshold); + +/** + * @brief Evaluate memory leaks. + * + * If the current memory leak level (counted from the last time calling \c unity_utils_record_free_mem() ) exceeds + * the threshold set before via \c unity_utils_set_leak_level(), a unit test failure will be triggered. + * + * @note The user MUST set the allowed leak threshold before via \c unity_utils_set_leak_level(), otherwise the + * allowed leak threshold is undefined. + */ +void unity_utils_evaluate_leaks(void); + +/** + * @brief Helper function to setup and initialize heap tracing. + * + * @param num_heap_records the size of the heap record butter, + * counted in number of heap record elements (heap_trace_record_t). + * Use a default value of 80 if no special requirements need to be met. + */ +void unity_utils_setup_heap_record(size_t num_heap_records); + +#ifdef __cplusplus +} +#endif diff --git a/components/unity/port/esp/unity_utils_memory_esp.c b/components/unity/port/esp/unity_utils_memory_esp.c new file mode 100644 index 0000000000..9edf9d0e8d --- /dev/null +++ b/components/unity/port/esp/unity_utils_memory_esp.c @@ -0,0 +1,40 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp_heap_caps.h" +#include "unity_test_utils.h" +#ifdef CONFIG_HEAP_TRACING +#include "esp_heap_trace.h" +#endif + +static size_t s_before_free_8bit; +static size_t s_before_free_32bit; + +void unity_utils_record_free_mem(void) +{ + s_before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + s_before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); +} + +void unity_utils_setup_heap_record(size_t num_heap_records) +{ +#ifdef CONFIG_HEAP_TRACING + static heap_trace_record_t *record_buffer; + if (!record_buffer) { + record_buffer = malloc(sizeof(heap_trace_record_t) * num_heap_records); + assert(record_buffer); + heap_trace_init_standalone(record_buffer, num_heap_records); + } +#endif +} + +void unity_utils_evaluate_leaks_direct(size_t threshold) +{ + size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); + unity_utils_check_leak(s_before_free_8bit, after_free_8bit, "8BIT", threshold); + unity_utils_check_leak(s_before_free_32bit, after_free_32bit, "32BIT", threshold); +} diff --git a/components/unity/port/linux/unity_utils_memory_linux.c b/components/unity/port/linux/unity_utils_memory_linux.c new file mode 100644 index 0000000000..9562abab21 --- /dev/null +++ b/components/unity/port/linux/unity_utils_memory_linux.c @@ -0,0 +1,13 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "unity_test_utils.h" + +void unity_utils_record_free_mem(void) { } + +void unity_utils_setup_heap_record(size_t num_heap_records) { } + +void unity_utils_evaluate_leaks_direct(size_t threshold) { } diff --git a/components/unity/unity_utils_memory.c b/components/unity/unity_utils_memory.c new file mode 100644 index 0000000000..dd2b3bcaa3 --- /dev/null +++ b/components/unity/unity_utils_memory.c @@ -0,0 +1,46 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "unity.h" +#include "unity_test_utils.h" + +static size_t s_allowed_leak_level; + +void unity_utils_set_leak_level(size_t leak_level) +{ + s_allowed_leak_level = leak_level; +} + +void unity_utils_check_leak(unsigned int before_free, + unsigned int after_free, + const char *type, + unsigned int threshold) +{ + int free_delta = (int)after_free - (int)before_free; + printf("MALLOC_CAP_%s usage: Free memory delta: %d Leak threshold: -%u \n", + type, + free_delta, + threshold); + + if (free_delta > 0) { + return; // free memory went up somehow + } + + unsigned int leaked = (size_t)(free_delta * -1); + + printf("MALLOC_CAP_%s %s leak: Before %u bytes free, After %u bytes free (delta %u)\n", + type, + leaked <= threshold ? "potential" : "critical", + before_free, after_free, leaked); + fflush(stdout); + TEST_ASSERT_MESSAGE(leaked <= threshold, "The test leaked too much memory"); +} + +void unity_utils_evaluate_leaks(void) +{ + unity_utils_evaluate_leaks_direct(s_allowed_leak_level); +}