| # This file is part of Autoconf. -*- Autoconf -*- |
| # M4 macros used in building test suites. |
| m4_define([_AT_COPYRIGHT_YEARS], [ |
| Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 |
| 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 |
| # Tuning help text. Additional tuning options can be appended 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)], 302) |
| m4_define([_m4_divert(HELP_OTHER)], 303) |
| m4_define([_m4_divert(HELP_END)], 304) |
| 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_groups_all], [])] |
| [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? at_groups is space-separated for entire test, |
| # newline-separated if only a subset of the testsuite is run. |
| case $at_groups in |
| *' '*' '* | *"$as_nl"*"$as_nl"* ) |
| at_print_banners=: ;; |
| * ) at_print_banners=false ;; |
| esac |
| # Text for banner N, set to empty 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]= |
| AS_ECHO(["$as_nl$at_banner_text$as_nl"]) |
| } # 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 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], |
| [ |
| # List of the tested programs. |
| at_tested='m4_ifdef([AT_tested], |
| [m4_translit(m4_dquote(m4_defn([AT_tested])), [ ], m4_newline)])' |
| # List of the all the test groups. |
| at_groups_all='AT_groups_all' |
| # 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])))" |
| |
| 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" : 'x[[^=]]*=\(.*\)'` ;; |
| *) 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=: |
| ;; |
| |
| --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 "]) |
| ;; |
| |
| # 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 -e 's/^.* \('$at_range_start' \)/\1/'` |
| AS_VAR_APPEND([at_groups], ["$at_range "]) |
| ;; |
| |
| [-[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 -e 's/\( '$at_range_end'\) .*$/\1/'` |
| AS_VAR_APPEND([at_groups], ["$at_range "]) |
| ;; |
| |
| [[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 -e 's/^.*\( '$at_range_start' \)/\1/' \ |
| -e 's/\( '$at_range_end'\) .*$/\1/'` |
| AS_VAR_APPEND([at_groups], ["$at_range "]) |
| ;; |
| |
| # Directory selection. |
| --directory | -C ) |
| at_prev=--directory |
| ;; |
| --directory=* ) |
| at_change_dir=: |
| at_dir=$at_optarg |
| ;; |
| |
| # 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 newlines. |
| at_groups_selected=`AS_ECHO(["$at_groups_selected"]) | sed 's/;.*//' | |
| tr "$as_nl" ' ' |
| ` |
| AS_VAR_APPEND([at_groups], ["$at_groups_selected "]) |
| ;; |
| 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.])]) |
| |
| # Selected test groups. |
| if test -z "$at_groups"; then |
| at_groups=$at_groups_all |
| else |
| # Sort the tests, removing duplicates. |
| at_groups=`AS_ECHO(["$at_groups"]) | tr ' ' "$as_nl" | sort -nu` |
| 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_divert_push([HELP_TUNING])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] |
| -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] |
| -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])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 |
| # Passing at_groups is tricky. We cannot use it to form a literal string |
| # or regexp because of the limitation of AIX awk. And Solaris' awk |
| # doesn't grok more than 99 fields in a record, so we have to use `split'. |
| # at_groups needs to be space-separated for this script to work. |
| case $at_groups in |
| *"$as_nl"* ) |
| at_groups=`AS_ECHO(["$at_groups"]) | tr "$as_nl" ' '` ;; |
| esac |
| AS_ECHO(["$at_groups$as_nl$at_help_all"]) | |
| awk 'BEGIN { FS = ";" } |
| NR == 1 { |
| for (n = split ($ 0, a, " "); n; n--) |
| selected[[a[n]]] = 1 |
| next |
| } |
| 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 |
| if test x- = "x$at_dir" ; then |
| at_dir=./- |
| fi |
| 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_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]) |
| m4_define([AT_JOB_FIFO_FD], [6]) |
| [#] 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 |
| |
| # Try to find a few ChangeLogs in case it might help determining the |
| # exact version. Use the relative dir: if the top dir is a symlink, |
| # find will not follow it (and options to follow the links are not |
| # portable), which would result in no output here. Prune directories |
| # matching the package tarname, since they tend to be leftovers from |
| # `make dist' or `make distcheck' and contain redundant or stale logs. |
| if test -n "$at_top_srcdir"; then |
| AS_BOX([ChangeLogs.]) |
| echo |
| for at_file in `find "$at_top_srcdir" m4_ifdef([AT_PACKAGE_TARNAME], |
| [-name "AT_PACKAGE_TARNAME-*" -prune -o ])-name ChangeLog -print` |
| do |
| AS_ECHO(["$as_me: $at_file:"]) |
| sed 's/^/| /;10q' $at_file |
| echo |
| done |
| |
| 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 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_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 |
| *:99) |
| at_msg='FAILED ('`cat "$at_check_line_file"`')' |
| at_res=fail |
| at_errexit=$at_errexit_p |
| ;; |
| yes:0) |
| at_msg="UNEXPECTED PASS" |
| at_res=xpass |
| at_errexit=$at_errexit_p |
| ;; |
| no:0) |
| at_msg="ok" |
| at_res=pass |
| at_errexit=false |
| ;; |
| *:77) |
| at_msg='skipped ('`cat "$at_check_line_file"`')' |
| at_res=skip |
| at_errexit=false |
| ;; |
| yes:*) |
| at_msg='expected failure ('`cat "$at_check_line_file"`')' |
| at_res=xfail |
| at_errexit=false |
| ;; |
| no:*) |
| at_msg='FAILED ('`cat "$at_check_line_file"`')' |
| at_res=fail |
| at_errexit=$at_errexit_p |
| ;; |
| 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_msg"]) |
| else |
| # Make sure there is a separator even with long titles. |
| AS_ECHO([" $at_msg"]) |
| 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. |
| if $at_debug_p; then |
| at_fn_create_debugging_script |
| 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_all "]) | \ |
| sed 's/\( '$at_jobs'\) .*/\1/'` |
| |
| 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 |
| exec AT_JOB_FIFO_FD>"$at_job_fifo" |
| 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 token >&AT_JOB_FIFO_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" # AT_JOB_FIFO_FD>&- |
| then :; else |
| AS_WARN([unable to parse test group: $at_group]) |
| at_failed=: |
| fi |
| at_fn_group_postprocess |
| echo token >&AT_JOB_FIFO_FD |
| ) & |
| $at_job_control_off |
| if $at_first; then |
| at_first=false |
| exec AT_JOB_FIFO_FD<"$at_job_fifo" |
| fi |
| shift # Consume one token. |
| if test $[@%:@] -gt 0; then :; else |
| read at_token <&AT_JOB_FIFO_FD || break |
| set x $[*] |
| fi |
| test -f "$at_stop_file" && break |
| done |
| # 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_FD |
| fi |
| exec AT_JOB_FIFO_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." |
| else |
| # Don't you just love exponential explosion of the number of cases? |
| 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." ;; |
| 0:0:*) at_result="$at_result behaved as expected." ;; |
| |
| # 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_result" |
| echo "$at_result" >&AS_MESSAGE_LOG_FD |
| else |
| echo "ERROR: $at_result" >&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_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_[]m4_bpatsubst([AT_first_option], -, _)=false])dnl |
| at_arg_given_[]m4_bpatsubst([AT_first_option], -, _)=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=--m4_bpatsubst([AT_first_option], -, _) |
| ;; |
| AT_case_arg ) |
| at_arg_[]m4_bpatsubst([AT_first_option], -, _)=$at_optarg |
| at_arg_given_[]m4_bpatsubst([AT_first_option], -, _)=: |
| $4 |
| ;;], |
| [ AT_case ) |
| at_optarg=: |
| at_arg_[]m4_bpatsubst([AT_first_option], -, _)=: |
| at_arg_given_[]m4_bpatsubst([AT_first_option], -, _)=: |
| m4_ifval([$4],[$4])dnl |
| ;; |
| AT_case_no ) |
| at_optarg=false |
| at_arg_[]m4_bpatsubst([AT_first_option], -, _)=false |
| at_arg_given_[]m4_bpatsubst([AT_first_option], -, _)=: |
| 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_[]m4_bpatsubst([AT_first_option], -, _)],,[$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 set of OPTIONS with arguments. Add HELP-TEXT to the HELP_OTHER |
| # diversion. |
| # |
| # Preceding dashes should not be passed into 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 --noOPTION is received, and `false' by default. |
| # |
| # Run ACTION-IF-GIVEN each time an option in OPTIONS is encountered with |
| # $at_optarg 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 |
| # if no option from OPTIONS was found. |
| 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 OPTIONS with arguments, seperated by commas. Add HELP-TEXT |
| # to the HELP_OTHER diversion. |
| # |
| # Preceding dashes should not be passed into 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 with |
| # $at_optarg set. $at_optarg is actually just a copy of $at_arg_OPTION. |
| # |
| # ACTION-IF-NOT-GIVEN will be run once after option parsing is complete |
| # if no option from OPTIONS was found. |
| 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_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_append([AT_groups_all], [ ]m4_defn([AT_ordinal])) |
| m4_divert_push([TEST_GROUPS])dnl |
| [#AT_START_]AT_ordinal |
| @%:@ AT_ordinal. m4_defn([AT_line]): m4_defn([AT_description]) |
| at_setup_line='m4_defn([AT_line])' |
| m4_if(AT_banner_ordinal, [0], [], [at_fn_banner AT_banner_ordinal |
| ])dnl |
| at_desc="AS_ESCAPE(m4_dquote(m4_defn([AT_description])))" |
| at_desc_line=m4_format(["%3d: $at_desc%*s"], AT_ordinal, |
| m4_max(0, m4_eval(47 - m4_qlen(m4_defn([AT_description])))), []) |
| $at_quiet AS_ECHO_N(["$at_desc_line"]) |
| 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 |
| echo "# -*- compilation -*-" >> "$at_group_log" |
| ( |
| AS_ECHO(["AT_ordinal. m4_defn([AT_line]): testing $1..."]) |
| $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 | 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 end with |
| # an end of line. |
| # 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], |
| [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]], |
| []))]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" |
| 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 |