1#!/bin/sh 2# 3# units - Units test harness for ctags 4# 5# Copyright (C) 2014 Masatake YAMATO 6# 7# This program is free software; you can redistribute it and/or modify 8# it under the terms of the GNU General Public License as published by 9# the Free Software Foundation; either version 2 of the License, or 10# (at your option) any later version. 11# 12# This program is distributed in the hope that it will be useful, 13# but WITHOUT ANY WARRANTY; without even the implied warranty of 14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15# GNU General Public License for more details. 16# 17# You should have received a copy of the GNU General Public License 18# along with this program. If not, see <http://www.gnu.org/licenses/>. 19# 20if test -n "${ZSH_VERSION+set}"; then 21 set -o SH_WORD_SPLIT 22 set +o NOMATCH 23fi 24 25# 26# Global Parameters 27# 28SHELL=/bin/sh 29CTAGS=./ctags 30READTAGS=./readtags 31WITH_TIMEOUT= 32WITH_VALGRIND= 33COLORIZED_OUTPUT=yes 34[ -f /dev/stdout ] && COLORIZED_OUTPUT=no 35CATEGORIES= 36UNITS= 37LANGUAGES= 38PRETENSE_OPTS= 39RUN_SHRINK= 40QUIET= 41SHOW_DIFF_OUTPUT= 42VALIDATORS= 43 44# 45# Internal variables and constants 46# 47_CMDLINE= 48_CMDLINE_FOR_SHRINKING= 49_PREPERE_ENV= 50_FEATURE_LIST= 51readonly _DEFAULT_CATEGORY=ROOT 52readonly _TIMEOUT_EXIT=124 53readonly _VG_TIMEOUT_FACTOR=10 54readonly _VALGRIND_EXIT=58 55readonly _BASH_INTERRUPT_EXIT=59 56readonly _LINE_SPLITTER=$(if type dos2unix > /dev/null 2>&1; then echo "dos2unix"; else echo "cat"; fi) 57readonly _STDERR_OUTPUT_NAME="STDERR.tmp" 58readonly _DIFF_OUTPUT_NAME="DIFF.tmp" 59readonly _NOISE_REPORT_MAX_COLUMN=50 60readonly _VALIDATION_EXIT_INVALID=2 61readonly _NOOP_VALIDATOR="NONE" 62readonly _KNOWN_INVALIDATION_VALIDATOR="KNOWN-INVALIDATION" 63if stat --help >/dev/null 2>&1; then 64 readonly _FSIZE="stat -c %s" # GNU coreutils 65else 66 readonly _FSIZE="stat -f %z" # BSD based OSes including macOS 67fi 68 69_RUNNABLE_VALIDATORS= 70_UNAVAILABLE_VALIDATORS= 71 72# 73# Result files and results 74# 75readonly R_PASSED="_PASSED.result" 76readonly R_FIXED="_FIXED.result" 77readonly R_FAILED_BY_STATUS="_FAILED_BY_STATUS.result" 78readonly R_FAILED_BY_DIFF="_FAILED_BY_DIFF.result" 79readonly R_SKIPPED_BY_FEATURES="_SKIPPED_BY_FEATURES.result" 80readonly R_SKIPPED_BY_LANGUAGES="_SKIPPED_BY_LANGUAGES.result" 81readonly R_SKIPPED_BY_ILOOP="_SKIPPED_BY_ILOOP.result" 82readonly R_KNOWN_BUGS="_KNOWN_BUGS.result" 83readonly R_FAILED_BY_TIMEED_OUT="_FAILED_BY_TIMEED_OUT.result" 84readonly R_BROKEN_ARGS_CTAGS="_BROKEN_ARGS_CTAGS.result" 85readonly R_VALGRIND="_VALGRIND.result" 86L_PASSED= 87L_FIXED= 88L_FAILED_BY_STATUS= 89L_FAILED_BY_DIFF= 90L_SKIPPED_BY_FEATURES= 91L_SKIPPED_BY_LANGUAGES= 92L_SKIPPED_BY_ILOOP= 93L_KNOWN_BUGS= 94L_FAILED_BY_TIMEED_OUT= 95L_BROKEN_ARGS_CTAGS= 96L_VALGRIND= 97 98V_VALID=0 99V_INVALID=0 100V_SKIP_VALIDATOR_UNAVAILABLE=0 101V_SKIP_KNOWN_INVALIDATION=0 102 103# 104# TODO 105# 106# * write new class 'r' (category directory) to units.rst. 107# * write new class 'v' (skip the checking by valgrind) to units.rst. 108# 109action_help () 110{ 111 cat <<EOF 112Usage: 113 $(help_help) 114 115 $(help_run) 116 117 $(help_clean) 118 119 $(help_fuzz) 120 121 $(help_shrink) 122 123 $(help_noise) 124 125 $(help_tmain) 126 127 $(help_chop) 128 129 $(help_validate_input) 130 131 $(help_clean_tmain) 132EOF 133} 134 135help_help() 136{ 137 echo "$0 help|--help" 138} 139 140ERROR () 141{ 142 local status_="$1" 143 local msg="$2" 144 shift 2 145 echo "$msg" 1>&2 146 exit $status_ 147} 148 149line() 150{ 151 local c=${1:--} 152 local no_newline="${2}" 153 local i=0 154 while [ $i -lt 60 ]; do 155 printf "%c" "$c" 156 i=$(( i + 1 )) 157 done 158 159 if ! [ "${no_newline}" = "--no-newline" ]; then 160 echo 161 fi 162} 163 164count_list () 165{ 166 echo $# 167} 168 169member_p () 170{ 171 local elt="$1" 172 shift 173 local x 174 175 for x in "$@"; do 176 if [ "$x" = "$elt" ]; then 177 return 0 178 fi 179 done 180 181 return 1 182} 183 184clean_tcase () 185{ 186 local d="$1" 187 local bundles="$2" 188 local b 189 190 if [ -d "$d" ]; then 191 if [ -f "${bundles}" ]; then 192 while read b; do 193 rm -rf "${b}" 194 done < "${bundles}" 195 rm ${bundles} 196 fi 197 rm -f "$d"/*.tmp "$d"/*.TMP 198 fi 199} 200 201check_availability() 202{ 203 local cmd="$1" 204 shift 205 type "${cmd}" > /dev/null 2>&1 || ERROR 1 "${cmd} command is not available" 206} 207 208check_units () 209{ 210 local name="$1" 211 local category="$2" 212 shift 2 213 local u 214 215 for u in "$@"; do 216 if echo "${u}" | grep -q /; then 217 if [ "${u%/*}" = "${category}" ] && [ "${u#*/}" = "${name}" ]; then 218 return 0 219 fi 220 elif [ "${u}" = "${name}" ]; then 221 return 0 222 fi 223 done 224 return 1 225} 226 227init_features() 228{ 229 _FEATURE_LIST=$( ${CTAGS} --quiet --options=NONE \ 230 --list-features --with-list-header=no \ 231 2> /dev/null \ 232 | "${_LINE_SPLITTER}" \ 233 | cut -f 1 -d ' ') 234} 235 236check_features() 237{ 238 local flag="$1" 239 local ffile 240 local feature 241 242 if [ "${flag}" = "-f" ]; then 243 ffile="$2" 244 elif [ "${flag}" = "-e" ]; then 245 feature="$2" 246 fi 247 shift 2 248 249 local f 250 local found 251 local found_unexpectedly 252 local expected; 253 254 255 for expected in $([ -f "$ffile" ] && cat "$ffile") ${feature}; do 256 found=no 257 found_unexpectedly=no 258 for f in ${_FEATURE_LIST} ; do 259 [ "$expected" = "$f" ] && found=yes 260 [ "$expected" = '!'"$f" ] && found_unexpectedly=yes 261 done 262 if [ "${found_unexpectedly}" = yes ]; then 263 echo "$expected" 264 return 1 265 elif ! [ "$found" = yes ]; then 266 echo "$expected" 267 return 1 268 fi 269 done 270 271 return 0 272} 273 274check_languages() 275{ 276 local lfile="$1" 277 shift 278 279 local l 280 local found 281 local expected; 282 283 284 # 285 # TODO: consider the value of LANGUAGES 286 # 287 while read expected; do 288 found=no 289 for l in $( ${_CMDLINE} --list-languages 2>/dev/null | "${_LINE_SPLITTER}" |sed -e 's/ //' ); do 290 [ "$expected" = "$l" ] && found=yes 291 done 292 if ! [ "$found" = yes ]; then 293 echo "$expected" 294 return 1 295 fi 296 done < "$lfile" 297 298 return 0 299} 300 301decorate () 302{ 303 local decorator="$1" 304 local msg="$2" 305 306 case "$decorator" in 307 red) decorator=31 ;; 308 green) decorator=32 ;; 309 yellow) decorator=33 ;; 310 *) ERROR 1 "INTERNAL ERROR: wrong run_result function: $f" 311 esac 312 313 if [ "${COLORIZED_OUTPUT}" = 'yes' ]; then 314 printf '%b\n' "\033[${decorator}m${msg}\033[39m" 315 else 316 printf '%b\n' "${msg}" 317 fi 318} 319 320run_result () 321{ 322 local result_type="$1" 323 local msg="$2" 324 local output="$3" 325 shift 3 326 local f="run_result_${result_type}" 327 local tmp 328 329 type "$f" > /dev/null 2>&1 || ERROR 1 \ 330 "${msg}INTERNAL ERROR: wrong run_result function: $f" 331 332 "$f" "${msg}" "$@" 333 334 tmp="${COLORIZED_OUTPUT}" 335 COLORIZED_OUTPUT=no 336 "$f" "${msg}" "$@" > "${output}" 337 COLORIZED_OUTPUT="${tmp}" 338} 339 340run_result_skip () 341{ 342 local msg="$1" 343 shift 1 344 345 if [ -n "$1" ]; then 346 printf '%b%b\n' "${msg}" $(decorate yellow "skipped")" ($1)" 347 else 348 printf '%b%b\n' "${msg}" $(decorate yellow "skipped") 349 fi 350} 351 352run_result_error () 353{ 354 local msg="$1" 355 shift 1 356 357 if [ ! -n "$1" ]; then 358 printf '%b%b\n' "${msg}" $(decorate red "failed") 359 else 360 printf '%b%b\n' "${msg}" $(decorate red "failed")" ($1)" 361 fi 362} 363 364run_result_ok () 365{ 366 local msg="$1" 367 shift 1 368 369 if [ ! -n "$1" ]; then 370 printf '%b%b\n' "${msg}" $(decorate green "passed") 371 else 372 printf '%b%b\n' "${msg}" $(decorate green "passed")" ($1)" 373 fi 374} 375 376run_result_known_error () 377{ 378 local msg="$1" 379 shift 1 380 381 printf '%b%b\n' "${msg}" $(decorate yellow "failed")" (KNOWN bug)" 382} 383 384run_shrink () 385{ 386 local cmdline_template="$1" 387 local input="$2" 388 local output="$3" 389 local lang="$4" 390 shift 4 391 392 echo "Shrinking ${input} as ${lang}" 393 shrink_main "${cmdline_template}" "${input}" "${output}" 1 yes 394} 395 396# filters out the directory prefix in a ctags input 397ctags_basename_filter_regex='s%\(^[^ ]\{1,\} \)\(/\{0,1\}\([^/ ]\{1,\}/\)*\)%\1%' 398ctags_basename_filter() 399{ 400 sed "${ctags_basename_filter_regex}" 401} 402 403# About "input" in the expression, see units.py. 404etags_basename_filter_regex='s%.*\/\(input[-._][[:print:]]\{1,\}\),\([0-9]\{1,\}$\)%\1,\2%' 405etags_basename_filter() 406{ 407 sed "${etags_basename_filter_regex}" 408} 409 410xref_basename_filter_regex='s%\(.*[[:digit:]]\{1,\} \)\([^ ]\{1,\}[^ ]\{1,\}\)/\([^ ].\{1,\}.\{1,\}$\)%\1\3%' 411xref_basename_filter() 412{ 413 sed "${xref_basename_filter_regex}" 414} 415 416json_basename_filter_regex='s%\("path": \)"[^"]\{1,\}/\([^/"]\{1,\}\)"%\1"\2"%' 417json_basename_filter() 418{ 419 sed "${json_basename_filter_regex}" 420} 421 422run_record_cmdline () 423{ 424 local ffilter="$1" 425 local ocmdline="$2" 426 427 printf "%s\n%s \\\\\n| %s \\\\\n| %s\n" \ 428 "${_PREPERE_ENV}" \ 429 "${_CMDLINE}" \ 430 "sed '${tags_basename_filter_regex}'" \ 431 "${ffilter}" \ 432 > "${ocmdline}" 433} 434 435# 436# All files and directories other than input.*, expected.tags, 437# args.ctags, README*, features, languages, and filters under srcdir 438# are copied to builddir. These copied files are called bundles. 439# 440prepare_bundles () 441{ 442 local from=$1 443 local to=$2 444 local obundles=$3 445 local src 446 local dist 447 448 for src in ${from}/*; do 449 if [ "${from}"'/*' = "${src}" ]; then 450 break 451 fi 452 case "${src##*/}" in 453 input.*) 454 continue 455 ;; 456 expected.tags*) 457 continue 458 ;; 459 README*) 460 continue 461 ;; 462 features|languages|filters) 463 continue 464 ;; 465 args.ctags) 466 continue 467 ;; 468 *) 469 dist="${to}/${src##*/}" 470 if ! cp -a "${src}" "${to}"; then 471 ERROR 1 "failure in copying bundle file \"${src}\" to \"${to}\"" 472 else 473 echo ${dist} >> ${obundles} 474 fi 475 ;; 476 esac 477 done 478} 479 480direq () 481{ 482 [ "$(cd ${1} && pwd)" = "$(cd ${2} && pwd)" ] 483 return $? 484} 485 486anon_normalize () 487{ 488 local ctags=$1 489 local input_actual 490 491 if [ -n "$2" ]; then 492 input_actual=$2 493 shift 2 494 495 # TODO: "Units" should not be hardcoded. 496 local input_expected="./Units${input_actual#*/Units}" 497 498 local actual=$(${CTAGS} --quiet --options=NONE --_anonhash="${input_actual}") 499 local expected=$(${CTAGS} --quiet --options=NONE --_anonhash="${input_expected}") 500 501 sed -e s/${actual}/${expected}/g | anon_normalize "${ctags}" "$@" 502 else 503 cat 504 fi 505} 506 507run_tcase () 508{ 509 local input="$1" 510 local t="$2" 511 local name="$3" 512 local class="$4" 513 local category="$5" 514 local build_t="$6" 515 shift 6 516 # The rest of arguments ($@) are extra inputs 517 518 # I violate the naming convention of build_* to reduce typing 519 local o=${build_t} 520 521 local fargs="$t/args.ctags" 522 local ffeatures="$t/features" 523 local flanguages="$t/languages" 524 local ffilter="$t/filter" 525 526 # 527 # tags-e if for etags output(-e). TAGS is good 528 # suffix but foo.tags and foo.TAGS may be the same on Windows. 529 # tags-x is for cross reference output(-x). 530 # tags-json is for json output. 531 # 532 # fexpected must be set even if none of 533 # expected.{tags,tags-e,tags-x,tags-json} exits. 534 # 535 local fexpected="$t/expected.tags" 536 local output_type=ctags 537 local output_label= 538 local output_tflag= 539 local output_feature= 540 local output_lang_extras= 541 542 if [ -f "$t/expected.tags" ]; then 543 : 544 elif [ -f "$t/expected.tags-e" ]; then 545 fexpected=$t/expected.tags-e 546 output_type=etags 547 output_label=/${output_type} 548 output_tflag="-e --tag-relative=no" 549 elif [ -f "$t/expected.tags-x" ]; then 550 fexpected=$t/expected.tags-x 551 output_type=xref 552 output_label=/${output_type} 553 output_tflag=-x 554 elif [ -f "$t/expected.tags-json" ]; then 555 fexpected=$t/expected.tags-json 556 output_type=json 557 output_label=/${output_type} 558 output_tflag="--output-format=json" 559 output_feature=json 560 fi 561 562 if [ $# -gt 0 ]; then 563 output_lang_extras=" (multi inputs)" 564 fi 565 566 [ -x "$ffilter" ] || ffilter=cat 567 568 # 569 # All generated file must have suffix ".tmp". 570 # 571 local ostderr="$o/${_STDERR_OUTPUT_NAME}" 572 local orawout="$o/RAWOUT.tmp" 573 local ofiltered="$o/FILTERED.tmp" 574 local odiff="$o/${_DIFF_OUTPUT_NAME}" 575 local ocmdline="$o/CMDLINE.tmp" 576 local ovalgrind="$o/VALGRIND.tmp" 577 local oresult="$o/RESULT.tmp" 578 local oshrink_template="$o/SHRINK-%s.tmp" 579 local obundles="$o/BUNDLES" 580 local oshrink 581 582 local guessed_lang 583 local guessed_lang_no_slash 584 local cmdline_template 585 local timeout_value 586 local tmp 587 local msg 588 589 local broke_args_ctags 590 591 # 592 # Filtered by UNIT 593 # 594 if [ -n "${UNITS}" ]; then 595 check_units "${name}" "${category}" ${UNITS} || return 1 596 fi 597 598 # 599 # Build _CMDLINE 600 # 601 _CMDLINE="${CTAGS} --verbose --options=NONE --fields=-T $PRETENSE_OPTS --optlib-dir=+$t/optlib -o -" 602 [ -f "${fargs}" ] && _CMDLINE="${_CMDLINE} --options=${fargs}" 603 604 if [ -f "${fargs}" ] && ! ${_CMDLINE} --_force-quit=0 > /dev/null 2>&1; then 605 broke_args_ctags=1 606 fi 607 608 # 609 # Filtered by LANGUAGES 610 # 611 guessed_lang=$( ${_CMDLINE} --print-language "$input" 2>/dev/null | sed -n 's/^.*: //p') 612 if [ -n "${LANGUAGES}" ]; then 613 member_p "${guessed_lang}" ${LANGUAGES} || return 1 614 fi 615 guessed_lang_no_slash=$(echo "${guessed_lang}" | tr '/' '-') 616 oshrink=$(printf "${oshrink_template}" "${guessed_lang_no_slash}") 617 618 clean_tcase "${o}" "${obundles}" 619 mkdir -p "${o}" 620 if ! direq "${o}" "${t}"; then 621 prepare_bundles ${t} ${o} "${obundles}" 622 fi 623 624 625 msg=$(printf '%-60s' "Testing ${name} as ${guessed_lang}${output_lang_extras}${output_label}") 626 627 if tmp=$( ( [ -n "${output_feature}" ] && ! check_features -e "${output_feature}" ) || 628 ( [ -f "${ffeatures}" ] && ! check_features -f "${ffeatures}" ) ); then 629 echo "${category}/${name}" >> ${R_SKIPPED_BY_FEATURES} 630 case "${tmp}" in 631 !*) run_result skip "${msg}" "${oresult}" "unwanted feature \"${tmp#?}\" is available";; 632 *) run_result skip "${msg}" "${oresult}" "required feature \"${tmp}\" is not available";; 633 esac 634 return 1 635 elif [ -f "${flanguages}" ] && ! tmp=$(check_languages "${flanguages}"); then 636 echo "${category}/${name}" >> ${R_SKIPPED_BY_LANGUAGES} 637 run_result skip "${msg}" "${oresult}" "required language parser \"$tmp\" is not available" 638 return 1 639 elif [ "$WITH_TIMEOUT" = 0 ] && [ "${class}" = 'i' ]; then 640 echo "${category}/${name}" >> ${R_SKIPPED_BY_ILOOP} 641 run_result skip "${msg}" "${oresult}" "may cause an infinite loop" 642 return 1 643 elif [ "$broke_args_ctags" = 1 ]; then 644 run_result error "${msg}" '/dev/null' "broken args.ctags?" 645 echo "${category}/${name}/" >> ${R_BROKEN_ARGS_CTAGS} 646 return 1 647 fi 648 649 cmdline_template="${_CMDLINE} --language-force=${guessed_lang} %s > /dev/null 2>&1" 650 _CMDLINE="${_CMDLINE} ${output_tflag} ${input} $@" 651 652 timeout_value=$WITH_TIMEOUT 653 if [ "$WITH_VALGRIND" = yes ]; then 654 _CMDLINE="valgrind --leak-check=full --error-exitcode=${_VALGRIND_EXIT} --log-file=${ovalgrind} ${_CMDLINE}" 655 timeout_value=$(( timeout_value * ${_VG_TIMEOUT_FACTOR} )) 656 fi 657 658 if ! [ "$timeout_value" = 0 ]; then 659 _CMDLINE="timeout $timeout_value ${_CMDLINE}" 660 fi 661 662 { 663 ( 664 # 665 # When a launched process is exited abnormally, the parent shell reports it 666 # to stderr: See j_strsignal function call in wait_for in bash-4.2/nojobs.c. 667 # This becomes noise; close the stderr of subshell. 668 # 669 exec 2>&-; 670 # 671 # The original bug report(#1100 by @techee): 672 # -------------------------------------------------------------------------- 673 # When running 674 # 675 # make units VG=1 676 # 677 # one cannot stop its execution by pressing Ctrl+C and 678 # there doesn't seem to be any way (except for looking at 679 # processes which run and killing them) to stop its 680 # execution. 681 # 682 trap "exit ${_BASH_INTERRUPT_EXIT}" INT; 683 ${_CMDLINE} 2> "${ostderr}" > "${orawout}" 684 ) 685 tmp="$?" 686 run_record_cmdline "${ffilter}" "${ocmdline}" 687 } 688 if [ "$tmp" != 0 ]; then 689 if [ "${tmp}" = "${_BASH_INTERRUPT_EXIT}" ]; then 690 ERROR 1 "The execution is interrupted" 691 elif ! [ "$WITH_TIMEOUT" = 0 ] && [ "${tmp}" = "${_TIMEOUT_EXIT}" ]; then 692 echo "${category}/${name}" >> ${R_FAILED_BY_TIMEED_OUT} 693 run_result error "${msg}" "${oresult}" "TIMED OUT" 694 run_record_cmdline "${ffilter}" "${ocmdline}" 695 [ "${RUN_SHRINK}" = 'yes' ] \ 696 && [ $# -eq 0 ] \ 697 && run_shrink "${cmdline_template}" "${input}" "${oshrink}" "${guessed_lang}" 698 return 1 699 elif [ "$WITH_VALGRIND" = 'yes' ] && [ "${tmp}" = "${_VALGRIND_EXIT}" ] && ! [ "${class}" = v ]; then 700 echo "${category}/${name}" >> ${R_VALGRIND} 701 run_result error "${msg}" "${oresult}" "valgrind-error" 702 run_record_cmdline "${ffilter}" "${ocmdline}" 703 return 1 704 elif [ "$class" = 'b' ]; then 705 echo "${category}/${name}" >> ${R_KNOWN_BUGS} 706 run_result known_error "${msg}" "${oresult}" 707 run_record_cmdline "${ffilter}" "${ocmdline}" 708 [ "${RUN_SHRINK}" = 'yes' ] \ 709 && [ $# -eq 0 ] \ 710 && run_shrink "${cmdline_template}" "${input}" "${oshrink}" "${guessed_lang}" 711 return 0 712 else 713 echo "${category}/${name}" >> ${R_FAILED_BY_STATUS} 714 run_result error "${msg}" "${oresult}" "unexpected exit status: $tmp" 715 run_record_cmdline "${ffilter}" "${ocmdline}" 716 [ "${RUN_SHRINK}" = 'yes' ] \ 717 && [ $# -eq 0 ] \ 718 && run_shrink "${cmdline_template}" "${input}" "${oshrink}" "${guessed_lang}" 719 return 1 720 fi 721 elif [ "$WITH_VALGRIND" = 'yes' ] && [ "$class" = 'v' ]; then 722 echo "${category}/${name}" >> ${R_FIXED} 723 fi 724 725 if ! [ -f "${fexpected}" ]; then 726 clean_tcase "${o}" "${obundles}" 727 if [ "$class" = 'b' ]; then 728 echo "${category}/${name}" >> ${R_FIXED} 729 elif [ "$class" = 'i' ]; then 730 echo "${category}/${name}" >> ${R_FIXED} 731 fi 732 echo "${category}/${name}" >> ${R_PASSED} 733 run_result ok "${msg}" '/dev/null' "\"expected.tags*\" not found" 734 return 0 735 fi 736 737 ${output_type}_basename_filter < "${orawout}" | \ 738 anon_normalize "${CTAGS}" "${input}" "$@" | \ 739 $ffilter > "${ofiltered}" 740 741 { 742 diff -U 0 -I '^!_TAG' --strip-trailing-cr "${fexpected}" "${ofiltered}" > "${odiff}" 743 tmp="$?" 744 } 745 if [ "${tmp}" = 0 ]; then 746 clean_tcase "${o}" "${obundles}" 747 if [ "${class}" = 'b' ]; then 748 echo "${category}/${name}" >> ${R_FIXED} 749 elif ! [ "$WITH_TIMEOUT" = 0 ] && [ "${class}" = 'i' ]; then 750 echo "${category}/${name}" >> ${R_FIXED} 751 fi 752 753 echo "${category}/${name}" >> ${R_PASSED} 754 run_result ok "${msg}" '/dev/null' 755 return 0 756 else 757 if [ "${class}" = 'b' ]; then 758 echo "${category}/${name}" >> ${R_KNOWN_BUGS} 759 run_result known_error "${msg}" "${oresult}" 760 run_record_cmdline "${ffilter}" "${ocmdline}" 761 return 0 762 else 763 echo "${category}/${name}" >> ${R_FAILED_BY_DIFF} 764 run_result error "${msg}" "${oresult}" "unexpected output" 765 run_record_cmdline "${ffilter}" "${ocmdline}" 766 return 1 767 fi 768 fi 769} 770 771 772failure_in_globing () 773{ 774 # skip if globing failed, also ignore backup files 775 case $1 in 776 *\~) return 0 ;; 777 *\**) return 0 ;; 778 *) return 1 ;; 779 esac 780} 781 782run_dir () 783{ 784 local category="$1" 785 local base_dir="$2" 786 local build_base_dir="$3" 787 shift 3 788 789 local tcase_dir 790 local build_tcase_dir 791 local input 792 local name 793 local dname 794 local class 795 796 local extra_tmp 797 local extra_inputs 798 799 # 800 # Filtered by CATEGORIES 801 # 802 if [ -n "$CATEGORIES" ] && ! member_p "${category}" $CATEGORIES; then 803 return 1 804 fi 805 806 echo 807 echo "Category: $category" 808 line 809 for input in ${base_dir}/*.[dbtiv]/input.*; do 810 if failure_in_globing "$input"; then 811 continue 812 fi 813 814 dname=$(dirname $input) 815 extra_inputs=$(for extra_tmp in $dname/input[-_][0-9].* \ 816 $dname/input[-_][0-9][-_]*.* ; do 817 if failure_in_globing "$extra_tmp"; then 818 continue 819 fi 820 echo "$extra_tmp" 821 done | sort) 822 823 tcase_dir="${input%/input.*}" 824 build_tcase_dir="${build_base_dir}/${tcase_dir#${base_dir}/}" 825 name="${tcase_dir%.[dbtiv]}" 826 name="${name##*/}" 827 class="${tcase_dir#*${name}.}" 828 # Run this in parallel 829 run_tcase "${input}" "${tcase_dir}" "${name}" "${class}" "${category}" "${build_tcase_dir}" ${extra_inputs} & 830 done 831 wait 832 833 return 0 834} 835 836run_show_diff_output () 837{ 838 local units_dir="$1" 839 local t="$2" 840 841 printf " " 842 line . 843 sed -e 's/^.*$/ &/' ${units_dir}/${t}.*/${_DIFF_OUTPUT_NAME} 844 echo 845} 846 847run_show_stderr_output () 848{ 849 local units_dir="$1" 850 local t="$2" 851 852 printf " " 853 line . 854 sed -e 's/^.*$/ &/' ${units_dir}/${t}.*/${_STDERR_OUTPUT_NAME} | tail -50 855 echo 856} 857 858run_summary () 859{ 860 local build_dir="${1}" 861 local t 862 863 echo 864 echo "Summary (see CMDLINE.tmp to reproduce without test harness)" 865 line 866 867 printf ' %-40s' "#passed:" 868 L_PASSED=$([ -f $R_PASSED ] && cat $R_PASSED) 869 count_list $L_PASSED 870 871 printf ' %-40s' "#FIXED:" 872 L_FIXED=$([ -f $R_FIXED ] && cat $R_FIXED) 873 count_list $L_FIXED 874 for t in $L_FIXED; do 875 echo " ${t#${_DEFAULT_CATEGORY}/}" 876 done 877 878 printf ' %-40s' "#FAILED (broken args.ctags?):" 879 L_BROKEN_ARGS_CTAGS=$([ -f $R_BROKEN_ARGS_CTAGS ] && cat $R_BROKEN_ARGS_CTAGS) 880 count_list $L_BROKEN_ARGS_CTAGS 881 for t in $L_BROKEN_ARGS_CTAGS; do 882 echo " ${t#${_DEFAULT_CATEGORY}/}" 883 done 884 885 printf ' %-40s' "#FAILED (unexpected-exit-status):" 886 L_FAILED_BY_STATUS=$([ -f $R_FAILED_BY_STATUS ] && cat $R_FAILED_BY_STATUS) 887 count_list $L_FAILED_BY_STATUS 888 for t in $L_FAILED_BY_STATUS; do 889 echo " ${t#${_DEFAULT_CATEGORY}/}" 890 if [ "${SHOW_DIFF_OUTPUT}" = yes ]; then 891 run_show_stderr_output "${build_dir}" "${t#${_DEFAULT_CATEGORY}/}" 892 fi 893 done 894 895 printf ' %-40s' "#FAILED (unexpected-output):" 896 L_FAILED_BY_DIFF=$([ -f $R_FAILED_BY_DIFF ] && cat $R_FAILED_BY_DIFF) 897 count_list $L_FAILED_BY_DIFF 898 for t in $L_FAILED_BY_DIFF; do 899 echo " ${t#${_DEFAULT_CATEGORY}/}" 900 if [ "${SHOW_DIFF_OUTPUT}" = yes ]; then 901 run_show_stderr_output "${build_dir}" "${t#${_DEFAULT_CATEGORY}/}" 902 run_show_diff_output "${build_dir}" "${t#${_DEFAULT_CATEGORY}/}" 903 fi 904 done 905 906 if ! [ "$WITH_TIMEOUT" = 0 ]; then 907 printf ' %-40s' "#TIMED-OUT (${WITH_TIMEOUT}s)" 908 L_FAILED_BY_TIMEED_OUT=$([ -f $R_FAILED_BY_TIMEED_OUT ] && cat $R_FAILED_BY_TIMEED_OUT) 909 count_list $L_FAILED_BY_TIMEED_OUT 910 for t in $L_FAILED_BY_TIMEED_OUT; do 911 echo " ${t#${_DEFAULT_CATEGORY}/}" 912 done 913 fi 914 915 printf ' %-40s' "#skipped (features):" 916 L_SKIPPED_BY_FEATURES=$([ -f $R_SKIPPED_BY_FEATURES ] && cat $R_SKIPPED_BY_FEATURES) 917 count_list $L_SKIPPED_BY_FEATURES 918 for t in $L_SKIPPED_BY_FEATURES; do 919 echo " ${t#${_DEFAULT_CATEGORY}/}" 920 done 921 922 printf ' %-40s' "#skipped (languages):" 923 L_SKIPPED_BY_LANGUAGES=$([ -f $R_SKIPPED_BY_LANGUAGES ] && cat $R_SKIPPED_BY_LANGUAGES) 924 count_list $L_SKIPPED_BY_LANGUAGES 925 for t in $L_SKIPPED_BY_LANGUAGES; do 926 echo " ${t#${_DEFAULT_CATEGORY}/}" 927 done 928 929 if [ "$WITH_TIMEOUT" = 0 ]; then 930 printf ' %-40s' "#skipped (infinite-loop):" 931 L_SKIPPED_BY_ILOOP=$([ -f $R_SKIPPED_BY_ILOOP ] && cat $R_SKIPPED_BY_ILOOP) 932 count_list $L_SKIPPED_BY_ILOOP 933 for t in $L_SKIPPED_BY_ILOOP; do 934 echo " ${t#${_DEFAULT_CATEGORY}/}" 935 done 936 fi 937 938 printf ' %-40s' "#known-bugs:" 939 L_KNOWN_BUGS=$([ -f $R_KNOWN_BUGS ] && cat $R_KNOWN_BUGS) 940 count_list $L_KNOWN_BUGS 941 for t in $L_KNOWN_BUGS; do 942 echo " ${t#${_DEFAULT_CATEGORY}/}" 943 done 944 945 if [ "$WITH_VALGRIND" = yes ]; then 946 printf ' %-40s' "#valgrind-error:" 947 L_VALGRIND=$([ -f $R_VALGRIND ] && cat $R_VALGRIND) 948 count_list $L_VALGRIND 949 for t in $L_VALGRIND; do 950 echo " ${t#${_DEFAULT_CATEGORY}/}" 951 done 952 fi 953} 954 955make_pretense_map () 956{ 957 local ifs=$IFS 958 local p 959 local r 960 961 IFS=, 962 for p in $1; do 963 newlang=${p%/*} 964 oldlang=${p#*/} 965 966 if [ -z "$newlang" ]; then 967 ERROR 1 "newlang part of --pretend option arg is empty" 968 fi 969 if [ -z "$oldlang" ]; then 970 ERROR 1 "oldlang part of --pretend option arg is empty" 971 fi 972 973 r="$r --_pretend-$newlang=$oldlang" 974 done 975 IFS=$ifs 976 echo $r 977} 978 979delete_result_files () 980{ 981 rm -f ${R_PASSED} ${R_FIXED} ${R_FAILED_BY_STATUS} ${R_FAILED_BY_DIFF} \ 982 ${R_SKIPPED_BY_FEATURES} ${R_SKIPPED_BY_LANGUAGES} \ 983 ${R_SKIPPED_BY_ILOOP} ${R_KNOWN_BUGS} ${R_FAILED_BY_TIMEED_OUT} \ 984 ${R_BROKEN_ARGS_CTAGS} 985} 986 987action_run () 988{ 989 local action="$1" 990 shift 991 992 local units_dir 993 local build_dir 994 local d 995 local build_d 996 local category 997 998 local c 999 1000 while [ $# -gt 0 ]; do 1001 case $1 in 1002 --ctags) 1003 shift 1004 CTAGS="$1" 1005 shift 1006 ;; 1007 --ctags=*) 1008 CTAGS="${1#--ctags=}" 1009 shift 1010 ;; 1011 --categories) 1012 shift 1013 for c in $(echo "$1" | tr ',' ' '); do 1014 if [ "$c" = "ROOT" ]; then 1015 CATEGORIES="$CATEGORIES ROOT" 1016 else 1017 CATEGORIES="$CATEGORIES ${c%.r}.r" 1018 fi 1019 done 1020 shift 1021 ;; 1022 --categories=*) 1023 for c in $(echo "${1#--categories=}" | tr ',' ' '); do 1024 if [ "$c" = "ROOT" ]; then 1025 CATEGORIES="$CATEGORIES ROOT" 1026 else 1027 CATEGORIES="$CATEGORIES ${c%.r}.r" 1028 fi 1029 done 1030 shift 1031 ;; 1032 --units) 1033 shift 1034 UNITS=$(echo "$1" | tr ',' ' ') 1035 shift 1036 ;; 1037 --units=*) 1038 UNITS=$(echo "${1#--units=}" | tr ',' ' ') 1039 shift 1040 ;; 1041 --languages) 1042 shift 1043 LANGUAGES=$(echo "${1}" | tr ',' ' ') 1044 shift 1045 ;; 1046 --languages=*) 1047 LANGUAGES=$(echo "${1#--languages=}" | tr ',' ' ') 1048 shift 1049 ;; 1050 --with-timeout) 1051 shift 1052 WITH_TIMEOUT="$1" 1053 shift 1054 ;; 1055 --with-timeout=*) 1056 WITH_TIMEOUT="${1#--with-timeout=}" 1057 shift 1058 ;; 1059 --with-valgrind) 1060 shift 1061 WITH_VALGRIND=yes 1062 ;; 1063 --colorized-output) 1064 shift 1065 COLORIZED_OUTPUT="$1" 1066 shift 1067 ;; 1068 --colorized-output=*) 1069 COLORIZED_OUTPUT="${1#--colorized-output=}" 1070 shift 1071 ;; 1072 --run-shrink) 1073 RUN_SHRINK=yes 1074 shift 1075 ;; 1076 --show-diff-output) 1077 SHOW_DIFF_OUTPUT=yes 1078 shift 1079 ;; 1080 --with-pretense-map) 1081 shift 1082 PRETENSE_OPTS=$(make_pretense_map "$1") 1083 shift 1084 ;; 1085 --with-pretense-map=*) 1086 PRETENSE_OPTS=$(make_pretense_map "${1#--with-pretense-map=}") 1087 shift 1088 ;; 1089 -*) 1090 ERROR 1 "unknown option \"${1}\" for ${action} action" 1091 ;; 1092 *) 1093 units_dir="$1" 1094 shift 1095 build_dir="${1:-${units_dir}}" 1096 shift 1097 break 1098 ;; 1099 esac 1100 done 1101 1102 if [ $# -gt 0 ]; then 1103 ERROR 1 "too many arguments for ${action} action: $*" 1104 elif [ -z "$units_dir" ]; then 1105 ERROR 1 "UNITS_DIR parameter is not given in ${action} action" 1106 fi 1107 1108 if ! [ -d "$units_dir" ]; then 1109 ERROR 1 "No such directory: ${units_dir}" 1110 fi 1111 1112 case "${build_dir}" in 1113 /*) ;; 1114 *) build_dir=$(pwd)/${build_dir} ;; 1115 esac 1116 1117 if ! [ -d "$build_dir" ]; then 1118 ERROR 1 "No such directory(build_dir): ${build_dir}" 1119 fi 1120 1121 if ! [ -f "${CTAGS}" ]; then 1122 ERROR 1 "no such file: ${CTAGS}" 1123 elif ! [ -e "${CTAGS}" ]; then 1124 ERROR 1 "${CTAGS} is not an executable file" 1125 fi 1126 1127 if ! ( [ "${COLORIZED_OUTPUT}" = 'yes' ] || [ "${COLORIZED_OUTPUT}" = 'no' ] ); then 1128 ERROR 1 "unexpected option argument for --colorized-output: ${COLORIZED_OUTPUT}" 1129 fi 1130 1131 : ${WITH_TIMEOUT:=0} 1132 [ "$WITH_TIMEOUT" = 0 ] || check_availability timeout 1133 [ "$WITH_VALGRIND" = 'yes' ] && check_availability valgrind 1134 [ "$MSYSTEM" != '' ] && check_availability dos2unix 1135 check_availability grep 1136 check_availability diff 1137 init_features 1138 1139 delete_result_files 1140 1141 category="${_DEFAULT_CATEGORY}" 1142 if [ -z "$CATEGORIES" ] \ 1143 || ( [ -n "$CATEGORIES" ] && member_p "${category}" $CATEGORIES ); then 1144 run_dir "${category}" "${units_dir}" "${build_dir}" 1145 fi 1146 1147 for d in ${units_dir}/*.r; do 1148 [ -d "$d" ] || continue 1149 category="${d##*/}" 1150 build_d=${build_dir}/${category} 1151 run_dir "${category}" "$d" "${build_d}" 1152 done 1153 1154 run_summary "${build_dir}" 1155 delete_result_files 1156 1157 if [ -n "${L_FAILED_BY_STATUS}" ] || 1158 [ -n "${L_FAILED_BY_DIFF}" ] || 1159 [ -n "${L_FAILED_BY_TIMEED_OUT}" ] || 1160 [ -n "${L_BROKEN_ARGS_CTAGS}" ]; then 1161 return 1 1162 else 1163 return 0 1164 fi 1165} 1166 1167help_run () 1168{ 1169cat <<EOF 1170$0 run [OPTIONS] UNITS-DIR 1171 1172 Run all tests case under UNITS-DIR. 1173 1174 OPTIONS: 1175 --ctags CTAGS: ctags executable file for testing 1176 --categories CATEGORY1[,CATEGORY2,...]: run only CATEGORY* related cases. 1177 Category selection is done in upper 1178 layer than unit selection. This 1179 means even if a unit is specified 1180 with --units, it can be ignored 1181 is a category the units doesn't 1182 belong to is specified with 1183 --categories option. 1184 --colorized-output yes|no: print the result in color. 1185 --skip NAME: skip the case NAME (TODO: NOT IMPLEMENTED YET) 1186 --languages PARSER1[,PARSER2,...]: run only PARSER* related cases. 1187 --units UNITS1[,UNITS2,...]: run only UNIT(S). 1188 --with-timeout DURATION: run a test case under timeout 1189 command with SECOND. 1190 0 means no timeout(default). 1191 --with-valgrind: run a test case under valgrind 1192 If this option given, DURATION is changed to 1193 DURATION := DURATION * ${_VG_TIMEOUT_FACTOR} 1194 --show-diff-output: show diff output for failed test cases in the summary. 1195 --with-pretense-map=NEWLANG0/OLDLANG0[,...]: make NEWLANG parser pretend 1196 OLDLANG. 1197EOF 1198} 1199 1200action_clean () 1201{ 1202 local action="$1" 1203 shift 1204 1205 local units_dir=$1 1206 shift 1207 1208 local bundles 1209 local b 1210 1211 if [ $# -gt 0 ]; then 1212 ERROR 1 "too many arguments for ${action} action: $*" 1213 elif [ -z "$units_dir" ]; then 1214 ERROR 1 "UNITS_DIR parameter is not given in ${action} action" 1215 fi 1216 1217 if ! [ -d "$units_dir" ]; then 1218 ERROR 0 "No such directory: ${units_dir}" 1219 fi 1220 1221 check_availability find 1222 check_availability rm 1223 1224 for bundles in $(find "$units_dir" -name "BUNDLES"); do 1225 while read b; do 1226 rm -rf "${b}" 1227 done < ${bundles} 1228 rm ${bundles} 1229 done 1230 1231 rm -f $(find "$units_dir" -name '*.tmp') 1232 rm -f $(find "$units_dir" -name '*.TMP') 1233 return 0 1234} 1235 1236help_clean () 1237{ 1238cat <<EOF 1239$0 clean UNITS-DIR 1240 1241 Clean all files created during units testing 1242EOF 1243 1244} 1245 1246shrink_prepare () 1247{ 1248 local output="$1" 1249 local input="$2" 1250 local start="$3" 1251 local len="$4" 1252 1253 1254 dd bs=1 count="${len}" skip="${start}" < "${input}" 2>/dev/null > "${output}" 1255} 1256 1257shrink_test () 1258{ 1259 local cmdline="$1" 1260 local input="$2" 1261 local start="$3" 1262 local len="$4" 1263 local output="$5" 1264 local r 1265 local msg 1266 1267 shrink_prepare "${output}" "${input}" "${start}" "${len}" 1268 [ "${QUIET}" = 'yes' ] || printf "[%-5u %6u]..." "${start}" $(( start + len )) 1>&2 1269 eval "${cmdline}" > /dev/null 2>&1 1270 r="$?" 1271 if [ "$r" -eq 0 ]; then 1272 msg='ok' 1273 elif [ "$r" -eq "${_TIMEOUT_EXIT}" ]; then 1274 msg='timeout' 1275 else 1276 msg='failed' 1277 fi 1278 [ "${QUIET}" = 'yes' ] || printf "%s(%u)\n" "$msg" "$r" 1>&2 1279 return $r 1280} 1281 1282shrink_bisect () 1283{ 1284 local cmdline="$1" 1285 local input="$2" 1286 local len="$3" 1287 local output="$4" 1288 1289 local end 1290 local start 1291 local step 1292 local delta 1293 1294 local failed 1295 local successful 1296 1297 end="${len}" 1298 failed="${len}" 1299 successful=0 1300 1301 step=0 1302 while true; do 1303 delta=$((len >> (step + 1))) 1304 if [ "${delta}" -eq 0 ]; then 1305 delta=1 1306 fi 1307 if shrink_test "${cmdline}" "${input}" 0 "${end}" "${output}"; then 1308 successful="${end}" 1309 if [ $(( end + 1 )) -eq "${failed}" ]; then 1310 end="${failed}" 1311 break 1312 else 1313 end=$((end + delta)) 1314 fi 1315 else 1316 failed="$end" 1317 if [ $(( successful + 1 )) -eq "${end}" ]; then 1318 break 1319 else 1320 end=$((end - delta)) 1321 fi 1322 fi 1323 step=$((step + 1 )) 1324 done 1325 1326 len="${end}" 1327 start=0 1328 failed=0 1329 successful="${end}" 1330 step=0 1331 while true; do 1332 delta=$((len >> (step + 1))) 1333 if [ "${delta}" -eq 0 ]; then 1334 delta=1 1335 fi 1336 if shrink_test "${cmdline}" "${input}" "${start}" $((end - start)) "${output}"; then 1337 successful="${start}" 1338 if [ $(( start - 1 )) -eq "${failed}" ]; then 1339 start=$((start - 1)) 1340 break 1341 else 1342 start=$((start - delta)) 1343 fi 1344 else 1345 failed="${start}" 1346 if [ $((successful - 1)) -eq "${start}" ]; then 1347 break 1348 else 1349 start=$((start + delta)) 1350 fi 1351 fi 1352 step=$((step + 1)) 1353 done 1354 1355 len=$((end - start)) 1356 shrink_prepare "${output}" "${input}" "${start}" "${len}" 1357 [ "${QUIET}" = 'yes' ] || echo "Minimal badinput: ${output}" 1358 [ "${QUIET}" = 'yes' ] || line . 1359 cat "${output}" 1360 echo 1361 1362 return 0 1363} 1364 1365shrink_main () 1366{ 1367 local cmdline_template="$1" 1368 local cmdline 1369 local input="$2" 1370 local len 1371 local output="$3" 1372 local duration="$4" 1373 local foreground="$5" 1374 1375 if ! [ -f "${input}" ]; then 1376 ERROR 1 "No such file: ${input}" 1377 elif ! [ -r "${input}" ]; then 1378 ERROR 1 "Cannot read a file: ${input}" 1379 fi 1380 1381 if ! cat < /dev/null > "${output}"; then 1382 ERROR 1 "Cannot modify a file: ${output}" 1383 fi 1384 1385 cmdline=$(printf "${cmdline_template}" "${output}") 1386 if [ -n "${duration}" ] && ! [ "${duration}" -eq 0 ]; then 1387 if [ "${foreground}" = 'yes' ]; then 1388 cmdline="timeout --foreground ${duration} ${cmdline}" 1389 else 1390 cmdline="timeout ${duration} ${cmdline}" 1391 fi 1392 fi 1393 1394 len=$(${_FSIZE} "${input}") 1395 1396 if shrink_test "${cmdline}" "${input}" 0 "${len}" "${output}"; then 1397 printf "the target command line exits normally against the original input\n" 1>&2 1398 return 1 1399 fi 1400 1401 if ! shrink_test "${cmdline}" "${input}" 0 0 "${output}"; then 1402 printf "the target command line exits abnormally against the empty input\n" 1>&2 1403 return 1 1404 fi 1405 1406 shrink_bisect "${cmdline}" "${input}" "${len}" "${output}" 1407} 1408 1409action_shrink () 1410{ 1411 local action="$1" 1412 shift 1413 1414 local cmdline_template 1415 local input 1416 local output 1417 1418 local timeout 1419 local duration 1420 local foreground 1421 1422 1423 while [ $# -gt 0 ]; do 1424 case $1 in 1425 --timeout) 1426 shift 1427 duration=$1 1428 shift 1429 ;; 1430 --timeout=*) 1431 duration="${1#--timeout=}" 1432 shift 1433 ;; 1434 --foreground) 1435 foreground=yes 1436 shift 1437 ;; 1438 --quiet) 1439 QUIET=yes 1440 shift 1441 ;; 1442 -*) 1443 ERROR 1 "unknown option \"${1}\" for ${action} action" 1444 ;; 1445 *) 1446 break 1447 ;; 1448 esac 1449 done 1450 1451 if [ $# -lt 3 ]; then 1452 ERROR 1 "too few arguments for ${action} action: $*" 1453 elif [ $# -gt 3 ]; then 1454 ERROR 1 "too many arguments for ${action} action: $*" 1455 fi 1456 1457 if [ -n "${foreground}" ] && [ -z "${duration}" ]; then 1458 ERROR 1 "--foreground option is meaningful only if --timeout option is specified." 1459 fi 1460 1461 cmdline_template=$1 1462 input=$2 1463 output=$3 1464 shift 3 1465 1466 shrink_main "${cmdline_template}" "${input}" "${output}" ${duration} ${foreground} 1467 return $? 1468} 1469 1470help_shrink () 1471{ 1472cat <<EOF 1473$0 shrink [OPTIONS] CMD_TEMPLATE INPUT OUTPUT 1474 1475 Shrink the input while the execution of CMD_TEMPLATE is failed 1476 and find minimal unwanted input. 1477 1478 OPTIONS: 1479 --timeout N: Run CMD under timeout command with duration N 1480 --foreground: add --foreground option to timeout command. 1481 can be used with --timeout option. 1482 EXAMPLES: 1483 misc/units shrink "u-ctags -o - %s" original-input.js /tmp/anyname.js 1484EOF 1485} 1486#action_shrink shrink --timeout=1 --foreground "./a.out < %s" input.txt output.txt 1487 1488fuzz_shrink () 1489{ 1490 local cmdline_template="$1" 1491 local input="$2" 1492 local output="$3" 1493 local lang="$4" 1494 shift 4 1495 1496 [ "${QUIET}" = 'yes' ] || { 1497 echo "Shrinking ${input} as ${lang}" 1498 line . 1499 } 1500 shrink_main "${cmdline_template}" "${input}" "${output}" 1 yes 1501} 1502 1503fuzz_lang_file () 1504{ 1505 local lang="$1" 1506 local file="$2" 1507 shift 2 1508 local r 1509 1510 local dir="${file%/*}" 1511 local ovalgrind="${dir}/VALGRIND-${lang}.tmp" 1512 local ocmdline="${dir}/CMDLINE-${lang}.tmp" 1513 local oshrink="${dir}/SHRINK-${lang}.tmp" 1514 1515 local cmdline 1516 local cmdline_for_shirking 1517 1518 rm -f "${ovalgrind}" "${ocmdline}" "${oshrink}" 1519 1520 1521 if [ "${WITH_VALGRIND}" = 'yes' ]; then 1522 cmdline=$( printf "${_CMDLINE} --language-force=${lang} ${file}" "${ovalgrind}" ) 1523 else 1524 cmdline="${_CMDLINE} --language-force=${lang} ${file}" 1525 1526 fi 1527 cmdline_for_shirking="${_CMDLINE_FOR_SHRINKING} --language-force=${lang} %s" 1528 1529 1530 [ "${QUIET}" = 'yes' ] || printf "." 1531 echo "${cmdline}" > "${ocmdline}" 1532 ${cmdline} > /dev/null 1533 r=$? 1534 1535 case $r in 1536 0) 1537 rm -f "${ovalgrind}" "${ocmdline}" 1538 return 0 1539 ;; 1540 ${_TIMEOUT_EXIT}) 1541 [ "${QUIET}" = 'yes' ] || echo 1542 printf '%-40s' "[timeout $lang]" 1543 echo "$f" 1544 [ "${RUN_SHRINK}" = 'yes' ] && fuzz_shrink "${cmdline_for_shirking}" "${file}" "${oshrink}" "${lang}" 1545 return 1 1546 ;; 1547 ${_VALGRIND_EXIT}) 1548 [ "${QUIET}" = 'yes' ] || echo 1549 printf '%-40s' "[valgrind-error $lang]" 1550 echo "$f" 1551 return 1 1552 ;; 1553 *) 1554 [ "${QUIET}" = 'yes' ] || echo 1555 printf '%-40s' "[unexpected-status($r) $lang]" 1556 echo "$f" 1557 [ "${RUN_SHRINK}" = 'yes' ] && fuzz_shrink "${cmdline_for_shirking}" "${file}" "${oshrink}" "${lang}" 1558 return 1 1559 ;; 1560 esac 1561 1562 return $r 1563} 1564 1565fuzz_lang () 1566{ 1567 local lang="$1" 1568 local dir="$2" 1569 shift 2 1570 local f 1571 local r=0 1572 1573 [ "${QUIET}" = 'yes' ] || printf '%-60s\n' "Semi-fuzzing (${lang})" 1574 for f in $(find "${dir}" -type f -name 'input.*'); do 1575 if ! fuzz_lang_file "${lang}" "${f}"; then 1576 r=1 1577 break 1578 fi 1579 done 1580 [ "${QUIET}" = 'yes' ] || echo 1581 return $r 1582} 1583 1584action_fuzz () 1585{ 1586 action_fuzz_common fuzz_lang "$@" 1587} 1588 1589action_fuzz_common () 1590{ 1591 local fn="$1" 1592 local action="$2" 1593 shift 2 1594 1595 local units_dir 1596 local cmdline 1597 local lang 1598 local r 1599 1600 while [ $# -gt 0 ]; do 1601 case $1 in 1602 --ctags) 1603 shift 1604 CTAGS="$1" 1605 shift 1606 ;; 1607 --ctags=*) 1608 CTAGS="${1#--ctags=}" 1609 shift 1610 ;; 1611 --languages) 1612 shift 1613 LANGUAGES=$(echo "${1}" | tr ',' ' ') 1614 shift 1615 ;; 1616 --languages=*) 1617 LANGUAGES=$(echo "${1#--languages=}" | tr ',' ' ') 1618 shift 1619 ;; 1620 --quiet) 1621 QUIET=yes 1622 shift 1623 ;; 1624 --with-timeout) 1625 shift 1626 WITH_TIMEOUT="$1" 1627 shift 1628 ;; 1629 --with-timeout=*) 1630 WITH_TIMEOUT="${1#--with-timeout=}" 1631 shift 1632 ;; 1633 --with-valgrind) 1634 shift 1635 WITH_VALGRIND=yes 1636 ;; 1637 --colorized-output) 1638 shift 1639 COLORIZED_OUTPUT="$1" 1640 shift 1641 ;; 1642 --colorized-output=*) 1643 COLORIZED_OUTPUT="${1#--colorized-output=}" 1644 shift 1645 ;; 1646 --run-shrink) 1647 RUN_SHRINK=yes 1648 shift 1649 ;; 1650 -*) 1651 ERROR 1 "unknown option \"${1}\" for ${action} action" 1652 ;; 1653 *) 1654 units_dir="$1" 1655 shift 1656 break; 1657 ;; 1658 esac 1659 done 1660 1661 if [ $# -gt 0 ]; then 1662 ERROR 1 "too many arguments for ${action} action: $*" 1663 elif [ -z "$units_dir" ]; then 1664 ERROR 1 "UNITS_DIR parameter is not given in ${action} action" 1665 fi 1666 1667 if ! [ -d "$units_dir" ]; then 1668 ERROR 0 "No such directory: ${units_dir}" 1669 fi 1670 1671 if ! [ -f "${CTAGS}" ]; then 1672 ERROR 1 "no such file: ${CTAGS}" 1673 elif ! [ -e "${CTAGS}" ]; then 1674 ERROR 1 "${CTAGS} is not an executable file" 1675 fi 1676 1677 if ! ( [ "${COLORIZED_OUTPUT}" = 'yes' ] || [ "${COLORIZED_OUTPUT}" = 'no' ] ); then 1678 ERROR 1 "unexpected option argument for --colorized-output: ${COLORIZED_OUTPUT}" 1679 fi 1680 1681 : ${WITH_TIMEOUT:=2} 1682 [ "$WITH_TIMEOUT" = 0 ] || check_availability timeout 1683 [ "$WITH_VALGRIND" = 'yes' ] && check_availability valgrind 1684 check_availability find 1685 1686 cmdline="${CTAGS} --quiet --options=NONE --kinds-all=* --fields=*" 1687 _CMDLINE="${cmdline} -G -o - " 1688 _CMDLINE_FOR_SHRINKING="${_CMDLINE}" 1689 if [ "$WITH_VALGRIND" = yes ]; then 1690 _CMDLINE="valgrind --leak-check=full --error-exitcode=${_VALGRIND_EXIT} --log-file=%s ${_CMDLINE}" 1691 WITH_TIMEOUT=$(( WITH_TIMEOUT * ${_VG_TIMEOUT_FACTOR} )) 1692 fi 1693 1694 if ! [ "$WITH_TIMEOUT" = 0 ]; then 1695 _CMDLINE="timeout --foreground $WITH_TIMEOUT ${_CMDLINE}" 1696 _CMDLINE_FOR_SHRINKING="timeout --foreground 1 ${_CMDLINE_FOR_SHRINKING}" 1697 fi 1698 1699 for lang in $( ${cmdline} --list-languages 2>/dev/null | "${_LINE_SPLITTER}" |sed -e 's/ //' ) ; do 1700 if [ -n "${LANGUAGES}" ] && ! member_p "${lang}" ${LANGUAGES}; then 1701 continue 1702 fi 1703 "${fn}" "${lang}" "${units_dir}" 1704 r=$? 1705 done 1706 1707 return $r 1708} 1709 1710help_fuzz () 1711{ 1712cat <<EOF 1713$0 fuzz [OPTIONS] UNITS-DIR 1714 1715 Run all tests case under UNITS-DIR. 1716 1717 OPTIONS: 1718 --ctags CTAGS: ctags executable file for testing 1719 --languages PARSER1[,PARSER2,...]: run only PARSER* related cases 1720 --quiet: don't print dots as passed test cases. 1721 --with-timeout DURATION: run a test case under timeout 1722 command with SECOND. 1723 0 means no timeout. 1724 default is 1. 1725 --with-valgrind: run a test case under valgrind 1726 If this option given, DURATION is changed to 1727 DURATION := DURATION * ${_VG_TIMEOUT_FACTOR} 1728EOF 1729} 1730 1731noise_reduce () 1732{ 1733 local input="$1" 1734 local len="$2" 1735 local pos="$3" 1736 shift 3 1737 1738 dd bs=1 count=$pos skip=0 if="$input" 1739 dd bs=1 count=$(( len - pos - 1 )) skip=$(( pos + 1 )) if="$input" 1740} 1741 1742noise_inject () 1743{ 1744 local input="$1" 1745 local len="$2" 1746 local pos="$3" 1747 local c="$4" 1748 shift 4 1749 1750 dd bs=1 count=$pos skip=0 if="$input" 1751 printf "%c" "$c" 1752 dd bs=1 count=$(( len - pos )) skip=$pos if="$input" 1753} 1754 1755noise_report_line () 1756{ 1757 local pos="$1" 1758 local len="$2" 1759 local status_="$3" 1760 local how="$4" 1761 1762 local progress_offset=$(( pos % _NOISE_REPORT_MAX_COLUMN )) 1763 local nspace 1764 1765 1766 if [ $((pos + 1)) -eq "${len}" ] || [ $status_ -gt 0 ]; then 1767 nspace=0 1768 while [ $nspace -lt $(( _NOISE_REPORT_MAX_COLUMN - progress_offset - 1)) ]; do 1769 printf ' ' 1770 nspace=$((nspace + 1)) 1771 done 1772 printf " %s %d/%d" "${how}" "$pos" "${len}" 1773 fi 1774} 1775 1776noise_lang_file_noisespec () 1777{ 1778 local input="$1" 1779 local len="$2" 1780 local pos="$3" 1781 local c="$4" 1782 local genfn="$5" 1783 local lang="$6" 1784 local how="$7" 1785 shift 7 1786 1787 local msg 1788 if [ "${how}" = + ]; then 1789 msg=INJECTED 1790 how="${how}${c}" 1791 else 1792 msg=REDUCED 1793 how="${how} " 1794 fi 1795 1796 local dir="${input%/*}" 1797 local onoised=$(printf "%s/NOISE-INPUT-%s-%s-%d.tmp" "${dir}" "${msg}" "$pos" \'$c) 1798 local ocmdline=$(printf "%s/NOISE-CMDLINE-%s-%s-%d.tmp" "${dir}" "${msg}" "$pos" \'$c) 1799 local ovalgrind=$(printf "%s/NOISE-VALGRIND-%s-%s-%d.tmp" "${dir}" "${msg}" "$pos" \'$c) 1800 local oshrink=$(printf "%s/NOISE-SHRINK-%s-%s-%d.tmp" "${dir}" "${msg}" "$pos" \'$c) 1801 1802 local cmdline 1803 local cmdline_for_shirking 1804 local progress_offset 1805 local r 1806 1807 rm -f "${ocmdline}" "${ovalgrind}" "${onoised}" "${oshrink}" 1808 if [ "${WITH_VALGRIND}" = 'yes' ]; then 1809 cmdline=$( printf "${_CMDLINE} --language-force=${lang} ${onoised}" "${ovalgrind}" ) 1810 else 1811 cmdline="${_CMDLINE} --language-force=${lang} ${onoised}" 1812 fi 1813 cmdline_for_shirking="${_CMDLINE_FOR_SHRINKING} --language-force=${lang} %s" 1814 1815 "${genfn}" "${input}" "${len}" "$pos" "$c" > "$onoised" 2> /dev/null 1816 1817 progress_offset=$(( pos % _NOISE_REPORT_MAX_COLUMN )) 1818 if [ "${progress_offset}" -eq 0 ]; then 1819 [ $pos -gt 0 ] && printf " %s %d/%d" "${how}" "$pos" "${len}" 1820 echo 1821 fi 1822 1823 echo "${cmdline}" > "${ocmdline}" 1824 ( exec 2>&-; ${cmdline} 2> /dev/null > /dev/null ) 1825 r=$? 1826 case $r in 1827 0) 1828 printf 'o' 1829 noise_report_line "${pos}" "${len}" "${r}" "${how}" 1830 rm "${onoised}" 1831 rm -f "${ovalgrind}" "${ocmdline}" 1832 ;; 1833 ${_TIMEOUT_EXIT}) 1834 printf "T" 1835 noise_report_line "${pos}" "${len}" "${r}" "${how}" 1836 printf '\n%-20s\n' "[timeout $lang]" "$onoised" 1837 [ "${RUN_SHRINK}" = 'yes' ] && fuzz_shrink "${cmdline_for_shirking}" "${onoised}" "${oshrink}" "${lang}" 1838 ;; 1839 ${_VALGRIND_EXIT}) 1840 printf "V" 1841 noise_report_line "${pos}" "${len}" "${r}" "${how}" 1842 printf '\n%-20s %s\n' "[valgrind-error $lang]" "$onoised" 1843 ;; 1844 *) 1845 printf "!" 1846 noise_report_line "${pos}" "${len}" "${r}" "${how}" 1847 printf '\n%-20s %s\n' "[unexpected-status($r) $lang]" "$onoised" 1848 [ "${RUN_SHRINK}" = 'yes' ] && fuzz_shrink "${cmdline_for_shirking}" "${onoised}" "${oshrink}" "${lang}" 1849 ;; 1850 esac 1851 return $r 1852} 1853 1854noise_lang_file () 1855{ 1856 local lang="$1" 1857 local input="$2" 1858 shift 2 1859 1860 1861 local cmdline 1862 local cmdline_for_shirking 1863 local len=$(${_FSIZE} "${input}") 1864 local r 1865 local i 1866 local c 1867 local guessed_lang 1868 1869 guessed_lang=$( ${_CMDLINE_FOR_SHRINKING} --print-language "${input}" 2>/dev/null | sed -n 's/^.*: //p') 1870 if [ "${lang}" != "${guessed_lang}" ]; then 1871 return 0 1872 fi 1873 1874 i=0 1875 c='!' 1876 echo "Testing cases derived from: ${input}" 1877 line '.' --no-newline 1878 while [ "$i" -lt "$len" ]; do 1879 if noise_lang_file_noisespec "${input}" "${len}" "$i" "$c" noise_reduce "${lang}" -; then 1880 i=$(( i + 1 )) 1881 else 1882 echo 1883 return 1 1884 fi 1885 done 1886 1887 for c in 'a' '0' \ 1888 '!' '@' '#' '$' '%' '^' '&' '*' '(' ')' '-' '=' '_' \ 1889 '+' '|' '[' ']' '{' '}' '\' ';' "'" ':' '"' ',' '.' \ 1890 '/' '<' '>' '?' '`' '~'; do 1891 i=0 1892 while [ "$i" -lt "$len" ]; do 1893 if noise_lang_file_noisespec "${input}" "${len}" "$i" "$c" noise_inject "${lang}" +; then 1894 i=$(( i + 1 )) 1895 else 1896 echo 1897 return 1 1898 fi 1899 done 1900 done 1901 echo 1902 return 0 1903} 1904 1905noise_lang () 1906{ 1907 local lang="$1" 1908 local dir="$2" 1909 shift 2 1910 local f 1911 local r 1912 printf '%-60s\n' "Noised-fuzzing (${lang})" 1913 line '-' 1914 1915 r=0 1916 for f in $(find "${dir}" -type f -name 'input.*'); do 1917 if ! noise_lang_file "${lang}" "${f}"; then 1918 r=1 1919 break 1920 fi 1921 done 1922 echo 1923 return $r 1924} 1925 1926action_noise () 1927{ 1928 action_fuzz_common noise_lang "$@" 1929} 1930 1931help_noise () 1932{ 1933cat <<EOF 1934$0 noise [OPTIONS] UNITS-DIR 1935 1936 Run all tests case for LANGUAGE with "noise" for 1937 finding unexpected behavior like entering an 1938 infinite loop. 1939 Here "noise" means removing one byte from 1940 somewhere file position of the original test case; 1941 or adding something one byte to 1942 somewhere file position of the original test case. 1943 1944 OPTIONS: 1945 --ctags CTAGS: ctags executable file for testing 1946 --languages PARSER1[,PARSER2,...]: run only PARSER* related cases 1947 --quiet: don't print dots as passed test cases. 1948 --with-timeout DURATION: run a test case under timeout 1949 command with SECOND. 1950 0 means no timeout. 1951 default is 1. 1952 --with-valgrind: run a test case under valgrind 1953 If this option given, DURATION is changed to 1954 DURATION := DURATION * ${_VG_TIMEOUT_FACTOR} 1955EOF 1956} 1957 1958 1959tmain_compare_result() 1960{ 1961 local build_topdir=$1 1962 local f 1963 1964 for f in ${build_topdir}/*/*-diff.txt; do 1965 if [ -f "$f" ]; then 1966 echo "$f" 1967 echo 1968 cat "$f" | sed -e 's|.*| &|' 1969 echo 1970 fi 1971 done 1972 if [ -f ${build_topdir}/*/gdb-backtrace.txt ]; then 1973 cat ${build_topdir}/*/gdb-backtrace.txt 1974 fi 1975} 1976 1977tmain_compare() 1978{ 1979 local subdir=$1 1980 local build_subdir=$2 1981 local aspect=$3 1982 local generated 1983 local msg 1984 1985 msg=$(printf '%-60s' "${aspect}") 1986 generated=${build_subdir}/${aspect}-diff.txt 1987 if diff -U 0 --strip-trailing-cr \ 1988 ${subdir}/${aspect}-expected.txt \ 1989 ${build_subdir}/${aspect}-actual.txt \ 1990 > ${generated} 2>&1; then 1991 run_result ok "${msg}" '/dev/null' 1992 rm ${generated} 1993 return 0 1994 else 1995 run_result error "${msg}" '/dev/null' "diff: ${generated}" 1996 return 1 1997 fi 1998} 1999 2000failed_git_marker () 2001{ 2002 local f=$1 2003 local l 2004 2005 if type "git" > /dev/null 2>&1; then 2006 l=$(git ls-files -- "$f") 2007 if [ -z "$l" ]; then 2008 echo '<G>' 2009 fi 2010 fi 2011} 2012 2013is_crashed () 2014{ 2015 local f=$1 2016 2017 grep -q -i "core dump" "$f" 2018} 2019 2020print_backtraces() 2021{ 2022 local ctags_exe=$1 2023 shift 1 2024 2025 local coref 2026 for coref in "$@"; do 2027 if [ -f "${coref}" ]; then 2028 gdb "${ctags_exe}" -c "${coref}" -ex where -batch 2029 else 2030 echo "no such file: ${coref}" 2031 fi 2032 done 2033} 2034 2035CODE_FOR_IGNORING_THIS_TMAIN_TEST=77 2036tmain_run () 2037{ 2038 local topdir=$1 2039 local build_topdir=$2 2040 shift 2 2041 local units="$@" 2042 2043 local subdir 2044 local basedir 2045 2046 local test_name 2047 local r_failed="_failed.result" 2048 local failed 2049 local f 2050 2051 local aspect 2052 local engine 2053 2054 local r 2055 local a 2056 local status_=0 2057 local msg 2058 2059 local need_rearrange 2060 2061 if ! [ $(basename "${CTAGS}") = 'ctags' ]; then 2062 need_rearrange=yes 2063 fi 2064 2065 rm -f ${r_failed} 2066 basedir=$(pwd) 2067 for subdir in ${topdir}/*.d; do 2068 if [ "${subdir}" = ${topdir}/'*.d' ]; then 2069 return 1 2070 fi 2071 2072 test_name=$(basename ${subdir} .d) 2073 2074 if [ -n "${units}" ] && ! member_p "${test_name}" ${units}; then 2075 continue 2076 fi 2077 2078 build_subdir=${build_topdir}/$(basename ${subdir}) 2079 if ! mkdir -p ${build_subdir}; then 2080 return 1 2081 fi 2082 2083 # Run this block in parallel 2084 ( 2085 rm -f ${build_subdir}/*-actual.txt 2086 2087 msg="\nTesting ${test_name}" 2088 msg="${msg}\n$(line '-')" 2089 ( 2090 cd ${subdir} 2091 ${SHELL} run.sh \ 2092 ${basedir}/${CTAGS} \ 2093 ${build_subdir} \ 2094 ${basedir}/${READTAGS} 2095 ) > ${build_subdir}/stdout-actual.txt 2> ${build_subdir}/stderr-actual.txt 2096 r=$? 2097 echo $r > ${build_subdir}/exit-actual.txt 2098 2099 if [ -n "${need_rearrange}" ]; then 2100 sed -i -e 's|^'$(basename "${CTAGS}")':|ctags:|' ${build_subdir}/stderr-actual.txt 2101 fi 2102 2103 if [ $r = $CODE_FOR_IGNORING_THIS_TMAIN_TEST ]; then 2104 msg="${msg}\n$(run_result skip "" '/dev/null' "$(cat ${build_subdir}/stdout-actual.txt)")" 2105 for a in ${build_subdir}/*-actual.txt; do 2106 if [ -f "$a" ]; then 2107 rm $a 2108 fi 2109 done 2110 printf "%b\n" "${msg}" 2111 exit 2112 fi 2113 2114 if [ -f ${build_subdir}/tags ]; then 2115 mv ${build_subdir}/tags ${build_subdir}/tags-actual.txt 2116 fi 2117 for aspect in stdout stderr exit tags; do 2118 if [ -f ${subdir}/${aspect}-expected.txt ]; then 2119 engine=compare 2120 msg="${msg}\n$(tmain_${engine} ${subdir} ${build_subdir} ${aspect})" 2121 if [ $? -eq 0 ]; then 2122 rm ${build_subdir}/${aspect}-actual.txt 2123 else 2124 echo "${test_name}/${aspect}-${engine}$(failed_git_marker ${subdir}/${aspect}-expected.txt)" >> ${r_failed} 2125 if [ ${aspect} = stderr ] && 2126 is_crashed ${build_subdir}/${aspect}-actual.txt && 2127 type "gdb" > /dev/null 2>&1; then 2128 print_backtraces "${basedir}/${CTAGS}" \ 2129 ${build_subdir}/core* \ 2130 > ${build_subdir}/gdb-backtrace.txt 2131 fi 2132 fi 2133 elif [ -f ${build_subdir}/${aspect}-actual.txt ]; then 2134 rm ${build_subdir}/${aspect}-actual.txt 2135 fi 2136 done 2137 2138 printf "%b\n" "${msg}" 2139 ) & 2140 done 2141 wait 2142 2143 if [ -f "${r_failed}" ]; then 2144 status_=1 2145 echo 2146 echo Failed tests 2147 line '=' 2148 failed=$(cat ${r_failed}) 2149 for f in ${failed}; do 2150 echo $f | sed -e 's|<G>| (not committed/cached yet)|' 2151 done 2152 echo 2153 2154 if [ "${SHOW_DIFF_OUTPUT}" = yes ]; then 2155 engine=compare 2156 echo Detail "[$engine]" 2157 line '-' 2158 tmain_${engine}_result ${build_topdir} 2159 fi 2160 rm ${r_failed} 2161 fi 2162 2163 return $status_ 2164} 2165 2166action_tmain () 2167{ 2168 local action="$1" 2169 shift 2170 local tmain_dir 2171 local build_dir 2172 2173 while [ $# -gt 0 ]; do 2174 case $1 in 2175 --ctags) 2176 shift 2177 CTAGS="$1" 2178 shift 2179 ;; 2180 --ctags=*) 2181 CTAGS="${1#--ctags=}" 2182 shift 2183 ;; 2184 --colorized-output) 2185 shift 2186 COLORIZED_OUTPUT="$1" 2187 shift 2188 ;; 2189 --colorized-output=*) 2190 COLORIZED_OUTPUT="${1#--colorized-output=}" 2191 shift 2192 ;; 2193 --with-valgrind) 2194 shift 2195 WITH_VALGRIND=yes 2196 ;; 2197 --show-diff-output) 2198 SHOW_DIFF_OUTPUT=yes 2199 shift 2200 ;; 2201 --readtags=*) 2202 READTAGS="${1#--readtags=}" 2203 shift 2204 ;; 2205 --units) 2206 shift 2207 UNITS=$(echo "$1" | tr ',' ' ') 2208 shift 2209 ;; 2210 --units=*) 2211 UNITS=$(echo "${1#--units=}" | tr ',' ' ') 2212 shift 2213 ;; 2214 -*) 2215 ERROR 1 "unknown option \"${1}\" for ${action} action" 2216 ;; 2217 *) 2218 tmain_dir="$1" 2219 shift 2220 build_dir=${1:-${tmain_dir}} 2221 if [ -n "$1" ]; then 2222 shift 2223 fi 2224 break 2225 ;; 2226 esac 2227 done 2228 2229 if [ $# -gt 0 ]; then 2230 ERROR 1 "too many arguments for ${action} action: $*" 2231 elif [ -z "$tmain_dir" ]; then 2232 ERROR 1 "TMAIN_DIR parameter is not given in ${action} action" 2233 fi 2234 2235 if ! [ -d "$tmain_dir" ]; then 2236 ERROR 1 "No such directory(tmain_dir): ${tmain_dir}" 2237 fi 2238 2239 case "${build_dir}" in 2240 /*) ;; 2241 *) build_dir=$(pwd)/${build_dir} ;; 2242 esac 2243 if ! [ -d "$build_dir" ]; then 2244 ERROR 1 "No such directory(build_dir): ${build_dir}" 2245 fi 2246 2247 if ! [ -f "${CTAGS}" ]; then 2248 ERROR 1 "no such file: ${CTAGS}" 2249 elif ! [ -e "${CTAGS}" ]; then 2250 ERROR 1 "${CTAGS} is not an executable file" 2251 fi 2252 2253 if ! ( [ "${COLORIZED_OUTPUT}" = 'yes' ] || [ "${COLORIZED_OUTPUT}" = 'no' ] ); then 2254 ERROR 1 "unexpected option argument for --colorized-output: ${COLORIZED_OUTPUT}" 2255 fi 2256 2257 check_availability awk 2258 check_availability diff 2259 2260 tmain_run ${tmain_dir} ${build_dir} ${UNITS} 2261 return $? 2262} 2263 2264help_tmain () 2265{ 2266 cat <<EOF 2267$0 tmain [OPTIONS] TMAIN-DIR [BUILD-DIR] 2268 2269 Run tests for main part of ctags. 2270 If BUILD-DIR is not given, TMAIN-DIR is reused as BUILD-DIR. 2271 2272 OPTIONS: 2273 2274 --ctags CTAGS: ctags executable file for testing 2275 --colorized-output yes|no: print the result in color. 2276 --with-valgrind: (not implemented) run a test case under valgrind 2277 --show-diff-output: (not implemented)show diff output for failed test cases in the summary. 2278 --units UNITS1[,UNITS2,...]: run only Tmain/UNIT*.d (.d is not needed) 2279EOF 2280} 2281 2282action_clean_tmain() 2283{ 2284 local action="$1" 2285 shift 2286 2287 local tmain_dir=$1 2288 shift 2289 2290 if [ $# -gt 0 ]; then 2291 ERROR 1 "too many arguments for ${action} action: $*" 2292 elif [ -z "$tmain_dir" ]; then 2293 ERROR 1 "TMAIN_DIR parameter is not given in ${action} action" 2294 fi 2295 2296 if ! [ -d "$tmain_dir" ]; then 2297 ERROR 1 "No such directory: ${tmain_dir}" 2298 fi 2299 2300 check_availability find 2301 check_availability rm 2302 2303 local object 2304 local type 2305 for object in stdout stderr exit tags; do 2306 for type in actual diff; do 2307 rm -f $(find "$tmain_dir" -name ${object}-${type}.txt) 2308 rm -f $(find "$tmain_dir" -name gdb-backtrace.txt) 2309 done 2310 done 2311 return 0 2312} 2313 2314help_clean_tmain () 2315{ 2316 cat <<EOF 2317$0 clean_tmain TMAIN-DIR 2318 2319 Clean all files created during tmain testing 2320EOF 2321 2322} 2323 2324help_chop () 2325{ 2326 cat <<EOF 2327$0 chop|slap [OPTIONS] UNITS-DIR 2328 2329 OPTIONS: 2330 2331 --ctags CTAGS: ctags executable file for testing 2332 --languages PARSER1[,PARSER2,...]: run only PARSER* related cases 2333 --quiet: don't print dots as passed test cases. 2334 --with-timeout DURATION: run a test case under timeout 2335 command with SECOND. 2336 0 means no timeout. 2337 default is 1. 2338 --with-valgrind: run a test case under valgrind 2339 If this option given, DURATION is changed to 2340 DURATION := DURATION * ${_VG_TIMEOUT_FACTOR} 2341EOF 2342} 2343 2344action_chop () 2345{ 2346 if [ "$1" = "chop" ]; then 2347 action_fuzz_common chop_lang "$@" 2348 else 2349 action_fuzz_common slap_lang "$@" 2350 fi 2351} 2352 2353chop_lang() 2354{ 2355 chop_lang_common "tail" "$@" 2356} 2357 2358slap_lang() 2359{ 2360 chop_lang_common "head" "$@" 2361} 2362 2363chop_lang_common () 2364{ 2365 local endpoint=$1 2366 shift 1 2367 2368 local lang="$1" 2369 local dir="$2" 2370 shift 2 2371 local f 2372 local r 2373 2374 printf '%-60s\n' "Fuzzing by truncating input from ${endpoint} (${lang})" 2375 line '-' 2376 2377 r=0 2378 for f in $(find "${dir}" -type f -name 'input.*'); do 2379 if ! chop_lang_file "$1" "${lang}" "${f}"; then 2380 r=1 2381 break 2382 fi 2383 done 2384 echo 2385 return $r 2386} 2387 2388chop_lang_file () 2389{ 2390 local endpoint=$1 2391 shift 1 2392 2393 local lang="$1" 2394 local input="$2" 2395 shift 2 2396 2397 local r 2398 local cmdline 2399 local cmdline_for_shirking 2400 local len=$(${_FSIZE} "${input}") 2401 2402 local guessed_lang 2403 guessed_lang=$( ${_CMDLINE_FOR_SHRINKING} --print-language "${input}" 2>/dev/null | sed -n 's/^.*: //p') 2404 if [ "${lang}" != "${guessed_lang}" ]; then 2405 return 0 2406 fi 2407 2408 i=0 2409 echo "Testing cases derived from: ${input}" 2410 line '.' --no-newline 2411 2412 r=0 2413 while [ "$i" -lt "$len" ]; do 2414 if chop_lang_file_chopspec "${endpoint}" "${input}" "${len}" "$i" "${lang}"; then 2415 i=$(( i + 1 )) 2416 else 2417 r=1 2418 break 2419 fi 2420 done 2421 echo 2422 return $r 2423} 2424 2425chop() 2426{ 2427 local endpoint=$1 2428 local input=$2 2429 local pos=$3 2430 local len=$4 2431 2432 if [ "${endpoint}" = "tail" ]; then 2433 dd if=$input bs=1 count=$pos 2434 else 2435 dd if=$input bs=1 count=$((len - pos)) skip=$pos 2436 fi 2437} 2438 2439chop_lang_file_chopspec() 2440{ 2441 local endpoint=$1 2442 shift 1 2443 2444 local input="$1" 2445 local len="$2" 2446 local pos="$3" 2447 local lang="$4" 2448 shift 4 2449 2450 local dir="${input%/*}" 2451 local ochopped=$(printf "%s/CHOP-INPUT-%s.tmp" "${dir}" "$pos") 2452 local ocmdline=$(printf "%s/CHOP-CMDLINE-%s.tmp" "${dir}" "$pos") 2453 local ovalgrind=$(printf "%s/CHOP-VALGRIND-%s.tmp" "${dir}" "$pos") 2454 local oshrink=$(printf "%s/CHOP-SHRINK-%s.tmp" "${dir}" "$pos") 2455 2456 local cmdline 2457 local cmdline_for_shirking 2458 local progress_offset 2459 local r 2460 2461 rm -f "${ocmdline}" "${ovalgrind}" "${ochopped}" "${oshrink}" 2462 2463 if [ "${WITH_VALGRIND}" = 'yes' ]; then 2464 cmdline=$( printf "${_CMDLINE} --language-force=${lang} ${ochopped}" "${ovalgrind}" ) 2465 else 2466 cmdline="${_CMDLINE} --language-force=${lang} ${ochopped}" 2467 fi 2468 cmdline_for_shirking="${_CMDLINE_FOR_SHRINKING} --language-force=${lang} %s" 2469 2470 chop "${endpoint}" "${input}" "${pos}" "${len}" > "$ochopped" 2> /dev/null 2471 2472 progress_offset=$(( pos % _NOISE_REPORT_MAX_COLUMN )) 2473 if [ "${progress_offset}" -eq 0 ]; then 2474 [ $pos -gt 0 ] && printf " %d/%d" "$pos" "${len}" 2475 echo 2476 fi 2477 2478 echo "${cmdline}" > "${ocmdline}" 2479 ( exec 2>&-; ${cmdline} 2> /dev/null > /dev/null ) 2480 r=$? 2481 case $r in 2482 0) 2483 printf 'o' 2484 noise_report_line "${pos}" "${len}" "${r}" "" 2485 rm "${ochopped}" 2486 rm -f "${ovalgrind}" "${ocmdline}" 2487 ;; 2488 ${_TIMEOUT_EXIT}) 2489 printf "T" 2490 noise_report_line "${pos}" "${len}" "${r}" "" 2491 printf '\n%-20s\n' "[timeout $lang]" "$ochopped" 2492 [ "${RUN_SHRINK}" = 'yes' ] && fuzz_shrink "${cmdline_for_shirking}" "${ochopped}" "${oshrink}" "${lang}" 2493 ;; 2494 ${_VALGRIND_EXIT}) 2495 printf "V" 2496 noise_report_line "${pos}" "${len}" "${r}" "" 2497 printf '\n%-20s %s\n' "[valgrind-error $lang]" "$ochopped" 2498 ;; 2499 *) 2500 printf "!" 2501 noise_report_line "${pos}" "${len}" "${r}" "" 2502 printf '\n%-20s %s\n' "[unexpected-status($r) $lang]" "$ochopped" 2503 [ "${RUN_SHRINK}" = 'yes' ] && fuzz_shrink "${cmdline_for_shirking}" "${ochopped}" "${oshrink}" "${lang}" 2504 ;; 2505 esac 2506 return $r 2507} 2508 2509help_validate_input () 2510{ 2511 cat <<EOF 2512$0 validate-input [OPTIONS] UNITS-DIR VALIDATORS-DIR 2513 2514 Validate the input files (only for the test cases specifying validators.) 2515 2516 OPTIONS: 2517 --validators=validator[,...]: Validate test cases specifying 2518 given validators. 2519 --colorized-output: yes|no: print the result in color. 2520EOF 2521} 2522 2523has_validator_acceptable_name () 2524{ 2525 local validator=$1 2526 if [ -z "${validator}" ]; then 2527 return 1 2528 fi 2529 2530 echo "${validator}" | grep -q "^[-a-zA-Z+#0-9]\+$" 2531} 2532 2533is_validator_runnable () 2534{ 2535 local v=$1 2536 local d=$2 2537 shift 2 2538 2539 "$d/"validator-"$v" is_runnable 2540} 2541 2542update_validator_list () 2543{ 2544 local type=$1 2545 local v=$2 2546 2547 case $type in 2548 runnable) 2549 if ! member_p "$v" ${_RUNNABLE_VALIDATORS}; then 2550 _RUNNABLE_VALIDATORS="${_RUNNABLE_VALIDATORS} $v" 2551 fi 2552 ;; 2553 unavailable) 2554 if ! member_p "$v" ${_UNAVAILABLE_VALIDATORS}; then 2555 _UNAVAILABLE_VALIDATORS="${_UNAVAILABLE_VALIDATORS} $v" 2556 fi 2557 ;; 2558 *) 2559 ERROR 1 "INTERNAL ERROR: wrong validator list type: ${type}" 2560 ;; 2561 esac 2562} 2563 2564# 2565# Whether a validator can be run or not. 2566# 2567# This runs "is_runnable" subcommand of the validator. 2568# 2569validate_validator () 2570{ 2571 local v=$1 2572 local validators_dir=$2 2573 local make_error=$3 2574 2575 if member_p "$v" ${_RUNNABLE_VALIDATORS}; then 2576 return 0 2577 elif member_p "$v" ${_UNAVAILABLE_VALIDATORS}; then 2578 return 1 2579 fi 2580 2581 if ! has_validator_acceptable_name "$v"; then 2582 if [ "${make_error}" = "error" ]; then 2583 ERROR 1 "Unacceptable validator name: $v" 2584 else 2585 update_validator_list unavailable "$v" 2586 return 1 2587 fi 2588 elif ! [ -f "${validators_dir}/validator-$v" ]; then 2589 if [ "${make_error}" = "error" ]; then 2590 ERROR 1 "No such validator: $v (${validators_dir}/validator-$v)" 2591 else 2592 update_validator_list unavailable "$v" 2593 return 1 2594 fi 2595 elif ! [ -x "${validators_dir}/validator-$v" ]; then 2596 if [ "$make_error" = "error" ]; then 2597 ERROR 1 "Not executable: $v (${validators_dir}/validator-$v)" 2598 else 2599 update_validator_list unavailable "$v" 2600 return 1 2601 fi 2602 elif ! is_validator_runnable "$v" "${validators_dir}"; then 2603 if [ "${make_error}" = "error" ]; then 2604 ERROR 1 "$v (${validators_dir}/validator-$v) is not ready to run" 2605 else 2606 update_validator_list unavailable "$v" 2607 return 1 2608 fi 2609 fi 2610 2611 update_validator_list runnable "$v" 2612 return 0 2613} 2614 2615# 2616# Choose a validator suitable for the current context. 2617# 2618# Return value 2619# 0: The caller should run echo'ed validator. 2620# 2621# 1: The caller should skip the input. It means 2622# - the suitable validator is not listed in VALIDATORS, or 2623# - no expected.tags and no validator file exist. 2624# The caller doesn't have to update any validation counters. 2625# 2: The caller should skip the input and update the unavailable 2626# counter. 2627# 2628resolve_validator () 2629{ 2630 local validator_file=$1 2631 local default_validator=$2 2632 local has_expected_tags=$3 2633 local validators_dir=$4 2634 2635 shift 4 2636 2637 local candidate_validator 2638 local local_validator 2639 2640 if [ -r "$validator_file" ]; then 2641 local_validator=$(cat "${validator_file}" | grep -v '#') 2642 if [ -z "${local_validator}" ]; then 2643 ERROR 1 "Empty validator specfile: ${local_validator}" 2644 else 2645 candidate_validator=${local_validator} 2646 fi 2647 elif [ "$has_expected_tags" = no ]; then 2648 return 1 2649 else 2650 candidate_validator=${default_validator} 2651 fi 2652 2653 if [ -z "${candidate_validator}" ]; then 2654 return 2 2655 elif [ -z "${VALIDATORS}" ] || member_p "${candidate_validator}" ${VALIDATORS}; then 2656 echo "${candidate_validator}" 2657 if validate_validator "${candidate_validator}" "${validators_dir}" noerror; then 2658 return 0 2659 else 2660 return 2 2661 fi 2662 else 2663 return 1 2664 fi 2665} 2666 2667# 2668# Report the result of validation for INPUT with decoration 2669# The validation result counters are updated here. 2670# 2671validate_report_one () 2672{ 2673 local input=$1 2674 local validator=$2 2675 local status=$3 2676 shift 3 2677 2678 if [ "${validator}" = "${_NOOP_VALIDATOR}" ]; then 2679 return 2680 fi 2681 2682 local i=$(basename $input) 2683 local d=$(basename $(dirname $input)) 2684 2685 printf '%-65s' "$d/$i with ${validator}" 2686 case "${status}" in 2687 valid) 2688 if [ "${validator}" = "${_KNOWN_INVALIDATION_VALIDATOR}" ]; then 2689 printf '%b\n' $(decorate yellow "known-invalidation") 2690 V_SKIP_KNOWN_INVALIDATION=$(( V_SKIP_KNOWN_INVALIDATION + 1 )) 2691 else 2692 printf '%b\n' $(decorate green "valid") 2693 V_VALID=$(( V_VALID + 1)) 2694 fi 2695 ;; 2696 invalid) 2697 printf '%b\n' $(decorate red "invalid") 2698 V_INVALID=$(( V_INVALID + 1)) 2699 ;; 2700 unavailable) 2701 printf '%b\n' $(decorate yellow "unavailable") 2702 V_SKIP_VALIDATOR_UNAVAILABLE=$(( V_SKIP_VALIDATOR_UNAVAILABLE + 1)) 2703 ;; 2704 *) 2705 ERROR 1 "INTERNAL ERROR: wrong validation status: ${status}" 2706 ;; 2707 esac 2708} 2709 2710# 2711# Run a validator for the given input 2712# 2713# This runs "validate" subcommand of the validator. 2714# 2715validate_file () 2716{ 2717 local input=$1 2718 local validator=$2 2719 local validators_dir=$3 2720 shift 3 2721 2722 ${validators_dir}/validator-${validator} validate $input 2723 return $? 2724} 2725 2726# 2727# Validate input files under *.[dbtiv]. 2728# 2729validate_dir () 2730{ 2731 local base_dir=$1 2732 local default_validator=$2 2733 local validators_dir=$3 2734 shift 3 2735 2736 local f 2737 local t 2738 2739 local v0 s0 inputs0 2740 local v s inputs 2741 2742 # 2743 # No expected.tags* implies the input is invalid. 2744 # We don't have to run any validator as far as no 2745 # ./validator explicitly is given. 2746 # 2747 local has_expected_tags=no 2748 for f in "${base_dir}"/expected.tags*; do 2749 [ -r "$f" ] || continue 2750 failure_in_globing "$f" && continue 2751 has_expected_tags=yes 2752 break 2753 done 2754 2755 # A validator specified in ./validator is used for validating ./input.foo. 2756 # It will be used for validating ./input[-_]*.foo, too if ./validator[-_]*. 2757 # doesn't exit. 2758 inputs0=$(for f in "${base_dir}"/input.*; do 2759 [ -r "$f" ] || continue 2760 failure_in_globing "$f" && continue 2761 echo $f 2762 done | sort) 2763 v0=$(resolve_validator "${base_dir}"/validator \ 2764 "${default_validator}" \ 2765 "${has_expected_tags}" \ 2766 "${validators_dir}") 2767 s0=$? 2768 2769 case "$s0" in 2770 0) 2771 update_validator_list runnable "$v0" 2772 for f in $inputs0; do 2773 if validate_file "$f" "$v0" "${validators_dir}"; then 2774 validate_report_one "$f" "$v0" valid 2775 else 2776 validate_report_one "$f" "$v0" invalid 2777 fi 2778 done 2779 default_validator=$v0 2780 ;; 2781 1) 2782 # no action needed 2783 ;; 2784 2) 2785 update_validator_list unavailable "$v0" 2786 for f in $inputs0; do 2787 validate_report_one "$f" "$v0" unavailable 2788 done 2789 default_validator= 2790 ;; 2791 esac 2792 2793 inputs=$(for f in "${base_dir}"/input[-_][0-9].* \ 2794 "${base_dir}"/input[-_][0-9][-_]*.*; do 2795 [ -r "$f" ] || continue 2796 failure_in_globing "$f" && continue 2797 echo $f 2798 done | sort) 2799 2800 for f in $inputs; do 2801 t=$(basename $f) 2802 t=${t#input}; t=${t%.*} 2803 v=$(resolve_validator "${base_dir}"/validator"$t" \ 2804 "$default_validator" \ 2805 "$has_expected_tags" \ 2806 "$validators_dir") 2807 s=$? 2808 case "$s" in 2809 0) 2810 update_validator_list runnable "$v0" 2811 if validate_file "$f" "$v" "${validators_dir}"; then 2812 validate_report_one "$f" "$v" valid 2813 else 2814 validate_report_one "$f" "$v" invalid 2815 fi 2816 ;; 2817 1) 2818 # no action needed 2819 ;; 2820 2) 2821 update_validator_list unavailable "$v0" 2822 validate_report_one "$f" "$v" unavailable 2823 ;; 2824 esac 2825 done 2826} 2827 2828# 2829# Validate input files under *.r. 2830# 2831validate_category () 2832{ 2833 local category=$1 2834 local base_dir=$2 2835 local validators_dir=$3 2836 shift 3 2837 2838 local d 2839 local default_validator=${_NOOP_VALIDATOR} 2840 2841 if [ -r "${base_dir}/validator" ]; then 2842 default_validator=$(cat "${base_dir}/validator" | grep -v '#') 2843 if [ -z "${default_validator}" ]; then 2844 ERROR 1 "Empty validator specfile (in ${base_dir}/validator)" 2845 fi 2846 2847 validate_validator "${default_validator}" "${validators_dir}" noerror 2848 fi 2849 2850 for d in "${base_dir}"/*.[dbtiv]; do 2851 [ -d "$d" ] || continue 2852 validate_dir "$d" "${default_validator}" "${validators_dir}" 2853 done 2854} 2855 2856# 2857# Report the summary of validations 2858# 2859validate_summary () 2860{ 2861 echo 2862 echo "Summary" 2863 line 2864 2865 printf ' %-40s' "#valid:" 2866 printf '%b\n' $(decorate green "${V_VALID}") 2867 2868 printf ' %-40s' "#invalid:" 2869 if [ "${V_INVALID}" = 0 ]; then 2870 echo "${V_INVALID}" 2871 else 2872 printf '%b\n' $(decorate red "${V_INVALID}") 2873 fi 2874 2875 printf ' %-40s' "#skipped (known invalidation)" 2876 if [ "${V_SKIP_KNOWN_INVALIDATION}" = 0 ]; then 2877 echo 0 2878 else 2879 printf '%b\n' $(decorate yellow "${V_SKIP_KNOWN_INVALIDATION}") 2880 fi 2881 2882 printf ' %-40s' "#skipped (validator unavailable)" 2883 if [ "${V_SKIP_VALIDATOR_UNAVAILABLE}" = 0 ]; then 2884 echo 0 2885 else 2886 local u 2887 printf '%b\n' $(decorate yellow "${V_SKIP_VALIDATOR_UNAVAILABLE}") 2888 2889 echo 2890 echo "Unavailable validators" 2891 line 2892 for u in ${_UNAVAILABLE_VALIDATORS}; do 2893 echo " $u" 2894 done 2895 fi 2896 2897 if [ "${V_INVALID}" = 0 ]; then 2898 return 0 2899 else 2900 return "${_VALIDATION_EXIT_INVALID}" 2901 fi 2902} 2903 2904action_validate_input () 2905{ 2906 local action=$1 2907 shift 2908 2909 local units_dir 2910 local validators_dir 2911 local validators 2912 2913 local v 2914 local d 2915 2916 while [ $# -gt 0 ]; do 2917 case $1 in 2918 --validators) 2919 shift 2920 validators=$1 2921 shift 2922 ;; 2923 --validators=*) 2924 validators=${1#--validators=} 2925 shift 2926 ;; 2927 --colorized-output) 2928 shift 2929 COLORIZED_OUTPUT=$1 2930 shift 2931 ;; 2932 --colorized-output=*) 2933 COLORIZED_OUTPUT=${1#--colorized-output=} 2934 shift 2935 ;; 2936 -*) 2937 ERROR 1 "unknown option \"${1}\" for ${action} action" 2938 ;; 2939 *) 2940 units_dir=$1 2941 shift 2942 validators_dir=$1 2943 shift 2944 break; 2945 ;; 2946 esac 2947 done 2948 2949 if [ $# -gt 0 ]; then 2950 ERROR 1 "too many arguments for ${action} action: $*" 2951 elif [ -z "${units_dir}" ]; then 2952 ERROR 1 "UNITS_DIR parameter is not given in ${action} action" 2953 elif [ -z "${validators_dir}" ]; then 2954 ERROR 1 "VALIDATORS_DIR parameter is not given in ${action} action" 2955 fi 2956 2957 if ! [ -d "${units_dir}" ]; then 2958 ERROR 1 "No such directory: ${units_dir}" 2959 elif ! [ -d "${validators_dir}" ]; then 2960 ERROR 1 "No such directory: ${units_dir}" 2961 fi 2962 2963 if ! ( [ "${COLORIZED_OUTPUT}" = 'yes' ] || [ "${COLORIZED_OUTPUT}" = 'no' ] ); then 2964 ERROR 1 "unexpected option argument for --colorized-output: ${COLORIZED_OUTPUT}" 2965 fi 2966 2967 if [ -n "${validators}" ]; then 2968 VALIDATORS=$(echo "${validators}" | tr ',' ' ') 2969 for v in ${VALIDATORS}; do 2970 validate_validator "$v" "${validators_dir}" error 2971 done 2972 fi 2973 2974 for d in ${units_dir}/*.r; do 2975 [ -d "$d" ] || continue 2976 category=${d##*/} 2977 echo 2978 echo "Category: ${category}" 2979 line 2980 validate_category "${category}" "$d" "${validators_dir}" 2981 done 2982 2983 echo 2984 echo "Category: ${_DEFAULT_CATEGORY}" 2985 line 2986 for d in ${units_dir}/*.[dbtiv]; do 2987 [ -d "$d" ] || continue 2988 validate_dir "$d" "${_NOOP_VALIDATOR}" "$validators_dir" 2989 done 2990 2991 validate_summary 2992 return $? 2993} 2994 2995# * Avoid issues between sed and the locale settings by overriding it using 2996# LC_ALL, which takes precedence over all other locale configurations: 2997# https://www.gnu.org/software/gettext/manual/html_node/Locale-Environment-Variables.html 2998# 2999# * Avoid unexpected pathname conversion on MSYS2. 3000# https://github.com/msys2/msys2/wiki/Porting#filesystem-namespaces 3001prepare_environment () 3002{ 3003 _PREPERE_ENV=$(cat <<'EOF' 3004LC_ALL="C"; export LC_ALL 3005MSYS2_ARG_CONV_EXCL='--regex-;--_scopesep' export MSYS2_ARG_CONV_EXCL 3006EOF 3007) 3008 eval ${_PREPERE_ENV} 3009} 3010 3011main () 3012{ 3013 if [ $# = 0 ]; then 3014 action_help 1>&2 3015 exit 1 3016 fi 3017 3018 case $1 in 3019 help|-h|--help) 3020 action_help 3021 return 0 3022 ;; 3023 run) 3024 action_run "$@" 3025 return $? 3026 ;; 3027 clean) 3028 action_clean "$@" 3029 return $? 3030 ;; 3031 fuzz) 3032 action_fuzz "$@" 3033 return $? 3034 ;; 3035 noise) 3036 action_noise "$@" 3037 return $? 3038 ;; 3039 tmain) 3040 action_tmain "$@" 3041 return $? 3042 ;; 3043 clean-tmain) 3044 action_clean_tmain "$@" 3045 return $? 3046 ;; 3047 shrink) 3048 action_shrink "$@" 3049 return $? 3050 ;; 3051 chop) 3052 action_chop "$@" 3053 return $? 3054 ;; 3055 slap) 3056 action_chop "$@" 3057 return $? 3058 ;; 3059 validate-input) 3060 action_validate_input "$@" 3061 return $? 3062 ;; 3063 *) 3064 ERROR 1 "unknown action: $1" 3065 ;; 3066 esac 3067} 3068 3069prepare_environment 3070main "$@" 3071exit $? 3072