diff options
| author | sexenteen <[email protected]> | 2026-11-03 23:31:45 +0800 |
|---|---|---|
| committer | Rbb666 <[email protected]> | 2026-11-09 09:31:47 +0800 |
| commit | cc533832d3b6596e3d3964f557e52cb8cd1fee30 (patch) | |
| tree | 8aa66460731f63744ad3821259846a787a5e5541 | |
| parent | b3a0eb5e12c6dca8145fc49d76c4785478c9e55b (diff) | |
feat[utest]: repeat the automatic run and sample free/ps between loops
| -rw-r--r-- | .github/workflows/utest_auto_run.yml | 51 | ||||
| -rw-r--r-- | components/utilities/Kconfig | 14 | ||||
| -rw-r--r-- | components/utilities/utest/utest.c | 62 |
3 files changed, 123 insertions, 4 deletions
diff --git a/.github/workflows/utest_auto_run.yml b/.github/workflows/utest_auto_run.yml index 00dba488b7..a6596c19e2 100644 --- a/.github/workflows/utest_auto_run.yml +++ b/.github/workflows/utest_auto_run.yml @@ -51,6 +51,11 @@ on: required: false default: 'manual' type: string + utest_loop: + description: '自动运行重复次数 (1-18),大于 1 时每轮结束后执行 free/ps 观测内存与线程变化' + required: false + default: '1' + type: string concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} @@ -274,7 +279,31 @@ jobs: - name: Build BSP if: ${{ env.TOOLCHAIN_INSTALLED != '' && success() }} + env: + # Passed through env rather than interpolated into the script, so a value + # like "$(...)" or a backtick can never be executed by the shell. + UTEST_LOOP_INPUT: ${{ github.event.inputs.utest_loop }} run: | + # Validate the loop count before anything is appended to .config. The value + # must be a plain decimal integer in 1..18: 18 loops x 20 minutes is the + # 360 minute step budget of the monitor step, so a larger value could only + # be cancelled by GitHub without a useful message. + UTEST_LOOP="${UTEST_LOOP_INPUT:-1}" + case "$UTEST_LOOP" in + ''|*[!0-9]*) + echo "ERROR: utest_loop must be a decimal integer in 1..18, got '$UTEST_LOOP'" + exit 1 + ;; + esac + # 10# forces a decimal parse, so 08/09 are not treated as octal + UTEST_LOOP=$((10#$UTEST_LOOP)) + if [ "$UTEST_LOOP" -lt 1 ] || [ "$UTEST_LOOP" -gt 18 ]; then + echo "ERROR: utest_loop must be in 1..18, got '$UTEST_LOOP'" + exit 1 + fi + echo "UTEST_LOOP=$UTEST_LOOP" >> $GITHUB_ENV + echo "utest auto run loop: $UTEST_LOOP" + # Keep regular RISC-V jobs UP; SMP coverage has its own matrix entry. if [[ "$TEST_BSP_ROOT" == "bsp/qemu-virt64-riscv" && "${{ matrix.platform.SMP_RUN }}" != "smp" ]]; then echo "# CONFIG_RT_USING_SMP is not set" >> $TEST_BSP_ROOT/.config @@ -300,6 +329,12 @@ jobs: cat $UTEST_CONFIG_ROOT/$TEST_CONFIG_FILE >> $TEST_BSP_ROOT/.config echo >> $TEST_BSP_ROOT/.config + # Repeat the automatic utest run when a loop count is requested, so that + # resources leaked by a test case show up as growing memory/threads. + if [ "$UTEST_LOOP" -ne 1 ]; then + echo "CONFIG_RT_UTEST_AUTO_RUN_LOOP=$UTEST_LOOP" >> $TEST_BSP_ROOT/.config + fi + # Keep standard qemu tests free of PM/tickless side effects. if [[ "${{ matrix.platform.KERNEL }}" == "standard" && ( "$TEST_BSP_ROOT" == "bsp/qemu-virt64-riscv" || "$TEST_BSP_ROOT" == "bsp/qemu-virt64-aarch64" ) ]]; then echo "# CONFIG_RT_USING_PM is not set" >> $TEST_BSP_ROOT/.config @@ -419,7 +454,7 @@ jobs: if: ${{ env.TOOLCHAIN_INSTALLED != '' && success() }} # Same known-flaky qemu-virt64-riscv standard kernel utest run as the previous step. continue-on-error: ${{ matrix.platform.UTEST == 'RISCV' && (matrix.config_file == 'kernel/ipc.cfg' || matrix.config_file == 'kernel/kernel_basic.cfg') }} - timeout-minutes: 20 + timeout-minutes: 360 run: | LOG_FILE="qemu_output_${TEST_QEMU_ARCH}.log" FAILURE_DETECTED=false @@ -437,7 +472,15 @@ jobs: echo " VV VV " echo "==========================================================================================" set +e - timeout 20m bash -c ' + # Bound the QEMU wait by the loop count (20 minutes per loop). The step level + # timeout-minutes cannot take an expression, so the real limit is applied here. + LOOP_NUM="${UTEST_LOOP:-1}" + case "$LOOP_NUM" in + ''|*[!0-9]*) LOOP_NUM=1 ;; + esac + MONITOR_TIMEOUT_MIN=$(( LOOP_NUM * 20 )) + echo "Monitoring the QEMU log for at most ${MONITOR_TIMEOUT_MIN} minutes (utest_loop=${LOOP_NUM})" + timeout "${MONITOR_TIMEOUT_MIN}m" bash -c ' tail -n +1 -f "$1" | while IFS= read -r line; do echo "$line" if [[ "$line" == *"[ FAILED ] [ result ]"* ]]; then @@ -454,7 +497,7 @@ jobs: exit 1 fi - if [[ "$line" == *"[==========] [ utest ] finished"* ]]; then + if [[ "$line" == *"[==========] [ utest ] all loops finished"* ]]; then if [[ "$FAILURE_DETECTED" == "true" ]]; then echo "==========================================================================================" echo " || || " @@ -477,7 +520,7 @@ jobs: if [[ $monitor_status -eq 124 ]]; then echo "==========================================================================================" echo " || || " - echo " || Error: Utest run timed out after 20 minutes. Exiting... || " + echo " || Error: Utest run timed out after ${MONITOR_TIMEOUT_MIN} minutes. Exiting... || " echo " VV VV " echo "==========================================================================================" pkill -f "qemu-system-${TEST_QEMU_ARCH}.*${TEST_BSP_ROOT}/rtthread.bin" || true diff --git a/components/utilities/Kconfig b/components/utilities/Kconfig index c32cd692bd..75a622a437 100644 --- a/components/utilities/Kconfig +++ b/components/utilities/Kconfig @@ -226,6 +226,20 @@ config RT_USING_UTEST help If enable this option, the test cases will be run automatically when board boot up. + config RT_UTEST_AUTO_RUN_LOOP + int "Loop count of the automatic run" + depends on RT_UTEST_USING_AUTO_RUN + range 1 18 + default 1 + help + How many times the automatic run repeats the registered test cases. + When it is greater than 1, "free" and "ps" are executed after each + loop, so memory or thread leaks show up in the log as growing numbers. + + The upper bound matches the CI budget: the utest_auto_run workflow + budgets 20 minutes per loop with a 360 minute step timeout, so 18 + loops are the most that can finish before GitHub cancels the step. + if FINSH_USING_OPTION_COMPLETION config RT_UTEST_MAX_OPTIONS int "Maximum number of utest cases for auto-completion" diff --git a/components/utilities/utest/utest.c b/components/utilities/utest/utest.c index f9f80fdff9..81cd1d7154 100644 --- a/components/utilities/utest/utest.c +++ b/components/utilities/utest/utest.c @@ -14,6 +14,10 @@ #include "utest.h" #include "utest_log.h" +#ifdef RT_USING_FINSH +#include <msh.h> +#endif + #define DBG_TAG "utest" #ifdef UTEST_DEBUG #define DBG_LVL DBG_LOG @@ -222,6 +226,44 @@ static int utest_help(void) return 0; } +#ifdef RT_UTEST_AUTO_RUN_LOOP +static void utest_loop_runtime_info(rt_uint32_t loop) +{ + if (tc_loop <= 1) + { + return; + } + + LOG_I("[==========] [ utest ] loop %u/%u runtime info", (rt_uint32_t)loop, (rt_uint32_t)tc_loop); + +#ifdef RT_USING_FINSH + { + static char free_cmd[] = "free"; + static char ps_cmd[] = "ps"; + int ret = 0; + + ret |= msh_exec(free_cmd, sizeof(free_cmd) - 1); + ret |= msh_exec(ps_cmd, sizeof(ps_cmd) - 1); + if (ret != 0) + { + /* do not fail the test run just because the commands are absent, but make it visible */ + LOG_W("[==========] [ utest ] 'free'/'ps' are not available, runtime info is incomplete"); + } + } +#else + { + rt_size_t total = 0; + rt_size_t used = 0; + rt_size_t max_used = 0; + + rt_memory_info(&total, &used, &max_used); + LOG_I("[==========] [ utest ] memory total:%u used:%u maximum:%u", + (rt_uint32_t)total, (rt_uint32_t)used, (rt_uint32_t)max_used); + } +#endif /* RT_USING_FINSH */ +} +#endif /* RT_UTEST_AUTO_RUN_LOOP */ + static void utest_do_run(const char *utest_name) { rt_size_t i; @@ -352,7 +394,18 @@ static void utest_do_run(const char *utest_name) } LOG_I("[==========] [ utest ] finished"); + +#ifdef RT_UTEST_AUTO_RUN_LOOP + utest_loop_runtime_info(index + 1); +#endif } + +#ifdef RT_UTEST_AUTO_RUN_LOOP + /* Emitted once, after every loop and its runtime info. CI uses this line + * instead of the per-loop "finished" one, otherwise it would stop after the + * first loop and miss the remaining loops and their free/ps output. */ + LOG_I("[==========] [ utest ] all loops finished"); +#endif } static void utest_thr_entry(void *para) @@ -378,7 +431,16 @@ static void utest_thread_create(const char *utest_name) #ifdef RT_UTEST_USING_AUTO_RUN static int utest_auto_run(void) { +#ifdef RT_UTEST_AUTO_RUN_LOOP + tc_loop = RT_UTEST_AUTO_RUN_LOOP; +#else tc_loop = 1; +#endif + if (tc_loop == 0) + { + tc_loop = 1; + } + utest_thread_create(RT_NULL); return RT_EOK; } |
