Update our tapview copy.
authorEric S. Raymond <esr@thyrsus.com>
Tue, 28 Feb 2023 20:04:42 +0000 (15:04 -0500)
committerEric S. Raymond <esr@thyrsus.com>
Tue, 28 Feb 2023 20:04:42 +0000 (15:04 -0500)
tests/tapview

index 9acd0c4e25b1a627af8543d73c7022a1da4d104f..ca984eb9e91eea2f77ee841cf32f49d51aa78f1d 100755 (executable)
@@ -6,28 +6,14 @@
 # This code is intended to be embedded in your project. The author
 # grants permission for it to be distributed under the prevailing
 # license of your project if you choose, provided that license is
-# OSD-compliant; otherwise the following SPDX tag applies.
+# OSD-compliant; otherwise the following SPDX tag incorporates a
+# license by reference.
 #
 # SPDX-License-Identifier: BSD-2-Clause
 #
-# This is version 1.0
+# This is version 1.6
 # A newer version may be available at https://gitlab.com/esr/tapview
 #
-# POSIX allows but does not mandate that -n suppresses emission of a
-# trailing newline in echo. Thus, some shell builtin echos don't do
-# that.  Cope gracefully.
-# shellcheck disable=SC2039
-if [ "$(echo -n "a"; echo "b")" != "ab" ]
-then
-    ECHO="echo"
-elif [ "$(/bin/echo -n "a"; /bin/echo "b")" = "ab" ]
-then
-    ECHO="/bin/echo"
-else
-    echo "tapview: bailing out, your echo lacks -n support."
-    exit 3
-fi
-
 OK="."
 FAIL="F"
 SKIP="s"
@@ -36,44 +22,94 @@ TODO_OK="u"
 
 ship_char() {
     # shellcheck disable=SC2039
-    "${ECHO}" -n "$1"
+    printf '%s' "$1"   # https://www.etalabs.net/sh_tricks.html
 }
 
 ship_line() {
     report="${report}${1}\n"
 }
 
+ship_error() {
+    # Terminate dot display and bail out with error
+    if [ "${testcount}" -gt 0 ]
+    then
+       echo ""
+    fi
+    report="${report}${1}\n"
+    echo "${report}"
+    exit 1
+}
+
 testcount=0
 failcount=0
 skipcount=0
 todocount=0
-test_before_plan=no
-test_after_plan=no
-expect=""
 status=0
-
 report=""
 IFS=""
-state=start
+ln=0
+state=plaintext
+
+# shellcheck disable=SC2086
+context_get () { printenv "ctx_${1}${depth}"; }
+context_set () { export "ctx_${1}${depth}=${2}"; }
+
+context_push () {
+    context_set plan ""
+    context_set count 0
+    context_set test_before_plan no
+    context_set test_after_plan no
+    context_set expect ""
+    context_set bail no
+    context_set strict no
+}
+
+context_pop () {
+    if [ "$(context_get count)" -gt 0 ] && [ -z "$(context_get plan)" ]
+    then
+       ship_line "Missing a plan at line ${ln}."
+       status=1
+    elif [ "$(context_get test_before_plan)" = "yes" ] && [ "$(context_get test_after_plan)" = "yes" ] 
+    then
+       ship_line "A plan line may only be placed before or after all tests."
+       status=1
+    elif [ "$(context_get plan)" != "" ] && [ "$(context_get expect)" -gt "$(context_get count)" ]
+    then
+       ship_line "Expected $(context_get expect) tests but only ${testcount} ran."
+       status=1
+    fi
+}
+
+depth=0
+context_push
+
 while read -r line
 do
+    ln=$((ln + 1))
+    # Process bailout
     if expr "$line" : "Bail out!" >/dev/null
     then
        ship_line "$line"
        status=2
        break
     fi
