busser-bats 0.6.0 → 0.7.0
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.
- checksums.yaml +4 -4
- data/.github/workflows/publish.yml +22 -0
- data/.gitignore +0 -1
- data/.release-please-manifest.json +1 -1
- data/.rubocop.yml +1 -4
- data/CHANGELOG.md +11 -0
- data/CONTRIBUTING.md +86 -0
- data/Gemfile +7 -5
- data/README.md +100 -28
- data/Rakefile +42 -15
- data/config/features/support/env.rb +0 -5
- data/features/support/env.rb +0 -12
- data/features/test_command.feature +31 -31
- data/lib/busser/bats/version.rb +3 -1
- data/lib/busser/runner_plugin/bats.rb +27 -3
- data/spec/busser/runner_plugin/bats_spec.rb +41 -0
- data/spec/spec_helper.rb +2 -0
- data/vendor/bats/LICENSE.md +53 -0
- data/vendor/bats/README.md +84 -242
- data/vendor/bats/VERSION.txt +1 -1
- data/vendor/bats/bin/bats +75 -0
- data/vendor/bats/install.sh +20 -28
- data/vendor/bats/lib/bats-core/common.bash +278 -0
- data/vendor/bats/lib/bats-core/formatter.bash +151 -0
- data/vendor/bats/lib/bats-core/preprocessing.bash +19 -0
- data/vendor/bats/lib/bats-core/semaphore.bash +113 -0
- data/vendor/bats/lib/bats-core/test_functions.bash +539 -0
- data/vendor/bats/lib/bats-core/tracing.bash +425 -0
- data/vendor/bats/lib/bats-core/validator.bash +37 -0
- data/vendor/bats/lib/bats-core/warnings.bash +44 -0
- data/vendor/bats/libexec/bats-core/bats +549 -0
- data/vendor/bats/libexec/bats-core/bats-exec-file +402 -0
- data/vendor/bats/libexec/bats-core/bats-exec-suite +342 -0
- data/vendor/bats/libexec/bats-core/bats-exec-test +383 -0
- data/vendor/bats/libexec/bats-core/bats-format-cat +6 -0
- data/vendor/bats/libexec/bats-core/bats-format-junit +293 -0
- data/vendor/bats/libexec/bats-core/bats-format-pretty +360 -0
- data/vendor/bats/libexec/bats-core/bats-format-tap +55 -0
- data/vendor/bats/libexec/bats-core/bats-format-tap13 +89 -0
- data/vendor/bats/libexec/bats-core/bats-gather-tests +402 -0
- data/vendor/bats/libexec/bats-core/bats-preprocess +118 -0
- data/vendor/bats/man/Makefile +16 -3
- data/vendor/bats/man/bats.1 +100 -58
- data/vendor/bats/man/bats.1.ronn +102 -12
- data/vendor/bats/man/bats.7 +205 -83
- data/vendor/bats/man/bats.7.ronn +262 -14
- data/vendor/bats/uninstall.sh +63 -0
- metadata +25 -6
- data/.tool-versions +0 -1
- data/vendor/bats/.gitattributes +0 -3
- data/vendor/bats/.travis.yml +0 -5
- data/vendor/bats/LICENSE +0 -20
- data/vendor/bats/bin/bats +0 -1
- data/vendor/bats/package.json +0 -9
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
bats_prefix_lines_for_tap_output() {
|
|
4
|
+
while IFS= read -r line; do
|
|
5
|
+
printf '# %s\n' "$line" || break # avoid feedback loop when errors are redirected into BATS_OUT (see #353)
|
|
6
|
+
done
|
|
7
|
+
if [[ -n "$line" ]]; then
|
|
8
|
+
printf '# %s\n' "$line"
|
|
9
|
+
fi
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function bats_replace_filename() {
|
|
13
|
+
local line
|
|
14
|
+
while read -r line; do
|
|
15
|
+
printf "%s\n" "${line//$BATS_TEST_SOURCE/$BATS_TEST_FILENAME}"
|
|
16
|
+
done
|
|
17
|
+
if [[ -n "$line" ]]; then
|
|
18
|
+
printf "%s\n" "${line//$BATS_TEST_SOURCE/$BATS_TEST_FILENAME}"
|
|
19
|
+
fi
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
bats_quote_code() { # <var> <code>
|
|
23
|
+
printf -v "$1" -- "%s%s%s" "$BATS_BEGIN_CODE_QUOTE" "$2" "$BATS_END_CODE_QUOTE"
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
bats_check_valid_version() {
|
|
27
|
+
if [[ ! $1 =~ [0-9]+.[0-9]+.[0-9]+ ]]; then
|
|
28
|
+
printf "ERROR: version '%s' must be of format <major>.<minor>.<patch>!\n" "$1" >&2
|
|
29
|
+
exit 1
|
|
30
|
+
fi
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
# compares two versions. Return 0 when version1 < version2
|
|
34
|
+
bats_version_lt() { # <version1> <version2>
|
|
35
|
+
bats_check_valid_version "$1"
|
|
36
|
+
bats_check_valid_version "$2"
|
|
37
|
+
|
|
38
|
+
local -a version1_parts version2_parts
|
|
39
|
+
IFS=. read -ra version1_parts <<<"$1"
|
|
40
|
+
IFS=. read -ra version2_parts <<<"$2"
|
|
41
|
+
|
|
42
|
+
local -i i
|
|
43
|
+
for i in {0..2}; do
|
|
44
|
+
if ((version1_parts[i] < version2_parts[i])); then
|
|
45
|
+
return 0
|
|
46
|
+
elif ((version1_parts[i] > version2_parts[i])); then
|
|
47
|
+
return 1
|
|
48
|
+
fi
|
|
49
|
+
done
|
|
50
|
+
# if we made it this far, they are equal -> also not less then
|
|
51
|
+
return 2 # use other failing return code to distinguish equal from gt
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
# ensure a minimum version of bats is running or exit with failure
|
|
55
|
+
bats_require_minimum_version() { # <required version>
|
|
56
|
+
local required_minimum_version=$1
|
|
57
|
+
|
|
58
|
+
if bats_version_lt "$BATS_VERSION" "$required_minimum_version"; then
|
|
59
|
+
printf "BATS_VERSION=%s does not meet required minimum %s\n" "$BATS_VERSION" "$required_minimum_version"
|
|
60
|
+
exit 1
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
if bats_version_lt "$BATS_GUARANTEED_MINIMUM_VERSION" "$required_minimum_version"; then
|
|
64
|
+
BATS_GUARANTEED_MINIMUM_VERSION="$required_minimum_version"
|
|
65
|
+
fi
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
# returns 0 when search-value is found in array
|
|
69
|
+
bats_linear_reverse_search() { # <search-value> <array-name>
|
|
70
|
+
local -r search_value=$1 array_name=$2
|
|
71
|
+
eval "local -ri array_length=\${#${array_name}[@]}"
|
|
72
|
+
# shellcheck disable=SC2154
|
|
73
|
+
for ((i=array_length - 1; i >=0; --i)); do
|
|
74
|
+
eval "local value=\"\${${array_name}[i]}\""
|
|
75
|
+
# shellcheck disable=SC2154
|
|
76
|
+
if [[ $value == "$search_value" ]]; then
|
|
77
|
+
return 0
|
|
78
|
+
fi
|
|
79
|
+
done
|
|
80
|
+
return 1
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
# returns 0 when search-value is found in array
|
|
84
|
+
bats_binary_search() { # <search-value> <array-name>
|
|
85
|
+
if [[ $# -ne 2 ]]; then
|
|
86
|
+
printf "ERROR: bats_binary_search requires exactly 2 arguments: <search value> <array name>\n" >&2
|
|
87
|
+
return 2
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
local -r search_value=$1 array_name=$2
|
|
91
|
+
|
|
92
|
+
# we'd like to test if array is set but we cannot distinguish unset from empty arrays, so we need to skip that
|
|
93
|
+
|
|
94
|
+
local start=0 mid end mid_value
|
|
95
|
+
# start is inclusive, end is exclusive ...
|
|
96
|
+
eval "end=\${#${array_name}[@]}"
|
|
97
|
+
|
|
98
|
+
# so start == end means empty search space
|
|
99
|
+
while ((start < end)); do
|
|
100
|
+
mid=$(((start + end) / 2))
|
|
101
|
+
eval "mid_value=\${${array_name}[$mid]}"
|
|
102
|
+
if [[ "$mid_value" == "$search_value" ]]; then
|
|
103
|
+
return 0
|
|
104
|
+
elif [[ "$mid_value" < "$search_value" ]]; then
|
|
105
|
+
# This branch excludes equality -> +1 to skip the mid element.
|
|
106
|
+
# This +1 also avoids endless recursion on odd sized search ranges.
|
|
107
|
+
start=$((mid + 1))
|
|
108
|
+
else
|
|
109
|
+
end=$mid
|
|
110
|
+
fi
|
|
111
|
+
done
|
|
112
|
+
|
|
113
|
+
# did not find it -> its not there
|
|
114
|
+
return 1
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
# store the values in ascending (string!) order in result array
|
|
118
|
+
# Intended for short lists! (uses insertion sort)
|
|
119
|
+
bats_sort() { # <result-array-name> <values to sort...>
|
|
120
|
+
local -r result_name=$1
|
|
121
|
+
shift
|
|
122
|
+
|
|
123
|
+
if (($# == 1)) && [[ $1 == "" ]]; then
|
|
124
|
+
shift # remove leading "" to deal with bash 3 expanding "${empty_array[@]}" to one ""
|
|
125
|
+
fi
|
|
126
|
+
|
|
127
|
+
if (($# == 0)); then
|
|
128
|
+
eval "$result_name=()"
|
|
129
|
+
return 0
|
|
130
|
+
fi
|
|
131
|
+
|
|
132
|
+
local -a sorted_array=()
|
|
133
|
+
local -i i
|
|
134
|
+
while (( $# > 0 )); do # loop over input values
|
|
135
|
+
local current_value="$1"
|
|
136
|
+
shift
|
|
137
|
+
for ((i = ${#sorted_array[@]}; i >= 0; --i)); do # loop over output array from end
|
|
138
|
+
if (( i == 0 )) || [[ ${sorted_array[i - 1]} < $current_value ]]; then
|
|
139
|
+
# insert new element at (freed) desired location
|
|
140
|
+
sorted_array[i]=$current_value
|
|
141
|
+
break
|
|
142
|
+
else
|
|
143
|
+
# shift bigger elements one position to the end
|
|
144
|
+
sorted_array[i]=${sorted_array[i - 1]}
|
|
145
|
+
fi
|
|
146
|
+
done
|
|
147
|
+
done
|
|
148
|
+
|
|
149
|
+
eval "$result_name=(\"\${sorted_array[@]}\")"
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
# check if all search values (must be sorted!) are in the (sorted!) array
|
|
153
|
+
# Intended for short lists/arrays!
|
|
154
|
+
bats_all_in() { # <sorted-array> <sorted search values...>
|
|
155
|
+
local -r haystack_array=$1
|
|
156
|
+
shift
|
|
157
|
+
|
|
158
|
+
local -i haystack_length # just to appease shellcheck
|
|
159
|
+
eval "local -r haystack_length=\${#${haystack_array}[@]}"
|
|
160
|
+
|
|
161
|
+
local -i haystack_index=0 # initialize only here to continue from last search position
|
|
162
|
+
local search_value haystack_value # just to appease shellcheck
|
|
163
|
+
local -i i
|
|
164
|
+
for ((i = 1; i <= $#; ++i)); do
|
|
165
|
+
eval "local search_value=${!i}"
|
|
166
|
+
for (( ; haystack_index < haystack_length; ++haystack_index)); do
|
|
167
|
+
eval "local haystack_value=\${${haystack_array}[$haystack_index]}"
|
|
168
|
+
if [[ $haystack_value > "$search_value" ]]; then
|
|
169
|
+
# we passed the location this value would have been at -> not found
|
|
170
|
+
return 1
|
|
171
|
+
elif [[ $haystack_value == "$search_value" ]]; then
|
|
172
|
+
continue 2 # search value found -> try the next one
|
|
173
|
+
fi
|
|
174
|
+
done
|
|
175
|
+
return 1 # we ran of the end of the haystack without finding the value!
|
|
176
|
+
done
|
|
177
|
+
|
|
178
|
+
# did not return from loop above -> all search values were found
|
|
179
|
+
return 0
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
# check if any search value (must be sorted!) is in the (sorted!) array
|
|
183
|
+
# intended for short lists/arrays
|
|
184
|
+
bats_any_in() { # <sorted-array> <sorted search values>
|
|
185
|
+
local -r haystack_array=$1
|
|
186
|
+
shift
|
|
187
|
+
|
|
188
|
+
local -i haystack_length # just to appease shellcheck
|
|
189
|
+
eval "local -r haystack_length=\${#${haystack_array}[@]}"
|
|
190
|
+
|
|
191
|
+
local -i haystack_index=0 # initialize only here to continue from last search position
|
|
192
|
+
local search_value haystack_value # just to appease shellcheck
|
|
193
|
+
local -i i
|
|
194
|
+
for ((i = 1; i <= $#; ++i)); do
|
|
195
|
+
eval "local search_value=${!i}"
|
|
196
|
+
for (( ; haystack_index < haystack_length; ++haystack_index)); do
|
|
197
|
+
eval "local haystack_value=\${${haystack_array}[$haystack_index]}"
|
|
198
|
+
if [[ $haystack_value > "$search_value" ]]; then
|
|
199
|
+
continue 2 # search value not in array! -> try next
|
|
200
|
+
elif [[ $haystack_value == "$search_value" ]]; then
|
|
201
|
+
return 0 # search value found
|
|
202
|
+
fi
|
|
203
|
+
done
|
|
204
|
+
done
|
|
205
|
+
|
|
206
|
+
# did not return from loop above -> no search value was found
|
|
207
|
+
return 1
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
bats_trim() { # <output-variable> <string>
|
|
211
|
+
local -r bats_trim_ltrimmed=${2#"${2%%[![:space:]]*}"} # cut off leading whitespace
|
|
212
|
+
# shellcheck disable=SC2034 # used in eval!
|
|
213
|
+
local -r bats_trim_trimmed=${bats_trim_ltrimmed%"${bats_trim_ltrimmed##*[![:space:]]}"} # cut off trailing whitespace
|
|
214
|
+
eval "$1=\$bats_trim_trimmed"
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
# a helper function to work around unbound variable errors with ${arr[@]} on Bash 3
|
|
218
|
+
bats_append_arrays_as_args() { # <array...> -- <command ...>
|
|
219
|
+
local -a trailing_args=()
|
|
220
|
+
while (($# > 0)) && [[ $1 != -- ]]; do
|
|
221
|
+
local array=$1
|
|
222
|
+
shift
|
|
223
|
+
|
|
224
|
+
if eval "(( \${#${array}[@]} > 0 ))"; then
|
|
225
|
+
eval "trailing_args+=(\"\${${array}[@]}\")"
|
|
226
|
+
fi
|
|
227
|
+
done
|
|
228
|
+
shift # remove -- separator
|
|
229
|
+
|
|
230
|
+
if (($# == 0)); then
|
|
231
|
+
printf "Error: append_arrays_as_args is missing a command or -- separator\n" >&2
|
|
232
|
+
return 1
|
|
233
|
+
fi
|
|
234
|
+
|
|
235
|
+
if ((${#trailing_args[@]} > 0)); then
|
|
236
|
+
"$@" "${trailing_args[@]}"
|
|
237
|
+
else
|
|
238
|
+
"$@"
|
|
239
|
+
fi
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
bats_format_file_line_reference() { # <output> <file> <line>
|
|
243
|
+
# shellcheck disable=SC2034 # will be used in subimplementation
|
|
244
|
+
local output="${1?}"
|
|
245
|
+
shift
|
|
246
|
+
"bats_format_file_line_reference_${BATS_LINE_REFERENCE_FORMAT?}" "$@"
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
bats_format_file_line_reference_comma_line() {
|
|
250
|
+
printf -v "$output" "%s, line %d" "$@"
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
bats_format_file_line_reference_colon() {
|
|
254
|
+
printf -v "$output" "%s:%d" "$@"
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
# approximate realpath without subshell
|
|
258
|
+
bats_approx_realpath() { # <output-variable> <path>
|
|
259
|
+
local output=$1 path=$2
|
|
260
|
+
if [[ $path != /* ]]; then
|
|
261
|
+
path="$PWD/$path"
|
|
262
|
+
fi
|
|
263
|
+
# x/./y -> x/y
|
|
264
|
+
path=${path//\/.\//\/}
|
|
265
|
+
printf -v "$output" "%s" "$path"
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
bats_format_file_line_reference_uri() {
|
|
269
|
+
local filename=${1?} line=${2?}
|
|
270
|
+
bats_approx_realpath filename "$filename"
|
|
271
|
+
printf -v "$output" "file://%s:%d" "$filename" "$line"
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
# execute command with backed up path
|
|
275
|
+
# to prevent path mocks from interfering with our internals
|
|
276
|
+
bats_execute() { # <command...>
|
|
277
|
+
PATH="${BATS_SAVED_PATH?}" "$@"
|
|
278
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
# reads (extended) bats tap streams from stdin and calls callback functions for each line
|
|
4
|
+
#
|
|
5
|
+
# Segmenting functions
|
|
6
|
+
# ====================
|
|
7
|
+
# bats_tap_stream_plan <number of tests> -> when the test plan is encountered
|
|
8
|
+
# bats_tap_stream_suite <file name> -> when a new file is begun WARNING: extended only
|
|
9
|
+
# bats_tap_stream_begin <test index> <test name> -> when a new test is begun WARNING: extended only
|
|
10
|
+
#
|
|
11
|
+
# Test result functions
|
|
12
|
+
# =====================
|
|
13
|
+
# If timing was enabled, BATS_FORMATTER_TEST_DURATION will be set to their duration in milliseconds
|
|
14
|
+
# bats_tap_stream_ok <test index> <test name> -> when a test was successful
|
|
15
|
+
# bats_tap_stream_not_ok <test index> <test name> -> when a test has failed. If the failure was due to a timeout,
|
|
16
|
+
# BATS_FORMATTER_TEST_TIMEOUT is set to the timeout duration in seconds
|
|
17
|
+
# bats_tap_stream_skipped <test index> <test name> <skip reason> -> when a test was skipped
|
|
18
|
+
#
|
|
19
|
+
# Context functions
|
|
20
|
+
# =================
|
|
21
|
+
# bats_tap_stream_comment <comment text without leading '# '> <scope> -> when a comment line was encountered,
|
|
22
|
+
# scope tells the last encountered of plan, begin, ok, not_ok, skipped, suite
|
|
23
|
+
# bats_tap_stream_unknown <full line> <scope> -> when a line is encountered that does not match the previous entries,
|
|
24
|
+
# scope @see bats_tap_stream_comment
|
|
25
|
+
# forwards all input as is, when there is no TAP test plan header
|
|
26
|
+
function bats_parse_internal_extended_tap() {
|
|
27
|
+
local header_pattern='[0-9]+\.\.[0-9]+'
|
|
28
|
+
IFS= read -r header
|
|
29
|
+
|
|
30
|
+
if [[ "$header" =~ $header_pattern ]]; then
|
|
31
|
+
bats_tap_stream_plan "${header:3}"
|
|
32
|
+
else
|
|
33
|
+
# If the first line isn't a TAP plan, print it and pass the rest through
|
|
34
|
+
printf '%s\n' "$header"
|
|
35
|
+
exec cat
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
ok_line_regexpr="ok ([0-9]+) (.*)"
|
|
39
|
+
skip_line_regexpr="ok ([0-9]+) (.*) # skip( (.*))?$"
|
|
40
|
+
timeout_line_regexpr="not ok ([0-9]+) (.*) # timeout after ([0-9]+)s$"
|
|
41
|
+
not_ok_line_regexpr="not ok ([0-9]+) (.*)"
|
|
42
|
+
|
|
43
|
+
timing_expr="in ([0-9]+)ms$"
|
|
44
|
+
local test_name begin_index last_begin_index try_index ok_index not_ok_index index scope
|
|
45
|
+
begin_index=0
|
|
46
|
+
last_begin_index=-1
|
|
47
|
+
try_index=0
|
|
48
|
+
index=0
|
|
49
|
+
scope=plan
|
|
50
|
+
while IFS= read -r line; do
|
|
51
|
+
unset BATS_FORMATTER_TEST_DURATION BATS_FORMATTER_TEST_TIMEOUT
|
|
52
|
+
case "$line" in
|
|
53
|
+
'begin '*) # this might only be called in extended tap output
|
|
54
|
+
scope=begin
|
|
55
|
+
begin_index=${line#begin }
|
|
56
|
+
begin_index=${begin_index%% *}
|
|
57
|
+
if [[ $begin_index == "$last_begin_index" ]]; then
|
|
58
|
+
(( ++try_index ))
|
|
59
|
+
else
|
|
60
|
+
try_index=0
|
|
61
|
+
fi
|
|
62
|
+
test_name="${line#begin "$begin_index" }"
|
|
63
|
+
bats_tap_stream_begin "$begin_index" "$test_name"
|
|
64
|
+
;;
|
|
65
|
+
'ok '*)
|
|
66
|
+
((++index))
|
|
67
|
+
if [[ "$line" =~ $ok_line_regexpr ]]; then
|
|
68
|
+
ok_index="${BASH_REMATCH[1]}"
|
|
69
|
+
test_name="${BASH_REMATCH[2]}"
|
|
70
|
+
if [[ "$line" =~ $skip_line_regexpr ]]; then
|
|
71
|
+
scope=skipped
|
|
72
|
+
test_name="${BASH_REMATCH[2]}" # cut off name before "# skip"
|
|
73
|
+
local skip_reason="${BASH_REMATCH[4]}"
|
|
74
|
+
if [[ "$test_name" =~ $timing_expr ]]; then
|
|
75
|
+
local BATS_FORMATTER_TEST_DURATION="${BASH_REMATCH[1]}"
|
|
76
|
+
test_name="${test_name% in "${BATS_FORMATTER_TEST_DURATION}"ms}"
|
|
77
|
+
bats_tap_stream_skipped "$ok_index" "$test_name" "$skip_reason"
|
|
78
|
+
else
|
|
79
|
+
bats_tap_stream_skipped "$ok_index" "$test_name" "$skip_reason"
|
|
80
|
+
fi
|
|
81
|
+
else
|
|
82
|
+
scope=ok
|
|
83
|
+
if [[ "$line" =~ $timing_expr ]]; then
|
|
84
|
+
local BATS_FORMATTER_TEST_DURATION="${BASH_REMATCH[1]}"
|
|
85
|
+
bats_tap_stream_ok "$ok_index" "${test_name% in "${BASH_REMATCH[1]}"ms}"
|
|
86
|
+
else
|
|
87
|
+
bats_tap_stream_ok "$ok_index" "$test_name"
|
|
88
|
+
fi
|
|
89
|
+
fi
|
|
90
|
+
else
|
|
91
|
+
printf "ERROR: could not match ok line: %s" "$line" >&2
|
|
92
|
+
exit 1
|
|
93
|
+
fi
|
|
94
|
+
;;
|
|
95
|
+
'not ok '*)
|
|
96
|
+
((++index))
|
|
97
|
+
scope=not_ok
|
|
98
|
+
if [[ "$line" =~ $not_ok_line_regexpr ]]; then
|
|
99
|
+
not_ok_index="${BASH_REMATCH[1]}"
|
|
100
|
+
test_name="${BASH_REMATCH[2]}"
|
|
101
|
+
if [[ "$line" =~ $timeout_line_regexpr ]]; then
|
|
102
|
+
not_ok_index="${BASH_REMATCH[1]}"
|
|
103
|
+
test_name="${BASH_REMATCH[2]}"
|
|
104
|
+
# shellcheck disable=SC2034 # used in bats_tap_stream_ok
|
|
105
|
+
local BATS_FORMATTER_TEST_TIMEOUT="${BASH_REMATCH[3]}"
|
|
106
|
+
fi
|
|
107
|
+
if [[ "$test_name" =~ $timing_expr ]]; then
|
|
108
|
+
# shellcheck disable=SC2034 # used in bats_tap_stream_ok
|
|
109
|
+
local BATS_FORMATTER_TEST_DURATION="${BASH_REMATCH[1]}"
|
|
110
|
+
test_name="${test_name% in "${BASH_REMATCH[1]}"ms}"
|
|
111
|
+
fi
|
|
112
|
+
bats_tap_stream_not_ok "$not_ok_index" "$test_name"
|
|
113
|
+
else
|
|
114
|
+
printf "ERROR: could not match not ok line: %s" "$line" >&2
|
|
115
|
+
exit 1
|
|
116
|
+
fi
|
|
117
|
+
;;
|
|
118
|
+
'# '*)
|
|
119
|
+
bats_tap_stream_comment "${line:2}" "$scope"
|
|
120
|
+
;;
|
|
121
|
+
'#')
|
|
122
|
+
bats_tap_stream_comment "" "$scope"
|
|
123
|
+
;;
|
|
124
|
+
'suite '*)
|
|
125
|
+
scope=suite
|
|
126
|
+
# pass on the
|
|
127
|
+
bats_tap_stream_suite "${line:6}"
|
|
128
|
+
;;
|
|
129
|
+
*)
|
|
130
|
+
bats_tap_stream_unknown "$line" "$scope"
|
|
131
|
+
;;
|
|
132
|
+
esac
|
|
133
|
+
done
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
normalize_base_path() { # <target variable> <base path>
|
|
137
|
+
# the relative path root to use for reporting filenames
|
|
138
|
+
# this is mainly intended for suite mode, where this will be the suite root folder
|
|
139
|
+
local base_path="$2"
|
|
140
|
+
# use the containing directory when --base-path is a file
|
|
141
|
+
if [[ ! -d "$base_path" ]]; then
|
|
142
|
+
base_path="$(dirname "$base_path")"
|
|
143
|
+
fi
|
|
144
|
+
# get the absolute path
|
|
145
|
+
base_path="$(cd "$base_path" && pwd)"
|
|
146
|
+
# ensure the path ends with / to strip that later on
|
|
147
|
+
if [[ "${base_path}" != *"/" ]]; then
|
|
148
|
+
base_path="$base_path/"
|
|
149
|
+
fi
|
|
150
|
+
printf -v "$1" "%s" "$base_path"
|
|
151
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
bats_export_preprocess_source_BATS_TEST_SOURCE() {
|
|
4
|
+
# export to make it visible to bats_evaluate_preprocessed_source
|
|
5
|
+
# since the latter runs in bats-exec-test's bash while this runs in bats-exec-file's
|
|
6
|
+
export BATS_TEST_SOURCE="$BATS_RUN_TMPDIR/${BATS_TEST_FILE_NUMBER?}-${BATS_TEST_FILENAME##*/}.src"
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
bats_preprocess_source() { # index
|
|
10
|
+
bats_export_preprocess_source_BATS_TEST_SOURCE
|
|
11
|
+
# shellcheck disable=SC2153
|
|
12
|
+
CHECK_BATS_COMMENT_COMMANDS=1 "$BATS_ROOT/libexec/bats-core/bats-preprocess" "$BATS_TEST_FILENAME" >"$BATS_TEST_SOURCE"
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
bats_evaluate_preprocessed_source() {
|
|
16
|
+
# Dynamically loaded user files provided outside of Bats.
|
|
17
|
+
# shellcheck disable=SC1090
|
|
18
|
+
source "${BATS_TEST_SOURCE?}"
|
|
19
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
bats_run_under_flock() {
|
|
4
|
+
flock "$BATS_SEMAPHORE_DIR" "$@"
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
bats_run_under_shlock() {
|
|
8
|
+
local lockfile="$BATS_SEMAPHORE_DIR/shlock.lock"
|
|
9
|
+
while ! shlock -p $$ -f "$lockfile"; do
|
|
10
|
+
sleep 1
|
|
11
|
+
done
|
|
12
|
+
# we got the lock now, execute the command
|
|
13
|
+
"$@"
|
|
14
|
+
local status=$?
|
|
15
|
+
# free the lock
|
|
16
|
+
rm -f "$lockfile"
|
|
17
|
+
return $status
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
# setup the semaphore environment for the loading file
|
|
21
|
+
bats_semaphore_setup() {
|
|
22
|
+
export -f bats_semaphore_get_free_slot_count
|
|
23
|
+
export -f bats_semaphore_acquire_while_locked
|
|
24
|
+
export BATS_SEMAPHORE_DIR="$BATS_RUN_TMPDIR/semaphores"
|
|
25
|
+
|
|
26
|
+
if command -v flock >/dev/null; then
|
|
27
|
+
BATS_LOCKING_IMPLEMENTATION=flock
|
|
28
|
+
elif command -v shlock >/dev/null; then
|
|
29
|
+
BATS_LOCKING_IMPLEMENTATION=shlock
|
|
30
|
+
else
|
|
31
|
+
printf "ERROR: flock/shlock is required for parallelization within files!\n" >&2
|
|
32
|
+
exit 1
|
|
33
|
+
fi
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
# $1 - output directory for stdout/stderr
|
|
37
|
+
# $@ - command to run
|
|
38
|
+
# run the given command in a semaphore
|
|
39
|
+
# block when there is no free slot for the semaphore
|
|
40
|
+
# when there is a free slot, run the command in background
|
|
41
|
+
# gather the output of the command in files in the given directory
|
|
42
|
+
bats_semaphore_run() {
|
|
43
|
+
local output_dir=$1
|
|
44
|
+
shift
|
|
45
|
+
local semaphore_slot
|
|
46
|
+
semaphore_slot=$(bats_semaphore_acquire_slot)
|
|
47
|
+
bats_semaphore_release_wrapper "$output_dir" "$semaphore_slot" "$@" &
|
|
48
|
+
printf "%d\n" "$!"
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
# $1 - output directory for stdout/stderr
|
|
52
|
+
# $@ - command to run
|
|
53
|
+
# this wraps the actual function call to install some traps on exiting
|
|
54
|
+
bats_semaphore_release_wrapper() {
|
|
55
|
+
local output_dir="$1"
|
|
56
|
+
local semaphore_name="$2"
|
|
57
|
+
shift 2 # all other parameters will be use for the command to execute
|
|
58
|
+
|
|
59
|
+
# shellcheck disable=SC2064 # we want to expand the semaphore_name right now!
|
|
60
|
+
trap "status=$?; bats_semaphore_release_slot '$semaphore_name'; exit $status" EXIT
|
|
61
|
+
|
|
62
|
+
mkdir -p "$output_dir"
|
|
63
|
+
"$@" 2>"$output_dir/stderr" >"$output_dir/stdout"
|
|
64
|
+
local status=$?
|
|
65
|
+
|
|
66
|
+
# bash bug: the exit trap is not called for the background process
|
|
67
|
+
bats_semaphore_release_slot "$semaphore_name"
|
|
68
|
+
trap - EXIT # avoid calling release twice
|
|
69
|
+
return $status
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
bats_semaphore_acquire_while_locked() {
|
|
73
|
+
if [[ $(bats_semaphore_get_free_slot_count) -gt 0 ]]; then
|
|
74
|
+
local slot=0
|
|
75
|
+
while [[ -e "$BATS_SEMAPHORE_DIR/slot-$slot" ]]; do
|
|
76
|
+
((++slot))
|
|
77
|
+
done
|
|
78
|
+
if [[ $slot -lt $BATS_SEMAPHORE_NUMBER_OF_SLOTS ]]; then
|
|
79
|
+
touch "$BATS_SEMAPHORE_DIR/slot-$slot" && printf "%d\n" "$slot" && return 0
|
|
80
|
+
fi
|
|
81
|
+
fi
|
|
82
|
+
return 1
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
# block until a semaphore slot becomes free
|
|
86
|
+
# prints the number of the slot that it received
|
|
87
|
+
bats_semaphore_acquire_slot() {
|
|
88
|
+
mkdir -p "$BATS_SEMAPHORE_DIR"
|
|
89
|
+
# wait for a slot to become free
|
|
90
|
+
# TODO: avoid busy waiting by using signals -> this opens op prioritizing possibilities as well
|
|
91
|
+
while true; do
|
|
92
|
+
# don't lock for reading, we are fine with spuriously getting no free slot
|
|
93
|
+
if [[ $(bats_semaphore_get_free_slot_count) -gt 0 ]]; then
|
|
94
|
+
bats_run_under_"$BATS_LOCKING_IMPLEMENTATION" \
|
|
95
|
+
bash -c bats_semaphore_acquire_while_locked \
|
|
96
|
+
&& break
|
|
97
|
+
fi
|
|
98
|
+
sleep 1
|
|
99
|
+
done
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
bats_semaphore_release_slot() {
|
|
103
|
+
# we don't need to lock this, since only our process owns this file
|
|
104
|
+
# and freeing a semaphore cannot lead to conflicts with others
|
|
105
|
+
rm "$BATS_SEMAPHORE_DIR/slot-$1" # this will fail if we had not acquired a semaphore!
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
bats_semaphore_get_free_slot_count() {
|
|
109
|
+
# find might error out without returning something useful when a file is deleted,
|
|
110
|
+
# while the directory is traversed -> only continue when there was no error
|
|
111
|
+
until used_slots=$(find "$BATS_SEMAPHORE_DIR" -name 'slot-*' 2>/dev/null | wc -l); do :; done
|
|
112
|
+
echo $((BATS_SEMAPHORE_NUMBER_OF_SLOTS - used_slots))
|
|
113
|
+
}
|