#!/bin/bash
#
# Copyright (C) 2009 Red Hat, Inc.
# Copyright (c) 2000-2002,2005-2006 Silicon Graphics, Inc.  All Rights Reserved.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it would be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
#
# Control script for QA
#

tmp=/tmp/$$
status=0
needwrap=true
try=0
n_bad=0
bad=""
notrun=""
interrupt=true

# by default don't output timestamps
timestamp=${TIMESTAMP:=false}

# generic initialization
export iam=check

cd $(readlink -f $(dirname $0))

# we need common.config
if ! . ./common.config
then
    echo "$iam: failed to source common.config"
    exit 1
fi

_setenvironment()
{
    MSGVERB="text:action"
    export MSGVERB
}

here=`pwd`
rm -f $here/$iam.out
_setenvironment

check=${check-true}

diff="diff -u"
verbose=false
group=false
xgroup=false
exit_on_err=false
showme=false
sortme=false
expunge=true
have_test_arg=false
randomize=false
valgrind=false
keep_passed=false

rm -f $tmp.list $tmp.tmp $tmp.sed

for r
do

    if $group
    then
	# arg after -g
	group_list=`sed -n <group -e 's/$/ /' -e "/^[0-9][0-9][0-9].* $r /"'{
s/ .*//p
}'`
	if [ -z "$group_list" ]
	then
	    echo "Group \"$r\" is empty or not defined?"
	    exit 1
	fi
	[ ! -s $tmp.list ] && touch $tmp.list
	for t in $group_list
	do
	    if grep -s "^$t\$" $tmp.list >/dev/null
	    then
		:
	    else
		echo "$t" >>$tmp.list
	    fi
	done
	group=false
	continue

    elif $xgroup
    then
	# arg after -x
	[ ! -s $tmp.list ] && ls [0-9][0-9][0-9] [0-9][0-9][0-9][0-9] >$tmp.list 2>/dev/null
	group_list=`sed -n <group -e 's/$/ /' -e "/^[0-9][0-9][0-9].* $r /"'{
s/ .*//p
}'`
	if [ -z "$group_list" ]
	then
	    echo "Group \"$r\" is empty or not defined?"
	    exit 1
	fi
	numsed=0
	rm -f $tmp.sed
	for t in $group_list
	do
	    if [ $numsed -gt 100 ]
	    then
		sed -f $tmp.sed <$tmp.list >$tmp.tmp
		mv $tmp.tmp $tmp.list
		numsed=0
		rm -f $tmp.sed
	    fi
	    echo "/^$t\$/d" >>$tmp.sed
	    numsed=`expr $numsed + 1`
	done
	sed -f $tmp.sed <$tmp.list >$tmp.tmp
	mv $tmp.tmp $tmp.list
	xgroup=false
	continue
    fi

    xpand=true
    case "$r"
    in

	-\? | -h | --help)	# usage
	    echo "Usage: $0 [options] [testlist]"'

common options
    -v			verbose

check options
    -sheepdog           test sheepdog
    -corosync           use corosync driver (default: local)
    -zk                 use zookeeper driver
    -valgrind           use valgrind
    -dio                use direct IO for sheep backend store
    -xdiff		graphical mode diff
    -md                 enable md for backend
    -e			exit immediately on test failure
    -n			show me, do not run tests
    -T			output timestamps
    -r 			randomize test order
    -keep-passed	keep directories of passed tests

testlist options
    -g group[,group...]	include tests from these groups
    -x group[,group...]	exclude tests from these groups
    NNN			include test NNN
    NNN-NNN		include test range (eg. 012-021)
