@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.

@@ -0,0 +1,402 @@
1
+ #!/usr/bin/env bash
2
+ set -eET
3
+
4
+ args=("$@")
5
+ filter_tags_list=()
6
+
7
+ # shellcheck source=lib/bats-core/common.bash disable=SC2153
8
+ source "$BATS_ROOT/$BATS_LIBDIR/bats-core/common.bash"
9
+ # shellcheck source=lib/bats-core/preprocessing.bash
10
+ source "$BATS_ROOT/$BATS_LIBDIR/bats-core/preprocessing.bash"
11
+
12
+ abort() {
13
+ printf 'ERROR: '
14
+ # shellcheck disable=SC2059
15
+ printf "$@"
16
+ exit 1
17
+ } >&2
18
+
19
+ read_tags() {
20
+ local IFS=,
21
+ read -ra tags <<<"$1" || true
22
+ if ((${#tags[@]} > 0)); then
23
+ for ((i = 0; i < ${#tags[@]}; ++i)); do
24
+ bats_trim "tags[$i]" "${tags[$i]}"
25
+ done
26
+ bats_sort sorted_tags "${tags[@]}"
27
+ # shellcheck disable=SC2154
28
+ filter_tags_list+=("${sorted_tags[*]}")
29
+ else
30
+ filter_tags_list+=("")
31
+ fi
32
+ }
33
+
34
+ while [[ "$#" -ne 0 ]]; do
35
+ case "$1" in
36
+ --dummy-flag)
37
+ ;;
38
+ --filter-tags)
39
+ shift
40
+ read_tags "$1"
41
+ ;;
42
+ --)
43
+ shift 1
44
+ break
45
+ ;;
46
+ *)
47
+ abort "Unknown flag %s in command:\nbats-gather-tests %s" "$1" "${args[*]}"
48
+ ;;
49
+ esac
50
+ shift 1
51
+ done
52
+
53
+
54
+ # shellcheck source=lib/bats-core/test_functions.bash disable=SC2153
55
+ # required to provide e.g. `load` for free code (some users rely on this)
56
+ source "$BATS_ROOT/$BATS_LIBDIR/bats-core/test_functions.bash"
57
+ # _bats_test_functions_setup will be called per file further down
58
+
59
+ # override test_functions.bash's version to use it for test registration
60
+ bats_test_function() {
61
+ local -a tags=() current_tags=()
62
+ local description=
63
+
64
+ while (( $# > 0 )); do
65
+ case "$1" in
66
+ --description)
67
+ description=$2
68
+ shift 2
69
+ ;;
70
+ --tags)
71
+ if [[ "$2" != "" ]]; then # avoid unbound variable with set -u Bash 3
72
+ IFS=, read -ra current_tags <<<"$2" || true
73
+ tags+=("${current_tags[@]}")
74
+ fi
75
+ shift 2
76
+ ;;
77
+ --)
78
+ shift
79
+ break
80
+ ;;
81
+ *)
82
+ printf "ERROR: unknown option %s for bats_test_function" "$1" >&2
83
+ exit 1
84
+ ;;
85
+ esac
86
+ done
87
+
88
+ if (( ${#tags[@]} > 1 )); then # avoid unbound variable with set -u Bash 3
89
+ bats_sort tags "${tags[@]}"
90
+ fi
91
+
92
+ line="$BATS_TEST_FILENAME"
93
+ line+=$'\t'
94
+ line+="$*"
95
+ # always execute should_skip_because_of_status for its sideeffects
96
+ if should_skip_because_of_status || should_skip_because_of_focus || should_skip_because_of_filter_tags || should_skip_because_of_filter || should_skip_because_of_negative_filter; then
97
+ return 0
98
+ fi
99
+
100
+ # dereferencing ${test_names[*]} fails on older bash versions when empty -> check before
101
+ if [[ ${#test_names[@]} -gt 0 && " ${test_names[*]} " == *" $line "* ]]; then
102
+ test_dupes+=("$line")
103
+ fi
104
+ test_names+=("$line")
105
+
106
+ local args
107
+ printf -v args '%q ' "$@"
108
+ #echo "Adding test $line as '$BATS_TEST_FILENAME$args'" >&2
109
+ printf "%s\t%s\n" "$BATS_TEST_FILENAME" "$args" >> "$TESTS_LIST_FILE"
110
+ }
111
+
112
+ function should_skip_because_of_focus() {
113
+ if bats_all_in tags 'bats:focus'; then
114
+ if [[ $focus_mode == 1 ]]; then
115
+ # focused tests in focus mode should just be registered
116
+ :
117
+ else
118
+ # the current test enables focus mode ...
119
+ focus_mode=1
120
+ # ... -> remove previously found, unfocused tests
121
+ included_tests=()
122
+ : >| "$TESTS_LIST_FILE"
123
+ fi
124
+ elif [[ $focus_mode == 1 ]]; then
125
+ # the current test is not focused but focus mode is enabled -> filter out
126
+ return 0
127
+ # no else -> unfocused tests outside focus mode should just be registered
128
+ fi
129
+ return 1
130
+ }
131
+
132
+ function should_skip_because_of_filter_tags() {
133
+ if (( ${#filter_tags_list[@]} > 0 )); then
134
+ for filter_tags in "${filter_tags_list[@]}"; do
135
+ # empty search tags only match empty test tags!
136
+ if [[ -z "$filter_tags" ]]; then
137
+ if [[ ${#tags[@]} -eq 0 ]]; then
138
+ return 1
139
+ fi
140
+ continue
141
+ fi
142
+ # non empty filter tags must be processed
143
+ local -a positive_filter_tags=() negative_filter_tags=()
144
+ IFS=, read -ra filter_tags <<<"$filter_tags" || true
145
+ for filter_tag in "${filter_tags[@]}"; do
146
+ if [[ $filter_tag == !* ]]; then
147
+ bats_trim filter_tag "${filter_tag#!}"
148
+ negative_filter_tags+=("${filter_tag}")
149
+ else
150
+ positive_filter_tags+=("${filter_tag}")
151
+ fi
152
+ done
153
+ if bats_append_arrays_as_args positive_filter_tags -- bats_all_in tags &&
154
+ ! bats_append_arrays_as_args negative_filter_tags -- bats_any_in tags; then
155
+ return 1
156
+ fi
157
+ done
158
+ return 0 # skip, because no match was found
159
+ fi
160
+ return 1 # no filter tags -> nothing to skip
161
+ }
162
+
163
+ if [[ -n "${filter-}" ]]; then
164
+ function should_skip_because_of_filter() {
165
+ # shellcheck disable=SC2154 # filter should be inherited as env var
166
+ ! [[ "$description" =~ $filter ]]
167
+ }
168
+ else
169
+ function should_skip_because_of_filter() {
170
+ # skip this when there is no $filter
171
+ return 1
172
+ }
173
+ fi
174
+
175
+ if [[ -n "${nfilter-}" ]]; then
176
+ function should_skip_because_of_negative_filter() {
177
+ # shellcheck disable=SC2154 # nfilter should be inherited as env var
178
+ [[ "$description" =~ $nfilter ]]
179
+ }
180
+ else
181
+ function should_skip_because_of_negative_filter() {
182
+ # skip this when there is no $nfilter
183
+ return 1
184
+ }
185
+ fi
186
+
187
+ function should_skip_because_of_status() {
188
+ # disable this filter if not activated by $filter_status
189
+ return 1
190
+ }
191
+
192
+ # shellcheck disable=SC2154 # filter_status is set in the environment
193
+ if [[ -n "${filter_status-}" ]]; then
194
+ case "$filter_status" in
195
+ failed)
196
+ bats_filter_test_by_status() { # <line>
197
+ ! bats_binary_search "$1" "passed_tests"
198
+ }
199
+ ;;
200
+ passed)
201
+ bats_filter_test_by_status() {
202
+ ! bats_binary_search "$1" "failed_tests"
203
+ }
204
+ ;;
205
+ missed)
206
+ bats_filter_test_by_status() {
207
+ ! bats_binary_search "$1" "failed_tests" && ! bats_binary_search "$1" "passed_tests"
208
+ }
209
+ ;;
210
+ *)
211
+ printf "Error: Unknown value '%s' for --filter-status. Valid values are 'failed' and 'missed'.\n" "$filter_status" >&2
212
+ exit 1
213
+ ;;
214
+ esac
215
+
216
+ if IFS='' read -d $'\n' -r BATS_PREVIOUS_RUNLOG_FILE < <(ls -1r "$BATS_RUN_LOGS_DIRECTORY"); then
217
+ BATS_PREVIOUS_RUNLOG_FILE="$BATS_RUN_LOGS_DIRECTORY/$BATS_PREVIOUS_RUNLOG_FILE"
218
+ if [[ $BATS_PREVIOUS_RUNLOG_FILE == "$BATS_RUNLOG_FILE" ]]; then
219
+ count=$(find "$BATS_RUN_LOGS_DIRECTORY" -name "$BATS_RUNLOG_DATE*" | wc -l)
220
+ BATS_RUNLOG_FILE="$BATS_RUN_LOGS_DIRECTORY/${BATS_RUNLOG_DATE}-$count.log"
221
+ fi
222
+ failed_tests=()
223
+ passed_tests=()
224
+ # store tests that were already filtered out in the last run for the same filter reason
225
+ last_filtered_tests=()
226
+ i=0
227
+ while read -rd $'\n' line; do
228
+ ((++i))
229
+ case "$line" in
230
+ "passed "*)
231
+ passed_tests+=("${line#passed }")
232
+ ;;
233
+ "failed "*)
234
+ failed_tests+=("${line#failed }")
235
+ ;;
236
+ "status-filtered $filter_status"*) # pick up tests that were filtered in the last round for the same status
237
+ last_filtered_tests+=("${line#status-filtered "$filter_status" }")
238
+ ;;
239
+ "status-filtered "*) # ignore other status-filtered lines
240
+ ;;
241
+ "#"*) # allow for comments
242
+ ;;
243
+ *)
244
+ printf "Error: %s:%d: Invalid format: %s\n" "$BATS_PREVIOUS_RUNLOG_FILE" "$i" "$line" >&2
245
+ exit 1
246
+ ;;
247
+ esac
248
+ done < <(sort "$BATS_PREVIOUS_RUNLOG_FILE")
249
+
250
+ # enable filter only, when there is something to filter
251
+ function should_skip_because_of_status() {
252
+ #echo "Match:"
253
+ #echo "$line"
254
+ #printf "%s\n" "${failed_tests[@]}"
255
+ #echo "or"
256
+ #printf "%s\n" "${last_filtered_tests[@]}"
257
+ if bats_filter_test_by_status "$line"; then
258
+ if ! bats_binary_search "$line" last_filtered_tests; then
259
+ included_tests+=("$line")
260
+ #echo "included"
261
+ return 1
262
+ fi
263
+ fi
264
+ #echo "excluded"
265
+ excluded_tests+=("$line")
266
+ return 0
267
+ } >&2
268
+ else
269
+ printf "No recording of previous runs found. Running all tests!\n" >&2
270
+ fi
271
+ else
272
+ : #printf "Not filtering by status!\n" >&2
273
+ fi
274
+
275
+ # shellcheck source=lib/bats-core/tracing.bash
276
+ source "$BATS_ROOT/$BATS_LIBDIR/bats-core/tracing.bash"
277
+
278
+ BATS_OUT="$BATS_RUN_TMPDIR/gather-tests.out"
279
+ touch "$BATS_OUT"
280
+
281
+ bats_gather_tests_exit_trap() {
282
+ local bats_gather_tests_exit_status=$?
283
+ trap - ERR EXIT DEBUG
284
+ if (( bats_gather_tests_exit_status != 0)); then
285
+ printf "1..1\nnot ok 1 bats-gather-tests\n"
286
+ # play back traces from test file evaluation
287
+ bats_replace_filename < "$BATS_TRACE"
288
+ bats_replace_filename <"$BATS_OUT" | bats_prefix_lines_for_tap_output
289
+ fi >&2
290
+ exit "$bats_gather_tests_exit_status"
291
+ }
292
+
293
+ trap bats_gather_tests_exit_trap EXIT
294
+
295
+ # prepare tracing for errors during test file evaluation
296
+ BATS_TRACE="$BATS_RUN_TMPDIR/bats-gather-tests.trace"
297
+ touch "$BATS_TRACE"
298
+
299
+ bats_gather_tests_source_exit_trap() {
300
+ local bats_gather_tests_source_exit_status=$?
301
+ trap - ERR EXIT DEBUG
302
+ if (( bats_gather_tests_source_exit_status != 0)); then
303
+ bats_get_failure_stack_trace stack_trace
304
+ # shellcheck disable=SC2154
305
+ bats_print_stack_trace "${stack_trace[@]}"
306
+ # TODO: why doesn't this work via ERR trap?
307
+ # shellcheck disable=SC2154,SC2034
308
+ BATS_ERROR_STATUS=$bats_gather_tests_source_exit_status
309
+ bats_print_failed_command "${stack_trace[@]}"
310
+ fi >>"$BATS_TRACE"
311
+ exit "$bats_gather_tests_source_exit_status"
312
+ }
313
+
314
+ bats_gather_tests_for_file() {
315
+ local test_names=() test_dupes=() included_tests=() excluded_tests=()
316
+
317
+ trap bats_gather_tests_source_exit_trap EXIT
318
+ bats_setup_tracing
319
+ bats_set_stacktrace_limit
320
+
321
+ # do the actual evaluation for gathering the tests
322
+ # shellcheck disable=SC1090
323
+ BATS_TEST_DIRNAME="${filename%/*}" source "$BATS_TEST_SOURCE" 1>>"$BATS_OUT" 2>&1
324
+
325
+ if [[ "${#test_dupes[@]}" -ne 0 ]]; then
326
+ printf 'file_duplicate_test_names="%q"\n' "${test_dupes[*]#$filename$'\t'}"
327
+ fi
328
+
329
+ if [[ -n "$filter_status" ]]; then
330
+ # save filtered tests to exclude them again in next round
331
+ for test_line in "${excluded_tests[@]}"; do
332
+ printf "status-filtered %s %s\n" "$filter_status" "$test_line"
333
+ done >>"$BATS_RUNLOG_FILE"
334
+ fi
335
+
336
+
337
+ printf "file_test_count=%d\n" "${#test_names[@]}"
338
+ printf "file_included_test_count=%d\n" "${#included_tests[@]}"
339
+ printf "focus_mode=%d\n" "$focus_mode"
340
+ }
341
+
342
+ focus_mode=0
343
+ total_test_count=0
344
+ total_included_test_count=0
345
+ export BATS_TEST_FILE_NUMBER=0
346
+
347
+ for filename in "$@"; do
348
+ (( ++BATS_TEST_FILE_NUMBER ))
349
+ if [[ ! -f "$filename" ]]; then
350
+ abort 'Test file "%s" does not exist.\n' "${filename}"
351
+ fi
352
+
353
+ BATS_TEST_FILENAME="$filename"
354
+ _bats_test_functions_setup -1 # invalid TEST_NUMBER, as this is not a test
355
+
356
+ # shellcheck disable=SC2034
357
+ BATS_TEST_NAME=source
358
+ bats_preprocess_source # uses BATS_TEST_FILENAME, BATS_TEST_FILE_NUMBER
359
+
360
+ file_duplicate_test_names=""
361
+ file_test_count=0
362
+ file_included_test_count=0
363
+ saved_focus_mode=$focus_mode
364
+
365
+ # get new values for the variables above
366
+ if [[ $BASH_VERSION == 4.3.* ]]; then
367
+ # Bash 4.3 has function scoping issues when this is run in $() -> work around via file
368
+ bats_gather_tests_var_transfer_file=$BATS_RUN_TMPDIR/gather-tests-var-transfer
369
+ (set -eET; bats_gather_tests_for_file >"$bats_gather_tests_var_transfer_file")
370
+ result=$(<"$bats_gather_tests_var_transfer_file")
371
+ else
372
+ # separate retrieval from eval to avoid hiding the exit code
373
+ result="$(set -eET; bats_gather_tests_for_file)"
374
+ fi
375
+
376
+ eval "$result"
377
+
378
+ if [[ -n "$file_duplicate_test_names" ]]; then
379
+ trap - EXIT # prevent 1..1 from being printed
380
+ abort 'Duplicate test name(s) in file "%s": %s' "$filename" "$file_duplicate_test_names"
381
+ fi
382
+
383
+ total_test_count=$((total_test_count + file_test_count))
384
+
385
+ # did focus mode turn on in this file? (cannot turn off afterwards)
386
+ if (( saved_focus_mode != focus_mode)); then # -> only count new tests
387
+ total_included_test_count=$file_included_test_count
388
+ else # -> count previous tests as well
389
+ total_included_test_count=$((total_included_test_count + file_included_test_count))
390
+ fi
391
+ done
392
+
393
+ if [[ -n "$filter_status" ]]; then
394
+ if (( total_test_count == 0 && total_included_test_count == 0 )); then
395
+ printf "There were no tests of status '%s' in the last recorded run.\n" "$filter_status" >&2
396
+ fi
397
+ fi
398
+
399
+ # communicate to the caller that we are running in focus mode
400
+ if (( focus_mode )); then
401
+ printf "focus_mode\n"
402
+ fi
@@ -0,0 +1,118 @@
1
+ #!/usr/bin/env bash
2
+ set -e
3
+
4
+ bats_encode_test_name() {
5
+ local name="$1"
6
+ local result='test_'
7
+ local hex_code
8
+
9
+ if [[ ! "$name" =~ [^[:alnum:]\ _-] ]]; then
10
+ name="${name//_/-5f}"
11
+ name="${name//-/-2d}"
12
+ name="${name// /_}"
13
+ result+="$name"
14
+ else
15
+ local length="${#name}"
16
+ local char i
17
+
18
+ for ((i = 0; i < length; i++)); do
19
+ char="${name:$i:1}"
20
+ if [[ "$char" == ' ' ]]; then
21
+ result+='_'
22
+ elif [[ "$char" =~ [[:alnum:]] ]]; then
23
+ result+="$char"
24
+ else
25
+ printf -v 'hex_code' -- '-%02x' \'"$char"
26
+ result+="$hex_code"
27
+ fi
28
+ done
29
+ fi
30
+
31
+ printf -v "$2" '%s' "$result"
32
+ }
33
+
34
+ BATS_TEST_PATTERN="^[[:blank:]]*@test[[:blank:]]+(.*[^[:blank:]])[[:blank:]]+\{(.*)\$"
35
+ BATS_TEST_PATTERN_COMMENT="[[:blank:]]*([^[:blank:]()]+)[[:blank:]]*\(?\)?[[:blank:]]+\{[[:blank:]]+#[[:blank:]]*@test[[:blank:]]*\$"
36
+ BATS_COMMENT_COMMAND_PATTERN="^[[:blank:]]*#[[:blank:]]*bats[[:blank:]]+(.*)$"
37
+ BATS_VALID_TAG_PATTERN="[-_:[:alnum:]]+"
38
+ BATS_VALID_TAGS_PATTERN="^ *($BATS_VALID_TAG_PATTERN)?( *, *$BATS_VALID_TAG_PATTERN)* *$"
39
+
40
+ # shellcheck source=lib/bats-core/common.bash
41
+ source "$BATS_ROOT/$BATS_LIBDIR/bats-core/common.bash"
42
+
43
+ extract_tags() { # <tag_type/return_var> <tags-string>
44
+ local -r tag_type=$1 tags_string=$2
45
+ local -a tags=()
46
+
47
+ if [[ $tags_string =~ $BATS_VALID_TAGS_PATTERN ]]; then
48
+ IFS=, read -ra tags <<<"$tags_string"
49
+ local -ri length=${#tags[@]}
50
+ for ((i = 0; i < length; ++i)); do
51
+ local element="tags[$i]"
52
+ bats_trim "$element" "${!element}" 2>/dev/null # printf on bash 3 will complain but work anyways
53
+ if [[ -z "${!element}" && -n "${CHECK_BATS_COMMENT_COMMANDS:-}" ]]; then
54
+ printf "%s:%d: Error: Invalid %s: '%s'. " "$test_file" "$line_number" "$tag_type" "$tags_string"
55
+ printf "Tags must not be empty. Please remove redundant commas!\n"
56
+ exit_code=1
57
+ fi
58
+ done
59
+ elif [[ -n "${CHECK_BATS_COMMENT_COMMANDS:-}" ]]; then
60
+ printf "%s:%d: Error: Invalid %s: '%s'. " "$test_file" "$line_number" "$tag_type" "$tags_string"
61
+ printf "Valid tags must match %s and be separated with comma (and optional spaces)\n" "$BATS_VALID_TAG_PATTERN"
62
+ exit_code=1
63
+ fi >&2
64
+ if ((${#tags[@]} > 0)); then
65
+ eval "$tag_type=(\"\${tags[@]}\")"
66
+ else
67
+ eval "$tag_type=()"
68
+ fi
69
+ }
70
+
71
+ test_file="$1"
72
+ test_tags=()
73
+ file_tags=()
74
+ line_number=0
75
+ exit_code=0
76
+ EMPTY_BODY_REGEX='[[:space:]]*\}'
77
+ IFS=,
78
+ {
79
+ while IFS= read -r line; do
80
+ ((++line_number))
81
+ line="${line//$'\r'/}"
82
+ if [[ "$line" =~ $BATS_TEST_PATTERN ]] || [[ "$line" =~ $BATS_TEST_PATTERN_COMMENT ]]; then
83
+ name="${BASH_REMATCH[1]#[\'\"]}"
84
+ name="${name%[\'\"]}"
85
+ body="${BASH_REMATCH[2]:-}"
86
+ bats_encode_test_name "$name" 'encoded_name'
87
+
88
+ if [[ "$body" =~ $EMPTY_BODY_REGEX ]]; then
89
+ # ":;" is needed for empty {} after test
90
+ lead=':; '
91
+ else
92
+ # avoid injecting non user code into tests
93
+ lead=''
94
+ fi
95
+
96
+ # shellcheck disable=SC2154 # encoded_name is declare via bats_encode_test_name
97
+ printf 'bats_test_function --description %q --tags "%s" --tags "%s" -- %s;' "$name" "${test_tags[*]-}" "${file_tags[*]-}" "$encoded_name"
98
+ printf '%s() { %s%s\n' "${encoded_name:?}" "$lead" "$body" || :
99
+
100
+ test_tags=() # reset test tags for next test
101
+ else
102
+ if [[ "$line" =~ $BATS_COMMENT_COMMAND_PATTERN ]]; then
103
+ command=${BASH_REMATCH[1]}
104
+ case $command in
105
+ 'test_tags='*)
106
+ extract_tags test_tags "${command#test_tags=}"
107
+ ;;
108
+ 'file_tags='*)
109
+ extract_tags file_tags "${command#file_tags=}"
110
+ ;;
111
+ esac
112
+ fi
113
+ printf '%s\n' "$line"
114
+ fi
115
+ done
116
+ } <<<"$(<"$test_file")"$'\n'
117
+
118
+ exit $exit_code
package/man/Makefile ADDED
@@ -0,0 +1,23 @@
1
+ # Makefile
2
+ #
3
+ # bats-core manpages
4
+ #
5
+ RONN := ronn -W
6
+ PAGES := bats.1 bats.7
7
+ ORG := bats-core
8
+ MANUAL := 'Bash Automated Testing System'
9
+ ISOFMT := $(shell date -I)
10
+ RM := rm -f
11
+
12
+ .PHONY: all clean
13
+
14
+ all: $(PAGES)
15
+
16
+ bats.1: bats.1.ronn
17
+ $(RONN) --date=$(ISOFMT) --manual=$(MANUAL) --organization=$(ORG) --roff $<
18
+
19
+ bats.7: bats.7.ronn
20
+ $(RONN) --date=$(ISOFMT) --manual=$(MANUAL) --organization=$(ORG) --roff $<
21
+
22
+ clean:
23
+ $(RM) $(PAGES)
package/man/README.md ADDED
@@ -0,0 +1,5 @@
1
+ Bats man pages are generated with [Ronn](http://rtomayko.github.io/ronn/).
2
+
3
+ After making changes to `bats.1.ronn` or `bats.7.ronn`, run `make` in
4
+ this directory to generate `bats.1` and `bats.7`. **Do not edit the
5
+ `bats.1` or `bats.7` files directly.**
package/man/bats.1 ADDED
@@ -0,0 +1,143 @@
1
+ .\" generated with Ronn-NG/v0.10.1
2
+ .\" http://github.com/apjanke/ronn-ng/tree/0.10.1
3
+ .TH "BATS" "1" "July 2026" "bats-core" "Bash Automated Testing System"
4
+ .SH "NAME"
5
+ \fBbats\fR \- Bash Automated Testing System
6
+ .SH "SYNOPSIS"
7
+ Usage: bats [OPTIONS] \fItests\fR bats [\-h | \-v]
8
+ .P
9
+ \fItests\fR is the path to a Bats test file, or the path to a directory containing Bats test files (ending with "\.bats")
10
+ .SH "DESCRIPTION"
11
+ Bats is a TAP\-compliant testing framework for Bash\. It provides a simple way to verify that the UNIX programs you write behave as expected\.
12
+ .P
13
+ A Bats test file is a Bash script with special syntax for defining test cases\. Under the hood, each test case is just a function with a description\.
14
+ .P
15
+ Test cases consist of standard shell commands\. Bats makes use of Bash's \fBerrexit\fR (\fBset \-e\fR) option when running test cases\. If every command in the test case exits with a \fB0\fR status code (success), the test passes\. In this way, each line is an assertion of truth\.
16
+ .P
17
+ See \fBbats\fR(7) for more information on writing Bats tests\.
18
+ .SH "RUNNING TESTS"
19
+ To run your tests, invoke the \fBbats\fR interpreter with a path to a test file\. The file's test cases are run sequentially and in isolation\. If all the test cases pass, \fBbats\fR exits with a \fB0\fR status code\. If there are any failures, \fBbats\fR exits with a \fB1\fR status code\.
20
+ .P
21
+ You can invoke the \fBbats\fR interpreter with multiple test file arguments, or with a path to a directory containing multiple \fB\.bats\fR files\. Bats will run each test file individually and aggregate the results\. If any test case fails, \fBbats\fR exits with a \fB1\fR status code\.
22
+ .SH "FILTERING TESTS"
23
+ There are multiple mechanisms to filter which tests to execute:
24
+ .IP "\(bu" 4
25
+ \fB\-\-filter <regex>\fR to filter by test name
26
+ .IP "\(bu" 4
27
+ \fB\-\-filter\-status <status>\fR to filter by the test's status in the last run
28
+ .IP "\(bu" 4
29
+ \fB\-\-filter\-tags <tag\-list>\fR to filter by the tags of a test
30
+ .IP "" 0
31
+ .SH "\-\-FILTER\-TAGS <var>TAG\-LIST</var>"
32
+ Tags can be used for finegrained filtering of which tests to run via \fB\-\-filter\-tags\fR\. This accepts a comma separated list of tags\. Only tests that match all of these tags will be executed\. For example, \fBbats \-\-filter\-tags a,b,c\fR will pick up tests with tags \fBa,b,c\fR, but not tests that miss one or more of those tags\.
33
+ .P
34
+ Additionally, you can specify negative tags via \fBbats \-\-filter\-tags a,!b,c\fR, which now won't match tests with tags \fBa,b,c\fR, due to the \fBb\fR, but will select \fBa,c\fR\. To put it more formally, \fB\-\-filter\-tags\fR is a boolean conjunction\.
35
+ .P
36
+ To allow for more complex queries, you can specify multiple \fB\-\-filter\-tags\fR\. A test will be executed, if it matches at least one of them\. This means multiple \fB\-\-filter\-tags\fR form a boolean disjunction\.
37
+ .P
38
+ A query of \fB\-\-filter\-tags a,!b \-\-filter\-tags b,c\fR can be translated to: Execute only tests that (have tag a, but not tag b) or (have tag b and c)\.
39
+ .P
40
+ An empty tag list matches tests without tags\.
41
+ .SH "OPTIONS"
42
+ .IP "\(bu" 4
43
+ \fB\-\-abort\fR: Stop execution of suite on first failed test
44
+ .IP "\(bu" 4
45
+ \fB\-\-allow\-empty\-suite\fR: Exit with code 0 (instead of the default code 1) when no tests are found\. Can also be set via \fB$BATS_ALLOW_EMPTY_SUITE\fR (to any non\-empty value), which keeps compatibility with Bats versions that don't know this flag\.
46
+ .IP "\(bu" 4
47
+ \fB\-c\fR, \fB\-\-count\fR: Count the number of test cases without running any tests
48
+ .IP "\(bu" 4
49
+ \fB\-\-code\-quote\-style <style>\fR: A two character string of code quote delimiters or \fBcustom\fR which requires setting \fB$BATS_BEGIN_CODE_QUOTE\fR and \fB$BATS_END_CODE_QUOTE\fR\. Can also be set via \fB$BATS_CODE_QUOTE_STYLE\fR\.
50
+ .IP "\(bu" 4
51
+ \fB\-\-line\-reference\-format\fR Controls how file/line references e\.g\. in stack traces are printed:
52
+ .IP "\(bu" 4
53
+ comma_line (default): a\.bats, line 1
54
+ .IP "\(bu" 4
55
+ colon: a\.bats:1
56
+ .IP "\(bu" 4
57
+ uri: file:///tests/a\.bats:1
58
+ .IP "\(bu" 4
59
+ custom: provide your own via defining bats_format_file_line_reference_custom with parameters \fIfilename\fR \fIline\fR, store via \fBprintf \-v "$output"\fR
60
+ .IP "" 0
61
+
62
+ .IP "\(bu" 4
63
+ \fB\-\-errexit\fR: Enable errexit (\fBset \-e\fR) for commands run in \fBrun\fR
64
+ .IP "\(bu" 4
65
+ \fB\-f\fR, \fB\-\-filter <regex>\fR: Filter test cases by names matching the regular expression
66
+ .IP "\(bu" 4
67
+ \fB\-F\fR, \fB\-\-formatter <type>\fR: Switch between formatters: pretty (default), tap (default w/o term), tap13, junit, \fB/<absolute path to formatter>\fR
68
+ .IP "\(bu" 4
69
+ \fB\-\-filter\-status <status>\fR: Only run tests with the given status in the last completed (no CTRL+C/SIGINT) run\. Valid \fIstatus\fR values are: failed \- runs tests that failed or were not present in the last run missed \- runs tests that were not present in the last run
70
+ .IP "\(bu" 4
71
+ \fB\-\-filter\-tags <comma\-separated\-tag\-list>\fR: Only run tests that match all the tags in the list (\fB&&\fR)\. You can negate a tag via prepending \fB!\fR\. Specifying this flag multiple times allows for logical or (\fB||\fR): \fB\-\-filter\-tags A,B \-\-filter\-tags A,!C\fR matches tags \fB(A && B) || (A && !C)\fR
72
+ .IP "\(bu" 4
73
+ \fB\-\-gather\-test\-outputs\-in <directory>\fR: Gather the output of failing \fIand\fR passing tests as files in directory
74
+ .IP "\(bu" 4
75
+ \fB\-h\fR, \fB\-\-help\fR: Display this help message
76
+ .IP "\(bu" 4
77
+ \fB\-j\fR, \fB\-\-jobs <jobs>\fR: Number of parallel jobs (requires GNU parallel)
78
+ .IP "\(bu" 4
79
+ \fB\-\-negative\-filter <regex>\fR: Only run tests that do not match the regular expression
80
+ .IP "\(bu" 4
81
+ \fB\-\-no\-tempdir\-cleanup\fR: Preserve test output temporary directory
82
+ .IP "\(bu" 4
83
+ \fB\-\-no\-parallelize\-across\-files\fR: Serialize test file execution instead of running them in parallel (requires \-\-jobs >1)
84
+ .IP "\(bu" 4
85
+ \fB\-\-no\-parallelize\-within\-files\fR: Serialize test execution within files instead of running them in parallel (requires \-\-jobs >1)
86
+ .IP "\(bu" 4
87
+ \fB\-\-parallel\-binary\-name <name>\fR: Name of parallel binary
88
+ .IP "\(bu" 4
89
+ \fB\-\-report\-formatter <type>\fR: Switch between reporters (same options as \-\-formatter)
90
+ .IP "\(bu" 4
91
+ \fB\-o\fR, \fB\-\-output <dir>\fR: Directory to write report files
92
+ .IP "\(bu" 4
93
+ \fB\-p\fR, \fB\-\-pretty\fR: Shorthand for "\-\-formatter pretty"
94
+ .IP "\(bu" 4
95
+ \fB\-\-print\-output\-on\-failure\fR: Automatically print the value of \fB$output\fR on failed tests
96
+ .IP "\(bu" 4
97
+ \fB\-r\fR, \fB\-\-recursive\fR: Include tests in subdirectories
98
+ .IP "\(bu" 4
99
+ \fB\-\-show\-output\-of\-passing\-tests\fR: Print output of passing tests
100
+ .IP "\(bu" 4
101
+ \fB\-t\fR, \fB\-\-tap\fR: Shorthand for "\-\-formatter tap"
102
+ .IP "\(bu" 4
103
+ \fB\-T\fR, \fB\-\-timing\fR: Add timing information to tests
104
+ .IP "\(bu" 4
105
+ \fB\-x\fR, \fB\-\-trace\fR: Print test commands as they are executed (like \fBset \-x\fR)
106
+ .IP "\(bu" 4
107
+ \fB\-\-verbose\-run\fR: Make \fBrun\fR print \fB$output\fR by default
108
+ .IP "\(bu" 4
109
+ \fB\-v\fR, \fB\-\-version\fR: Display the version number
110
+ .IP "" 0
111
+ .SH "OUTPUT"
112
+ When you run Bats from a terminal, you'll see output as each test is performed, with a check\-mark next to the test's name if it passes or an "X" if it fails\.
113
+ .IP "" 4
114
+ .nf
115
+ $ bats addition\.bats
116
+ ✓ addition using bc
117
+ ✓ addition using dc
118
+
119
+ 2 tests, 0 failures
120
+ .fi
121
+ .IP "" 0
122
+ .P
123
+ If Bats is not connected to a terminal\-\-in other words, if you run it from a continuous integration system or redirect its output to a file\-\-the results are displayed in human\-readable, machine\-parsable TAP format\. You can force TAP output from a terminal by invoking Bats with the \fB\-\-tap\fR option\.
124
+ .IP "" 4
125
+ .nf
126
+ $ bats \-\-tap addition\.bats
127
+ 1\.\.2
128
+ ok 1 addition using bc
129
+ ok 2 addition using dc
130
+ .fi
131
+ .IP "" 0
132
+ .SH "EXIT STATUS"
133
+ The \fBbats\fR interpreter exits with a value of \fB0\fR if all test cases pass, or \fB1\fR if one or more test cases fail\.
134
+ .SH "SEE ALSO"
135
+ Bats wiki: \fIhttps://github\.com/bats\-core/bats\-core/wiki/\fR
136
+ .P
137
+ \fBbash\fR(1), \fBbats\fR(7)
138
+ .SH "COPYRIGHT"
139
+ (c) 2017\-2022 bats\-core organization
140
+ .br
141
+ (c) 2011\-2016 Sam Stephenson
142
+ .P
143
+ Bats is released under the terms of an MIT\-style license\.