# 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"
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
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
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 ]