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