Skip to content

Commit

Permalink
Rewrite test suite. Add new run_tests.sh file.
Browse files Browse the repository at this point in the history
run_tests.sh runs all tests and prints a statistic. Add support for
broken test cases.
  • Loading branch information
rudis committed Feb 7, 2012
1 parent 972e134 commit f82591a
Show file tree
Hide file tree
Showing 4 changed files with 252 additions and 105 deletions.
33 changes: 4 additions & 29 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,32 +1,7 @@
T= asm \
empty \
filesize \
flag_add \
grep \
math \
noargs_Qv \
noargs_f \
redo \
search_ascii \
seek \
undo \
woa \
write \
write_cache

# failing tests
FT= help_slash-cQ

all: ${T}
fail: ${FT}

${T}:
@cd t ; ./$@

${FT}:
@cd t ; ./$@
all:
sh run_tests.sh

clean:
rm -f t/out.* t/val.* t/rad.* t/exp.* t/radare2.core t/*.tmp
rm -rf tmp

.PHONY: all ${T} clean
.PHONY: all clean
49 changes: 39 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,35 +8,64 @@ Originally based on work by and now in collaboration with pancake.
Directory Hierarchy
-------------------

* t/: Test scripts.
* s/: Sample binaries.
* test.sh: Test driver script sourced by tests (not to be run manually).
* t/: Test scripts.
* s/: Sample binaries.
* test.sh: Test driver script sourced by tests (not to be run manually).
* run_tests.sh: Run all tests.

Requirements
------------

* Radare2 installed (and in $PATH or set the R2 environment).
* bmake or gmake.

Usage
-----

* Run 'make' in the top level directory to run *all* tests.
* To run individual tests, type 'make <testname>'.
* To run tests which we know are failing (and should not), run 'make fail'.
* Run './run_tests.sh' in the top level directory to run *all* tests
(alternatively you can use 'make').
* To run individual tests, type 'cd t; ./testname'.
* To remove old test results run 'make clean'.

Options
-------

The following options can be passed to make or the individual tests.
The following options can be passed to run_tests.sh or the individual tests
(environment variables).

* To run tests with valgrind, use 'VALGRIND=1'.
* To continue running tests if one failed, use 'NOEXIT=1'.
* To get verbose output, use 'VERBOSE=1' (always enabled for individual
tests).

Reporting Radare2 Bugs
----------------------

Please to not post Radare2 bugs on the r2-regressions github tracker. Instead
use te official r2 tracker:
use the official r2 tracker:

http://radare.org/y/?p=bugtracker

Writing test cases
------------------

Test cases are simple shell scripts (POSIX compliant) and either run by
run_tests.sh or manually in t/.

The following variables are available:

* FILE (required): File argument for radare2.
* ARGS (optional): Additional arguments for radare2. If not present no
additional arguments are used.
* CMDS (required): Commands to run, one per line. Just like in interactive
mode.
* EXPECT (required): Expected output.
* FILTER (optional): Filter program (like grep or sed) to filter radare2's
output before comparing it with EXPECT. Useful to fix
random output to generate stable tests.
* BROKEN (optional): This tests documents a bug which is not yet fixed.

All uppercase variable names are reserved for the test system.

The following functions are available:

* run_test(): Run the test with the variables. Can be called multiple times
in one test file.
65 changes: 65 additions & 0 deletions run_tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#!/bin/sh


die() {
echo "$1"
exit 1
}


# Statistics.
TESTS_RUN=0
TESTS_SUCCESS=0
TESTS_FAILED=0
TESTS_BROKEN=0
TESTS_FIXED=0

# Let tests.sh know the complete test suite is run, enables statistics.
R2_SOURCED=1

. ./tests.sh


# Run all tests.
cd t || die "t/ doesn't exist"
for file in *; do
NAME=$(echo "${file}" | sed 's/.sh$//')
. "./${file}"
done

# Print report.
echo
echo "RUN: ${TESTS_RUN}"
printf "SUCCESS: ";
if [ "${TESTS_SUCCESS}" -gt 0 ]; then
print_success "$TESTS_SUCCESS"
else
print_failed "${TESTS_SUCCESS}"
fi
printf "FAILED: ";
if [ "${TESTS_FAILED}" -gt 0 ]; then
print_failed "$TESTS_FAILED"
else
echo 0
fi
printf "BROKEN: ";
if [ "${TESTS_BROKEN}" -gt 0 ]; then
print_failed "$TESTS_BROKEN"
else
echo 0
fi
printf "FIXED: ";
if [ "${TESTS_FIXED}" -gt 0 ]; then
print_fixed "${TESTS_FIXED}"
else
echo 0
fi

# Proper exit code.
if [ "$TESTS_RUN" -eq "$TESTS_SUCCESS" ]; then
exit 0
elif [ "$TESTS_FIXED" -gt 0 ]; then
exit 2
else
exit 1
fi
210 changes: 144 additions & 66 deletions tests.sh
Original file line number Diff line number Diff line change
@@ -1,72 +1,150 @@
#!/do/not/execute

run_test() {
r2args="${r2} -e scr.color=0 -N -q -i ${rad} ${ARGS} ${FILE}"

# ${FILTER} can be used to filter out random results to create stable
# tests.
if [ -n "${FILTER}" ]; then
r2args="${r2args} 2>&1 | ${FILTER} > ${out}"
else
r2args="${r2args} > ${out} 2>&1"
fi

if [ -n "${VALGRIND}" ]; then
cmd="valgrind --error-exitcode=47 --log-file=${val} ${r2args}"
else
cmd="${r2args}"
fi
cmd="echo q | ${cmd}"

file=`basename $0`
if [ -z "${NAME}" ]; then
NAME=$file
else
NAME="$file: $NAME"
fi

echo "Next Test: ${NAME}"
echo "Running: ${cmd}"
NAME=

# put expected outcome and program to run in files
echo "${CMDS}" > ${rad}
echo "${EXPECT}" > ${exp}

eval ${cmd}
code=$?
if [ ${code} -eq 47 ]; then
printf "\033[31m"
echo "FAIL (Valgrind error)"
printf "\033[0m"
cat ${val}
[ -z "${NOEXIT}" ] && exit ${code}
elif [ ! ${code} -eq 0 ]; then
printf "\033[31m"
echo "FAIL (Radare2 crashed?)"
printf "\033[0m"
[ -z "${NOEXIT}" ] && exit ${code}
elif [ "`cat $out`" = "${EXPECT}" ]; then
printf "\033[32m"
echo "SUCCESS"
printf "\033[0m"
else
printf "\033[31m"
echo "FAIL (Unexpected outcome)"
printf "\033[0m"
diff -u ${exp} ${out}
[ -z "${NOEXIT}" ] && exit 1
fi
rm -f ${out} ${val} ${rad} ${exp}
echo "-------------------------------------------------------------------"
if [ -z "${R2}" ]; then
R2=$(which radare2)
fi

# Set by run_tests.sh if all tests are run - otherwise get it from test
# name.
if [ -z "${NAME}" ]; then
NAME=$(basename "$0" | sed 's/\.sh$//')
fi

printf "${NAME}"
[ -n "${VALGRIND}" ] && printf " (valgrind)"
printf " .. "

# Check required variables.
if [ -z "${FILE}" ]; then
test_failed "FILE missing!"
test_reset
return
fi
if [ -z "${CMDS}" ]; then
test_failed "CMDS missing!"
test_reset
return
fi
# ${EXPECT} can be empty. Don't check it.

# Verbose mode is always used if only a single test is run.
if [ -z "${R2_SOURCED}" ]; then
VERBOSE=1
fi

mkdir -p ../tmp
TMP_RAD=$(mktemp "../tmp/${NAME}-rad.XXXXXX")
TMP_OUT=$(mktemp "../tmp/${NAME}-out.XXXXXX")
TMP_VAL=$(mktemp "../tmp/${NAME}-val.XXXXXX")
TMP_EXP=$(mktemp "../tmp/${NAME}-exp.XXXXXX")

# No colors and no user configs.
R2ARGS="${R2} -e scr.color=0 -N -q -i ${TMP_RAD} ${ARGS} ${FILE}"
R2CMD=
# ${FILTER} can be used to filter out random results to create stable
# tests.
if [ -n "${FILTER}" ]; then
R2ARGS="${R2ARGS} 2>&1 | ${FILTER} > ${TMP_OUT}"
else
R2ARGS="${R2ARGS} > ${TMP_OUT} 2>&1"
fi
# Valgrind to detect memory corruption.
if [ -n "${VALGRIND}" ]; then
R2CMD="valgrind --error-exitcode=47 --log-file=${TMP_VAL}"
fi
R2CMD="echo q | ${R2CMD} ${R2ARGS}"

# Put expected outcome and program to run in files and run the test.
echo "${CMDS}" > ${TMP_RAD}
echo "${EXPECT}" > ${TMP_EXP}
if [ -n "${VERBOSE}" ]; then
echo "${R2CMD}"
fi
eval "${R2CMD}"
CODE=$?

if [ -n "${R2_SOURCED}" ]; then
TESTS_RUN=$(( TESTS_RUN + 1 ))
fi

if [ ${CODE} -eq 47 ]; then
test_failed "valgrind error"
if [ -n "${VERBOSE}" ]; then
cat "${TMP_VAL}"
echo
fi

elif [ ! ${CODE} -eq 0 ]; then
test_failed "radare2 crashed"
if [ -n "${VERBOSE}" ]; then
cat "${TMP_OUT}"
echo
fi

elif [ "$(cat "${TMP_OUT}")" = "${EXPECT}" ]; then
test_success

else
test_failed "unexpected outcome"
if [ -n "${VERBOSE}" ]; then
diff -u ${TMP_EXP} ${TMP_OUT}
echo
fi
fi

rm -f "${TMP_RAD}" "${TMP_OUT}" "${TMP_VAL}" "${TMP_EXP}"

# Reset most variables in case the next test script doesn't set them.
test_reset
}

test_reset() {
FILE=
ARGS=
CMDS=
EXPECT=
FILTER=
BROKEN=
}

r2=${R2}
if [ -z "${R2}" ]; then
r2=`which radare2`
fi
test_success() {
if [ -z "${BROKEN}" ]; then
print_success "OK "
else
print_fixed "FIXED"
fi

if [ -n "${R2_SOURCED}" ]; then
if [ -z "${BROKEN}" ]; then
TESTS_SUCCESS=$(( TESTS_SUCCESS + 1 ))
else
TESTS_FIXED=$(( TESTS_FIXED + 1 ))
fi
fi
}
test_failed() {
if [ -z "${BROKEN}" ]; then
print_failed "FAIL ($1)"
else
print_failed "BROKEN ($1)"
fi

out=`mktemp out.XXXXXX`
val=`mktemp val.XXXXXX`
rad=`mktemp rad.XXXXXX`
exp=`mktemp exp.XXXXXX`
if [ -n "${R2_SOURCED}" ]; then
if [ -z "${BROKEN}" ]; then
TESTS_FAILED=$(( TESTS_FAILED + 1 ))
else
TESTS_BROKEN=$(( TESTS_BROKEN + 1 ))
fi
fi
}

print_success() {
printf "%b" "\033[32m${*}\033[0m\n"
}
print_failed() {
printf "%b" "\033[31m${*}\033[0m\n"
}
print_fixed() {
printf "%b" "\033[33m${*}\033[0m\n"
}

0 comments on commit f82591a

Please sign in to comment.