1######################################################################## 2# # 3# This software is part of the ast package # 4# Copyright (c) 1994-2011 AT&T Intellectual Property # 5# and is licensed under the # 6# Eclipse Public License, Version 1.0 # 7# by AT&T Intellectual Property # 8# # 9# A copy of the License is available at # 10# http://www.eclipse.org/org/documents/epl-v10.html # 11# (with md5 checksum b35adb5213ca9657e911e9befb180842) # 12# # 13# Information and Software Systems Research # 14# AT&T Research # 15# Florham Park NJ # 16# # 17# Glenn Fowler <gsf@research.att.com> # 18# # 19######################################################################## 20: mktest - generate regress or shell regression test scripts 21 22command=mktest 23stdin=8 24stdout=9 25PREFIX=test 26STYLE=regress 27WIDTH=80 28 29eval "exec $stdout>&1" 30 31case $(getopts '[-][123:xyz]' opt --xyz 2>/dev/null; echo 0$opt) in 320123) ARGV0="-a $command" 33 USAGE=$' 34[-? 35@(#)$Id: mktest (AT&T Labs Research) 2010-08-11 $ 36] 37'$USAGE_LICENSE$' 38[+NAME?mktest - generate a regression test scripts] 39[+DESCRIPTION?\bmktest\b generates regression test scripts from test 40 template commands in the \aunit\a.\brt\b file. The generated test 41 script writes temporary output to '$PREFIX$'\aunit\a.tmp and compares 42 it to the expected output in '$PREFIX$'\aunit\a.out. Run the test 43 script with the \b--accept\b option to (re)generate the 44 '$PREFIX$'\aunit\a.out.] 45[s:style?The script style:]:[style:='$STYLE$'] 46 { 47 [+regress?\bregress\b(1) command input.] 48 [+shell?Standalone test shell script.] 49 } 50[w:width?Set the output format width to approximately 51 \awidth\a.]:[width:='$WIDTH$'] 52 53unit.rt [ unit [ arg ... ] ] 54 55[+INPUT FILES?The regression test command file \aunit\a\b.rt\b is a 56 \bksh\b(1) script that makes calls to the following functions:] 57 { 58 [+DATA \afile\a [ - | [ options ]] data]]?Create input data 59 \afile\a that is empty (-) or contains \adata\a subject to 60 \bprint\b(1) \aoptions\a or that is a copy of the DATA command 61 standard input. Set \afile\a to \b-\b to name the standard 62 input.] 63 [+DIAGNOSTICS?Diagnostic messages of unspecified format are 64 expected.] 65 [+DO \acommand\a [ \aarg\a ... ]]?Execute \acommand\a if the 66 current test is active.] 67 [+EXEC [ \aarg\a ... ]]?Run the command under test with 68 optional arguments. If the standard input is not specified then 69 the standard input of the previous EXEC is used. The standard 70 input of the first EXEC in a TEST group is an empty regular 71 file.] 72 [+EXPORT \aname\a=\avalue\a ...?Export list for subsequent 73 commands in the TEST group or for all TEST groups if before 74 the first TEST group.] 75 [+IGNORESPACE [ 0 | 1 ] 76 ?Ignore space differences when comparing expected output.] 77 [+KEEP \apattern\a ...?File match patterns of files to retain 78 between TEST groups.] 79 [+NOTE \acomment\a?\acomment\a is added to the current test 80 script.] 81 [+PROG \acommand\a [ \aarg\a ... ]]?Run \acommand\a with 82 optional arguments.] 83 [+TEST [ \anumber\a ]] [ \adescription\a ... ]]?Define a new 84 test group with optional \anumber\a and \adescripion\a.] 85 [+TWD [ \adir\a ... ]]?Set the temporary test dir to \adir\a. 86 The default is \aunit\a\b.tmp\b, where \aunit\a is the test 87 input file sans directory and suffix. If \adir\a matches \b/*\b 88 then it is the directory name; if \adir\a is non-null then the 89 prefix \b${TMPDIR:-/tmp}\b is added; otherwise if \adir\a is 90 omitted then 91 \b${TMPDIR:-/tmp}/tst-\b\aunit\a-$$-$RANDOM.\b\aunit\a is 92 used.] 93 [+UMASK [ \amask\a ]]?Run subsequent tests with \bumask\b(1) 94 \amask\a. If \amask\a is omitted then the original \bumask\b is 95 used.] 96 [+UNIT \acommand\a [ \aarg\a ... ]]?Define the command and 97 optional default arguments to be tested. \bUNIT\b explicitly 98 overrides the default command name derived from the test script 99 file name.] 100 [+WIDTH \awidth\a?Set the output format width to approximately 101 \awidth\a.] 102 } 103[+SEE ALSO?\bregress\b(1), \bksh\b(1)] 104' 105 ;; 106*) ARGV0="" 107 USAGE='s: unit.rt [ arg ... ]' 108 ;; 109esac 110 111typeset ARG SCRIPT UNIT TEMP=${TMPDIR:-/tmp}/$command.$$.tmp WORK 112typeset IO INPUT INPUT_N OUTPUT OUTPUT_N ERROR ERROR_N KEEP 113typeset -C STATE 114typeset -A DATA STATE.RESET REMOVE FORMAT 115integer KEEP_UNIT=0 SCRIPT_UNIT=0 TEST=0 CODE=0 EXIT=0 ACCEPT=0 DIAGNOSTICS=0 code 116 117while getopts $ARGV0 "$USAGE" OPT 118do case $OPT in 119 s) case $OPTARG in 120 regress|shell) 121 STYLE=$OPTARG 122 ;; 123 *) print -u2 -r -- $command: --style=$OPTARG: regress or shell expected 124 exit 1 125 ;; 126 esac 127 ;; 128 w) WIDTH=$OPTARG 129 ;; 130 *) OPTIND=0 131 getopts $ARGV0 "$USAGE" OPT '-?' 132 exit 2 133 ;; 134 esac 135done 136shift $OPTIND-1 137 138typeset SINGLE= quote='%${SINGLE}..${WIDTH}q' 139 140if [[ $1 == - ]] 141then shift 142fi 143if (( ! $# )) 144then 145 print -u2 -r -- $command: test command script path expected 146 exit 1 147fi 148SCRIPT=$1 149shift 150if [[ ! -r $SCRIPT ]] 151then print -u2 -r -- $command: $SCRIPT: cannot read 152 exit 1 153fi 154(ulimit -c 0) >/dev/null 2>&1 && ulimit -c 0 155if (( $# )) 156then set -A UNIT -- "$@" 157 KEEP_UNIT=1 158else ARG=${SCRIPT##*/} 159 set -A UNIT -- "${ARG%.*}" 160fi 161WORK=${UNIT[0]}.tmp 162rm -rf $WORK 163mkdir $WORK || exit 164export PATH=$PWD:$PATH 165 166function LINE 167{ 168 if [[ $STYLE == regress ]] 169 then print -u$stdout 170 fi 171} 172 173function NOTE 174{ 175 case $STYLE in 176 regress)LINE 177 print -u$stdout -r -- '#' "$@" 178 ;; 179 shell) print -u$stdout -r -f ": $QUOTE"$'\n' -- "$*" 180 ;; 181 esac 182} 183 184function UNIT 185{ 186 (( KEEP_UNIT )) || set -A UNIT -- "$@" 187 case $STYLE in 188 regress)LINE 189 print -u$stdout -r -f $'UNIT' 190 for ARG in "$@" 191 do print -u$stdout -r -f " $QUOTE" -- "$ARG" 192 done 193 print -u$stdout 194 ;; 195 shell) print -u$stdout -r -f $'set x' 196 for ARG in "$@" 197 do print -u$stdout -r -f " $QUOTE" -- "$ARG" 198 done 199 print -u$stdout 200 print -u$stdout shift 201 ;; 202 esac 203} 204 205function TEST 206{ 207 typeset i 208 typeset -A REM 209 if (( ${#STATE.RESET[@]} )) 210 then unset ${!STATE.RESET[@]} 211 case $STYLE in 212 shell) print -u$stdout -r -- unset ${!STATE.RESET[@]} ;; 213 esac 214 unset STATE.RESET 215 typeset -A STATE.RESET 216 fi 217 if (( ${#REMOVE[@]} )) 218 then rm -f -- "${!REMOVE[@]}" 219 case $STYLE in 220 shell) print -u$stdout -r -f $'rm -f' 221 for i in ${!REMOVE[@]} 222 do print -u$stdout -r -f " $QUOTE" "$i" 223 done 224 print -u$stdout 225 ;; 226 esac 227 for i in ${!REMOVE[@]} 228 do unset REMOVE[$i] 229 done 230 fi 231 rm -rf $WORK/* 232 if [[ $1 == +([0-9]) ]] 233 then TEST=${1##0} 234 shift 235 else ((TEST++)) 236 fi 237 LINE 238 case $STYLE in 239 regress)print -u$stdout -r -f "TEST %02d $QUOTE"$'\n' -- $TEST "$*" 240 ;; 241 shell) print -u$stdout -r -f ": TEST %02d $QUOTE"$'\n' -- $TEST "$*" 242 ;; 243 esac 244 : > $TEMP.INPUT > $TEMP.in 245 INPUT= 246 INPUT_N= 247 OUTPUT= 248 OUTPUT_N= 249 ERROR= 250 ERROR_N= 251 UMASK=$UMASK_ORIG 252 UMASK_DONE=$UMASK 253 CODE=0 254} 255 256function TWD 257{ 258 case $STYLE in 259 regress)LINE 260 print -u$stdout -r -f $'TWD' 261 for ARG in "$@" 262 do print -u$stdout -r -f " $QUOTE" -- "$ARG" 263 done 264 print -u$stdout 265 ;; 266 esac 267} 268 269function RUN 270{ 271 typeset i n p op unit sep output=1 error=1 exitcode=1 272 op=$1 273 shift 274 while : 275 do case $1 in 276 ++NOOUTPUT) output= ;; 277 ++NOERROR) error= ;; 278 ++NOEXIT) exitcode= ;; 279 ++*) print -u2 -r -- $command: $0: $1: unknown option; exit 1 ;; 280 *) break ;; 281 esac 282 shift 283 done 284 if [[ $op == PROG ]] 285 then unit=$1 286 shift 287 elif (( ! ${#UNIT[@]} )) 288 then print -u2 -r -- $command: $SCRIPT: UNIT statement or operand expected 289 exit 1 290 fi 291 LINE 292 case $STYLE in 293 regress)if [[ $op == PROG ]] 294 then print -u$stdout -r -f $'\t'"$op"$'\t'"$unit" 295 sep=$' ' 296 else print -u$stdout -r -f $'\t'"$op" 297 sep=$'\t' 298 fi 299 for ARG in "$@" 300 do LC_CTYPE=C print -u$stdout -r -f "$sep$QUOTE" -- "$ARG" 301 sep=$' ' 302 done 303 print -u$stdout 304 [[ ${DATA[-]} || /dev/fd/0 -ef /dev/fd/$stdin ]] || cat > $TEMP.in 305 IO=$(cat $TEMP.in; print :) 306 if [[ $IO == ?*$'\n:' ]] 307 then IO=${IO%??} 308 n= 309 else IO=${IO%?} 310 n=-n 311 fi 312 { 313 [[ $UMASK != $UMASK_ORIG ]] && umask $UMASK 314 cd $WORK 315 if [[ $op == PROG ]] 316 then "$unit" "$@" 317 code=$? 318 else "${UNIT[@]}" "$@" 319 code=$? 320 fi 321 cd .. 322 [[ $UMASK != $UMASK_ORIG ]] && umask $UMASK_ORIG 323 } < $TEMP.in > $TEMP.out 2> $TEMP.err 324 if [[ $IO != "$INPUT" || $n != "$INPUT_N" ]] 325 then INPUT=$IO 326 INPUT_N=$n 327 if [[ ${FORMAT[-]} ]] 328 then print -u$stdout -n -r -- $'\t\tINPUT' 329 print -u$stdout -r -f " $QUOTE" -- "${FORMAT[-]}" 330 print -u$stdout -r -f " $QUOTE" -- - 331 unset FORMAT[-] 332 else print -u$stdout -n -r -- $'\t\tINPUT' $n - 333 [[ $IO ]] && LC_CTYPE=C print -u$stdout -r -f " $QUOTE" -- "$IO" 334 fi 335 print -u$stdout 336 unset DATA[-] 337 fi 338 for i in ${!DATA[@]} 339 do if [[ ${FORMAT[$i]} ]] 340 then print -u$stdout -n -r -- $'\t\tINPUT' 341 print -u$stdout -r -f " $QUOTE" -- "${FORMAT[$i]}" 342 print -u$stdout -r -f " $QUOTE" -- "$i" 343 unset FORMAT[$i] 344 else case $i in 345 -) p=$TEMP.in ;; 346 *) p=$WORK/$i ;; 347 esac 348 IO=$(cat $p; print :) 349 if [[ $IO == ?*$'\n:' ]] 350 then IO=${IO%??} 351 n= 352 else IO=${IO%?} 353 n=-n 354 fi 355 print -u$stdout -n -r -- $'\t\tINPUT' $n 356 print -u$stdout -r -f " $QUOTE" -- "$i" 357 [[ $IO ]] && LC_CTYPE=C print -u$stdout -r -f " $QUOTE" -- "$IO" 358 fi 359 print -u$stdout 360 unset DATA[$i] 361 done 362 IO=$(cat $TEMP.out; print :) 363 if [[ $IO == ?*$'\n:' ]] 364 then IO=${IO%??} 365 n= 366 else IO=${IO%?} 367 n=-n 368 fi 369 if [[ $IO != "$OUTPUT" || $n != "$OUTPUT_N" ]] 370 then OUTPUT=$IO 371 OUTPUT_N=$n 372 if [[ $output ]] 373 then if [[ ! -s $TEMP.out ]] 374 then print -u$stdout -n -r -- $'\t\tOUTPUT' - 375 elif cmp -s $TEMP.in $TEMP.out 376 then OUTPUT=not-$OUTPUT 377 print -u$stdout -n -r -- $'\t\tSAME OUTPUT INPUT' 378 else print -u$stdout -n -r -- $'\t\tOUTPUT' $n - 379 [[ $IO ]] && LC_CTYPE=C print -u$stdout -r -f " $QUOTE" -- "$IO" 380 fi 381 print -u$stdout 382 fi 383 fi 384 IO=$(cat $TEMP.err; print :) 385 IO=${IO//$command\[*([0-9])\]:\ .\[*([0-9])\]:\ @(EXEC|PROG)\[*([0-9])\]:\ /} 386 if [[ $IO == ?*$'\n:' ]] 387 then IO=${IO%??} 388 n= 389 else IO=${IO%?} 390 n=-n 391 fi 392 if [[ $IO != "$ERROR" || $n != "$ERROR_N" ]] 393 then ERROR=$IO 394 ERROR_N=$n 395 if [[ $error ]] 396 then print -u$stdout -n -r -- $'\t\tERROR' $n - 397 [[ $IO ]] && LC_CTYPE=C print -u$stdout -r -f " $QUOTE" -- "$IO" 398 print -u$stdout 399 fi 400 fi 401 case $output:$error in 402 :) OUTPUT= 403 OUTPUT_N= 404 ERROR= 405 ERROR_N= 406 print -u$stdout -r -- $'\t\tIGNORE OUTPUT ERROR' 407 ;; 408 :1) OUTPUT= 409 OUTPUT_N= 410 print -u$stdout -r -- $'\t\tIGNORE OUTPUT' 411 ;; 412 1:) ERROR= 413 ERROR_N= 414 print -u$stdout -r -- $'\t\tIGNORE ERROR' 415 ;; 416 esac 417 if [[ $UMASK_DONE != $UMASK ]] 418 then UMASK_DONE=$UMASK 419 print -u$stdout -r -f $'\t\tUMASK %s\n' $UMASK 420 fi 421 if (( code != CODE )) 422 then (( CODE=code )) 423 if [[ $exitcode ]] 424 then print -u$stdout -r -f $'\t\tEXIT %d\n' $CODE 425 fi 426 fi 427 ;; 428 shell) [[ $UMASK != $UMASK_ORIG ]] && print -u$stdout -r -f "{ umask $UMASK; " 429 if [[ $op == PROG ]] 430 then print -u$stdout -r -f $'"'"$unit"$'"' 431 else print -u$stdout -r -f $'"$@"' 432 fi 433 for ARG in "$@" 434 do print -u$stdout -r -f " $QUOTE" -- "$ARG" 435 done 436 [[ $UMASK != $UMASK_ORIG ]] && print -u$stdout -r -f "umask $UMASK_ORIG; } " 437 if [[ ! $output ]] 438 then print -u$stdout -r -f " >/dev/null" 439 fi 440 if [[ ! $error ]] 441 then if [[ ! $output ]] 442 then print -u$stdout -r -f " 2>&1" 443 else print -u$stdout -r -f " 2>/dev/null" 444 fi 445 fi 446 IO=$(cat) 447 if [[ $IO ]] 448 then print -u$stdout -r -- "<<'!TEST-INPUT!'" 449 print -u$stdout -r -- "$IO" 450 print -u$stdout -r -- !TEST-INPUT! 451 else print -u$stdout 452 fi 453 if [[ $exitcode ]] 454 then print -u$stdout -r -- $'CODE=$?\ncase $CODE in\n0) ;;\n*) echo exit status $CODE ;;\nesac' 455 fi 456 ;; 457 esac 458} 459 460function DO 461{ 462 LINE 463 print -r $'\t'DO "$@" 464} 465 466function EXEC 467{ 468 RUN EXEC "$@" 469} 470 471function DATA 472{ 473 typeset f p o 474 f=$1 475 shift 476 case $f in 477 -) p=$TEMP.in ;; 478 *) p=$WORK/$f ;; 479 esac 480 case $1 in 481 '') cat ;; 482 -) ;; 483 *) print -r "$@" ;; 484 esac > $p 485 DATA[$f]=1 486 if (( $# == 1 )) && [[ $1 == -?* ]] 487 then FORMAT[$f]=$1 488 else FORMAT[$f]= 489 fi 490 if [[ $f != $KEEP ]] 491 then REMOVE[$f]=1 492 fi 493 if [[ $STYLE == shell ]] 494 then { 495 print -r -f "cat > $QUOTE <<'!TEST-INPUT!'"$'\n' -- "$f" 496 cat "$p" 497 print -r -- !TEST-INPUT! 498 } >&$stdout 499 fi 500} 501 502function KEEP 503{ 504 typeset p 505 for p 506 do if [[ $KEEP ]] 507 then KEEP=$KEEP'|' 508 fi 509 KEEP=$KEEP$p 510 done 511} 512 513function DIAGNOSTICS 514{ 515 LINE 516 case $STYLE in 517 regress) print -u$stdout -r $'DIAGNOSTICS' ;; 518 shell) DIAGNOSTICS=1 ;; 519 esac 520} 521 522function EXPORT 523{ 524 typeset x n v 525 LINE 526 case $STYLE in 527 regress) print -u$stdout -r -f $'EXPORT' ;; 528 shell) print -u$stdout -r -f $'export' ;; 529 esac 530 for x 531 do n=${x%%=*} 532 v=${x#*=} 533 export "$x" 534 print -u$stdout -r -f " %s=$QUOTE" "$n" "$v" 535 (( TEST )) && STATE.RESET["$n"]=1 536 done 537 print -u$stdout 538} 539 540function PROG 541{ 542 RUN PROG "$@" 543} 544 545function WIDTH 546{ 547 WIDTH=${1:-80} 548 eval QUOTE='"'$quote'"' 549} 550 551function IGNORESPACE 552{ 553 IGNORESPACE=-b 554 LINE 555 print -u$stdout -r IGNORESPACE 556} 557 558function UMASK # [ mask ] 559{ 560 [[ $UMASK_ORIG ]] || UMASK_ORIG=$(umask) 561 UMASK=$1 562 [[ $UMASK ]] || UMASK=$UMASK_ORIG 563} 564 565trap 'CODE=$?; rm -rf $TEMP.* $WORK; exit $CODE' 0 1 2 3 15 566 567typeset IGNORESPACE UMASK UMASK_ORIG UMASK_DONE 568UMASK_ORIG=$(umask) 569IFS=$IFS$'\n' 570 571print -u$stdout -r "# : : generated from $SCRIPT by $command : : #" 572case $STYLE in 573shell) cat <<! 574ACCEPT=0 575while : 576do case \$1 in 577 -a|--accept) 578 ACCEPT=1 579 ;; 580 --help|--man) 581 cat 1>&2 <<!! 582Usage: \\\$SHELL $PREFIX${UNIT[0]}.sh [ --accept ] [ unit ... ] 583 584${UNIT[0]} regression test script. Run this script to generate new 585results in $PREFIX${UNIT[0]}.tmp and compare with expected results in 586$PREFIX${UNIT[0]}.out. The --accept option generates $PREFIX${UNIT[0]}.tmp 587and moves it to $PREFIX${UNIT[0]}.out. 588!! 589 exit 2 590 ;; 591 -*) echo \$0: \$1: invalid option >&2 592 exit 1 593 ;; 594 *) break 595 ;; 596 esac 597 shift 598done 599export COLUMNS=80 600{ 601! 602 ;; 603esac 604 605export COLUMNS=80 606 607case $STYLE in 608shell) SINGLE='#' 609 eval QUOTE='"'$quote'"' 610 . $SCRIPT < /dev/null | sed -e $'s,\\\\n,\n,g' -e $'s,\\\\t,\t,g' -e $'s,\\$\',\',g' 611 ;; 612*) eval QUOTE='"'$quote'"' 613 : > $TEMP.INPUT > $TEMP.in 614 eval "exec $stdin<$TEMP.INPUT" 615 . $SCRIPT <&$stdin 616 ;; 617esac 618 619case $STYLE in 620shell) cat <<! 621} > $PREFIX${UNIT[0]}.tmp 2>&1 < /dev/null 622case \$ACCEPT in 6230) if grep ' 624$' $PREFIX${UNIT[0]}.tmp >/dev/null 625 then mv $PREFIX${UNIT[0]}.tmp $PREFIX${UNIT[0]}.junk 626 sed 's/ 627$//' < $PREFIX${UNIT[0]}.junk > $PREFIX${UNIT[0]}.tmp 628 rm -f $PREFIX${UNIT[0]}.junk 629 fi 630 if cmp -s $PREFIX${UNIT[0]}.tmp $PREFIX${UNIT[0]}.out 631 then echo ${UNIT[0]} tests PASSED 632 rm -f $PREFIX${UNIT[0]}.tmp 633 else echo ${UNIT[0]} tests FAILED 634 diff $IGNORESPACE $PREFIX${UNIT[0]}.tmp $PREFIX${UNIT[0]}.out 635 fi 636 ;; 637 638*) mv $PREFIX${UNIT[0]}.tmp $PREFIX${UNIT[0]}.out 639 ;; 640esac 641! 642 ;; 643esac 644