'
	    exit 0
	    ;;

	-corosync)
	    DRIVER="corosync"
	    xpand=false
	    ;;

	-zk)
	    DRIVER="zookeeper:localhost:2181,timeout=1000"
	    xpand=false
	    ;;

	-md)
	    MD=true
	    xpand=false
	    ;;

	-valgrind)
	    valgrind=true
	    xpand=false
	    ;;

	-dio)
	    SHEEP_OPTIONS="$SHEEP_OPTIONS -D"
	    xpand=false
	    ;;

	-g)	# -g group ... pick from group file
	    group=true
	    xpand=false
	    ;;

	-xdiff)	# graphical diff mode
	    xpand=false

	    if [ ! -z "$DISPLAY" ]
	    then
		which xdiff >/dev/null 2>&1 && diff=xdiff
		which gdiff >/dev/null 2>&1 && diff=gdiff
		which tkdiff >/dev/null 2>&1 && diff=tkdiff
		which xxdiff >/dev/null 2>&1 && diff=xxdiff
	    fi
	    ;;

	-e)	# exit immediately on test failure
	    exit_on_err=true
	    xpand=false
	    ;;
	-n)	# show me, don't do it
	    showme=true
	    xpand=false
	    ;;
        -r)	# randomize test order
	    randomize=true
	    xpand=false
	    ;;

	-T)	# turn on timestamp output
	    timestamp=true
	    xpand=false
	    ;;

	-v)
	    verbose=true
	    xpand=false
	    ;;
	-x)	# -x group ... exclude from group file
	    xgroup=true
	    xpand=false
	    ;;
	-keep-passed)
	    keep_passed=true
	    xpand=false
	    ;;
	'[0-9][0-9][0-9] [0-9][0-9][0-9][0-9]')
	    echo "No tests?"
	    status=1
	    exit $status
	    ;;

	[0-9]*-[0-9]*)
	    eval `echo $r | sed -e 's/^/start=/' -e 's/-/ end=/'`
	    ;;

	[0-9]*-)
	    eval `echo $r | sed -e 's/^/start=/' -e 's/-//'`
	    end=`echo [0-9][0-9][0-9] [0-9][0-9][0-9][0-9] | sed -e 's/\[0-9]//g' -e 's/  *$//' -e 's/.* //'`
	    if [ -z "$end" ]
	    then
		echo "No tests in range \"$r\"?"
		status=1
		exit $status
	    fi
	    ;;

	*)
	    start=$r
	    end=$r
	    ;;

    esac

    # get rid of leading 0s as can be interpreted as octal
    start=`echo $start | sed 's/^0*//'`
    end=`echo $end | sed 's/^0*//'`

    if $xpand
    then
	have_test_arg=true
	$AWK_PROG </dev/null '
BEGIN	{ for (t='$start'; t<='$end'; t++) printf "%03d\n",t }' \
	| while read id
	do
	    if grep -s "^$id " group >/dev/null
	    then
		# in group file ... OK
		echo $id >>$tmp.list
	    else
		if [ -f expunged ] && $expunge && egrep "^$id([ 	]|\$)" expunged >/dev/null
		then
		    # expunged ... will be reported, but not run, later
		    echo $id >>$tmp.list
		else
		    # oops
		    echo "$id - unknown test, ignored"
		fi
	    fi
	done
    fi

done

if [ -s $tmp.list ]
then
    # found some valid test numbers ... this is good
    :
else
    if $have_test_arg
    then
	# had test numbers, but none in group file ... do nothing
	touch $tmp.list
    else
	# no test numbers, do everything from group file
	sed -n -e '/^[0-9][0-9][0-9]*/s/[ 	].*//p' <group >$tmp.list
    fi
fi

# should be sort -n, but this did not work for Linux when this
# was ported from IRIX
#
list=`sort $tmp.list`
rm -f $tmp.list $tmp.tmp $tmp.sed

if $randomize
then
    list=`echo $list | awk -f randomize.awk`
fi

if $valgrind; then
    export SHEEP=_valgrind_sheep
    export DOG=_valgrind_dog
fi

# we need common.rc
if ! . ./common.rc
then
    echo "check: failed to source common.rc"
    exit 1
fi

_wallclock()
{
    date "+%H %M %S" | $AWK_PROG '{ print $1*3600 + $2*60 + $3 }'
}

_timestamp()
{
    now=`date "+%T"`
    echo -n " [$now]"
}

