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