feat(unity): Adds unit test ordering by file path and line number

Closes https://github.com/espressif/esp-idf/issues/15529
This commit is contained in:
Konstantin Kondrashov
2025-04-22 13:09:52 +03:00
parent dc678decf7
commit 81c08911f0
10 changed files with 131 additions and 5 deletions

View File

@ -58,4 +58,18 @@ menu "Unity unit testing library"
jumping back to the test menu. The jumping is usually occurs in assert jumping back to the test menu. The jumping is usually occurs in assert
functions such as TEST_ASSERT, TEST_FAIL etc. functions such as TEST_ASSERT, TEST_FAIL etc.
config UNITY_TEST_ORDER_BY_FILE_PATH_AND_LINE
bool "Order unit tests by file path and line number"
default n
help
If enabled, the Unity test framework will automatically insert test cases
in a sorted order at registration time (during constructor execution),
based on their source file path and line number.
This ensures consistent execution order across platforms (e.g., Linux vs. on-chip),
preserving the logical order in which tests are written in the source files.
Note, the file path used for sorting follows the full absolute path format.
/IDF/examples/system/unit_test/components/testable/test/test_mean.c
endmenu # "Unity unit testing library" endmenu # "Unity unit testing library"

View File

@ -0,0 +1,8 @@
# Documentation: .gitlab/ci/README.md#manifest-file-to-control-the-buildtest-apps
components/unity/test_apps:
enable:
- if: IDF_TARGET in["esp32", "linux"]
reason: need to test on a chip and linux targets
depends_components:
- unity

View File

@ -0,0 +1,7 @@
#This is the project CMakeLists.txt file for the test subproject
cmake_minimum_required(VERSION 3.16)
set(COMPONENTS main)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(unity_test_app)

View File

@ -0,0 +1,2 @@
| Supported Targets | ESP32 | Linux |
| ----------------- | ----- | ----- |

View File

@ -0,0 +1,3 @@
idf_component_register(SRC_DIRS "."
PRIV_REQUIRES unity
WHOLE_ARCHIVE)

View File

@ -0,0 +1,26 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "unity.h"
TEST_CASE("Test 1", "[test]")
{
TEST_ASSERT(1 == 1);
}
TEST_CASE("Test 2", "[test]")
{
TEST_ASSERT(1 == 1);
}
TEST_CASE("Test 3", "[test]")
{
TEST_ASSERT(1 == 1);
}
TEST_CASE("Test 4", "[test]")
{
TEST_ASSERT(1 == 1);
}

View File

@ -0,0 +1,11 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include "unity.h"
void app_main(void)
{
unity_run_menu();
}

View File

@ -0,0 +1,26 @@
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import pytest
from pytest_embedded import Dut
from pytest_embedded_idf.utils import idf_parametrize
def verify_test_order(dut: Dut) -> None:
dut.expect_exact('Press ENTER to see the list of tests.')
dut.write('\n')
dut.expect('Test 1')
dut.expect('Test 2')
dut.expect('Test 3')
dut.expect('Test 4')
@pytest.mark.generic
@idf_parametrize('target', ['esp32'], indirect=['target'])
def test_unit_test_order(dut: Dut) -> None:
verify_test_order(dut)
@pytest.mark.host_test
@idf_parametrize('target', ['linux'], indirect=['target'])
def test_unit_test_order_linux(dut: Dut) -> None:
verify_test_order(dut)

View File

@ -0,0 +1,2 @@
CONFIG_ESP_TASK_WDT_EN=n
CONFIG_UNITY_TEST_ORDER_BY_FILE_PATH_AND_LINE=y

View File

@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2016-2025 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@ -11,6 +11,7 @@
#include <stdio.h> #include <stdio.h>
#include "unity.h" #include "unity.h"
#include "esp_system.h" #include "esp_system.h"
#include "sdkconfig.h"
/* similar to UNITY_PRINT_EOL */ /* similar to UNITY_PRINT_EOL */
#define UNITY_PRINT_TAB() UNITY_OUTPUT_CHAR('\t') #define UNITY_PRINT_TAB() UNITY_OUTPUT_CHAR('\t')
@ -24,11 +25,37 @@ void unity_testcase_register(test_desc_t *desc)
if (!s_unity_tests_first) { if (!s_unity_tests_first) {
s_unity_tests_first = desc; s_unity_tests_first = desc;
s_unity_tests_last = desc; s_unity_tests_last = desc;
} else { return;
test_desc_t *temp = s_unity_tests_first;
s_unity_tests_first = desc;
s_unity_tests_first->next = temp;
} }
#if CONFIG_UNITY_TEST_ORDER_BY_FILE_PATH_AND_LINE
test_desc_t *prev = NULL;
test_desc_t *current = s_unity_tests_first;
while (current) {
int file_cmp = strcmp(desc->file, current->file);
if (file_cmp < 0 || (file_cmp == 0 && desc->line < current->line)) {
// Insert before current
if (prev) {
prev->next = desc;
} else {
// Inserting at the head
s_unity_tests_first = desc;
}
desc->next = current;
return;
}
prev = current;
current = current->next;
}
// Insert at the end
prev->next = desc;
s_unity_tests_last = desc;
#else
// Insert at head (original behavior)
desc->next = s_unity_tests_first;
s_unity_tests_first = desc;
#endif
} }
/* print the multiple function case name and its sub-menu /* print the multiple function case name and its sub-menu