@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.
@@ -0,0 +1,346 @@
1
+ #!/usr/bin/env bash
2
+ set -e
3
+
4
+ count_only_flag=''
5
+ filter=''
6
+ nfilter=''
7
+ num_jobs=${BATS_NUMBER_OF_PARALLEL_JOBS:-1}
8
+ parallel_binary_name=${BATS_PARALLEL_BINARY_NAME:-"parallel"}
9
+ BATS_ABORT_ON_FAILURE=
10
+ bats_no_parallelize_across_files=${BATS_NO_PARALLELIZE_ACROSS_FILES-}
11
+ bats_no_parallelize_within_files=
12
+ filter_status=''
13
+ gather_tests_flags=('--dummy-flag')
14
+ flags=('--dummy-flag') # add a dummy flag to prevent unset variable errors on empty array expansion in old bash versions
15
+ setup_suite_file=''
16
+ BATS_TRACE_LEVEL="${BATS_TRACE_LEVEL:-0}"
17
+ BATS_SHOW_OUTPUT_OF_SUCCEEDING_TESTS=
18
+ if [[ -n "${BATS_ALLOW_EMPTY_SUITE:-}" ]]; then
19
+ BATS_EMPTY_SUITE_STATUS=0
20
+ else
21
+ BATS_EMPTY_SUITE_STATUS=1
22
+ fi
23
+
24
+ abort() {
25
+ printf 'Error: %s\n' "$1" >&2
26
+ exit 1
27
+ }
28
+
29
+ # shellcheck source=lib/bats-core/common.bash disable=SC2153
30
+ source "$BATS_ROOT/$BATS_LIBDIR/bats-core/common.bash"
31
+
32
+ while [[ "$#" -ne 0 ]]; do
33
+ case "$1" in
34
+ -c)
35
+ count_only_flag=1
36
+ ;;
37
+ -f)
38
+ shift
39
+ filter="$1"
40
+ ;;
41
+ -j)
42
+ shift
43
+ num_jobs="$1"
44
+ flags+=('-j' "$num_jobs")
45
+ ;;
46
+ --parallel-binary-name)
47
+ shift
48
+ parallel_binary_name="$1"
49
+ ;;
50
+ -T)
51
+ flags+=('-T')
52
+ ;;
53
+ -x)
54
+ flags+=('-x')
55
+ ;;
56
+ --abort)
57
+ flags+=('--abort')
58
+ BATS_ABORT_ON_FAILURE=1
59
+ ;;
60
+ --no-parallelize-across-files)
61
+ bats_no_parallelize_across_files=1
62
+ ;;
63
+ --no-parallelize-within-files)
64
+ bats_no_parallelize_within_files=1
65
+ flags+=("--no-parallelize-within-files")
66
+ ;;
67
+ --filter-status)
68
+ shift
69
+ filter_status="$1"
70
+ ;;
71
+ --filter-tags)
72
+ shift
73
+ gather_tests_flags+=("--filter-tags" "$1")
74
+ ;;
75
+ --negative-filter)
76
+ shift
77
+ nfilter="$1"
78
+ ;;
79
+ --dummy-flag) ;;
80
+
81
+ --trace)
82
+ flags+=('--trace')
83
+ ((++BATS_TRACE_LEVEL)) # avoid returning 0
84
+ ;;
85
+ --print-output-on-failure)
86
+ flags+=(--print-output-on-failure)
87
+ ;;
88
+ --show-output-of-passing-tests)
89
+ flags+=(--show-output-of-passing-tests)
90
+ BATS_SHOW_OUTPUT_OF_SUCCEEDING_TESTS=1
91
+ ;;
92
+ --verbose-run)
93
+ flags+=(--verbose-run)
94
+ ;;
95
+ --gather-test-outputs-in)
96
+ shift
97
+ flags+=(--gather-test-outputs-in "$1")
98
+ ;;
99
+ --setup-suite-file)
100
+ shift
101
+ setup_suite_file="$1"
102
+ ;;
103
+ --allow-empty-suite)
104
+ BATS_EMPTY_SUITE_STATUS=0
105
+ ;;
106
+ *)
107
+ break
108
+ ;;
109
+ esac
110
+ shift
111
+ done
112
+
113
+ if [[ "$num_jobs" != 1 ]]; then
114
+ if ! type -p "${parallel_binary_name}" >/dev/null && "${parallel_binary_name}" --version &>/dev/null && [[ -z "$bats_no_parallelize_across_files" ]]; then
115
+ abort "Cannot execute \"${num_jobs}\" jobs without GNU parallel"
116
+ fi
117
+ # shellcheck source=lib/bats-core/semaphore.bash
118
+ source "${BATS_ROOT}/$BATS_LIBDIR/bats-core/semaphore.bash"
119
+ bats_semaphore_setup
120
+ fi
121
+
122
+ # create a file that contains all (filtered) tests to run from all files
123
+ TESTS_LIST_FILE="${BATS_RUN_TMPDIR}/test_list_file.txt"
124
+
125
+ TEST_ROOT=${1-}
126
+ TEST_ROOT=${TEST_ROOT%/*}
127
+ export BATS_RUN_LOGS_DIRECTORY="$TEST_ROOT/.bats/run-logs"
128
+ if [[ ! -d "$BATS_RUN_LOGS_DIRECTORY" ]]; then
129
+ if [[ -n "$filter_status" ]]; then
130
+ printf "Error: --filter-status needs '%s/' to save failed tests. Please create this folder, add it to .gitignore and try again.\n" "$BATS_RUN_LOGS_DIRECTORY"
131
+ exit 1
132
+ else
133
+ BATS_RUN_LOGS_DIRECTORY=
134
+ fi
135
+ # discard via sink instead of having a conditional later
136
+ export BATS_RUNLOG_FILE='/dev/null'
137
+ else
138
+ # use UTC (-u) to avoid problems with TZ changes
139
+ BATS_RUNLOG_DATE=$(date -u '+%Y-%m-%d %H:%M:%S UTC')
140
+ export BATS_RUNLOG_FILE="$BATS_RUN_LOGS_DIRECTORY/${BATS_RUNLOG_DATE}.log" BATS_RUNLOG_DATE
141
+ if [[ ! -w "$BATS_RUN_LOGS_DIRECTORY" ]]; then
142
+ printf "WARNING: Cannot write in %s. This run will not write a log!\n" "$BATS_RUN_LOGS_DIRECTORY" >&2
143
+ BATS_RUNLOG_FILE='/dev/null' # disable runlog file
144
+ fi
145
+ fi
146
+
147
+ # create file even if no tests are found so the `read` below won't fail
148
+ touch "$TESTS_LIST_FILE"
149
+
150
+ gather_tests_output=$(TESTS_LIST_FILE="$TESTS_LIST_FILE" filter="$filter" nfilter="$nfilter" filter_status="$filter_status" bats-gather-tests "${gather_tests_flags[@]}" -- "$@")
151
+ test_count=$(grep -c ^ "$TESTS_LIST_FILE" || true) # don't fail here when there are no tests
152
+ if [[ $gather_tests_output == focus_mode ]]; then
153
+ focus_mode=1
154
+ else
155
+ focus_mode=
156
+ fi
157
+
158
+ if [[ -n "$count_only_flag" ]]; then
159
+ printf '%d\n' "${test_count}"
160
+ # we don't want to pollute the runlog files, as no tests would have been run
161
+ [[ $BATS_RUNLOG_FILE != /dev/null ]] && rm "$BATS_RUNLOG_FILE"
162
+ exit 0
163
+ fi
164
+
165
+ if [[ -n "$bats_no_parallelize_across_files" ]] && [[ ! "$num_jobs" -gt 1 ]]; then
166
+ abort "The flag --no-parallelize-across-files requires at least --jobs 2"
167
+ fi
168
+
169
+ if [[ -n "$bats_no_parallelize_within_files" ]] && [[ ! "$num_jobs" -gt 1 ]]; then
170
+ abort "The flag --no-parallelize-across-files requires at least --jobs 2"
171
+ fi
172
+
173
+ # only abort on the lowest levels
174
+ trap 'BATS_INTERRUPTED=true' INT
175
+
176
+ if [[ -n "$focus_mode" ]]; then
177
+ printf "WARNING: This test run only contains tests tagged \`bats:focus\`!\n" >&2
178
+ fi
179
+
180
+ bats_exit_suite() {
181
+ if [[ "$focus_mode" == 1 && $bats_exec_suite_status -eq 0 ]]; then
182
+ if [[ ${BATS_NO_FAIL_FOCUS_RUN-} == 1 ]]; then
183
+ printf "WARNING: This test run only contains tests tagged \`bats:focus\`!\n" >&2
184
+ else
185
+ printf "Marking test run as failed due to \`bats:focus\` tag. (Set \`BATS_NO_FAIL_FOCUS_RUN=1\` to disable.)\n" >&2
186
+ bats_exec_suite_status=1
187
+ fi
188
+ fi
189
+
190
+ exit "$bats_exec_suite_status" # the actual exit code will be set by the exit trap using bats_exec_suite_status
191
+ }
192
+
193
+ bats_exec_suite_status=0
194
+ printf '1..%d\n' "${test_count}"
195
+
196
+ # No point on continuing if there's no tests.
197
+ if [[ "${test_count}" == 0 ]]; then
198
+ bats_exec_suite_status=$BATS_EMPTY_SUITE_STATUS
199
+ if (( bats_exec_suite_status != 0)); then
200
+ printf "ERROR: Found no tests. Use \`--allow-empty-suite\` or \`BATS_ALLOW_EMPTY_SUITE=1\` to suppress this error." >&2
201
+ fi
202
+ bats_exit_suite
203
+ fi
204
+
205
+ export BATS_SUITE_TMPDIR="${BATS_RUN_TMPDIR}/suite"
206
+ if ! mkdir "$BATS_SUITE_TMPDIR"; then
207
+ printf '%s\n' "Failed to create BATS_SUITE_TMPDIR" >&2
208
+ exit 1
209
+ fi
210
+
211
+ # Deduplicate filenames (without reordering) to avoid running duplicate tests n by n times.
212
+ # (see https://github.com/bats-core/bats-core/issues/329)
213
+ # If a file was specified multiple times, we already got it repeatedly in our TESTS_LIST_FILE.
214
+ # Thus, it suffices to bats-exec-file it once to run all repeated tests on it.
215
+ IFS=$'\n' read -d '' -r -a BATS_UNIQUE_TEST_FILENAMES < <(printf "%s\n" "$@" | nl | sort -k 2 | uniq -f 1 | sort -n | cut -f 2-) || true
216
+
217
+ # shellcheck source=lib/bats-core/tracing.bash
218
+ source "$BATS_ROOT/$BATS_LIBDIR/bats-core/tracing.bash"
219
+ bats_setup_tracing
220
+
221
+ trap bats_suite_exit_trap EXIT
222
+
223
+ exec 3<&1
224
+
225
+ # shellcheck disable=SC2317,SC2329
226
+ bats_suite_exit_trap() {
227
+ local print_bats_out="${BATS_SHOW_OUTPUT_OF_SUCCEEDING_TESTS}"
228
+ if [[ -z "${BATS_SETUP_SUITE_COMPLETED}" || -z "${BATS_TEARDOWN_SUITE_COMPLETED}" ]]; then
229
+ if [[ -z "${BATS_SETUP_SUITE_COMPLETED}" ]]; then
230
+ printf "not ok 1 setup_suite\n"
231
+ elif [[ -z "${BATS_TEARDOWN_SUITE_COMPLETED}" ]]; then
232
+ printf "not ok %d teardown_suite\n" $((test_count + 1))
233
+ fi
234
+ local stack_trace
235
+ bats_get_failure_stack_trace stack_trace
236
+ bats_print_stack_trace "${stack_trace[@]}"
237
+ bats_print_failed_command "${stack_trace[@]}"
238
+ print_bats_out=1
239
+ bats_exec_suite_status=1
240
+ fi
241
+
242
+ if [[ -n "$print_bats_out" ]]; then
243
+ bats_prefix_lines_for_tap_output <"$BATS_OUT"
244
+ fi
245
+
246
+ if [[ ${BATS_INTERRUPTED-NOTSET} != NOTSET ]]; then
247
+ printf "\n# Received SIGINT, aborting ...\n\n"
248
+ fi
249
+
250
+ if [[ -d "$BATS_RUN_LOGS_DIRECTORY" && -n "${BATS_INTERRUPTED:-}" ]]; then
251
+ # aborting a test run with CTRL+C does not save the runlog file
252
+ [[ "$BATS_RUNLOG_FILE" != /dev/null ]] && rm "$BATS_RUNLOG_FILE"
253
+ fi
254
+ exit "$bats_exec_suite_status"
255
+ } >&3
256
+
257
+ bats_run_teardown_suite() {
258
+ local bats_teardown_suite_status=0
259
+ # avoid being called twice, in case this is not called through bats_teardown_suite_trap
260
+ # but from the end of file
261
+ trap bats_suite_exit_trap EXIT
262
+
263
+ bats_set_stacktrace_limit
264
+
265
+ BATS_TEARDOWN_SUITE_COMPLETED=
266
+ teardown_suite >>"$BATS_OUT" 2>&1 || bats_teardown_suite_status=$?
267
+ if ((bats_teardown_suite_status == 0)); then
268
+ BATS_TEARDOWN_SUITE_COMPLETED=1
269
+ elif [[ -n "${BATS_SETUP_SUITE_COMPLETED:-}" ]]; then
270
+ # shellcheck disable=SC2034
271
+ BATS_DEBUG_LAST_STACK_TRACE_IS_VALID=1
272
+ BATS_ERROR_STATUS=$bats_teardown_suite_status
273
+ return $BATS_ERROR_STATUS
274
+ fi
275
+ }
276
+
277
+ # shellcheck disable=SC2317,SC2329
278
+ bats_teardown_suite_trap() {
279
+ bats_run_teardown_suite
280
+ bats_suite_exit_trap
281
+ }
282
+
283
+ teardown_suite() {
284
+ :
285
+ }
286
+
287
+ trap bats_teardown_suite_trap EXIT
288
+
289
+ BATS_OUT="$BATS_RUN_TMPDIR/suite.out"
290
+ if [[ -n "$setup_suite_file" ]]; then
291
+ setup_suite() {
292
+ printf "%s does not define \`setup_suite()\`\n" "$setup_suite_file" >&2
293
+ return 1
294
+ }
295
+
296
+ # shellcheck disable=SC2034 # will be used in the sourced file below
297
+ BATS_TEST_FILENAME="$setup_suite_file"
298
+ # shellcheck source=lib/bats-core/test_functions.bash
299
+ source "$BATS_ROOT/$BATS_LIBDIR/bats-core/test_functions.bash"
300
+ _bats_test_functions_setup -1 # invalid TEST_NUMBER, as this is not a test
301
+
302
+ # shellcheck disable=SC1090
303
+ source "$setup_suite_file"
304
+
305
+ bats_set_stacktrace_limit
306
+
307
+ set -eET
308
+ export BATS_SETUP_SUITE_COMPLETED=
309
+ setup_suite >>"$BATS_OUT" 2>&1
310
+ BATS_SETUP_SUITE_COMPLETED=1
311
+ set +ET
312
+ else
313
+ # prevent exit trap from printing an error because of incomplete setup_suite,
314
+ # when there was none to execute
315
+ BATS_SETUP_SUITE_COMPLETED=1
316
+ fi
317
+
318
+ if [[ "$num_jobs" -gt 1 ]] && [[ -z "$bats_no_parallelize_across_files" ]]; then
319
+ parallel_flags=(--keep-order --jobs "$num_jobs")
320
+ if (( BATS_ABORT_ON_FAILURE )); then
321
+ parallel_flags+=('--halt' 'now,fail=1')
322
+ fi
323
+ # run files in parallel to get the maximum pool of parallel tasks
324
+ # shellcheck disable=SC2086,SC2068
325
+ # we need to handle the quoting of ${flags[@]} ourselves,
326
+ # because parallel can only quote it as one
327
+ "${parallel_binary_name}" "${parallel_flags[@]}" -- "bats-exec-file" "$(printf "%q " "${flags[@]}")" "{#}" "{}" "$TESTS_LIST_FILE" <<< "$(printf "%s\n" "${BATS_UNIQUE_TEST_FILENAMES[@]}")" 2>&1 || bats_exec_suite_status=1
328
+ else
329
+ file_index=0
330
+ for filename in "${BATS_UNIQUE_TEST_FILENAMES[@]}"; do
331
+ (( ++file_index ))
332
+ if [[ "${BATS_INTERRUPTED-NOTSET}" != NOTSET ]]; then
333
+ bats_exec_suite_status=130 # bash's code for SIGINT exits
334
+ break
335
+ fi
336
+ bats-exec-file "${flags[@]}" "$file_index" "$filename" "${TESTS_LIST_FILE}" || bats_exec_suite_status=1
337
+ if (( BATS_ABORT_ON_FAILURE && bats_exec_suite_status > 0 )); then
338
+ break
339
+ fi
340
+ done
341
+ fi
342
+
343
+ set -eET
344
+ bats_run_teardown_suite
345
+
346
+ bats_exit_suite