# The Evaluator -- Shell Function Library # (c) 2001--2007 Martin Mares # General settings shopt -s dotglob # Logging functions. # File handles used: fd1=log, fd2=progress function log-init { exec >>$TDIR/log HAVE_LOG=1 } function pstart { echo >&2 -n "$@" } function pcont { echo >&2 -n "$@" } function pend { echo >&2 "$@" } function die { # Report an internal error echo >&2 "$@" [ -n "$HAVE_LOG" ] && echo "Fatal error: $@" exit 2 } function fatal { # Report a fatal error in the program being tested echo >&2 "$@" [ -n "$HAVE_LOG" ] && echo "Fatal error: $@" exit 1 } function try-ln { ln $1 $2 2>/dev/null || cp $1 $2 } # Sandbox subroutines function box-init { pstart "Preparing sandbox... " if [ -z "$TEST_USER" -o "$TEST_USER" == $EVAL_USER ] ; then pcont "running locally (INSECURE), " TEST_USER=$EVAL_USER BOXDIR=box BOXCMD=bin/box mkdir -p box else pcont "used account $TEST_USER, " BOXDIR=$MO_ROOT/eval/$TEST_USER BOXCMD=bin/box-$TEST_USER fi [ -d $BOXDIR -a -f $BOXCMD ] || die "Sandbox set up incorrectly" BOXCMD="$BOXCMD -c$BOXDIR" echo "Sandbox directory: $BOXDIR" echo "Sandbox command: $BOXCMD" box-clean pend "OK" } function box-clean { [ -n "$BOXCMD" ] || die "box-init not called" rm -rf $BOXDIR/* } # Initialization of testing directories function dir-init { pstart "Initializing... " HDIR=. PDIR=problems/$PROBLEM SDIR=solutions/$CONTESTANT/$PROBLEM TDIR=testing/$CONTESTANT/$PROBLEM TMPDIR=tmp [ -d $PDIR ] || die "Problem $PROBLEM not known" [ -d $SDIR ] || fatal "Solution of $PROBLEM not found" mkdir -p $TDIR $TMPDIR rm -rf $TDIR $TMPDIR mkdir -p $TDIR $TMPDIR cat >$TDIR/log <$TDIR/compile.out ; then COMPILE_MSG="`cat $TDIR/compile.out`" pend "FAILED: $COMPILE_MSG" echo "$COMPILE_MSG" return 1 fi cat $TDIR/compile.out rm $TDIR/compile.out echo "Compiler output files:" ls -Al $BOXDIR if [ ! -f $BOXDIR/$PROBLEM ] ; then pend "FAILED: Missing executable file" echo "Missing executable file" return 1 fi EXE=$TDIR/$PROBLEM cp -a $BOXDIR/$PROBLEM $EXE echo "Compiled OK, result copied to $EXE" pend "OK" } # Running of test program according to current task type (returns exit code and TEST_MSG) function test-run { test-run-$TASK_TYPE } function test-result { P=$1 M=$2 if [ -s $TDIR/$TEST.pts ] ; then P=`cat $TDIR/$TEST.pts` rm $TDIR/$TEST.pts fi echo "Verdict: $M" echo "Points: $P" test-verdict $P "$M" } function test-prolog { pcont " " box-clean echo "Executable file: $TDIR/$PROBLEM" if [ ! -x $TDIR/$PROBLEM ] ; then test-result 0 "Compile error." fi cp $TDIR/$PROBLEM $BOXDIR/ BOX_EXTRAS= IN_TYPE=${IN_TYPE:-$IO_TYPE} OUT_TYPE=${OUT_TYPE:-$IO_TYPE} case $IN_TYPE in file) echo "Input file: $PROBLEM.in (from $PDIR/$TEST.in)" try-ln $PDIR/$TEST.in $TDIR/$TEST.in cp $PDIR/$TEST.in $BOXDIR/$PROBLEM.in [ $TASK_TYPE == interactive ] || BOX_EXTRAS="$BOX_EXTRAS -i/dev/null" ;; stdio) echo "Input file: (from $PDIR/$TEST.in)" try-ln $PDIR/$TEST.in $TDIR/$TEST.in cp $PDIR/$TEST.in $BOXDIR/.stdin BOX_EXTRAS="$BOX_EXTRAS -i.stdin" ;; none) echo "Input file: " ;; *) die "Unknown IN_TYPE $IN_TYPE" ;; esac case $OUT_TYPE in file) echo "Output file: $PROBLEM.out" [ $TASK_TYPE == interactive ] || BOX_EXTRAS="$BOX_EXTRAS -o/dev/null" ;; stdio) echo "Output file: " BOX_EXTRAS="$BOX_EXTRAS -o.stdout" ;; none) echo "Output file: " ;; *) die "Unknown OUT_TYPE $OUT_TYPE" ;; esac echo "Timeout: $TIME_LIMIT s" echo "Memory: $MEM_LIMIT KB" eval $SANDBOX_INIT echo "Sandbox contents before start:" ls -Al $BOXDIR } function test-epilog { echo "Sandbox contents after exit:" ls -Al $BOXDIR case ${OUT_TYPE:-$IO_TYPE} in file) [ -f $BOXDIR/$PROBLEM.out ] || test-result 0 "No output file." cp $BOXDIR/$PROBLEM.out $TDIR/$TEST.out ;; stdio) [ -f $BOXDIR/.stdout ] || test-result 0 "No output file." cp $BOXDIR/.stdout $TDIR/$TEST.out ;; esac } # Running of test program with file input/output function test-run-file { test-prolog pcont " " BOXOPTS="`eval echo $TEST_SANDBOX_OPTS`$BOX_EXTRAS" echo "Sandbox options: $BOXOPTS" if ! $BOXCMD $BOXOPTS -- ./$PROBLEM 2>$TMPDIR/exec.out ; then cat $TMPDIR/exec.out MSG=`tail -1 $TMPDIR/exec.out` test-result 0 "$MSG" fi cat $TMPDIR/exec.out test-epilog } # Running of interactive test programs function test-run-interactive { test-prolog pcont " " BOXOPTS="`eval echo $TEST_SANDBOX_OPTS`$BOX_EXTRAS" echo "Sandbox options: $BOXOPTS" ICCMD=`eval echo $IA_CHECK` echo "Interactive checker: $ICCMD" if ! $HDIR/bin/iwrapper $BOXCMD $BOXOPTS -- ./$PROBLEM @@ $ICCMD 2>$TMPDIR/exec.out ; then cat $TMPDIR/exec.out MSG="`head -1 $TMPDIR/exec.out`" test-result 0 "$MSG" fi cat $TMPDIR/exec.out test-epilog } # Syntax checks function syntax-check { [ -n "$SYNTAX_CHECK" ] || return 0 pcont " " SCHECK=`eval echo $SYNTAX_CHECK` echo "Syntax check command: $SCHECK" if ! eval $SCHECK 2>$TMPDIR/exec.out ; then cat $TMPDIR/exec.out MSG=`tail -1 $TMPDIR/exec.out` if [ -z "$MSG" ] ; then MSG="Wrong syntax." ; fi test-result 0 "$MSG" fi cat $TMPDIR/exec.out } # Output checks function output-check { MSG= if [ -n "$OUTPUT_CHECK" -a "$OUT_TYPE" != none ] ; then pcont " " [ -f $PDIR/$TEST.out ] && ln $PDIR/$TEST.out $TDIR/$TEST.ok OCHECK=`eval echo $OUTPUT_CHECK` echo "Output check command: $OCHECK" if ! eval $OCHECK 2>$TMPDIR/exec.out ; then cat $TMPDIR/exec.out MSG=`tail -1 $TMPDIR/exec.out` if [ -z "$MSG" ] ; then MSG="Wrong answer." ; fi test-result 0 "$MSG" fi cat $TMPDIR/exec.out MSG=`tail -1 $TMPDIR/exec.out` fi if [ -z "$MSG" ] ; then MSG="OK" ; fi test-result $POINTS_PER_TEST "$MSG" } # Setup of public commands function public-setup { HDIR=$MO_ROOT PDIR=$MO_ROOT/problems/$PROBLEM SDIR=. TDIR=~/.test TMPDIR=~/.test [ -d $PDIR ] || die "Unknown problem $PROBLEM" pstart "Initializing... " mkdir -p $TDIR rm -rf $TDIR/* BOXDIR=~/.box mkdir -p $BOXDIR rm -rf $BOXDIR/* BOXCMD="$MO_ROOT/bin/box -c$BOXDIR" exec >check-log pend "OK (see 'check-log' for details)" } # Locate output of open data problem, test case TEST # Beware, SDIR and SRCN can contain spaces and other strange user-supplied characters. function open-locate { [ -f $PDIR/$TEST.in ] || die "Unknown test $TEST" if [ -n "$1" ] ; then SDIR=`dirname "$1"` SRCN=`basename "$1"` else SRCN=$SDIR/$PROBLEM$TEST.out fi [ -f $SRCN ] || fatal "Output file $SRCN not found" }