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