]> mj.ucw.cz Git - eval.git/blob - lib
237a89139141f97ee4d9608cd9e00c92faa4d210
[eval.git] / lib
1 # The Evaluator -- Shell Function Library
2 # (c) 2001--2004 Martin Mares <mj@ucw.cz>
3
4 # General settings
5 shopt -s dotglob
6
7 # Logging functions.
8 # File handles used: fd1=log, fd2=progress
9
10 function log-init
11 {
12         exec >>$TDIR/log
13         HAVE_LOG=1
14 }
15
16 function pstart
17 {
18         echo >&2 -n "$@"
19 }
20
21 function pcont
22 {
23         echo >&2 -n "$@"
24 }
25
26 function pend
27 {
28         echo >&2 "$@"
29 }
30
31 function die
32 {
33         # Report an internal error
34         echo >&2 "$@"
35         [ -n "$HAVE_LOG" ] && echo "Fatal error: $@"
36         exit 2
37 }
38
39 function fatal
40 {
41         # Report a fatal error in the program being tested
42         echo >&2 "$@"
43         [ -n "$HAVE_LOG" ] && echo "Fatal error: $@"
44         exit 1
45 }
46
47 function try-ln
48 {
49         ln $1 $2 2>/dev/null || cp $1 $2
50 }
51
52 # Sandbox subroutines
53
54 function box-init
55 {
56         pstart "Preparing sandbox... "
57         if [ -z "$TEST_USER" -o "$TEST_USER" == $EVAL_USER ] ; then
58                 pcont "running locally (INSECURE), "
59                 TEST_USER=$EVAL_USER
60                 BOXDIR=box
61                 BOXCMD=bin/box
62                 mkdir -p box
63         else
64                 pcont "used account $TEST_USER, "
65                 BOXDIR=$MO_ROOT/eval/$TEST_USER
66                 BOXCMD=bin/box-$TEST_USER
67         fi
68         [ -d $BOXDIR -a -f $BOXCMD ] || die "Sandbox set up incorrectly"
69         BOXCMD="$BOXCMD -c$BOXDIR"
70         echo "Sandbox directory: $BOXDIR"
71         echo "Sandbox command: $BOXCMD"
72         box-clean
73         pend "OK"
74 }
75
76 function box-clean
77 {
78         [ -n "$BOXCMD" ] || die "box-init not called"
79         rm -rf $BOXDIR/*
80 }
81
82 # Initialization of testing directories
83
84 function dir-init
85 {
86         pstart "Initializing... "
87         HDIR=.
88         PDIR=problems/$PROBLEM
89         SDIR=solutions/$CONTESTANT/$PROBLEM
90         TDIR=testing/$CONTESTANT/$PROBLEM
91         TMPDIR=tmp
92         [ -d $PDIR ] || die "Problem $PROBLEM not known"
93         [ -d $SDIR ] || fatal "Solution of $PROBLEM not found"
94         mkdir -p $TDIR $TMPDIR
95         rm -rf $TDIR $TMPDIR
96         mkdir -p $TDIR $TMPDIR
97         cat >$TDIR/log <<EOF
98 Testing solution of $PROBLEM by $CONTESTANT
99 Test started at `date`
100 Contestant's solution directory: $SDIR
101 Problem directory: $PDIR
102 Testing directory: $TDIR
103 EOF
104         pend "OK"
105 }
106
107 # Locating source file in SDIR, pass name in SRCN (without path) and extension in SRCEXT
108
109 function locate-source
110 {
111         pstart "Finding source... "
112         if [ -n "$1" ] ; then
113                 SRCBASE=$(echo $1 | sed 's/\.\([^.]\+\)//')
114                 SRCEXT=$(echo $1 | sed 's/.*\.\([^.]\+\)/\1/')
115                 if [ -n "$SRCEXT" ] ; then
116                         # Full name given, so just check the extension and existence
117                         SRCN=$1
118                         if [ ${SRCN:0:1} == / ] ; then
119                                 SRCN=${SRCN:1}
120                                 SDIR=
121                         fi
122                         [ -f "$SDIR/$SRCN" ] || die "Cannot find source file $SDIR/$SRCN"
123                         SRCEXT_OK=
124                         for a in $EXTENSIONS ; do
125                                 if [ $a == $SRCEXT ] ; then
126                                         pend $SDIR/$SRCN
127                                         echo "Explicitly set source file: $SDIR/$SRCN"
128                                         return 0
129                                 fi
130                         done
131                         die "Unknown extension .$SRCEXT"
132                 fi
133         else
134                 SBASE=$PROBLEM
135         fi
136         for a in $EXTENSIONS ; do
137                 if [ -f $SDIR/$SBASE.$a ] ; then
138                         [ -z "$SRCN" ] || die "Multiple source files found: $SDIR/$PROBLEM.$a and $SDIR/$SRCN. Please fix."
139                         SRCN=$SBASE.$a
140                         SRCEXT=$a
141                 fi
142         done
143         [ -n "$SRCN" ] || fatal "NOT FOUND"
144         pend $SRCN
145         echo "Found source file: $SDIR/$SRCN"
146 }
147
148 # Compilation (compile SDIR/SRCN with PDIR/COMP_EXTRAS to EXE=TDIR/PROBLEM)
149
150 function compile
151 {
152         pstart "Compiling... "
153         # Beware, the original SRCN can be a strange user-supplied name
154         SRC=$PROBLEM.$SRCEXT
155         cp -a $SDIR/$SRCN $TDIR/$SRC
156         if [ -n "$COMP_EXTRAS" ] ; then
157                 echo "Extras: $COMP_EXTRAS"
158                 for a in $COMP_EXTRAS ; do cp $PDIR/$a $TDIR/ ; done
159         fi
160         box-clean
161         for a in $SRC $COMP_EXTRAS ; do cp $TDIR/$a $BOXDIR/ ; done
162         EXE=$PROBLEM
163         CCMD=COMP_$SRCEXT
164         CCMD=`eval echo ${!CCMD}`
165         COMP_SANDBOX_OPTS=`eval echo $COMP_SANDBOX_OPTS`
166         echo "Compiler command: $CCMD"
167         echo "Compiler sandbox options: $COMP_SANDBOX_OPTS"
168         eval $COMP_SANDBOX_INIT
169
170         echo "Compiler input files:"
171         ls -Al $BOXDIR
172         echo "Compiler output:"
173         if ! $BOXCMD $COMP_SANDBOX_OPTS -- $CCMD 2>$TDIR/compile.out ; then
174                 COMPILE_MSG="`cat $TDIR/compile.out`"
175                 pend "FAILED: $COMPILE_MSG"
176                 echo "$COMPILE_MSG"
177                 return 1
178         fi
179         cat $TDIR/compile.out
180         rm $TDIR/compile.out
181         echo "Compiler output files:"
182         ls -Al $BOXDIR
183         if [ ! -f $BOXDIR/$PROBLEM ] ; then
184                 pend "FAILED: Missing executable file"
185                 echo "Missing executable file"
186                 return 1
187         fi
188         EXE=$TDIR/$PROBLEM
189         cp -a $BOXDIR/$PROBLEM $EXE
190         echo "Compiled OK, result copied to $EXE"
191         pend "OK"
192 }
193
194 # Running of test program according to current task type (returns exit code and TEST_MSG)
195
196 function test-run
197 {
198         test-run-$TASK_TYPE
199 }
200
201 function test-result
202 {
203         P=$1
204         M=$2
205         if [ -s $TDIR/$TEST.pts ] ; then
206                 P=`cat $TDIR/$TEST.pts`
207                 rm $TDIR/$TEST.pts
208         fi
209         echo "Verdict: $M"
210         echo "Points: $P"
211         test-verdict $P "$M"
212 }
213
214 function test-prolog
215 {
216         pcont "<init> "
217         box-clean
218         echo "Executable file: $TDIR/$PROBLEM"
219         if [ ! -x $TDIR/$PROBLEM ] ; then
220                 test-result 0 "Compile error."
221         fi
222         cp $TDIR/$PROBLEM $BOXDIR/
223         BOX_EXTRAS=
224         IN_TYPE=${IN_TYPE:-$IO_TYPE}
225         OUT_TYPE=${OUT_TYPE:-$IO_TYPE}
226         case $IN_TYPE in
227                 file)   echo "Input file: $PROBLEM.in (from $PDIR/$TEST.in)"
228                         try-ln $PDIR/$TEST.in $TDIR/$TEST.in
229                         cp $PDIR/$TEST.in $BOXDIR/$PROBLEM.in
230                         [ $TASK_TYPE == interactive ] || BOX_EXTRAS="$BOX_EXTRAS -i/dev/null"
231                         ;;
232                 stdio)  echo "Input file: <stdin> (from $PDIR/$TEST.in)"
233                         try-ln $PDIR/$TEST.in $TDIR/$TEST.in
234                         cp $PDIR/$TEST.in $BOXDIR/.stdin
235                         BOX_EXTRAS="$BOX_EXTRAS -i.stdin"
236                         ;;
237                 none)   echo "Input file: <none>"
238                         ;;
239                 *)      die "Unknown IN_TYPE $IN_TYPE"
240                         ;;
241         esac
242         case $OUT_TYPE in
243                 file)   echo "Output file: $PROBLEM.out"
244                         [ $TASK_TYPE == interactive ] || BOX_EXTRAS="$BOX_EXTRAS -o/dev/null"
245                         ;;
246                 stdio)  echo "Output file: <stdout>"
247                         BOX_EXTRAS="$BOX_EXTRAS -o.stdout"
248                         ;;
249                 none)   echo "Output file: <none>"
250                         ;;
251                 *)      die "Unknown OUT_TYPE $OUT_TYPE"
252                         ;;
253         esac
254         echo "Timeout: $TIME_LIMIT s"
255         echo "Memory: $MEM_LIMIT KB"
256         eval $SANDBOX_INIT
257         echo "Sandbox contents before start:"
258         ls -Al $BOXDIR
259 }
260
261 function test-epilog
262 {
263         echo "Sandbox contents after exit:"
264         ls -Al $BOXDIR
265         case ${OUT_TYPE:-$IO_TYPE} in
266                 file)   [ -f $BOXDIR/$PROBLEM.out ] || test-result 0 "No output file."
267                         cp $BOXDIR/$PROBLEM.out $TDIR/$TEST.out
268                         ;;
269                 stdio)  [ -f $BOXDIR/.stdout ] || test-result 0 "No output file."
270                         cp $BOXDIR/.stdout $TDIR/$TEST.out
271                         ;;
272         esac
273 }
274
275 # Running of test program with file input/output
276
277 function test-run-file
278 {
279         test-prolog
280         pcont "<run> "
281         BOXOPTS="`eval echo $TEST_SANDBOX_OPTS`$BOX_EXTRAS"
282         echo "Sandbox options: $BOXOPTS"
283         if ! $BOXCMD $BOXOPTS -- ./$PROBLEM 2>$TMPDIR/exec.out ; then
284                 cat $TMPDIR/exec.out
285                 MSG=`tail -1 $TMPDIR/exec.out`
286                 test-result 0 "$MSG"
287         fi
288         cat $TMPDIR/exec.out
289         test-epilog
290 }
291
292 # Running of interactive test programs
293
294 function test-run-interactive
295 {
296         test-prolog
297         pcont "<run> "
298         BOXOPTS="`eval echo $TEST_SANDBOX_OPTS`$BOX_EXTRAS"
299         echo "Sandbox options: $BOXOPTS"
300         ICCMD=`eval echo $IA_CHECK`
301         echo "Interactive checker: $ICCMD"
302         if ! $HDIR/bin/iwrapper $BOXCMD $BOXOPTS -- ./$PROBLEM @@ $ICCMD 2>$TMPDIR/exec.out ; then
303                 cat $TMPDIR/exec.out
304                 MSG="`head -1 $TMPDIR/exec.out`"
305                 test-result 0 "$MSG"
306         fi
307         cat $TMPDIR/exec.out
308         test-epilog
309 }
310
311 # Syntax checks
312
313 function syntax-check
314 {
315         [ -n "$SYNTAX_CHECK" ] || return 0
316         pcont "<syntax> "
317         SCHECK=`eval echo $SYNTAX_CHECK`
318         echo "Syntax check command: $SCHECK"
319         if ! eval $SCHECK 2>$TMPDIR/exec.out ; then
320                 cat $TMPDIR/exec.out
321                 MSG=`tail -1 $TMPDIR/exec.out`
322                 if [ -z "$MSG" ] ; then MSG="Wrong syntax." ; fi
323                 test-result 0 "$MSG"
324         fi
325         cat $TMPDIR/exec.out
326 }
327
328 # Output checks
329
330 function output-check
331 {
332         MSG=
333         if [ -n "$OUTPUT_CHECK" -a "$OUT_TYPE" != none ] ; then
334                 pcont "<check> "
335                 [ -f $PDIR/$TEST.out ] && ln $PDIR/$TEST.out $TDIR/$TEST.ok
336                 OCHECK=`eval echo $OUTPUT_CHECK`
337                 echo "Output check command: $OCHECK"
338                 if ! eval $OCHECK 2>$TMPDIR/exec.out ; then
339                         cat $TMPDIR/exec.out
340                         MSG=`tail -1 $TMPDIR/exec.out`
341                         if [ -z "$MSG" ] ; then MSG="Wrong answer." ; fi
342                         test-result 0 "$MSG"
343                 fi
344                 cat $TMPDIR/exec.out
345                 MSG=`tail -1 $TMPDIR/exec.out`
346         fi
347         if [ -z "$MSG" ] ; then MSG="OK" ; fi
348         test-result $POINTS_PER_TEST "$MSG"
349 }
350
351 # Setup of public commands
352
353 function public-setup
354 {
355         HDIR=$MO_ROOT
356         PDIR=$MO_ROOT/problems/$PROBLEM
357         SDIR=.
358         TDIR=~/.test
359         TMPDIR=~/.test
360         [ -d $PDIR ] || die "Unknown problem $PROBLEM"
361
362         pstart "Initializing... "
363         mkdir -p $TDIR
364         rm -rf $TDIR/*
365         BOXDIR=~/.box
366         mkdir -p $BOXDIR
367         rm -rf $BOXDIR/*
368         BOXCMD="$MO_ROOT/bin/box -c$BOXDIR"
369         exec >log
370         pend "OK  (see 'log' for details)"
371 }
372
373 # Locate output of open data problem, test case TEST
374
375 function open-locate
376 {
377         [ -f $PDIR/$TEST.in ] || die "Unknown test $TEST"
378         if [ -n "$1" ] ; then
379                 SRCN=$1
380         else
381                 SRCN=$SDIR/$PROBLEM$TEST.out
382         fi
383         [ -f $SRCN ] || fatal "Output file $SRCN not found"
384 }