xref: /linux/tools/objtool/tests/run-tests.sh (revision 5b6dca02b3e2254d2fd5706d76d7ab4610a67296)
1cff27d90SPuranjay Mohan#!/bin/bash
2cff27d90SPuranjay Mohan# SPDX-License-Identifier: GPL-2.0
3cff27d90SPuranjay Mohan#
4cff27d90SPuranjay Mohan# Run the objtool klp tests.  Each test-*.sh prints one TAP result line.
5c009fabcSSong Liu#
6d76312b8SSong Liu# Tests live in generic/ and in a directory per architecture.  A run executes
7d76312b8SSong Liu# generic/ plus the one matching this architecture, so a test which cannot
8d76312b8SSong Liu# apply here is not run rather than reporting a skip; what was left out is
9d76312b8SSong Liu# reported once, as a comment, so differing coverage is still visible.
10d76312b8SSong Liu#
11317cec90SSong Liu# A run covers one compiler and one architecture; CI runs the combinations.
12c009fabcSSong Liu# The harness checks the environment once up front and fails the run if the
13c009fabcSSong Liu# suite cannot execute, rather than letting every test skip and exit 0.
14cff27d90SPuranjay Mohan
15cff27d90SPuranjay Mohanset -u
16cff27d90SPuranjay Mohan
17317cec90SSong Liu# Determinism: the order test-*.sh expands in, and how grep's character ranges
18317cec90SSong Liu# and sort's collation behave inside the tests, are all locale-dependent.  A
19317cec90SSong Liu# suite whose results depend on the invoking shell's locale is a suite whose
20317cec90SSong Liu# failures cannot be reproduced.
21317cec90SSong Liuexport LC_ALL=C
22317cec90SSong Liu
23317cec90SSong Liuusage()
24317cec90SSong Liu{
25317cec90SSong Liu	cat <<EOF
268593055bSSong Liuusage: $(basename "$0") [-k|--keep] [test...]
27317cec90SSong Liu
28317cec90SSong LiuRun the objtool klp tests for this architecture: everything in generic/, plus
29317cec90SSong Liueverything in the directory named for it.  With no arguments, runs all of them.
30317cec90SSong LiuA test may be named with or without its "test-" prefix and ".sh" suffix, and is
31317cec90SSong Liulooked for in both directories.
32317cec90SSong Liu
338593055bSSong LiuOptions:
34*9b11acaaSJoe Lawrence    -k, --keep    same as KEEP=all (see below)
358593055bSSong Liu
36317cec90SSong LiuEnvironment:
37317cec90SSong Liu    OBJTOOL       objtool binary to test (default ../objtool)
38317cec90SSong Liu    CC            compiler used to build fixtures (default gcc)
39317cec90SSong Liu    ARCH          architecture the tests are for (default: uname -m)
40*9b11acaaSJoe Lawrence    KEEP          failed  keep only failing tests (default)
41*9b11acaaSJoe Lawrence                  all     keep every test's working directory
42*9b11acaaSJoe Lawrence                  none    remove all working directories
43317cec90SSong Liu
44317cec90SSong LiuA test which needs something of its own says so in its skip message.
45317cec90SSong LiuEOF
46317cec90SSong Liu	exit "${1:-0}"
47317cec90SSong Liu}
48317cec90SSong Liu
49cff27d90SPuranjay Mohancd "$(dirname "$0")" || exit 1
50cff27d90SPuranjay Mohan
51*9b11acaaSJoe Lawrencekeep_from_args=
52317cec90SSong Liuwhile [ $# -gt 0 ]; do
53317cec90SSong Liu	case "$1" in
54317cec90SSong Liu	-h|--help)	usage ;;
55*9b11acaaSJoe Lawrence	-k|--keep)	keep_from_args=all; shift ;;
56317cec90SSong Liu	--)		shift; break ;;
57317cec90SSong Liu	-*)		echo "unknown option: $1" >&2; usage 1 ;;
58317cec90SSong Liu	*)		break ;;
59317cec90SSong Liu	esac
60317cec90SSong Liudone
61317cec90SSong Liu
62*9b11acaaSJoe LawrenceKLP_TEST_KEEP="${KEEP:-failed}"
63*9b11acaaSJoe Lawrence[ -n "$keep_from_args" ] && KLP_TEST_KEEP="$keep_from_args"
64*9b11acaaSJoe Lawrencecase "$KLP_TEST_KEEP" in
65*9b11acaaSJoe Lawrenceall|none|failed) ;;
66*9b11acaaSJoe Lawrence*)
67*9b11acaaSJoe Lawrence	echo "invalid KEEP=$KLP_TEST_KEEP (want failed, all, or none)" >&2
68*9b11acaaSJoe Lawrence	exit 1
69*9b11acaaSJoe Lawrence	;;
70*9b11acaaSJoe Lawrenceesac
71*9b11acaaSJoe Lawrenceexport KLP_TEST_KEEP
72*9b11acaaSJoe Lawrence
73317cec90SSong Liuecho "TAP version 13"
74317cec90SSong Liu
75d76312b8SSong Liu# Sourcing the harness runs its preflight, which decides which architecture
76d76312b8SSong Liu# this run is for -- so the test list cannot be built before it has, and the
77d76312b8SSong Liu# tests inherit the answers rather than working them out again.
78c009fabcSSong Liu. ./lib.sh
79c009fabcSSong Liu
80d76312b8SSong Liudirs=( generic )
81d76312b8SSong Liu[ -d "$KLP_TEST_ARCH" ] && dirs+=( "$KLP_TEST_ARCH" )
82d76312b8SSong Liu
83317cec90SSong Liuif [ $# -gt 0 ]; then
84317cec90SSong Liu	tests=()
85317cec90SSong Liu	for arg in "$@"; do
86317cec90SSong Liu		name="test-${arg#test-}"; name="${name%.sh}.sh"
87317cec90SSong Liu		found=
88317cec90SSong Liu		for d in "${dirs[@]}"; do
89317cec90SSong Liu			[ -f "$d/$name" ] || continue
90317cec90SSong Liu			[ -x "$d/$name" ] ||
91317cec90SSong Liu				{ echo "not executable: $d/$name" >&2; exit 1; }
92317cec90SSong Liu			tests+=( "$d/$name" ); found=y
93317cec90SSong Liu		done
94317cec90SSong Liu		[ -n "$found" ] ||
95317cec90SSong Liu			{ echo "no such test for $KLP_TEST_ARCH: $arg" >&2; exit 1; }
96317cec90SSong Liu	done
97317cec90SSong Liuelse
98d76312b8SSong Liu	tests=()
99d76312b8SSong Liu	for d in "${dirs[@]}"; do
100d76312b8SSong Liu		for t in "$d"/test-*.sh; do
101d76312b8SSong Liu			[ -f "$t" ] && tests+=( "$t" )
102d76312b8SSong Liu		done
103d76312b8SSong Liu	done
104317cec90SSong Liu	[ "${#tests[@]}" -gt 0 ] ||
105317cec90SSong Liu		{ echo "1..0 # SKIP no tests found"; exit 0; }
106d76312b8SSong Liu
107317cec90SSong Liu	# Tests for another architecture are absent from this run entirely.  Say
108317cec90SSong Liu	# how many, so a run which covers less than the tree holds does not look
109317cec90SSong Liu	# like one that covers all of it.
110d76312b8SSong Liu	for d in */; do
111d76312b8SSong Liu		d="${d%/}"
112d76312b8SSong Liu		case "$d" in generic|"$KLP_TEST_ARCH") continue ;; esac
113d76312b8SSong Liu		n=$(ls "$d"/test-*.sh 2>/dev/null | wc -l)
114d76312b8SSong Liu		[ "$n" -gt 0 ] || continue
115d76312b8SSong Liu		echo "# not run: $n test$( [ "$n" = 1 ] || echo s ) in $d/" \
116d76312b8SSong Liu		     "(this run is $KLP_TEST_ARCH)"
117d76312b8SSong Liu	done
118317cec90SSong Liufi
119d76312b8SSong Liu
1208593055bSSong Liu# One directory for the whole run, one per test inside it, mirroring the
1218593055bSSong Liu# source layout.  A run then leaves a single thing behind instead of 39
1228593055bSSong Liu# scattered among everything else using mktemp.
1238593055bSSong Liurundir="$(mktemp -d "${TMPDIR:-/tmp}/klp-tests.XXXXXXXX")" ||
1248593055bSSong Liu	{ echo "Bail out! cannot create a working directory" >&2; exit 1; }
1258593055bSSong Liu
126*9b11acaaSJoe Lawrence# The run's own two levels: each test's directory, and the one per source
127*9b11acaaSJoe Lawrence# directory holding them.  Take them away if the tests left them empty, and
128*9b11acaaSJoe Lawrence# say so if they did not.  Either rmdir may fail -- the glob stays unexpanded
129*9b11acaaSJoe Lawrence# when nothing was created -- so ask the directory itself rather than trusting
130*9b11acaaSJoe Lawrence# the status.  Never rm -rf: what to keep is the tests' decision, made in
131*9b11acaaSJoe Lawrence# cleanup() as each one exits, and this must not overrule it.
132*9b11acaaSJoe Lawrencereap_rundir()
133*9b11acaaSJoe Lawrence{
134*9b11acaaSJoe Lawrence	rmdir "$rundir"/*/ 2>/dev/null
135*9b11acaaSJoe Lawrence	rmdir "$rundir" 2>/dev/null
136*9b11acaaSJoe Lawrence	[ -d "$rundir" ]
137*9b11acaaSJoe Lawrence}
138*9b11acaaSJoe Lawrence
139*9b11acaaSJoe Lawrence# An interrupted run has the same directory to answer for, and the tests it
140*9b11acaaSJoe Lawrence# never reached will not clean up on their way out.  The one it was running
141*9b11acaaSJoe Lawrence# has, and under the default it kept what it had built, so say where.
142*9b11acaaSJoe Lawrenceinterrupted()
143*9b11acaaSJoe Lawrence{
144*9b11acaaSJoe Lawrence	reap_rundir && echo "# interrupted; what was built is in $rundir"
145*9b11acaaSJoe Lawrence	exit 130
146*9b11acaaSJoe Lawrence}
147*9b11acaaSJoe Lawrencetrap interrupted INT TERM HUP
148*9b11acaaSJoe Lawrence
149cff27d90SPuranjay Mohanecho "1..${#tests[@]}"
150cff27d90SPuranjay Mohan
151317cec90SSong Liupass=0 fail=0 static_skip=0 probe_skip=0 xfail=0 xpass=0
152*9b11acaaSJoe Lawrencefailed_dirs=()
153317cec90SSong Liu
154cff27d90SPuranjay Mohanfor t in "${tests[@]}"; do
1558593055bSSong Liu	out="$(KLP_TEST_WORKDIR="$rundir/${t%.sh}" ./"$t" 2>&1)"
156317cec90SSong Liu	rc=$?
157317cec90SSong Liu
158317cec90SSong Liu	# A test prints one result line, but it is not necessarily the only
159317cec90SSong Liu	# thing it prints: objtool warns on stderr, and the runner captures
160317cec90SSong Liu	# that.  Classify the result line itself rather than the whole of the
161317cec90SSong Liu	# output, or a stray line ahead of it makes every pattern below miss and
162317cec90SSong Liu	# the exit status decide -- which would count an expected failure, which
163317cec90SSong Liu	# exits 0, as a pass.
164317cec90SSong Liu	result="$(printf '%s\n' "$out" | grep -E '^(ok|not ok)' | tail -1)"
165317cec90SSong Liu	rest="$(printf '%s\n' "$out" | grep -Ev '^(ok|not ok)')"
166317cec90SSong Liu
167317cec90SSong Liu	# Classify from the result line, not the exit status: a skip and a pass
168317cec90SSong Liu	# both exit 0, and telling them apart is the point of counting.
169317cec90SSong Liu	#
170317cec90SSong Liu	# The two skip kinds differ in what they promise.  A static skip was
171317cec90SSong Liu	# declared before the test ran ("clang does not do this"), so it is
172317cec90SSong Liu	# expected indefinitely.  A probe skip means the construct did not turn
173317cec90SSong Liu	# up this time, which is weaker and worth watching: one that becomes
174317cec90SSong Liu	# permanent is a fixture that quietly stopped testing anything.
175317cec90SSong Liu	case "$result" in
176317cec90SSong Liu	*"# SKIP (declared)"*)	static_skip=$((static_skip + 1)) ;;
177317cec90SSong Liu	*"# SKIP (probe)"*)	probe_skip=$((probe_skip + 1)) ;;
178317cec90SSong Liu	*"# SKIP"*)
179317cec90SSong Liu		# An undeclared skip: the test gave up for a reason it never
180317cec90SSong Liu		# said it might.  That is a hole, not an expected outcome.
181317cec90SSong Liu		#
182317cec90SSong Liu		# Replace the line rather than adding one.  Every test owes the
183317cec90SSong Liu		# plan exactly one result, and a consumer counting them is
184317cec90SSong Liu		# entitled to say so when the totals disagree.
185317cec90SSong Liu		rest="$rest${rest:+$'\n'}was: $result"
186317cec90SSong Liu		result="not ok - $(basename "$t" .sh): undeclared skip"
187317cec90SSong Liu		result="$result (use gcc_only/clang_only or require_input_*)"
188*9b11acaaSJoe Lawrence		fail=$((fail + 1)); failed_dirs+=( "$rundir/${t%.sh}" ) ;;
189317cec90SSong Liu	"not ok"*"# TODO"*)	xfail=$((xfail + 1)) ;;
190*9b11acaaSJoe Lawrence	"ok"*"# TODO"*)		xpass=$((xpass + 1)); failed_dirs+=( "$rundir/${t%.sh}" ) ;;
191*9b11acaaSJoe Lawrence	"not ok"*)		fail=$((fail + 1)); failed_dirs+=( "$rundir/${t%.sh}" ) ;;
192317cec90SSong Liu	"ok"*)			pass=$((pass + 1)) ;;
193317cec90SSong Liu	*)
194317cec90SSong Liu		# No result line at all: the test died before reporting.
195317cec90SSong Liu		rest="$rest${rest:+$'\n'}exited $rc without a result line"
196317cec90SSong Liu		result="not ok - $(basename "$t" .sh): no TAP result"
197*9b11acaaSJoe Lawrence		fail=$((fail + 1)); failed_dirs+=( "$rundir/${t%.sh}" ) ;;
198317cec90SSong Liu	esac
199317cec90SSong Liu
200317cec90SSong Liu	echo "$result"
201317cec90SSong Liu	[ -n "$rest" ] && printf '%s\n' "$rest" | sed 's/^[^#]/# &/'
202317cec90SSong Liu
203cff27d90SPuranjay Mohandone
204cff27d90SPuranjay Mohan
205317cec90SSong Liuecho "# pass:$pass fail:$fail static-skip:$static_skip" \
206317cec90SSong Liu     "probe-skip:$probe_skip xfail:$xfail xpass:$xpass"
207317cec90SSong Liu
208*9b11acaaSJoe Lawrencecase "$KLP_TEST_KEEP" in
209*9b11acaaSJoe Lawrenceall)
210*9b11acaaSJoe Lawrence	echo "# keep=all: workdirs kept in $rundir"
211*9b11acaaSJoe Lawrence	echo "# inspect: diff.log, readelf -S out.o under each test-* subdirectory"
212*9b11acaaSJoe Lawrence	echo "# cleanup: rm -rf $rundir"
213*9b11acaaSJoe Lawrence	;;
214*9b11acaaSJoe Lawrencefailed)
215*9b11acaaSJoe Lawrence	if [ "${#failed_dirs[@]}" -gt 0 ]; then
216*9b11acaaSJoe Lawrence		echo "# keep=failed: ${#failed_dirs[@]} failing test(s) kept under $rundir:"
217*9b11acaaSJoe Lawrence		for d in "${failed_dirs[@]}"; do
218*9b11acaaSJoe Lawrence			echo "#   ${d#"$rundir"/}/"
219*9b11acaaSJoe Lawrence		done
220*9b11acaaSJoe Lawrence		echo "# inspect: diff.log  readelf -S out.o"
221*9b11acaaSJoe Lawrence		echo "# one test: $PWD/run-tests.sh <name>"
222*9b11acaaSJoe Lawrence		echo "# cleanup: rm -rf $rundir"
223*9b11acaaSJoe Lawrence	elif reap_rundir; then
224*9b11acaaSJoe Lawrence		echo "# $rundir was not empty;" \
225*9b11acaaSJoe Lawrence		     "a test did not clean up after itself"
2268593055bSSong Liu	fi
227*9b11acaaSJoe Lawrence	;;
228*9b11acaaSJoe Lawrencenone)
229*9b11acaaSJoe Lawrence	if reap_rundir; then
230*9b11acaaSJoe Lawrence		echo "# $rundir was not empty;" \
231*9b11acaaSJoe Lawrence		     "a test did not clean up after itself"
232*9b11acaaSJoe Lawrence	elif [ "$fail" != 0 ] || [ "$xpass" != 0 ]; then
233*9b11acaaSJoe Lawrence		echo "# keep=none: artifacts were removed" \
234*9b11acaaSJoe Lawrence		     "(re-run with KEEP=failed or KEEP=all)"
235*9b11acaaSJoe Lawrence	fi
236*9b11acaaSJoe Lawrence	;;
237*9b11acaaSJoe Lawrenceesac
2388593055bSSong Liu
239317cec90SSong Liu[ "$fail" = 0 ] && [ "$xpass" = 0 ]
240