#! /bin/sh # mtests.sh : the moronic test suite suited for C projects # TODO # - support test specific subdir test_xxx.d/*.[ch] SCRIPT_DIR=${0%/*} SCRIPT_FILE=${0##*/} # colors RESET="" RED="" GREEN="" BROWN="" PURPLE="" # arguments ABORT=0 QUIET=0 COLOR=0 TESTS="" SRC_D="src" BUILD_D="build" # env vars CC="${CC:-"clang"}" CFLAGS="${CFLAGS:-"-O0 -ggdb -W -Wall -Wextra -Wshadow"}" # local vars TEST_N=0 PASS_N=0 MAIN_C="main.c" TEST_D="/tmp/__mtests" function fatal { echo -e "${RED}FATAL${RESET} $1" exit 1 } function say { [ $QUIET -eq 1 ] || echo -e "$1" } function sayn { [ $QUIET -eq 1 ] || echo -en "$1" } function say_anyway { if [ $QUIET -eq 1 ] then echo -e "$1" | sed s'/^ \+//' else echo -e "$1" fi } function load_main { INCLUDE="" for include in $(cat "$dir/$MAIN_C" | sed -n 's/#include\ \+"\(.*\)"/\1/p') do F=$(find $SRC_D -name $include) if [ -z "$F" ] then F=$(find $BUILD_D -name $include) [ ! -z "$F" ] || fatal "can't find $include in $SRC_D or $BUILD_D" fi INCLUDE="$INCLUDE -I${F%/*}" done LDF="" LDP="" for ld in $(cat "$dir/$MAIN_C" | sed -n 's/.*ld\ *:\ *\(.*\)$/\1/p') do F=$(find $BUILD_D -name $ld) [ ! -z "$F" ] || fatal "can't find $ld in $BUILD_D" LDP="$LDP -L${F%/*}" lib=${F##*/lib} lib=${lib%.so*} LDF="$LDF -l$lib" done } function check_dir { for file in $MAIN_C do f="$dir/$file" if [ ! -f "$f" -o ! -r "$f" ] then say " $f missing" say " leave" return 1 fi done return 0 } function enter_dir { say " enter ${PURPLE}$dir${RESET}" check_dir || return 1 load_main return 0 } function run_test { TEST=$(basename $test_c .c) TEST_C="$TEST.c" TEST_O="$TEST_D/$TEST" sayn " ${BROWN}run ${PURPLE}${test_c##*/}${RESET} " $CC $dir/$MAIN_C -o $TEST_O \ -DTESTC=$TEST_C -DCALL=$TEST -DFUNC="void $TEST(void)" \ $CFLAGS $INCLUDE $LDP $LDF || fatal " compilation of $test_c failed" TEST_N=$((TEST_N + 1)) $TEST_O && rm "$TEST_O" && say "${GREEN}PASS${RESET}" && PASS_N=$((PASS_N + 1)) && return say "${RED}FAIL${RESET}" say_anyway " $test_c" [ $ABORT -ne 1 ] && return say " see $BROWN$TEST_O$RESET" exit 1 } function run_dir { enter_dir || return for test_c in $(find $dir -name test_*.c | sort) do run_test done say " leave" } function report { [ $QUIET -eq 1 ] && exit 0 say "\n$PASS_N/$TEST_N tests passed" FAIL_N=$(ls -1 $TEST_D | wc -l) [ $FAIL_N -gt 0 ] && say "see $BROWN$TEST_D$RESET" exit 0 } # parse command line while [ $# -ge 1 ]; do case "$1" in -s|--src) shift [ $# -lt 1 ] && fatal "option -s is missing directory argument" SRC_D=$1 shift ;; -b|--build) shift [ $# -lt 1 ] && fatal "option -b is missing directory argument" BUILD_D=$1 shift ;; -a|--abort) ABORT=1 shift ;; -c|--color) COLOR=1 shift ;; -q|--quiet) QUIET=1 shift ;; -h|--help) echo "Usage: $SCRIPT_FILE [options] [tests]" echo echo "Options:" echo " -b, --abort Abort on test failure" echo " -q, --quiet Only output failed tests" echo " -c, --color Pretty pretty" echo " -s, --src directory Directory to search for tests into" echo " -b, --build directory Directory to search for built files into" echo " -h, --help This message." exit 0 ;; *) TESTS="$TESTS $1" shift ;; esac done # set colors if [ $COLOR -eq 1 ] then RESET="\033[0m" RED="\033[0;31m" GREEN="\033[0;32m" BROWN="\033[0;33m" PURPLE="\033[0;35m" fi # check arguments [ -d "$SRC_D" -a -r "$SRC_D" ] || fatal "$SRC_D is not a valid directory" [ -d "$BUILD_D" -a -r "$BUILD_D" ] || fatal "$BUILD_D is not a valid directory" rm -fr $TEST_D 2 > /dev/null mkdir -p $TEST_D || fatal "cannot create $TEST_D directory" if [ -z "$TESTS" ] then say "search for tests into $BROWN$SRC_D$RESET" TESTS=$(find $SRC_D -type d -name tests) fi # go for test_c in $TESTS do if [ -d "$test_c" ] then dir=$test_c run_dir elif [ -f "$test_c" ] then dir=${test_c%/*} enter_dir || continue run_test say " leave" else say "$BROWN$test_c$RESET can't be read" fi done report