-    if expr "$line" : '1\.\.[0-9][0-9]*' >/dev/null >/dev/null
+    # Use the current indent to choose a scope level
+    indent=$(expr "$line" : '[         ]*')
+    if [ "${indent}" -lt "${depth}" ]
     then
-       if [ "$expect" != "" ]
+       context_pop
+        depth="${indent}"
+    elif [ "${indent}" -gt "${depth}" ]
+    then
+       depth="${indent}"
+       context_push
+    fi
+    # Process a plan line
+    if expr "$line" : '[       ]*1\.\.[0-9][0-9]*' >/dev/null
+    then
+       if [ "$(context_get plan)" != "" ]
        then
-           if [ "${testcount}" -gt 0 ]
-           then
-               echo ""
-           fi
-           ship_line "Cannot have more than one plan line."
-           echo "${report}"
-           exit 1
+           ship_error "tapview: cannot have more than one plan line."
        fi
        if expr "$line" : ".* *SKIP" >/dev/null || expr "$line" : ".* *skip" >/dev/null
        then
@@ -81,62 +117,114 @@ do
            echo "${report}"
            exit 1      # Not specified in the standard whether this should exit 1 or 0
        fi
-       expect=$(expr "$line" : '1\.\.\([0-9][0-9]*\)')
+       context_set plan "${line}"
+       context_set expect "$(expr "$line" : '[         ]*1\.\.\([0-9][0-9]*\)')"
        continue
+    elif expr "$line" : '[     ]*[0-9][0-9]*\.\.[0-9][0-9]*' >/dev/null
+    then
+        echo "Ill-formed plan line at ${ln}"
+        exit 1
     fi
-    if expr "$line" : "ok" >/dev/null
+    # Check for out-of-order test point numbers with the sequence (TAP 14)
+    testpoint=$(expr "$line" : '.*ok  *\([0-9][0-9]*\)')
+    if [ "${testpoint}" != "" ] && [ "$(context_get expect)" != "" ] && [ "${testpoint}" -gt "$(context_get expect)" ]
     then
+       ship_error "tapview: testpoint number ${testpoint} is out of range for plan $(context_get plan)."
+    fi
+    # Process an ok line
+    if expr "$line" : "[       ]*ok" >/dev/null
+    then
+       context_set count $(($(context_get count) + 1)) 
        testcount=$((testcount + 1))
-       if [ "$expect" = "" ]
+       if [ "$(context_get plan)" = "" ]
        then
-           test_before_plan=yes
+           context_set test_before_plan yes
        else
-           test_after_plan=yes
+           context_set test_after_plan yes
        fi
-       if expr "$line" : ".*# *TODO" >/dev/null || expr "$line" : ".*# *todo" >/dev/null
+       if expr "$line" : "[^#]* # *TODO" >/dev/null || expr "$line" : "[^#]* # *todo" >/dev/null
        then
            ship_char ${TODO_OK}
            ship_line "$line"
            todocount=$((todocount + 1))
-       elif expr "$line" : ".*# *SKIP" >/dev/null || expr "$line" : ".*# *skip" >/dev/null
+           if expr "$line" : "[^#]*#[^ ]" >/dev/null
+           then
+               ship_line "Suspicious comment leader at ${ln}"
+           fi
+       elif expr "$line" : "[^#]* # *SKIP" >/dev/null || expr "$line" : "[^#]* # *skip" >/dev/null
        then
            ship_char ${SKIP}
            ship_line "$line"
            skipcount=$((skipcount + 1))
+           if expr "$line" : "[^#]*#[^ ]" >/dev/null
+           then
+               ship_line "Suspicious comment leader at ${ln}"
+           fi
        else
            ship_char ${OK}
        fi
-       state=ok
+       state=plaintext
        continue
     fi
-    if expr "$line" : "not ok" >/dev/null
+    # Process a not-ok line
+    if expr "$line" : "[       ]*not ok" >/dev/null
     then
+       context_set count $(($(context_get count) + 1)) 
        testcount=$((testcount + 1))
-       if [ "$expect" = "" ]
+       if [ "$(context_get plan)" = "" ]
        then
-           test_before_plan=yes
+           context_set test_before_plan yes
        else
-           test_after_plan=yes
+           context_set test_after_plan yes
        fi
-       if expr "$line" : ".*# *SKIP" >/dev/null || expr "$line" : ".*# *skip" >/dev/null
+       if expr "$line" : "[^#]* # *SKIP" >/dev/null || expr "$line" : "[^#]* # *skip" >/dev/null
        then
            ship_char "${SKIP}"
-           state=ok
+           state=plaintext
            skipcount=$((skipcount + 1))
+           if expr "$line" : "[^#]* #[^ ]" >/dev/null
+           then
+               ship_line "Suspicious comment leader at lime ${ln}"
+           fi
            continue
        fi
-       if expr "$line" : ".*# *TODO" >/dev/null || expr "$line" : ".*# *todo" >/dev/null
+       if expr "$line" : "[^#]* # *TODO" >/dev/null || expr "$line" : "[^#]* # *todo" >/dev/null
        then
            ship_char ${TODO_NOT_OK}
-           state=ok
+           state=plaintext
            todocount=$((todocount + 1))
+           if expr "$line" : "[^#]* #[^ ]" >/dev/null
+           then
+               ship_line "Suspicious comment leader at line ${ln}"
+           fi
            continue
        fi
        ship_char "${FAIL}"
        ship_line "$line"
-       state=not_ok
+       state=plaintext
        failcount=$((failcount + 1))
        status=1
+       if [ "$(context_get bail)" = yes ]
+       then
+           ship_line "Bailing out on line ${ln} due to +bail pragma."
+           break
+       fi
+       continue
+    fi
+    # Process a TAP 14 pragma
+    if expr "$line" : "pragma" >/dev/null
+    then
+       unset IFS
+       # shellcheck disable=SC2086
+       set -- $line
+       case "$2" in
+           +bail) context_set bail yes;;
+           -bail) context_set bail yes;;
+           +strict) context_set strict yes;;
+           -strict) context_set strict yes;;
+           *) ship_line "Pragma '$line' ignored";;
+       esac
+       IFS=""
        continue
     fi
     # shellcheck disable=SC2166
@@ -145,34 +233,35 @@ do
        ship_line "$line"
        if expr "$line" : '[    ]*\.\.\.' >/dev/null
        then
-           state=ok
+           state=plaintext
+       else
+           continue
        fi
     elif expr "$line" : "[     ]*---" >/dev/null
     then
        ship_line "$line"
        state=yaml
+       continue
+    fi
+    # Ignore blank lines and comments
+    if [ -z "$line" ] || expr "$line" : '[     ]+$' >/dev/null || expr "$line" : "#" >/dev/null
+    then
+       continue
+    fi
+    # Any line that is not a valid plan, test result, pragma,
+    # or comment lands here.
+    if [ "$(context_get strict)" = yes ]
+    then
+       ship_line "Bailing out on line ${ln} due to +strict pragma"
+       status=1
+       break
     fi
 done
 
 /bin/echo ""
 
-if [ -z "$expect" ]
-then
-    ship_line "Missing a plan."
-    status=1
-elif [ "$test_before_plan" = "yes" ] && [ "$test_after_plan" = "yes" ] 
-then
-    ship_line "A plan line may only be placed before or after all tests."
-    status=1
-elif [ "${expect}" -gt "${testcount}" ]
-then
-    ship_line "Expected ${expect} tests but only ${testcount} ran."
-    status=1
-elif [ "${expect}" -lt "${testcount}" ]
-then
-    ship_line "Expected ${expect} tests but ${testcount} ran."
-    status=1
-fi
+depth=0
+context_pop
 
 report="${report}${testcount} tests, ${failcount} failures"
 if [ "$todocount" != 0 ]