Skip to content

Commit b5482d4

Browse files
committed
Add script to generate an Istanbul coverage report
1 parent 6e1902f commit b5482d4

File tree

1 file changed

+343
-0
lines changed

1 file changed

+343
-0
lines changed
Lines changed: 343 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,343 @@
1+
#!/usr/bin/env bash
2+
#
3+
# Script to generate an [Istanbul][1] code coverage report from [tape][2] tests.
4+
#
5+
# [1]: https://github.com/gotwarlost/istanbul
6+
# [2]: https://github.com/substack/tape
7+
8+
9+
# VARIABLES #
10+
11+
# Cache provided arguments:
12+
args="$@"
13+
14+
# Root project directory:
15+
root="$(dirname $0)/../../.."
16+
17+
# Base directory in which to search for tests:
18+
dir="$PWD"
19+
20+
# Test folder name:
21+
folder='test'
22+
23+
# Test filter:
24+
filter='.*/.*'
25+
26+
# Test pattern:
27+
pattern='test*.js'
28+
29+
# Global coverage variable:
30+
global='__coverage__'
31+
32+
# Output directory for coverage reports:
33+
output=""
34+
35+
# Define the command for `node`:
36+
if [[ -z "$NODE" ]]; then
37+
cmd=node
38+
else
39+
cmd="$NODE"
40+
fi
41+
42+
# Define the tape test runner executable for Istanbul instrumented source code:
43+
test_runner="${root}/tools/test-cov/tape-istanbul/bin/cli"
44+
45+
# Define the path to the `tap-spec` executable:
46+
tap_reporter="${root}/node_modules/.bin/tap-spec"
47+
48+
49+
# FUNCTIONS #
50+
51+
# Prints usage information.
52+
usage() {
53+
cat << EOF
54+
55+
Generate an Istanbul coverage report from tape tests.
56+
57+
Options:
58+
59+
-h, --help Print this message.
60+
--dir dirpath Base directory in which to search for tests.
61+
--folder name Test folder name.
62+
--filter filter Directory path filter.
63+
--pattern pattern Filename pattern.
64+
--global variable Global coverage variable name.
65+
--output dirpath Coverage report output directory.
66+
67+
EOF
68+
}
69+
70+
# Defines an error handler.
71+
#
72+
# $1 - error status
73+
on_error() {
74+
echo 'ERROR: An error was encountered during execution.' >&2
75+
cleanup
76+
exit "$1"
77+
}
78+
79+
# Runs clean-up tasks.
80+
cleanup() {
81+
echo '' >&2
82+
return 0
83+
}
84+
85+
# Creates a directory recursively.
86+
#
87+
# $1 - directory path
88+
create_dir() {
89+
mkdir -p "$1"
90+
if [[ "$?" -ne 0 ]]; then
91+
echo "ERROR: unable to create directory: $1."
92+
return 1
93+
fi
94+
return 0
95+
}
96+
97+
# Returns a list of instrumented source code test directories.
98+
#
99+
# $1 - base directory
100+
# $2 - name
101+
# $3 - filter
102+
get_dirs() {
103+
# Determine the host kernel:
104+
local kernel=$(uname -s)
105+
106+
# On Mac OSX, in order to use `|` and other regular expression operators, we need to use enhanced regular expression syntax (-E); see https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man7/re_format.7.html#//apple_ref/doc/man/7/re_format.
107+
local flag
108+
if [[ "${kernel}" = "Darwin" ]]; then
109+
flag='-E'
110+
fi
111+
112+
# Find matching directories.
113+
dirs="$(find ${flag} $1 -type 'd' -name $2 -regex $3)"
114+
if [[ "$?" -ne 0 ]]; then
115+
echo 'ERROR: unable to find test directories.' >&2
116+
return 1
117+
fi
118+
echo "${dirs}"
119+
return 0
120+
}
121+
122+
# "Slugifies" a string.
123+
#
124+
# $1 - string to slugify
125+
get_slug() {
126+
echo "$1" | sed -e 's/[^[:alnum:]]/_/g' | tr -s '-' | tr A-Z a-z
127+
}
128+
129+
# Generates a coverage report name.
130+
#
131+
# $1 - path
132+
# $2 - output directory
133+
get_report_name() {
134+
local slug="$(get_slug $1)"
135+
echo "$2/coverage_${slug}_$(date +'%Y%m%d_%H%M%S')_$RANDOM.json"
136+
137+
}
138+
139+
# Generates a coverage report.
140+
generate_report() {
141+
echo 'Generating a coverage report...' >&2
142+
make test-istanbul-report
143+
if [[ "$?" -ne 0 ]]; then
144+
echo 'ERROR: unable to generate coverage report.' >&2
145+
return 1
146+
fi
147+
return 0
148+
}
149+
150+
# Runs tests within a specified directory.
151+
#
152+
# $1 - directory path
153+
# $2 - filename pattern
154+
# $3 - global coverage variable
155+
# $4 - output directory path
156+
run_dir_tests() {
157+
local name="$(get_report_name $1 $4)"
158+
local pattern="$1/**/$2"
159+
160+
"${cmd}" "${test_runner}" --dir "$1" --global "$3" --output "${name}" "${pattern}"
161+
162+
if [[ "$?" -ne 0 ]]; then
163+
return 1
164+
fi
165+
return 0
166+
}
167+
168+
# Runs tests.
169+
run_tests() {
170+
local dirs="$(get_dirs ${dir} ${folder} ${filter})"
171+
if [[ "$?" -ne 0 ]]; then
172+
return 1
173+
fi
174+
for d in "${dirs}"; do
175+
echo '' >&2
176+
echo "Running tests in directory: $d" >&2
177+
echo '' >&2
178+
run_dir_tests "${d}" "${pattern}" "${global}" "${output}"
179+
if [[ "$?" -ne 0 ]]; then
180+
return 1
181+
fi
182+
done
183+
return 0
184+
}
185+
186+
# Main execution sequence.
187+
main() {
188+
create_dir "${output}"
189+
if [[ "$?" -ne 0 ]]; then
190+
on_error 1
191+
fi
192+
run_tests
193+
if [[ "$?" -ne 0 ]]; then
194+
on_error 1
195+
fi
196+
generate_report
197+
if [[ "$?" -ne 0 ]]; then
198+
on_error 1
199+
fi
200+
cleanup
201+
exit 0
202+
}
203+
204+
# Set an error handler to print captured output and perform any clean-up tasks:
205+
trap 'on_error' ERR
206+
207+
# Run in the top-level project directory:
208+
cd "${root}"
209+
210+
# Parse command-line options:
211+
while :; do
212+
case "$1" in
213+
-h|--help)
214+
usage
215+
exit 0
216+
;;
217+
--dir)
218+
if [[ -n "$2" ]]; then
219+
dir="$2"
220+
shift
221+
else
222+
echo 'ERROR: "--dir" requires a non-empty option argument.' >&2
223+
on_error 1
224+
fi
225+
;;
226+
--dir=?*)
227+
# Delete everything up to "=" and assign the remainder:
228+
dir="${1#*=}"
229+
;;
230+
--dir=)
231+
# Handle empty "--dir=" option:
232+
echo 'ERROR: "--dir" requires a non-empty option argument.' >&2
233+
on_error 1
234+
;;
235+
--folder)
236+
if [[ -n "$2" ]]; then
237+
folder="$2"
238+
shift
239+
else
240+
echo 'ERROR: "--folder" requires a non-empty option argument.' >&2
241+
on_error 1
242+
fi
243+
;;
244+
--folder=?*)
245+
# Delete everything up to "=" and assign the remainder:
246+
folder="${1#*=}"
247+
;;
248+
--folder=)
249+
# Handle empty "--folder=" option:
250+
echo 'ERROR: "--folder" requires a non-empty option argument.' >&2
251+
on_error 1
252+
;;
253+
--filter)
254+
if [[ -n "$2" ]]; then
255+
filter="$2"
256+
shift
257+
else
258+
echo 'ERROR: "--filter" requires a non-empty option argument.' >&2
259+
on_error 1
260+
fi
261+
;;
262+
--filter=?*)
263+
# Delete everything up to "=" and assign the remainder:
264+
filter="${1#*=}"
265+
;;
266+
--filter=)
267+
# Handle empty "--filter=" option:
268+
echo 'ERROR: "--filter" requires a non-empty option argument.' >&2
269+
on_error 1
270+
;;
271+
--pattern)
272+
if [[ -n "$2" ]]; then
273+
pattern="$2"
274+
shift
275+
else
276+
echo 'ERROR: "--pattern" requires a non-empty option argument.' >&2
277+
on_error 1
278+
fi
279+
;;
280+
--pattern=?*)
281+
# Delete everything up to "=" and assign the remainder:
282+
pattern="${1#*=}"
283+
;;
284+
--pattern=)
285+
# Handle empty "--pattern=" option:
286+
echo 'ERROR: "--pattern" requires a non-empty option argument.' >&2
287+
on_error 1
288+
;;
289+
--global)
290+
if [[ -n "$2" ]]; then
291+
global="$2"
292+
shift
293+
else
294+
echo 'ERROR: "--global" requires a non-empty option argument.' >&2
295+
on_error 1
296+
fi
297+
;;
298+
--global=?*)
299+
# Delete everything up to "=" and assign the remainder:
300+
global="${1#*=}"
301+
;;
302+
--global=)
303+
# Handle empty "--global=" option:
304+
echo 'ERROR: "--global" requires a non-empty option argument.' >&2
305+
on_error 1
306+
;;
307+
--output)
308+
if [[ -n "$2" ]]; then
309+
output="$2"
310+
shift
311+
else
312+
echo 'ERROR: "--output" requires a non-empty option argument.' >&2
313+
on_error 1
314+
fi
315+
;;
316+
--output=?*)
317+
# Delete everything up to "=" and assign the remainder:
318+
output="${1#*=}"
319+
;;
320+
--output=)
321+
# Handle empty "--output=" option:
322+
echo 'ERROR: "--output" requires a non-empty option argument.' >&2
323+
on_error 1
324+
;;
325+
--)
326+
# End of options:
327+
shift
328+
break
329+
;;
330+
-?*)
331+
# Handle unknown options:
332+
printf 'WARN: Unknown option (ignored): %s\n' "$1" >&2
333+
;;
334+
*)
335+
# Default case (end the loop if no more options):
336+
break
337+
esac
338+
339+
shift
340+
done
341+
342+
# Run main:
343+
main

0 commit comments

Comments
 (0)