From: Wen Yang <wen.yang@linux.dev>
To: Gabriele Monaco <gmonaco@redhat.com>,
linux-trace-kernel@vger.kernel.org, linux-kernel@vger.kernel.org,
Steven Rostedt <rostedt@goodmis.org>
Cc: Nam Cao <namcao@linutronix.de>,
Thomas Weissschuh <thomas.weissschuh@linutronix.de>,
Tomas Glozar <tglozar@redhat.com>, John Kacur <jkacur@redhat.com>
Subject: Re: [PATCH v3 04/17] tools/rv: Add selftests
Date: Mon, 29 Jun 2026 01:10:34 +0800 [thread overview]
Message-ID: <0c230c01-77c8-4c9e-9f49-ecb9555402cf@linux.dev> (raw)
In-Reply-To: <20260625121440.116317-5-gmonaco@redhat.com>
On 6/25/26 20:14, Gabriele Monaco wrote:
> The rv tool needs automated testing to catch regressions and verify
> correct functionality across different usage scenarios.
>
> Add selftests that validate monitor listing (including containers and
> nested monitors), monitor execution with different configurations
> (reactors, verbose output, tracing), and trace output format for both
> per-task and per-cpu monitors. Error handling paths are also tested.
> Tests use a shared engine for common patterns.
>
> Acked-by: Nam Cao <namcao@linutronix.de>
> Signed-off-by: Gabriele Monaco <gmonaco@redhat.com>
> ---
> tools/verification/rv/Makefile | 5 +-
> tools/verification/rv/tests/rv_list.t | 48 ++++++++++
> tools/verification/rv/tests/rv_mon.t | 95 ++++++++++++++++++
> tools/verification/tests/engine.sh | 132 ++++++++++++++++++++++++++
> 4 files changed, 279 insertions(+), 1 deletion(-)
> create mode 100644 tools/verification/rv/tests/rv_list.t
> create mode 100644 tools/verification/rv/tests/rv_mon.t
> create mode 100644 tools/verification/tests/engine.sh
>
> diff --git a/tools/verification/rv/Makefile b/tools/verification/rv/Makefile
> index 5b898360ba..8ae5fc0d1d 100644
> --- a/tools/verification/rv/Makefile
> +++ b/tools/verification/rv/Makefile
> @@ -78,4 +78,7 @@ clean: doc_clean fixdep-clean
> $(Q)rm -f rv rv-static fixdep FEATURE-DUMP rv-*
> $(Q)rm -rf feature
>
> -.PHONY: FORCE clean
> +check: $(RV)
> + RV=$(RV) prove -o --directives -f tests/
> +
> +.PHONY: FORCE clean check
> diff --git a/tools/verification/rv/tests/rv_list.t b/tools/verification/rv/tests/rv_list.t
> new file mode 100644
> index 0000000000..201af33a52
> --- /dev/null
> +++ b/tools/verification/rv/tests/rv_list.t
> @@ -0,0 +1,48 @@
> +#!/bin/bash
> +# SPDX-License-Identifier: GPL-2.0
> +source ../tests/engine.sh
> +test_begin
> +
> +set_timeout 30s
> +
> +RVDIR=/sys/kernel/tracing/rv/
> +
> +# Help and basic tests
> +check "verify help page" \
> + "$RV --help" 0 "usage: rv command"
> +
> +check "verify list subcommand help" \
> + "$RV list --help" 0 "list all available monitors"
> +
> +all_nested=$(grep : $RVDIR/available_monitors | cut -d: -f2 | paste -s | sed 's/\t/\\|/g')
> +all_non_nested=$(grep -v : $RVDIR/available_monitors | cut -d: -f2 | paste -s | sed 's/\t/\\|/g')
> +sched_monitors=$(grep sched: $RVDIR/available_monitors | cut -d: -f2 | paste -s | sed 's/\t/\\|/g')
> +description_state="[[:space:]]\+[[:print:]]\+\[\(OFF\|ON\)\]"
> +line_nested=" - \($all_nested\)${description_state}"
> +line_non_nested="\($all_non_nested\)${description_state}"
> +
> +# List monitors and containers
> +check "list all monitors" \
> + "$RV list" 0 "" "" "^\($line_nested\|$line_non_nested\)$"
> +
> +check_if_exists "list container" \
> + "$RV list sched" "$RVDIR/monitors/sched" \
> + "" "-- No monitor found in container sched --" \
> + "^\($sched_monitors\)${description_state}$"
> +
> +check_if_exists "list non-container" \
> + "$RV list wwnr" "$RVDIR/monitors/wwnr" \
> + "-- No monitor found in container wwnr --" \
> + "^\( - \)\?[[:alnum:]]\+${description_state}$"
> +
> +check "list incomplete container name" \
> + "$RV list s" 0 "-- No monitor found in container s --"
> +
> +# Error handling tests
> +check "no command" \
> + "$RV" 1 "rv requires a command"
> +
> +check "invalid command" \
> + "$RV invalid" 1 "rv does not know the invalid command"
> +
> +test_end
> diff --git a/tools/verification/rv/tests/rv_mon.t b/tools/verification/rv/tests/rv_mon.t
> new file mode 100644
> index 0000000000..cbc346c74c
> --- /dev/null
> +++ b/tools/verification/rv/tests/rv_mon.t
> @@ -0,0 +1,95 @@
> +#!/bin/bash
> +# SPDX-License-Identifier: GPL-2.0
> +source ../tests/engine.sh
> +test_begin
> +
> +set_timeout 30s
> +
> +RVDIR=/sys/kernel/tracing/rv/
> +
> +# Help and basic tests
> +check "verify mon subcommand help" \
> + "$RV mon --help" 0 "run a monitor"
> +
> +# Error handling tests
> +check "mon without monitor name" \
> + "$RV mon" 1 "usage: rv mon"
> +
> +check "invalid monitor name" \
> + "$RV mon invalid" 1 "monitor invalid does not exist"
> +
> +if [ -d $RVDIR/monitors/wwnr ]; then
> +
> +check "invalid reactor name" \
> + "$RV mon wwnr -r invalid" 1 "failed to set invalid reactor, is it available?"
> +
> +check "monitor name is substring of another monitor" \
> + "$RV mon nr" 1 "monitor nr does not exist"
> +
> +check "already enabled monitor returns error" \
> + "echo 1 > $RVDIR/monitors/wwnr/enable; $RV mon wwnr" 1 \
> + "monitor wwnr (in-kernel) is already enabled"
> +echo 0 > $RVDIR/monitors/wwnr/enable
> +
> +fi
> +
> +# rv mon runs until terminated
> +set_expected_timeout 2s
> +
> +# Run monitors with different configurations
> +check_if_exists "run the monitor without parameters" \
> + "$RV mon wwnr" "$RVDIR/monitors/wwnr" "" "."
> +
> +check_if_exists "run the monitor as verbose" \
> + "$RV mon wwnr -v" "$RVDIR/monitors/wwnr" \
> + "my pid is \$pid" "\(event\|error\)"
> +
> +check_if_exists "run the monitor with a reactor" \
> + "$RV mon wwnr -r printk & sleep .5 && cat $RVDIR/monitors/wwnr/reactors && wait" \
> + "$RVDIR/monitors/wwnr/reactors" "\[printk\]"
> +
> +check_if_exists "reactor is restored after exit" \
> + "cat $RVDIR/monitors/wwnr/reactors" \
> + "$RVDIR/monitors/wwnr/reactors" "\[nop\]"
> +
> +check_if_exists "run a nested monitor with a reactor" \
> + "$RV mon snroc -r printk & sleep .5 && cat $RVDIR/monitors/sched/snroc/reactors && wait" \
> + "$RVDIR/monitors/sched/snroc/reactors" "\[printk\]"
> +
> +check_if_exists "run an explicitly nested monitor with a reactor" \
> + "$RV mon sched:sssw -r printk & sleep .5 && cat $RVDIR/monitors/sched/sssw/reactors && wait" \
> + "$RVDIR/monitors/sched/sssw/reactors" "\[printk\]"
> +
> +check_if_exists "run container monitor" \
> + "$RV mon sched & sleep .5 && cat $RVDIR/monitors/sched/{sssw,sco}/enable && wait" \
> + "$RVDIR/monitors/sched" "1" "0" "^1$"
> +
> +# Regexes for the trace
> +header="^[[:space:]]\+\(\([][A-Z_x<>-]\+\||\)[[:space:]]*\)\+$"
> +type="\(event\|error\)[[:space:]]\+"
> +genpid="[0-9]\+[[:space:]]\+"
> +selfpid="\$pid[[:space:]]\+"
> +cpu="\[[0-9]\{3\}\][[:space:]]\+"
> +state="[a-z_]\+ "
> +trace_task="${genpid}${cpu}${type}${genpid}${state}"
> +trace_task_self="${genpid}${cpu}${type}${selfpid}${state}"
> +trace_cpu="${genpid}${cpu}${type}${state}"
> +trace_cpu_self="${selfpid}${cpu}${type}${state}"
> +
> +check_if_exists "run per-task monitor with tracing" \
> + "$RV mon sssw -t" "$RVDIR/monitors/sched/sssw" \
> + "$header" "$trace_task_self" "\($header\|$trace_task\)"
> +
> +check_if_exists "run per-task monitor tracing also self" \
> + "$RV mon sched:sssw -t -s" "$RVDIR/monitors/sched/sssw" \
> + "$trace_task_self" "" "\($header\|$trace_task\)"
> +
> +check_if_exists "run per-cpu monitor with tracing" \
> + "$RV mon sched:sco -t" "$RVDIR/monitors/sched/sco" \
> + "$header" "$trace_cpu_self" "\($header\|$trace_cpu\)"
> +
> +check_if_exists "run per-cpu monitor tracing also self" \
> + "$RV mon sco -t -s" "$RVDIR/monitors/sched/sco" \
> + "$trace_cpu_self" "" "\($header\|$trace_cpu\)"
> +
> +test_end
> diff --git a/tools/verification/tests/engine.sh b/tools/verification/tests/engine.sh
> new file mode 100644
> index 0000000000..76cc254ff9
> --- /dev/null
> +++ b/tools/verification/tests/engine.sh
> @@ -0,0 +1,132 @@
> +#!/bin/bash
> +# SPDX-License-Identifier: GPL-2.0
> +test_begin() {
> + # Count tests to allow the test harness to double-check if all were
> + # included correctly.
> + ctr=0
> + [ -z "$RV" ] && RV="../rv/rv"
> + [ -n "$TEST_COUNT" ] && echo "1..$TEST_COUNT"
> +}
> +
> +failure() {
> + fail=1
> + if [ $# -gt 0 ]; then
> + failbuf+="$1"
> + failbuf+=$'\n'
> + fi
> +}
> +
> +report() {
> + local desc="$1"
> +
> + if [ "$fail" -eq 0 ]; then
> + echo "ok $ctr - $desc"
> + else
> + # Add output and exit code as comments in case of failure
> + echo "not ok $ctr - $desc"
> + echo -n "$failbuf"
> + echo "$result" | col -b | while read -r line; do echo "# $line"; done
> + printf "#\n# exit code %s\n" "$exitcode"
> + fi
> +}
> +
> +_check() {
> + local command=$2
> + local expected_exitcode=${3:-0}
> + local expected_output=$4
> + local unexpected_output=$5
> + local all_lines_pattern=$6
> + local patterns="$expected_output $unexpected_output $all_lines_pattern"
> +
> + eval "$TIMEOUT" "$command" &> check_output.$$ &
> + bgpid=$!
> + pid=$(pgrep -f "${command%%[|;&>]*}" | tail -n1)
The pgrep runs may immediately after the background fork, before the
child process has had time to exec.
--
Best wishes,
Wen
> + wait $bgpid
> + exitcode=$?
> + result=$(tr -d '\0' < check_output.$$)
> + rm -f check_output.$$
> +
> + failbuf=''
> + fail=0
> +
> + # Suppress any other error if a needed pid is empty
> + if [ -z "$pid" ] && grep -q "\$pid" <<< "$patterns"; then
> + result=''
> + failure "# Empty pid for $command"
> + return 1
> + fi
> +
> + expected_output="${expected_output//\$pid/$pid}"
> + unexpected_output="${unexpected_output//\$pid/$pid}"
> + all_lines_pattern="${all_lines_pattern//\$pid/$pid}"
> +
> + # Test if the results matches if requested
> + if [ -n "$expected_output" ] && ! grep -qe "$expected_output" <<< "$result"; then
> + failure "# Output match failed: \"$expected_output\""
> + fi
> +
> + if [ -n "$unexpected_output" ] && grep -qe "$unexpected_output" <<< "$result"; then
> + failure "# Output non-match failed: \"$unexpected_output\""
> + fi
> +
> + if [ -n "$all_lines_pattern" ] && grep -vqe "$all_lines_pattern" <<< "$result"; then
> + failure "# All-lines pattern failed: \"$all_lines_pattern\""
> + fi
> +
> + if [ $exitcode -ne "$expected_exitcode" ]; then
> + failure "# Expected exit code $expected_exitcode"
> + fi
> +}
> +
> +check() {
> + # Simple check: run the command with given arguments and test exit code.
> + # If TEST_COUNT is set, run the test. Otherwise, just count.
> + ctr=$((ctr + 1))
> + if [ -n "$TEST_COUNT" ]; then
> + _check "$@"
> + report "$1"
> + fi
> +}
> +
> +check_if_exists() {
> + # Conditional check that skips if a file or folder doesn't exist
> + local desc=$1
> + local command=$2
> + local file=$3
> + local expected_output=$4
> + local unexpected_output=$5
> + local all_lines_pattern=$6
> +
> + ctr=$((ctr + 1))
> + if [ -n "$TEST_COUNT" ]; then
> + if [ ! -e "$file" ]; then
> + echo "ok $ctr - $desc # SKIP file not found: $file"
> + else
> + _check "$desc" "$command" 0 "$expected_output" \
> + "$unexpected_output" "$all_lines_pattern"
> + report "$desc"
> + fi
> + fi
> +}
> +
> +set_timeout() {
> + TIMEOUT="timeout -v -k 15s $1"
> +}
> +
> +set_expected_timeout() {
> + TIMEOUT="timeout --preserve-status -k 15s $1"
> +}
> +
> +unset_timeout() {
> + unset TIMEOUT
> +}
> +
> +test_end() {
> + # If running without TEST_COUNT, tests are not actually run, just
> + # counted. In that case, re-run the test with the correct count.
> + [ -z "$TEST_COUNT" ] && TEST_COUNT=$ctr exec bash "$0" || true
> +}
> +
> +# Avoid any environmental discrepancies
> +export LC_ALL=C
> +unset_timeout
next prev parent reply other threads:[~2026-06-28 17:10 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-25 12:14 [PATCH v3 00/17] rv: Add selftests to tools and KUnit tests Gabriele Monaco
2026-06-25 12:14 ` [PATCH v3 01/17] rv: Use generic rv_this for the rv_monitor variable in LTL Gabriele Monaco
2026-06-25 12:14 ` [PATCH v3 02/17] tools/rv: Fix exit status when monitor execution fails Gabriele Monaco
2026-06-25 12:14 ` [PATCH v3 03/17] verification/rvgen: Improve rv_dir discovery in RVGenerator Gabriele Monaco
2026-06-25 12:14 ` [PATCH v3 04/17] tools/rv: Add selftests Gabriele Monaco
2026-06-28 17:10 ` Wen Yang [this message]
2026-06-25 12:14 ` [PATCH v3 05/17] verification/rvgen: Add golden and spec folders for tests Gabriele Monaco
2026-06-25 12:14 ` [PATCH v3 06/17] verification/rvgen: Add selftests Gabriele Monaco
2026-06-25 12:14 ` [PATCH v3 07/17] rv: Add KUnit stub to rv_react() and rv_*_task_monitor_slot() Gabriele Monaco
2026-06-25 12:14 ` [PATCH v3 08/17] rv: Export task monitor slot and react symbols Gabriele Monaco
2026-06-25 12:14 ` [PATCH v3 09/17] rv: Add KUnit tests for some DA/HA monitors Gabriele Monaco
2026-06-25 12:14 ` [PATCH v3 10/17] rv: Add KUnit stub for current Gabriele Monaco
2026-06-25 12:14 ` [PATCH v3 11/17] rv: Prevent unintentional tracepoints during KUnit tests Gabriele Monaco
2026-06-28 17:17 ` Wen Yang
2026-06-25 12:14 ` [PATCH v3 12/17] rv: Add KUnit tests for some LTL monitors Gabriele Monaco
2026-06-25 12:14 ` [PATCH v3 13/17] verification/rvgen: Add the rvgen kunit subcommand Gabriele Monaco
2026-06-25 12:14 ` [PATCH v3 14/17] verification/rvgen: Add selftests for rvgen kunit Gabriele Monaco
2026-06-28 17:06 ` Wen Yang
2026-06-25 12:14 ` [PATCH v3 15/17] selftests/verification: Fix wrong errexit assumption Gabriele Monaco
2026-06-25 12:14 ` [PATCH v3 16/17] selftests/verification: Rearrange the wwnr_printk test Gabriele Monaco
2026-06-25 12:14 ` [PATCH v3 17/17] selftests/verification: Add selftests for deadline and stall monitors Gabriele Monaco
2026-06-28 16:58 ` Wen Yang
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=0c230c01-77c8-4c9e-9f49-ecb9555402cf@linux.dev \
--to=wen.yang@linux.dev \
--cc=gmonaco@redhat.com \
--cc=jkacur@redhat.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-trace-kernel@vger.kernel.org \
--cc=namcao@linutronix.de \
--cc=rostedt@goodmis.org \
--cc=tglozar@redhat.com \
--cc=thomas.weissschuh@linutronix.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox