From: Thomas Haller <thaller@redhat.com>
To: NetFilter <netfilter-devel@vger.kernel.org>
Cc: Thomas Haller <thaller@redhat.com>
Subject: [PATCH nft 1/1] tests/shell: add NFT_TEST_FAIL_ON_SKIP_EXCEPT for allow-list of skipped tests (XFAIL)
Date: Wed, 18 Oct 2023 00:33:29 +0200 [thread overview]
Message-ID: <20231017223450.2613981-1-thaller@redhat.com> (raw)
Some tests cannot pass, for example due to missing kernel features or
kernel bugs. The tests make an educated guess (feature detection),
whether the failure is due to that, and marks the test as SKIP (exit
77). The problem is, the test might guess wrong and hide a real issue.
Add support for a NFT_TEST_FAIL_ON_SKIP_EXCEPT= regex to help with this.
Together with NFT_TEST_FAIL_ON_SKIP=y is enabled, test names that match
the regex are allowed to be skipped. Unexpected skips are treated as
fatal. This allows to maintain a list of tests that are known to be
skipped.
You can think of this as some sort of XFAIL/XPASS mechanism. The
difference is that usually XFAIL is part of the test. Here the failure
happens due to external conditions, as such you need to configure
NFT_TEST_FAIL_ON_SKIP_EXCEPT= per kernel. Also, usually XFAIL is about
failing tests, while this is about tests that are marked to be skipped.
But we mark them as skipped due to a heuristic, and those we want to
manually keep track off.
Why is NFT_TEST_FAIL_ON_SKIP_EXCEPT= useful and why doesn't it just work
automatically? It does work automatically (see use case 1 below). Trust
the automatism to the right thing, and don't bother. This is when you
don't trust the automatism and you curate a list of tests that are known
to be be skipped, but no others (see use case 3 below). In particular,
it's for running CI on a downstream kernel, where we expect a static
list of skipped tests and where we want to find any changes.
Consider this: there are three use case for running the tests.
1) The contributor, who wants to run the test suite. The tests make a
best effort to pass and when the test detects that the failure is
likely due to the kernel, then it will skip the test. This is the
default.
2) The maintainer, who runs latest kernel and expects that all tests are
passing. Any SKIP is likely a bug. This is achieved by setting
NFT_TEST_FAIL_ON_SKIP=y.
3) The downstream maintainer, who test a distro kernel that is known to
lack certain features. They know that a selected few tests should be
skipped, but most tests should pass. By setting NFT_TEST_FAIL_ON_SKIP=y
and NFT_TEST_FAIL_ON_SKIP_EXCEPT= they can specify which are expected to
be skipped. The regex is kernel/environment specific, and must be
actively curated.
BONUS) actually, cases 2) and 3) optimally run automated CI tests.
Then the test environment is defined with a particular kernel variant
and changes slowly over time. NFT_TEST_FAIL_ON_SKIP_EXCEPT= allows
to pick up any unexpected changes of the skipped/pass behavior of
tests.
If a test matches the regex but passes, this is also flagged as a
failure ([XPASS]). The user is required to maintain an accurate list of
XFAIL tests.
Example:
$ NFT_TEST_FAIL_ON_SKIP=y \
NFT_TEST_FAIL_ON_SKIP_EXCEPT='^(tests/shell/testcases/nft-f/0017ct_timeout_obj_0|tests/shell/testcases/listing/0020flowtable_0)$' \
./tests/shell/run-tests.sh \
tests/shell/testcases/nft-f/0017ct_timeout_obj_0 \
tests/shell/testcases/cache/0006_cache_table_flush \
tests/shell/testcases/listing/0013objects_0 \
tests/shell/testcases/listing/0020flowtable_0
...
I: [SKIPPED] 1/4 tests/shell/testcases/nft-f/0017ct_timeout_obj_0
I: [OK] 2/4 tests/shell/testcases/cache/0006_cache_table_flush
W: [FAIL-SKIP] 3/4 tests/shell/testcases/listing/0013objects_0
W: [XPASS] 4/4 tests/shell/testcases/listing/0020flowtable_0
This list of XFAIL tests is maintainable, because on a particular
downstream kernel, the number of tests known to be skipped is small and
relatively static. Also, you can generate the list easily (followed by
manual verification!) via
$ NFT_TEST_FAIL_ON_SKIP=n ./tests/shell/run-tests.sh -k
$ export NFT_TEST_FAIL_ON_SKIP_EXCEPT="$(cat /tmp/nft-test.latest.*/skip-if-fail-except)"
$ NFT_TEST_FAIL_ON_SKIP=y ./tests/shell/run-tests.sh
Signed-off-by: Thomas Haller <thaller@redhat.com>
---
Sorry for the overly long commit message. I hope it can be useful and
speak in favor of the patch (and not against it).
This is related to the "eval-exit-code" patch, as it's about how to
handle tests that are SKIPed. But it's relevant for any skipped test,
and not tied to that work.
tests/shell/helpers/test-wrapper.sh | 41 +++++++++++++++++++++
tests/shell/run-tests.sh | 55 ++++++++++++++++++++++-------
2 files changed, 83 insertions(+), 13 deletions(-)
diff --git a/tests/shell/helpers/test-wrapper.sh b/tests/shell/helpers/test-wrapper.sh
index 13b918f8b8e1..e8edb7332d24 100755
--- a/tests/shell/helpers/test-wrapper.sh
+++ b/tests/shell/helpers/test-wrapper.sh
@@ -176,10 +176,49 @@ if [ "$tainted_before" != "$tainted_after" ] ; then
rc_tainted=1
fi
+rc_fail_on_skip=0
+rc_fail_on_skip_xpass=0
+if [ "$NFT_TEST_FAIL_ON_SKIP" = y ] ; then
+ if [ -n "${NFT_TEST_FAIL_ON_SKIP_EXCEPT+x}" ] ; then
+
+ OLD_IFS="$IFS"
+ IFS=$'\n'
+ REGEXES=( $NFT_TEST_FAIL_ON_SKIP_EXCEPT )
+ IFS="$OLD_IFST"
+
+ re_match=0
+ for re in "${REGEXES[@]}" ; do
+ if [[ "$TEST" =~ $re ]] ; then
+ re_match=1
+ break;
+ fi
+ done
+
+ if [ "$re_match" -eq 1 ] ; then
+ if [ "$rc_test" -eq 0 ] ; then
+ echo "test passed although expected to be skipped (XPASS) by regex NFT_TEST_FAIL_ON_SKIP_EXCEPT /$NFT_TEST_FAIL_ON_SKIP_EXCEPT/" > "$NFT_TEST_TESTTMPDIR/rc-failed-skip"
+ rc_fail_on_skip_xpass=1
+ fi
+ else
+ if [ "$rc_test" -eq 77 ] ; then
+ echo "upgrade skip to failure due to NFT_TEST_FAIL_ON_SKIP. Regex NFT_TEST_FAIL_ON_SKIP_EXCEPT /$NFT_TEST_FAIL_ON_SKIP_EXCEPT/ does not match" > "$NFT_TEST_TESTTMPDIR/rc-failed-skip"
+ rc_fail_on_skip=1
+ fi
+ fi
+ else
+ if [ "$rc_test" -eq 77 ] ; then
+ echo "upgrade skip to failure due to NFT_TEST_FAIL_ON_SKIP" > "$NFT_TEST_TESTTMPDIR/rc-failed-skip"
+ rc_fail_on_skip=1
+ fi
+ fi
+fi
+
if [ "$rc_valgrind" -ne 0 ] ; then
rc_exit=122
elif [ "$rc_tainted" -ne 0 ] ; then
rc_exit=123
+elif [ "$rc_fail_on_skip" -ne 0 ] ; then
+ rc_exit=120
elif [ "$rc_test" -ge 118 -a "$rc_test" -le 124 ] ; then
# Special exit codes are reserved. Coerce them.
rc_exit=125
@@ -189,6 +228,8 @@ elif [ "$rc_dump" -ne 0 ] ; then
rc_exit=124
elif [ "$rc_chkdump" -ne 0 ] ; then
rc_exit=121
+elif [ "$rc_fail_on_skip_xpass" -ne 0 ] ; then
+ rc_exit=119
else
rc_exit=0
fi
diff --git a/tests/shell/run-tests.sh b/tests/shell/run-tests.sh
index 22105c2e90e2..8fceb2795113 100755
--- a/tests/shell/run-tests.sh
+++ b/tests/shell/run-tests.sh
@@ -221,6 +221,10 @@ usage() {
echo " kernel modules)."
echo " Parallel jobs requires unshare and are disabled with NFT_TEST_UNSHARE_CMD=\"\"."
echo " NFT_TEST_FAIL_ON_SKIP=*|y: if any jobs are skipped, exit with error."
+ echo " NFT_TEST_FAIL_ON_SKIP_EXCEPT=<regex>: only honored with NFT_TEST_FAIL_ON_SKIP=y. This is a regex for"
+ echo " tests for which a skip is expected and not fatal (XFAIL). Multiple regex can be separated by"
+ echo " newline. This allows that skip a selected set of known tests, while all other tests must pass."
+ echo " If a test passes but matches the regex, it is treated as failure too (XPASS)."
echo " NFT_TEST_RANDOM_SEED=<SEED>: The test runner will export the environment variable NFT_TEST_RANDOM_SEED"
echo " set to a random number. This can be used as a stable seed for tests to randomize behavior."
echo " Set this to a fixed value to get reproducible behavior."
@@ -305,6 +309,8 @@ export NFT_TEST_RANDOM_SEED
TESTS=()
+TESTS_SKIP_EXCEPT=()
+
SETUP_HOST=
SETUP_HOST_OTHER=
@@ -639,6 +645,11 @@ msg_info "conf: NFT_TEST_HAS_UNSHARED_MOUNT=$(printf '%q' "$NFT_TEST_HAS_UNSHARE
msg_info "conf: NFT_TEST_KEEP_LOGS=$(printf '%q' "$NFT_TEST_KEEP_LOGS")"
msg_info "conf: NFT_TEST_JOBS=$NFT_TEST_JOBS"
msg_info "conf: NFT_TEST_FAIL_ON_SKIP=$NFT_TEST_FAIL_ON_SKIP"
+if [ -z "${NFT_TEST_FAIL_ON_SKIP_EXCEPT+x}" ] ; then
+ msg_info "conf: # NFT_TEST_FAIL_ON_SKIP_EXCEPT unset"
+else
+ msg_info "conf: NFT_TEST_FAIL_ON_SKIP_EXCEPT=$(printf '%q' "$NFT_TEST_FAIL_ON_SKIP_EXCEPT")"
+fi
msg_info "conf: NFT_TEST_RANDOM_SEED=$NFT_TEST_RANDOM_SEED"
msg_info "conf: NFT_TEST_SHUFFLE_TESTS=$NFT_TEST_SHUFFLE_TESTS"
msg_info "conf: TMPDIR=$(printf '%q' "$_TMPDIR")"
@@ -707,6 +718,7 @@ kernel_cleanup() {
echo ""
ok=0
skipped=0
+skipped_as_fail=0
failed=0
kmem_runs=0
@@ -767,7 +779,7 @@ print_test_header() {
align_text text right "${#s_idx}" "$testidx_completed"
s_idx="$text/${#TESTS[@]}"
- align_text text left 12 "[$status]"
+ align_text text left 13 "[$status]"
_msg "$msglevel" "$text $s_idx $testfile"
}
@@ -786,10 +798,19 @@ print_test_result() {
elif [ "$rc_got" -eq 77 ] ; then
((skipped++))
result_msg_status="${YELLOW}SKIPPED$RESET"
+ TESTS_SKIP_EXCEPT+=( "^$testfile$" )
else
((failed++))
result_msg_level="W"
- if [ "$rc_got" -eq 121 ] ; then
+ if [ "$rc_got" -eq 119 ] ; then
+ # This means, the test passed, but it was matched by NFT_TEST_FAIL_ON_SKIP_EXCEPT regex.
+ # The regex is expected to match *exactly* the tests that are skipped. An unexpected PASS
+ # is also treated as a failure. Inspect NFT_TEST_FAIL_ON_SKIP_EXCEPT regex.
+ result_msg_status="XPASS"
+ elif [ "$rc_got" -eq 120 ] ; then
+ ((skipped_as_fail++))
+ result_msg_status="FAIL-SKIP"
+ elif [ "$rc_got" -eq 121 ] ; then
result_msg_status="CHK DUMP"
elif [ "$rc_got" -eq 122 ] ; then
result_msg_status="VALGRIND"
@@ -831,8 +852,14 @@ job_start() {
print_test_header I "$testfile" "$testidx" "EXECUTING"
fi
+ # NFT_TEST_FAIL_ON_SKIP_EXCEPT is already exported, if it happens to be set.
NFT_TEST_TESTTMPDIR="${JOBS_TEMPDIR["$testfile"]}" \
- NFT="$NFT" NFT_REAL="$NFT_REAL" DIFF="$DIFF" DUMPGEN="$DUMPGEN" $NFT_TEST_UNSHARE_CMD "$NFT_TEST_BASEDIR/helpers/test-wrapper.sh" "$testfile"
+ NFT="$NFT" \
+ NFT_REAL="$NFT_REAL" \
+ DIFF="$DIFF" \
+ DUMPGEN="$DUMPGEN" \
+ NFT_TEST_FAIL_ON_SKIP="$NFT_TEST_FAIL_ON_SKIP" \
+ $NFT_TEST_UNSHARE_CMD "$NFT_TEST_BASEDIR/helpers/test-wrapper.sh" "$testfile"
local rc_got=$?
if [ "$NFT_TEST_JOBS" -le 1 ] ; then
@@ -896,12 +923,7 @@ echo ""
kmemleak_found=0
check_kmemleak_force
-failed_total="$failed"
-if [ "$NFT_TEST_FAIL_ON_SKIP" = y ] ; then
- failed_total="$((failed_total + skipped))"
-fi
-
-if [ "$failed_total" -gt 0 ] ; then
+if [ "$failed" -gt 0 ] ; then
RR="$RED"
elif [ "$skipped" -gt 0 ] ; then
RR="$YELLOW"
@@ -926,17 +948,24 @@ END_TIME="$(cut -d ' ' -f1 /proc/uptime)"
WALL_TIME="$(awk -v start="$START_TIME" -v end="$END_TIME" "BEGIN { print(end - start) }")"
printf "%s\n" "$WALL_TIME" "$START_TIME" "$END_TIME" > "$NFT_TEST_TMPDIR/times"
-if [ "$failed_total" -gt 0 -o "$NFT_TEST_KEEP_LOGS" = y ] ; then
+if [ "${#TESTS_SKIP_EXCEPT[@]}" -gt 0 ] ; then
+ printf '%s\n' "${TESTS_SKIP_EXCEPT[@]}" | sort
+fi > "$NFT_TEST_TMPDIR/skip-if-fail-except"
+
+if [ "$failed" -gt 0 -o "$NFT_TEST_KEEP_LOGS" = y ] ; then
msg_info "check the temp directory \"$NFT_TEST_TMPDIR\" (\"$NFT_TEST_LATEST\")"
msg_info " ls -lad \"$NFT_TEST_LATEST\"/*/*"
msg_info " grep -R ^ \"$NFT_TEST_LATEST\"/"
+ if [ "${#TESTS_SKIP_EXCEPT[@]}" -gt 0 ] ; then
+ msg_info " generate XFAIL with NFT_TEST_FAIL_ON_SKIP_EXCEPT=\"\$(cat $NFT_TEST_LATEST/skip-if-fail-except)\""
+ fi
NFT_TEST_TMPDIR=
fi
-if [ "$failed" -gt 0 ] ; then
+if [ "$failed" -gt 0 -a "$skipped_as_fail" -eq "$failed" ] ; then
+ msg_info "Tests were skipped, but failed due to NFT_TEST_FAIL_ON_SKIP=y"
exit 1
-elif [ "$NFT_TEST_FAIL_ON_SKIP" = y -a "$skipped" -gt 0 ] ; then
- msg_info "some tests were skipped. Fail due to NFT_TEST_FAIL_ON_SKIP=y"
+elif [ "$failed" -gt 0 ] ; then
exit 1
elif [ "$ok" -eq 0 -a "$skipped" -gt 0 ] ; then
exit 77
--
2.41.0
next reply other threads:[~2023-10-17 22:35 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-10-17 22:33 Thomas Haller [this message]
2023-10-18 9:33 ` [PATCH nft 1/1] tests/shell: add NFT_TEST_FAIL_ON_SKIP_EXCEPT for allow-list of skipped tests (XFAIL) Pablo Neira Ayuso
2023-10-18 11:40 ` Thomas Haller
2023-10-19 9:20 ` Florian Westphal
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=20231017223450.2613981-1-thaller@redhat.com \
--to=thaller@redhat.com \
--cc=netfilter-devel@vger.kernel.org \
/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;
as well as URLs for NNTP newsgroup(s).