Will review in more detail later, but at first glance this looks quite good, and very capable.
I rebased the series against current HEAD to make testing easier.
Two minor things that jump out at me: * The need to specify the command line as "../sparse args $file" seems somewhat inelegant.
solved: no path in check-command now.
Also, allowing an alternate option "check-options" that just specifies sparse flags seems useful, and the default command could do something like "sparse $options $file"; that way, you can just say "check-options: -E", or "check-options: -Wthingy".
Humm, not sure if it's _that_ much useful to have check-options: -E -Wthingy instead of check-command: sparse -E -Wthingy $file for some reason I find the later less confusing, a matter of taste I guess.
* The need to prefix every line of output, rather than delimiting the start and end of the output, seems painful with large amounts of output.
Agreed. that's why ./test-suite format helps building such tags However I still run into a behavior that I cannot explain: validation$ echo $SHELL /bin/bash ---------- validation$ ../sparse -E preprocessor/preprocessor19.c preprocessor/preprocessor19.c:4:9: warning: preprocessor token A redefined preprocessor/preprocessor19.c:3:9: this was the original definition y ---------- validation$ ../sparse -E preprocessor/preprocessor19.c 2> o 1> o && cat o y processor/preprocessor19.c:4:9: warning: preprocessor token A redefined preprocessor/preprocessor19.c:3:9: this was the original definition ---------- validation$ ../sparse -E preprocessor/preprocessor19.c &> o && cat o preprocessor/preprocessor19.c:4:9: warning: preprocessor token A redefined preprocessor/preprocessor19.c:3:9: this was the original definition y If you look carefully the 2> 1> redirections have eaten "pre" of the first "preprocessor". Using &> show a more suitable behavior but it seems that &> is not supported by every Bourne shell (for instance dash (the default /bin/sh of Ubuntu 7.04 does not support &>) Any idea ? -- Damien
From 31cb2f2eafb9e7b88f3fb40500908853bfad24bf Mon Sep 17 00:00:00 2001 From: Damien Lespiau <damien.lespiau@xxxxxxxxx> Date: Fri, 29 Jun 2007 00:57:36 +0200 Subject: [PATCH] test-suite: a tiny test automation script This patch introduces test-suite, a simple script that makes test cases verification easier. Test cases in the validation directory are annotated and this script parses them to check if the actual result is the one expected by the test writer. Signed-off-by: Damien Lespiau <damien.lespiau@xxxxxxxxx> --- .gitignore | 5 + Makefile | 4 + validation/test-suite | 241 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 250 insertions(+), 0 deletions(-) create mode 100755 validation/test-suite diff --git a/.gitignore b/.gitignore index e22a8c6..faa046b 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,8 @@ patches-* patches series +# test-suite +*.diff +*.got +*.expected + diff --git a/Makefile b/Makefile index 039fe38..7ce9969 100644 --- a/Makefile +++ b/Makefile @@ -168,3 +168,7 @@ dist: exit 1 ; \ fi git archive --format=tar --prefix=sparse-$(VERSION)/ HEAD^{tree} | gzip -9 > sparse-$(VERSION).tar.gz + +check: all + $(Q)cd validation && ./test-suite + diff --git a/validation/test-suite b/validation/test-suite new file mode 100755 index 0000000..c2e0cbb --- /dev/null +++ b/validation/test-suite @@ -0,0 +1,241 @@ +#!/bin/sh + +#set -x + +default_path=".." +default_cmd="sparse \$file" +tests_list=`find . -name '*.c' | sed -e 's#^\./\(.*\)#\1#' | sort` +prog_name=`basename $0` + +# counts: +# - tests that have not been converted to test-suite format +# - tests that passed +# - tests that failed +# - tests that failed but are known to fail +unhandled_tests=0 +ok_tests=0 +ko_tests=0 +known_ko_tests=0 + +# defaults to not verbose +[ -z "$V" ] && V=0 + +## +# get_value(key, file) - gets the value of a (key, value) pair in file. +# +# returns 0 on success, 1 if the file does not have the key +get_value() +{ + last_result=`grep $1: $2 | sed -e "s/^.*$1:\(.*\)$/\1/"` + [ -z "$last_result" ] && return 1 + return 0 +} + +## +# get_tag(key, file) - does file has the tag key in it ? +# +# returns 0 if present, 1 otherwise +get_tag() +{ + last_result=`grep $1 $2` + return $? +} + +## +# verbose(string) - prints string if we are in verbose mode +verbose() +{ + [ "$V" -eq "1" ] && echo " $1" + return 0 +} + +## +# error(string[, die]) - prints an error and exits with value die if given +error() +{ + echo "error: $1" + [ -n "$2" ] && exit $2 + return 0 +} + +do_usage() +{ +echo "$prog_name - a tiny automatic testing script" +echo "Usage: $prog_name [command] [command arguments]" +echo +echo "commands:" +echo " none runs the whole test suite" +echo " single file runs the test in 'file'" +echo " format file [name [cmd]] helps writing a new test case using cmd" +echo +echo " help prints usage" +} + +## +# 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. +do_test() +{ + test_failed=0 + file="$1" + + # can this test be handled by test-suite ? + # (it has to have a check-name key in it) + get_value "check-name" $file + if [ "$?" -eq 1 ]; then + unhandled_tests=`expr $unhandled_tests + 1` + return 2 + fi + test_name=$last_result + + echo " TEST $test_name ($file)" + + # does the test provide a specific command ? + cmd=`eval echo $default_path/$default_cmd` + get_value "check-command" $file + if [ "$?" -eq "0" ]; then + last_result=`echo $last_result | sed -e 's/^ *//'` + cmd=`eval echo $default_path/$last_result` + fi + verbose "Using command : $cmd" + + # grab the expected exit value + get_value "check-exit-value" $file + if [ "$?" -eq "0" ]; then + expected_exit_value=`echo $last_result | tr -d ' '` + else + expected_exit_value=0 + fi + verbose "Expecting exit value: $expected_exit_value" + + # grab the expected output + get_value "check-output" $file + if [ "$?" -eq "0" ]; then + echo "$last_result" > "$file".expected + else + echo -n "" > "$file".expected + fi + + # grab the actual output & exit value + $cmd 1> $file.got 2> $file.got + actual_exit_value=$? + + diff -u "$file".expected "$file".got > "$file".diff + if [ "$?" -ne "0" ]; then + error "actual output does not match the expected one." + error "see $file.* for further investigation." + test_failed=1 + fi + + 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 + + if [ "$test_failed" -eq "1" ]; then + ko_tests=`expr $ko_tests + 1` + get_tag "check-known-to-fail" $file + [ "$?" -eq "0" ] && known_ko_tests=`expr $known_ko_tests + 1` + return 1 + else + ok_tests=`expr $ok_tests + 1` + return 0 + fi +} + +do_test_suite() +{ + for i in $tests_list; do + do_test "$i" + done + + # prints some numbers + tests_nr=`expr $ok_tests + $ko_tests` + echo -n "Out of $tests_nr tests, $ok_tests passed, $ko_tests failed" + echo " ($known_ko_tests of them are known to fail)" + if [ "$unhandled_tests" -ne "0" ]; then + echo "$unhandled_tests tests could not be handled by $prog_name" + fi +} + +## +# do_format(file[, name[, cmd]]) - helps a test writer to format test-suite tags +do_format() +{ + if [ -z "$2" ]; then + fname="$1" + fcmd=$default_cmd + elif [ -z "$3" ]; then + fname="$2" + fcmd=$default_cmd + else + fname="$2" + fcmd="$3" + fi + file="$1" + cmd=`eval echo $default_path/$fcmd` + $cmd 1> $file.got 2> $file.got + fexit_value=$? + foutput=`sed -e "s/^\(.*\)/ * check-output:\1/" $file.got` + format=`cat <<_EOF +/* + * check-name: $fname + * + * check-command: $fcmd + * check-exit-value: $fexit_value + * +$foutput + */ +_EOF +` + echo "$format" + return 0 +} + +## +# 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 +} + +case "$1" in + '') + do_test_suite + ;; + 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 + ;; + format) + arg_file "$2" + do_format "$2" "$3" "$4" + ;; + help | *) + do_usage + exit 1 + ;; +esac + +exit 0 + -- 1.5.2.1.280.g38570
From 35fb2463bb71fefdd6865db38cb70bdd0f28d9a4 Mon Sep 17 00:00:00 2001 From: Damien Lespiau <damien.lespiau@xxxxxxxxx> Date: Fri, 29 Jun 2007 00:57:38 +0200 Subject: [PATCH] Sample test-suite test cases A few examples meant to show the use of test-suite Signed-off-by: Damien Lespiau <damien.lespiau@xxxxxxxxx> --- validation/bad-assignment.c | 11 +++++++++++ validation/preprocessor/preprocessor1.c | 9 +++++++++ validation/preprocessor/preprocessor2.c | 9 +++++++++ validation/preprocessor/preprocessor3.c | 12 ++++++++++++ validation/struct-as.c | 8 ++++++++ 5 files changed, 49 insertions(+), 0 deletions(-) diff --git a/validation/bad-assignment.c b/validation/bad-assignment.c index 3b66a11..72b40d3 100644 --- a/validation/bad-assignment.c +++ b/validation/bad-assignment.c @@ -4,3 +4,14 @@ static int foo(int a) return a; } +/* + * check-name: bad assignement + * + * check-command: sparse $file + * check-exit-value: 1 + * + * check-output:bad-assignment.c:3:6: error: Expected ; at end of statement + * check-output:bad-assignment.c:3:6: error: got \ + * + * check-known-to-fail + */ diff --git a/validation/preprocessor/preprocessor1.c b/validation/preprocessor/preprocessor1.c index 5ae20aa..0eb8147 100644 --- a/validation/preprocessor/preprocessor1.c +++ b/validation/preprocessor/preprocessor1.c @@ -12,3 +12,12 @@ #define bar func( #define foo bar foo foo ) +/* + * check-name: preprocessor 1 + * + * check-command: sparse -E $file + * check-exit-value: 0 + * + * check-output: + * check-output:foo + */ diff --git a/validation/preprocessor/preprocessor2.c b/validation/preprocessor/preprocessor2.c index 340938e..098fe8c 100644 --- a/validation/preprocessor/preprocessor2.c +++ b/validation/preprocessor/preprocessor2.c @@ -13,3 +13,12 @@ #define BINARY(x, y) x + y UNARY(TWO) +/* + * check-name: preprocessor 2 + * + * check-command: sparse -E $file + * check-exit-value: 0 + * + * check-output: + * check-output:a + b + */ diff --git a/validation/preprocessor/preprocessor3.c b/validation/preprocessor/preprocessor3.c index 71b9acd..d689a1a 100644 --- a/validation/preprocessor/preprocessor3.c +++ b/validation/preprocessor/preprocessor3.c @@ -35,3 +35,15 @@ A() // B ( ) SCAN( A() ) // A ( ) SCAN(SCAN( A() )) // B ( ) SCAN(SCAN(SCAN( A() ))) // A ( ) +/* + * check-name: preprocessor 3 + * + * check-command: sparse -E $file + * check-exit-value: 0 + * + * check-output: + * check-output:B ( ) + * check-output:A ( ) + * check-output:B ( ) + * check-output:A ( ) + */ diff --git a/validation/struct-as.c b/validation/struct-as.c index 86b90d3..21ee218 100644 --- a/validation/struct-as.c +++ b/validation/struct-as.c @@ -14,3 +14,11 @@ static int broken(struct hello __user *sp) { test(&sp->a); } +/* + * check-name: address space of structure members + * + * check-command: sparse $file + * check-exit-value: 0 + * + + */ -- 1.5.2.1.280.g38570
From 2b5aa5e2525fef36335502eb37462c1ceb93e43d Mon Sep 17 00:00:00 2001 From: Damien Lespiau <damien.lespiau@xxxxxxxxx> Date: Fri, 29 Jun 2007 01:18:12 +0200 Subject: [PATCH] test-suite documentation A quick description of test-suite usage. Signed-off-by: Damien Lespiau <damien.lespiau@xxxxxxxxx> --- Documentation/test-suite | 113 ++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 113 insertions(+), 0 deletions(-) create mode 100644 Documentation/test-suite diff --git a/Documentation/test-suite b/Documentation/test-suite new file mode 100644 index 0000000..b6ef22c --- /dev/null +++ b/Documentation/test-suite @@ -0,0 +1,113 @@ + + + Sparse test suite + ~~~~~~~~~~~~~~~~~ + +Sparse has a number of test cases in its validation directory. The test-suite +script aims at making automated checking of these tests possible. It works by +embedding tags in C comments in the test cases. + +check-name: (mandatory) + Name of the test. + +check-description: (optional) + A description of what checks the test. + +check-command: (optional) + There are different kinds of tests. Some can validate sparse + preprocessor while others will use sparse, cgcc of even other backends + of the library. check-command allows you to give a custom command to + run the test-case. + The '$file' string is special. It will be expanded to the file name at + run time. + It defaults to "sparse $file". + +check-exit-value: (optional) + The expected exit value of check-command. It defaults to 0. + +check-output: (optional) + The expected output (stdout and stderr) of check-command. It defaults + to no output. + +check-known-to-fail (optional) + Mark the test as being known to fail. + + + Using test-suite + ~~~~~~~~~~~~~~~~ + +The test-suite script is called trough the check target of the Makefile. It +will try to check every test case it finds (find validation -name '*.c'). +It can be called to check a single test with: + +$ cd validation +$ ./test-suite single preprocessor1.c + TEST Preprocessor 1 (preprocessor1.c) +preprocessor1.c passed ! + + + Writing a test + ~~~~~~~~~~~~~~ + +test-suite comes with the format command to make a test easier to write: + + test-suite format file [name [cmd]] + +name: + check-name value. If no name is provided, it defaults to the file name. +cmd: + check-command value. It no cmd if provided, it defaults to + "sparse $file". + +The output of the test-suite format command can be redirected into the +test case to create a test-suite formated file. + +$ ./test-suite format bad-assignment.c Assignement >> bad-assignment.c +$ cat !$ +cat bad-assignment.c +static int foo(int a) +{ + a |=\1; + + return a; +} +/* + * check-name: Assignement + * + * check-command: sparse $file + * check-exit-value: 0 + * + * check-output:bad-assignment.c:3:6: error: Expected ; at end of statement + * check-output:bad-assignment.c:3:6: error: got \ + */ + +You can define the check-command you want to use for the test. $file will be +extended to the file name at run time. + +$ ./test-suite format preprocessor2.c "Preprocessor 2" \ + "sparse -E \$file" >> preprocessor2.c +$ cat !$ +/* + * This one we happen to get right. + * + * It should result in a simple + * + * a + b + * + * for a proper preprocessor. + */ +#define TWO a, b + +#define UNARY(x) BINARY(x) +#define BINARY(x, y) x + y + +UNARY(TWO) +/* + * check-name: Preprocessor 2 + * + * check-command: sparse -E $file + * check-exit-value: 0 + * + * check-output: + * check-output:a + b + */ -- 1.5.2.1.280.g38570