@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,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
+ }