diff options
Diffstat (limited to 'lib/autotest/general.m4')
-rw-r--r-- | lib/autotest/general.m4 | 2215 |
1 files changed, 2215 insertions, 0 deletions
diff --git a/lib/autotest/general.m4 b/lib/autotest/general.m4 new file mode 100644 index 0000000..60c0352 --- /dev/null +++ b/lib/autotest/general.m4 @@ -0,0 +1,2215 @@ +# This file is part of Autoconf. -*- Autoconf -*- +# M4 macros used in building test suites. +m4_define([_AT_COPYRIGHT_YEARS], [ +Copyright (C) 2000-2012 Free Software Foundation, Inc. +]) + +# This file is part of Autoconf. 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 3 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. +# +# Under Section 7 of GPL version 3, you are granted additional +# permissions described in the Autoconf Configure Script Exception, +# version 3.0, as published by the Free Software Foundation. +# +# You should have received a copy of the GNU General Public License +# and a copy of the Autoconf Configure Script Exception along with +# this program; see the files COPYINGv3 and COPYING.EXCEPTION +# respectively. If not, see <http://www.gnu.org/licenses/>. + + +# _m4_divert(DIVERSION-NAME) +# -------------------------- +# Convert a diversion name into its number. Otherwise, return +# DIVERSION-NAME which is supposed to be an actual diversion number. +# Of course it would be nicer to use m4_case here, instead of zillions +# of little macros, but it then takes twice longer to run `autoconf'! +# +# From M4sugar: +# -1. KILL +# 10000. GROW +# +# From M4sh: +# 0. BINSH +# 1. HEADER-REVISION +# 2. HEADER-COMMENT +# 3. HEADER-COPYRIGHT +# 4. M4SH-SANITIZE +# 5. M4SH-INIT +# 1000. BODY +# +# Defined below: +# - DEFAULTS +# Overall initialization, value of $at_groups_all. +# - PARSE_ARGS_BEGIN +# Setup defaults required for option processing. +# - PARSE_ARGS +# Option processing. After AT_INIT, user options can be entered here as +# cases of a case statement. +# - PARSE_ARGS_END +# Finish up the option processing. +# +# - HELP +# Start printing the help message. +# - HELP_MODES +# Modes help text. Additional modes can be appended as self-contained +# cat'd here-docs as generated by AS_HELP_STRING. +# - HELP_TUNING_BEGIN +# Tuning help text. This is for Autotest-provided text. +# - HELP_TUNING +# Additional tuning options' help text can be appended here as +# self-contained cat'd here-docs as generated by AS_HELP_STRING. +# - HELP_OTHER +# User help can be appended to this as self-contained cat'd here-docs. +# - HELP_END +# Finish up the help texts. +# +# - VERSION +# Head of the handling of --version. +# - VERSION_NOTICES +# Copyright notices for --version. +# - VERSION_END +# Tail of the handling of --version. +# +# - BANNERS +# Output shell initialization for the associative array of banner text. +# - TESTS_BEGIN +# Like DEFAULTS but run after argument processing for purposes of +# optimization. Do anything else that needs to be done to prepare for +# tests. Sets up verbose and log file descriptors. Sets and logs PATH. +# - PREPARE_TESTS +# Declares functions shared among the tests. Perform any user +# initialization to be shared among all tests. +# - TESTS +# The core of the test suite. +# +# - TEST_SCRIPT +# The collector for code for each test, the ``normal'' diversion, but +# undiverted into other locations before final output. +# +# - TEST_GROUPS +# Contents of each test group. The tests deliberately occur after the +# end of the shell script, so that the shell need not spend time parsing +# commands it will not execute. + +m4_define([_m4_divert(DEFAULTS)], 100) +m4_define([_m4_divert(PARSE_ARGS_BEGIN)], 200) +m4_define([_m4_divert(PARSE_ARGS)], 201) +m4_define([_m4_divert(PARSE_ARGS_END)], 202) +m4_define([_m4_divert(HELP)], 300) +m4_define([_m4_divert(HELP_MODES)], 301) +m4_define([_m4_divert(HELP_TUNING_BEGIN)], 302) +m4_define([_m4_divert(HELP_TUNING)], 303) +m4_define([_m4_divert(HELP_OTHER)], 304) +m4_define([_m4_divert(HELP_END)], 305) +m4_define([_m4_divert(VERSION)], 350) +m4_define([_m4_divert(VERSION_NOTICES)], 351) +m4_define([_m4_divert(VERSION_END)], 352) +m4_define([_m4_divert(BANNERS)], 400) +m4_define([_m4_divert(TESTS_BEGIN)], 401) +m4_define([_m4_divert(PREPARE_TESTS)], 402) +m4_define([_m4_divert(TESTS)], 403) +m4_define([_m4_divert(TEST_SCRIPT)], 450) +m4_define([_m4_divert(TEST_GROUPS)], 500) + + +# AT_LINE +# ------- +# Return the current file sans directory, a colon, and the current +# line. Be sure to return a _quoted_ file name, so if, for instance, +# the user is lunatic enough to have a file named `dnl' (and I, for +# one, love to be brainless and stubborn sometimes), then we return a +# quoted name. +# +# Gee, we can't use simply +# +# m4_bpatsubst(__file__, [^.*/\(.*\)], [[\1]]) +# +# since then, since `dnl' doesn't match the pattern, it is returned +# with once quotation level less, so you lose! And since GNU M4 +# is one of the biggest junk in the whole universe wrt regexp, don't +# even think about using `?' or `\?'. Bah, `*' will do. +# Pleeeeeeeease, Gary, provide us with dirname and ERE! +# +# M4 recompiles the regular expression for every m4_bpatsubst, but __file__ +# rarely changes. Be fast - only compute the dirname when necessary; for +# autoconf alone, this shaves off several seconds in building testsuite. +m4_define([_AT_LINE_file]) +m4_define([_AT_LINE_base]) +m4_define([AT_LINE], +[m4_if(m4_defn([_AT_LINE_file]), __file__, [], + [m4_do([m4_define([_AT_LINE_file], __file__)], + [m4_define([_AT_LINE_base], + m4_bregexp(/__file__, [/\([^/]*\)$], [[\1]]))])])dnl +m4_defn([_AT_LINE_base]):__line__]) + +# _AT_LINE_ESCAPED +# ---------------- +# Same as AT_LINE, but already escaped for the shell. +m4_define([_AT_LINE_ESCAPED], ["AS_ESCAPE(m4_dquote(AT_LINE))"]) + + +# _AT_NORMALIZE_TEST_GROUP_NUMBER(SHELL-VAR) +# ------------------------------------------ +# Normalize SHELL-VAR so that its value has the same number of digits as +# all the other test group numbers. +m4_define([_AT_NORMALIZE_TEST_GROUP_NUMBER], +[ + eval 'while :; do + case $$1 in #( + '"$at_format"'*) break;; + esac + $1=0$$1 + done' +]) + +# _AT_DEFINE_INIT(NAME, [DEFINITION]) +# ----------------------------------- +# Define macro NAME to die if invoked prior to AT_INIT, and to DEFINITION +# after AT_INIT. +m4_define([_AT_DEFINE_INIT], +[m4_define($@)m4_pushdef([$1], [m4_fatal([$1: missing AT_INIT detected])])dnl +m4_append([_AT_DEFINE_INIT_LIST], [[$1]], [,])]) + +# _AT_DEFINE_SETUP(NAME, [DEFINITION]) +# ------------------------------------ +# Define macro NAME to die if invoked outside AT_SETUP/AT_CLEANUP, and +# to DEFINITION otherwise. +m4_define([_AT_DEFINE_SETUP], +[m4_define([$1], [m4_ifndef([AT_ingroup], + [m4_fatal([$1: missing AT_SETUP detected])])$2])]) + + +# AT_INIT([TESTSUITE-NAME]) +# ------------------------- +# Begin test suite. +m4_define([AT_INIT], +[m4_pushdef([AT_INIT], [m4_fatal([$0: invoked multiple times])])] +[m4_pattern_forbid([^_?AT_])] +[m4_pattern_allow([^_ATEOF$])] +[m4_ifndef([AT_PACKAGE_BUGREPORT], [m4_fatal( + [$1: AT_PACKAGE_BUGREPORT is missing, consider writing package.m4])])] +[m4_define([AT_TESTSUITE_NAME], + m4_defn([AT_PACKAGE_STRING])[ test suite]m4_ifval([$1], + [m4_expand([: $1])]))] +[m4_define([AT_ordinal], 0)] +[m4_define([AT_banner_ordinal], 0)] +[m4_define([AT_help_all], [])] +[m4_map_args([_m4_popdef], _AT_DEFINE_INIT_LIST)] +[m4_wrap([_AT_FINISH])] +[AS_INIT[]]dnl +dnl We don't use m4sh's BODY diversion, but AS_INIT sticks a banner there. +dnl This trick removes that banner, since it adds nothing to autotest. +[m4_cleardivert([BODY])]dnl +[AS_ME_PREPARE[]]dnl +[m4_divert_push([DEFAULTS])]dnl +[AT_COPYRIGHT(m4_defn([_AT_COPYRIGHT_YEARS]), [ +m4_copyright_condense])] +[AT_COPYRIGHT( +[This test suite is free software; the Free Software Foundation gives +unlimited permission to copy, distribute and modify it.], [m4_echo])] +[AS_PREPARE + +SHELL=${CONFIG_SHELL-/bin/sh} + +# How were we run? +at_cli_args="$[@]" + +m4_divert_push([BANNERS])dnl + +# Should we print banners? Yes if more than one test is run. +case $at_groups in #( + *$as_nl* ) + at_print_banners=: ;; #( + * ) at_print_banners=false ;; +esac +# Text for banner N, set to a single space once printed. +m4_divert_pop([BANNERS])dnl back to DEFAULTS +m4_divert_push([PREPARE_TESTS])dnl + +m4_text_box([Autotest shell functions.]) + +AS_FUNCTION_DESCRIBE([at_fn_banner], [NUMBER], +[Output banner NUMBER, provided the testsuite is running multiple groups +and this particular banner has not yet been printed.]) +at_fn_banner () +{ + $at_print_banners || return 0 + eval at_banner_text=\$at_banner_text_$[1] + test "x$at_banner_text" = "x " && return 0 + eval "at_banner_text_$[1]=\" \"" + if test -z "$at_banner_text"; then + $at_first || echo + else + AS_ECHO(["$as_nl$at_banner_text$as_nl"]) + fi +} # at_fn_banner + +AS_FUNCTION_DESCRIBE([at_fn_check_prepare_notrace], [REASON LINE], +[Perform AT_CHECK preparations for the command at LINE for an +untraceable command; REASON is the reason for disabling tracing.]) +at_fn_check_prepare_notrace () +{ + $at_trace_echo "Not enabling shell tracing (command contains $[1])" + AS_ECHO(["$[2]"]) >"$at_check_line_file" + at_check_trace=: at_check_filter=: + : >"$at_stdout"; : >"$at_stderr" +} + +AS_FUNCTION_DESCRIBE([at_fn_check_prepare_trace], [LINE], +[Perform AT_CHECK preparations for the command at LINE for a traceable +command.]) +at_fn_check_prepare_trace () +{ + AS_ECHO(["$[1]"]) >"$at_check_line_file" + at_check_trace=$at_traceon at_check_filter=$at_check_filter_trace + : >"$at_stdout"; : >"$at_stderr" +} + +AS_FUNCTION_DESCRIBE([at_fn_check_prepare_dynamic], [COMMAND LINE], +[Decide if COMMAND at LINE is traceable at runtime, and call the +appropriate preparation function.]) +at_fn_check_prepare_dynamic () +{ + case $[1] in + *$as_nl*) + at_fn_check_prepare_notrace 'an embedded newline' "$[2]" ;; + *) + at_fn_check_prepare_trace "$[2]" ;; + esac +} + +AS_FUNCTION_DESCRIBE([at_fn_filter_trace], [], +[Remove the lines in the file "$at_stderr" generated by "set -x" and print +them to stderr.]) +at_fn_filter_trace () +{ + mv "$at_stderr" "$at_stder1" + grep '^ *+' "$at_stder1" >&2 + grep -v '^ *+' "$at_stder1" >"$at_stderr" +} + +AS_FUNCTION_DESCRIBE([at_fn_log_failure], [FILE-LIST], +[Copy the files in the list on stdout with a "> " prefix, and exit the shell +with a failure exit code.]) +at_fn_log_failure () +{ + for file + do AS_ECHO(["$file:"]); sed 's/^/> /' "$file"; done + echo 1 > "$at_status_file" + exit 1 +} + +AS_FUNCTION_DESCRIBE([at_fn_check_skip], [EXIT-CODE LINE], +[Check whether EXIT-CODE is a special exit code (77 or 99), and if so exit +the test group subshell with that same exit code. Use LINE in any report +about test failure.]) +at_fn_check_skip () +{ + case $[1] in + 99) echo 99 > "$at_status_file"; at_failed=: + AS_ECHO(["$[2]: hard failure"]); exit 99;; + 77) echo 77 > "$at_status_file"; exit 77;; + esac +} + +AS_FUNCTION_DESCRIBE([at_fn_check_status], [EXPECTED EXIT-CODE LINE], +[Check whether EXIT-CODE is the EXPECTED exit code, and if so do nothing. +Otherwise, if it is 77 or 99, exit the test group subshell with that same +exit code; if it is anything else print an error message referring to LINE, +and fail the test.]) +at_fn_check_status () +{ +dnl This order ensures that we don't `skip' if we are precisely checking +dnl $? = 77 or $? = 99. + case $[2] in + $[1] ) ;; + 77) echo 77 > "$at_status_file"; exit 77;; + 99) echo 99 > "$at_status_file"; at_failed=: + AS_ECHO(["$[3]: hard failure"]); exit 99;; + *) AS_ECHO(["$[3]: exit code was $[2], expected $[1]"]) + at_failed=:;; + esac +} + +AS_FUNCTION_DESCRIBE([at_fn_diff_devnull], [FILE], +[Emit a diff between /dev/null and FILE. Uses "test -s" to avoid useless +diff invocations.]) +at_fn_diff_devnull () +{ + test -s "$[1]" || return 0 + $at_diff "$at_devnull" "$[1]" +} + +AS_FUNCTION_DESCRIBE([at_fn_test], [NUMBER], +[Parse out test NUMBER from the tail of this file.]) +at_fn_test () +{ + eval at_sed=\$at_sed$[1] + sed "$at_sed" "$at_myself" > "$at_test_source" +} + +AS_FUNCTION_DESCRIBE([at_fn_create_debugging_script], [], +[Create the debugging script $at_group_dir/run which will reproduce the +current test group.]) +at_fn_create_debugging_script () +{ + { + echo "#! /bin/sh" && + echo 'test "${ZSH_VERSION+set}" = set dnl +&& alias -g '\''${1+"$[@]"}'\''='\''"$[@]"'\''' && + AS_ECHO(["cd '$at_dir'"]) && + AS_ECHO(["exec \${CONFIG_SHELL-$SHELL} \"$at_myself\" -v -d ]dnl +[$at_debug_args $at_group \${1+\"\$[@]\"}"]) && + echo 'exit 1' + } >"$at_group_dir/run" && + chmod +x "$at_group_dir/run" +} + +m4_text_box([End of autotest shell functions.]) +m4_divert_pop([PREPARE_TESTS])dnl back to DEFAULTS + +# Not all shells have the 'times' builtin; the subshell is needed to make +# sure we discard the 'times: not found' message from the shell. +at_times_p=false +(times) >/dev/null 2>&1 && at_times_p=: + +# CLI Arguments to pass to the debugging scripts. +at_debug_args= +# -e sets to true +at_errexit_p=false +# Shall we be verbose? ':' means no, empty means yes. +at_verbose=: +at_quiet= +# Running several jobs in parallel, 0 means as many as test groups. +at_jobs=1 +at_traceon=: +at_trace_echo=: +at_check_filter_trace=: + +# Shall we keep the debug scripts? Must be `:' when the suite is +# run by a debug script, so that the script doesn't remove itself. +at_debug_p=false +# Display help message? +at_help_p=false +# Display the version message? +at_version_p=false +# List test groups? +at_list_p=false +# --clean +at_clean=false +# Test groups to run +at_groups= +# Whether to rerun failed tests. +at_recheck= +# Whether a write failure occurred +at_write_fail=0 + +# The directory we run the suite in. Default to . if no -C option. +at_dir=`pwd` +# An absolute reference to this testsuite script. +dnl m4-double quote, to preserve [] +[case $as_myself in + [\\/]* | ?:[\\/]* ) at_myself=$as_myself ;; + * ) at_myself=$at_dir/$as_myself ;; +esac] +# Whether -C is in effect. +at_change_dir=false +m4_divert_pop([DEFAULTS])dnl +m4_define([_AT_FINISH], +[m4_ifdef([AT_ingroup], [m4_fatal([missing AT_CLEANUP detected])])dnl +m4_divert_text([DEFAULTS], +[ +# Whether to enable colored test results. +at_color=m4_ifdef([AT_color], [AT_color], [no]) +# List of the tested programs. +at_tested='m4_ifdef([AT_tested], + [m4_translit(m4_dquote(m4_defn([AT_tested])), [ ], m4_newline)])' +# As many question marks as there are digits in the last test group number. +# Used to normalize the test group numbers so that `ls' lists them in +# numerical order. +at_format='m4_bpatsubst(m4_defn([AT_ordinal]), [.], [?])' +# Description of all the test groups. +at_help_all="AS_ESCAPE(m4_dquote(m4_defn([AT_help_all])))" +# List of the all the test groups. +at_groups_all=`AS_ECHO(["$at_help_all"]) | sed 's/;.*//'` + +AS_FUNCTION_DESCRIBE([at_fn_validate_ranges], [NAME...], +[Validate and normalize the test group number contained in each +variable NAME. Leading zeroes are treated as decimal.]) +at_fn_validate_ranges () +{ + for at_grp + do + eval at_value=\$$at_grp + if test $at_value -lt 1 || test $at_value -gt AT_ordinal; then + AS_ECHO(["invalid test group: $at_value"]) >&2 + exit 1 + fi + case $at_value in + 0*) # We want to treat leading 0 as decimal, like expr and test, but + # AS_VAR_ARITH treats it as octal if it uses $(( )). + # With XSI shells, ${at_value#${at_value%%[1-9]*}} avoids the + # expr fork, but it is not worth the effort to determine if the + # shell supports XSI when the user can just avoid leading 0. + eval $at_grp='`expr $at_value + 0`' ;; + esac + done +}])])dnl +m4_divert_push([PARSE_ARGS])dnl + +at_prev= +for at_option +do + # If the previous option needs an argument, assign it. + if test -n "$at_prev"; then + at_option=$at_prev=$at_option + at_prev= + fi + + case $at_option in + *=?*) at_optarg=`expr "X$at_option" : '[[^=]]*=\(.*\)'` ;; + *) at_optarg= ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $at_option in + --help | -h ) + at_help_p=: + ;; + + --list | -l ) + at_list_p=: + ;; + + --version | -V ) + at_version_p=: + ;; + + --clean | -c ) + at_clean=: + ;; + + --color ) + at_color=always + ;; + --color=* ) + case $at_optarg in + no | never | none) at_color=never ;; + auto | tty | if-tty) at_color=auto ;; + always | yes | force) at_color=always ;; + *) at_optname=`echo " $at_option" | sed 's/^ //; s/=.*//'` + AS_ERROR([unrecognized argument to $at_optname: $at_optarg]) ;; + esac + ;; + + --debug | -d ) + at_debug_p=: + ;; + + --errexit | -e ) + at_debug_p=: + at_errexit_p=: + ;; + + --verbose | -v ) + at_verbose=; at_quiet=: + ;; + + --trace | -x ) + at_traceon='set -x' + at_trace_echo=echo + at_check_filter_trace=at_fn_filter_trace + ;; + + [[0-9] | [0-9][0-9] | [0-9][0-9][0-9] | [0-9][0-9][0-9][0-9]]) + at_fn_validate_ranges at_option + AS_VAR_APPEND([at_groups], ["$at_option$as_nl"]) + ;; + + # Ranges + [[0-9]- | [0-9][0-9]- | [0-9][0-9][0-9]- | [0-9][0-9][0-9][0-9]-]) + at_range_start=`echo $at_option |tr -d X-` + at_fn_validate_ranges at_range_start + at_range=`AS_ECHO(["$at_groups_all"]) | \ + sed -ne '/^'$at_range_start'$/,$p'` + AS_VAR_APPEND([at_groups], ["$at_range$as_nl"]) + ;; + + [-[0-9] | -[0-9][0-9] | -[0-9][0-9][0-9] | -[0-9][0-9][0-9][0-9]]) + at_range_end=`echo $at_option |tr -d X-` + at_fn_validate_ranges at_range_end + at_range=`AS_ECHO(["$at_groups_all"]) | \ + sed -ne '1,/^'$at_range_end'$/p'` + AS_VAR_APPEND([at_groups], ["$at_range$as_nl"]) + ;; + + [[0-9]-[0-9] | [0-9]-[0-9][0-9] | [0-9]-[0-9][0-9][0-9]] | \ + [[0-9]-[0-9][0-9][0-9][0-9] | [0-9][0-9]-[0-9][0-9]] | \ + [[0-9][0-9]-[0-9][0-9][0-9] | [0-9][0-9]-[0-9][0-9][0-9][0-9]] | \ + [[0-9][0-9][0-9]-[0-9][0-9][0-9]] | \ + [[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]] | \ + [[0-9][0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]] ) + at_range_start=`expr $at_option : '\(.*\)-'` + at_range_end=`expr $at_option : '.*-\(.*\)'` + if test $at_range_start -gt $at_range_end; then + at_tmp=$at_range_end + at_range_end=$at_range_start + at_range_start=$at_tmp + fi + at_fn_validate_ranges at_range_start at_range_end + at_range=`AS_ECHO(["$at_groups_all"]) | \ + sed -ne '/^'$at_range_start'$/,/^'$at_range_end'$/p'` + AS_VAR_APPEND([at_groups], ["$at_range$as_nl"]) + ;; + + # Directory selection. + --directory | -C ) + at_prev=--directory + ;; + --directory=* ) + at_change_dir=: + at_dir=$at_optarg + if test x- = "x$at_dir" ; then + at_dir=./- + fi + ;; + + # Parallel execution. + --jobs | -j ) + at_jobs=0 + ;; + --jobs=* | -j[[0-9]]* ) + if test -n "$at_optarg"; then + at_jobs=$at_optarg + else + at_jobs=`expr X$at_option : 'X-j\(.*\)'` + fi + case $at_jobs in *[[!0-9]]*) + at_optname=`echo " $at_option" | sed 's/^ //; s/[[0-9=]].*//'` + AS_ERROR([non-numeric argument to $at_optname: $at_jobs]) ;; + esac + ;; + + # Keywords. + --keywords | -k ) + at_prev=--keywords + ;; + --keywords=* ) + at_groups_selected=$at_help_all + at_save_IFS=$IFS + IFS=, + set X $at_optarg + shift + IFS=$at_save_IFS + for at_keyword + do + at_invert= + case $at_keyword in + '!'*) + at_invert="-v" + at_keyword=`expr "X$at_keyword" : 'X!\(.*\)'` + ;; + esac + # It is on purpose that we match the test group titles too. + at_groups_selected=`AS_ECHO(["$at_groups_selected"]) | + grep -i $at_invert ["^[1-9][^;]*;.*[; ]$at_keyword[ ;]"]` + done + # Smash the keywords. + at_groups_selected=`AS_ECHO(["$at_groups_selected"]) | sed 's/;.*//'` + AS_VAR_APPEND([at_groups], ["$at_groups_selected$as_nl"]) + ;; + --recheck) + at_recheck=: + ;; +m4_divert_pop([PARSE_ARGS])dnl +dnl Process *=* last to allow for user specified --option=* type arguments. +m4_divert_push([PARSE_ARGS_END])dnl + + *=*) + at_envvar=`expr "x$at_option" : 'x\([[^=]]*\)='` + # Reject names that are not valid shell variable names. + case $at_envvar in + '' | [[0-9]]* | *[[!_$as_cr_alnum]]* ) + AS_ERROR([invalid variable name: `$at_envvar']) ;; + esac + at_value=`AS_ECHO(["$at_optarg"]) | sed "s/'/'\\\\\\\\''/g"` + # Export now, but save eval for later and for debug scripts. + export $at_envvar + AS_VAR_APPEND([at_debug_args], [" $at_envvar='$at_value'"]) + ;; + + *) AS_ECHO(["$as_me: invalid option: $at_option"]) >&2 + AS_ECHO(["Try \`$[0] --help' for more information."]) >&2 + exit 1 + ;; + esac +done + +# Verify our last option didn't require an argument +AS_IF([test -n "$at_prev"], [AS_ERROR([`$at_prev' requires an argument])]) + +# The file containing the suite. +at_suite_log=$at_dir/$as_me.log + +# Selected test groups. +if test -z "$at_groups$at_recheck"; then + at_groups=$at_groups_all +else + if test -n "$at_recheck" && test -r "$at_suite_log"; then + at_oldfails=`sed -n [' + /^Failed tests:$/,/^Skipped tests:$/{ + s/^[ ]*\([1-9][0-9]*\):.*/\1/p + } + /^Unexpected passes:$/,/^## Detailed failed tests/{ + s/^[ ]*\([1-9][0-9]*\):.*/\1/p + } + /^## Detailed failed tests/q + '] "$at_suite_log"` + AS_VAR_APPEND([at_groups], ["$at_oldfails$as_nl"]) + fi + # Sort the tests, removing duplicates. + at_groups=`AS_ECHO(["$at_groups"]) | sort -nu | sed '/^$/d'` +fi + +if test x"$at_color" = xalways \ + || { test x"$at_color" = xauto && test -t 1; }; then + at_red=`printf '\033@<:@0;31m'` + at_grn=`printf '\033@<:@0;32m'` + at_lgn=`printf '\033@<:@1;32m'` + at_blu=`printf '\033@<:@1;34m'` + at_std=`printf '\033@<:@m'` +else + at_red= at_grn= at_lgn= at_blu= at_std= +fi +m4_divert_pop([PARSE_ARGS_END])dnl +m4_divert_push([HELP])dnl + +# Help message. +if $at_help_p; then + cat <<_ATEOF || at_write_fail=1 +Usage: $[0] [[OPTION]... [VARIABLE=VALUE]... [TESTS]] + +Run all the tests, or the selected TESTS, given by numeric ranges, and +save a detailed log file. Upon failure, create debugging scripts. + +Do not change environment variables directly. Instead, set them via +command line arguments. Set \`AUTOTEST_PATH' to select the executables +to exercise. Each relative directory is expanded as build and source +directories relative to the top level of this distribution. +E.g., from within the build directory /tmp/foo-1.0, invoking this: + + $ $[0] AUTOTEST_PATH=bin + +is equivalent to the following, assuming the source directory is /src/foo-1.0: + + PATH=/tmp/foo-1.0/bin:/src/foo-1.0/bin:\$PATH $[0] +_ATEOF +m4_divert_pop([HELP])dnl +m4_divert_push([HELP_MODES])dnl +cat <<_ATEOF || at_write_fail=1 + +Operation modes: + -h, --help print the help message, then exit + -V, --version print version number, then exit + -c, --clean remove all the files this test suite might create and exit + -l, --list describes all the tests, or the selected TESTS +_ATEOF +m4_divert_pop([HELP_MODES])dnl +m4_wrap([m4_divert_push([HELP_TUNING_BEGIN])dnl +cat <<_ATEOF || at_write_fail=1 + +dnl extra quoting prevents emacs whitespace mode from putting tabs in output +Execution tuning: + -C, --directory=DIR +[ change to directory DIR before starting] + --color[[=never|auto|always]] +[ ]m4_ifdef([AT_color], + [disable colored test results, or enable even without terminal], + [enable colored test results on terminal, or always]) + -j, --jobs[[=N]] +[ Allow N jobs at once; infinite jobs with no arg (default 1)] + -k, --keywords=KEYWORDS +[ select the tests matching all the comma-separated KEYWORDS] +[ multiple \`-k' accumulate; prefixed \`!' negates a KEYWORD] + --recheck select all tests that failed or passed unexpectedly last time + -e, --errexit abort as soon as a test fails; implies --debug + -v, --verbose force more detailed output +[ default for debugging scripts] + -d, --debug inhibit clean up and top-level logging +[ default for debugging scripts] + -x, --trace enable tests shell tracing +_ATEOF +m4_divert_pop([HELP_TUNING_BEGIN])])dnl +m4_divert_push([HELP_END])dnl +cat <<_ATEOF || at_write_fail=1 + +Report bugs to <AT_PACKAGE_BUGREPORT>.dnl +m4_ifdef([AT_PACKAGE_NAME], +[m4_ifset([AT_PACKAGE_URL], [ +m4_defn([AT_PACKAGE_NAME]) home page: <AT_PACKAGE_URL>.])dnl +m4_if(m4_index(m4_defn([AT_PACKAGE_NAME]), [GNU ]), [0], [ +General help using GNU software: <http://www.gnu.org/gethelp/>.])]) +_ATEOF + exit $at_write_fail +fi + +# List of tests. +if $at_list_p; then + cat <<_ATEOF || at_write_fail=1 +AT_TESTSUITE_NAME test groups: + + NUM: FILE-NAME:LINE TEST-GROUP-NAME + KEYWORDS + +_ATEOF + # Pass an empty line as separator between selected groups and help. + AS_ECHO(["$at_groups$as_nl$as_nl$at_help_all"]) | + awk 'NF == 1 && FS != ";" { + selected[[$ 1]] = 1 + next + } + /^$/ { FS = ";" } + NF > 0 { + if (selected[[$ 1]]) { + printf " %3d: %-18s %s\n", $ 1, $ 2, $ 3 + if ($ 4) { + lmax = 79 + indent = " " + line = indent + len = length (line) + n = split ($ 4, a, " ") + for (i = 1; i <= n; i++) { + l = length (a[[i]]) + 1 + if (i > 1 && len + l > lmax) { + print line + line = indent " " a[[i]] + len = length (line) + } else { + line = line " " a[[i]] + len += l + } + } + if (n) + print line + } + } + }' || at_write_fail=1 + exit $at_write_fail +fi +m4_divert_pop([HELP_END])dnl +m4_divert_push([VERSION])dnl +if $at_version_p; then + AS_ECHO(["$as_me (AT_PACKAGE_STRING)"]) && + cat <<\_ATEOF || at_write_fail=1 +m4_divert_pop([VERSION])dnl +m4_divert_push([VERSION_END])dnl +_ATEOF + exit $at_write_fail +fi +m4_divert_pop([VERSION_END])dnl +m4_divert_push([TESTS_BEGIN])dnl + +# Take any -C into account. +if $at_change_dir ; then + test x != "x$at_dir" && cd "$at_dir" \ + || AS_ERROR([unable to change directory]) + at_dir=`pwd` +fi + +# Load the config files for any default variable assignments. +for at_file in atconfig atlocal +do + test -r $at_file || continue + . ./$at_file || AS_ERROR([invalid content: $at_file]) +done + +# Autoconf <=2.59b set at_top_builddir instead of at_top_build_prefix: +: "${at_top_build_prefix=$at_top_builddir}" + +# Perform any assignments requested during argument parsing. +eval "$at_debug_args" + +# atconfig delivers names relative to the directory the test suite is +# in, but the groups themselves are run in testsuite-dir/group-dir. +if test -n "$at_top_srcdir"; then + builddir=../.. + for at_dir_var in srcdir top_srcdir top_build_prefix + do + AS_VAR_COPY([at_val], [at_$at_dir_var]) + case $at_val in + [[\\/$]]* | ?:[[\\/]]* ) at_prefix= ;; + *) at_prefix=../../ ;; + esac + AS_VAR_SET([$at_dir_var], [$at_prefix$at_val]) + done +fi + +m4_text_box([Directory structure.]) + +# This is the set of directories and files used by this script +# (non-literals are capitalized): +# +# TESTSUITE - the testsuite +# TESTSUITE.log - summarizes the complete testsuite run +# TESTSUITE.dir/ - created during a run, remains after -d or failed test +# + at-groups/ - during a run: status of all groups in run +# | + NNN/ - during a run: meta-data about test group NNN +# | | + check-line - location (source file and line) of current AT_CHECK +# | | + status - exit status of current AT_CHECK +# | | + stdout - stdout of current AT_CHECK +# | | + stder1 - stderr, including trace +# | | + stderr - stderr, with trace filtered out +# | | + test-source - portion of testsuite that defines group +# | | + times - timestamps for computing duration +# | | + pass - created if group passed +# | | + xpass - created if group xpassed +# | | + fail - created if group failed +# | | + xfail - created if group xfailed +# | | + skip - created if group skipped +# + at-stop - during a run: end the run if this file exists +# + at-source-lines - during a run: cache of TESTSUITE line numbers for extraction +# + 0..NNN/ - created for each group NNN, remains after -d or failed test +# | + TESTSUITE.log - summarizes the group results +# | + ... - files created during the group + +# The directory the whole suite works in. +# Should be absolute to let the user `cd' at will. +at_suite_dir=$at_dir/$as_me.dir +# The file containing the suite ($at_dir might have changed since earlier). +at_suite_log=$at_dir/$as_me.log +# The directory containing helper files per test group. +at_helper_dir=$at_suite_dir/at-groups +# Stop file: if it exists, do not start new jobs. +at_stop_file=$at_suite_dir/at-stop +# The fifo used for the job dispatcher. +at_job_fifo=$at_suite_dir/at-job-fifo + +if $at_clean; then + test -d "$at_suite_dir" && + find "$at_suite_dir" -type d ! -perm -700 -exec chmod u+rwx \{\} \; + rm -f -r "$at_suite_dir" "$at_suite_log" + exit $? +fi + +# Don't take risks: use only absolute directories in PATH. +# +# For stand-alone test suites (ie. atconfig was not found), +# AUTOTEST_PATH is relative to `.'. +# +# For embedded test suites, AUTOTEST_PATH is relative to the top level +# of the package. Then expand it into build/src parts, since users +# may create executables in both places. +AUTOTEST_PATH=`AS_ECHO(["$AUTOTEST_PATH"]) | sed "s|:|$PATH_SEPARATOR|g"` +at_path= +_AS_PATH_WALK([$AUTOTEST_PATH $PATH], +[test -n "$at_path" && AS_VAR_APPEND([at_path], [$PATH_SEPARATOR]) +case $as_dir in + [[\\/]]* | ?:[[\\/]]* ) + AS_VAR_APPEND([at_path], ["$as_dir"]) + ;; + * ) + if test -z "$at_top_build_prefix"; then + # Stand-alone test suite. + AS_VAR_APPEND([at_path], ["$as_dir"]) + else + # Embedded test suite. + AS_VAR_APPEND([at_path], ["$at_top_build_prefix$as_dir$PATH_SEPARATOR"]) + AS_VAR_APPEND([at_path], ["$at_top_srcdir/$as_dir"]) + fi + ;; +esac]) + +# Now build and simplify PATH. +# +# There might be directories that don't exist, but don't redirect +# builtins' (eg., cd) stderr directly: Ultrix's sh hates that. +at_new_path= +_AS_PATH_WALK([$at_path], +[test -d "$as_dir" || continue +case $as_dir in + [[\\/]]* | ?:[[\\/]]* ) ;; + * ) as_dir=`(cd "$as_dir" && pwd) 2>/dev/null` ;; +esac +case $PATH_SEPARATOR$at_new_path$PATH_SEPARATOR in + *$PATH_SEPARATOR$as_dir$PATH_SEPARATOR*) ;; + $PATH_SEPARATOR$PATH_SEPARATOR) at_new_path=$as_dir ;; + *) AS_VAR_APPEND([at_new_path], ["$PATH_SEPARATOR$as_dir"]) ;; +esac]) +PATH=$at_new_path +export PATH + +# Setting up the FDs. +m4_define([AS_MESSAGE_LOG_FD], [5]) +dnl The parent needs two fds to the same fifo, otherwise, there is a race +dnl where the parent can read the fifo before a child opens it for writing +m4_define([AT_JOB_FIFO_IN_FD], [6]) +m4_define([AT_JOB_FIFO_OUT_FD], [7]) +[#] AS_MESSAGE_LOG_FD is the log file. Not to be overwritten if `-d'. +if $at_debug_p; then + at_suite_log=/dev/null +else + : >"$at_suite_log" +fi +exec AS_MESSAGE_LOG_FD>>"$at_suite_log" + +# Banners and logs. +AS_BOX(m4_defn([AT_TESTSUITE_NAME])[.]) +{ + AS_BOX(m4_defn([AT_TESTSUITE_NAME])[.]) + echo + + AS_ECHO(["$as_me: command line was:"]) + AS_ECHO([" \$ $[0] $at_cli_args"]) + echo + + # If ChangeLog exists, list a few lines in case it might help determining + # the exact version. + if test -n "$at_top_srcdir" && test -f "$at_top_srcdir/ChangeLog"; then + AS_BOX([ChangeLog.]) + echo + sed 's/^/| /;10q' "$at_top_srcdir/ChangeLog" + echo + fi + + AS_UNAME + echo + + # Contents of the config files. + for at_file in atconfig atlocal + do + test -r $at_file || continue + AS_ECHO(["$as_me: $at_file:"]) + sed 's/^/| /' $at_file + echo + done +} >&AS_MESSAGE_LOG_FD + +m4_divert_pop([TESTS_BEGIN])dnl +m4_divert_push([PREPARE_TESTS])dnl +{ + AS_BOX([Tested programs.]) + echo +} >&AS_MESSAGE_LOG_FD + +# Report what programs are being tested. +for at_program in : $at_tested +do + test "$at_program" = : && continue + case $at_program in + [[\\/]* | ?:[\\/]* ) $at_program_=$at_program ;;] + * ) + _AS_PATH_WALK([$PATH], [test -f "$as_dir/$at_program" && break]) + at_program_=$as_dir/$at_program ;; + esac + if test -f "$at_program_"; then + { + AS_ECHO(["$at_srcdir/AT_LINE: $at_program_ --version"]) + "$at_program_" --version </dev/null + echo + } >&AS_MESSAGE_LOG_FD 2>&1 + else + AS_ERROR([cannot find $at_program]) + fi +done + +{ + AS_BOX([Running the tests.]) +} >&AS_MESSAGE_LOG_FD + +at_start_date=`date` +at_start_time=`date +%s 2>/dev/null` +AS_ECHO(["$as_me: starting at: $at_start_date"]) >&AS_MESSAGE_LOG_FD +m4_divert_pop([PREPARE_TESTS])dnl +m4_divert_push([TESTS])dnl + +# Create the master directory if it doesn't already exist. +AS_MKDIR_P(["$at_suite_dir"]) || + AS_ERROR([cannot create `$at_suite_dir']) + +# Can we diff with `/dev/null'? DU 5.0 refuses. +if diff /dev/null /dev/null >/dev/null 2>&1; then + at_devnull=/dev/null +else + at_devnull=$at_suite_dir/devnull + >"$at_devnull" +fi + +# Use `diff -u' when possible. +if at_diff=`diff -u "$at_devnull" "$at_devnull" 2>&1` && test -z "$at_diff" +then + at_diff='diff -u' +else + at_diff=diff +fi + +# Get the last needed group. +for at_group in : $at_groups; do :; done + +# Extract the start and end lines of each test group at the tail +# of this file +awk ' +BEGIN { FS="" } +/^@%:@AT_START_/ { + start = NR +} +/^@%:@AT_STOP_/ { + test = substr ($ 0, 10) + print "at_sed" test "=\"1," start "d;" (NR-1) "q\"" + if (test == "'"$at_group"'") exit +}' "$at_myself" > "$at_suite_dir/at-source-lines" && +. "$at_suite_dir/at-source-lines" || + AS_ERROR([cannot create test line number cache]) +rm -f "$at_suite_dir/at-source-lines" + +# Set number of jobs for `-j'; avoid more jobs than test groups. +set X $at_groups; shift; at_max_jobs=$[@%:@] +if test $at_max_jobs -eq 0; then + at_jobs=1 +fi +if test $at_jobs -ne 1 && + { test $at_jobs -eq 0 || test $at_jobs -gt $at_max_jobs; }; then + at_jobs=$at_max_jobs +fi + +# If parallel mode, don't output banners, don't split summary lines. +if test $at_jobs -ne 1; then + at_print_banners=false + at_quiet=: +fi + +# Set up helper dirs. +rm -rf "$at_helper_dir" && +mkdir "$at_helper_dir" && +cd "$at_helper_dir" && +{ test -z "$at_groups" || mkdir $at_groups; } || +AS_ERROR([testsuite directory setup failed]) + +# Functions for running a test group. We leave the actual +# test group execution outside of a shell function in order +# to avoid hitting zsh 4.x exit status bugs. + +AS_FUNCTION_DESCRIBE([at_fn_group_prepare], [], +[Prepare for running a test group.]) +at_fn_group_prepare () +{ + # The directory for additional per-group helper files. + at_job_dir=$at_helper_dir/$at_group + # The file containing the location of the last AT_CHECK. + at_check_line_file=$at_job_dir/check-line + # The file containing the exit status of the last command. + at_status_file=$at_job_dir/status + # The files containing the output of the tested commands. + at_stdout=$at_job_dir/stdout + at_stder1=$at_job_dir/stder1 + at_stderr=$at_job_dir/stderr + # The file containing the code for a test group. + at_test_source=$at_job_dir/test-source + # The file containing dates. + at_times_file=$at_job_dir/times + + # Be sure to come back to the top test directory. + cd "$at_suite_dir" + + # Clearly separate the test groups when verbose. + $at_first || $at_verbose echo + + at_group_normalized=$at_group + _AT_NORMALIZE_TEST_GROUP_NUMBER(at_group_normalized) + + # Create a fresh directory for the next test group, and enter. + # If one already exists, the user may have invoked ./run from + # within that directory; we remove the contents, but not the + # directory itself, so that we aren't pulling the rug out from + # under the shell's notion of the current directory. + at_group_dir=$at_suite_dir/$at_group_normalized + at_group_log=$at_group_dir/$as_me.log + _AS_CLEAN_DIR("$at_group_dir") || + AS_WARN([test directory for $at_group_normalized could not be cleaned]) + # Be tolerant if the above `rm' was not able to remove the directory. + AS_MKDIR_P(["$at_group_dir"]) + + echo 0 > "$at_status_file" + + # In verbose mode, append to the log file *and* show on + # the standard output; in quiet mode only write to the log. + if test -z "$at_verbose"; then + at_tee_pipe='tee -a "$at_group_log"' + else + at_tee_pipe='cat >> "$at_group_log"' + fi +} + +AS_FUNCTION_DESCRIBE([at_fn_group_banner], [[ORDINAL LINE DESC PAD [BANNER]]], +[Declare the test group ORDINAL, located at LINE with group description +DESC, and residing under BANNER. Use PAD to align the status column.]) +at_fn_group_banner () +{ + at_setup_line="$[2]" + test -n "$[5]" && at_fn_banner $[5] + at_desc="$[3]" + case $[1] in + [[0-9]]) at_desc_line=" $[1]: ";; + [[0-9][0-9]]) at_desc_line=" $[1]: " ;; + [*]) at_desc_line="$[1]: " ;; + esac + AS_VAR_APPEND([at_desc_line], ["$[3]$[4]"]) + $at_quiet AS_ECHO_N(["$at_desc_line"]) + echo "# -*- compilation -*-" >> "$at_group_log" +} + +AS_FUNCTION_DESCRIBE([at_fn_group_postprocess], [], +[Perform cleanup after running a test group.]) +at_fn_group_postprocess () +{ + # Be sure to come back to the suite directory, in particular + # since below we might `rm' the group directory we are in currently. + cd "$at_suite_dir" + + if test ! -f "$at_check_line_file"; then + sed "s/^ */$as_me: WARNING: /" <<_ATEOF + A failure happened in a test group before any test could be + run. This means that test suite is improperly designed. Please + report this failure to <AT_PACKAGE_BUGREPORT>. +_ATEOF + AS_ECHO(["$at_setup_line"]) >"$at_check_line_file" + at_status=99 + fi + $at_verbose AS_ECHO_N(["$at_group. $at_setup_line: "]) + AS_ECHO_N(["$at_group. $at_setup_line: "]) >> "$at_group_log" + case $at_xfail:$at_status in + yes:0) + at_msg="UNEXPECTED PASS" + at_res=xpass + at_errexit=$at_errexit_p + at_color=$at_red + ;; + no:0) + at_msg="ok" + at_res=pass + at_errexit=false + at_color=$at_grn + ;; + *:77) + at_msg='skipped ('`cat "$at_check_line_file"`')' + at_res=skip + at_errexit=false + at_color=$at_blu + ;; + no:* | *:99) + at_msg='FAILED ('`cat "$at_check_line_file"`')' + at_res=fail + at_errexit=$at_errexit_p + at_color=$at_red + ;; + yes:*) + at_msg='expected failure ('`cat "$at_check_line_file"`')' + at_res=xfail + at_errexit=false + at_color=$at_lgn + ;; + esac + echo "$at_res" > "$at_job_dir/$at_res" + # In parallel mode, output the summary line only afterwards. + if test $at_jobs -ne 1 && test -n "$at_verbose"; then + AS_ECHO(["$at_desc_line $at_color$at_msg$at_std"]) + else + # Make sure there is a separator even with long titles. + AS_ECHO([" $at_color$at_msg$at_std"]) + fi + at_log_msg="$at_group. $at_desc ($at_setup_line): $at_msg" + case $at_status in + 0|77) + # $at_times_file is only available if the group succeeded. + # We're not including the group log, so the success message + # is written in the global log separately. But we also + # write to the group log in case they're using -d. + if test -f "$at_times_file"; then + at_log_msg="$at_log_msg ("`sed 1d "$at_times_file"`')' + rm -f "$at_times_file" + fi + AS_ECHO(["$at_log_msg"]) >> "$at_group_log" + AS_ECHO(["$at_log_msg"]) >&AS_MESSAGE_LOG_FD + + # Cleanup the group directory, unless the user wants the files + # or the success was unexpected. + if $at_debug_p || test $at_res = xpass; then + at_fn_create_debugging_script + if test $at_res = xpass && $at_errexit; then + echo stop > "$at_stop_file" + fi + else + if test -d "$at_group_dir"; then + find "$at_group_dir" -type d ! -perm -700 -exec chmod u+rwx \{\} \; + rm -fr "$at_group_dir" + fi + rm -f "$at_test_source" + fi + ;; + *) + # Upon failure, include the log into the testsuite's global + # log. The failure message is written in the group log. It + # is later included in the global log. + AS_ECHO(["$at_log_msg"]) >> "$at_group_log" + + # Upon failure, keep the group directory for autopsy, and create + # the debugging script. With -e, do not start any further tests. + at_fn_create_debugging_script + if $at_errexit; then + echo stop > "$at_stop_file" + fi + ;; + esac +} + + +m4_text_box([Driver loop.]) + +dnl Catching signals correctly: +dnl +dnl The first idea was: trap the signal, send it to all spawned jobs, +dnl then reset the handler and reraise the signal for ourselves. +dnl However, before exiting, ksh will then send the signal to all +dnl process group members, potentially killing the outer testsuite +dnl and/or the 'make' process driving us. +dnl So now the strategy is: trap the signal, send it to all spawned jobs, +dnl then exit the script with the right status. +dnl +dnl In order to let the jobs know about the signal, we cannot just send it +dnl to the current process group (kill $SIG 0), for the same reason as above. +dnl Also, it does not reliably stop the suite to send the signal to the +dnl spawned processes, because they might not transport it further +dnl (maybe this can be fixed?). +dnl +dnl So what we do is enable shell job control if available, which causes the +dnl shell to start each parallel task as its own shell job, thus as a new +dnl process group leader. We then send the signal to all new process groups. + +dnl Do we have job control? +if (set -m && set +m && set +b) >/dev/null 2>&1; then + set +b + at_job_control_on='set -m' at_job_control_off='set +m' at_job_group=- +else + at_job_control_on=: at_job_control_off=: at_job_group= +fi + +for at_signal in 1 2 15; do +dnl This signal handler is not suitable for PIPE: it causes writes. +dnl The code that was interrupted may have the errexit, monitor, or xtrace +dnl flags enabled, so sanitize. + trap 'set +x; set +e + $at_job_control_off + at_signal='"$at_signal"' +dnl Safety belt: even with runaway processes, prevent starting new jobs. + echo stop > "$at_stop_file" +dnl Do not enter this area multiple times, do not kill self prematurely. + trap "" $at_signal +dnl Gather process group IDs of currently running jobs. + at_pgids= + for at_pgid in `jobs -p 2>/dev/null`; do + at_pgids="$at_pgids $at_job_group$at_pgid" + done +dnl Ignore `kill' errors, as some jobs may have finished in the meantime. + test -z "$at_pgids" || kill -$at_signal $at_pgids 2>/dev/null +dnl wait until all jobs have exited. + wait +dnl Status output. Do this after waiting for the jobs, for ordered output. +dnl Avoid scribbling onto the end of a possibly incomplete line. + if test "$at_jobs" -eq 1 || test -z "$at_verbose"; then + echo >&2 + fi + at_signame=`kill -l $at_signal 2>&1 || echo $at_signal` + set x $at_signame + test $# -gt 2 && at_signame=$at_signal + AS_WARN([caught signal $at_signame, bailing out]) +dnl Do not reinstall the default handler here and reraise the signal to +dnl let the default handler do its job, see the note about ksh above. +dnl trap - $at_signal +dnl kill -$at_signal $$ +dnl Instead, exit with appropriate status. + AS_VAR_ARITH([exit_status], [128 + $at_signal]) + AS_EXIT([$exit_status])' $at_signal +done + +rm -f "$at_stop_file" +at_first=: + +if test $at_jobs -ne 1 && + rm -f "$at_job_fifo" && + test -n "$at_job_group" && + ( mkfifo "$at_job_fifo" && trap 'exit 1' PIPE STOP TSTP ) 2>/dev/null +then + # FIFO job dispatcher. + +dnl Since we use job control, we need to propagate TSTP. +dnl This handler need not be used for serial execution. +dnl Again, we should stop all processes in the job groups, otherwise +dnl the stopping will not be effective while one test group is running. +dnl Apparently ksh does not honor the TSTP trap. +dnl As a safety measure, not use the same variable names as in the +dnl termination handlers above, one might get called during execution +dnl of the other. + trap 'at_pids= + for at_pid in `jobs -p`; do + at_pids="$at_pids $at_job_group$at_pid" + done +dnl Send it to all spawned jobs, ignoring those finished meanwhile. + if test -n "$at_pids"; then +dnl Unfortunately, ksh93 fork-bombs when we send TSTP, so send STOP +dnl if this might be ksh (STOP prevents possible TSTP handlers inside +dnl AT_CHECKs from running). Then stop ourselves. + at_sig=TSTP + test "${TMOUT+set}" = set && at_sig=STOP + kill -$at_sig $at_pids 2>/dev/null + fi + kill -STOP $$ +dnl We got a CONT, so let's go again. Passing this to all processes +dnl in the groups is necessary (because we stopped them), but it may +dnl cause changed test semantics; e.g., a sleep will be interrupted. + test -z "$at_pids" || kill -CONT $at_pids 2>/dev/null' TSTP + + echo + # Turn jobs into a list of numbers, starting from 1. + at_joblist=`AS_ECHO(["$at_groups"]) | sed -n 1,${at_jobs}p` + + set X $at_joblist + shift + for at_group in $at_groups; do +dnl Enable job control only for spawning the test group: +dnl Let the jobs to run in separate process groups, but +dnl avoid all the status output by the shell. + $at_job_control_on 2>/dev/null + ( + # Start one test group. + $at_job_control_off +dnl First child must open the fifo to avoid blocking parent; all other +dnl children inherit it already opened from the parent. + if $at_first; then + exec AT_JOB_FIFO_OUT_FD>"$at_job_fifo" + else +dnl Children do not need parent's copy of fifo. + exec AT_JOB_FIFO_IN_FD<&- + fi +dnl When a child receives PIPE, be sure to write back the token, +dnl so the master does not hang waiting for it. +dnl errexit and xtrace should not be set in this shell instance, +dnl except as debug measures. However, shells such as dash may +dnl optimize away the _AT_CHECK subshell, so normalize here. + trap 'set +x; set +e +dnl Ignore PIPE signals that stem from writing back the token. + trap "" PIPE + echo stop > "$at_stop_file" + echo >&AT_JOB_FIFO_OUT_FD +dnl Do not reraise the default PIPE handler. +dnl It wreaks havoc with ksh, see above. +dnl trap - 13 +dnl kill -13 $$ + AS_EXIT([141])' PIPE + at_fn_group_prepare + if cd "$at_group_dir" && + at_fn_test $at_group && + . "$at_test_source" + then :; else + AS_WARN([unable to parse test group: $at_group]) + at_failed=: + fi + at_fn_group_postprocess + echo >&AT_JOB_FIFO_OUT_FD + ) & + $at_job_control_off + if $at_first; then + at_first=false + exec AT_JOB_FIFO_IN_FD<"$at_job_fifo" AT_JOB_FIFO_OUT_FD>"$at_job_fifo" + fi + shift # Consume one token. + if test $[@%:@] -gt 0; then :; else + read at_token <&AT_JOB_FIFO_IN_FD || break + set x $[*] + fi + test -f "$at_stop_file" && break + done + exec AT_JOB_FIFO_OUT_FD>&- + # Read back the remaining ($at_jobs - 1) tokens. + set X $at_joblist + shift + if test $[@%:@] -gt 0; then + shift + for at_job + do + read at_token + done <&AT_JOB_FIFO_IN_FD + fi + exec AT_JOB_FIFO_IN_FD<&- + wait +else + # Run serially, avoid forks and other potential surprises. + for at_group in $at_groups; do + at_fn_group_prepare + if cd "$at_group_dir" && + at_fn_test $at_group && + . "$at_test_source"; then :; else + AS_WARN([unable to parse test group: $at_group]) + at_failed=: + fi + at_fn_group_postprocess + test -f "$at_stop_file" && break + at_first=false + done +fi + +# Wrap up the test suite with summary statistics. +cd "$at_helper_dir" + +# Use ?..???? when the list must remain sorted, the faster * otherwise. +at_pass_list=`for f in */pass; do echo $f; done | sed '/\*/d; s,/pass,,'` +at_skip_list=`for f in */skip; do echo $f; done | sed '/\*/d; s,/skip,,'` +at_xfail_list=`for f in */xfail; do echo $f; done | sed '/\*/d; s,/xfail,,'` +at_xpass_list=`for f in ?/xpass ??/xpass ???/xpass ????/xpass; do + echo $f; done | sed '/?/d; s,/xpass,,'` +at_fail_list=`for f in ?/fail ??/fail ???/fail ????/fail; do + echo $f; done | sed '/?/d; s,/fail,,'` + +set X $at_pass_list $at_xpass_list $at_xfail_list $at_fail_list $at_skip_list +shift; at_group_count=$[@%:@] +set X $at_xpass_list; shift; at_xpass_count=$[@%:@]; at_xpass_list=$[*] +set X $at_xfail_list; shift; at_xfail_count=$[@%:@] +set X $at_fail_list; shift; at_fail_count=$[@%:@]; at_fail_list=$[*] +set X $at_skip_list; shift; at_skip_count=$[@%:@] + +AS_VAR_ARITH([at_run_count], [$at_group_count - $at_skip_count]) +AS_VAR_ARITH([at_unexpected_count], [$at_xpass_count + $at_fail_count]) +AS_VAR_ARITH([at_total_fail_count], [$at_xfail_count + $at_fail_count]) + +# Back to the top directory. +cd "$at_dir" +rm -rf "$at_helper_dir" + +# Compute the duration of the suite. +at_stop_date=`date` +at_stop_time=`date +%s 2>/dev/null` +AS_ECHO(["$as_me: ending at: $at_stop_date"]) >&AS_MESSAGE_LOG_FD +case $at_start_time,$at_stop_time in + [[0-9]*,[0-9]*]) + AS_VAR_ARITH([at_duration_s], [$at_stop_time - $at_start_time]) + AS_VAR_ARITH([at_duration_m], [$at_duration_s / 60]) + AS_VAR_ARITH([at_duration_h], [$at_duration_m / 60]) + AS_VAR_ARITH([at_duration_s], [$at_duration_s % 60]) + AS_VAR_ARITH([at_duration_m], [$at_duration_m % 60]) + at_duration="${at_duration_h}h ${at_duration_m}m ${at_duration_s}s" + AS_ECHO(["$as_me: test suite duration: $at_duration"]) >&AS_MESSAGE_LOG_FD + ;; +esac + +echo +AS_BOX([Test results.]) +echo +{ + echo + AS_BOX([Test results.]) + echo +} >&AS_MESSAGE_LOG_FD + +dnl +dnl FIXME: this code is as far from i18n-cleanness as man +dnl could imagine... +dnl +if test $at_run_count = 1; then + at_result="1 test" + at_were=was +else + at_result="$at_run_count tests" + at_were=were +fi +if $at_errexit_p && test $at_unexpected_count != 0; then + if test $at_xpass_count = 1; then + at_result="$at_result $at_were run, one passed" + else + at_result="$at_result $at_were run, one failed" + fi + at_result="$at_result unexpectedly and inhibited subsequent tests." + at_color=$at_red +else + # Don't you just love exponential explosion of the number of cases? + at_color=$at_red + case $at_xpass_count:$at_fail_count:$at_xfail_count in + # So far, so good. + 0:0:0) at_result="$at_result $at_were successful." at_color=$at_grn ;; + 0:0:*) at_result="$at_result behaved as expected." at_color=$at_lgn ;; + + # Some unexpected failures + 0:*:0) at_result="$at_result $at_were run, +$at_fail_count failed unexpectedly." ;; + + # Some failures, both expected and unexpected + 0:*:1) at_result="$at_result $at_were run, +$at_total_fail_count failed ($at_xfail_count expected failure)." ;; + 0:*:*) at_result="$at_result $at_were run, +$at_total_fail_count failed ($at_xfail_count expected failures)." ;; + + # No unexpected failures, but some xpasses + *:0:*) at_result="$at_result $at_were run, +$at_xpass_count passed unexpectedly." ;; + + # No expected failures, but failures and xpasses + *:1:0) at_result="$at_result $at_were run, +$at_unexpected_count did not behave as expected dnl +($at_fail_count unexpected failure)." ;; + *:*:0) at_result="$at_result $at_were run, +$at_unexpected_count did not behave as expected dnl +($at_fail_count unexpected failures)." ;; + + # All of them. + *:*:1) at_result="$at_result $at_were run, +$at_xpass_count passed unexpectedly, +$at_total_fail_count failed ($at_xfail_count expected failure)." ;; + *:*:*) at_result="$at_result $at_were run, +$at_xpass_count passed unexpectedly, +$at_total_fail_count failed ($at_xfail_count expected failures)." ;; + esac + + if test $at_skip_count = 0 && test $at_run_count -gt 1; then + at_result="All $at_result" + fi +fi + +# Now put skips in the mix. +case $at_skip_count in + 0) ;; + 1) at_result="$at_result +1 test was skipped." ;; + *) at_result="$at_result +$at_skip_count tests were skipped." ;; +esac + +if test $at_unexpected_count = 0; then + echo "$at_color$at_result$at_std" + echo "$at_result" >&AS_MESSAGE_LOG_FD +else + echo "${at_color}ERROR: $at_result$at_std" >&2 + echo "ERROR: $at_result" >&AS_MESSAGE_LOG_FD + { + echo + AS_BOX([Summary of the failures.]) + + # Summary of failed and skipped tests. + if test $at_fail_count != 0; then + echo "Failed tests:" + $SHELL "$at_myself" $at_fail_list --list + echo + fi + if test $at_skip_count != 0; then + echo "Skipped tests:" + $SHELL "$at_myself" $at_skip_list --list + echo + fi + if test $at_xpass_count != 0; then + echo "Unexpected passes:" + $SHELL "$at_myself" $at_xpass_list --list + echo + fi + if test $at_fail_count != 0; then + AS_BOX([Detailed failed tests.]) + echo + for at_group in $at_fail_list + do + at_group_normalized=$at_group + _AT_NORMALIZE_TEST_GROUP_NUMBER(at_group_normalized) + cat "$at_suite_dir/$at_group_normalized/$as_me.log" + echo + done + echo + fi + if test -n "$at_top_srcdir"; then + AS_BOX([${at_top_build_prefix}config.log]) + sed 's/^/| /' ${at_top_build_prefix}config.log + echo + fi + } >&AS_MESSAGE_LOG_FD + + AS_BOX([$as_me.log was created.]) + + echo + if $at_debug_p; then + at_msg='per-test log files' + else + at_msg="\`${at_testdir+${at_testdir}/}$as_me.log'" + fi + AS_ECHO(["Please send $at_msg and all information you think might help: + + To: <AT_PACKAGE_BUGREPORT> + Subject: @<:@AT_PACKAGE_STRING@:>@ $as_me: dnl +$at_fail_list${at_fail_list:+ failed${at_xpass_list:+, }}dnl +$at_xpass_list${at_xpass_list:+ passed unexpectedly} + +You may investigate any problem if you feel able to do so, in which +case the test suite provides a good starting point. Its output may +be found below \`${at_testdir+${at_testdir}/}$as_me.dir'. +"]) + exit 1 +fi + +exit 0 + +m4_text_box([Actual tests.]) +m4_divert_pop([TESTS])dnl +dnl End of AT_INIT: divert to KILL, only test groups are to be +dnl output, the rest is ignored. Current diversion is BODY, inherited +dnl from M4sh. +m4_divert([KILL]) +])# AT_INIT + + +# _AT_ARG_OPTION(OPTIONS,HELP-TEXT,[ARGS],[ACTION-IF-GIVEN], +# [ACTION-IF-NOT-GIVEN]) +# ---------------------------------------------------------- +# Internal implementation of AT_ARG_OPTION & AT_ARG_OPTION_ARG +m4_defun([_AT_ARG_OPTION], +[m4_divert_once([HELP_OTHER], +[cat <<_ATEOF || at_write_fail=1 + +Other options: +_ATEOF +])dnl m4_divert_once HELP_OTHER +m4_divert_text([HELP_OTHER], +[cat <<_ATEOF || at_write_fail=1 +$2 +_ATEOF])dnl +dnl Turn our options into our desired strings +m4_ifdef([AT_first_option],[m4_undefine([AT_first_option])])dnl +m4_ifdef([AT_case],[m4_undefine([AT_case])])dnl +m4_ifdef([AT_case_no],[m4_undefine([AT_case_no])])dnl +m4_ifdef([AT_case_arg],[m4_undefine([AT_case_arg])])dnl +m4_foreach([AT_option], m4_split(m4_normalize([$1]),[[ \|]+]), +[m4_define_default([AT_first_option],AT_option)dnl +m4_define_default([AT_first_option_tr], + [m4_bpatsubst(m4_defn([AT_first_option]), -, [_])])dnl +m4_append([AT_case],m4_if(m4_len(AT_option),1,[],[-])[-]AT_option, [ | ])dnl +m4_append([AT_case_no],[--no-]AT_option, [ | ])dnl +m4_append([AT_case_arg], + m4_if(m4_len(AT_option),1,[],[-])[-]AT_option[=*], [ | ])dnl +])dnl m4_foreach AT_option +dnl keep track so we or the user may process ACTION-IF-NOT-GIVEN +m4_divert_once([PARSE_ARGS_BEGIN], +[ +## +## Set up package specific options. +## +])dnl +m4_divert_text([PARSE_ARGS_BEGIN], +[dnl Provide a default value for options without arguments. +m4_ifvaln([$3],,[at_arg_[]AT_first_option_tr=false])dnl +at_arg_given_[]AT_first_option_tr=false +])dnl m4_divert_text DEFAULTS +m4_divert_text([PARSE_ARGS], +[dnl Parse the options and args when necessary. +m4_ifvaln([$3], +[ AT_case ) + at_prev=--AT_first_option_tr + ;; + AT_case_arg ) + at_arg_[]AT_first_option_tr=$at_optarg + at_arg_given_[]AT_first_option_tr=: + $4 + ;;], +[ AT_case ) + at_optarg=: + at_arg_[]AT_first_option_tr=: + at_arg_given_[]AT_first_option_tr=: + m4_ifval([$4],[$4])[]dnl + ;; + AT_case_no ) + at_optarg=false + at_arg_[]AT_first_option_tr=false + at_arg_given_[]AT_first_option_tr=: + m4_ifval([$4],[$4])[]dnl + ;;])dnl m4_ifvaln $3 +])dnl m4_divert_text PARSE_ARGS +m4_ifvaln([$5], +[m4_divert_once([PARSE_ARGS_END], +[ +## +## Process package specific options when _not_ supplied. +##])dnl m4_divert_once PARSE_ARGS_END +m4_divert_text([PARSE_ARGS_END], +[ +AS_IF([$at_arg_given_[]AT_first_option_tr],,[$5])dnl +])dnl m4_divert_text PARSE_ARGS_END +])dnl m4_ifvaln $5 +])dnl _AT_ARG_OPTION + + +# AT_ARG_OPTION(OPTIONS,HELP-TEXT,[ACTION-IF-GIVEN],[ACTION-IF-NOT-GIVEN]) +# ------------------------------------------------------------------------ +# Accept a list of space-separated OPTIONS, all aliases of the first one. +# Add HELP-TEXT to the HELP_OTHER diversion. +# +# Leading dashes should not be passed in OPTIONS. Users will be required +# to pass `--' before long options and `-' before single character options. +# +# $at_arg_OPTION will be set to `:' if this option is received, `false' if +# if --no-OPTION is received, and `false' by default. +# +# Run ACTION-IF-GIVEN each time an option in OPTIONS is encountered; here, +# $at_optarg will be set to `:' or `false' as appropriate. $at_optarg is +# actually just a copy of $at_arg_OPTION. +# +# ACTION-IF-NOT-GIVEN will be run once after option parsing is complete and +# if no option from OPTIONS was used. +m4_defun([AT_ARG_OPTION],[_AT_ARG_OPTION([$1],[$2],,[$3],[$4])]) + + +# AT_ARG_OPTION_ARG(OPTIONS,HELP-TEXT,[ACTION-IF-GIVEN],[ACTION-IF-NOT-GIVEN]) +# ---------------------------------------------------------------------------- +# Accept a set of space-separated OPTIONS with arguments, all aliases of the +# first one. Add HELP-TEXT to the HELP_OTHER diversion. +# +# Leading dashes should not be passed in OPTIONS. Users will be required +# to pass `--' before long options and `-' before single character options. +# +# By default, any argument to these options will be assigned to the shell +# variable $at_arg_OPTION, where OPTION is the first option in OPTIONS with +# any `-' characters replaced with `_'. +# +# Run ACTION-IF-GIVEN each time an option in OPTIONS is encountered; here, +# $at_optarg will be set to the option argument. $at_optarg is actually just +# a copy of $at_arg_OPTION. +# +# ACTION-IF-NOT-GIVEN will be run once after option parsing is complete +# and if no option from OPTIONS was used. +m4_defun([AT_ARG_OPTION_ARG],[_AT_ARG_OPTION([$1],[$2],1,[$3],[$4])]) + + +# AT_TESTED(PROGRAMS) +# ------------------- +# Specify the list of programs exercised by the test suite. Their +# versions are logged, and in the case of embedded test suite, they +# must correspond to the version of the package. PATH should be +# already preset so the proper executable will be selected. +m4_define([AT_TESTED], +[m4_append_uniq_w([AT_tested], [$1])]) + + +# AT_COPYRIGHT(TEXT, [FILTER = m4_newline]) +# ----------------------------------------- +# Emit TEXT, a copyright notice, in the top of the test suite and in +# --version output. Macros in TEXT are evaluated once. Process +# the --version output through FILTER (m4_newline, m4_do, and +# m4_copyright_condense are common filters). +m4_define([AT_COPYRIGHT], +[AS_COPYRIGHT([$1])[]]dnl +[m4_divert_text([VERSION_NOTICES], +[m4_default([$2], [m4_newline])([$1])])])# AT_COPYRIGHT + + +# AT_COLOR_TESTS +# -------------- +# Enable colored test results if standard error is connected to a terminal. +m4_define([AT_COLOR_TESTS], +[m4_define([AT_color], [auto])]) + +# AT_SETUP(DESCRIPTION) +# --------------------- +# Start a group of related tests, all to be executed in the same subshell. +# The group is testing what DESCRIPTION says. +_AT_DEFINE_INIT([AT_SETUP], +[m4_ifdef([AT_ingroup], [m4_fatal([$0: nested AT_SETUP detected])], + [m4_define([AT_ingroup], [AS_ECHO(["$at_setup_line"]) >"$at_check_line_file" +])]) +m4_ifdef([AT_keywords], [m4_undefine([AT_keywords])]) +m4_define([AT_capture_files], []) +m4_define([AT_line], AT_LINE) +m4_define([AT_xfail], [at_xfail=no]) +m4_define([AT_description], m4_expand([$1])) +m4_define([AT_ordinal], m4_incr(AT_ordinal)) +m4_divert_push([TEST_GROUPS])dnl +[#AT_START_]AT_ordinal +at_fn_group_banner AT_ordinal 'm4_defn([AT_line])' \ + "AS_ESCAPE(m4_dquote(m4_defn([AT_description])))" m4_format(["%*s"], + m4_max(0, m4_eval(47 - m4_qlen(m4_defn([AT_description])))), [])m4_if( + AT_banner_ordinal, [0], [], [ AT_banner_ordinal]) +m4_divert_push([TEST_SCRIPT])dnl +]) + + +# AT_FAIL_IF(SHELL-EXPRESSION) +# ---------------------------- +# Make the test die with hard failure if SHELL-EXPRESSION evaluates to +# true (exitcode = 0). +_AT_DEFINE_SETUP([AT_FAIL_IF], +[dnl +dnl Try to limit the amount of conditionals that we emit. +m4_case([$1], + [], [], + [false], [], + [:], [_AT_CHECK_EXIT([], [99])], + [true], [_AT_CHECK_EXIT([], [99])], + [_AT_CHECK_EXIT([$1], [99])])]) + + +# AT_SKIP_IF(SHELL-EXPRESSION) +# ---------------------------- +# Skip the rest of the group if SHELL-EXPRESSION evaluates to true +# (exitcode = 0). +_AT_DEFINE_SETUP([AT_SKIP_IF], +[dnl +dnl Try to limit the amount of conditionals that we emit. +m4_case([$1], + [], [], + [false], [], + [:], [_AT_CHECK_EXIT([], [77])], + [true], [_AT_CHECK_EXIT([], [77])], + [_AT_CHECK_EXIT([$1], [77])])]) + + +# AT_XFAIL_IF(SHELL-EXPRESSION) +# ----------------------------- +# Set up the test to be expected to fail if SHELL-EXPRESSION evaluates to +# true (exitcode = 0). +_AT_DEFINE_SETUP([AT_XFAIL_IF], +[dnl +dnl Try to limit the amount of conditionals that we emit. +m4_case([$1], + [], [], + [false], [], + [:], [m4_define([AT_xfail], [at_xfail=yes])], + [true], [m4_define([AT_xfail], [at_xfail=yes])], + [m4_append([AT_xfail], [ + $1 && at_xfail=yes])])]) + + +# AT_KEYWORDS(KEYWORDS) +# --------------------- +# Declare a list of keywords associated to the current test group. +# Since the -k option is case-insensitive, the list is stored in lower case +# to avoid duplicates that differ only by case. +_AT_DEFINE_SETUP([AT_KEYWORDS], +[m4_append_uniq_w([AT_keywords], m4_tolower(_m4_expand([$1 +])))]) + + +# AT_CAPTURE_FILE(FILE) +# --------------------- +# If the current test group does not behave as expected, save the contents of +# FILE in the test suite log. +_AT_DEFINE_SETUP([AT_CAPTURE_FILE], +[m4_append_uniq([AT_capture_files], ["$1"], [ \ +])]) + + +# AT_CLEANUP +# ---------- +# Complete a group of related tests. +_AT_DEFINE_INIT([AT_CLEANUP], +[m4_ifdef([AT_ingroup], [AT_ingroup[]_m4_undefine([AT_ingroup])], + [m4_fatal([$0: missing AT_SETUP detected])])dnl +m4_append([AT_help_all], +m4_defn([AT_ordinal]);m4_defn([AT_line]);m4_defn([AT_description]);dnl +m4_ifdef([AT_keywords], [m4_defn([AT_keywords])]); +)dnl +m4_divert_pop([TEST_SCRIPT])dnl Back to TEST_GROUPS +AT_xfail +( + AS_ECHO(["AT_ordinal. $at_setup_line: testing $at_desc ..."]) + $at_traceon +m4_undivert([TEST_SCRIPT])dnl Insert the code here + set +x + $at_times_p && times >"$at_times_file" +) AS_MESSAGE_LOG_FD>&1 2>&1 AT_JOB_FIFO_OUT_FD>&- | eval $at_tee_pipe +read at_status <"$at_status_file" +[#AT_STOP_]AT_ordinal +m4_divert_pop([TEST_GROUPS])dnl Back to KILL. +])# AT_CLEANUP + + +# AT_BANNER([TEXT]) +# ----------------- +# Start a category of related test groups. If multiple groups are executed, +# output TEXT as a banner without any shell expansion, prior to any test +# from the category. If TEXT is empty, no banner is printed. +_AT_DEFINE_INIT([AT_BANNER], +[m4_ifdef([AT_ingroup], [m4_fatal([$0: nested AT_SETUP detected])])dnl +m4_define([AT_banner_ordinal], m4_incr(AT_banner_ordinal)) +m4_divert_text([BANNERS], +[@%:@ Banner AT_banner_ordinal. AT_LINE +@%:@ Category starts at test group m4_incr(AT_ordinal). +at_banner_text_[]AT_banner_ordinal="AS_ESCAPE([$1])"])dnl +])# AT_BANNER + + +# AT_DATA(FILE, CONTENTS) +# ----------------------- +# Initialize an input data FILE with given CONTENTS, which should be +# empty or end with a newline. +# This macro is not robust to active symbols in CONTENTS *on purpose*. +# If you don't want CONTENTS to be evaluated, quote it twice. +_AT_DEFINE_SETUP([AT_DATA], +[m4_if([$2], [], [: >$1], + [$2], [[]], [: >$1], +[cat >$1 <<'_ATEOF' +$2[]_ATEOF +])]) + + +# AT_CHECK(COMMANDS, [STATUS = 0], STDOUT, STDERR, +# [RUN-IF-FAIL], [RUN-IF-PASS]) +# ------------------------------------------------ +# Execute a test by performing given shell COMMANDS. These commands +# should normally exit with STATUS, while producing expected STDOUT and +# STDERR contents. Shell metacharacters in STDOUT and STDERR are +# _not_ processed by the shell, but are treated as string literals. +# +# STATUS, STDOUT, and STDERR are not checked if equal to `ignore'. +# +# If STDOUT is `expout', then stdout is compared to the content of the file +# `expout'. Likewise for STDERR and `experr'. +# +# If STDOUT is `stdout', then the stdout is left in the file `stdout', +# likewise for STDERR and `stderr'. Don't do this: +# +# AT_CHECK([command >out]) +# # Some checks on `out' +# +# do this instead: +# +# AT_CHECK([command], [], [stdout]) +# # Some checks on `stdout' +# +# You might wonder why you can't just use `ignore', then directly use stdout +# and stderr left by the test suite: +# +# AT_CHECK([command], [], [ignore]) +# AT_CHECK([check stdout]) +# +# If the test suite always captured data in the file `stdout', then the +# second command would be trying to read and write from the same file, with +# undefined behavior. Therefore, the test suite actually captures data in +# an internal file of a different name, and only creates `stdout' when +# explicitly requested. +# +# Any line of stderr starting with leading blanks and a `+' are filtered +# out, since most shells when tracing include subshell traces in stderr. +# This may cause spurious failures when the test suite is run with `-x'. +# +_AT_DEFINE_SETUP([AT_CHECK], +[_AT_CHECK(m4_expand([$1]), [$2], AS_ESCAPE(m4_dquote(m4_expand([$3]))), + AS_ESCAPE(m4_dquote(m4_expand([$4]))), [$5], [$6])]) + +# AT_CHECK_UNQUOTED(COMMANDS, [STATUS = 0], STDOUT, STDERR, +# [RUN-IF-FAIL], [RUN-IF-PASS]) +# --------------------------------------------------------- +# Like AT_CHECK, but do not AS_ESCAPE shell metacharacters in the STDOUT +# and STDERR arguments before running the comparison. +_AT_DEFINE_SETUP([AT_CHECK_UNQUOTED], +[_AT_CHECK(m4_expand([$1]), [$2], AS_ESCAPE(m4_dquote(m4_expand([$3])), [""]), + AS_ESCAPE(m4_dquote(m4_expand([$4])), [""]), [$5], [$6])]) + +# AT_CHECK_NOESCAPE(COMMANDS, [STATUS = 0], STDOUT, STDERR, +# [RUN-IF-FAIL], [RUN-IF-PASS]) +# --------------------------------------------------------- +# Obsolete spelling of AT_CHECK_UNQUOTED. +m4_define([AT_CHECK_NOESCAPE], +[m4_warn([obsolete], [consider using AT_CHECK_UNQUOTED instead of $0])]dnl +[_AT_CHECK(m4_expand([$1]), [$2], m4_expand([$3]), + m4_expand([$4]), [$5], [$6])]) + + +# _AT_DECIDE_TRACEABLE(COMMANDS) +# ------------------------------ +# Worker for _AT_CHECK that expands to shell code. If COMMANDS are safe to +# trace with `set -x', the shell code will evaluate to true. Otherwise, +# the shell code will print a message stating an aspect of COMMANDS that makes +# tracing them unsafe, and evaluate to false. +# +# Tracing COMMANDS is not safe if they contain a command that spans multiple +# lines. When the test suite user passes `-x' or `--trace', the test suite +# precedes every command with a `set -x'. Since most tests expect a specific +# stderr, if only to confirm that it is empty, the test suite filters ^+ from +# the captured stderr before comparing with the expected stderr. If a command +# spans multiple lines, so will its trace, but a `+' only prefixes the first +# line of that trace: +# +# $ echo 'foo +# bar' +# => stdout +# foo +# bar +# => stderr +# + foo +# bar +# +# In a subset of cases, one could filter such extended shell traces from +# stderr. Since test commands spanning several lines are rare, I chose +# instead to simply not trace COMMANDS that could yield multiple trace lines. +# Distinguishing such COMMANDS became the task at hand. +# +# These features may cause a shell command to span multiple lines: +# +# (a) A quoted literal newline. +# Example: +# echo foo' +# 'bar +# M4 is a hostile language for the job of parsing COMMANDS to determine whether +# each literal newline is quoted, so we simply disable tracing for all COMMANDS +# that bear literal newlines. +# +# (b) A command substitution not subject to word splitting. +# Example: +# var=$(printf 'foo\nbar') +# Example: +# echo "`printf 'foo\\nbar`" +# One cannot know in general the number of lines a command substitution will +# yield without executing the substituted command. As such, we disable tracing +# for all COMMANDS containing these constructs. +# +# (c) A parameter expansion not subject to word splitting. +# Example: +# var=foo' +# 'bar +# echo "$var" +# Parameter expansions appear in COMMANDS with much greater frequency than do +# newlines and command substitutions, so disabling tracing for all such +# COMMANDS would much more substantially devalue `testsuite -x'. To determine +# which parameter expansions yield multiple lines, we escape all ``', `"', +# and `\' in a copy of COMMANDS and expand that string within double quotes +# at runtime. If the result of that expansion contains multiple lines, the +# test suite disables tracing for the command in question. +# +# This method leads the test suite to expand some parameters that the shell +# itself will never expand due to single-quotes or backslash escapes. This is +# not a problem for `$foo' expansions, which will simply yield the empty string +# or some unrelated value. A `${...}' expansion could actually form invalid +# shell code, however; consider `${=foo}'. Therefore, we disable tracing for +# all COMMANDS containing `${...}'. This affects few COMMANDS. +# +# This macro falls in a very hot path; the Autoconf test suite expands it 1640 +# times as of this writing. To give a sense of the impact of the heuristics I +# just described, the test suite preemptively disables tracing for 31 of those, +# and 268 contain parameter expansions that require runtime evaluation. The +# balance are always safe to trace. +m4_define([_AT_DECIDE_TRACEABLE], +dnl Utility macro. +dnl +dnl Examine COMMANDS for a reason to never trace COMMANDS. +[m4_pushdef([at_reason], +m4_cond([m4_eval(m4_index([$1], [`]) >= 0)], [1], + [[a `...` command substitution]], + [m4_eval(m4_index([$1], [$(]) >= 0)], [1], + [[a $(...) command substitution]], + [m4_eval(m4_index([$1], [${]) >= 0)], [1], + [[a ${...} parameter expansion]], + [m4_eval(m4_index([$1], m4_newline) >= 0)], [1], + [[an embedded newline]], + [m4_eval(m4_bregexp([$1], [[^|]|[^|]]) >= 0)], [1], + [[a shell pipeline]], + []))]dnl No reason. +[m4_if(m4_index(_m4_defn([at_reason]), [a]), [0],]dnl +dnl We know at build time that tracing COMMANDS is never safe. +[[at_fn_check_prepare_notrace '_m4_defn([at_reason])'], + m4_index([$1], [$]), [-1],]dnl +dnl We know at build time that tracing COMMANDS is always safe. +[[at_fn_check_prepare_trace],]dnl +dnl COMMANDS may contain parameter expansions; expand them at runtime. +[[at_fn_check_prepare_dynamic "AS_ESCAPE([[$1]], [`\"])"])[]]dnl +[_m4_popdef([at_reason])]) + + +# AT_DIFF_STDERR/AT_DIFF_STDOUT +# ----------------------------- +# These are subroutines of AT_CHECK. Using indirect dispatch is a tad +# faster than using m4_case, and these are called very frequently. +m4_define([AT_DIFF_STDERR(stderr)], + [echo stderr:; tee stderr <"$at_stderr"]) +m4_define([AT_DIFF_STDERR(stderr-nolog)], + [echo stderr captured; cp "$at_stderr" stderr]) +m4_define([AT_DIFF_STDERR(ignore)], + [echo stderr:; cat "$at_stderr"]) +m4_define([AT_DIFF_STDERR(ignore-nolog)]) +m4_define([AT_DIFF_STDERR(experr)], + [$at_diff experr "$at_stderr" || at_failed=:]) +m4_define([AT_DIFF_STDERR()], + [at_fn_diff_devnull "$at_stderr" || at_failed=:]) + +m4_define([AT_DIFF_STDOUT(stdout)], + [echo stdout:; tee stdout <"$at_stdout"]) +m4_define([AT_DIFF_STDOUT(stdout-nolog)], + [echo stdout captured; cp "$at_stdout" stdout]) +m4_define([AT_DIFF_STDOUT(ignore)], + [echo stdout:; cat "$at_stdout"]) +m4_define([AT_DIFF_STDOUT(ignore-nolog)]) +m4_define([AT_DIFF_STDOUT(expout)], + [$at_diff expout "$at_stdout" || at_failed=:]) +m4_define([AT_DIFF_STDOUT()], + [at_fn_diff_devnull "$at_stdout" || at_failed=:]) + +# _AT_CHECK(COMMANDS, [STATUS = 0], STDOUT, STDERR, +# [RUN-IF-FAIL], [RUN-IF-PASS]) +# ------------------------------------------------- +# Worker for AT_CHECK and AT_CHECK_UNQUOTED, with COMMANDS, STDOUT, and +# STDERR pre-expanded. +# +# Implementation Details +# ---------------------- +# Ideally, we would like to run +# +# ( $at_traceon; COMMANDS >at-stdout 2> at-stderr ) +# +# but we must group COMMANDS as it is not limited to a single command, and +# then the shells will save the traces in at-stderr. So we have to filter +# them out when checking stderr, and we must send them into the test suite's +# stderr to honor -x properly. Since only the first line of the trace of a +# multiline command starts with a `+', and I know of no straightforward way to +# filter out the unadorned trace lines, we disable shell tracing entirely for +# commands that could span multiple lines. +# +# Limiting COMMANDS to a single command is not good either, since then +# the user herself would use {} or (), and then we face the same problem. +# +# But then, there is no point in running +# +# ( $at_traceon { $1 ; } >at-stdout 2>at-stder1 ) +# +# instead of the simpler +# +# ( $at_traceon; $1 ) >at-stdout 2>at-stder1 +# +# Note that we truncate and append to the output files, to avoid losing +# output from multiple concurrent processes, e.g., an inner testsuite +# with parallel jobs. +m4_define([_AT_CHECK], +[m4_define([AT_ingroup])]dnl +[{ set +x +AS_ECHO(["$at_srcdir/AT_LINE: AS_ESCAPE([[$1]])"]) +_AT_DECIDE_TRACEABLE([$1]) _AT_LINE_ESCAPED +( $at_check_trace; [$1] +) >>"$at_stdout" 2>>"$at_stderr" AS_MESSAGE_LOG_FD>&- +at_status=$? at_failed=false +$at_check_filter +m4_ifdef([AT_DIFF_STDERR($4)], [m4_indir([AT_DIFF_STDERR($4)])], + [echo >>"$at_stderr"; AS_ECHO([["$4"]]) | \ + $at_diff - "$at_stderr" || at_failed=:]) +m4_ifdef([AT_DIFF_STDOUT($3)], [m4_indir([AT_DIFF_STDOUT($3)])], + [echo >>"$at_stdout"; AS_ECHO([["$3"]]) | \ + $at_diff - "$at_stdout" || at_failed=:]) +m4_if([$2], [ignore], [at_fn_check_skip], + [at_fn_check_status m4_default([$2], [0])]) $at_status "$at_srcdir/AT_LINE" +m4_ifvaln([$5$6], [AS_IF($at_failed, [$5], [$6])])]dnl +[$at_failed && at_fn_log_failure AT_capture_files +$at_traceon; } +])# _AT_CHECK + +# _AT_CHECK_EXIT(COMMANDS, [EXIT-STATUS-IF-PASS]) +# ----------------------------------------------- +# Minimal version of _AT_CHECK for AT_SKIP_IF and AT_FAIL_IF. +m4_define([_AT_CHECK_EXIT], +[m4_define([AT_ingroup])]dnl +[AS_ECHO(_AT_LINE_ESCAPED) >"$at_check_line_file" +m4_ifval([$1], [($1) \ + && ])at_fn_check_skip $2 "$at_srcdir/AT_LINE"])# _AT_CHECK_EXIT |