######################################################################## # # # This software is part of the ast package # # Copyright (c) 1996-2011 AT&T Intellectual Property # # and is licensed under the # # Eclipse Public License, Version 1.0 # # by AT&T Intellectual Property # # # # A copy of the License is available at # # http://www.eclipse.org/org/documents/epl-v10.html # # (with md5 checksum b35adb5213ca9657e911e9befb180842) # # # # Information and Software Systems Research # # AT&T Research # # Florham Park NJ # # # # Glenn Fowler # # Phong Vo # # Doug McIlroy # # # ######################################################################## : sorttest [ sort test-options ... ] # Tests for the Unix sort utility # Test Posix features except for locale. # Test some nonstandard features if present. AWK=awk CC=${CC:-${TESTCC:-cc}} LC_ALL=C NAWK=nawk SUM=sum SORT=sort TMP=sort.tmp export TEST AWK CC LC_ALL SUM SORT TMP case $# in 0) set $SORT ;; esac # Some intermediate file tests require ~100 fds, # and some systems have low ~64 fds defaults. (ulimit -n 128) >/dev/null 2>&1 && ulimit -n 128 # Other tests may be needed for files too big to fit in memory; # see TEST=15 below SORT=$1 shift SORT_ARGS="$@" path=`pwd`:$PATH:/usr/5bin:/bin:/usr/bin ifs=${IFS-' '} IFS=":" set $path IFS=$ifs for dir do case $dir in "") continue ;; esac case $NAWK in */*) ;; *) if test -x $dir/$NAWK then NAWK=$dir/$NAWK else case $AWK in */*) ;; *) test -x $dir/$AWK && AWK=$dir/$AWK ;; esac fi ;; esac case $SORT in */*) ;; *) test -x $dir/$SORT && SORT=$dir/$SORT ;; esac case $SUM in */*) ;; *) if test -x $dir/$SUM then AB='abc pdq xyz' BA='xyz pdq abc' ab=`echo "$AB" | $SUM 2>/dev/null` ba=`echo "$BA" | $SUM 2>/dev/null` case $ab in $ba) SUM=$dir/$SUM ;; *) for old in -o '-o 2' -s -xatt do ab=`echo "$AB" | $dir/$SUM $old 2>/dev/null` case $ab in ?*) ba=`echo "$BA" | $dir/$SUM $old 2>/dev/null` case $ab in $ba) SUM="$dir/$SUM $old" break ;; esac ;; esac done ;; esac fi ;; esac done case $NAWK in */*) AWK=$NAWK ;; esac echo pathname and options of item under test echo " $SORT $SORT_ARGS" echo SORT="$SORT $SORT_ARGS" # All work done in local temp dir. trap "cd ..; rm -rf $TMP; exit" 0 1 2 13 15 rm -rf $TMP if mkdir $TMP then cd $TMP else echo "mkdir $TMP FAILED" exit 1 fi # Initialize switches for nonstandard features. # Use parenthesized settings for supported features. o=: # officially obsolescent features: +1 -2 O=: # really obsolescent features: displaced -o (o=) g=: # -g numeric sort including e-format numbers (g=) k=: # -Rr -k.p.l fixed records and fields M=: # -M sort by month names (M=) s=: # -s stable, do not compare raw bytes on equal keys (s=) S=: # -S unstable, compare raw bytes on equal keys (S=) x=: # -x method, alternate sort method (x=) X=: # -Xtest, -Xread ensures read over memory map y= # -y user-specified memory size (y=-y4096) # Detect what features are supported, assuming bad options cause # errors. Set switches accordingly. echo obsolescent and nonstandard features recognized, if any if $SORT +0 /dev/null; then o= echo ' +1 -2'; fi if $SORT /dev/null -o xx 2>/dev/null; then O= echo ' displaced -o'; fi if $SORT -g /dev/null; then g= echo ' -g g-format numbers'; fi if test Xcx.by.az = X`echo by.cx.az | $SORT -R3 -k.2.1 2>/dev/null`; then k= echo ' -Rr -k.p:l fixed length records'; fi if $SORT -M /dev/null; then M= echo ' -M months'; fi if $SORT -s /dev/null; then s= echo ' -s stable'; fi if $SORT -S /dev/null; then S= echo ' -S unstable'; fi if $SORT -x default /dev/null; then x= echo ' -x method'; fi if test Xok = X`$SORT -Xtest 2>/dev/null`; then X= echo ' -Xread ensures read instead of memory map'; fi if $SORT -y4096 /dev/null; then y=-y4096 echo ' -y space'; fi if $SORT -z10000 /dev/null; then echo ' -z recsize (not exercised)'; fi if $SORT -T. /dev/null; then echo ' -T tempdir (not exercised)'; fi # xsort testno options # Sort file "in" with specified options. # Compare with file "out" if that is supplied, # otherwise make plausibility checks on output cat >xsort <xx && \$SORT -c "\$@" xx 2>/dev/null then if test -f out then cmp -s xx out >/dev/null && exit 0 echo \$TEST\$X comparison FAILED else test "\`$SUM ysort error=0 warning=0 works=0 $SORT "\$@" 2>out in1 || error=1 test -s out && warning=1 test -s in1 && works=1 case \$error\$warning\$works in 000) echo " \$@" does not indicate trouble, but does not sort ;; 001) echo " \$@" does not indicate trouble ;; 010) echo " \$@" warns, does not sort, and yields exit status zero ;; 011) echo " \$@" warns and continues ;; 100) echo " \$@" yields nonzero exit status and does not sort ;; 101) echo " \$@" yields nonzero exit status, but sorts ;; 111) echo " \$@" warns and yields nonzero exit status, but sorts esac ! chmod +x ysort # linecount testno file count # declares the given "testno" to be in error if number of # lines in "file" differs from "count" cat >linecount <<'!' $AWK 'END{ if(NR!='$3') print "'$TEST$1' FAILED" }' $2 ! chmod +x linecount # instability by default for the standard case $S in '') SORT="$SORT -S" ;; esac # check behavior in questionable cases echo echo "behavior in questionable cases (other than message and exit)" echo hi >in rm -f nosuchfile ./ysort nosuchfile if test ! -f unwritablefile then echo x >unwritablefile chmod 0 unwritablefile fi ./ysort -o unwritablefile in <in1 <in2 <out 2>/dev/null then opt="-k1,1 -f -k2,2 :" if cmp -s out in >/dev/null then echo " $opt -f applies to fields 1 and 2" elif cmp -s out in1 >/dev/null then echo " $opt -f applies to field 2 only" elif cmp -s out in1 >/dev/null then echo " $opt -f ineffectual" elif cmp -s out /dev/null >/dev/null then echo " $opt exit status zero, but no output" else echo " $opt inexplicable" fi fi # generate large data here to smooth the test feedback echo echo "generating large data files for tests that follow (long)" $AWK 'BEGIN { for(i=0; i<100000; i++) print rand() }' 14.in $AWK 'BEGIN { x = "xxxxxxxxxx" x = x x x x x x x x for(i=0; i<8000; i++) print rand(), x }' >15.in in </dev/null && echo ${TEST}A FAILED ./xsort B || echo ${TEST}B $SUM is probably unsuitable - see comments $o $SORT -o in +0 in || echo ${TEST}C FAILED #--------------------------------------------------------------- TEST=02; echo $TEST "output from -c" cat >in <out 2>xx && echo ${TEST}A FAILED test -s out && echo ${TEST}B FAILED test -s xx || echo option -c is quiet "(legal, not classical)" $SORT -c /dev/null 2>xx || echo ${TEST}C FAILED test -s xx && echo ${TEST}D FAILED #--------------------------------------------------------------- TEST=03; echo $TEST "-n" cat >in <out <in <xx cmp -s xx out >/dev/null || echo ${TEST}B FAILED $SORT in | $SORT -cr 2>/dev/null && echo ${TEST}C FAILED #--------------------------------------------------------------- TEST=05; echo $TEST "fields, reverse fields, -c status return" cat >in <out </dev/null && ${TEST}G FAILED #--------------------------------------------------------------- TEST=06; echo $TEST "-t" cat >in <in <out <in <out <out <out <in <in <in1 <out <xx cmp -s xx out >/dev/null || echo ${TEST}A FAILED #--------------------------------------------------------------- TEST=11; echo $TEST "multiple files, -o overwites input, -m, -mu" cat >in </dev/null || echo ${TEST}C FAILED #--------------------------------------------------------------- TEST=12; echo $TEST "does -mu pick the first among equals?" cat >in <out <8000 bytes, keys >16000), -r" $AWK ' BEGIN { x="x" for(i=1; i<=12; i++) x = x x for(i=15; i<=25; i++) print x i }' >in =15; i--) print x i }' >out "out"; x = $0 } ' xx ./xsort C -n -u #--------------------------------------------------------------- TEST=15; echo $TEST "force intermediate files if possible" # option -y4096 ($y) should force a multi-stage internal merge # option -Xread ($X) should force read over mmap case "$y" in "") echo ${TEST} inadequate test of large files - revise parameters esac cp 15.in in rm -f out ./xsort A -r $y cat in | ./xsort - B -r $y $X ./xsort C -r $y -Xread $X cat in | $X ./xsort - D -r $y -Xread rm -f in1 $SORT -r -o in1 in $AWK '$0 "x" != x { print ; x = $0 "x" }' in1 >out ./xsort E -u -r $y cat in | ./xsort - F -u -r $y $X ./xsort G -u -r $y -Xread $X cat in | $X ./xsort - H -u -r $y -Xread $SORT -r -u -m -o in1 in1 cmp -s in1 out >/dev/null || echo ${TEST}I FAILED rm in in1 out #--------------------------------------------------------------- TEST=16; echo $TEST "-nr, -nm, file name -" $AWK 'BEGIN { for(i=-100; i<=100; i+=2) printf "%.10d\n", i }' >in xx $AWK '$0+0 != 101-NR { print "'${TEST}A' FAILED"; exit }' xx $AWK 'BEGIN { for(i=-99; i<=100; i+=2) print i }' xx $AWK '$0+0 != -101+NR { print "'${TEST}B' FAILED"; exit }' xx #--------------------------------------------------------------- TEST=17; echo $TEST "-d, fields without end, modifier override" cat >in <out <in <out <xx ./linecount C xx 2 #--------------------------------------------------------------- TEST=19; echo $TEST "-i, -d, -f" cat >xx.c < static void run(int i, int j){ for( ; i<=j; i++) printf("%.3o %c\n",i,i); } int main(){ run(0, 011); /* 012=='\n' */ run(013, 0377); return 0; } ! $CC -o xx.exe xx.c ./xx.exe >in cat >xx.c < static void run(int i, int j){ for( ; i<=j; i++) printf("%.3o %c\n",i,i); } int main(){ run(0, 011); run(013, ' '-1); run(0177, 0377); run(' ', 0176); return 0; } ! $CC -o xx.exe xx.c ./xx.exe >out ./xsort A -i -k 2 cat >xx.c < static void run(int i, int j){ for( ; i<=j; i++) printf("%.3o %c\n",i,i); } int main(){ run(0, 010); /* 011=='\t', 012=='\n' */ run(013, ' '-1); run(' '+1, '0'-1); run('9'+1, 'A'-1); run('Z'+1, 'a'-1); run('z'+1, 0377); run('\t', '\t'); run(' ', ' '); run('0', '9'); run('A', 'Z'); run('a', 'z'); return 0; } ! $CC -o xx.exe xx.c ./xx.exe >out ./xsort B -d -k 2 cat >xx.c < static void run(int i, int j){ for( ; i<=j; i++) printf("%.3o %c\n",i,i); } int main(){ int i; run(0, 011); run(013, 'A'-1); for(i='A'; i<='Z'; i++) printf("%.3o %c\n%.3o %c\n",i,i,i+040,i+040); run('Z'+1, 'a'-1); run('z'+1, 0377); return 0; } ! $CC -o xx.exe xx.c ./xx.exe >out rm -f xx.c xx.o xx.exe ./xsort C -f -k 2 #--------------------------------------------------------------- TEST=20; echo $TEST "-d, -f, -b applies only to fields" cat >in <out <xx.c <<'!' #include int main() { printf("\n%cb\n%ca\n",0,0); return 0; } ! $CC -o xx.exe xx.c ./xx.exe >in $SORT -u in >xx cmp -s in xx >/dev/null && echo ${TEST}A FAILED test "`wc -c in <out <xx $SORT -oxx /dev/null || echo ${TEST}A FAILED $SORT -c in <out <in <xx $SORT -c -t: -k2n xx 2>/dev/null || echo ${TEST}A FAILED $SORT -k2,2.1b -k2 in >xx $SORT -c -t: -k3n xx 2>/dev/null || echo ${TEST}B FAILED $SORT -k2.3 -k2 in >xx $SORT -c -t: -k4n xx 2>/dev/null || echo ${TEST}C FAILED $SORT -k2b,2.3 -k2 in >xx $SORT -c -t: -k5n xx 2>/dev/null || echo ${TEST}D FAILED $SORT -k2.3,2.1b -k2 in >xx $SORT -c -t: -k6n xx 2>/dev/null || echo ${TEST}E FAILED $SORT -k2,2.1b -k2r in >xx $SORT -c -t: -k7n xx 2>/dev/null || echo ${TEST}F FAILED $SORT -b -k2,2 -k2 in >xx $SORT -c -t: -k8n xx 2>/dev/null || echo ${TEST}G FAILED $SORT -b -k2,2b -k2 in >xx # perhaps same as G $SORT -c -t: -k3n xx 2>/dev/null || echo ${TEST}H FAILED\ "(standard is not clear on this)" #--------------------------------------------------------------- TEST=26; echo $TEST "empty fields, out of bounds fields " cat >in <in <-k <xx || echo ${TEST}A argument FAILED cmp -s xx -k || echo ${TEST}A comparison FAILED cat >in <- >-o >in1 $SORT -- -o in1 - out cmp -s in out >/dev/null || echo ${TEST}C FAILED test -s in1 && echo ${TEST}D FAILED #--------------------------------------------------------------- TEST=30; echo $TEST "missing newline" $AWK 'BEGIN{ printf "%s", "x"}' >in echo x >out ./xsort "" 2>/dev/null #--------------------------------------------------------------- TEST=31; echo $TEST "-M, multiple fields" cat >in <out <in <out <in <out <in <out <out <in <out <xx && $g $SORT -c -gu xx || echo ${TEST}B FAILED $g ./linecount C xx 3 #--------------------------------------------------------------- TEST=36; echo $TEST "-s" cat >in <out <in <in1 <out <xx $s cmp -s xx out >/dev/null || echo ${TEST}A FAILED cat >out <xx $s cmp -s xx out >/dev/null || echo ${TEST}B FAILED #--------------------------------------------------------------- TEST=38; echo $TEST "-s" $s $AWK ' BEGIN { for(i=1; i<50; i++) for(j=1; j<=i; j++) { print i, 2 >"in" print i, 1 >"in1" } }' out $s $AWK ' func stop() { print "'$TEST' FAILED"; exit } $1!=last1 { if(count!=last1 || $2!=2) stop(); count = 0} $1==last1 && $2!=last2 { if(count!=last1 || $2!=1) stop(); count = 0 } { count++; last1 = $1; last2 = $2 } ' out #--------------------------------------------------------------- TEST=39; echo $TEST "empty fields" cat >in <in <in < in echo hozopoonbkkjoieigishwewebdlao > out $k ./xsort A -s -r -R2 -k.2.1 ( echo; echo ) >> in cat >out < in echo ' zaz mam mzm aza ' > out $k ./xsort C -s -r -R4 -k.2.1 $k ./xsort D -s -r -R4 -k1.2,1.2 echo ' zaz mzm mam aza ' > out $k ./xsort E -s -r -R4 -k.2.2 $k ./xsort F -s -r -R4 -k1.2,1.3 $k ./xsort G -s -r -R4 -k1.2,1.2 -k1.3,1.3 echo ' mzm aza zaz mam ' > out $k ./xsort H -s -r -R4 -k1.3,1.3 -k1.2,1.2 done