Ready to ship 2.8
[super-star-trek.git] / test / tapview
1 #! /bin/sh
2 # tapview - a TAP (Test Anything Protocol) viewer in pure POSIX shell
3 #
4 # SPDX-FileCopyrightText: (C) Eric S. Raymond <esr@thyrsus.com>
5 # SPDX-License-Identifier: MIT-0
6 #
7 # This code is intended to be embedded in your project. The author
8 # grants permission for it to be distributed under the prevailing
9 # license of your project if you choose, provided that license is
10 # OSD-compliant; otherwise the following SPDX tag incorporates the
11 # MIT No Attribution license by reference.
12 #
13 # A newer version may be available at https://gitlab.com/esr/tapview
14 # Check your last commit dqte for this file against the commit list
15 # there to see if it might be a good idea to update.
16 #
17 OK="."
18 FAIL="F"
19 SKIP="s"
20 TODO_NOT_OK="x"
21 TODO_OK="u"
22
23 LF='
24 '
25
26 ship_char() {
27     # shellcheck disable=SC2039
28     printf '%s' "$1"    # https://www.etalabs.net/sh_tricks.html
29 }
30
31 ship_line() {
32     report="${report}${1}$LF"
33 }
34
35 ship_error() {
36     # Terminate dot display and bail out with error
37     if [ "${testcount}" -gt 0 ]
38     then
39         echo ""
40     fi
41     report="${report}${1}$LF"
42     echo "${report}"
43     exit 1
44 }
45
46 testcount=0
47 failcount=0
48 skipcount=0
49 todocount=0
50 status=0
51 report=""
52 IFS=""
53 ln=0
54 state=plaintext
55
56 # shellcheck disable=SC2086
57 context_get () { printenv "ctx_${1}${depth}"; }
58 context_set () { export "ctx_${1}${depth}=${2}"; }
59
60 context_push () {
61     context_set plan ""
62     context_set count 0
63     context_set test_before_plan no
64     context_set test_after_plan no
65     context_set expect ""
66     context_set bail no
67     context_set strict no
68 }
69
70 context_pop () {
71     if [ "$(context_get count)" -gt 0 ] && [ -z "$(context_get plan)" ]
72     then
73         ship_line "Missing a plan at line ${ln}."
74         status=1
75     elif [ "$(context_get test_before_plan)" = "yes" ] && [ "$(context_get test_after_plan)" = "yes" ] 
76     then
77         ship_line "A plan line may only be placed before or after all tests."
78         status=1
79     elif [ "$(context_get plan)" != "" ] && [ "$(context_get expect)" -gt "$(context_get count)" ]
80     then
81         ship_line "Expected $(context_get expect) tests but only ${testcount} ran."
82         status=1
83      elif [ "$(context_get plan)" != "" ] && [ "$(context_get expect)" -lt "$(context_get count)" ]
84     then
85         ship_line "${testcount} ran but $(context_get expect) expected."
86         status=1
87     fi
88 }
89
90 depth=0
91 context_push
92
93 while read -r line
94 do
95     ln=$((ln + 1))
96     # Process bailout
97     if expr "$line" : "Bail out!" >/dev/null
98     then
99         ship_line "$line"
100         status=2
101         break
102     fi
103     # Use the current indent to choose a scope level
104     indent=$(expr "$line" : '[  ]*')
105     if [ "${indent}" -lt "${depth}" ]
106     then
107         context_pop
108         depth="${indent}"
109     elif [ "${indent}" -gt "${depth}" ]
110     then
111         depth="${indent}"
112         context_push
113     fi
114     # Process a plan line
115     if expr "$line" : '[        ]*1\.\.[0-9][0-9]*' >/dev/null
116     then
117         if [ "$(context_get plan)" != "" ]
118         then
119             ship_error "tapview: cannot have more than one plan line."
120         fi
121         if expr "$line" : ".* *SKIP" >/dev/null || expr "$line" : ".* *skip" >/dev/null
122         then
123             ship_line "$line"
124             echo "${report}"
125             exit 1      # Not specified in the standard whether this should exit 1 or 0
126         fi
127         context_set plan "${line}"
128         context_set expect "$(expr "$line" : '[         ]*1\.\.\([0-9][0-9]*\)')"
129         continue
130     elif expr "$line" : '[      ]*[0-9][0-9]*\.\.[0-9][0-9]*' >/dev/null
131     then
132          echo "Ill-formed plan line at ${ln}"
133          exit 1
134     fi
135     # Check for out-of-order test point numbers with the sequence (TAP 14)
136     testpoint=$(expr "$line" : '.*ok  *\([0-9][0-9]*\)')
137     if [ "${testpoint}" != "" ] && [ "$(context_get expect)" != "" ] && [ "${testpoint}" -gt "$(context_get expect)" ]
138     then
139         ship_error "tapview: testpoint number ${testpoint} is out of range for plan $(context_get plan)."
140     fi
141     # Process an ok line
142     if expr "$line" : "[        ]*ok" >/dev/null
143     then
144         context_set count $(($(context_get count) + 1)) 
145         testcount=$((testcount + 1))
146         if [ "$(context_get plan)" = "" ]
147         then
148             context_set test_before_plan yes
149         else
150             context_set test_after_plan yes
151         fi
152         if expr "$line" : "[^#]* # *TODO" >/dev/null || expr "$line" : "[^#]* # *todo" >/dev/null
153         then
154             ship_char ${TODO_OK}
155             ship_line "$line"
156             todocount=$((todocount + 1))
157             if expr "$line" : "[^#]*#[^ ]" >/dev/null
158             then
159                 ship_line "Suspicious comment leader at ${ln}"
160             fi
161         elif expr "$line" : "[^#]* # *SKIP" >/dev/null || expr "$line" : "[^#]* # *skip" >/dev/null
162         then
163             ship_char ${SKIP}
164             ship_line "$line"
165             skipcount=$((skipcount + 1))
166             if expr "$line" : "[^#]*#[^ ]" >/dev/null
167             then
168                 ship_line "Suspicious comment leader at ${ln}"
169             fi
170         else
171             ship_char ${OK}
172         fi
173         state=plaintext
174         continue
175     fi
176     # Process a not-ok line
177     if expr "$line" : "[        ]*not ok" >/dev/null
178     then
179         context_set count $(($(context_get count) + 1)) 
180         testcount=$((testcount + 1))
181         if [ "$(context_get plan)" = "" ]
182         then
183             context_set test_before_plan yes
184         else
185             context_set test_after_plan yes
186         fi
187         if expr "$line" : "[^#]* # *SKIP" >/dev/null || expr "$line" : "[^#]* # *skip" >/dev/null
188         then
189             ship_char "${SKIP}"
190             state=plaintext
191             skipcount=$((skipcount + 1))
192             if expr "$line" : "[^#]* #[^ ]" >/dev/null
193             then
194                 ship_line "Suspicious comment leader at lime ${ln}"
195             fi
196             continue
197         fi
198         if expr "$line" : "[^#]* # *TODO" >/dev/null || expr "$line" : "[^#]* # *todo" >/dev/null
199         then
200             ship_char ${TODO_NOT_OK}
201             state=plaintext
202             todocount=$((todocount + 1))
203             if expr "$line" : "[^#]* #[^ ]" >/dev/null
204             then
205                 ship_line "Suspicious comment leader at line ${ln}"
206             fi
207             continue
208         fi
209         ship_char "${FAIL}"
210         ship_line "$line"
211         state=plaintext
212         failcount=$((failcount + 1))
213         status=1
214         if [ "$(context_get bail)" = yes ]
215         then
216             ship_line "Bailing out on line ${ln} due to +bail pragma."
217             break
218         fi
219         continue
220     fi
221     # Process a TAP 14 pragma
222     if expr "$line" : "pragma" >/dev/null
223     then
224         unset IFS
225         # shellcheck disable=SC2086
226         set -- $line
227         case "$2" in
228             +bail) context_set bail yes;;
229             -bail) context_set bail yes;;
230             +strict) context_set strict yes;;
231             -strict) context_set strict yes;;
232             *) ship_line "Pragma '$line' ignored";;
233         esac
234         IFS=""
235         continue
236     fi
237     # shellcheck disable=SC2166
238     if [ "${state}" = "yaml" ]
239     then
240         ship_line "$line"
241         if expr "$line" : '[    ]*\.\.\.' >/dev/null
242         then
243             state=plaintext
244         else
245             continue
246         fi
247     elif expr "$line" : "[      ]*---" >/dev/null
248     then
249         ship_line "$line"
250         state=yaml
251         continue
252     fi
253     # Ignore blank lines and comments
254     if [ -z "$line" ] || expr "$line" : '[      ]+$' >/dev/null || expr "$line" : "#" >/dev/null
255     then
256         continue
257     fi
258     # Any line that is not a valid plan, test result, pragma,
259     # or comment lands here.
260     if [ "$(context_get strict)" = yes ]
261     then
262         ship_line "Bailing out on line ${ln} due to +strict pragma"
263         status=1
264         break
265     fi
266 done
267
268 /bin/echo ""
269
270 depth=0
271 context_pop
272
273 report="${report}${testcount} tests, ${failcount} failures"
274 if [ "$todocount" != 0 ]
275 then
276     report="${report}, ${todocount} TODOs"
277 fi
278 if [ "$skipcount" != 0 ]
279 then
280     report="${report}, ${skipcount} SKIPs"
281 fi
282
283 echo "${report}."
284
285 exit "${status}"
286
287 # end