#!/usr/bin/env bash # somewhat portable way to detect CPU count detect_cpu_count () { if [ "$CPUS" = "" ]; then # Windows standard environment variable CPUS="$NUMBER_OF_PROCESSORS" fi if [ "$CPUS" = "" ]; then # Linux CPUS=`getconf _NPROCESSORS_ONLN 2>/dev/null` fi if [ "$CPUS" = "" ]; then # FreeBSD CPUS=`getconf NPROCESSORS_ONLN 2>/dev/null` fi if [ "$CPUS" = "" ]; then # nothing helped CPUS="1" fi } lc_run() { LANG=C LC_ALL=C "$@" } valgrind="" skeleton=0 keep_tmp_files=0 wine="" re2c="./re2c" threads=`detect_cpu_count; echo $CPUS` tests=() for arg in $* do case $arg in "--valgrind" ) valgrind=`which valgrind` ;; "--skeleton" ) skeleton=1 ;; "--keep-tmp-files" ) keep_tmp_files=1 ;; "--wine" ) wine=`which wine` re2c="${re2c}.exe" ;; "-j"* ) number=${arg#-j} number_pattern='^[0-9]+$' if [[ $number =~ $number_pattern ]] then threads=$number fi ;; * ) tests+=("$arg") ;; esac done echo "Running in ${threads} thread(s)" test_srcdir="" if [ ${#tests[@]} -eq 0 ] then test_srcdir="@top_srcdir@/test" tests=(`find $test_srcdir -name '*.re' | sort`) fi test_blddir="test_"`date +%y%m%d%H%M%S` rm -rf $test_blddir && mkdir $test_blddir tests_per_thread=$((${#tests[@]} / threads + 1)) packs=() for ((i = 0; i < threads; i++)) do j=$((i * tests_per_thread)) packs[$i]=${tests[@]:j:tests_per_thread} done if test ! -x "${re2c}" then echo "Cannot find re2c executable (${re2c})." exit 1 fi if test -n "${valgrind}" then valgrind_options=( "-q" "--track-origins=yes" "--num-callers=50" "--leak-check=full" "--show-reachable=yes" "--malloc-fill=0xa1" "--free-fill=0xa1" ) valgrind="${valgrind} ${valgrind_options[@]}" echo $valgrind fi diff_prog="diff" if test -n "${wine}" then # ignore whitespace at the end of line diff_prog="${diff_prog} -b" fi run_pack() { local log="$1" shift 1 local errcnt=0 for x in $* do cd $test_blddir # preserve directory structure unless given explicit args if [ -z "$test_srcdir" ] then local outx=`basename $x` else # remove prefix local outx=${x:$((${#test_srcdir} + 1))} mkdir -p `dirname $outx` fi local outc="${outx%.re}.c" # filename (dot short* (long arg?)*)? ext # must keep to POSIX standard: no syntactic sugar like +,?, etc. # if you change this regexp, try it with 'sed --posix' local switches=`basename "$x" | lc_run sed \ -e 's/^[^.]*\.re$//g' \ -e 's/^[^.]*\.\(.*\)\.re$/\1/g' \ -e 's/^\([^-]\)/-\1/' \ -e 's/--\([^ (-]*\)/ --\1/g' \ -e 's/(\([^)]*\))/ \1/g'` # check that flags do not contain uppercase letters: # file extensions are case-insensitive on some platforms printf "%s" "$switches" | lc_run grep -q "[A-Z]" \ && { echo "bad file extension: '$outx' (uppercase letters are not allowed)"; exit 1; } local switches="$switches -o $outc --no-version --no-generation-date" # enable warnings globally local switches="$switches -W" if [ $skeleton -eq 1 ] then local switches="$switches --skeleton -Werror-undefined-control-flow" local status=0 ${valgrind} ${wine} ../${re2c} $switches "../$x" 2>"$outc.stderr" || status=1 [ $status -eq 0 ] && { @CC@ -Wall -Wextra -o "$outc.out" "$outc" 2>>"$outc.stderr" || status=2; } [ $status -eq 0 ] && { ./"$outc.out" 2>>"$outc.stderr" || status=3; } case $status in 0 ) local msg="OK" ;; 1 ) local msg="OK (expected re2c error)" ;; 2 ) local msg="FAIL (compilation error)" ;; 3 ) local msg="FAIL (runtime error)" ;; esac printf "%-25s $outx\n" "$msg" if [ $status -le 1 ]; then [ $keep_tmp_files -eq 0 ] && rm -f "$outc"{,.line*.{input,keys},.stderr,.out} else cp -f "../$x" "$outx" local errcnt=$(($errcnt + 1)) fi else # create sandbox tmpdir="tmp_$log" \ && mkdir "$tmpdir" \ && cd "$tmpdir" \ && mkdir -p `dirname "$outx"` \ && cp "../../$x" "$outx" # run re2c $valgrind $wine ../../$re2c $switches "$outx" 2>"$outc.stderr" 1>&2 # paste all files dropped by re2c into output file rm "$outx" && find . -type f \ | lc_run sort \ | xargs cat \ >> "../$outc" # cleanup sandbox cd .. \ && rm -rf "$tmpdir" # compare results local c="${x%.re}.c" local status="" [ -z $status ] && status=`[ -f "../$c" ] || echo "MISSING"` [ -z $status ] && status=`$diff_prog "../$c" "$outc" > "$outc.diff" || echo "FAIL"` [ -z $status ] && status="OK" if [ $status = "OK" ] then rm "$outc" "$outc.diff" else cp -f "../$x" "$outx" cp -f "../$c" "$outc.orig" local errcnt=$(($errcnt + 1)) fi printf "%-10s $outx\n" "$status" fi cd .. done echo $errcnt > $log } cleanup() { rm -f ${logs[@]} kill ${wait_pids[@]} wait ${wait_pids[@]} printf "\nEh...\n" exit 1 } logs=() wait_pids=() trap cleanup INT for ((i = 0; i < ${#packs[@]}; i++)) do logs[$i]=`date +%y%m%d%H%M%S`_$i run_pack ${logs[i]} ${packs[i]} & wait_pids+=( $! ) done wait ${wait_pids[@]} errors=0 for ((i = 0; i < ${#logs[@]}; i++)) do error=`cat ${logs[i]}` errors=$((errors + error)) rm -f ${logs[i]} done # remove empty directories for d in `find $test_blddir -depth -type d` do entries=`ls -A "$d"` if [ -z "$entries" ] then rmdir "$d" fi done if [ $errors -eq 0 ] then echo "All ${#tests[@]} tests passed successfully." exit 0 else echo "Error: $errors out ${#tests[@]} tests failed." exit 1 fi