tests: avoid a spurious failure on MSYS
[platform/upstream/automake.git] / lib / tap-driver.sh
index 09fcaee..19aa531 100755 (executable)
@@ -1,5 +1,5 @@
 #! /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
@@ -23,7 +23,7 @@
 # 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.
@@ -115,14 +115,34 @@ else
   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" \
@@ -133,7 +153,7 @@ fi
         -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
@@ -146,7 +166,7 @@ fi
 
 function fatal(msg)
 {
-  print me ": " msg | "cat >&3"
+  print me ": " msg | "cat >&2"
   exit 1
 }
 
@@ -194,35 +214,35 @@ function get_global_test_result()
 {
     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)
@@ -253,10 +273,10 @@ function report(result, details)
   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)
@@ -293,7 +313,7 @@ function handle_tap_result()
   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"
@@ -328,11 +348,9 @@ function handle_tap_plan(planned, skip_reason)
 
 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)
@@ -383,7 +401,6 @@ function setup_result_obj(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.
 
@@ -399,6 +416,20 @@ function setup_result_obj(line)
   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)
@@ -416,6 +447,32 @@ function setup_result_obj(line)
   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
@@ -486,7 +543,7 @@ while (1)
         $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
@@ -519,12 +576,16 @@ while (1)
         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!"
@@ -559,6 +620,13 @@ if (!bailed_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()
@@ -569,7 +637,7 @@ exit 0
 '
 
 # 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"