summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsexenteen <[email protected]>2026-11-03 23:31:45 +0800
committerRbb666 <[email protected]>2026-11-09 09:31:47 +0800
commitcc533832d3b6596e3d3964f557e52cb8cd1fee30 (patch)
tree8aa66460731f63744ad3821259846a787a5e5541
parentb3a0eb5e12c6dca8145fc49d76c4785478c9e55b (diff)
feat[utest]: repeat the automatic run and sample free/ps between loops
-rw-r--r--.github/workflows/utest_auto_run.yml51
-rw-r--r--components/utilities/Kconfig14
-rw-r--r--components/utilities/utest/utest.c62
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;
}