11c235774Smrg#! /bin/sh 274835918Smrg# Copyright (C) 2011-2021 Free Software Foundation, Inc. 31c235774Smrg# 41c235774Smrg# This program is free software; you can redistribute it and/or modify 51c235774Smrg# it under the terms of the GNU General Public License as published by 61c235774Smrg# the Free Software Foundation; either version 2, or (at your option) 71c235774Smrg# any later version. 81c235774Smrg# 91c235774Smrg# This program is distributed in the hope that it will be useful, 101c235774Smrg# but WITHOUT ANY WARRANTY; without even the implied warranty of 111c235774Smrg# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 121c235774Smrg# GNU General Public License for more details. 131c235774Smrg# 141c235774Smrg# You should have received a copy of the GNU General Public License 1574835918Smrg# along with this program. If not, see <https://www.gnu.org/licenses/>. 161c235774Smrg 171c235774Smrg# As a special exception to the GNU General Public License, if you 181c235774Smrg# distribute this file as part of a program that contains a 191c235774Smrg# configuration script generated by Autoconf, you may include it under 201c235774Smrg# the same distribution terms that you use for the rest of that program. 211c235774Smrg 221c235774Smrg# This file is maintained in Automake, please report 231c235774Smrg# bugs to <bug-automake@gnu.org> or send patches to 241c235774Smrg# <automake-patches@gnu.org>. 251c235774Smrg 261c235774Smrgscriptversion=2013-12-23.17; # UTC 271c235774Smrg 281c235774Smrg# Make unconditional expansion of undefined variables an error. This 291c235774Smrg# helps a lot in preventing typo-related bugs. 301c235774Smrgset -u 311c235774Smrg 321c235774Smrgme=tap-driver.sh 331c235774Smrg 341c235774Smrgfatal () 351c235774Smrg{ 361c235774Smrg echo "$me: fatal: $*" >&2 371c235774Smrg exit 1 381c235774Smrg} 391c235774Smrg 401c235774Smrgusage_error () 411c235774Smrg{ 421c235774Smrg echo "$me: $*" >&2 431c235774Smrg print_usage >&2 441c235774Smrg exit 2 451c235774Smrg} 461c235774Smrg 471c235774Smrgprint_usage () 481c235774Smrg{ 491c235774Smrg cat <<END 501c235774SmrgUsage: 511c235774Smrg tap-driver.sh --test-name=NAME --log-file=PATH --trs-file=PATH 521c235774Smrg [--expect-failure={yes|no}] [--color-tests={yes|no}] 531c235774Smrg [--enable-hard-errors={yes|no}] [--ignore-exit] 541c235774Smrg [--diagnostic-string=STRING] [--merge|--no-merge] 551c235774Smrg [--comments|--no-comments] [--] TEST-COMMAND 561c235774SmrgThe '--test-name', '-log-file' and '--trs-file' options are mandatory. 571c235774SmrgEND 581c235774Smrg} 591c235774Smrg 601c235774Smrg# TODO: better error handling in option parsing (in particular, ensure 611c235774Smrg# TODO: $log_file, $trs_file and $test_name are defined). 621c235774Smrgtest_name= # Used for reporting. 631c235774Smrglog_file= # Where to save the result and output of the test script. 641c235774Smrgtrs_file= # Where to save the metadata of the test run. 651c235774Smrgexpect_failure=0 661c235774Smrgcolor_tests=0 671c235774Smrgmerge=0 681c235774Smrgignore_exit=0 691c235774Smrgcomments=0 701c235774Smrgdiag_string='#' 711c235774Smrgwhile test $# -gt 0; do 721c235774Smrg case $1 in 731c235774Smrg --help) print_usage; exit $?;; 741c235774Smrg --version) echo "$me $scriptversion"; exit $?;; 751c235774Smrg --test-name) test_name=$2; shift;; 761c235774Smrg --log-file) log_file=$2; shift;; 771c235774Smrg --trs-file) trs_file=$2; shift;; 781c235774Smrg --color-tests) color_tests=$2; shift;; 791c235774Smrg --expect-failure) expect_failure=$2; shift;; 801c235774Smrg --enable-hard-errors) shift;; # No-op. 811c235774Smrg --merge) merge=1;; 821c235774Smrg --no-merge) merge=0;; 831c235774Smrg --ignore-exit) ignore_exit=1;; 841c235774Smrg --comments) comments=1;; 851c235774Smrg --no-comments) comments=0;; 861c235774Smrg --diagnostic-string) diag_string=$2; shift;; 871c235774Smrg --) shift; break;; 881c235774Smrg -*) usage_error "invalid option: '$1'";; 891c235774Smrg esac 901c235774Smrg shift 911c235774Smrgdone 921c235774Smrg 931c235774Smrgtest $# -gt 0 || usage_error "missing test command" 941c235774Smrg 951c235774Smrgcase $expect_failure in 961c235774Smrg yes) expect_failure=1;; 971c235774Smrg *) expect_failure=0;; 981c235774Smrgesac 991c235774Smrg 1001c235774Smrgif test $color_tests = yes; then 1011c235774Smrg init_colors=' 1021c235774Smrg color_map["red"]="[0;31m" # Red. 1031c235774Smrg color_map["grn"]="[0;32m" # Green. 1041c235774Smrg color_map["lgn"]="[1;32m" # Light green. 1051c235774Smrg color_map["blu"]="[1;34m" # Blue. 1061c235774Smrg color_map["mgn"]="[0;35m" # Magenta. 1071c235774Smrg color_map["std"]="[m" # No color. 1081c235774Smrg color_for_result["ERROR"] = "mgn" 1091c235774Smrg color_for_result["PASS"] = "grn" 1101c235774Smrg color_for_result["XPASS"] = "red" 1111c235774Smrg color_for_result["FAIL"] = "red" 1121c235774Smrg color_for_result["XFAIL"] = "lgn" 1131c235774Smrg color_for_result["SKIP"] = "blu"' 1141c235774Smrgelse 1151c235774Smrg init_colors='' 1161c235774Smrgfi 1171c235774Smrg 1181c235774Smrg# :; is there to work around a bug in bash 3.2 (and earlier) which 1191c235774Smrg# does not always set '$?' properly on redirection failure. 1201c235774Smrg# See the Autoconf manual for more details. 1211c235774Smrg:;{ 1221c235774Smrg ( 1231c235774Smrg # Ignore common signals (in this subshell only!), to avoid potential 1241c235774Smrg # problems with Korn shells. Some Korn shells are known to propagate 1251c235774Smrg # to themselves signals that have killed a child process they were 1261c235774Smrg # waiting for; this is done at least for SIGINT (and usually only for 1271c235774Smrg # it, in truth). Without the `trap' below, such a behaviour could 1281c235774Smrg # cause a premature exit in the current subshell, e.g., in case the 1291c235774Smrg # test command it runs gets terminated by a SIGINT. Thus, the awk 1301c235774Smrg # script we are piping into would never seen the exit status it 1311c235774Smrg # expects on its last input line (which is displayed below by the 1321c235774Smrg # last `echo $?' statement), and would thus die reporting an internal 1331c235774Smrg # error. 1341c235774Smrg # For more information, see the Autoconf manual and the threads: 13574835918Smrg # <https://lists.gnu.org/archive/html/bug-autoconf/2011-09/msg00004.html> 1361c235774Smrg # <http://mail.opensolaris.org/pipermail/ksh93-integration-discuss/2009-February/004121.html> 1371c235774Smrg trap : 1 3 2 13 15 1381c235774Smrg if test $merge -gt 0; then 1391c235774Smrg exec 2>&1 1401c235774Smrg else 1411c235774Smrg exec 2>&3 1421c235774Smrg fi 1431c235774Smrg "$@" 1441c235774Smrg echo $? 1451c235774Smrg ) | LC_ALL=C ${AM_TAP_AWK-awk} \ 1461c235774Smrg -v me="$me" \ 1471c235774Smrg -v test_script_name="$test_name" \ 1481c235774Smrg -v log_file="$log_file" \ 1491c235774Smrg -v trs_file="$trs_file" \ 1501c235774Smrg -v expect_failure="$expect_failure" \ 1511c235774Smrg -v merge="$merge" \ 1521c235774Smrg -v ignore_exit="$ignore_exit" \ 1531c235774Smrg -v comments="$comments" \ 1541c235774Smrg -v diag_string="$diag_string" \ 1551c235774Smrg' 1561c235774Smrg# TODO: the usages of "cat >&3" below could be optimized when using 1571c235774Smrg# GNU awk, and/on on systems that supports /dev/fd/. 1581c235774Smrg 1591c235774Smrg# Implementation note: in what follows, `result_obj` will be an 1601c235774Smrg# associative array that (partly) simulates a TAP result object 1611c235774Smrg# from the `TAP::Parser` perl module. 1621c235774Smrg 1631c235774Smrg## ----------- ## 1641c235774Smrg## FUNCTIONS ## 1651c235774Smrg## ----------- ## 1661c235774Smrg 1671c235774Smrgfunction fatal(msg) 1681c235774Smrg{ 1691c235774Smrg print me ": " msg | "cat >&2" 1701c235774Smrg exit 1 1711c235774Smrg} 1721c235774Smrg 1731c235774Smrgfunction abort(where) 1741c235774Smrg{ 1751c235774Smrg fatal("internal error " where) 1761c235774Smrg} 1771c235774Smrg 1781c235774Smrg# Convert a boolean to a "yes"/"no" string. 1791c235774Smrgfunction yn(bool) 1801c235774Smrg{ 1811c235774Smrg return bool ? "yes" : "no"; 1821c235774Smrg} 1831c235774Smrg 1841c235774Smrgfunction add_test_result(result) 1851c235774Smrg{ 1861c235774Smrg if (!test_results_index) 1871c235774Smrg test_results_index = 0 1881c235774Smrg test_results_list[test_results_index] = result 1891c235774Smrg test_results_index += 1 1901c235774Smrg test_results_seen[result] = 1; 1911c235774Smrg} 1921c235774Smrg 1931c235774Smrg# Whether the test script should be re-run by "make recheck". 1941c235774Smrgfunction must_recheck() 1951c235774Smrg{ 1961c235774Smrg for (k in test_results_seen) 1971c235774Smrg if (k != "XFAIL" && k != "PASS" && k != "SKIP") 1981c235774Smrg return 1 1991c235774Smrg return 0 2001c235774Smrg} 2011c235774Smrg 2021c235774Smrg# Whether the content of the log file associated to this test should 2031c235774Smrg# be copied into the "global" test-suite.log. 2041c235774Smrgfunction copy_in_global_log() 2051c235774Smrg{ 2061c235774Smrg for (k in test_results_seen) 2071c235774Smrg if (k != "PASS") 2081c235774Smrg return 1 2091c235774Smrg return 0 2101c235774Smrg} 2111c235774Smrg 2121c235774Smrgfunction get_global_test_result() 2131c235774Smrg{ 2141c235774Smrg if ("ERROR" in test_results_seen) 2151c235774Smrg return "ERROR" 2161c235774Smrg if ("FAIL" in test_results_seen || "XPASS" in test_results_seen) 2171c235774Smrg return "FAIL" 2181c235774Smrg all_skipped = 1 2191c235774Smrg for (k in test_results_seen) 2201c235774Smrg if (k != "SKIP") 2211c235774Smrg all_skipped = 0 2221c235774Smrg if (all_skipped) 2231c235774Smrg return "SKIP" 2241c235774Smrg return "PASS"; 2251c235774Smrg} 2261c235774Smrg 2271c235774Smrgfunction stringify_result_obj(result_obj) 2281c235774Smrg{ 2291c235774Smrg if (result_obj["is_unplanned"] || result_obj["number"] != testno) 2301c235774Smrg return "ERROR" 2311c235774Smrg 2321c235774Smrg if (plan_seen == LATE_PLAN) 2331c235774Smrg return "ERROR" 2341c235774Smrg 2351c235774Smrg if (result_obj["directive"] == "TODO") 2361c235774Smrg return result_obj["is_ok"] ? "XPASS" : "XFAIL" 2371c235774Smrg 2381c235774Smrg if (result_obj["directive"] == "SKIP") 2391c235774Smrg return result_obj["is_ok"] ? "SKIP" : COOKED_FAIL; 2401c235774Smrg 2411c235774Smrg if (length(result_obj["directive"])) 2421c235774Smrg abort("in function stringify_result_obj()") 2431c235774Smrg 2441c235774Smrg return result_obj["is_ok"] ? COOKED_PASS : COOKED_FAIL 2451c235774Smrg} 2461c235774Smrg 2471c235774Smrgfunction decorate_result(result) 2481c235774Smrg{ 2491c235774Smrg color_name = color_for_result[result] 2501c235774Smrg if (color_name) 2511c235774Smrg return color_map[color_name] "" result "" color_map["std"] 2521c235774Smrg # If we are not using colorized output, or if we do not know how 2531c235774Smrg # to colorize the given result, we should return it unchanged. 2541c235774Smrg return result 2551c235774Smrg} 2561c235774Smrg 2571c235774Smrgfunction report(result, details) 2581c235774Smrg{ 2591c235774Smrg if (result ~ /^(X?(PASS|FAIL)|SKIP|ERROR)/) 2601c235774Smrg { 2611c235774Smrg msg = ": " test_script_name 2621c235774Smrg add_test_result(result) 2631c235774Smrg } 2641c235774Smrg else if (result == "#") 2651c235774Smrg { 2661c235774Smrg msg = " " test_script_name ":" 2671c235774Smrg } 2681c235774Smrg else 2691c235774Smrg { 2701c235774Smrg abort("in function report()") 2711c235774Smrg } 2721c235774Smrg if (length(details)) 2731c235774Smrg msg = msg " " details 2741c235774Smrg # Output on console might be colorized. 2751c235774Smrg print decorate_result(result) msg 2761c235774Smrg # Log the result in the log file too, to help debugging (this is 2771c235774Smrg # especially true when said result is a TAP error or "Bail out!"). 2781c235774Smrg print result msg | "cat >&3"; 2791c235774Smrg} 2801c235774Smrg 2811c235774Smrgfunction testsuite_error(error_message) 2821c235774Smrg{ 2831c235774Smrg report("ERROR", "- " error_message) 2841c235774Smrg} 2851c235774Smrg 2861c235774Smrgfunction handle_tap_result() 2871c235774Smrg{ 2881c235774Smrg details = result_obj["number"]; 2891c235774Smrg if (length(result_obj["description"])) 2901c235774Smrg details = details " " result_obj["description"] 2911c235774Smrg 2921c235774Smrg if (plan_seen == LATE_PLAN) 2931c235774Smrg { 2941c235774Smrg details = details " # AFTER LATE PLAN"; 2951c235774Smrg } 2961c235774Smrg else if (result_obj["is_unplanned"]) 2971c235774Smrg { 2981c235774Smrg details = details " # UNPLANNED"; 2991c235774Smrg } 3001c235774Smrg else if (result_obj["number"] != testno) 3011c235774Smrg { 3021c235774Smrg details = sprintf("%s # OUT-OF-ORDER (expecting %d)", 3031c235774Smrg details, testno); 3041c235774Smrg } 3051c235774Smrg else if (result_obj["directive"]) 3061c235774Smrg { 3071c235774Smrg details = details " # " result_obj["directive"]; 3081c235774Smrg if (length(result_obj["explanation"])) 3091c235774Smrg details = details " " result_obj["explanation"] 3101c235774Smrg } 3111c235774Smrg 3121c235774Smrg report(stringify_result_obj(result_obj), details) 3131c235774Smrg} 3141c235774Smrg 3151c235774Smrg# `skip_reason` should be empty whenever planned > 0. 3161c235774Smrgfunction handle_tap_plan(planned, skip_reason) 3171c235774Smrg{ 3181c235774Smrg planned += 0 # Avoid getting confused if, say, `planned` is "00" 3191c235774Smrg if (length(skip_reason) && planned > 0) 3201c235774Smrg abort("in function handle_tap_plan()") 3211c235774Smrg if (plan_seen) 3221c235774Smrg { 3231c235774Smrg # Error, only one plan per stream is acceptable. 3241c235774Smrg testsuite_error("multiple test plans") 3251c235774Smrg return; 3261c235774Smrg } 3271c235774Smrg planned_tests = planned 3281c235774Smrg # The TAP plan can come before or after *all* the TAP results; we speak 3291c235774Smrg # respectively of an "early" or a "late" plan. If we see the plan line 3301c235774Smrg # after at least one TAP result has been seen, assume we have a late 3311c235774Smrg # plan; in this case, any further test result seen after the plan will 3321c235774Smrg # be flagged as an error. 3331c235774Smrg plan_seen = (testno >= 1 ? LATE_PLAN : EARLY_PLAN) 3341c235774Smrg # If testno > 0, we have an error ("too many tests run") that will be 3351c235774Smrg # automatically dealt with later, so do not worry about it here. If 3361c235774Smrg # $plan_seen is true, we have an error due to a repeated plan, and that 3371c235774Smrg # has already been dealt with above. Otherwise, we have a valid "plan 3381c235774Smrg # with SKIP" specification, and should report it as a particular kind 3391c235774Smrg # of SKIP result. 3401c235774Smrg if (planned == 0 && testno == 0) 3411c235774Smrg { 3421c235774Smrg if (length(skip_reason)) 3431c235774Smrg skip_reason = "- " skip_reason; 3441c235774Smrg report("SKIP", skip_reason); 3451c235774Smrg } 3461c235774Smrg} 3471c235774Smrg 3481c235774Smrgfunction extract_tap_comment(line) 3491c235774Smrg{ 3501c235774Smrg if (index(line, diag_string) == 1) 3511c235774Smrg { 3521c235774Smrg # Strip leading `diag_string` from `line`. 3531c235774Smrg line = substr(line, length(diag_string) + 1) 3541c235774Smrg # And strip any leading and trailing whitespace left. 3551c235774Smrg sub("^[ \t]*", "", line) 3561c235774Smrg sub("[ \t]*$", "", line) 3571c235774Smrg # Return what is left (if any). 3581c235774Smrg return line; 3591c235774Smrg } 3601c235774Smrg return ""; 3611c235774Smrg} 3621c235774Smrg 3631c235774Smrg# When this function is called, we know that line is a TAP result line, 3641c235774Smrg# so that it matches the (perl) RE "^(not )?ok\b". 3651c235774Smrgfunction setup_result_obj(line) 3661c235774Smrg{ 3671c235774Smrg # Get the result, and remove it from the line. 3681c235774Smrg result_obj["is_ok"] = (substr(line, 1, 2) == "ok" ? 1 : 0) 3691c235774Smrg sub("^(not )?ok[ \t]*", "", line) 3701c235774Smrg 3711c235774Smrg # If the result has an explicit number, get it and strip it; otherwise, 37274835918Smrg # automatically assign the next test number to it. 3731c235774Smrg if (line ~ /^[0-9]+$/ || line ~ /^[0-9]+[^a-zA-Z0-9_]/) 3741c235774Smrg { 3751c235774Smrg match(line, "^[0-9]+") 3761c235774Smrg # The final `+ 0` is to normalize numbers with leading zeros. 3771c235774Smrg result_obj["number"] = substr(line, 1, RLENGTH) + 0 3781c235774Smrg line = substr(line, RLENGTH + 1) 3791c235774Smrg } 3801c235774Smrg else 3811c235774Smrg { 3821c235774Smrg result_obj["number"] = testno 3831c235774Smrg } 3841c235774Smrg 3851c235774Smrg if (plan_seen == LATE_PLAN) 3861c235774Smrg # No further test results are acceptable after a "late" TAP plan 3871c235774Smrg # has been seen. 3881c235774Smrg result_obj["is_unplanned"] = 1 3891c235774Smrg else if (plan_seen && testno > planned_tests) 3901c235774Smrg result_obj["is_unplanned"] = 1 3911c235774Smrg else 3921c235774Smrg result_obj["is_unplanned"] = 0 3931c235774Smrg 3941c235774Smrg # Strip trailing and leading whitespace. 3951c235774Smrg sub("^[ \t]*", "", line) 3961c235774Smrg sub("[ \t]*$", "", line) 3971c235774Smrg 3981c235774Smrg # This will have to be corrected if we have a "TODO"/"SKIP" directive. 3991c235774Smrg result_obj["description"] = line 4001c235774Smrg result_obj["directive"] = "" 4011c235774Smrg result_obj["explanation"] = "" 4021c235774Smrg 4031c235774Smrg if (index(line, "#") == 0) 4041c235774Smrg return # No possible directive, nothing more to do. 4051c235774Smrg 4061c235774Smrg # Directives are case-insensitive. 4071c235774Smrg rx = "[ \t]*#[ \t]*([tT][oO][dD][oO]|[sS][kK][iI][pP])[ \t]*" 4081c235774Smrg 4091c235774Smrg # See whether we have the directive, and if yes, where. 4101c235774Smrg pos = match(line, rx "$") 4111c235774Smrg if (!pos) 4121c235774Smrg pos = match(line, rx "[^a-zA-Z0-9_]") 4131c235774Smrg 4141c235774Smrg # If there was no TAP directive, we have nothing more to do. 4151c235774Smrg if (!pos) 4161c235774Smrg return 4171c235774Smrg 4181c235774Smrg # Let`s now see if the TAP directive has been escaped. For example: 4191c235774Smrg # escaped: ok \# SKIP 4201c235774Smrg # not escaped: ok \\# SKIP 4211c235774Smrg # escaped: ok \\\\\# SKIP 4221c235774Smrg # not escaped: ok \ # SKIP 4231c235774Smrg if (substr(line, pos, 1) == "#") 4241c235774Smrg { 4251c235774Smrg bslash_count = 0 4261c235774Smrg for (i = pos; i > 1 && substr(line, i - 1, 1) == "\\"; i--) 4271c235774Smrg bslash_count += 1 4281c235774Smrg if (bslash_count % 2) 4291c235774Smrg return # Directive was escaped. 4301c235774Smrg } 4311c235774Smrg 4321c235774Smrg # Strip the directive and its explanation (if any) from the test 4331c235774Smrg # description. 4341c235774Smrg result_obj["description"] = substr(line, 1, pos - 1) 4351c235774Smrg # Now remove the test description from the line, that has been dealt 4361c235774Smrg # with already. 4371c235774Smrg line = substr(line, pos) 4381c235774Smrg # Strip the directive, and save its value (normalized to upper case). 4391c235774Smrg sub("^[ \t]*#[ \t]*", "", line) 4401c235774Smrg result_obj["directive"] = toupper(substr(line, 1, 4)) 4411c235774Smrg line = substr(line, 5) 4421c235774Smrg # Now get the explanation for the directive (if any), with leading 4431c235774Smrg # and trailing whitespace removed. 4441c235774Smrg sub("^[ \t]*", "", line) 4451c235774Smrg sub("[ \t]*$", "", line) 4461c235774Smrg result_obj["explanation"] = line 4471c235774Smrg} 4481c235774Smrg 4491c235774Smrgfunction get_test_exit_message(status) 4501c235774Smrg{ 4511c235774Smrg if (status == 0) 4521c235774Smrg return "" 4531c235774Smrg if (status !~ /^[1-9][0-9]*$/) 4541c235774Smrg abort("getting exit status") 4551c235774Smrg if (status < 127) 4561c235774Smrg exit_details = "" 4571c235774Smrg else if (status == 127) 4581c235774Smrg exit_details = " (command not found?)" 4591c235774Smrg else if (status >= 128 && status <= 255) 4601c235774Smrg exit_details = sprintf(" (terminated by signal %d?)", status - 128) 4611c235774Smrg else if (status > 256 && status <= 384) 4621c235774Smrg # We used to report an "abnormal termination" here, but some Korn 4631c235774Smrg # shells, when a child process die due to signal number n, can leave 4641c235774Smrg # in $? an exit status of 256+n instead of the more standard 128+n. 4651c235774Smrg # Apparently, both behaviours are allowed by POSIX (2008), so be 4661c235774Smrg # prepared to handle them both. See also Austing Group report ID 4671c235774Smrg # 0000051 <http://www.austingroupbugs.net/view.php?id=51> 4681c235774Smrg exit_details = sprintf(" (terminated by signal %d?)", status - 256) 4691c235774Smrg else 4701c235774Smrg # Never seen in practice. 4711c235774Smrg exit_details = " (abnormal termination)" 4721c235774Smrg return sprintf("exited with status %d%s", status, exit_details) 4731c235774Smrg} 4741c235774Smrg 4751c235774Smrgfunction write_test_results() 4761c235774Smrg{ 4771c235774Smrg print ":global-test-result: " get_global_test_result() > trs_file 4781c235774Smrg print ":recheck: " yn(must_recheck()) > trs_file 4791c235774Smrg print ":copy-in-global-log: " yn(copy_in_global_log()) > trs_file 4801c235774Smrg for (i = 0; i < test_results_index; i += 1) 4811c235774Smrg print ":test-result: " test_results_list[i] > trs_file 4821c235774Smrg close(trs_file); 4831c235774Smrg} 4841c235774Smrg 4851c235774SmrgBEGIN { 4861c235774Smrg 4871c235774Smrg## ------- ## 4881c235774Smrg## SETUP ## 4891c235774Smrg## ------- ## 4901c235774Smrg 4911c235774Smrg'"$init_colors"' 4921c235774Smrg 4931c235774Smrg# Properly initialized once the TAP plan is seen. 4941c235774Smrgplanned_tests = 0 4951c235774Smrg 4961c235774SmrgCOOKED_PASS = expect_failure ? "XPASS": "PASS"; 4971c235774SmrgCOOKED_FAIL = expect_failure ? "XFAIL": "FAIL"; 4981c235774Smrg 4991c235774Smrg# Enumeration-like constants to remember which kind of plan (if any) 5001c235774Smrg# has been seen. It is important that NO_PLAN evaluates "false" as 5011c235774Smrg# a boolean. 5021c235774SmrgNO_PLAN = 0 5031c235774SmrgEARLY_PLAN = 1 5041c235774SmrgLATE_PLAN = 2 5051c235774Smrg 5061c235774Smrgtestno = 0 # Number of test results seen so far. 5071c235774Smrgbailed_out = 0 # Whether a "Bail out!" directive has been seen. 5081c235774Smrg 5091c235774Smrg# Whether the TAP plan has been seen or not, and if yes, which kind 5101c235774Smrg# it is ("early" is seen before any test result, "late" otherwise). 5111c235774Smrgplan_seen = NO_PLAN 5121c235774Smrg 5131c235774Smrg## --------- ## 5141c235774Smrg## PARSING ## 5151c235774Smrg## --------- ## 5161c235774Smrg 5171c235774Smrgis_first_read = 1 5181c235774Smrg 5191c235774Smrgwhile (1) 5201c235774Smrg { 5211c235774Smrg # Involutions required so that we are able to read the exit status 5221c235774Smrg # from the last input line. 5231c235774Smrg st = getline 5241c235774Smrg if (st < 0) # I/O error. 5251c235774Smrg fatal("I/O error while reading from input stream") 5261c235774Smrg else if (st == 0) # End-of-input 5271c235774Smrg { 5281c235774Smrg if (is_first_read) 5291c235774Smrg abort("in input loop: only one input line") 5301c235774Smrg break 5311c235774Smrg } 5321c235774Smrg if (is_first_read) 5331c235774Smrg { 5341c235774Smrg is_first_read = 0 5351c235774Smrg nextline = $0 5361c235774Smrg continue 5371c235774Smrg } 5381c235774Smrg else 5391c235774Smrg { 5401c235774Smrg curline = nextline 5411c235774Smrg nextline = $0 5421c235774Smrg $0 = curline 5431c235774Smrg } 5441c235774Smrg # Copy any input line verbatim into the log file. 5451c235774Smrg print | "cat >&3" 5461c235774Smrg # Parsing of TAP input should stop after a "Bail out!" directive. 5471c235774Smrg if (bailed_out) 5481c235774Smrg continue 5491c235774Smrg 5501c235774Smrg # TAP test result. 5511c235774Smrg if ($0 ~ /^(not )?ok$/ || $0 ~ /^(not )?ok[^a-zA-Z0-9_]/) 5521c235774Smrg { 5531c235774Smrg testno += 1 5541c235774Smrg setup_result_obj($0) 5551c235774Smrg handle_tap_result() 5561c235774Smrg } 5571c235774Smrg # TAP plan (normal or "SKIP" without explanation). 5581c235774Smrg else if ($0 ~ /^1\.\.[0-9]+[ \t]*$/) 5591c235774Smrg { 5601c235774Smrg # The next two lines will put the number of planned tests in $0. 5611c235774Smrg sub("^1\\.\\.", "") 5621c235774Smrg sub("[^0-9]*$", "") 5631c235774Smrg handle_tap_plan($0, "") 5641c235774Smrg continue 5651c235774Smrg } 5661c235774Smrg # TAP "SKIP" plan, with an explanation. 5671c235774Smrg else if ($0 ~ /^1\.\.0+[ \t]*#/) 5681c235774Smrg { 5691c235774Smrg # The next lines will put the skip explanation in $0, stripping 5701c235774Smrg # any leading and trailing whitespace. This is a little more 5711c235774Smrg # tricky in truth, since we want to also strip a potential leading 5721c235774Smrg # "SKIP" string from the message. 5731c235774Smrg sub("^[^#]*#[ \t]*(SKIP[: \t][ \t]*)?", "") 5741c235774Smrg sub("[ \t]*$", ""); 5751c235774Smrg handle_tap_plan(0, $0) 5761c235774Smrg } 5771c235774Smrg # "Bail out!" magic. 5781c235774Smrg # Older versions of prove and TAP::Harness (e.g., 3.17) did not 5791c235774Smrg # recognize a "Bail out!" directive when preceded by leading 5801c235774Smrg # whitespace, but more modern versions (e.g., 3.23) do. So we 5811c235774Smrg # emulate the latter, "more modern" behaviour. 5821c235774Smrg else if ($0 ~ /^[ \t]*Bail out!/) 5831c235774Smrg { 5841c235774Smrg bailed_out = 1 5851c235774Smrg # Get the bailout message (if any), with leading and trailing 5861c235774Smrg # whitespace stripped. The message remains stored in `$0`. 5871c235774Smrg sub("^[ \t]*Bail out![ \t]*", ""); 5881c235774Smrg sub("[ \t]*$", ""); 5891c235774Smrg # Format the error message for the 5901c235774Smrg bailout_message = "Bail out!" 5911c235774Smrg if (length($0)) 5921c235774Smrg bailout_message = bailout_message " " $0 5931c235774Smrg testsuite_error(bailout_message) 5941c235774Smrg } 5951c235774Smrg # Maybe we have too look for dianogtic comments too. 5961c235774Smrg else if (comments != 0) 5971c235774Smrg { 5981c235774Smrg comment = extract_tap_comment($0); 5991c235774Smrg if (length(comment)) 6001c235774Smrg report("#", comment); 6011c235774Smrg } 6021c235774Smrg } 6031c235774Smrg 6041c235774Smrg## -------- ## 6051c235774Smrg## FINISH ## 6061c235774Smrg## -------- ## 6071c235774Smrg 6081c235774Smrg# A "Bail out!" directive should cause us to ignore any following TAP 6091c235774Smrg# error, as well as a non-zero exit status from the TAP producer. 6101c235774Smrgif (!bailed_out) 6111c235774Smrg { 6121c235774Smrg if (!plan_seen) 6131c235774Smrg { 6141c235774Smrg testsuite_error("missing test plan") 6151c235774Smrg } 6161c235774Smrg else if (planned_tests != testno) 6171c235774Smrg { 6181c235774Smrg bad_amount = testno > planned_tests ? "many" : "few" 6191c235774Smrg testsuite_error(sprintf("too %s tests run (expected %d, got %d)", 6201c235774Smrg bad_amount, planned_tests, testno)) 6211c235774Smrg } 6221c235774Smrg if (!ignore_exit) 6231c235774Smrg { 6241c235774Smrg # Fetch exit status from the last line. 6251c235774Smrg exit_message = get_test_exit_message(nextline) 6261c235774Smrg if (exit_message) 6271c235774Smrg testsuite_error(exit_message) 6281c235774Smrg } 6291c235774Smrg } 6301c235774Smrg 6311c235774Smrgwrite_test_results() 6321c235774Smrg 6331c235774Smrgexit 0 6341c235774Smrg 6351c235774Smrg} # End of "BEGIN" block. 6361c235774Smrg' 6371c235774Smrg 6381c235774Smrg# TODO: document that we consume the file descriptor 3 :-( 6391c235774Smrg} 3>"$log_file" 6401c235774Smrg 6411c235774Smrgtest $? -eq 0 || fatal "I/O or internal error" 6421c235774Smrg 6431c235774Smrg# Local Variables: 6441c235774Smrg# mode: shell-script 6451c235774Smrg# sh-indentation: 2 64674835918Smrg# eval: (add-hook 'before-save-hook 'time-stamp) 6471c235774Smrg# time-stamp-start: "scriptversion=" 6481c235774Smrg# time-stamp-format: "%:y-%02m-%02d.%02H" 64974835918Smrg# time-stamp-time-zone: "UTC0" 6501c235774Smrg# time-stamp-end: "; # UTC" 6511c235774Smrg# End: 652