@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,539 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # this must be called for each test file!
4
+ _bats_test_functions_setup() { # <BATS_TEST_NUMBER>
5
+ BATS_TEST_DIRNAME="${BATS_TEST_FILENAME%/*}"
6
+ BATS_TEST_NAMES=()
7
+ # shellcheck disable=SC2034
8
+ BATS_TEST_NUMBER=${1?}
9
+ }
10
+
11
+ # shellcheck source=lib/bats-core/warnings.bash
12
+ source "$BATS_ROOT/$BATS_LIBDIR/bats-core/warnings.bash"
13
+
14
+ # find_in_bats_lib_path echoes the first recognized load path to
15
+ # a library in BATS_LIB_PATH or relative to BATS_TEST_DIRNAME.
16
+ #
17
+ # Libraries relative to BATS_TEST_DIRNAME take precedence over
18
+ # BATS_LIB_PATH.
19
+ #
20
+ # If no library is found find_in_bats_lib_path returns 1.
21
+ find_in_bats_lib_path() { # <return-var> <library-name>
22
+ local return_var="${1:?}"
23
+ local library_name="${2:?}"
24
+
25
+ local -a bats_lib_paths
26
+ IFS=: read -ra bats_lib_paths <<<"$BATS_LIB_PATH"
27
+
28
+ for path in "${bats_lib_paths[@]}"; do
29
+ if [[ -f "$path/$library_name" ]]; then
30
+ printf -v "$return_var" "%s" "$path/$library_name"
31
+ # A library load path was found, return
32
+ return 0
33
+ elif [[ -f "$path/$library_name/load.bash" ]]; then
34
+ printf -v "$return_var" "%s" "$path/$library_name/load.bash"
35
+ # A library load path was found, return
36
+ return 0
37
+ fi
38
+ done
39
+
40
+ return 1
41
+ }
42
+
43
+ # bats_internal_load expects an absolute path that is a library load path.
44
+ #
45
+ # If the library load path points to a file (a library loader) it is
46
+ # sourced.
47
+ #
48
+ # If it points to a directory all files ending in .bash inside of the
49
+ # directory are sourced.
50
+ #
51
+ # If the sourcing of the library loader or of a file in a library
52
+ # directory fails bats_internal_load prints an error message and returns 1.
53
+ #
54
+ # If the passed library load path is not absolute or is not a valid file
55
+ # or directory bats_internal_load prints an error message and returns 1.
56
+ bats_internal_load() {
57
+ local library_load_path="${1:?}"
58
+
59
+ if [[ "${library_load_path:0:1}" != / ]]; then
60
+ printf "Passed library load path is not an absolute path: %s\n" "$library_load_path" >&2
61
+ return 1
62
+ fi
63
+
64
+ # library_load_path is a library loader
65
+ if [[ -f "$library_load_path" ]]; then
66
+ # shellcheck disable=SC1090
67
+ if ! source "$library_load_path"; then
68
+ printf "Error while sourcing library loader at '%s'\n" "$library_load_path" >&2
69
+ return 1
70
+ fi
71
+ return 0
72
+ fi
73
+
74
+ printf "Passed library load path is neither a library loader nor library directory: %s\n" "$library_load_path" >&2
75
+ return 1
76
+ }
77
+
78
+ # bats_load_safe accepts an argument called 'slug' and attempts to find and
79
+ # source a library based on the slug.
80
+ #
81
+ # A slug can be an absolute path or a relative path.
82
+ #
83
+ # If the slug is not an absolute path it is resolved relative to
84
+ # BATS_TEST_DIRNAME
85
+ #
86
+ # The resolved slug is passed to bats_internal_load.
87
+ # If bats_internal_load fails bats_load_safe returns 1.
88
+ #
89
+ # If no library load path can be found bats_load_safe prints an error message
90
+ # and returns 1.
91
+ bats_load_safe() {
92
+ local slug="${1:?}"
93
+ if [[ ${slug:0:1} != / ]]; then # relative paths are relative to BATS_TEST_DIRNAME
94
+ slug="$BATS_TEST_DIRNAME/$slug"
95
+ fi
96
+
97
+ if [[ -f "$slug.bash" ]]; then
98
+ bats_internal_load "$slug.bash"
99
+ return $?
100
+ elif [[ -f "$slug" ]]; then
101
+ bats_internal_load "$slug"
102
+ return $?
103
+ fi
104
+
105
+ # loading from PATH (retained for backwards compatibility)
106
+ if [[ ! -f "$1" ]] && type -P "$1" >/dev/null; then
107
+ # shellcheck disable=SC1090
108
+ source "$1"
109
+ return $?
110
+ fi
111
+
112
+ # No library load path can be found
113
+ printf "bats_load_safe: Could not find '%s'[.bash]\n" "$slug" >&2
114
+ return 1
115
+ }
116
+
117
+ bats_load_library_safe() { # <slug>
118
+ local slug="${1:?}" library_path
119
+
120
+ # Check for library load paths in BATS_TEST_DIRNAME and BATS_LIB_PATH
121
+ if [[ ${slug:0:1} != / ]]; then
122
+ if ! find_in_bats_lib_path library_path "$slug"; then
123
+ printf "Could not find library '%s' relative to test file or in BATS_LIB_PATH\n" "$slug" >&2
124
+ return 1
125
+ fi
126
+ else
127
+ # absolute paths are taken as is
128
+ library_path="$slug"
129
+ if [[ ! -f "$library_path" ]]; then
130
+ printf "Could not find library on absolute path '%s'\n" "$library_path" >&2
131
+ return 1
132
+ fi
133
+ fi
134
+
135
+ bats_internal_load "$library_path"
136
+ return $?
137
+ }
138
+
139
+ # immediately exit on error, use bats_load_library_safe to catch and handle errors
140
+ bats_load_library() { # <slug>
141
+ if ! bats_load_library_safe "$@"; then
142
+ exit 1
143
+ fi
144
+ }
145
+
146
+ # load acts like bats_load_safe but exits the shell instead of returning 1.
147
+ load() {
148
+ if ! bats_load_safe "$@"; then
149
+ exit 1
150
+ fi
151
+ }
152
+
153
+ bats_redirect_stderr_into_file() {
154
+ "$@" 2>>"$bats_run_separate_stderr_file" # use >> to see collisions' content
155
+ }
156
+
157
+ bats_merge_stdout_and_stderr() {
158
+ "$@" 2>&1
159
+ }
160
+
161
+ # write separate lines from <input-var> into <output-array>
162
+ bats_separate_lines() { # <output-array> <input-var>
163
+ local -r output_array_name="$1"
164
+ local -r input_var_name="$2"
165
+ local input="${!input_var_name}"
166
+ if [[ $keep_empty_lines ]]; then
167
+ local bats_separate_lines_lines=()
168
+ if [[ -n "$input" ]]; then # avoid getting an empty line for empty input
169
+ # remove one trailing \n if it exists to compensate its addition by <<<
170
+ input=${input%$'\n'}
171
+ while IFS= read -r line; do
172
+ bats_separate_lines_lines+=("$line")
173
+ done <<<"${input}"
174
+ fi
175
+ eval "${output_array_name}=(\"\${bats_separate_lines_lines[@]}\")"
176
+ else
177
+ # shellcheck disable=SC2034,SC2206
178
+ IFS=$'\n' read -d '' -r -a "$output_array_name" <<<"${!input_var_name}" || true # don't fail due to EOF
179
+ fi
180
+ }
181
+
182
+ bats_errexit_subshell() { # <command with args...>
183
+ (
184
+ set -eET
185
+ "$@"
186
+ )
187
+ }
188
+
189
+ bats_pipe() { # [-N] [--] command0 [ \| command1 [ \| command2 [...]]]
190
+ # This will run each command given, piping them appropriately.
191
+ # Meant to be used in combination with `run` helper to allow piped commands
192
+ # to be used.
193
+ # Note that `\|` must be used, not `|`.
194
+ # By default, the exit code of this command will be the last failure in the
195
+ # chain of piped commands (similar to `set -o pipefail`).
196
+ # Supplying -N (e.g. -0) will instead always use the exit code of the command
197
+ # at that position in the chain.
198
+ # --returned-status=N could be used as an alternative to -N. This also allows
199
+ # for negative values (which count from the end in reverse order).
200
+
201
+ local pipestatus_position=
202
+
203
+ # parse options starting with -
204
+ while [[ $# -gt 0 ]] && [[ $1 == -* ]]; do
205
+ case "$1" in
206
+ -[0-9]*)
207
+ pipestatus_position="${1#-}"
208
+ ;;
209
+ --returned-status*)
210
+ if [ "$1" = "--returned-status" ]; then
211
+ pipestatus_position="$2"
212
+ shift
213
+ elif [[ "$1" =~ ^--returned-status= ]]; then
214
+ pipestatus_position="${1#--returned-status=}"
215
+ else
216
+ printf "Usage error: unknown flag '%s'" "$1" >&2
217
+ return 1
218
+ fi
219
+ ;;
220
+ --)
221
+ shift # eat the -- before breaking away
222
+ break
223
+ ;;
224
+ *)
225
+ printf "Usage error: unknown flag '%s'" "$1" >&2
226
+ return 1
227
+ ;;
228
+ esac
229
+ shift
230
+ done
231
+
232
+ # parse and validate arguments, escape as necessary
233
+ local -a commands_and_args=("$@")
234
+ local -a escaped_args=()
235
+ local -i pipe_count=0
236
+ local -i previous_pipe_index=-1
237
+ local -i index=0
238
+ for (( index = 0; index < $#; index++ )); do
239
+ local current_command_or_arg="${commands_and_args[$index]}"
240
+ local escaped_arg="$current_command_or_arg"
241
+ if [[ "$current_command_or_arg" != '|' ]]; then
242
+ # escape args to protect them when eval'd (e.g. if they contain whitespace).
243
+ printf -v escaped_arg "%q" "$current_command_or_arg"
244
+ elif [ "$current_command_or_arg" = "|" ]; then
245
+ if [ "$index" -eq 0 ]; then
246
+ printf "Usage error: Cannot have leading \`\\|\`.\n" >&2
247
+ return 1
248
+ fi
249
+ if (( (previous_pipe_index + 1) >= index )); then
250
+ printf "Usage error: Cannot have consecutive \`\\|\`. Found at argument position '%s'.\n" "$index" >&2
251
+ return 1
252
+ fi
253
+ (( ++pipe_count ))
254
+ previous_pipe_index="$index"
255
+ fi
256
+ escaped_args+=("$escaped_arg")
257
+ done
258
+
259
+ if (( (previous_pipe_index > 0) && (previous_pipe_index == ($# - 1)) )); then
260
+ printf "Usage error: Cannot have trailing \`\\|\`.\n" >&2
261
+ return 1
262
+ fi
263
+
264
+ if (( pipe_count == 0 )); then
265
+ # Don't allow for no pipes. This might be a typo in the test,
266
+ # e.g. `run bats_pipe command0 | command1`
267
+ # instead of `run bats_pipe command0 \| command1`
268
+ # Unfortunately, we can't catch `run bats_pipe command0 \| command1 | command2`.
269
+ # But this check is better than just allowing no pipes.
270
+ printf "Usage error: No \`\\|\`s found. Is this an error?\n" >&2
271
+ return 1
272
+ fi
273
+
274
+ # there will be pipe_count + 1 entries in PIPE_STATUS (pipe_count number of \|'s between each entry).
275
+ # valid indices are [-(pipe_count + 1), pipe_count]
276
+ if [ -n "$pipestatus_position" ] && (( (pipestatus_position > pipe_count) || (-pipestatus_position > (pipe_count + 1)) )); then
277
+ printf "Usage error: Too large of -N argument (or --returned-status) given. Argument value: '%s'.\n" "$pipestatus_position" >&2
278
+ return 1
279
+ fi
280
+
281
+ # run commands and return appropriate pipe status
282
+ local -a __bats_pipe_eval_pipe_status=()
283
+ eval "${escaped_args[*]}" '; __bats_pipe_eval_pipe_status=(${PIPESTATUS[@]})'
284
+
285
+ local result_status=
286
+ if [ -z "$pipestatus_position" ]; then
287
+ # if we are performing default "last failure" behavior,
288
+ # iterate backwards through pipe_status to find the last error.
289
+ result_status=0
290
+ for index in "${!__bats_pipe_eval_pipe_status[@]}"; do
291
+ # OSX bash doesn't support negative indexing.
292
+ local backward_iter_index="$((${#__bats_pipe_eval_pipe_status[@]} - index - 1))"
293
+ local status_at_backward_iter_index="${__bats_pipe_eval_pipe_status[$backward_iter_index]}"
294
+ if (( status_at_backward_iter_index != 0 )); then
295
+ result_status="$status_at_backward_iter_index"
296
+ break;
297
+ fi
298
+ done
299
+ elif (( pipestatus_position >= 0 )); then
300
+ result_status="${__bats_pipe_eval_pipe_status[$pipestatus_position]}"
301
+ else
302
+ # Must use positive values for some bash's (like OSX).
303
+ local backward_iter_index="$((${#__bats_pipe_eval_pipe_status[@]} + pipestatus_position))"
304
+ result_status="${__bats_pipe_eval_pipe_status[$backward_iter_index]}"
305
+ fi
306
+
307
+ return "$result_status"
308
+ }
309
+
310
+ run() { # [!|-N] [--keep-empty-lines] [--separate-stderr] [--] <command to run...>
311
+ local expected_rc=
312
+ local keep_empty_lines=
313
+ local output_case=merged
314
+ local has_flags=
315
+ # parse options starting with -
316
+ while [[ $# -gt 0 ]] && [[ $1 == -* || $1 == '!' ]]; do
317
+ has_flags=1
318
+ case "$1" in
319
+ '!')
320
+ expected_rc=-1
321
+ ;;
322
+ -[0-9]*)
323
+ expected_rc=${1#-}
324
+ if [[ $expected_rc =~ [^0-9] ]]; then
325
+ printf "Usage error: run: '-NNN' requires numeric NNN (got: %s)\n" "$expected_rc" >&2
326
+ return 1
327
+ elif [[ $expected_rc -gt 255 ]]; then
328
+ printf "Usage error: run: '-NNN': NNN must be <= 255 (got: %d)\n" "$expected_rc" >&2
329
+ return 1
330
+ fi
331
+ ;;
332
+ --keep-empty-lines)
333
+ keep_empty_lines=1
334
+ ;;
335
+ --separate-stderr)
336
+ output_case="separate"
337
+ ;;
338
+ --)
339
+ shift # eat the -- before breaking away
340
+ break
341
+ ;;
342
+ *)
343
+ printf "Usage error: unknown flag '%s'" "$1" >&2
344
+ return 1
345
+ ;;
346
+ esac
347
+ shift
348
+ done
349
+
350
+ local saved_traps
351
+ saved_traps=$(trap -p INT ERR DEBUG)
352
+ # This has to be restored on return from this function to avoid leaking our trap INT into surrounding code.
353
+ # Non zero exits won't restore under the assumption that they will fail the test before it can be aborted,
354
+ # which allows us to avoid duplicating the restore code on every exit path
355
+ trap bats_interrupt_trap_in_run INT
356
+ trap - ERR DEBUG
357
+
358
+ if [[ -n $has_flags ]]; then
359
+ bats_warn_minimum_guaranteed_version "Using flags on \`run\`" 1.5.0
360
+ fi
361
+
362
+ local -a pre_command=()
363
+ # make sure tmpdir exists in case run() is called outside a @test
364
+ local tmpdir=${BATS_TEST_TMPDIR:-${BATS_FILE_TMPDIR:-${BATS_RUN_TMPDIR}}}
365
+
366
+ case "$output_case" in
367
+ merged) # redirects stderr into stdout and fills only $output/$lines
368
+ pre_command=(bats_merge_stdout_and_stderr)
369
+ ;;
370
+ separate) # splits stderr into own file and fills $stderr/$stderr_lines too
371
+ local bats_run_separate_stderr_file
372
+ bats_run_separate_stderr_file="$(mktemp "${tmpdir}/separate-stderr-XXXXXX")"
373
+ pre_command=(bats_redirect_stderr_into_file)
374
+ ;;
375
+ esac
376
+
377
+ if [[ -n "${BATS_RUN_ERREXIT:-}" ]]; then
378
+ pre_command=(bats_errexit_subshell "${pre_command[@]}")
379
+ fi
380
+
381
+ local origFlags="$-"
382
+ set +eET
383
+
384
+ if [[ $keep_empty_lines ]]; then
385
+ # 'output', 'status', 'lines' are global variables available to tests.
386
+ # preserve trailing newlines by appending . and removing it later
387
+ # shellcheck disable=SC2034
388
+ output="$(
389
+ "${pre_command[@]}" "$@"
390
+ status=$?
391
+ printf .
392
+ exit $status
393
+ )"
394
+ status=$?
395
+ output="${output%.}"
396
+ else
397
+ # 'output', 'status', 'lines' are global variables available to tests.
398
+ # shellcheck disable=SC2034
399
+ output="$("${pre_command[@]}" "$@")"
400
+ status=$?
401
+ fi
402
+
403
+ bats_separate_lines lines output
404
+
405
+ if [[ "$output_case" == separate ]]; then
406
+ # shellcheck disable=SC2034
407
+ read -d '' -r stderr <"$bats_run_separate_stderr_file" || true
408
+ bats_separate_lines stderr_lines stderr
409
+ else
410
+ unset stderr stderr_lines
411
+ fi
412
+
413
+ # shellcheck disable=SC2034
414
+ BATS_RUN_COMMAND="${*}"
415
+ set "-$origFlags"
416
+
417
+ bats_run_print_output() {
418
+ if [[ -n "$output" ]]; then
419
+ printf "%s\n" "$output"
420
+ fi
421
+ if [[ "$output_case" == separate && -n "$stderr" ]]; then
422
+ printf "stderr:\n%s\n" "$stderr"
423
+ fi
424
+ }
425
+
426
+ if [[ -n "$expected_rc" ]]; then
427
+ if [[ "$expected_rc" = "-1" ]]; then
428
+ if [[ "$status" -eq 0 ]]; then
429
+ BATS_ERROR_SUFFIX=", expected nonzero exit code!"
430
+ bats_run_print_output
431
+ eval "$saved_traps"
432
+ return 1
433
+ fi
434
+ elif [ "$status" -ne "$expected_rc" ]; then
435
+ # shellcheck disable=SC2034
436
+ BATS_ERROR_SUFFIX=", expected exit code $expected_rc, got $status"
437
+ bats_run_print_output
438
+ eval "$saved_traps"
439
+ return 1
440
+ fi
441
+ elif [[ "$status" -eq 127 ]]; then # "command not found"
442
+ bats_generate_warning 1 "$BATS_RUN_COMMAND"
443
+ fi
444
+
445
+ if [[ ${BATS_VERBOSE_RUN:-} ]]; then
446
+ bats_run_print_output
447
+ fi
448
+
449
+ # don't leak our trap into surrounding code
450
+ eval "$saved_traps"
451
+ }
452
+
453
+ setup() {
454
+ return 0
455
+ }
456
+
457
+ teardown() {
458
+ return 0
459
+ }
460
+
461
+ skip() {
462
+ # if this is a skip in teardown ...
463
+ if [[ -n "${BATS_TEARDOWN_STARTED-}" ]]; then
464
+ # ... we want to skip the rest of teardown.
465
+ # communicate to bats_exit_trap that the teardown was completed without error
466
+ # shellcheck disable=SC2034
467
+ BATS_TEARDOWN_COMPLETED=1
468
+ # if we are already in the exit trap (e.g. due to previous skip) ...
469
+ if [[ "$BATS_TEARDOWN_STARTED" == as-exit-trap ]]; then
470
+ # ... we need to do the rest of the tear_down_trap that would otherwise be skipped after the next call to exit
471
+ bats_exit_trap
472
+ # and then do the exit (at the end of this function)
473
+ fi
474
+ # if we aren't in exit trap, the normal exit handling should suffice
475
+ else
476
+ # ... this is either skip in test or skip in setup.
477
+ # Following variables are used in bats-exec-test which sources this file
478
+ # shellcheck disable=SC2034
479
+ BATS_TEST_SKIPPED="${1:-1}"
480
+ # shellcheck disable=SC2034
481
+ BATS_TEST_COMPLETED=1
482
+ fi
483
+ exit 0
484
+ }
485
+
486
+ bats_test_function() {
487
+ local tags=() current_tags=()
488
+ while (( $# > 0 )); do
489
+ case "$1" in
490
+ --description)
491
+ local test_description=
492
+ # use eval to resolve variable references in test names
493
+ eval "printf -v test_description '%s' \"$2\""
494
+ shift 2
495
+ ;;
496
+ --tags)
497
+ if [[ "$2" != "" ]]; then # avoid unbound variable with set -u Bash 3
498
+ IFS=',' read -ra current_tags <<<"$2"
499
+ tags+=("${current_tags[@]}")
500
+ fi
501
+ shift 2
502
+ ;;
503
+ --)
504
+ shift
505
+ break
506
+ ;;
507
+ *)
508
+ printf "ERROR: unknown option %s for bats_test_function" "$1" >&2
509
+ exit 1
510
+ ;;
511
+ esac
512
+ done
513
+
514
+ if (( ${#tags[@]} > 1 )); then # avoid unbound variable with set -u Bash 3
515
+ bats_sort tags "${tags[@]}"
516
+ fi
517
+
518
+ BATS_TEST_NAMES+=("$*")
519
+ local quoted_parameters
520
+ printf -v quoted_parameters " %q" "$@"
521
+ quoted_parameters=${quoted_parameters:1} # cut off leading space
522
+
523
+ # if this is the currently selected test, set tags and name
524
+ # this should only be entered from bats-exec-test
525
+ if [[ ${BATS_TEST_NAME-} == "$quoted_parameters" ]]; then
526
+ # shellcheck disable=SC2034
527
+ BATS_TEST_TAGS=("${tags[@]+${tags[@]}}")
528
+ export BATS_TEST_DESCRIPTION="${test_description-$*}"
529
+ # shellcheck disable=SC2034
530
+ BATS_TEST_COMMAND=("$@")
531
+ fi
532
+ }
533
+
534
+ # decides whether a failed test should be run again
535
+ bats_should_retry_test() {
536
+ # test try number starts at 1
537
+ # 0 retries means run only first try
538
+ ((BATS_TEST_TRY_NUMBER <= BATS_TEST_RETRIES))
539
+ }