@bats-hardened/bats 1.14.1-alpha02
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of @bats-hardened/bats might be problematic. Click here for more details.
- package/LICENSE.md +53 -0
- package/README.md +135 -0
- package/bin/bats +75 -0
- package/install.sh +29 -0
- package/lib/bats-core/common.bash +278 -0
- package/lib/bats-core/formatter.bash +151 -0
- package/lib/bats-core/preprocessing.bash +19 -0
- package/lib/bats-core/semaphore.bash +113 -0
- package/lib/bats-core/test_functions.bash +539 -0
- package/lib/bats-core/tracing.bash +425 -0
- package/lib/bats-core/validator.bash +37 -0
- package/lib/bats-core/warnings.bash +44 -0
- package/libexec/bats-core/bats +555 -0
- package/libexec/bats-core/bats-exec-file +402 -0
- package/libexec/bats-core/bats-exec-suite +346 -0
- package/libexec/bats-core/bats-exec-test +383 -0
- package/libexec/bats-core/bats-format-cat +6 -0
- package/libexec/bats-core/bats-format-junit +293 -0
- package/libexec/bats-core/bats-format-pretty +360 -0
- package/libexec/bats-core/bats-format-tap +55 -0
- package/libexec/bats-core/bats-format-tap13 +89 -0
- package/libexec/bats-core/bats-gather-tests +402 -0
- package/libexec/bats-core/bats-preprocess +118 -0
- package/man/Makefile +23 -0
- package/man/README.md +5 -0
- package/man/bats.1 +143 -0
- package/man/bats.1.ronn +201 -0
- package/man/bats.7 +300 -0
- package/man/bats.7.ronn +404 -0
- package/package.json +36 -0
- package/uninstall.sh +63 -0
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -eET
|
|
3
|
+
|
|
4
|
+
# Variables used in other scripts.
|
|
5
|
+
BATS_ENABLE_TIMING=''
|
|
6
|
+
BATS_EXTENDED_SYNTAX=''
|
|
7
|
+
BATS_TRACE_LEVEL="${BATS_TRACE_LEVEL:-0}"
|
|
8
|
+
BATS_PRINT_OUTPUT_ON_FAILURE="${BATS_PRINT_OUTPUT_ON_FAILURE:-}"
|
|
9
|
+
BATS_SHOW_OUTPUT_OF_SUCCEEDING_TESTS="${BATS_SHOW_OUTPUT_OF_SUCCEEDING_TESTS:-}"
|
|
10
|
+
BATS_VERBOSE_RUN="${BATS_VERBOSE_RUN:-}"
|
|
11
|
+
BATS_GATHER_TEST_OUTPUTS_IN="${BATS_GATHER_TEST_OUTPUTS_IN:-}"
|
|
12
|
+
BATS_TEST_NAME_PREFIX="${BATS_TEST_NAME_PREFIX:-}"
|
|
13
|
+
|
|
14
|
+
while [[ "$#" -ne 0 ]]; do
|
|
15
|
+
case "$1" in
|
|
16
|
+
-T)
|
|
17
|
+
BATS_ENABLE_TIMING='-T'
|
|
18
|
+
;;
|
|
19
|
+
-x)
|
|
20
|
+
# shellcheck disable=SC2034
|
|
21
|
+
BATS_EXTENDED_SYNTAX='-x'
|
|
22
|
+
;;
|
|
23
|
+
--dummy-flag) ;;
|
|
24
|
+
|
|
25
|
+
--trace)
|
|
26
|
+
((++BATS_TRACE_LEVEL)) # avoid returning 0
|
|
27
|
+
;;
|
|
28
|
+
--print-output-on-failure)
|
|
29
|
+
BATS_PRINT_OUTPUT_ON_FAILURE=1
|
|
30
|
+
;;
|
|
31
|
+
--show-output-of-passing-tests)
|
|
32
|
+
BATS_SHOW_OUTPUT_OF_SUCCEEDING_TESTS=1
|
|
33
|
+
;;
|
|
34
|
+
--verbose-run)
|
|
35
|
+
BATS_VERBOSE_RUN=1
|
|
36
|
+
;;
|
|
37
|
+
--gather-test-outputs-in)
|
|
38
|
+
shift
|
|
39
|
+
BATS_GATHER_TEST_OUTPUTS_IN="$1"
|
|
40
|
+
;;
|
|
41
|
+
*)
|
|
42
|
+
break
|
|
43
|
+
;;
|
|
44
|
+
esac
|
|
45
|
+
shift
|
|
46
|
+
done
|
|
47
|
+
|
|
48
|
+
export BATS_TEST_FILENAME="$1"
|
|
49
|
+
export BATS_TEST_NAME="$2"
|
|
50
|
+
export BATS_SUITE_TEST_NUMBER="$3"
|
|
51
|
+
export BATS_TEST_NUMBER="$4"
|
|
52
|
+
BATS_TEST_TRY_NUMBER="$5"
|
|
53
|
+
|
|
54
|
+
if [[ -z "$BATS_TEST_FILENAME" ]]; then
|
|
55
|
+
printf 'usage: bats-exec-test <filename>\n' >&2
|
|
56
|
+
exit 1
|
|
57
|
+
elif [[ ! -f "$BATS_TEST_FILENAME" ]]; then
|
|
58
|
+
printf 'bats: %s does not exist\n' "$BATS_TEST_FILENAME" >&2
|
|
59
|
+
exit 1
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
bats_create_test_tmpdirs() {
|
|
63
|
+
local tests_tmpdir="${BATS_RUN_TMPDIR}/test"
|
|
64
|
+
if ! mkdir -p "$tests_tmpdir"; then
|
|
65
|
+
printf 'Failed to create: %s\n' "$tests_tmpdir" >&2
|
|
66
|
+
exit 1
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
BATS_TEST_TMPDIR="$tests_tmpdir/$BATS_SUITE_TEST_NUMBER"
|
|
70
|
+
if ! mkdir "$BATS_TEST_TMPDIR"; then
|
|
71
|
+
printf 'Failed to create BATS_TEST_TMPDIR%d: %s\n' "$BATS_TEST_TRY_NUMBER" "$BATS_TEST_TMPDIR" >&2
|
|
72
|
+
exit 1
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
printf "%s\n" "$BATS_TEST_NAME" >> "$BATS_TEST_TMPDIR.name" # append name in case of test retries
|
|
76
|
+
|
|
77
|
+
export BATS_TEST_TMPDIR
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
# load the test helper functions like `load` or `run` that are needed to run a (preprocessed) .bats file without bash errors
|
|
81
|
+
# shellcheck source=lib/bats-core/test_functions.bash disable=SC2153
|
|
82
|
+
source "$BATS_ROOT/$BATS_LIBDIR/bats-core/test_functions.bash"
|
|
83
|
+
_bats_test_functions_setup "$BATS_TEST_NUMBER"
|
|
84
|
+
|
|
85
|
+
# shellcheck source=lib/bats-core/tracing.bash disable=SC2153
|
|
86
|
+
source "$BATS_ROOT/$BATS_LIBDIR/bats-core/tracing.bash"
|
|
87
|
+
|
|
88
|
+
bats_teardown_trap() {
|
|
89
|
+
bats_check_status_from_trap
|
|
90
|
+
local bats_teardown_trap_status=0
|
|
91
|
+
|
|
92
|
+
bats_set_stacktrace_limit
|
|
93
|
+
|
|
94
|
+
# mark the start of this function to distinguish where skip is called
|
|
95
|
+
# parameter 1 will signify the reason why this function was called
|
|
96
|
+
# this is used to identify when this is called as exit trap function
|
|
97
|
+
BATS_TEARDOWN_STARTED=${1:-1}
|
|
98
|
+
teardown >>"$BATS_OUT" 2>&1 || bats_teardown_trap_status="$?"
|
|
99
|
+
|
|
100
|
+
if [[ $bats_teardown_trap_status -eq 0 ]]; then
|
|
101
|
+
BATS_TEARDOWN_COMPLETED=1
|
|
102
|
+
elif [[ -n "$BATS_TEST_COMPLETED" ]]; then
|
|
103
|
+
BATS_DEBUG_LAST_STACK_TRACE_IS_VALID=1
|
|
104
|
+
BATS_ERROR_STATUS="$bats_teardown_trap_status"
|
|
105
|
+
fi
|
|
106
|
+
|
|
107
|
+
bats_exit_trap
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
# shellcheck source=lib/bats-core/common.bash
|
|
111
|
+
source "$BATS_ROOT/$BATS_LIBDIR/bats-core/common.bash"
|
|
112
|
+
|
|
113
|
+
bats_exit_trap() {
|
|
114
|
+
local status
|
|
115
|
+
local exit_metadata=''
|
|
116
|
+
trap - ERR EXIT
|
|
117
|
+
if [[ -n "${BATS_TEST_TIMEOUT:-}" ]]; then
|
|
118
|
+
# Kill the watchdog in the case of of kernel finished before the timeout
|
|
119
|
+
bats_abort_timeout_countdown || status=1
|
|
120
|
+
fi
|
|
121
|
+
|
|
122
|
+
if [[ -n "$BATS_TEST_SKIPPED" ]]; then
|
|
123
|
+
exit_metadata=' # skip'
|
|
124
|
+
if [[ "$BATS_TEST_SKIPPED" != '1' ]]; then
|
|
125
|
+
exit_metadata+=" $BATS_TEST_SKIPPED"
|
|
126
|
+
fi
|
|
127
|
+
elif [[ "${BATS_TIMED_OUT-NOTSET}" != NOTSET ]]; then
|
|
128
|
+
exit_metadata=" # timeout after ${BATS_TEST_TIMEOUT}s"
|
|
129
|
+
fi
|
|
130
|
+
|
|
131
|
+
BATS_TEST_TIME=''
|
|
132
|
+
if [[ -n "$BATS_ENABLE_TIMING" ]]; then
|
|
133
|
+
get_mills_since_epoch BATS_TEST_END_TIME
|
|
134
|
+
BATS_TEST_TIME=" in "$((BATS_TEST_END_TIME - BATS_TEST_START_TIME))"ms"
|
|
135
|
+
fi
|
|
136
|
+
|
|
137
|
+
local print_bats_out="${BATS_SHOW_OUTPUT_OF_SUCCEEDING_TESTS}"
|
|
138
|
+
|
|
139
|
+
local should_retry=''
|
|
140
|
+
if [[ -z "$BATS_TEST_COMPLETED" || -z "$BATS_TEARDOWN_COMPLETED" || "${BATS_INTERRUPTED-NOTSET}" != NOTSET ]]; then
|
|
141
|
+
if [[ "$BATS_ERROR_STATUS" -eq 0 ]]; then
|
|
142
|
+
# For some versions of bash, `$?` may not be set properly for some error
|
|
143
|
+
# conditions before triggering the EXIT trap directly (see #72 and #81).
|
|
144
|
+
# Thanks to the `BATS_TEARDOWN_COMPLETED` signal, this will pinpoint such
|
|
145
|
+
# errors if they happen during `teardown()` when `bats_perform_test` calls
|
|
146
|
+
# `bats_teardown_trap` directly after the test itself passes.
|
|
147
|
+
#
|
|
148
|
+
# If instead the test fails, and the `teardown()` error happens while
|
|
149
|
+
# `bats_teardown_trap` runs as the EXIT trap, the test will fail with no
|
|
150
|
+
# output, since there's no way to reach the `bats_exit_trap` call.
|
|
151
|
+
BATS_ERROR_STATUS=1
|
|
152
|
+
fi
|
|
153
|
+
if bats_should_retry_test; then
|
|
154
|
+
should_retry=1
|
|
155
|
+
status=126 # signify retry
|
|
156
|
+
rm -r "$BATS_TEST_TMPDIR" # clean up for retry
|
|
157
|
+
else
|
|
158
|
+
printf 'not ok %d %s%s\n' "$BATS_SUITE_TEST_NUMBER" "${BATS_TEST_NAME_PREFIX:-}${BATS_TEST_DESCRIPTION}${BATS_TEST_TIME}" "$exit_metadata" >&3
|
|
159
|
+
if (( ${#BATS_TEST_TAGS[@]} > 0 )); then
|
|
160
|
+
printf '# tags:'
|
|
161
|
+
printf ' %s' "${BATS_TEST_TAGS[@]}"
|
|
162
|
+
printf '\n'
|
|
163
|
+
fi >&3
|
|
164
|
+
local stack_trace
|
|
165
|
+
bats_get_failure_stack_trace stack_trace
|
|
166
|
+
bats_print_stack_trace "${stack_trace[@]}" >&3
|
|
167
|
+
bats_print_failed_command "${stack_trace[@]}" >&3
|
|
168
|
+
|
|
169
|
+
if [[ $BATS_PRINT_OUTPUT_ON_FAILURE ]]; then
|
|
170
|
+
if [[ -n "${output:-}" ]]; then
|
|
171
|
+
printf "Last output:\n%s\n" "$output"
|
|
172
|
+
fi
|
|
173
|
+
if [[ -n "${stderr:-}" ]]; then
|
|
174
|
+
printf "Last stderr: \n%s\n" "$stderr"
|
|
175
|
+
fi
|
|
176
|
+
fi >>"$BATS_OUT"
|
|
177
|
+
|
|
178
|
+
print_bats_out=1
|
|
179
|
+
status=1
|
|
180
|
+
local state=failed
|
|
181
|
+
fi
|
|
182
|
+
else
|
|
183
|
+
printf 'ok %d %s%s\n' "$BATS_SUITE_TEST_NUMBER" "${BATS_TEST_NAME_PREFIX:-}${BATS_TEST_DESCRIPTION}${BATS_TEST_TIME}" \
|
|
184
|
+
"$exit_metadata" >&3
|
|
185
|
+
status=0
|
|
186
|
+
local state=passed
|
|
187
|
+
fi
|
|
188
|
+
|
|
189
|
+
if [[ -z "$should_retry" ]]; then
|
|
190
|
+
printf "%s %s\t%s\n" "$state" "$BATS_TEST_FILENAME" "$BATS_TEST_NAME" >>"$BATS_RUNLOG_FILE"
|
|
191
|
+
|
|
192
|
+
if [[ $print_bats_out ]]; then
|
|
193
|
+
bats_prefix_lines_for_tap_output <"$BATS_OUT" | bats_replace_filename >&3
|
|
194
|
+
fi
|
|
195
|
+
fi
|
|
196
|
+
if [[ $BATS_GATHER_TEST_OUTPUTS_IN ]]; then
|
|
197
|
+
local try_suffix=
|
|
198
|
+
if [[ -n "$should_retry" ]]; then
|
|
199
|
+
try_suffix="-try$BATS_TEST_TRY_NUMBER"
|
|
200
|
+
fi
|
|
201
|
+
# Only copy if BATS_OUT exists (it may not exist if test produced no output)
|
|
202
|
+
if [[ -f "$BATS_OUT" ]]; then
|
|
203
|
+
cp "$BATS_OUT" "$BATS_GATHER_TEST_OUTPUTS_IN/$BATS_SUITE_TEST_NUMBER$try_suffix-${BATS_TEST_DESCRIPTION//\//%2F}.log"
|
|
204
|
+
fi
|
|
205
|
+
fi
|
|
206
|
+
rm -f "$BATS_OUT"
|
|
207
|
+
exit "$status"
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
# Marks the test as failed due to timeout.
|
|
211
|
+
# The actual termination of subprocesses is done via pkill in the background
|
|
212
|
+
# process in bats_start_timeout_countdown
|
|
213
|
+
# shellcheck disable=SC2317,SC2329
|
|
214
|
+
bats_timeout_trap() {
|
|
215
|
+
BATS_TIMED_OUT=1
|
|
216
|
+
BATS_DEBUG_LAST_STACK_TRACE_IS_VALID=
|
|
217
|
+
exit 1
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
# find pids of process and all its descendants
|
|
221
|
+
bats_find_processes_of() { # <parent-pid>
|
|
222
|
+
local -ri parent_pid=$1
|
|
223
|
+
child_pids=("$1")
|
|
224
|
+
{
|
|
225
|
+
read -ra header
|
|
226
|
+
local pid_col ppid_col
|
|
227
|
+
for ((i = 0; i < ${#header[@]}; ++i)); do
|
|
228
|
+
if [[ ${header[$i]} == "PID" ]]; then
|
|
229
|
+
pid_col=$i
|
|
230
|
+
fi
|
|
231
|
+
if [[ ${header[$i]} == "PPID" ]]; then
|
|
232
|
+
ppid_col=$i
|
|
233
|
+
fi
|
|
234
|
+
done
|
|
235
|
+
# PID PPID child_pids
|
|
236
|
+
# 1 0 (2)
|
|
237
|
+
# 2 1 (2)
|
|
238
|
+
# 3 1 (2)
|
|
239
|
+
# 4 3 (2)
|
|
240
|
+
# 5 2 (2 5)
|
|
241
|
+
# 6 5 (2 5 6)
|
|
242
|
+
# assumes pids are in ascending order and there are no orphans in the chain
|
|
243
|
+
|
|
244
|
+
if (( BASH_VERSINFO[0] < 4 )); then
|
|
245
|
+
# BASHPID is not available before bash 4
|
|
246
|
+
BASHPID=$(sh -c 'echo $PPID')
|
|
247
|
+
fi
|
|
248
|
+
while read -ra row; do
|
|
249
|
+
local -i ppid=${row[ppid_col]} pid=${row[$pid_col]}
|
|
250
|
+
if (( ppid == parent_pid)) || bats_linear_reverse_search "$ppid" child_pids; then
|
|
251
|
+
# exclude `ps` command substitution below
|
|
252
|
+
if (( pid != BASHPID)); then
|
|
253
|
+
child_pids+=("$pid")
|
|
254
|
+
fi
|
|
255
|
+
fi
|
|
256
|
+
done
|
|
257
|
+
# MSYS does not support -o and rows are not sorted by pid
|
|
258
|
+
} < <(ps -Ao pid,ppid,args 2>/dev/null || { ps -ef | sort -k 2 -h; })
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
# send signal to process and all its descendants
|
|
262
|
+
bats_kill_processes_of() { # <parent-pid> [<signal>]
|
|
263
|
+
local -ir parent_pid="${1?}"
|
|
264
|
+
local -r signal=${2?}
|
|
265
|
+
bats_find_processes_of "$parent_pid"
|
|
266
|
+
kill "-$signal" "${child_pids[@]}"
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
# sets a timeout for this process
|
|
270
|
+
bats_start_timeout_countdown() { # <timeout>
|
|
271
|
+
local -ri timeout=$1
|
|
272
|
+
local -ri target_pid=$$
|
|
273
|
+
# shellcheck disable=SC2064
|
|
274
|
+
trap "bats_timeout_trap $target_pid" TERM
|
|
275
|
+
if ! (command -v ps || command -v pkill) >/dev/null; then
|
|
276
|
+
printf "Error: Cannot execute timeout because neither pkill nor ps are available on this system!\n" >&2
|
|
277
|
+
exit 1
|
|
278
|
+
fi
|
|
279
|
+
# Start another process to kill the children of this process
|
|
280
|
+
(
|
|
281
|
+
# with sleep in foreground this shell wouldn't receive signals,
|
|
282
|
+
# so we use wait below and kill sleep explicitly when signalled to do so
|
|
283
|
+
# On Windows the TERM signal does not seem to shut down sleep ->
|
|
284
|
+
# close all fds to avoid blocking IO when sleep does not turn off
|
|
285
|
+
(eval exec {0..255}">&-"; sleep "$timeout") &
|
|
286
|
+
# shellcheck disable=SC2064
|
|
287
|
+
trap "kill $!; exit 0" TERM
|
|
288
|
+
wait
|
|
289
|
+
trap '' TERM
|
|
290
|
+
bats_kill_processes_of $target_pid TERM
|
|
291
|
+
) &
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
bats_abort_timeout_countdown() {
|
|
295
|
+
# kill the countdown process, don't care if its still there
|
|
296
|
+
kill "$BATS_killer_pid" &>/dev/null || true
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
if [[ -n "${EPOCHREALTIME-}" ]]; then
|
|
301
|
+
get_mills_since_epoch() { # <output-variable>
|
|
302
|
+
local -r output_variable="$1"
|
|
303
|
+
local int frac
|
|
304
|
+
# allow for different decimal separators
|
|
305
|
+
IFS=., read -r int frac <<<"$EPOCHREALTIME"
|
|
306
|
+
printf -v "$output_variable" "%d" "${int}${frac::3}"
|
|
307
|
+
}
|
|
308
|
+
else
|
|
309
|
+
get_mills_since_epoch() { # <output-variable>
|
|
310
|
+
local -r output_variable="$1"
|
|
311
|
+
local ms_since_epoch
|
|
312
|
+
ms_since_epoch=$(bats_execute date +%s%N)
|
|
313
|
+
if [[ "$ms_since_epoch" == *N || "${#ms_since_epoch}" -lt 19 ]]; then
|
|
314
|
+
ms_since_epoch=$(($(bats_execute date +%s) * 1000))
|
|
315
|
+
else
|
|
316
|
+
ms_since_epoch=$((ms_since_epoch / 1000000))
|
|
317
|
+
fi
|
|
318
|
+
printf -v "$output_variable" "%d" "$ms_since_epoch"
|
|
319
|
+
}
|
|
320
|
+
fi
|
|
321
|
+
|
|
322
|
+
bats_perform_test() {
|
|
323
|
+
if ! declare -F "${BATS_TEST_NAME%% *}" &>/dev/null; then
|
|
324
|
+
local quoted_test_name
|
|
325
|
+
bats_quote_code quoted_test_name "$BATS_TEST_NAME"
|
|
326
|
+
printf "bats: unknown test name %s\n" "$quoted_test_name" >&2
|
|
327
|
+
exit 1
|
|
328
|
+
fi
|
|
329
|
+
|
|
330
|
+
# is this skipped from outside ?
|
|
331
|
+
if [[ -n "${BATS_TEST_SKIPPED-}" ]]; then
|
|
332
|
+
# forward skip (with message) by overriding setup
|
|
333
|
+
# shellcheck disable=SC2317
|
|
334
|
+
setup() {
|
|
335
|
+
skip "$BATS_TEST_SKIPPED"
|
|
336
|
+
}
|
|
337
|
+
fi
|
|
338
|
+
|
|
339
|
+
if [[ -n "${BATS_TEST_TIMEOUT:-}" ]]; then
|
|
340
|
+
bats_start_timeout_countdown "$BATS_TEST_TIMEOUT"
|
|
341
|
+
declare -r BATS_killer_pid=$!
|
|
342
|
+
fi
|
|
343
|
+
BATS_TEST_COMPLETED=
|
|
344
|
+
BATS_TEST_SKIPPED=${BATS_TEST_SKIPPED-}
|
|
345
|
+
BATS_TEARDOWN_COMPLETED=
|
|
346
|
+
BATS_ERROR_STATUS=
|
|
347
|
+
bats_setup_tracing
|
|
348
|
+
# use parameter to mark this call as trap call
|
|
349
|
+
# shellcheck disable=SC2064
|
|
350
|
+
trap "bats_teardown_trap as-exit-trap" EXIT
|
|
351
|
+
|
|
352
|
+
if [[ -n "$BATS_EXTENDED_SYNTAX" ]]; then
|
|
353
|
+
printf 'begin %d %s\n' "$BATS_SUITE_TEST_NUMBER" "${BATS_TEST_NAME_PREFIX:-}$BATS_TEST_DESCRIPTION" >&3
|
|
354
|
+
fi
|
|
355
|
+
|
|
356
|
+
get_mills_since_epoch BATS_TEST_START_TIME
|
|
357
|
+
{
|
|
358
|
+
bats_set_stacktrace_limit
|
|
359
|
+
setup "$@"
|
|
360
|
+
"$@"
|
|
361
|
+
} >>"$BATS_OUT" 2>&1 4>&1
|
|
362
|
+
|
|
363
|
+
BATS_TEST_COMPLETED=1
|
|
364
|
+
# shellcheck disable=SC2064
|
|
365
|
+
trap "bats_exit_trap" EXIT
|
|
366
|
+
bats_teardown_trap "" # pass empty parameter to signify call outside trap
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
trap bats_interrupt_trap INT
|
|
370
|
+
|
|
371
|
+
# shellcheck source=lib/bats-core/preprocessing.bash
|
|
372
|
+
source "$BATS_ROOT/$BATS_LIBDIR/bats-core/preprocessing.bash"
|
|
373
|
+
|
|
374
|
+
exec 3<&1
|
|
375
|
+
BATS_OUT="$BATS_RUN_TMPDIR/test/$BATS_SUITE_TEST_NUMBER.out"
|
|
376
|
+
|
|
377
|
+
bats_create_test_tmpdirs
|
|
378
|
+
bats_evaluate_preprocessed_source
|
|
379
|
+
|
|
380
|
+
readonly BATS_TEST_TAGS
|
|
381
|
+
|
|
382
|
+
# use eval to parse (internally quoted!) test command into parameters
|
|
383
|
+
bats_perform_test "${BATS_TEST_COMMAND[@]}"
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# shellcheck source=lib/bats-core/formatter.bash
|
|
5
|
+
source "$BATS_ROOT/$BATS_LIBDIR/bats-core/formatter.bash"
|
|
6
|
+
|
|
7
|
+
init_suite() {
|
|
8
|
+
suite_test_exec_time=0
|
|
9
|
+
# since we have to print the suite header before its contents but we don't know the contents before the header,
|
|
10
|
+
# we have to buffer the contents
|
|
11
|
+
_suite_buffer=""
|
|
12
|
+
test_result_state="" # declare for the first flush, when no test has been encountered
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
init_file() {
|
|
16
|
+
file_count=0
|
|
17
|
+
file_failures=0
|
|
18
|
+
file_skipped=0
|
|
19
|
+
file_exec_time=0
|
|
20
|
+
test_exec_time=0
|
|
21
|
+
name=""
|
|
22
|
+
_buffer=""
|
|
23
|
+
_buffer_log=""
|
|
24
|
+
_system_out_log=""
|
|
25
|
+
test_result_state="" # mark that no test has run in this file so far
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
host() {
|
|
29
|
+
local hostname="${HOST:-}"
|
|
30
|
+
[[ -z "$hostname" ]] && hostname="${HOSTNAME:-}"
|
|
31
|
+
[[ -z "$hostname" ]] && hostname="$(uname -n)"
|
|
32
|
+
[[ -z "$hostname" ]] && hostname="$(hostname -f)"
|
|
33
|
+
|
|
34
|
+
echo "$hostname"
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
# convert $1 (time in milliseconds) to seconds
|
|
38
|
+
milliseconds_to_seconds() {
|
|
39
|
+
# we cannot rely on having bc for this calculation
|
|
40
|
+
full_seconds=$(($1 / 1000))
|
|
41
|
+
remaining_milliseconds=$(($1 % 1000))
|
|
42
|
+
if [[ $remaining_milliseconds -eq 0 ]]; then
|
|
43
|
+
printf "%d" "$full_seconds"
|
|
44
|
+
else
|
|
45
|
+
printf "%d.%03d" "$full_seconds" "$remaining_milliseconds"
|
|
46
|
+
fi
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
suite_header() {
|
|
50
|
+
printf "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
|
|
51
|
+
<testsuites time=\"%s\">\n" "$(milliseconds_to_seconds "${suite_test_exec_time}")"
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
file_header() {
|
|
55
|
+
timestamp=$(date -u +"%Y-%m-%dT%H:%M:%S")
|
|
56
|
+
printf "<testsuite name=\"%s\" tests=\"%s\" failures=\"%s\" errors=\"0\" skipped=\"%s\" time=\"%s\" timestamp=\"%s\" hostname=\"%s\">\n" \
|
|
57
|
+
"$(xml_escape "${class}")" "${file_count}" "${file_failures}" "${file_skipped}" "$(milliseconds_to_seconds "${file_exec_time}")" "${timestamp}" "$(host)"
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
file_footer() {
|
|
61
|
+
printf "</testsuite>\n"
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
suite_footer() {
|
|
65
|
+
printf "</testsuites>\n"
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
print_test_case() {
|
|
69
|
+
if [[ "$test_result_state" == ok && -z "$_system_out_log" && -z "$_buffer_log" ]]; then
|
|
70
|
+
# pass and no output can be shortened
|
|
71
|
+
printf " <testcase classname=\"%s\" name=\"%s\" time=\"%s\" />\n" "$(xml_escape "${class}")" "$(xml_escape "${name}")" "$(milliseconds_to_seconds "${test_exec_time}")"
|
|
72
|
+
else
|
|
73
|
+
printf " <testcase classname=\"%s\" name=\"%s\" time=\"%s\">\n" "$(xml_escape "${class}")" "$(xml_escape "${name}")" "$(milliseconds_to_seconds "${test_exec_time}")"
|
|
74
|
+
if [[ -n "$_system_out_log" ]]; then
|
|
75
|
+
printf " <system-out>%s</system-out>\n" "$(xml_escape "${_system_out_log}")"
|
|
76
|
+
fi
|
|
77
|
+
if [[ "$test_result_state" == not_ok ]]; then # Failed tests need a <failure> element.
|
|
78
|
+
# If we have system err output, we use that as the failure text.
|
|
79
|
+
printf " <failure type=\"failure\">%s</failure>\n" "$(xml_escape "${_buffer_log}")"
|
|
80
|
+
else
|
|
81
|
+
if [[ -n "$_buffer_log" ]]; then
|
|
82
|
+
printf " <system-err>%s</system-err>\n" "$(xml_escape "${_buffer_log}")"
|
|
83
|
+
fi
|
|
84
|
+
fi
|
|
85
|
+
if [[ "$test_result_state" == skipped ]]; then
|
|
86
|
+
printf " <skipped>%s</skipped>\n" "$(xml_escape "$test_skip_message")"
|
|
87
|
+
fi
|
|
88
|
+
printf " </testcase>\n"
|
|
89
|
+
fi
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
xml_escape() { # <string to escape>
|
|
93
|
+
local output=${1//&/\&}
|
|
94
|
+
output=${output//</\<}
|
|
95
|
+
output=${output//>/\>}
|
|
96
|
+
output=${output//'"'/\"}
|
|
97
|
+
output=${output//\'/\'}
|
|
98
|
+
# remove ANSI escape sequences (e.g. color codes, cursor movements)
|
|
99
|
+
local CONTROL_CHAR=$'\033'
|
|
100
|
+
local REGEX="$CONTROL_CHAR\[[0-9;]*[a-zA-Z]"
|
|
101
|
+
while [[ "$output" =~ $REGEX ]]; do
|
|
102
|
+
output=${output//${BASH_REMATCH[0]}/}
|
|
103
|
+
done
|
|
104
|
+
printf "%s" "$output"
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
suite_buffer() {
|
|
108
|
+
local output
|
|
109
|
+
output="$(
|
|
110
|
+
"$@"
|
|
111
|
+
printf "x"
|
|
112
|
+
)" # use x marker to avoid losing trailing newlines
|
|
113
|
+
_suite_buffer="${_suite_buffer}${output%x}"
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
suite_flush() {
|
|
117
|
+
echo -n "${_suite_buffer}"
|
|
118
|
+
_suite_buffer=""
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
buffer() {
|
|
122
|
+
local output
|
|
123
|
+
output="$(
|
|
124
|
+
"$@"
|
|
125
|
+
printf "x"
|
|
126
|
+
)" # use x marker to avoid losing trailing newlines
|
|
127
|
+
_buffer="${_buffer}${output%x}"
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
flush() {
|
|
131
|
+
echo -n "${_buffer}"
|
|
132
|
+
_buffer=""
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
log_system_err() {
|
|
136
|
+
if [[ -n "$_buffer_log" ]]; then
|
|
137
|
+
_buffer_log="${_buffer_log}
|
|
138
|
+
$1"
|
|
139
|
+
else
|
|
140
|
+
_buffer_log="$1"
|
|
141
|
+
fi
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
flush_log() {
|
|
145
|
+
if [[ -n "$test_result_state" ]]; then
|
|
146
|
+
buffer print_test_case
|
|
147
|
+
fi
|
|
148
|
+
_buffer_log=""
|
|
149
|
+
_system_out_log=""
|
|
150
|
+
test_result_state="" # Clean out result from last test. Retried tests will have multiple begin calls.
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
log_system_out() {
|
|
154
|
+
if [[ -n "$_system_out_log" ]]; then
|
|
155
|
+
_system_out_log="${_system_out_log}
|
|
156
|
+
$1"
|
|
157
|
+
else
|
|
158
|
+
_system_out_log="$1"
|
|
159
|
+
fi
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
finish_file() {
|
|
163
|
+
if [[ "${class}" != JUNIT_FORMATTER_NO_FILE_ENCOUNTERED ]]; then
|
|
164
|
+
file_header
|
|
165
|
+
printf "%s" "${_buffer}"
|
|
166
|
+
file_footer
|
|
167
|
+
class=''
|
|
168
|
+
name=''
|
|
169
|
+
fi
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
finish_suite() {
|
|
173
|
+
flush_log
|
|
174
|
+
suite_header
|
|
175
|
+
suite_flush
|
|
176
|
+
finish_file # must come after suite flush to not print the last file before the others
|
|
177
|
+
suite_footer
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
bats_tap_stream_plan() { # <number of tests>
|
|
181
|
+
:
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
bats_tap_stream_begin() { # <test index> <test name>
|
|
185
|
+
flush_log
|
|
186
|
+
# set after flushing to avoid overriding name of test
|
|
187
|
+
name="$2"
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
bats_tap_stream_ok() { # <test index> <test name>
|
|
191
|
+
test_exec_time=${BATS_FORMATTER_TEST_DURATION:-0}
|
|
192
|
+
((file_count += 1))
|
|
193
|
+
test_result_state='ok'
|
|
194
|
+
file_exec_time="$((file_exec_time + test_exec_time))"
|
|
195
|
+
suite_test_exec_time=$((suite_test_exec_time + test_exec_time))
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
bats_tap_stream_skipped() { # <test index> <test name> <skip reason>
|
|
199
|
+
test_exec_time=${BATS_FORMATTER_TEST_DURATION:-0}
|
|
200
|
+
((file_count += 1))
|
|
201
|
+
((file_skipped += 1))
|
|
202
|
+
test_result_state='skipped'
|
|
203
|
+
test_exec_time=0
|
|
204
|
+
test_skip_message="$3"
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
bats_tap_stream_not_ok() { # <test index> <test name>
|
|
208
|
+
if [[ -z "${name:-}" ]]; then
|
|
209
|
+
# Can be called (after bats_tap_stream_plan) before anything else if the test fails in setup_suite
|
|
210
|
+
bats_tap_stream_suite "setup_suite"
|
|
211
|
+
bats_tap_stream_begin "$1" "$2"
|
|
212
|
+
fi
|
|
213
|
+
test_exec_time=${BATS_FORMATTER_TEST_DURATION:-0}
|
|
214
|
+
((file_count += 1))
|
|
215
|
+
((file_failures += 1))
|
|
216
|
+
test_result_state=not_ok
|
|
217
|
+
file_exec_time="$((file_exec_time + test_exec_time))"
|
|
218
|
+
suite_test_exec_time=$((suite_test_exec_time + test_exec_time))
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
bats_tap_stream_comment() { # <comment text without leading '# '> <scope>
|
|
222
|
+
local comment="$1" scope="$2"
|
|
223
|
+
case "$scope" in
|
|
224
|
+
begin)
|
|
225
|
+
# everything that happens between begin and [not] ok is FD3 output from the test
|
|
226
|
+
log_system_out "$comment"
|
|
227
|
+
;;
|
|
228
|
+
ok | skipped)
|
|
229
|
+
# non failed tests can produce FD3 output
|
|
230
|
+
log_system_out "$comment"
|
|
231
|
+
;;
|
|
232
|
+
*)
|
|
233
|
+
# everything else is considered error output
|
|
234
|
+
log_system_err "$1"
|
|
235
|
+
;;
|
|
236
|
+
esac
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
bats_tap_stream_suite() { # <file name>
|
|
240
|
+
flush_log
|
|
241
|
+
suite_buffer finish_file
|
|
242
|
+
init_file
|
|
243
|
+
class="${1/$BASE_PATH/}"
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
bats_tap_stream_unknown() { # <full line>
|
|
247
|
+
local line="$1" scope="$2"
|
|
248
|
+
log_system_out "$line"
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
main() {
|
|
252
|
+
local _buffer_log="" \
|
|
253
|
+
name="" \
|
|
254
|
+
class="JUNIT_FORMATTER_NO_FILE_ENCOUNTERED" \
|
|
255
|
+
_buffer="" \
|
|
256
|
+
_buffer_log="" \
|
|
257
|
+
_suite_buffer="" \
|
|
258
|
+
_system_out_log="" \
|
|
259
|
+
test_result_state="" \
|
|
260
|
+
test_skip_message=""
|
|
261
|
+
|
|
262
|
+
local -i file_count=0 \
|
|
263
|
+
file_failures=0 \
|
|
264
|
+
file_skipped=0 \
|
|
265
|
+
file_exec_time=0 \
|
|
266
|
+
test_exec_time=0 \
|
|
267
|
+
suite_test_exec_time=0
|
|
268
|
+
|
|
269
|
+
local BASE_PATH=.
|
|
270
|
+
|
|
271
|
+
while [[ "$#" -ne 0 ]]; do
|
|
272
|
+
case "$1" in
|
|
273
|
+
--base-path)
|
|
274
|
+
shift
|
|
275
|
+
normalize_base_path BASE_PATH "$1"
|
|
276
|
+
;;
|
|
277
|
+
esac
|
|
278
|
+
shift
|
|
279
|
+
done
|
|
280
|
+
|
|
281
|
+
init_suite
|
|
282
|
+
trap finish_suite EXIT
|
|
283
|
+
trap '' INT
|
|
284
|
+
|
|
285
|
+
bats_parse_internal_extended_tap
|
|
286
|
+
|
|
287
|
+
trap - EXIT
|
|
288
|
+
finish_suite # ensure we run this with the local variables still in scope
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
# [\x00-\x08\x0B\x0C\x0E-\x1F] can't use $'\x00' as this terminates the string early
|
|
292
|
+
BATS_JUNIT_FORMATTER_DELETE_CHARS_DEFAULT='\000'$'\x01\x02\x03\x04\x05\x06\x07\x08\x0B\x0C\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F'
|
|
293
|
+
main "$@" | tr -d "${BATS_JUNIT_FORMATTER_DELETE_CHARS-$BATS_JUNIT_FORMATTER_DELETE_CHARS_DEFAULT}"
|