#! /bin/sh
-# Copyright (C) 2011 Free Software Foundation, Inc.
+# Copyright (C) 2011-2013 Free Software Foundation, Inc.
#
# 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
# bugs to <bug-automake@gnu.org> or send patches to
# <automake-patches@gnu.org>.
-scriptversion=2011-08-21.21; # UTC
+scriptversion=2011-12-27.17; # UTC
# Make unconditional expansion of undefined variables an error. This
# helps a lot in preventing typo-related bugs.
init_colors=''
fi
-{
- # FIXME: this usage loses the test program exit status. We should
- # probably rewrite the awk script to use the
- # expression | getline [var]
- # idiom, which should allow us to obtain the final exit status from
- # <expression> when closing it.
- { test $merge -eq 0 || exec 2>&1; "$@"; echo $?; } \
- | LC_ALL=C ${AM_TAP_AWK-awk} \
+# :; is there to work around a bug in bash 3.2 (and earlier) which
+# does not always set '$?' properly on redirection failure.
+# See the Autoconf manual for more details.
+:;{
+ (
+ # Ignore common signals (in this subshell only!), to avoid potential
+ # problems with Korn shells. Some Korn shells are known to propagate
+ # to themselves signals that have killed a child process they were
+ # waiting for; this is done at least for SIGINT (and usually only for
+ # it, in truth). Without the `trap' below, such a behaviour could
+ # cause a premature exit in the current subshell, e.g., in case the
+ # test command it runs gets terminated by a SIGINT. Thus, the awk
+ # script we are piping into would never seen the exit status it
+ # expects on its last input line (which is displayed below by the
+ # last `echo $?' statement), and would thus die reporting an internal
+ # error.
+ # For more information, see the Autoconf manual and the threads:
+ # <http://lists.gnu.org/archive/html/bug-autoconf/2011-09/msg00004.html>
+ # <http://mail.opensolaris.org/pipermail/ksh93-integration-discuss/2009-February/004121.html>
+ trap : 1 3 2 13 15
+ if test $merge -gt 0; then
+ exec 2>&1
+ else
+ exec 2>&3
+ fi
+ "$@"
+ echo $?
+ ) | LC_ALL=C ${AM_TAP_AWK-awk} \
-v me="$me" \
-v test_script_name="$test_name" \
-v log_file="$log_file" \
-v comments="$comments" \
-v diag_string="$diag_string" \
'
-# FIXME: the usages of "cat >&3" below could be optimized whne using
+# FIXME: the usages of "cat >&3" below could be optimized when using
# FIXME: GNU awk, and/on on systems that supports /dev/fd/.
# Implementation note: in what follows, `result_obj` will be an
function fatal(msg)
{
- print me ": " msg | "cat >&3"
+ print me ": " msg | "cat >&2"
exit 1
}
{
if ("ERROR" in test_results_seen)
return "ERROR"
+ if ("FAIL" in test_results_seen || "XPASS" in test_results_seen)
+ return "FAIL"
all_skipped = 1
for (k in test_results_seen)
if (k != "SKIP")
all_skipped = 0
if (all_skipped)
return "SKIP"
- if ("FAIL" in test_results_seen || "XPASS" in test_results_seen)
- return "FAIL"
return "PASS";
}
-function stringify_result_obj(obj)
+function stringify_result_obj(result_obj)
{
- if (obj["is_unplanned"] || obj["number"] != testno)
+ if (result_obj["is_unplanned"] || result_obj["number"] != testno)
return "ERROR"
if (plan_seen == LATE_PLAN)
return "ERROR"
if (result_obj["directive"] == "TODO")
- return obj["is_ok"] ? "XPASS" : "XFAIL"
+ return result_obj["is_ok"] ? "XPASS" : "XFAIL"
if (result_obj["directive"] == "SKIP")
- return obj["is_ok"] ? "SKIP" : COOKED_FAIL;
+ return result_obj["is_ok"] ? "SKIP" : COOKED_FAIL;
if (length(result_obj["directive"]))
abort("in function stringify_result_obj()")
- return obj["is_ok"] ? COOKED_PASS : COOKED_FAIL
+ return result_obj["is_ok"] ? COOKED_PASS : COOKED_FAIL
}
function decorate_result(result)
if (length(details))
msg = msg " " details
# Output on console might be colorized.
- print decorate_result(result) msg | "cat >&3";
+ print decorate_result(result) msg
# Log the result in the log file too, to help debugging (this is
# especially true when said result is a TAP error or "Bail out!").
- print result msg;
+ print result msg | "cat >&3";
}
function testsuite_error(error_message)
report(stringify_result_obj(result_obj), details)
}
-# `skip_reason` should be emprty whenever planned > 0.
+# `skip_reason` should be empty whenever planned > 0.
function handle_tap_plan(planned, skip_reason)
{
planned += 0 # Avoid getting confused if, say, `planned` is "00"
function extract_tap_comment(line)
{
- # FIXME: verify there is not an off-by-one bug here.
if (index(line, diag_string) == 1)
{
# Strip leading `diag_string` from `line`.
- # FIXME: verify there is not an off-by-one bug here.
line = substr(line, length(diag_string) + 1)
# And strip any leading and trailing whitespace left.
sub("^[ \t]*", "", line)
result_obj["directive"] = ""
result_obj["explanation"] = ""
- # TODO: maybe we should allow a way to escape "#"?
if (index(line, "#") == 0)
return # No possible directive, nothing more to do.
if (!pos)
return
+ # Let`s now see if the TAP directive has been escaped. For example:
+ # escaped: ok \# SKIP
+ # not escaped: ok \\# SKIP
+ # escaped: ok \\\\\# SKIP
+ # not escaped: ok \ # SKIP
+ if (substr(line, pos, 1) == "#")
+ {
+ bslash_count = 0
+ for (i = pos; i > 1 && substr(line, i - 1, 1) == "\\"; i--)
+ bslash_count += 1
+ if (bslash_count % 2)
+ return # Directive was escaped.
+ }
+
# Strip the directive and its explanation (if any) from the test
# description.
result_obj["description"] = substr(line, 1, pos - 1)
result_obj["explanation"] = line
}
+function get_test_exit_message(status)
+{
+ if (status == 0)
+ return ""
+ if (status !~ /^[1-9][0-9]*$/)
+ abort("getting exit status")
+ if (status < 127)
+ exit_details = ""
+ else if (status == 127)
+ exit_details = " (command not found?)"
+ else if (status >= 128 && status <= 255)
+ exit_details = sprintf(" (terminated by signal %d?)", status - 128)
+ else if (status > 256 && status <= 384)
+ # We used to report an "abnormal termination" here, but some Korn
+ # shells, when a child process die due to signal number n, can leave
+ # in $? an exit status of 256+n instead of the more standard 128+n.
+ # Apparently, both behaviours are allowed by POSIX (2008), so be
+ # prepared to handle them both. See also Austing Group report ID
+ # 0000051 <http://www.austingroupbugs.net/view.php?id=51>
+ exit_details = sprintf(" (terminated by signal %d?)", status - 256)
+ else
+ # Never seen in practice.
+ exit_details = " (abnormal termination)"
+ return sprintf("exited with status %d%s", status, exit_details)
+}
+
function write_test_results()
{
print ":global-test-result: " get_global_test_result() > trs_file
$0 = curline
}
# Copy any input line verbatim into the log file.
- print
+ print | "cat >&3"
# Parsing of TAP input should stop after a "Bail out!" directive.
if (bailed_out)
continue
handle_tap_plan(0, $0)
}
# "Bail out!" magic.
- else if ($0 ~ /^Bail out!/)
+ # Older versions of prove and TAP::Harness (e.g., 3.17) did not
+ # recognize a "Bail out!" directive when preceded by leading
+ # whitespace, but more modern versions (e.g., 3.23) do. So we
+ # emulate the latter, "more modern" behaviour.
+ else if ($0 ~ /^[ \t]*Bail out!/)
{
bailed_out = 1
# Get the bailout message (if any), with leading and trailing
# whitespace stripped. The message remains stored in `$0`.
- sub("^Bail out![ \t]*", "");
+ sub("^[ \t]*Bail out![ \t]*", "");
sub("[ \t]*$", "");
# Format the error message for the
bailout_message = "Bail out!"
testsuite_error(sprintf("too %s tests run (expected %d, got %d)",
bad_amount, planned_tests, testno))
}
+ if (!ignore_exit)
+ {
+ # Fetch exit status from the last line.
+ exit_message = get_test_exit_message(nextline)
+ if (exit_message)
+ testsuite_error(exit_message)
+ }
}
write_test_results()
'
# TODO: document that we consume the file descriptor 3 :-(
-} 3>&1 >"$log_file" 2>&1
+} 3>"$log_file"
test $? -eq 0 || fatal "I/O or internal error"