123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606 |
- #!/bin/sh
- #set -x
- cd $(dirname "$0")
- default_path=".."
- default_cmd="sparse \$file"
- default_args="$SPARSE_TEST_ARGS"
- tests_list=""
- prog_name=`basename $0`
- if [ ! -x "$default_path/sparse-llvm" ]; then
- disabled_cmds="sparsec sparsei sparse-llvm sparse-llvm-dis"
- fi
- # flags:
- # - some tests gave an unexpected result
- failed=0
- # counts:
- # - tests that have not been converted to test-suite format
- # - tests that are disabled
- # - tests that passed
- # - tests that failed
- # - tests that failed but are known to fail
- unhandled_tests=0
- disabled_tests=0
- ok_tests=0
- ko_tests=0
- known_ko_tests=0
- # defaults to not verbose
- [ -z "$V" ] && V=0
- vquiet=""
- quiet=0
- abort=0
- ##
- # verbose(string) - prints string if we are in verbose mode
- verbose()
- {
- [ "$V" -eq "1" ] && echo " $1"
- return 0
- }
- ##
- # warning(string) - prints a warning
- warning()
- {
- [ "$quiet" -ne 1 ] && echo "warning: $1"
- return 0
- }
- ##
- # error(string[, die]) - prints an error and exits with value die if given
- error()
- {
- [ "$quiet" -ne 1 ] && echo "error: $1"
- [ -n "$2" ] && exit $2
- return 0
- }
- ##
- # get_tag_value(file) - get the 'check-<...>' tags & values
- get_tag_value()
- {
- check_name=""
- check_command="$default_cmd"
- check_exit_value=0
- check_timeout=0
- check_known_to_fail=0
- check_error_ignore=0
- check_output_ignore=0
- check_output_contains=0
- check_output_excludes=0
- check_output_pattern=0
- check_arch_ignore=""
- check_arch_only=""
- check_assert=""
- check_cpp_if=""
- lines=$(grep 'check-[a-z-]*' $1 | \
- sed -e 's/^.*\(check-[a-z-]*:*\) *\(.*\)$/\1 \2/')
- while read tag val; do
- #echo "-> tag: '$tag'"
- #echo "-> val: '$val'"
- case $tag in
- check-name:) check_name="$val" ;;
- check-command:) check_command="$val" ;;
- check-exit-value:) check_exit_value="$val" ;;
- check-timeout:) [ -z "$val" ] && val=1
- check_timeout="$val" ;;
- check-known-to-fail) check_known_to_fail=1 ;;
- check-error-ignore) check_error_ignore=1 ;;
- check-output-ignore) check_output_ignore=1 ;;
- check-output-contains:) check_output_contains=1 ;;
- check-output-excludes:) check_output_excludes=1 ;;
- check-output-pattern) check_output_pattern=1 ;;
- check-arch-ignore:) arch=$(uname -m)
- check_arch_ignore="$val" ;;
- check-arch-only:) arch=$(uname -m)
- check_arch_only="$val" ;;
- check-assert:) check_assert="$val" ;;
- check-cpp-if:) check_cpp_if="$val" ;;
- check-description:) ;; # ignore
- check-note:) ;; # ignore
- check-warning:) ;; # ignore
- check-error-start) ;; # ignore
- check-error-end) ;; # ignore
- check-output-start) ;; # ignore
- check-output-end) ;; # ignore
- check-should-pass) ;; # ignore, unused annotation
- check-should-fail) ;; # ignore, unused annotation
- check-should-warn) ;; # ignore, unused annotation
- check-*) error "$1: unknown tag '$tag'" 1 ;;
- esac
- done << EOT
- $lines
- EOT
- }
- ##
- # helper for has_(each|none)_patterns()
- has_patterns()
- {
- ifile="$1"
- patt="$2"
- ofile="$3"
- cmp="$4"
- msg="$5"
- grep "$patt:" "$ifile" | \
- sed -e "s/^.*$patt: *\(.*\)$/\1/" | \
- while read val; do
- grep -s -q "$val" "$ofile"
- if [ "$?" $cmp 0 ]; then
- error " Pattern '$val' unexpectedly $msg"
- return 1
- fi
- done
- return $?
- }
- ##
- # has_each_patterns(ifile tag ofile) - does ofile contains some
- # of the patterns given by ifile's tags?
- #
- # returns 0 if all present, 1 otherwise
- has_each_patterns()
- {
- has_patterns "$1" "$2" "$4" -ne "$3"
- }
- ##
- # has_none_patterns(ifile tag ofile) - does ofile contains some
- # of the patterns given by ifile's tags?
- #
- # returns 1 if any present, 0 otherwise
- has_none_patterns()
- {
- has_patterns "$1" "$2" "$4" -eq "$3"
- }
- ##
- # minmax_patterns(ifile tag ofile) - does ofile contains the
- # the patterns given by ifile's tags
- # the right number of time?
- minmax_patterns()
- {
- ifile="$1"
- patt="$2"
- ofile="$3"
- grep "$patt([0-9-]*\(, *\)*[0-9-]*):" "$ifile" | \
- sed -e "s/^.*$patt(\([0-9]*\)): *\(.*\)/\1 eq \2/" \
- -e "s/^.*$patt(\([0-9-]*\), *\([0-9-]*\)): *\(.*\)/\1 \2 \3/" | \
- while read min max pat; do
- n=$(grep -s "$pat" "$ofile" | wc -l)
- if [ "$max" = "eq" ]; then
- if [ "$n" -ne "$min" ]; then
- error " Pattern '$pat' expected $min times but got $n times"
- return 1
- fi
- continue
- fi
- if [ "$min" != '-' ]; then
- if [ "$n" -lt "$min" ]; then
- error " Pattern '$pat' expected min $min times but got $n times"
- return 1
- fi
- fi
- if [ "$max" != '-' ]; then
- if [ "$n" -gt "$max" ]; then
- error " Pattern '$pat' expected max $max times but got $n times"
- return 1
- fi
- fi
- done
- return $?
- }
- ##
- # arg_file(filename) - checks if filename exists
- arg_file()
- {
- [ -z "$1" ] && {
- do_usage
- exit 1
- }
- [ -e "$1" ] || {
- error "Can't open file $1"
- exit 1
- }
- return 0
- }
- ##
- do_usage()
- {
- echo "$prog_name - a tiny automatic testing script"
- echo "Usage: $prog_name [option(s)] [command] [arguments]"
- echo
- echo "options:"
- echo " -a|--abort Abort the tests as soon as one fails."
- echo " -q|--quiet Be extra quiet while running the tests."
- echo " --args='...' Add these options to the test command."
- echo
- echo "commands:"
- echo " [file ...] Runs the test suite on the given file(s)."
- echo " If a directory is given, run only those files."
- echo " If no file is given, run the whole testsuite."
- echo " single file Run the test in 'file'."
- echo " format file [name [cmd]] Help writing a new test case using cmd."
- echo
- echo " [command] help Print usage."
- }
- disable()
- {
- disabled_tests=$(($disabled_tests + 1))
- if [ -z "$vquiet" ]; then
- echo " SKIP $1 ($2)"
- fi
- }
- ##
- # do_test(file) - tries to validate a test case
- #
- # it "parses" file, looking for check-* tags and tries to validate
- # the test against an expected result
- # returns:
- # - 0 if the test passed,
- # - 1 if it failed,
- # - 2 if it is not a "test-suite" test.
- # - 3 if the test is disabled.
- do_test()
- {
- test_failed=0
- file="$1"
- quiet=0
- get_tag_value $file
- # can this test be handled by test-suite ?
- # (it has to have a check-name key in it)
- if [ "$check_name" = "" ]; then
- warning "$file: test unhandled"
- unhandled_tests=$(($unhandled_tests + 1))
- return 2
- fi
- test_name="$check_name"
- # does the test provide a specific command ?
- if [ "$check_command" = "" ]; then
- check_command="$defaut_command"
- fi
- # check for disabled commands
- set -- $check_command
- base_cmd=$1
- for i in $disabled_cmds; do
- if [ "$i" = "$base_cmd" ] ; then
- disable "$test_name" "$file"
- return 3
- fi
- done
- if [ "$check_arch_ignore" != "" ]; then
- if echo $arch | egrep -q -w "$check_arch_ignore"; then
- disable "$test_name" "$file"
- return 3
- fi
- fi
- if [ "$check_arch_only" != "" ]; then
- if ! (echo $arch | egrep -q -w "$check_arch_only"); then
- disable "$test_name" "$file"
- return 3
- fi
- fi
- if [ "$check_assert" != "" ]; then
- res=$(../sparse - 2>&1 >/dev/null <<- EOF
- _Static_assert($check_assert, "$check_assert");
- EOF
- )
- if [ "$res" != "" ]; then
- disable "$test_name" "$file"
- return 3
- fi
- fi
- if [ "$check_cpp_if" != "" ]; then
- res=$(../sparse -E - 2>/dev/null <<- EOF
- #if !($check_cpp_if)
- fail
- #endif
- EOF
- )
- if [ "$res" != "" ]; then
- disable "$test_name" "$file"
- return 3
- fi
- fi
- if [ -z "$vquiet" ]; then
- echo " TEST $test_name ($file)"
- fi
- verbose "Using command : $(echo "$@")"
- # grab the expected exit value
- expected_exit_value=$check_exit_value
- verbose "Expecting exit value: $expected_exit_value"
- # do we want a timeout?
- pre_cmd=""
- if [ $check_timeout -ne 0 ]; then
- pre_cmd="timeout $check_timeout"
- fi
- shift
- # launch the test command and
- # grab the actual output & exit value
- eval $pre_cmd $default_path/$base_cmd $default_args "$@" \
- 1> $file.output.got 2> $file.error.got
- actual_exit_value=$?
- must_fail=$check_known_to_fail
- [ $must_fail -eq 1 ] && [ $V -eq 0 ] && quiet=1
- known_ko_tests=$(($known_ko_tests + $must_fail))
- for stream in error output; do
- eval ignore=\$check_${stream}_ignore
- [ $ignore -eq 1 ] && continue
- # grab the expected output
- sed -n "/check-$stream-start/,/check-$stream-end/p" $file \
- | grep -v check-$stream > "$file".$stream.expected
- diff -u "$file".$stream.expected "$file".$stream.got > "$file".$stream.diff
- if [ "$?" -ne "0" ]; then
- error "actual $stream text does not match expected $stream text."
- error "see $file.$stream.* for further investigation."
- [ $quiet -ne 1 ] && cat "$file".$stream.diff
- test_failed=1
- fi
- done
- if [ "$actual_exit_value" -ne "$expected_exit_value" ]; then
- error "Actual exit value does not match the expected one."
- error "expected $expected_exit_value, got $actual_exit_value."
- test_failed=1
- fi
- # verify the 'check-output-contains/excludes' tags
- if [ $check_output_contains -eq 1 ]; then
- has_each_patterns "$file" 'check-output-contains' absent $file.output.got
- if [ "$?" -ne "0" ]; then
- test_failed=1
- fi
- fi
- if [ $check_output_excludes -eq 1 ]; then
- has_none_patterns "$file" 'check-output-excludes' present $file.output.got
- if [ "$?" -ne "0" ]; then
- test_failed=1
- fi
- fi
- if [ $check_output_pattern -eq 1 ]; then
- # verify the 'check-output-pattern(...)' tags
- minmax_patterns "$file" 'check-output-pattern' $file.output.got
- if [ "$?" -ne "0" ]; then
- test_failed=1
- fi
- fi
- if [ "$must_fail" -eq "1" ]; then
- if [ "$test_failed" -eq "1" ]; then
- [ -z "$vquiet" ] && \
- echo "info: XFAIL: test '$file' is known to fail"
- else
- echo "error: XPASS: test '$file' is known to fail but succeed!"
- fi
- else
- if [ "$test_failed" -eq "1" ]; then
- echo "error: FAIL: test '$file' failed"
- else
- [ "$V" -ne "0" ] && \
- echo "info: PASS: test '$file' passed"
- fi
- fi
- if [ "$test_failed" -ne "$must_fail" ]; then
- [ $abort -eq 1 ] && exit 1
- test_failed=1
- failed=1
- fi
- if [ "$test_failed" -eq "1" ]; then
- ko_tests=$(($ko_tests + 1))
- else
- ok_tests=$(($ok_tests + 1))
- rm -f $file.{error,output}.{expected,got,diff}
- fi
- return $test_failed
- }
- do_test_suite()
- {
- for i in $tests_list; do
- do_test "$i"
- done
- OK=OK
- [ $failed -eq 0 ] || OK=KO
- # prints some numbers
- tests_nr=$(($ok_tests + $ko_tests))
- echo "$OK: out of $tests_nr tests, $ok_tests passed, $ko_tests failed"
- if [ "$known_ko_tests" -ne 0 ]; then
- echo " $known_ko_tests of them are known to fail"
- fi
- if [ "$unhandled_tests" -ne "0" ]; then
- echo " $unhandled_tests tests could not be handled by $prog_name"
- fi
- if [ "$disabled_tests" -ne "0" ]; then
- echo " $disabled_tests tests were disabled"
- fi
- }
- ##
- do_format_help() {
- echo "Usage: $prog_name [option(s)] [--]format file [name [cmd]]"
- echo
- echo "options:"
- echo " -a append the created test to the input file"
- echo " -f write a test known to fail"
- echo " -l write a test for linearized code"
- echo " -p write a test for pre-processing"
- echo
- echo "argument(s):"
- echo " file file containing the test case(s)"
- echo " name name for the test case (defaults to file)"
- echo " cmd command to be used (defaults to 'sparse \$file')"
- }
- ##
- # do_format([options,] file[, name[, cmd]]) - helps a test writer to format test-suite tags
- do_format()
- {
- def_cmd="$default_cmd"
- append=0
- linear=0
- fail=0
- while [ $# -gt 0 ] ; do
- case "$1" in
- -a)
- append=1 ;;
- -f)
- fail=1 ;;
- -l)
- def_cmd='test-linearize -Wno-decl $file'
- linear=1 ;;
- -p)
- def_cmd='sparse -E $file' ;;
- help|-*)
- do_format_help
- return 0
- ;;
- *) break ;;
- esac
- shift
- continue
- done
- if [ $# -lt 1 -o $# -gt 3 ]; then
- do_format_help
- return 0
- fi
- arg_file "$1" || return 1
- file="$1"
- fname="$2"
- [ -z "$fname" ] && fname="$(basename "$1" .c)"
- fcmd="$3"
- [ -z "$fcmd" ] && fcmd="$def_cmd"
- cmd=`eval echo $default_path/$fcmd`
- $cmd 1> $file.output.got 2> $file.error.got
- fexit_value=$?
- [ $append != 0 ] && exec >> $file
- cat <<_EOF
- /*
- * check-name: $fname
- _EOF
- if [ "$fcmd" != "$default_cmd" ]; then
- echo " * check-command: $fcmd"
- fi
- if [ "$fexit_value" -ne "0" ]; then
- echo " * check-exit-value: $fexit_value"
- fi
- if [ $fail != 0 ]; then
- echo " * check-known-to-fail"
- fi
- if [ $linear != 0 ]; then
- echo ' *'
- echo ' * check-output-ignore'
- echo ' * check-output-contains: xyz\\\\.'
- echo ' * check-output-excludes: \\\\.'
- fi
- for stream in output error; do
- if [ -s "$file.$stream.got" ]; then
- echo " *"
- echo " * check-$stream-start"
- cat "$file.$stream.got"
- echo " * check-$stream-end"
- fi
- done
- echo " */"
- return 0
- }
- ## allow flags from environment
- set -- $SPARSE_TEST_FLAGS "$@"
- ## process the flags
- while [ "$#" -gt "0" ]; do
- case "$1" in
- -a|--abort)
- abort=1
- ;;
- -q|--quiet)
- vquiet=1
- ;;
- --args=*)
- default_args="${1#--args=}";
- ;;
- single|--single)
- arg_file "$2"
- do_test "$2"
- case "$?" in
- 0) echo "$2 passed !";;
- 1) echo "$2 failed !";;
- 2) echo "$2 can't be handled by $prog_name";;
- esac
- exit $failed
- ;;
- format|--format)
- shift
- do_format "$@"
- exit 0
- ;;
- help)
- do_usage
- exit 1
- ;;
- *.c|*.cdoc)
- tests_list="$tests_list $1"
- ;;
- *)
- if [ ! -d "$1" ]; then
- do_usage
- exit 1
- fi
- tests_list="$tests_list $(find "$1" -name '*.c' | sort)"
- ;;
- esac
- shift
- done
- if [ -z "$tests_list" ]; then
- tests_list=`find . -name '*.c' | sed -e 's#^\./\(.*\)#\1#' | sort`
- fi
- do_test_suite
- exit $failed
|