mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
feature (unity): added memory leak check API, integrated it into cxx tests
This commit is contained in:
parent
f3e8a17285
commit
905f5c3d41
@ -1,7 +1,6 @@
|
|||||||
# This is the project CMakeLists.txt file for the test subproject
|
# This is the project CMakeLists.txt file for the test subproject
|
||||||
cmake_minimum_required(VERSION 3.5)
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
|
||||||
set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components")
|
|
||||||
set(COMPONENTS main)
|
set(COMPONENTS main)
|
||||||
|
|
||||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
@ -1,3 +1,2 @@
|
|||||||
idf_component_register(SRCS "test_exception.cpp"
|
idf_component_register(SRCS "test_exception.cpp"
|
||||||
REQUIRES test_utils
|
|
||||||
PRIV_REQUIRES unity driver)
|
PRIV_REQUIRES unity driver)
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include "unity.h"
|
#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):
|
/* 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
|
- 8 bytes are allocated by __cxa_get_globals() to keep __cxa_eh_globals
|
||||||
@ -25,12 +25,12 @@
|
|||||||
|
|
||||||
extern "C" void setUp()
|
extern "C" void setUp()
|
||||||
{
|
{
|
||||||
test_utils_record_free_mem();
|
unity_utils_record_free_mem();
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void tearDown()
|
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]")
|
TEST_CASE("c++ exceptions work", "[cxx] [exceptions]")
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
# This is the project CMakeLists.txt file for the test subproject
|
# This is the project CMakeLists.txt file for the test subproject
|
||||||
cmake_minimum_required(VERSION 3.5)
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
|
||||||
set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components")
|
|
||||||
set(COMPONENTS main)
|
set(COMPONENTS main)
|
||||||
|
|
||||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
@ -1,3 +1,2 @@
|
|||||||
idf_component_register(SRCS "test_exception_no_except.cpp"
|
idf_component_register(SRCS "test_exception_no_except.cpp"
|
||||||
REQUIRES test_utils
|
|
||||||
PRIV_REQUIRES unity driver)
|
PRIV_REQUIRES unity driver)
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "unity.h"
|
#include "unity.h"
|
||||||
#include "memory_checks.h"
|
|
||||||
|
|
||||||
extern "C" void setUp()
|
extern "C" void setUp()
|
||||||
{
|
{
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
# This is the project CMakeLists.txt file for the test subproject
|
# This is the project CMakeLists.txt file for the test subproject
|
||||||
cmake_minimum_required(VERSION 3.5)
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
|
||||||
set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components")
|
|
||||||
set(COMPONENTS main)
|
set(COMPONENTS main)
|
||||||
|
|
||||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
idf_component_register(SRCS "test_cxx_general.cpp"
|
idf_component_register(SRCS "test_cxx_general.cpp"
|
||||||
PRIV_REQUIRES unity test_utils driver)
|
PRIV_REQUIRES unity driver)
|
||||||
|
@ -11,18 +11,17 @@
|
|||||||
#include "freertos/semphr.h"
|
#include "freertos/semphr.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "unity.h"
|
#include "unity.h"
|
||||||
#include "memory_checks.h"
|
#include "unity_test_utils.h"
|
||||||
|
|
||||||
extern "C" void setUp()
|
extern "C" void setUp()
|
||||||
{
|
{
|
||||||
test_utils_set_leak_level(0, ESP_LEAK_TYPE_CRITICAL, ESP_COMP_LEAK_GENERAL);
|
unity_utils_set_leak_level(0);
|
||||||
test_utils_record_free_mem();
|
unity_utils_record_free_mem();
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void tearDown()
|
extern "C" void tearDown()
|
||||||
{
|
{
|
||||||
size_t leak_level = test_utils_get_leak_level(ESP_LEAK_TYPE_CRITICAL, ESP_COMP_LEAK_GENERAL);
|
unity_utils_evaluate_leaks();
|
||||||
test_utils_finish_and_evaluate_leaks(leak_level, leak_level);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char* TAG = "cxx";
|
static const char* TAG = "cxx";
|
||||||
@ -45,7 +44,7 @@ static int non_pod_test_helper(int new_val)
|
|||||||
// Will fail if run twice
|
// Will fail if run twice
|
||||||
TEST_CASE("can use static initializers for non-POD types", "[restart_init]")
|
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(42, non_pod_test_helper(1));
|
||||||
TEST_ASSERT_EQUAL(1, non_pod_test_helper(0));
|
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_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);
|
s_slow_init_sem = xSemaphoreCreateCounting(10, 0);
|
||||||
TEST_ASSERT_NOT_NULL(s_slow_init_sem);
|
TEST_ASSERT_NOT_NULL(s_slow_init_sem);
|
||||||
int task_count = 0;
|
int task_count = 0;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
# This is the project CMakeLists.txt file for the test subproject
|
# This is the project CMakeLists.txt file for the test subproject
|
||||||
cmake_minimum_required(VERSION 3.5)
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
|
||||||
set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components")
|
|
||||||
set(COMPONENTS main)
|
set(COMPONENTS main)
|
||||||
|
|
||||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
@ -1,3 +1,2 @@
|
|||||||
idf_component_register(SRCS "test_rtti.cpp"
|
idf_component_register(SRCS "test_rtti.cpp"
|
||||||
REQUIRES test_utils
|
|
||||||
PRIV_REQUIRES unity driver)
|
PRIV_REQUIRES unity driver)
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <typeinfo>
|
#include <typeinfo>
|
||||||
#include "unity.h"
|
#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):
|
/* 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
|
- 8 bytes are allocated by __cxa_get_globals() to keep __cxa_eh_globals
|
||||||
@ -26,14 +26,13 @@
|
|||||||
|
|
||||||
extern "C" void setUp()
|
extern "C" void setUp()
|
||||||
{
|
{
|
||||||
test_utils_set_leak_level(0, ESP_LEAK_TYPE_CRITICAL, ESP_COMP_LEAK_GENERAL);
|
unity_utils_set_leak_level(0);
|
||||||
test_utils_record_free_mem();
|
unity_utils_record_free_mem();
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void tearDown()
|
extern "C" void tearDown()
|
||||||
{
|
{
|
||||||
size_t leak_level = test_utils_get_leak_level(ESP_LEAK_TYPE_CRITICAL, ESP_COMP_LEAK_GENERAL);
|
unity_utils_evaluate_leaks();
|
||||||
test_utils_finish_and_evaluate_leaks(leak_level, leak_level);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
using namespace std;
|
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_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;
|
bool thrown = false;
|
||||||
DerivedA derived_a;
|
DerivedA derived_a;
|
||||||
Base &base = derived_a;
|
Base &base = derived_a;
|
||||||
|
@ -15,7 +15,10 @@ endif()
|
|||||||
|
|
||||||
if(CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER)
|
if(CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER)
|
||||||
list(APPEND srcs "unity_runner.c")
|
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 srcs "unity_utils_freertos.c")
|
||||||
|
list(APPEND requires "freertos")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(CONFIG_UNITY_ENABLE_FIXTURE)
|
if(CONFIG_UNITY_ENABLE_FIXTURE)
|
||||||
@ -23,8 +26,13 @@ if(CONFIG_UNITY_ENABLE_FIXTURE)
|
|||||||
list(APPEND includes "unity/extras/fixture/src")
|
list(APPEND includes "unity/extras/fixture/src")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
list(APPEND srcs "unity_utils_memory.c")
|
||||||
|
|
||||||
if(NOT "${target}" STREQUAL "linux")
|
if(NOT "${target}" STREQUAL "linux")
|
||||||
list(APPEND srcs "unity_port_esp32.c")
|
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()
|
endif()
|
||||||
|
|
||||||
idf_component_register(SRCS "${srcs}"
|
idf_component_register(SRCS "${srcs}"
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
|
#include "unity_test_utils_memory.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
80
components/unity/include/unity_test_utils_memory.h
Normal file
80
components/unity/include/unity_test_utils_memory.h
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#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
|
40
components/unity/port/esp/unity_utils_memory_esp.c
Normal file
40
components/unity/port/esp/unity_utils_memory_esp.c
Normal file
@ -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);
|
||||||
|
}
|
13
components/unity/port/linux/unity_utils_memory_linux.c
Normal file
13
components/unity/port/linux/unity_utils_memory_linux.c
Normal file
@ -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) { }
|
46
components/unity/unity_utils_memory.c
Normal file
46
components/unity/unity_utils_memory.c
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#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);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user