#!/bin/sh # # units - Units test harness for ctags # # Copyright (C) 2014 Masatake YAMATO # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # if test -n "${ZSH_VERSION+set}"; then set -o SH_WORD_SPLIT set +o NOMATCH fi # # Global Parameters # SHELL=/bin/sh CTAGS=./ctags READTAGS=./readtags WITH_TIMEOUT= WITH_VALGRIND= COLORIZED_OUTPUT=yes [ -f /dev/stdout ] && COLORIZED_OUTPUT=no CATEGORIES= UNITS= LANGUAGES= PRETENSE_OPTS= RUN_SHRINK= QUIET= SHOW_DIFF_OUTPUT= VALIDATORS= # # Internal variables and constants # _CMDLINE= _CMDLINE_FOR_SHRINKING= _PREPERE_ENV= _FEATURE_LIST= readonly _DEFAULT_CATEGORY=ROOT readonly _TIMEOUT_EXIT=124 readonly _VG_TIMEOUT_FACTOR=10 readonly _VALGRIND_EXIT=58 readonly _BASH_INTERRUPT_EXIT=59 readonly _LINE_SPLITTER=$(if type dos2unix > /dev/null 2>&1; then echo "dos2unix"; else echo "cat"; fi) readonly _STDERR_OUTPUT_NAME="STDERR.tmp" readonly _DIFF_OUTPUT_NAME="DIFF.tmp" readonly _NOISE_REPORT_MAX_COLUMN=50 readonly _VALIDATION_EXIT_INVALID=2 readonly _NOOP_VALIDATOR="NONE" readonly _KNOWN_INVALIDATION_VALIDATOR="KNOWN-INVALIDATION" if stat --help >/dev/null 2>&1; then readonly _FSIZE="stat -c %s" # GNU coreutils else readonly _FSIZE="stat -f %z" # BSD based OSes including macOS fi _RUNNABLE_VALIDATORS= _UNAVAILABLE_VALIDATORS= # # Result files and results # readonly R_PASSED="_PASSED.result" readonly R_FIXED="_FIXED.result" readonly R_FAILED_BY_STATUS="_FAILED_BY_STATUS.result" readonly R_FAILED_BY_DIFF="_FAILED_BY_DIFF.result" readonly R_SKIPPED_BY_FEATURES="_SKIPPED_BY_FEATURES.result" readonly R_SKIPPED_BY_LANGUAGES="_SKIPPED_BY_LANGUAGES.result" readonly R_SKIPPED_BY_ILOOP="_SKIPPED_BY_ILOOP.result" readonly R_KNOWN_BUGS="_KNOWN_BUGS.result" readonly R_FAILED_BY_TIMEED_OUT="_FAILED_BY_TIMEED_OUT.result" readonly R_BROKEN_ARGS_CTAGS="_BROKEN_ARGS_CTAGS.result" readonly R_VALGRIND="_VALGRIND.result" L_PASSED= L_FIXED= L_FAILED_BY_STATUS= L_FAILED_BY_DIFF= L_SKIPPED_BY_FEATURES= L_SKIPPED_BY_LANGUAGES= L_SKIPPED_BY_ILOOP= L_KNOWN_BUGS= L_FAILED_BY_TIMEED_OUT= L_BROKEN_ARGS_CTAGS= L_VALGRIND= V_VALID=0 V_INVALID=0 V_SKIP_VALIDATOR_UNAVAILABLE=0 V_SKIP_KNOWN_INVALIDATION=0 # # TODO # # * write new class 'r' (category directory) to units.rst. # * write new class 'v' (skip the checking by valgrind) to units.rst. # action_help () { cat <&2 exit $status_ } line() { local c=${1:--} local no_newline="${2}" local i=0 while [ $i -lt 60 ]; do printf "%c" "$c" i=$(( i + 1 )) done if ! [ "${no_newline}" = "--no-newline" ]; then echo fi } count_list () { echo $# } member_p () { local elt="$1" shift local x for x in "$@"; do if [ "$x" = "$elt" ]; then return 0 fi done return 1 } clean_tcase () { local d="$1" local bundles="$2" local b if [ -d "$d" ]; then if [ -f "${bundles}" ]; then while read b; do rm -rf "${b}" done < "${bundles}" rm ${bundles} fi rm -f "$d"/*.tmp "$d"/*.TMP fi } check_availability() { local cmd="$1" shift type "${cmd}" > /dev/null 2>&1 || ERROR 1 "${cmd} command is not available" } check_units () { local name="$1" local category="$2" shift 2 local u for u in "$@"; do if echo "${u}" | grep -q /; then if [ "${u%/*}" = "${category}" ] && [ "${u#*/}" = "${name}" ]; then return 0 fi elif [ "${u}" = "${name}" ]; then return 0 fi done return 1 } init_features() { _FEATURE_LIST=$( ${CTAGS} --quiet --options=NONE \ --list-features --with-list-header=no \ 2> /dev/null \ | "${_LINE_SPLITTER}" \ | cut -f 1 -d ' ') } check_features() { local flag="$1" local ffile local feature if [ "${flag}" = "-f" ]; then ffile="$2" elif [ "${flag}" = "-e" ]; then feature="$2" fi shift 2 local f local found local found_unexpectedly local expected; for expected in $([ -f "$ffile" ] && cat "$ffile") ${feature}; do found=no found_unexpectedly=no for f in ${_FEATURE_LIST} ; do [ "$expected" = "$f" ] && found=yes [ "$expected" = '!'"$f" ] && found_unexpectedly=yes done if [ "${found_unexpectedly}" = yes ]; then echo "$expected" return 1 elif ! [ "$found" = yes ]; then echo "$expected" return 1 fi done return 0 } check_languages() { local lfile="$1" shift local l local found local expected; # # TODO: consider the value of LANGUAGES # while read expected; do found=no for l in $( ${_CMDLINE} --list-languages 2>/dev/null | "${_LINE_SPLITTER}" |sed -e 's/ //' ); do [ "$expected" = "$l" ] && found=yes done if ! [ "$found" = yes ]; then echo "$expected" return 1 fi done < "$lfile" return 0 } decorate () { local decorator="$1" local msg="$2" case "$decorator" in red) decorator=31 ;; green) decorator=32 ;; yellow) decorator=33 ;; *) ERROR 1 "INTERNAL ERROR: wrong run_result function: $f" esac if [ "${COLORIZED_OUTPUT}" = 'yes' ]; then printf '%b\n' "\033[${decorator}m${msg}\033[39m" else printf '%b\n' "${msg}" fi } run_result () { local result_type="$1" local msg="$2" local output="$3" shift 3 local f="run_result_${result_type}" local tmp type "$f" > /dev/null 2>&1 || ERROR 1 \ "${msg}INTERNAL ERROR: wrong run_result function: $f" "$f" "${msg}" "$@" tmp="${COLORIZED_OUTPUT}" COLORIZED_OUTPUT=no "$f" "${msg}" "$@" > "${output}" COLORIZED_OUTPUT="${tmp}" } run_result_skip () { local msg="$1" shift 1 if [ -n "$1" ]; then printf '%b%b\n' "${msg}" $(decorate yellow "skipped")" ($1)" else printf '%b%b\n' "${msg}" $(decorate yellow "skipped") fi } run_result_error () { local msg="$1" shift 1 if [ ! -n "$1" ]; then printf '%b%b\n' "${msg}" $(decorate red "failed") else printf '%b%b\n' "${msg}" $(decorate red "failed")" ($1)" fi } run_result_ok () { local msg="$1" shift 1 if [ ! -n "$1" ]; then printf '%b%b\n' "${msg}" $(decorate green "passed") else printf '%b%b\n' "${msg}" $(decorate green "passed")" ($1)" fi } run_result_known_error () { local msg="$1" shift 1 printf '%b%b\n' "${msg}" $(decorate yellow "failed")" (KNOWN bug)" } run_shrink () { local cmdline_template="$1" local input="$2" local output="$3" local lang="$4" shift 4 echo "Shrinking ${input} as ${lang}" shrink_main "${cmdline_template}" "${input}" "${output}" 1 yes } # filters out the directory prefix in a ctags input ctags_basename_filter_regex='s%\(^[^ ]\{1,\} \)\(/\{0,1\}\([^/ ]\{1,\}/\)*\)%\1%' ctags_basename_filter() { sed "${ctags_basename_filter_regex}" } # About "input" in the expression, see units.py. etags_basename_filter_regex='s%.*\/\(input[-._][[:print:]]\{1,\}\),\([0-9]\{1,\}$\)%\1,\2%' etags_basename_filter() { sed "${etags_basename_filter_regex}" } xref_basename_filter_regex='s%\(.*[[:digit:]]\{1,\} \)\([^ ]\{1,\}[^ ]\{1,\}\)/\([^ ].\{1,\}.\{1,\}$\)%\1\3%' xref_basename_filter() { sed "${xref_basename_filter_regex}" } json_basename_filter_regex='s%\("path": \)"[^"]\{1,\}/\([^/"]\{1,\}\)"%\1"\2"%' json_basename_filter() { sed "${json_basename_filter_regex}" } run_record_cmdline () { local ffilter="$1" local ocmdline="$2" printf "%s\n%s \\\\\n| %s \\\\\n| %s\n" \ "${_PREPERE_ENV}" \ "${_CMDLINE}" \ "sed '${tags_basename_filter_regex}'" \ "${ffilter}" \ > "${ocmdline}" } # # All files and directories other than input.*, expected.tags, # args.ctags, README*, features, languages, and filters under srcdir # are copied to builddir. These copied files are called bundles. # prepare_bundles () { local from=$1 local to=$2 local obundles=$3 local src local dist for src in ${from}/*; do if [ "${from}"'/*' = "${src}" ]; then break fi case "${src##*/}" in input.*) continue ;; expected.tags*) continue ;; README*) continue ;; features|languages|filters) continue ;; args.ctags) continue ;; *) dist="${to}/${src##*/}" if ! cp -a "${src}" "${to}"; then ERROR 1 "failure in copying bundle file \"${src}\" to \"${to}\"" else echo ${dist} >> ${obundles} fi ;; esac done } direq () { [ "$(cd ${1} && pwd)" = "$(cd ${2} && pwd)" ] return $? } anon_normalize () { local ctags=$1 local input_actual if [ -n "$2" ]; then input_actual=$2 shift 2 # TODO: "Units" should not be hardcoded. local input_expected="./Units${input_actual#*/Units}" local actual=$(${CTAGS} --quiet --options=NONE --_anonhash="${input_actual}") local expected=$(${CTAGS} --quiet --options=NONE --_anonhash="${input_expected}") sed -e s/${actual}/${expected}/g | anon_normalize "${ctags}" "$@" else cat fi } run_tcase () { local input="$1" local t="$2" local name="$3" local class="$4" local category="$5" local build_t="$6" shift 6 # The rest of arguments ($@) are extra inputs # I violate the naming convention of build_* to reduce typing local o=${build_t} local fargs="$t/args.ctags" local ffeatures="$t/features" local flanguages="$t/languages" local ffilter="$t/filter" # # tags-e if for etags output(-e). TAGS is good # suffix but foo.tags and foo.TAGS may be the same on Windows. # tags-x is for cross reference output(-x). # tags-json is for json output. # # fexpected must be set even if none of # expected.{tags,tags-e,tags-x,tags-json} exits. # local fexpected="$t/expected.tags" local output_type=ctags local output_label= local output_tflag= local output_feature= local output_lang_extras= if [ -f "$t/expected.tags" ]; then : elif [ -f "$t/expected.tags-e" ]; then fexpected=$t/expected.tags-e output_type=etags output_label=/${output_type} output_tflag="-e --tag-relative=no" elif [ -f "$t/expected.tags-x" ]; then fexpected=$t/expected.tags-x output_type=xref output_label=/${output_type} output_tflag=-x elif [ -f "$t/expected.tags-json" ]; then fexpected=$t/expected.tags-json output_type=json output_label=/${output_type} output_tflag="--output-format=json" output_feature=json fi if [ $# -gt 0 ]; then output_lang_extras=" (multi inputs)" fi [ -x "$ffilter" ] || ffilter=cat # # All generated file must have suffix ".tmp". # local ostderr="$o/${_STDERR_OUTPUT_NAME}" local orawout="$o/RAWOUT.tmp" local ofiltered="$o/FILTERED.tmp" local odiff="$o/${_DIFF_OUTPUT_NAME}" local ocmdline="$o/CMDLINE.tmp" local ovalgrind="$o/VALGRIND.tmp" local oresult="$o/RESULT.tmp" local oshrink_template="$o/SHRINK-%s.tmp" local obundles="$o/BUNDLES" local oshrink local guessed_lang local guessed_lang_no_slash local cmdline_template local timeout_value local tmp local msg local broke_args_ctags # # Filtered by UNIT # if [ -n "${UNITS}" ]; then check_units "${name}" "${category}" ${UNITS} || return 1 fi # # Build _CMDLINE # _CMDLINE="${CTAGS} --verbose --options=NONE --fields=-T $PRETENSE_OPTS --optlib-dir=+$t/optlib -o -" [ -f "${fargs}" ] && _CMDLINE="${_CMDLINE} --options=${fargs}" if [ -f "${fargs}" ] && ! ${_CMDLINE} --_force-quit=0 > /dev/null 2>&1; then broke_args_ctags=1 fi # # Filtered by LANGUAGES # guessed_lang=$( ${_CMDLINE} --print-language "$input" 2>/dev/null | sed -n 's/^.*: //p') if [ -n "${LANGUAGES}" ]; then member_p "${guessed_lang}" ${LANGUAGES} || return 1 fi guessed_lang_no_slash=$(echo "${guessed_lang}" | tr '/' '-') oshrink=$(printf "${oshrink_template}" "${guessed_lang_no_slash}") clean_tcase "${o}" "${obundles}" mkdir -p "${o}" if ! direq "${o}" "${t}"; then prepare_bundles ${t} ${o} "${obundles}" fi msg=$(printf '%-60s' "Testing ${name} as ${guessed_lang}${output_lang_extras}${output_label}") if tmp=$( ( [ -n "${output_feature}" ] && ! check_features -e "${output_feature}" ) || ( [ -f "${ffeatures}" ] && ! check_features -f "${ffeatures}" ) ); then echo "${category}/${name}" >> ${R_SKIPPED_BY_FEATURES} case "${tmp}" in !*) run_result skip "${msg}" "${oresult}" "unwanted feature \"${tmp#?}\" is available";; *) run_result skip "${msg}" "${oresult}" "required feature \"${tmp}\" is not available";; esac return 1 elif [ -f "${flanguages}" ] && ! tmp=$(check_languages "${flanguages}"); then echo "${category}/${name}" >> ${R_SKIPPED_BY_LANGUAGES} run_result skip "${msg}" "${oresult}" "required language parser \"$tmp\" is not available" return 1 elif [ "$WITH_TIMEOUT" = 0 ] && [ "${class}" = 'i' ]; then echo "${category}/${name}" >> ${R_SKIPPED_BY_ILOOP} run_result skip "${msg}" "${oresult}" "may cause an infinite loop" return 1 elif [ "$broke_args_ctags" = 1 ]; then run_result error "${msg}" '/dev/null' "broken args.ctags?" echo "${category}/${name}/" >> ${R_BROKEN_ARGS_CTAGS} return 1 fi cmdline_template="${_CMDLINE} --language-force=${guessed_lang} %s > /dev/null 2>&1" _CMDLINE="${_CMDLINE} ${output_tflag} ${input} $@" timeout_value=$WITH_TIMEOUT if [ "$WITH_VALGRIND" = yes ]; then _CMDLINE="valgrind --leak-check=full --error-exitcode=${_VALGRIND_EXIT} --log-file=${ovalgrind} ${_CMDLINE}" timeout_value=$(( timeout_value * ${_VG_TIMEOUT_FACTOR} )) fi if ! [ "$timeout_value" = 0 ]; then _CMDLINE="timeout $timeout_value ${_CMDLINE}" fi { ( # # When a launched process is exited abnormally, the parent shell reports it # to stderr: See j_strsignal function call in wait_for in bash-4.2/nojobs.c. # This becomes noise; close the stderr of subshell. # exec 2>&-; # # The original bug report(#1100 by @techee): # -------------------------------------------------------------------------- # When running # # make units VG=1 # # one cannot stop its execution by pressing Ctrl+C and # there doesn't seem to be any way (except for looking at # processes which run and killing them) to stop its # execution. # trap "exit ${_BASH_INTERRUPT_EXIT}" INT; ${_CMDLINE} 2> "${ostderr}" > "${orawout}" ) tmp="$?" run_record_cmdline "${ffilter}" "${ocmdline}" } if [ "$tmp" != 0 ]; then if [ "${tmp}" = "${_BASH_INTERRUPT_EXIT}" ]; then ERROR 1 "The execution is interrupted" elif ! [ "$WITH_TIMEOUT" = 0 ] && [ "${tmp}" = "${_TIMEOUT_EXIT}" ]; then echo "${category}/${name}" >> ${R_FAILED_BY_TIMEED_OUT} run_result error "${msg}" "${oresult}" "TIMED OUT" run_record_cmdline "${ffilter}" "${ocmdline}" [ "${RUN_SHRINK}" = 'yes' ] \ && [ $# -eq 0 ] \ && run_shrink "${cmdline_template}" "${input}" "${oshrink}" "${guessed_lang}" return 1 elif [ "$WITH_VALGRIND" = 'yes' ] && [ "${tmp}" = "${_VALGRIND_EXIT}" ] && ! [ "${class}" = v ]; then echo "${category}/${name}" >> ${R_VALGRIND} run_result error "${msg}" "${oresult}" "valgrind-error" run_record_cmdline "${ffilter}" "${ocmdline}" return 1 elif [ "$class" = 'b' ]; then echo "${category}/${name}" >> ${R_KNOWN_BUGS} run_result known_error "${msg}" "${oresult}" run_record_cmdline "${ffilter}" "${ocmdline}" [ "${RUN_SHRINK}" = 'yes' ] \ && [ $# -eq 0 ] \ && run_shrink "${cmdline_template}" "${input}" "${oshrink}" "${guessed_lang}" return 0 else echo "${category}/${name}" >> ${R_FAILED_BY_STATUS} run_result error "${msg}" "${oresult}" "unexpected exit status: $tmp" run_record_cmdline "${ffilter}" "${ocmdline}" [ "${RUN_SHRINK}" = 'yes' ] \ && [ $# -eq 0 ] \ && run_shrink "${cmdline_template}" "${input}" "${oshrink}" "${guessed_lang}" return 1 fi elif [ "$WITH_VALGRIND" = 'yes' ] && [ "$class" = 'v' ]; then echo "${category}/${name}" >> ${R_FIXED} fi if ! [ -f "${fexpected}" ]; then clean_tcase "${o}" "${obundles}" if [ "$class" = 'b' ]; then echo "${category}/${name}" >> ${R_FIXED} elif [ "$class" = 'i' ]; then echo "${category}/${name}" >> ${R_FIXED} fi echo "${category}/${name}" >> ${R_PASSED} run_result ok "${msg}" '/dev/null' "\"expected.tags*\" not found" return 0 fi ${output_type}_basename_filter < "${orawout}" | \ anon_normalize "${CTAGS}" "${input}" "$@" | \ $ffilter > "${ofiltered}" { diff -U 0 -I '^!_TAG' --strip-trailing-cr "${fexpected}" "${ofiltered}" > "${odiff}" tmp="$?" } if [ "${tmp}" = 0 ]; then clean_tcase "${o}" "${obundles}" if [ "${class}" = 'b' ]; then echo "${category}/${name}" >> ${R_FIXED} elif ! [ "$WITH_TIMEOUT" = 0 ] && [ "${class}" = 'i' ]; then echo "${category}/${name}" >> ${R_FIXED} fi echo "${category}/${name}" >> ${R_PASSED} run_result ok "${msg}" '/dev/null' return 0 else if [ "${class}" = 'b' ]; then echo "${category}/${name}" >> ${R_KNOWN_BUGS} run_result known_error "${msg}" "${oresult}" run_record_cmdline "${ffilter}" "${ocmdline}" return 0 else echo "${category}/${name}" >> ${R_FAILED_BY_DIFF} run_result error "${msg}" "${oresult}" "unexpected output" run_record_cmdline "${ffilter}" "${ocmdline}" return 1 fi fi } failure_in_globing () { # skip if globing failed, also ignore backup files case $1 in *\~) return 0 ;; *\**) return 0 ;; *) return 1 ;; esac } run_dir () { local category="$1" local base_dir="$2" local build_base_dir="$3" shift 3 local tcase_dir local build_tcase_dir local input local name local dname local class local extra_tmp local extra_inputs # # Filtered by CATEGORIES # if [ -n "$CATEGORIES" ] && ! member_p "${category}" $CATEGORIES; then return 1 fi echo echo "Category: $category" line for input in ${base_dir}/*.[dbtiv]/input.*; do if failure_in_globing "$input"; then continue fi dname=$(dirname $input) extra_inputs=$(for extra_tmp in $dname/input[-_][0-9].* \ $dname/input[-_][0-9][-_]*.* ; do if failure_in_globing "$extra_tmp"; then continue fi echo "$extra_tmp" done | sort) tcase_dir="${input%/input.*}" build_tcase_dir="${build_base_dir}/${tcase_dir#${base_dir}/}" name="${tcase_dir%.[dbtiv]}" name="${name##*/}" class="${tcase_dir#*${name}.}" # Run this in parallel run_tcase "${input}" "${tcase_dir}" "${name}" "${class}" "${category}" "${build_tcase_dir}" ${extra_inputs} & done wait return 0 } run_show_diff_output () { local units_dir="$1" local t="$2" printf " " line . sed -e 's/^.*$/ &/' ${units_dir}/${t}.*/${_DIFF_OUTPUT_NAME} echo } run_show_stderr_output () { local units_dir="$1" local t="$2" printf " " line . sed -e 's/^.*$/ &/' ${units_dir}/${t}.*/${_STDERR_OUTPUT_NAME} | tail -50 echo } run_summary () { local build_dir="${1}" local t echo echo "Summary (see CMDLINE.tmp to reproduce without test harness)" line printf ' %-40s' "#passed:" L_PASSED=$([ -f $R_PASSED ] && cat $R_PASSED) count_list $L_PASSED printf ' %-40s' "#FIXED:" L_FIXED=$([ -f $R_FIXED ] && cat $R_FIXED) count_list $L_FIXED for t in $L_FIXED; do echo " ${t#${_DEFAULT_CATEGORY}/}" done printf ' %-40s' "#FAILED (broken args.ctags?):" L_BROKEN_ARGS_CTAGS=$([ -f $R_BROKEN_ARGS_CTAGS ] && cat $R_BROKEN_ARGS_CTAGS) count_list $L_BROKEN_ARGS_CTAGS for t in $L_BROKEN_ARGS_CTAGS; do echo " ${t#${_DEFAULT_CATEGORY}/}" done printf ' %-40s' "#FAILED (unexpected-exit-status):" L_FAILED_BY_STATUS=$([ -f $R_FAILED_BY_STATUS ] && cat $R_FAILED_BY_STATUS) count_list $L_FAILED_BY_STATUS for t in $L_FAILED_BY_STATUS; do echo " ${t#${_DEFAULT_CATEGORY}/}" if [ "${SHOW_DIFF_OUTPUT}" = yes ]; then run_show_stderr_output "${build_dir}" "${t#${_DEFAULT_CATEGORY}/}" fi done printf ' %-40s' "#FAILED (unexpected-output):" L_FAILED_BY_DIFF=$([ -f $R_FAILED_BY_DIFF ] && cat $R_FAILED_BY_DIFF) count_list $L_FAILED_BY_DIFF for t in $L_FAILED_BY_DIFF; do echo " ${t#${_DEFAULT_CATEGORY}/}" if [ "${SHOW_DIFF_OUTPUT}" = yes ]; then run_show_stderr_output "${build_dir}" "${t#${_DEFAULT_CATEGORY}/}" run_show_diff_output "${build_dir}" "${t#${_DEFAULT_CATEGORY}/}" fi done if ! [ "$WITH_TIMEOUT" = 0 ]; then printf ' %-40s' "#TIMED-OUT (${WITH_TIMEOUT}s)" L_FAILED_BY_TIMEED_OUT=$([ -f $R_FAILED_BY_TIMEED_OUT ] && cat $R_FAILED_BY_TIMEED_OUT) count_list $L_FAILED_BY_TIMEED_OUT for t in $L_FAILED_BY_TIMEED_OUT; do echo " ${t#${_DEFAULT_CATEGORY}/}" done fi printf ' %-40s' "#skipped (features):" L_SKIPPED_BY_FEATURES=$([ -f $R_SKIPPED_BY_FEATURES ] && cat $R_SKIPPED_BY_FEATURES) count_list $L_SKIPPED_BY_FEATURES for t in $L_SKIPPED_BY_FEATURES; do echo " ${t#${_DEFAULT_CATEGORY}/}" done printf ' %-40s' "#skipped (languages):" L_SKIPPED_BY_LANGUAGES=$([ -f $R_SKIPPED_BY_LANGUAGES ] && cat $R_SKIPPED_BY_LANGUAGES) count_list $L_SKIPPED_BY_LANGUAGES for t in $L_SKIPPED_BY_LANGUAGES; do echo " ${t#${_DEFAULT_CATEGORY}/}" done if [ "$WITH_TIMEOUT" = 0 ]; then printf ' %-40s' "#skipped (infinite-loop):" L_SKIPPED_BY_ILOOP=$([ -f $R_SKIPPED_BY_ILOOP ] && cat $R_SKIPPED_BY_ILOOP) count_list $L_SKIPPED_BY_ILOOP for t in $L_SKIPPED_BY_ILOOP; do echo " ${t#${_DEFAULT_CATEGORY}/}" done fi printf ' %-40s' "#known-bugs:" L_KNOWN_BUGS=$([ -f $R_KNOWN_BUGS ] && cat $R_KNOWN_BUGS) count_list $L_KNOWN_BUGS for t in $L_KNOWN_BUGS; do echo " ${t#${_DEFAULT_CATEGORY}/}" done if [ "$WITH_VALGRIND" = yes ]; then printf ' %-40s' "#valgrind-error:" L_VALGRIND=$([ -f $R_VALGRIND ] && cat $R_VALGRIND) count_list $L_VALGRIND for t in $L_VALGRIND; do echo " ${t#${_DEFAULT_CATEGORY}/}" done fi } make_pretense_map () { local ifs=$IFS local p local r IFS=, for p in $1; do newlang=${p%/*} oldlang=${p#*/} if [ -z "$newlang" ]; then ERROR 1 "newlang part of --pretend option arg is empty" fi if [ -z "$oldlang" ]; then ERROR 1 "oldlang part of --pretend option arg is empty" fi r="$r --_pretend-$newlang=$oldlang" done IFS=$ifs echo $r } delete_result_files () { rm -f ${R_PASSED} ${R_FIXED} ${R_FAILED_BY_STATUS} ${R_FAILED_BY_DIFF} \ ${R_SKIPPED_BY_FEATURES} ${R_SKIPPED_BY_LANGUAGES} \ ${R_SKIPPED_BY_ILOOP} ${R_KNOWN_BUGS} ${R_FAILED_BY_TIMEED_OUT} \ ${R_BROKEN_ARGS_CTAGS} } action_run () { local action="$1" shift local units_dir local build_dir local d local build_d local category local c while [ $# -gt 0 ]; do case $1 in --ctags) shift CTAGS="$1" shift ;; --ctags=*) CTAGS="${1#--ctags=}" shift ;; --categories) shift for c in $(echo "$1" | tr ',' ' '); do if [ "$c" = "ROOT" ]; then CATEGORIES="$CATEGORIES ROOT" else CATEGORIES="$CATEGORIES ${c%.r}.r" fi done shift ;; --categories=*) for c in $(echo "${1#--categories=}" | tr ',' ' '); do if [ "$c" = "ROOT" ]; then CATEGORIES="$CATEGORIES ROOT" else CATEGORIES="$CATEGORIES ${c%.r}.r" fi done shift ;; --units) shift UNITS=$(echo "$1" | tr ',' ' ') shift ;; --units=*) UNITS=$(echo "${1#--units=}" | tr ',' ' ') shift ;; --languages) shift LANGUAGES=$(echo "${1}" | tr ',' ' ') shift ;; --languages=*) LANGUAGES=$(echo "${1#--languages=}" | tr ',' ' ') shift ;; --with-timeout) shift WITH_TIMEOUT="$1" shift ;; --with-timeout=*) WITH_TIMEOUT="${1#--with-timeout=}" shift ;; --with-valgrind) shift WITH_VALGRIND=yes ;; --colorized-output) shift COLORIZED_OUTPUT="$1" shift ;; --colorized-output=*) COLORIZED_OUTPUT="${1#--colorized-output=}" shift ;; --run-shrink) RUN_SHRINK=yes shift ;; --show-diff-output) SHOW_DIFF_OUTPUT=yes shift ;; --with-pretense-map) shift PRETENSE_OPTS=$(make_pretense_map "$1") shift ;; --with-pretense-map=*) PRETENSE_OPTS=$(make_pretense_map "${1#--with-pretense-map=}") shift ;; -*) ERROR 1 "unknown option \"${1}\" for ${action} action" ;; *) units_dir="$1" shift build_dir="${1:-${units_dir}}" shift break ;; esac done if [ $# -gt 0 ]; then ERROR 1 "too many arguments for ${action} action: $*" elif [ -z "$units_dir" ]; then ERROR 1 "UNITS_DIR parameter is not given in ${action} action" fi if ! [ -d "$units_dir" ]; then ERROR 1 "No such directory: ${units_dir}" fi case "${build_dir}" in /*) ;; *) build_dir=$(pwd)/${build_dir} ;; esac if ! [ -d "$build_dir" ]; then ERROR 1 "No such directory(build_dir): ${build_dir}" fi if ! [ -f "${CTAGS}" ]; then ERROR 1 "no such file: ${CTAGS}" elif ! [ -e "${CTAGS}" ]; then ERROR 1 "${CTAGS} is not an executable file" fi if ! ( [ "${COLORIZED_OUTPUT}" = 'yes' ] || [ "${COLORIZED_OUTPUT}" = 'no' ] ); then ERROR 1 "unexpected option argument for --colorized-output: ${COLORIZED_OUTPUT}" fi : ${WITH_TIMEOUT:=0} [ "$WITH_TIMEOUT" = 0 ] || check_availability timeout [ "$WITH_VALGRIND" = 'yes' ] && check_availability valgrind [ "$MSYSTEM" != '' ] && check_availability dos2unix check_availability grep check_availability diff init_features delete_result_files category="${_DEFAULT_CATEGORY}" if [ -z "$CATEGORIES" ] \ || ( [ -n "$CATEGORIES" ] && member_p "${category}" $CATEGORIES ); then run_dir "${category}" "${units_dir}" "${build_dir}" fi for d in ${units_dir}/*.r; do [ -d "$d" ] || continue category="${d##*/}" build_d=${build_dir}/${category} run_dir "${category}" "$d" "${build_d}" done run_summary "${build_dir}" delete_result_files if [ -n "${L_FAILED_BY_STATUS}" ] || [ -n "${L_FAILED_BY_DIFF}" ] || [ -n "${L_FAILED_BY_TIMEED_OUT}" ] || [ -n "${L_BROKEN_ARGS_CTAGS}" ]; then return 1 else return 0 fi } help_run () { cat </dev/null > "${output}" } shrink_test () { local cmdline="$1" local input="$2" local start="$3" local len="$4" local output="$5" local r local msg shrink_prepare "${output}" "${input}" "${start}" "${len}" [ "${QUIET}" = 'yes' ] || printf "[%-5u %6u]..." "${start}" $(( start + len )) 1>&2 eval "${cmdline}" > /dev/null 2>&1 r="$?" if [ "$r" -eq 0 ]; then msg='ok' elif [ "$r" -eq "${_TIMEOUT_EXIT}" ]; then msg='timeout' else msg='failed' fi [ "${QUIET}" = 'yes' ] || printf "%s(%u)\n" "$msg" "$r" 1>&2 return $r } shrink_bisect () { local cmdline="$1" local input="$2" local len="$3" local output="$4" local end local start local step local delta local failed local successful end="${len}" failed="${len}" successful=0 step=0 while true; do delta=$((len >> (step + 1))) if [ "${delta}" -eq 0 ]; then delta=1 fi if shrink_test "${cmdline}" "${input}" 0 "${end}" "${output}"; then successful="${end}" if [ $(( end + 1 )) -eq "${failed}" ]; then end="${failed}" break else end=$((end + delta)) fi else failed="$end" if [ $(( successful + 1 )) -eq "${end}" ]; then break else end=$((end - delta)) fi fi step=$((step + 1 )) done len="${end}" start=0 failed=0 successful="${end}" step=0 while true; do delta=$((len >> (step + 1))) if [ "${delta}" -eq 0 ]; then delta=1 fi if shrink_test "${cmdline}" "${input}" "${start}" $((end - start)) "${output}"; then successful="${start}" if [ $(( start - 1 )) -eq "${failed}" ]; then start=$((start - 1)) break else start=$((start - delta)) fi else failed="${start}" if [ $((successful - 1)) -eq "${start}" ]; then break else start=$((start + delta)) fi fi step=$((step + 1)) done len=$((end - start)) shrink_prepare "${output}" "${input}" "${start}" "${len}" [ "${QUIET}" = 'yes' ] || echo "Minimal badinput: ${output}" [ "${QUIET}" = 'yes' ] || line . cat "${output}" echo return 0 } shrink_main () { local cmdline_template="$1" local cmdline local input="$2" local len local output="$3" local duration="$4" local foreground="$5" if ! [ -f "${input}" ]; then ERROR 1 "No such file: ${input}" elif ! [ -r "${input}" ]; then ERROR 1 "Cannot read a file: ${input}" fi if ! cat < /dev/null > "${output}"; then ERROR 1 "Cannot modify a file: ${output}" fi cmdline=$(printf "${cmdline_template}" "${output}") if [ -n "${duration}" ] && ! [ "${duration}" -eq 0 ]; then if [ "${foreground}" = 'yes' ]; then cmdline="timeout --foreground ${duration} ${cmdline}" else cmdline="timeout ${duration} ${cmdline}" fi fi len=$(${_FSIZE} "${input}") if shrink_test "${cmdline}" "${input}" 0 "${len}" "${output}"; then printf "the target command line exits normally against the original input\n" 1>&2 return 1 fi if ! shrink_test "${cmdline}" "${input}" 0 0 "${output}"; then printf "the target command line exits abnormally against the empty input\n" 1>&2 return 1 fi shrink_bisect "${cmdline}" "${input}" "${len}" "${output}" } action_shrink () { local action="$1" shift local cmdline_template local input local output local timeout local duration local foreground while [ $# -gt 0 ]; do case $1 in --timeout) shift duration=$1 shift ;; --timeout=*) duration="${1#--timeout=}" shift ;; --foreground) foreground=yes shift ;; --quiet) QUIET=yes shift ;; -*) ERROR 1 "unknown option \"${1}\" for ${action} action" ;; *) break ;; esac done if [ $# -lt 3 ]; then ERROR 1 "too few arguments for ${action} action: $*" elif [ $# -gt 3 ]; then ERROR 1 "too many arguments for ${action} action: $*" fi if [ -n "${foreground}" ] && [ -z "${duration}" ]; then ERROR 1 "--foreground option is meaningful only if --timeout option is specified." fi cmdline_template=$1 input=$2 output=$3 shift 3 shrink_main "${cmdline_template}" "${input}" "${output}" ${duration} ${foreground} return $? } help_shrink () { cat < "${ocmdline}" ${cmdline} > /dev/null r=$? case $r in 0) rm -f "${ovalgrind}" "${ocmdline}" return 0 ;; ${_TIMEOUT_EXIT}) [ "${QUIET}" = 'yes' ] || echo printf '%-40s' "[timeout $lang]" echo "$f" [ "${RUN_SHRINK}" = 'yes' ] && fuzz_shrink "${cmdline_for_shirking}" "${file}" "${oshrink}" "${lang}" return 1 ;; ${_VALGRIND_EXIT}) [ "${QUIET}" = 'yes' ] || echo printf '%-40s' "[valgrind-error $lang]" echo "$f" return 1 ;; *) [ "${QUIET}" = 'yes' ] || echo printf '%-40s' "[unexpected-status($r) $lang]" echo "$f" [ "${RUN_SHRINK}" = 'yes' ] && fuzz_shrink "${cmdline_for_shirking}" "${file}" "${oshrink}" "${lang}" return 1 ;; esac return $r } fuzz_lang () { local lang="$1" local dir="$2" shift 2 local f local r=0 [ "${QUIET}" = 'yes' ] || printf '%-60s\n' "Semi-fuzzing (${lang})" for f in $(find "${dir}" -type f -name 'input.*'); do if ! fuzz_lang_file "${lang}" "${f}"; then r=1 break fi done [ "${QUIET}" = 'yes' ] || echo return $r } action_fuzz () { action_fuzz_common fuzz_lang "$@" } action_fuzz_common () { local fn="$1" local action="$2" shift 2 local units_dir local cmdline local lang local r while [ $# -gt 0 ]; do case $1 in --ctags) shift CTAGS="$1" shift ;; --ctags=*) CTAGS="${1#--ctags=}" shift ;; --languages) shift LANGUAGES=$(echo "${1}" | tr ',' ' ') shift ;; --languages=*) LANGUAGES=$(echo "${1#--languages=}" | tr ',' ' ') shift ;; --quiet) QUIET=yes shift ;; --with-timeout) shift WITH_TIMEOUT="$1" shift ;; --with-timeout=*) WITH_TIMEOUT="${1#--with-timeout=}" shift ;; --with-valgrind) shift WITH_VALGRIND=yes ;; --colorized-output) shift COLORIZED_OUTPUT="$1" shift ;; --colorized-output=*) COLORIZED_OUTPUT="${1#--colorized-output=}" shift ;; --run-shrink) RUN_SHRINK=yes shift ;; -*) ERROR 1 "unknown option \"${1}\" for ${action} action" ;; *) units_dir="$1" shift break; ;; esac done if [ $# -gt 0 ]; then ERROR 1 "too many arguments for ${action} action: $*" elif [ -z "$units_dir" ]; then ERROR 1 "UNITS_DIR parameter is not given in ${action} action" fi if ! [ -d "$units_dir" ]; then ERROR 0 "No such directory: ${units_dir}" fi if ! [ -f "${CTAGS}" ]; then ERROR 1 "no such file: ${CTAGS}" elif ! [ -e "${CTAGS}" ]; then ERROR 1 "${CTAGS} is not an executable file" fi if ! ( [ "${COLORIZED_OUTPUT}" = 'yes' ] || [ "${COLORIZED_OUTPUT}" = 'no' ] ); then ERROR 1 "unexpected option argument for --colorized-output: ${COLORIZED_OUTPUT}" fi : ${WITH_TIMEOUT:=2} [ "$WITH_TIMEOUT" = 0 ] || check_availability timeout [ "$WITH_VALGRIND" = 'yes' ] && check_availability valgrind check_availability find cmdline="${CTAGS} --quiet --options=NONE --kinds-all=* --fields=*" _CMDLINE="${cmdline} -G -o - " _CMDLINE_FOR_SHRINKING="${_CMDLINE}" if [ "$WITH_VALGRIND" = yes ]; then _CMDLINE="valgrind --leak-check=full --error-exitcode=${_VALGRIND_EXIT} --log-file=%s ${_CMDLINE}" WITH_TIMEOUT=$(( WITH_TIMEOUT * ${_VG_TIMEOUT_FACTOR} )) fi if ! [ "$WITH_TIMEOUT" = 0 ]; then _CMDLINE="timeout --foreground $WITH_TIMEOUT ${_CMDLINE}" _CMDLINE_FOR_SHRINKING="timeout --foreground 1 ${_CMDLINE_FOR_SHRINKING}" fi for lang in $( ${cmdline} --list-languages 2>/dev/null | "${_LINE_SPLITTER}" |sed -e 's/ //' ) ; do if [ -n "${LANGUAGES}" ] && ! member_p "${lang}" ${LANGUAGES}; then continue fi "${fn}" "${lang}" "${units_dir}" r=$? done return $r } help_fuzz () { cat < "$onoised" 2> /dev/null progress_offset=$(( pos % _NOISE_REPORT_MAX_COLUMN )) if [ "${progress_offset}" -eq 0 ]; then [ $pos -gt 0 ] && printf " %s %d/%d" "${how}" "$pos" "${len}" echo fi echo "${cmdline}" > "${ocmdline}" ( exec 2>&-; ${cmdline} 2> /dev/null > /dev/null ) r=$? case $r in 0) printf 'o' noise_report_line "${pos}" "${len}" "${r}" "${how}" rm "${onoised}" rm -f "${ovalgrind}" "${ocmdline}" ;; ${_TIMEOUT_EXIT}) printf "T" noise_report_line "${pos}" "${len}" "${r}" "${how}" printf '\n%-20s\n' "[timeout $lang]" "$onoised" [ "${RUN_SHRINK}" = 'yes' ] && fuzz_shrink "${cmdline_for_shirking}" "${onoised}" "${oshrink}" "${lang}" ;; ${_VALGRIND_EXIT}) printf "V" noise_report_line "${pos}" "${len}" "${r}" "${how}" printf '\n%-20s %s\n' "[valgrind-error $lang]" "$onoised" ;; *) printf "!" noise_report_line "${pos}" "${len}" "${r}" "${how}" printf '\n%-20s %s\n' "[unexpected-status($r) $lang]" "$onoised" [ "${RUN_SHRINK}" = 'yes' ] && fuzz_shrink "${cmdline_for_shirking}" "${onoised}" "${oshrink}" "${lang}" ;; esac return $r } noise_lang_file () { local lang="$1" local input="$2" shift 2 local cmdline local cmdline_for_shirking local len=$(${_FSIZE} "${input}") local r local i local c local guessed_lang guessed_lang=$( ${_CMDLINE_FOR_SHRINKING} --print-language "${input}" 2>/dev/null | sed -n 's/^.*: //p') if [ "${lang}" != "${guessed_lang}" ]; then return 0 fi i=0 c='!' echo "Testing cases derived from: ${input}" line '.' --no-newline while [ "$i" -lt "$len" ]; do if noise_lang_file_noisespec "${input}" "${len}" "$i" "$c" noise_reduce "${lang}" -; then i=$(( i + 1 )) else echo return 1 fi done for c in 'a' '0' \ '!' '@' '#' '$' '%' '^' '&' '*' '(' ')' '-' '=' '_' \ '+' '|' '[' ']' '{' '}' '\' ';' "'" ':' '"' ',' '.' \ '/' '<' '>' '?' '`' '~'; do i=0 while [ "$i" -lt "$len" ]; do if noise_lang_file_noisespec "${input}" "${len}" "$i" "$c" noise_inject "${lang}" +; then i=$(( i + 1 )) else echo return 1 fi done done echo return 0 } noise_lang () { local lang="$1" local dir="$2" shift 2 local f local r printf '%-60s\n' "Noised-fuzzing (${lang})" line '-' r=0 for f in $(find "${dir}" -type f -name 'input.*'); do if ! noise_lang_file "${lang}" "${f}"; then r=1 break fi done echo return $r } action_noise () { action_fuzz_common noise_lang "$@" } help_noise () { cat < ${generated} 2>&1; then run_result ok "${msg}" '/dev/null' rm ${generated} return 0 else run_result error "${msg}" '/dev/null' "diff: ${generated}" return 1 fi } failed_git_marker () { local f=$1 local l if type "git" > /dev/null 2>&1; then l=$(git ls-files -- "$f") if [ -z "$l" ]; then echo '' fi fi } is_crashed () { local f=$1 grep -q -i "core dump" "$f" } print_backtraces() { local ctags_exe=$1 shift 1 local coref for coref in "$@"; do if [ -f "${coref}" ]; then gdb "${ctags_exe}" -c "${coref}" -ex where -batch else echo "no such file: ${coref}" fi done } CODE_FOR_IGNORING_THIS_TMAIN_TEST=77 tmain_run () { local topdir=$1 local build_topdir=$2 shift 2 local units="$@" local subdir local basedir local test_name local r_failed="_failed.result" local failed local f local aspect local engine local r local a local status_=0 local msg local need_rearrange if ! [ $(basename "${CTAGS}") = 'ctags' ]; then need_rearrange=yes fi rm -f ${r_failed} basedir=$(pwd) for subdir in ${topdir}/*.d; do if [ "${subdir}" = ${topdir}/'*.d' ]; then return 1 fi test_name=$(basename ${subdir} .d) if [ -n "${units}" ] && ! member_p "${test_name}" ${units}; then continue fi build_subdir=${build_topdir}/$(basename ${subdir}) if ! mkdir -p ${build_subdir}; then return 1 fi # Run this block in parallel ( rm -f ${build_subdir}/*-actual.txt msg="\nTesting ${test_name}" msg="${msg}\n$(line '-')" ( cd ${subdir} ${SHELL} run.sh \ ${basedir}/${CTAGS} \ ${build_subdir} \ ${basedir}/${READTAGS} ) > ${build_subdir}/stdout-actual.txt 2> ${build_subdir}/stderr-actual.txt r=$? echo $r > ${build_subdir}/exit-actual.txt if [ -n "${need_rearrange}" ]; then sed -i -e 's|^'$(basename "${CTAGS}")':|ctags:|' ${build_subdir}/stderr-actual.txt fi if [ $r = $CODE_FOR_IGNORING_THIS_TMAIN_TEST ]; then msg="${msg}\n$(run_result skip "" '/dev/null' "$(cat ${build_subdir}/stdout-actual.txt)")" for a in ${build_subdir}/*-actual.txt; do if [ -f "$a" ]; then rm $a fi done printf "%b\n" "${msg}" exit fi if [ -f ${build_subdir}/tags ]; then mv ${build_subdir}/tags ${build_subdir}/tags-actual.txt fi for aspect in stdout stderr exit tags; do if [ -f ${subdir}/${aspect}-expected.txt ]; then engine=compare msg="${msg}\n$(tmain_${engine} ${subdir} ${build_subdir} ${aspect})" if [ $? -eq 0 ]; then rm ${build_subdir}/${aspect}-actual.txt else echo "${test_name}/${aspect}-${engine}$(failed_git_marker ${subdir}/${aspect}-expected.txt)" >> ${r_failed} if [ ${aspect} = stderr ] && is_crashed ${build_subdir}/${aspect}-actual.txt && type "gdb" > /dev/null 2>&1; then print_backtraces "${basedir}/${CTAGS}" \ ${build_subdir}/core* \ > ${build_subdir}/gdb-backtrace.txt fi fi elif [ -f ${build_subdir}/${aspect}-actual.txt ]; then rm ${build_subdir}/${aspect}-actual.txt fi done printf "%b\n" "${msg}" ) & done wait if [ -f "${r_failed}" ]; then status_=1 echo echo Failed tests line '=' failed=$(cat ${r_failed}) for f in ${failed}; do echo $f | sed -e 's|| (not committed/cached yet)|' done echo if [ "${SHOW_DIFF_OUTPUT}" = yes ]; then engine=compare echo Detail "[$engine]" line '-' tmain_${engine}_result ${build_topdir} fi rm ${r_failed} fi return $status_ } action_tmain () { local action="$1" shift local tmain_dir local build_dir while [ $# -gt 0 ]; do case $1 in --ctags) shift CTAGS="$1" shift ;; --ctags=*) CTAGS="${1#--ctags=}" shift ;; --colorized-output) shift COLORIZED_OUTPUT="$1" shift ;; --colorized-output=*) COLORIZED_OUTPUT="${1#--colorized-output=}" shift ;; --with-valgrind) shift WITH_VALGRIND=yes ;; --show-diff-output) SHOW_DIFF_OUTPUT=yes shift ;; --readtags=*) READTAGS="${1#--readtags=}" shift ;; --units) shift UNITS=$(echo "$1" | tr ',' ' ') shift ;; --units=*) UNITS=$(echo "${1#--units=}" | tr ',' ' ') shift ;; -*) ERROR 1 "unknown option \"${1}\" for ${action} action" ;; *) tmain_dir="$1" shift build_dir=${1:-${tmain_dir}} if [ -n "$1" ]; then shift fi break ;; esac done if [ $# -gt 0 ]; then ERROR 1 "too many arguments for ${action} action: $*" elif [ -z "$tmain_dir" ]; then ERROR 1 "TMAIN_DIR parameter is not given in ${action} action" fi if ! [ -d "$tmain_dir" ]; then ERROR 1 "No such directory(tmain_dir): ${tmain_dir}" fi case "${build_dir}" in /*) ;; *) build_dir=$(pwd)/${build_dir} ;; esac if ! [ -d "$build_dir" ]; then ERROR 1 "No such directory(build_dir): ${build_dir}" fi if ! [ -f "${CTAGS}" ]; then ERROR 1 "no such file: ${CTAGS}" elif ! [ -e "${CTAGS}" ]; then ERROR 1 "${CTAGS} is not an executable file" fi if ! ( [ "${COLORIZED_OUTPUT}" = 'yes' ] || [ "${COLORIZED_OUTPUT}" = 'no' ] ); then ERROR 1 "unexpected option argument for --colorized-output: ${COLORIZED_OUTPUT}" fi check_availability awk check_availability diff tmain_run ${tmain_dir} ${build_dir} ${UNITS} return $? } help_tmain () { cat </dev/null | sed -n 's/^.*: //p') if [ "${lang}" != "${guessed_lang}" ]; then return 0 fi i=0 echo "Testing cases derived from: ${input}" line '.' --no-newline r=0 while [ "$i" -lt "$len" ]; do if chop_lang_file_chopspec "${endpoint}" "${input}" "${len}" "$i" "${lang}"; then i=$(( i + 1 )) else r=1 break fi done echo return $r } chop() { local endpoint=$1 local input=$2 local pos=$3 local len=$4 if [ "${endpoint}" = "tail" ]; then dd if=$input bs=1 count=$pos else dd if=$input bs=1 count=$((len - pos)) skip=$pos fi } chop_lang_file_chopspec() { local endpoint=$1 shift 1 local input="$1" local len="$2" local pos="$3" local lang="$4" shift 4 local dir="${input%/*}" local ochopped=$(printf "%s/CHOP-INPUT-%s.tmp" "${dir}" "$pos") local ocmdline=$(printf "%s/CHOP-CMDLINE-%s.tmp" "${dir}" "$pos") local ovalgrind=$(printf "%s/CHOP-VALGRIND-%s.tmp" "${dir}" "$pos") local oshrink=$(printf "%s/CHOP-SHRINK-%s.tmp" "${dir}" "$pos") local cmdline local cmdline_for_shirking local progress_offset local r rm -f "${ocmdline}" "${ovalgrind}" "${ochopped}" "${oshrink}" if [ "${WITH_VALGRIND}" = 'yes' ]; then cmdline=$( printf "${_CMDLINE} --language-force=${lang} ${ochopped}" "${ovalgrind}" ) else cmdline="${_CMDLINE} --language-force=${lang} ${ochopped}" fi cmdline_for_shirking="${_CMDLINE_FOR_SHRINKING} --language-force=${lang} %s" chop "${endpoint}" "${input}" "${pos}" "${len}" > "$ochopped" 2> /dev/null progress_offset=$(( pos % _NOISE_REPORT_MAX_COLUMN )) if [ "${progress_offset}" -eq 0 ]; then [ $pos -gt 0 ] && printf " %d/%d" "$pos" "${len}" echo fi echo "${cmdline}" > "${ocmdline}" ( exec 2>&-; ${cmdline} 2> /dev/null > /dev/null ) r=$? case $r in 0) printf 'o' noise_report_line "${pos}" "${len}" "${r}" "" rm "${ochopped}" rm -f "${ovalgrind}" "${ocmdline}" ;; ${_TIMEOUT_EXIT}) printf "T" noise_report_line "${pos}" "${len}" "${r}" "" printf '\n%-20s\n' "[timeout $lang]" "$ochopped" [ "${RUN_SHRINK}" = 'yes' ] && fuzz_shrink "${cmdline_for_shirking}" "${ochopped}" "${oshrink}" "${lang}" ;; ${_VALGRIND_EXIT}) printf "V" noise_report_line "${pos}" "${len}" "${r}" "" printf '\n%-20s %s\n' "[valgrind-error $lang]" "$ochopped" ;; *) printf "!" noise_report_line "${pos}" "${len}" "${r}" "" printf '\n%-20s %s\n' "[unexpected-status($r) $lang]" "$ochopped" [ "${RUN_SHRINK}" = 'yes' ] && fuzz_shrink "${cmdline_for_shirking}" "${ochopped}" "${oshrink}" "${lang}" ;; esac return $r } help_validate_input () { cat <&2 exit 1 fi case $1 in help|-h|--help) action_help return 0 ;; run) action_run "$@" return $? ;; clean) action_clean "$@" return $? ;; fuzz) action_fuzz "$@" return $? ;; noise) action_noise "$@" return $? ;; tmain) action_tmain "$@" return $? ;; clean-tmain) action_clean_tmain "$@" return $? ;; shrink) action_shrink "$@" return $? ;; chop) action_chop "$@" return $? ;; slap) action_chop "$@" return $? ;; validate-input) action_validate_input "$@" return $? ;; *) ERROR 1 "unknown action: $1" ;; esac } prepare_environment main "$@" exit $?