setup: add section support
[xfstests-dev.git] / check
1 #!/bin/bash
2 #
3 # Control script for QA
4 #
5 # Copyright (c) 2000-2002,2006 Silicon Graphics, Inc.  All Rights Reserved.
6 #
7 # This program is free software; you can redistribute it and/or
8 # modify it under the terms of the GNU General Public License as
9 # published by the Free Software Foundation.
10 #
11 # This program is distributed in the hope that it would be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write the Free Software Foundation,
18 # Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19 #
20 #
21
22 tmp=/tmp/$$
23 status=0
24 needwrap=true
25 needsum=true
26 n_try=0
27 try=""
28 n_bad=0
29 sum_bad=0
30 bad=""
31 notrun=""
32 interrupt=true
33 diff="diff -u"
34 showme=false
35 have_test_arg=false
36 randomize=false
37 export here=`pwd`
38 xfile=""
39
40 DUMP_OUTPUT=false
41
42 # start the initialisation work now
43 iam=check
44
45 export MSGVERB="text:action"
46 export QA_CHECK_FS=${QA_CHECK_FS:=true}
47
48 # number of diff lines from a failed test, 0 for whole output
49 export DIFF_LENGTH=${DIFF_LENGTH:=10}
50
51 # by default don't output timestamps
52 timestamp=${TIMESTAMP:=false}
53
54 rm -f $tmp.list $tmp.tmp $tmp.grep $here/$iam.out $tmp.xlist
55
56 SRC_GROUPS="generic shared"
57 export SRC_DIR="tests"
58
59 usage()
60 {
61     echo "Usage: $0 [options] [testlist]"'
62
63 check options
64     -nfs                test NFS
65     -cifs               test CIFS
66     -overlay            test overlay
67     -tmpfs              test TMPFS
68     -l                  line mode diff
69     -udiff              show unified diff (default)
70     -n                  show me, do not run tests
71     -T                  output timestamps
72     -r                  randomize test order
73     -d                  dump test output to stdout
74     --large-fs          optimise scratch device for large filesystems
75     -s section          run only specified section from config file
76
77 testlist options
78     -g group[,group...] include tests from these groups
79     -x group[,group...] exclude tests from these groups
80     -X file             exclude individual tests
81     -E external_file    exclude individual tests
82     [testlist]          include tests matching names in testlist
83 '
84             exit 0
85 }
86
87 get_group_list()
88 {
89         grp=$1
90
91         for d in $SRC_GROUPS $FSTYP; do
92                 l=$(sed -n < $SRC_DIR/$d/group \
93                         -e 's/#.*//' \
94                         -e 's/$/ /' \
95                         -e "s;^\($VALID_TEST_NAME\).* $grp .*;$SRC_DIR/$d/\1;p")
96                 grpl="$grpl $l"
97         done
98         echo $grpl
99 }
100
101 # Find all tests, excluding files that are test metadata such as group files.
102 # It matches test names against $VALID_TEST_NAME defined in common/rc
103 get_all_tests()
104 {
105         touch $tmp.list
106         for d in $SRC_GROUPS $FSTYP; do
107                 ls $SRC_DIR/$d/* | \
108                         grep -v "\..*" | \
109                         grep "^$SRC_DIR/$d/$VALID_TEST_NAME"| \
110                         grep -v "group\|Makefile" >> $tmp.list 2>/dev/null
111         done
112 }
113
114 # takes the list of tests to run in $tmp.list, and removes the tests passed to
115 # the function from that list.
116 trim_test_list()
117 {
118         test_list="$*"
119
120         rm -f $tmp.grep
121         numsed=0
122         for t in $test_list
123         do
124             if [ $numsed -gt 100 ]; then
125                 grep -v -f $tmp.grep <$tmp.list >$tmp.tmp
126                 mv $tmp.tmp $tmp.list
127                 numsed=0
128                 rm -f $tmp.grep
129             fi
130             echo "^$t\$" >>$tmp.grep
131             numsed=`expr $numsed + 1`
132         done
133         grep -v -f $tmp.grep <$tmp.list >$tmp.tmp
134         mv $tmp.tmp $tmp.list
135 }
136
137
138 _wallclock()
139 {
140     date "+%s"
141 }
142
143 _timestamp()
144 {
145     now=`date "+%T"`
146     echo -n " [$now]"
147 }
148
149 _prepare_test_list()
150 {
151         unset list
152         # Tests specified on the command line
153         if [ -s $tmp.arglist ]; then
154                 cat $tmp.arglist > $tmp.list
155         else
156                 touch $tmp.list
157         fi
158
159         # Specified groups to include
160         for group in $GROUP_LIST; do
161                 list=$(get_group_list $group)
162                 if [ -z "$list" ]; then
163                         echo "Group \"$group\" is empty or not defined?"
164                         exit 1
165                 fi
166
167                 for t in $list; do
168                         grep -s "^$t\$" $tmp.list >/dev/null || \
169                                                         echo "$t" >>$tmp.list
170                 done
171         done
172
173         if ! $have_test_arg && [ -z "$GROUP_LIST" ]; then
174                 # no test numbers, do everything
175                 get_all_tests
176         fi
177
178         # Specified groups to exclude
179         for xgroup in $XGROUP_LIST; do
180                 list=$(get_group_list $xgroup)
181                 if [ -z "$list" ]; then
182                         echo "Group \"$xgroup\" is empty or not defined?"
183                         exit 1
184                 fi
185
186                 trim_test_list $list
187         done
188
189         # sort the list of tests into numeric order
190         list=`sort -n $tmp.list | uniq`
191         rm -f $tmp.list $tmp.tmp $tmp.grep
192
193         if $randomize
194         then
195                 list=`echo $list | awk -f randomize.awk`
196         fi
197 }
198
199 # Process command arguments first.
200 while [ $# -gt 0 ]; do
201         case "$1" in
202         -\? | -h | --help) usage ;;
203
204         -nfs)           FSTYP=nfs ;;
205         -cifs)          FSTYP=cifs ;;
206         -overlay)       FSTYP=overlay ;;
207         -tmpfs)         FSTYP=tmpfs ;;
208
209         -g)     group=$2 ; shift ;
210                 GROUP_LIST="$GROUP_LIST ${group//,/ }"
211                 ;;
212
213         -x)     xgroup=$2 ; shift ;
214                 XGROUP_LIST="$XGROUP_LIST ${xgroup//,/ }"
215                 ;;
216
217         -X)     xfile=$2; shift ;
218                 for d in $SRC_GROUPS $FSTYP; do
219                         [ -f $SRC_DIR/$d/$xfile ] || continue
220                         for f in `cat $SRC_DIR/$d/$xfile`; do
221                                 echo $d/$f >> $tmp.xlist
222                         done
223                 done
224                 ;;
225         -E)     xfile=$2; shift ;
226                 if [ -f $xfile ]; then
227                         cat "$xfile" >> $tmp.xlist
228                 fi
229                 ;;
230         -s)     RUN_SECTION="$RUN_SECTION $2"; shift ;;
231         -l)     diff="diff" ;;
232         -udiff) diff="$diff -u" ;;
233
234         -n)     showme=true ;;
235         -r)     randomize=true ;;
236
237         -T)     timestamp=true ;;
238         -d)     DUMP_OUTPUT=true ;;
239
240         --large-fs) export LARGE_SCRATCH_DEV=yes ;;
241         --extra-space=*) export SCRATCH_DEV_EMPTY_SPACE=${r#*=} ;;
242
243         -*)     usage ;;
244         *)      # not an argument, we've got tests now.
245                 have_test_arg=true ;;
246         esac
247
248         # if we've found a test specification, the break out of the processing
249         # loop before we shift the arguments so that this is the first argument
250         # that we process in the test arg loop below.
251         if $have_test_arg; then
252                 break;
253         fi
254
255         shift
256 done
257
258 # we need common/config, source it after processing args, overlay needs FSTYP
259 # set before sourcing common/config
260 if ! . ./common/config; then
261         echo "$iam: failed to source common/config"
262         exit 1
263 fi
264
265 # Process tests from command line now.
266 if $have_test_arg; then
267         while [ $# -gt 0 ]; do
268                 case "$1" in
269                 -*)     echo "Arguments before tests, please!"
270                         status=1
271                         exit $status
272                         ;;
273                 *)      test_dir=`dirname $1`
274                         test_dir=${test_dir#$SRC_DIR/*}
275                         test_name=`basename $1`
276                         group_file=$SRC_DIR/$test_dir/group
277
278                         if egrep "^$test_name" $group_file >/dev/null ; then
279                                 # in group file ... OK
280                                 echo $SRC_DIR/$test_dir/$test_name >>$tmp.arglist
281                         else
282                                 # oops
283                                 echo "$1 - unknown test, ignored"
284                         fi
285                         ;;
286                 esac
287
288                 shift
289         done
290 fi
291
292 # we need common/rc
293 if ! . ./common/rc
294 then
295     echo "check: failed to source common/rc"
296     exit 1
297 fi
298
299 if [ `id -u` -ne 0 ]
300 then
301     echo "check: QA must be run as root"
302     exit 1
303 fi
304
305 _wipe_counters()
306 {
307         n_try="0"
308         n_bad="0"
309         unset try notrun bad
310 }
311
312 _wrapup()
313 {
314     seq="check"
315     check="$RESULT_BASE/check"
316
317     if $showme
318     then
319         :
320     elif $needwrap
321     then
322         if [ -f $check.time -a -f $tmp.time ]
323         then
324             cat $check.time $tmp.time \
325             | $AWK_PROG '
326         { t[$1] = $2 }
327 END     { if (NR > 0) {
328             for (i in t) print i " " t[i]
329           }
330         }' \
331             | sort -n >$tmp.out
332             mv $tmp.out $check.time
333         fi
334
335         echo "" >>$check.log
336         date >>$check.log
337         echo $list | fmt | sed -e 's/^/    /' -e "s;$SRC_DIR/;;g" >>$check.log
338         $interrupt && echo "Interrupted!" >>$check.log
339
340         echo "SECTION       -- $section" >>$tmp.summary
341         echo "=========================" >>$tmp.summary
342         if [ ! -z "$n_try" -a $n_try != 0 ]
343         then
344             echo "Ran:$try"
345             echo "Ran:$try" >>$tmp.summary
346         fi
347
348         if [ ! -z "$notrun" ]
349         then
350             echo "Not run:$notrun"
351             echo "Not run:$notrun" >>$check.log
352             echo "Not run:$notrun" >>$tmp.summary
353         fi
354
355         if [ ! -z "$n_bad" -a $n_bad != 0 ]
356         then
357             echo "Failures:$bad"
358             echo "Failed $n_bad of $n_try tests"
359             echo "Failures:$bad" | fmt >>$check.log
360             echo "Failed $n_bad of $n_try tests" >>$check.log
361             echo "Failures:$bad" >>$tmp.summary
362             echo "Failed $n_bad of $n_try tests" >>$tmp.summary
363         else
364             echo "Passed all $n_try tests"
365             echo "Passed all $n_try tests" >>$check.log
366             echo "Passed all $n_try tests" >>$tmp.summary
367         fi
368         echo "" >>$tmp.summary
369         needwrap=false
370     fi
371
372     sum_bad=`expr $sum_bad + $n_bad`
373     _wipe_counters
374     rm -f /tmp/*.rawout /tmp/*.out /tmp/*.err /tmp/*.time
375     if ! $OPTIONS_HAVE_SECTIONS; then
376         rm -f $tmp.*
377     fi
378 }
379
380 _summary()
381 {
382         _wrapup
383         if $showme; then
384                 :
385         elif $needsum; then
386                 count=`wc -L $tmp.summary | cut -f1 -d" "`
387                 cat $tmp.summary
388                 needsum=false
389         fi
390         rm -f $tmp.*
391 }
392
393 _check_filesystems()
394 {
395         if [ -f ${RESULT_DIR}/require_test ]; then
396                 _check_test_fs || err=true
397                 rm -f ${RESULT_DIR}/require_test
398         fi
399         if [ -f ${RESULT_DIR}/require_scratch ]; then
400                 _check_scratch_fs || err=true
401                 rm -f ${RESULT_DIR}/require_scratch
402         fi
403 }
404
405 _prepare_test_list
406
407 if $OPTIONS_HAVE_SECTIONS; then
408         trap "_summary; exit \$status" 0 1 2 3 15
409 else
410         trap "_wrapup; exit \$status" 0 1 2 3 15
411 fi
412
413 for section in $HOST_OPTIONS_SECTIONS; do
414         OLD_FSTYP=$FSTYP
415         OLD_MOUNT_OPTIONS=$MOUNT_OPTIONS
416         get_next_config $section
417
418         # Do we need to run only some sections ?
419         if [ ! -z "$RUN_SECTION" ]; then
420                 skip=true
421                 for s in $RUN_SECTION; do
422                         if [ $section == $s ]; then
423                                 skip=false
424                                 break;
425                         fi
426                 done
427                 if $skip; then
428                         continue
429                 fi
430         fi
431
432         mkdir -p $RESULT_BASE
433         if [ ! -d $RESULT_BASE ]; then
434                 echo "failed to create results directory $RESULT_BASE"
435                 status=1
436                 exit
437         fi
438
439         if $OPTIONS_HAVE_SECTIONS; then
440                 echo "SECTION       -- $section"
441         fi
442
443         if $RECREATE_TEST_DEV || [ "$OLD_FSTYP" != "$FSTYP" ]; then
444                 echo "RECREATING    -- $FSTYP on $TEST_DEV"
445                 _test_unmount 2> /dev/null
446                 if ! _test_mkfs >$tmp.err 2>&1
447                 then
448                         echo "our local _test_mkfs routine ..."
449                         cat $tmp.err
450                         echo "check: failed to mkfs \$TEST_DEV using specified options"
451                         status=1
452                         exit
453                 fi
454                 out=`_mount_or_remount_rw "$MOUNT_OPTIONS" $TEST_DEV $TEST_DIR`
455                 if [ $? -ne 1 ]; then
456                         echo $out
457                         status=1
458                         exit
459                 fi
460                 _prepare_test_list
461         elif [ "$OLD_MOUNT_OPTIONS" != "$MOUNT_OPTIONS" ]; then
462                 _test_unmount 2> /dev/null
463                 out=`_mount_or_remount_rw "$MOUNT_OPTIONS" $TEST_DEV $TEST_DIR`
464                 if [ $? -ne 1 ]; then
465                         echo $out
466                         status=1
467                         exit
468                 fi
469         fi
470
471         init_rc
472
473         seq="check"
474         check="$RESULT_BASE/check"
475
476         # don't leave old full output behind on a clean run
477         rm -f $check.full
478
479         [ -f $check.time ] || touch $check.time
480
481         # print out our test configuration
482         echo "FSTYP         -- `_full_fstyp_details`"
483         echo "PLATFORM      -- `_full_platform_details`"
484         if [ ! -z "$SCRATCH_DEV" ]; then
485           echo "MKFS_OPTIONS  -- `_scratch_mkfs_options`"
486           echo "MOUNT_OPTIONS -- `_scratch_mount_options`"
487         fi
488         echo
489         needwrap=true
490
491         if [ ! -z "$SCRATCH_DEV" ]; then
492           _scratch_unmount 2> /dev/null
493           # call the overridden mkfs - make sure the FS is built
494           # the same as we'll create it later.
495
496           if ! _scratch_mkfs >$tmp.err 2>&1
497           then
498               echo "our local _scratch_mkfs routine ..."
499               cat $tmp.err
500               echo "check: failed to mkfs \$SCRATCH_DEV using specified options"
501               status=1
502               exit
503           fi
504
505           # call the overridden mount - make sure the FS mounts with
506           # the same options that we'll mount with later.
507           if ! _scratch_mount >$tmp.err 2>&1
508           then
509               echo "our local mount routine ..."
510               cat $tmp.err
511               echo "check: failed to mount \$SCRATCH_DEV using specified options"
512               status=1
513               exit
514           fi
515         fi
516
517         seqres="$check"
518         _check_test_fs
519
520         for seq in $list
521         do
522             err=false
523
524             # the filename for the test and the name output are different.
525             # we don't include the tests/ directory in the name output.
526             export seqnum=`echo $seq | sed -e "s;$SRC_DIR/;;"`
527
528             # Similarly, the result directory needs to replace the tests/
529             # part of the test location.
530             group=`dirname $seq`
531             if $OPTIONS_HAVE_SECTIONS; then
532                 export RESULT_DIR=`echo $group | sed -e "s;$SRC_DIR;${RESULT_BASE}/$section;"`
533                 seqres="$RESULT_BASE/$section/$seqnum"
534             else
535                 export RESULT_DIR=`echo $group | sed -e "s;$SRC_DIR;$RESULT_BASE;"`
536                 seqres="$RESULT_BASE/$seqnum"
537             fi
538
539             mkdir -p $RESULT_DIR
540
541             echo -n "$seqnum"
542
543                 if $showme; then
544                         echo
545                         continue
546                 elif [ ! -f $seq ]; then
547                         # Try to get full name in case the user supplied only seq id
548                         # and the test has a name. A bit of hassle to find really
549                         # the test and not its sample output or helping files.
550                         bname=$(basename $seq)
551                         full_seq=$(find $(dirname $seq) -name $bname* -executable |
552                                 awk '(NR == 1 || length < length(shortest)) { shortest = $0 }\
553                                         END { print shortest }')
554                         if [ -f $full_seq ] \
555                                 && [ x$(echo $bname | grep -o "^$VALID_TEST_ID") != x ]; then
556                                 seq=$full_seq
557                                 seqnum=${full_seq#*/}
558                         fi
559                 fi
560
561                 if [ ! -f $seq ]; then
562                         echo " - no such test?"
563                 else
564                 # really going to try and run this one
565                 #
566                 rm -f $seqres.out.bad
567
568                 # check if we really should run it
569                 if [ -s $tmp.xlist ]; then
570                         if grep $seqnum $tmp.xlist > /dev/null 2>&1 ; then
571                                 echo "       [expunged]"
572                                 continue
573                         fi
574                 fi
575
576                 # slashes now in names, sed barfs on them so use grep
577                 lasttime=`grep -w ^$seqnum $check.time | awk '// {print $2}'`
578                 if [ "X$lasttime" != X ]; then
579                         echo -n " ${lasttime}s ..."
580                 else
581                         echo -n "       "       # prettier output with timestamps.
582                 fi
583                 rm -f core $seqres.notrun
584
585                 start=`_wallclock`
586                 $timestamp && echo -n " ["`date "+%T"`"]"
587                 [ ! -x $seq ] && chmod u+x $seq # ensure we can run it
588                 $LOGGER_PROG "run xfstest $seqnum"
589                 if [ -w /dev/kmsg ]; then
590                         export date_time=`date +"%F %T"`
591                         echo "run fstests $seqnum at $date_time" > /dev/kmsg
592                         # _check_dmesg depends on this log in dmesg
593                         touch ${RESULT_DIR}/check_dmesg
594                 fi
595                 if [ "$DUMP_OUTPUT" = true ]; then
596                         ./$seq 2>&1 | tee $tmp.rawout
597                         # Because $? would get tee's return code
598                         sts=${PIPESTATUS[0]}
599                 else
600                         ./$seq >$tmp.rawout 2>&1
601                         sts=$?
602                 fi
603                 $timestamp && _timestamp
604                 stop=`_wallclock`
605
606                 _fix_malloc <$tmp.rawout >$tmp.out
607                 rm -f $tmp.rawout
608
609                 if [ -f core ]
610                 then
611                     echo -n " [dumped core]"
612                     mv core $RESULT_BASE/$seqnum.core
613                     err=true
614                 fi
615
616                 if [ -f $seqres.notrun ]
617                 then
618                     $timestamp || echo -n " [not run] "
619                     $timestamp && echo " [not run]" && echo -n "        $seqnum -- "
620                     cat $seqres.notrun
621                     notrun="$notrun $seqnum"
622                 else
623                     if [ $sts -ne 0 ]
624                     then
625                         echo -n " [failed, exit status $sts]"
626                         err=true
627                     fi
628                     if [ ! -f $seq.out ]
629                     then
630                         echo " - no qualified output"
631                         err=true
632                     else
633
634                         # coreutils 8.16+ changed quote formats in error messages from
635                         # `foo' to 'foo'. Filter old versions to match the new version.
636                         sed -i "s/\`/\'/g" $tmp.out
637                         if diff $seq.out $tmp.out >/dev/null 2>&1
638                         then
639                             if $err
640                             then
641                                 :
642                             else
643                                 echo "$seqnum `expr $stop - $start`" >>$tmp.time
644                                 echo -n " `expr $stop - $start`s"
645                             fi
646                             echo ""
647                         else
648                             echo " - output mismatch (see $seqres.out.bad)"
649                             mv $tmp.out $seqres.out.bad
650                             $diff $seq.out $seqres.out.bad | {
651                                 if test "$DIFF_LENGTH" -le 0; then
652                                         cat
653                                 else
654                                         head -n "$DIFF_LENGTH"
655                                         echo "..."
656                                         echo "(Run '$diff $seq.out $seqres.out.bad'" \
657                                                 " to see the entire diff)"
658                                 fi; } | \
659                                 sed -e 's/^\(.\)/    \1/'
660                             err=true
661                         fi
662                     fi
663                     try="$try $seqnum"
664                     n_try=`expr $n_try + 1`
665                     _check_filesystems
666                     _check_dmesg || err=true
667                 fi
668
669             fi
670
671             # come here for each test, except when $showme is true
672             #
673             if $err
674             then
675                 bad="$bad $seqnum"
676                 n_bad=`expr $n_bad + 1`
677                 quick=false
678             fi
679
680             seq="after_$seqnum"
681         done
682         _wrapup
683         echo
684
685         _test_unmount 2> /dev/null
686         _scratch_unmount 2> /dev/null
687 done
688
689 interrupt=false
690 status=`expr $sum_bad`
691 exit