1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
|
#!/usr/bin/env bash
# reads (extended) bats tap streams from stdin and calls callback functions for each line
#
# Segmenting functions
# ====================
# bats_tap_stream_plan <number of tests> -> when the test plan is encountered
# bats_tap_stream_suite <file name> -> when a new file is begun WARNING: extended only
# bats_tap_stream_begin <test index> <test name> -> when a new test is begun WARNING: extended only
#
# Test result functions
# =====================
# If timing was enabled, BATS_FORMATTER_TEST_DURATION will be set to their duration in milliseconds
# bats_tap_stream_ok <test index> <test name> -> when a test was successful
# bats_tap_stream_not_ok <test index> <test name> -> when a test has failed. If the failure was due to a timeout,
# BATS_FORMATTER_TEST_TIMEOUT is set to the timeout duration in seconds
# bats_tap_stream_skipped <test index> <test name> <skip reason> -> when a test was skipped
#
# Context functions
# =================
# bats_tap_stream_comment <comment text without leading '# '> <scope> -> when a comment line was encountered,
# scope tells the last encountered of plan, begin, ok, not_ok, skipped, suite
# bats_tap_stream_unknown <full line> <scope> -> when a line is encountered that does not match the previous entries,
# scope @see bats_tap_stream_comment
# forwards all input as is, when there is no TAP test plan header
function bats_parse_internal_extended_tap() {
local header_pattern='[0-9]+\.\.[0-9]+'
IFS= read -r header
if [[ "$header" =~ $header_pattern ]]; then
bats_tap_stream_plan "${header:3}"
else
# If the first line isn't a TAP plan, print it and pass the rest through
printf '%s\n' "$header"
exec cat
fi
ok_line_regexpr="ok ([0-9]+) (.*)"
skip_line_regexpr="ok ([0-9]+) (.*) # skip( (.*))?$"
timeout_line_regexpr="not ok ([0-9]+) (.*) # timeout after ([0-9]+)s$"
not_ok_line_regexpr="not ok ([0-9]+) (.*)"
timing_expr="in ([0-9]+)ms$"
local test_name begin_index last_begin_index try_index ok_index not_ok_index index scope
begin_index=0
last_begin_index=-1
try_index=0
index=0
scope=plan
while IFS= read -r line; do
unset BATS_FORMATTER_TEST_DURATION BATS_FORMATTER_TEST_TIMEOUT
case "$line" in
'begin '*) # this might only be called in extended tap output
scope=begin
begin_index=${line#begin }
begin_index=${begin_index%% *}
if [[ $begin_index == "$last_begin_index" ]]; then
(( ++try_index ))
else
try_index=0
fi
test_name="${line#begin "$begin_index" }"
bats_tap_stream_begin "$begin_index" "$test_name"
;;
'ok '*)
((++index))
if [[ "$line" =~ $ok_line_regexpr ]]; then
ok_index="${BASH_REMATCH[1]}"
test_name="${BASH_REMATCH[2]}"
if [[ "$line" =~ $skip_line_regexpr ]]; then
scope=skipped
test_name="${BASH_REMATCH[2]}" # cut off name before "# skip"
local skip_reason="${BASH_REMATCH[4]}"
if [[ "$test_name" =~ $timing_expr ]]; then
local BATS_FORMATTER_TEST_DURATION="${BASH_REMATCH[1]}"
test_name="${test_name% in "${BATS_FORMATTER_TEST_DURATION}"ms}"
bats_tap_stream_skipped "$ok_index" "$test_name" "$skip_reason"
else
bats_tap_stream_skipped "$ok_index" "$test_name" "$skip_reason"
fi
else
scope=ok
if [[ "$line" =~ $timing_expr ]]; then
local BATS_FORMATTER_TEST_DURATION="${BASH_REMATCH[1]}"
bats_tap_stream_ok "$ok_index" "${test_name% in "${BASH_REMATCH[1]}"ms}"
else
bats_tap_stream_ok "$ok_index" "$test_name"
fi
fi
else
printf "ERROR: could not match ok line: %s" "$line" >&2
exit 1
fi
;;
'not ok '*)
((++index))
scope=not_ok
if [[ "$line" =~ $not_ok_line_regexpr ]]; then
not_ok_index="${BASH_REMATCH[1]}"
test_name="${BASH_REMATCH[2]}"
if [[ "$line" =~ $timeout_line_regexpr ]]; then
not_ok_index="${BASH_REMATCH[1]}"
test_name="${BASH_REMATCH[2]}"
# shellcheck disable=SC2034 # used in bats_tap_stream_ok
local BATS_FORMATTER_TEST_TIMEOUT="${BASH_REMATCH[3]}"
fi
if [[ "$test_name" =~ $timing_expr ]]; then
# shellcheck disable=SC2034 # used in bats_tap_stream_ok
local BATS_FORMATTER_TEST_DURATION="${BASH_REMATCH[1]}"
test_name="${test_name% in "${BASH_REMATCH[1]}"ms}"
fi
bats_tap_stream_not_ok "$not_ok_index" "$test_name"
else
printf "ERROR: could not match not ok line: %s" "$line" >&2
exit 1
fi
;;
'# '*)
bats_tap_stream_comment "${line:2}" "$scope"
;;
'#')
bats_tap_stream_comment "" "$scope"
;;
'suite '*)
scope=suite
# pass on the
bats_tap_stream_suite "${line:6}"
;;
*)
bats_tap_stream_unknown "$line" "$scope"
;;
esac
done
}
normalize_base_path() { # <target variable> <base path>
# the relative path root to use for reporting filenames
# this is mainly intended for suite mode, where this will be the suite root folder
local base_path="$2"
# use the containing directory when --base-path is a file
if [[ ! -d "$base_path" ]]; then
base_path="$(dirname "$base_path")"
fi
# get the absolute path
base_path="$(cd "$base_path" && pwd)"
# ensure the path ends with / to strip that later on
if [[ "${base_path}" != *"/" ]]; then
base_path="$base_path/"
fi
printf -v "$1" "%s" "$base_path"
}
|