1#!/bin/sh 2# 3# Copyright (c) 2011-2016 Dmitry V. Levin <ldv@altlinux.org> 4# All rights reserved. 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions 8# are met: 9# 1. Redistributions of source code must retain the above copyright 10# notice, this list of conditions and the following disclaimer. 11# 2. Redistributions in binary form must reproduce the above copyright 12# notice, this list of conditions and the following disclaimer in the 13# documentation and/or other materials provided with the distribution. 14# 3. The name of the author may not be used to endorse or promote products 15# derived from this software without specific prior written permission. 16# 17# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28ME_="${0##*/}" 29LOG="$ME_.tmp" 30OUT="$LOG.out" 31EXP="$LOG.exp" 32NAME="${ME_%.test}" 33 34warn_() { printf >&2 '%s\n' "$*"; } 35fail_() { warn_ "$ME_: failed test: $*"; exit 1; } 36skip_() { warn_ "$ME_: skipped test: $*"; exit 77; } 37framework_failure_() { warn_ "$ME_: framework failure: $*"; exit 99; } 38framework_skip_() { warn_ "$ME_: framework skip: $*"; exit 77; } 39 40check_prog() 41{ 42 type "$@" > /dev/null 2>&1 || 43 framework_skip_ "$* is not available" 44} 45 46dump_log_and_fail_with() 47{ 48 cat < "$LOG" 49 fail_ "$*" 50} 51 52run_prog() 53{ 54 if [ $# -eq 0 ]; then 55 set -- "./$NAME" 56 fi 57 args="$*" 58 "$@" || { 59 rc=$? 60 if [ $rc -eq 77 ]; then 61 skip_ "$args exited with code 77" 62 else 63 fail_ "$args failed with code $rc" 64 fi 65 } 66} 67 68 69run_prog_skip_if_failed() 70{ 71 args="$*" 72 "$@" || framework_skip_ "$args failed with code $?" 73} 74 75run_strace() 76{ 77 > "$LOG" || fail_ "failed to write $LOG" 78 args="$*" 79 $STRACE -o "$LOG" "$@" || 80 dump_log_and_fail_with "$STRACE $args failed with code $?" 81} 82 83run_strace_merge() 84{ 85 rm -f -- "$LOG".[0-9]* 86 run_strace -ff -tt "$@" 87 "$srcdir"/../strace-log-merge "$LOG" > "$LOG" || 88 dump_log_and_fail_with 'strace-log-merge failed with code $?' 89 rm -f -- "$LOG".[0-9]* 90} 91 92check_gawk() 93{ 94 check_prog gawk 95 check_prog grep 96 97 local program="$1"; shift 98 if grep '^@include[[:space:]]' < "$program" > /dev/null; then 99 gawk '@include "/dev/null"' < /dev/null || 100 framework_skip_ 'gawk does not support @include' 101 fi 102} 103 104# Usage: [FILE_TO_CHECK [AWK_PROGRAM [ERROR_MESSAGE [EXTRA_AWK_OPTIONS...]]]] 105# Check whether AWK_PROGRAM matches FILE_TO_CHECK using gawk. 106# If it doesn't, dump FILE_TO_CHECK and fail with ERROR_MESSAGE. 107match_awk() 108{ 109 local output program error 110 if [ $# -eq 0 ]; then 111 output="$LOG" 112 else 113 output="$1"; shift 114 fi 115 if [ $# -eq 0 ]; then 116 program="$srcdir/$NAME.awk" 117 else 118 program="$1"; shift 119 fi 120 if [ $# -eq 0 ]; then 121 error="$STRACE $args output mismatch" 122 else 123 error="$1"; shift 124 fi 125 126 check_gawk "$program" 127 128 AWKPATH="$srcdir" gawk -f "$program" "$@" < "$output" || { 129 cat < "$output" 130 fail_ "$error" 131 } 132} 133 134# Usage: [FILE_TO_CHECK [FILE_TO_COMPATE_WITH [ERROR_MESSAGE]]] 135# Check whether FILE_TO_CHECK differs from FILE_TO_COMPATE_WITH. 136# If it does, dump the difference and fail with ERROR_MESSAGE. 137match_diff() 138{ 139 local output expected error 140 if [ $# -eq 0 ]; then 141 output="$LOG" 142 else 143 output="$1"; shift 144 fi 145 if [ $# -eq 0 ]; then 146 expected="$srcdir/$NAME.expected" 147 else 148 expected="$1"; shift 149 fi 150 if [ $# -eq 0 ]; then 151 error="$STRACE $args output mismatch" 152 else 153 error="$1"; shift 154 fi 155 156 check_prog diff 157 158 diff -- "$expected" "$output" || 159 fail_ "$error" 160} 161 162# Usage: [FILE_TO_CHECK [FILE_WITH_PATTERNS [ERROR_MESSAGE]]] 163# Check whether all patterns listed in FILE_WITH_PATTERNS 164# match FILE_TO_CHECK using egrep. 165# If at least one of these patterns does not match, 166# dump both files and fail with ERROR_MESSAGE. 167match_grep() 168{ 169 local output patterns error pattern cnt failed= 170 if [ $# -eq 0 ]; then 171 output="$LOG" 172 else 173 output="$1"; shift 174 fi 175 if [ $# -eq 0 ]; then 176 patterns="$srcdir/$NAME.expected" 177 else 178 patterns="$1"; shift 179 fi 180 if [ $# -eq 0 ]; then 181 error="$STRACE $args output mismatch" 182 else 183 error="$1"; shift 184 fi 185 186 check_prog wc 187 check_prog grep 188 189 cnt=1 190 while read -r pattern; do 191 LC_ALL=C grep -E -x -e "$pattern" < "$output" > /dev/null || { 192 test -n "$failed" || { 193 echo 'Failed patterns of expected output:' 194 failed=1 195 } 196 printf '#%d: %s\n' "$cnt" "$pattern" 197 } 198 cnt=$(($cnt + 1)) 199 done < "$patterns" 200 test -z "$failed" || { 201 echo 'Actual output:' 202 cat < "$output" 203 fail_ "$error" 204 } 205} 206 207# Usage: run_strace_match_diff [args to run_strace] 208run_strace_match_diff() 209{ 210 args="$*" 211 [ -n "$args" -a -z "${args##*-e trace=*}" ] || 212 set -- -e trace="$NAME" "$@" 213 run_prog > /dev/null 214 run_strace "$@" $args > "$EXP" 215 match_diff "$LOG" "$EXP" 216 rm -f "$EXP" 217} 218 219# Print kernel version code. 220# usage: kernel_version_code $(uname -r) 221kernel_version_code() 222{ 223 ( 224 set -f 225 IFS=. 226 set -- $1 227 v1="${1%%[!0-9]*}" && [ -n "$v1" ] || v1=0 228 v2="${2%%[!0-9]*}" && [ -n "$v2" ] || v2=0 229 v3="${3%%[!0-9]*}" && [ -n "$v3" ] || v3=0 230 echo "$(($v1 * 65536 + $v2 * 256 + $v3))" 231 ) 232} 233 234# Usage: require_min_kernel_version_or_skip 3.0 235require_min_kernel_version_or_skip() 236{ 237 local uname_r 238 uname_r="$(uname -r)" 239 240 [ "$(kernel_version_code "$uname_r")" -ge \ 241 "$(kernel_version_code "$1")" ] || 242 skip_ "the kernel release $uname_r is not $1 or newer" 243} 244 245# Usage: grep_pid_status $pid GREP-OPTIONS... 246grep_pid_status() 247{ 248 local pid 249 pid=$1; shift 250 cat < "/proc/$pid/status" | grep "$@" 251} 252 253check_prog cat 254check_prog rm 255 256rm -f "$LOG" 257 258[ -n "${STRACE-}" ] || { 259 STRACE=../strace 260 case "${LOG_COMPILER-} ${LOG_FLAGS-}" in 261 *--suppressions=*--error-exitcode=*--tool=*) 262 # add valgrind command prefix 263 STRACE="${LOG_COMPILER-} ${LOG_FLAGS-} $STRACE" 264 ;; 265 esac 266} 267 268: "${TIMEOUT_DURATION:=60}" 269: "${SLEEP_A_BIT:=sleep 1}" 270 271[ -z "${VERBOSE-}" ] || 272 set -x 273