_wrapup()
{
    # for hangcheck ...
    # remove files that were used by hangcheck
    #
    [ -f /tmp/check.pid ] && rm -rf /tmp/check.pid
    [ -f /tmp/check.sts ] && rm -rf /tmp/check.sts

    if $showme
    then
	:
    elif $needwrap
    then
	if [ -f check.time -a -f $tmp.time ]
	then
	    cat check.time $tmp.time \
	    | $AWK_PROG '
	{ t[$1] = $2 }
END	{ if (NR > 0) {
	    for (i in t) print i " " t[i]
	  }
	}' \
	    | sort -n >$tmp.out
	    mv $tmp.out check.time
	fi

	if [ -f $tmp.expunged ]
	then
	    notrun=`wc -l <$tmp.expunged | sed -e 's/  *//g'`
	    try=`expr $try - $notrun`
	    list=`echo "$list" | sed -f $tmp.expunged`
	fi

	echo "" >>check.log
	date >>check.log
	echo $list | fmt | sed -e 's/^/    /' >>check.log
	$interrupt && echo "Interrupted!" >>check.log
        
	if [ ! -z "$notrun" ]
	then
	    echo "Not run:$notrun"
	    echo "Not run:$notrun" >>check.log
	fi
        if [ ! -z "$n_bad" -a $n_bad != 0 ]
	then
	    echo "Failures:$bad"
	    echo "Failed $n_bad of $try tests"
	    echo "Failures:$bad" | fmt >>check.log
	    echo "Failed $n_bad of $try tests" >>check.log
	else
	    echo "Passed all $try tests"
	    echo "Passed all $try tests" >>check.log
	fi
	needwrap=false
    fi

    rm -f /tmp/*.out /tmp/*.err /tmp/*.time
    rm -f /tmp/check.pid /tmp/check.sts
    rm -f $tmp.*
}

trap "_wrapup; exit \$status" 0 1 2 3 15

# for hangcheck ...
# Save pid of check in a well known place, so that hangcheck can be sure it
# has the right pid (getting the pid from ps output is not reliable enough).
#
rm -rf /tmp/check.pid
echo $$ >/tmp/check.pid

# for hangcheck ...
# Save the status of check in a well known place, so that hangcheck can be
# sure to know where check is up to (getting test number from ps output is
# not reliable enough since the trace stuff has been introduced).
#
rm -rf /tmp/check.sts
echo "preamble" >/tmp/check.sts

# don't leave old full output behind on a clean run
rm -f check.full

[ -f check.time ] || touch check.time

FULL_HOST_DETAILS=`_full_platform_details`

cat <<EOF
PLATFORM      -- $FULL_HOST_DETAILS

EOF

seq="check"

[ -n "$TESTS_REMAINING_LOG" ] && echo $list > $TESTS_REMAINING_LOG

for seq in $list
do
    STORE="$WD/$seq"
    err=false
    echo -n "$seq"
    if [ -n "$TESTS_REMAINING_LOG" ] ; then
        sed -e "s/$seq//" -e 's/  / /' -e 's/^ *//' $TESTS_REMAINING_LOG > $TESTS_REMAINING_LOG.tmp
        mv $TESTS_REMAINING_LOG.tmp $TESTS_REMAINING_LOG
        sync
    fi

    if $showme
    then
	description=`sed -n '3s/^#//p' $seq`
	echo " $description"
	continue
    elif [ -f expunged ] && $expunge && egrep "^$seq([ 	]|\$)" expunged >/dev/null
    then
	echo " - expunged"
	rm -f $seq.out.bad
	echo "/^$seq\$/d" >>$tmp.expunged
    elif [ ! -f $seq ]
    then
	echo " - no such test?"
	echo "/^$seq\$/d" >>$tmp.expunged
    else
	# really going to try and run this one
	#
	rm -f $seq.out.bad
	lasttime=`sed -n -e "/^$seq /s/.* //p" <check.time`
	description=`sed -n '3s/^#//p' $seq`
	if [ "X$lasttime" != X ]; then
		echo -n " Last Used:${lasttime}s. $description"
	else
		echo -n " $description"	# prettier output with timestamps.
	fi
	rm -f core $seq.notrun

	# for hangcheck ...
	echo "$seq" >/tmp/check.sts

	start=`_wallclock`
	$timestamp && echo -n "	["`date "+%T"`"]"
	[ ! -x $seq ] && chmod u+x $seq # ensure we can run it
	./$seq >$tmp.out 2>&1
	sts=$?
	$timestamp && _timestamp
	stop=`_wallclock`

	if [ -f core ]
	then
	    echo -n " [dumped core]"
	    mv core $seq.core
	    err=true
	fi

	if [ -f $seq.notrun ]
	then
	    $timestamp || echo -n " [not run] "
	    $timestamp && echo " [not run]" && echo -n "	$seq -- "
	    cat $seq.notrun
	    notrun="$notrun $seq"
	else
	    if [ $sts -ne 0 ]
	    then
		echo -n " [failed, exit status $sts]"
		err=true
	    fi
	    if [ ! -f $seq.out ]
	    then
		echo " - no qualified output"
		err=true
	    else
		if diff $seq.out $tmp.out >/dev/null 2>&1
		then
		    echo ""
		    if $err
		    then
			:
		    else
			echo "$seq `expr $stop - $start`" >>$tmp.time
			if ! $keep_passed
			then
			    _cleanup
			    rm -rf $STORE
			fi
		    fi
		else
		    echo " - output mismatch (see $seq.out.bad)"
		    mv $tmp.out $seq.out.bad
		    $diff $seq.out $seq.out.bad
		    err=true
		fi
	    fi
	fi

    fi

    # come here for each test, except when $showme is true
    #
    [ -f $seq.notrun ] || try=`expr $try + 1`
    if $err
    then
	bad="$bad $seq"
	n_bad=`expr $n_bad + 1`
	quick=false
	if $exit_on_err
	then
	    break
	fi
    fi
    
    seq="after_$seq"
done

interrupt=false
status=`expr $n_bad`
exit
