Skip to content
This repository has been archived by the owner on Apr 29, 2021. It is now read-only.

Fix macOS/Bash 3.2 breakage; eliminate subshells from exec-test, preprocess #210

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 60 additions & 47 deletions libexec/bats-exec-test
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ else
shift
fi

BATS_TEST_DIRNAME="$(dirname "$BATS_TEST_FILENAME")"
BATS_TEST_DIRNAME="${BATS_TEST_FILENAME%/*}"
BATS_TEST_NAMES=()

load() {
Expand All @@ -39,10 +39,10 @@ load() {
filename="$BATS_TEST_DIRNAME/${name}.bash"
fi

[ -f "$filename" ] || {
if [[ ! -f "$filename" ]]; then
echo "bats: $filename does not exist" >&2
exit 1
}
fi

source "${filename}"
}
Expand Down Expand Up @@ -101,39 +101,42 @@ bats_capture_stack_trace() {
local teardown_pattern=" teardown $BATS_TEST_SOURCE"

local frame
local index=1
local i

while frame="$(caller "$index")"; do
for ((i=2; i != ${#FUNCNAME[@]}; ++i)); do
frame="${BASH_LINENO[$((i-1))]} ${FUNCNAME[$i]} ${BASH_SOURCE[$i]}"
BATS_CURRENT_STACK_TRACE["${#BATS_CURRENT_STACK_TRACE[@]}"]="$frame"
if [[ "$frame" = *"$test_pattern" || \
"$frame" = *"$setup_pattern" || \
"$frame" = *"$teardown_pattern" ]]; then
break
else
let index+=1
fi
done

BATS_SOURCE="$(bats_frame_filename "${BATS_CURRENT_STACK_TRACE[0]}")"
BATS_LINENO="$(bats_frame_lineno "${BATS_CURRENT_STACK_TRACE[0]}")"
bats_frame_filename "${BATS_CURRENT_STACK_TRACE[0]}" 'BATS_SOURCE'
bats_frame_lineno "${BATS_CURRENT_STACK_TRACE[0]}" 'BATS_LINENO'
}

bats_print_stack_trace() {
local frame
local index=1
local count="${#@}"
local filename
local lineno

for frame in "$@"; do
local filename="$(bats_trim_filename "$(bats_frame_filename "$frame")")"
local lineno="$(bats_frame_lineno "$frame")"
bats_frame_filename "$frame" 'filename'
bats_trim_filename "$filename" 'filename'
bats_frame_lineno "$frame" 'lineno'

if [ $index -eq 1 ]; then
echo -n "# ("
else
echo -n "# "
fi

local fn="$(bats_frame_function "$frame")"
local fn
bats_frame_function "$frame" 'fn'
if [ "$fn" != "$BATS_TEST_NAME" ]; then
echo -n "from function \`$fn' "
fi
Expand All @@ -151,12 +154,16 @@ bats_print_stack_trace() {
bats_print_failed_command() {
local frame="$1"
local status="$2"
local filename="$(bats_frame_filename "$frame")"
local lineno="$(bats_frame_lineno "$frame")"
local filename
local lineno
local failed_line
local failed_command

local failed_line="$(bats_extract_line "$filename" "$lineno")"
local failed_command="$(bats_strip_string "$failed_line")"
echo -n "# \`${failed_command}' "
bats_frame_filename "$frame" 'filename'
bats_frame_lineno "$frame" 'lineno'
bats_extract_line "$filename" "$lineno" 'failed_line'
bats_strip_string "$failed_line" 'failed_command'
printf '%s' "# \`${failed_command}' "

if [ $status -eq 1 ]; then
echo "failed"
Expand All @@ -166,49 +173,46 @@ bats_print_failed_command() {
}

bats_frame_lineno() {
local frame="$1"
local lineno="${frame%% *}"
echo "$lineno"
printf -v "$2" '%s' "${1%% *}"
}

bats_frame_function() {
local frame="$1"
local rest="${frame#* }"
local fn="${rest%% *}"
echo "$fn"
local __bff_function="${1#* }"
printf -v "$2" '%s' "${__bff_function%% *}"
}

bats_frame_filename() {
local frame="$1"
local rest="${frame#* }"
local filename="${rest#* }"
local __bff_filename="${1#* }"
__bff_filename="${__bff_filename#* }"

if [ "$filename" = "$BATS_TEST_SOURCE" ]; then
echo "$BATS_TEST_FILENAME"
else
echo "$filename"
if [ "$__bff_filename" = "$BATS_TEST_SOURCE" ]; then
__bff_filename="$BATS_TEST_FILENAME"
fi
printf -v "$2" '%s' "$__bff_filename"
}

bats_extract_line() {
local filename="$1"
local lineno="$2"
sed -n "${lineno}p" "$filename"
local __bats_extract_line_line
local __bats_extract_line_index='0'

while IFS= read -r __bats_extract_line_line; do
if [[ "$((++__bats_extract_line_index))" -eq "$2" ]]; then
printf -v "$3" '%s' "${__bats_extract_line_line%$'\r'}"
break
fi
done <"$1"
}

bats_strip_string() {
local string="$1"
printf "%s" "$string" | sed -e "s/^[ "$'\t'"]*//" -e "s/[ "$'\t'"]*$//"
[[ "$1" =~ ^[[:space:]]*(.*)[[:space:]]*$ ]]
printf -v "$2" '%s' "${BASH_REMATCH[1]}"
}

bats_trim_filename() {
local filename="$1"
local length="${#BATS_CWD}"

if [ "${filename:0:length+1}" = "${BATS_CWD}/" ]; then
echo "${filename:length+1}"
if [[ "$1" =~ ^${BATS_CWD}/ ]]; then
printf -v "$2" '%s' "${1#$BATS_CWD/}"
else
echo "$filename"
printf -v "$2" '%s' "$1"
fi
}

Expand All @@ -218,13 +222,22 @@ bats_debug_trap() {
fi
}

# When running under Bash 3.2.57(1)-release on macOS, the `ERR` trap may not
# always fire, but the `EXIT` trap will. For this reason we call it at the very
# beginning of `bats_teardown_trap` (the `DEBUG` trap for the call will move
# `BATS_CURRENT_STACK_TRACE` to `BATS_PREVIOUS_STACK_TRACE`) and check the value
# of `$?` before taking other actions.
bats_error_trap() {
BATS_ERROR_STATUS="$?"
BATS_ERROR_STACK_TRACE=( "${BATS_PREVIOUS_STACK_TRACE[@]}" )
trap - debug
local status="$?"
if [[ "$status" -ne '0' ]]; then
BATS_ERROR_STATUS="$status"
BATS_ERROR_STACK_TRACE=( "${BATS_PREVIOUS_STACK_TRACE[@]}" )
trap - debug
fi
}

bats_teardown_trap() {
bats_error_trap
trap "bats_exit_trap" exit
local status=0
teardown >>"$BATS_OUT" 2>&1 || status="$?"
Expand Down Expand Up @@ -280,7 +293,7 @@ bats_perform_tests() {

bats_perform_test() {
BATS_TEST_NAME="$1"
if [ "$(type -t "$BATS_TEST_NAME" || true)" = "function" ]; then
if declare -F "$BATS_TEST_NAME" >/dev/null; then
BATS_TEST_NUMBER="$2"
if [ -z "$BATS_TEST_NUMBER" ]; then
echo "1..1"
Expand Down Expand Up @@ -313,7 +326,7 @@ BATS_OUT="${BATS_TMPNAME}.out"

bats_preprocess_source() {
BATS_TEST_SOURCE="${BATS_TMPNAME}.src"
{ tr -d '\r' < "$BATS_TEST_FILENAME"; echo; } | bats-preprocess > "$BATS_TEST_SOURCE"
. bats-preprocess <<< "$(< "$BATS_TEST_FILENAME")"$'\n' > "$BATS_TEST_SOURCE"
trap "bats_cleanup_preprocessed_source" err exit
trap "bats_cleanup_preprocessed_source; exit 1" int
}
Expand Down
17 changes: 10 additions & 7 deletions libexec/bats-preprocess
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ set -e
encode_name() {
local name="$1"
local result="test_"
local hex_code

if [[ ! "$name" =~ [^[:alnum:]\ _-] ]]; then
name="${name//_/-5f}"
Expand All @@ -21,27 +22,29 @@ encode_name() {
elif [[ "$char" =~ [[:alnum:]] ]]; then
result+="$char"
else
result+="$(printf -- "-%02x" \'"$char")"
printf -v 'hex_code' -- "-%02x" \'"$char"
result+="$hex_code"
fi
done
fi

echo "$result"
printf -v "$2" '%s' "$result"
}

tests=()
index=0
pattern='^ *@test *([^ ].*) *\{ *(.*)$'
pattern='^ *@test +(.+) +\{ *(.*)$'

while IFS= read -r line; do
line="${line//$'\r'}"
let index+=1
if [[ "$line" =~ $pattern ]]; then
quoted_name="${BASH_REMATCH[1]}"
name="${BASH_REMATCH[1]#[\'\"]}"
name="${name%[\'\"]}"
body="${BASH_REMATCH[2]}"
name="$(eval echo "$quoted_name")"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doesn't removing eval, line 41 on $quoted_name, remove expected behavior on variable expansion in the string or such?

encoded_name="$(encode_name "$name")"
encode_name "$name" 'encoded_name'
tests["${#tests[@]}"]="$encoded_name"
echo "${encoded_name}() { bats_test_begin ${quoted_name} ${index}; ${body}"
echo "${encoded_name}() { bats_test_begin \"${name}\" ${index}; ${body}"
else
printf "%s\n" "$line"
fi
Expand Down
14 changes: 14 additions & 0 deletions test/bats.bats
Original file line number Diff line number Diff line change
Expand Up @@ -262,3 +262,17 @@ fixtures bats
[ $status -eq 0 ]
[ "${lines[1]}" = "ok 1 loop_func" ]
}

@test "expand variables in test name" {
SUITE='test/suite' run bats "$FIXTURE_ROOT/expand_var_in_test_name.bats"
[ $status -eq 0 ]
[ "${lines[1]}" = "ok 1 test/suite: test with variable in name" ]
}

@test "handle quoted and unquoted test names" {
run bats "$FIXTURE_ROOT/quoted_and_unquoted_test_names.bats"
[ $status -eq 0 ]
[ "${lines[1]}" = "ok 1 single-quoted name" ]
[ "${lines[2]}" = "ok 2 double-quoted name" ]
[ "${lines[3]}" = "ok 3 unquoted name" ]
}
3 changes: 3 additions & 0 deletions test/fixtures/bats/expand_var_in_test_name.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@test "$SUITE: test with variable in name" {
true
}
11 changes: 11 additions & 0 deletions test/fixtures/bats/quoted_and_unquoted_test_names.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@test 'single-quoted name' {
true
}

@test "double-quoted name" {
true
}

@test unquoted name {
true
}
2 changes: 1 addition & 1 deletion test/test_helper.bash
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
fixtures() {
FIXTURE_ROOT="$BATS_TEST_DIRNAME/fixtures/$1"
RELATIVE_FIXTURE_ROOT="$(bats_trim_filename "$FIXTURE_ROOT")"
bats_trim_filename "$FIXTURE_ROOT" 'RELATIVE_FIXTURE_ROOT'
}

setup() {
Expand Down