From a3396cbc891d5f76832cd0cae65bf427c35bfb6c Mon Sep 17 00:00:00 2001 From: Mike Bland Date: Tue, 14 Feb 2017 16:22:02 -0500 Subject: [PATCH 01/20] exec-test: Refactor bats_frame_* functions Preserves existing behavior. Next step will be to take the target variable name as the second argument. --- libexec/bats-exec-test | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/libexec/bats-exec-test b/libexec/bats-exec-test index bdce3c8..518c116 100755 --- a/libexec/bats-exec-test +++ b/libexec/bats-exec-test @@ -166,28 +166,22 @@ bats_print_failed_command() { } bats_frame_lineno() { - local frame="$1" - local lineno="${frame%% *}" - echo "$lineno" + printf '%s\n' "${1%% *}" } bats_frame_function() { - local frame="$1" - local rest="${frame#* }" - local fn="${rest%% *}" - echo "$fn" + local __bff_function="${1#* }" + printf '%s\n' "${__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 '%s\n' "$__bff_filename" } bats_extract_line() { From 93d3cec96f25ce6171ff25b3c70a0a083712ce13 Mon Sep 17 00:00:00 2001 From: Mike Bland Date: Tue, 14 Feb 2017 16:37:35 -0500 Subject: [PATCH 02/20] exec-test: Use printf -v in bats_frame_* functions This is part of the effort to improve performance by reducing the number of command substitutions/subshells spawned by `bats_debug_trap`. Under Bash 3.2.57(1)-release on a MacBook Pro with a 2.9GHz Intel Core i5 CPU and 8GB 1867MHz DDR3 RAM, this makes `bin/bats test/` go from: 44 tests, 0 failures real 0m7.565s user 0m3.664s sys 0m3.368s to: real 0m6.449s user 0m3.290s sys 0m2.665s --- libexec/bats-exec-test | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/libexec/bats-exec-test b/libexec/bats-exec-test index 518c116..734f874 100755 --- a/libexec/bats-exec-test +++ b/libexec/bats-exec-test @@ -114,18 +114,21 @@ bats_capture_stack_trace() { 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' + filename="$(bats_trim_filename "$filename")" + bats_frame_lineno "$frame" 'lineno' if [ $index -eq 1 ]; then echo -n "# (" @@ -133,7 +136,8 @@ bats_print_stack_trace() { 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 @@ -151,8 +155,11 @@ 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 + + bats_frame_filename "$frame" 'filename' + bats_frame_lineno "$frame" 'lineno' local failed_line="$(bats_extract_line "$filename" "$lineno")" local failed_command="$(bats_strip_string "$failed_line")" @@ -166,12 +173,12 @@ bats_print_failed_command() { } bats_frame_lineno() { - printf '%s\n' "${1%% *}" + printf -v "$2" '%s' "${1%% *}" } bats_frame_function() { local __bff_function="${1#* }" - printf '%s\n' "${__bff_function%% *}" + printf -v "$2" '%s' "${__bff_function%% *}" } bats_frame_filename() { @@ -181,7 +188,7 @@ bats_frame_filename() { if [ "$__bff_filename" = "$BATS_TEST_SOURCE" ]; then __bff_filename="$BATS_TEST_FILENAME" fi - printf '%s\n' "$__bff_filename" + printf -v "$2" '%s' "$__bff_filename" } bats_extract_line() { From 39440c57824e5fa6eff1c6173f733fe0e481d1fe Mon Sep 17 00:00:00 2001 From: Mike Bland Date: Wed, 15 Feb 2017 09:44:09 -0500 Subject: [PATCH 03/20] exec-test: Replace `dirname` call with `%/*` --- libexec/bats-exec-test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libexec/bats-exec-test b/libexec/bats-exec-test index 734f874..c9e0a7e 100755 --- a/libexec/bats-exec-test +++ b/libexec/bats-exec-test @@ -26,7 +26,7 @@ else shift fi -BATS_TEST_DIRNAME="$(dirname "$BATS_TEST_FILENAME")" +BATS_TEST_DIRNAME="${BATS_TEST_FILENAME%/*}" BATS_TEST_NAMES=() load() { From eaa151fb698f9fdc3c4e29ab9275f9b9529665bd Mon Sep 17 00:00:00 2001 From: Mike Bland Date: Wed, 15 Feb 2017 10:01:32 -0500 Subject: [PATCH 04/20] exec-test: Use printf -v in bats_trim_filename --- libexec/bats-exec-test | 11 ++++------- test/test_helper.bash | 2 +- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/libexec/bats-exec-test b/libexec/bats-exec-test index c9e0a7e..fce77aa 100755 --- a/libexec/bats-exec-test +++ b/libexec/bats-exec-test @@ -127,7 +127,7 @@ bats_print_stack_trace() { for frame in "$@"; do bats_frame_filename "$frame" 'filename' - filename="$(bats_trim_filename "$filename")" + bats_trim_filename "$filename" 'filename' bats_frame_lineno "$frame" 'lineno' if [ $index -eq 1 ]; then @@ -203,13 +203,10 @@ bats_strip_string() { } 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 } diff --git a/test/test_helper.bash b/test/test_helper.bash index 84eee8c..302f743 100644 --- a/test/test_helper.bash +++ b/test/test_helper.bash @@ -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() { From 3bc03796e9274e10fe412700e6128d7538ab3046 Mon Sep 17 00:00:00 2001 From: Mike Bland Date: Wed, 15 Feb 2017 10:07:22 -0500 Subject: [PATCH 05/20] exec-test: Use printf -v in bats_strip_string --- libexec/bats-exec-test | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/libexec/bats-exec-test b/libexec/bats-exec-test index fce77aa..55f3281 100755 --- a/libexec/bats-exec-test +++ b/libexec/bats-exec-test @@ -162,8 +162,10 @@ bats_print_failed_command() { bats_frame_lineno "$frame" 'lineno' local failed_line="$(bats_extract_line "$filename" "$lineno")" - local failed_command="$(bats_strip_string "$failed_line")" - echo -n "# \`${failed_command}' " + local failed_command + + bats_strip_string "$failed_line" 'failed_command' + printf '%s' "# \`${failed_command}' " if [ $status -eq 1 ]; then echo "failed" @@ -198,8 +200,8 @@ bats_extract_line() { } 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() { From 698fa766a3ee7b62c60c1dd08879347526aa8c7d Mon Sep 17 00:00:00 2001 From: Mike Bland Date: Wed, 15 Feb 2017 10:13:20 -0500 Subject: [PATCH 06/20] exec-test: Replace `type -t` with `command -F` Also eliminates a subshell. --- libexec/bats-exec-test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libexec/bats-exec-test b/libexec/bats-exec-test index 55f3281..71ca9b0 100755 --- a/libexec/bats-exec-test +++ b/libexec/bats-exec-test @@ -289,7 +289,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" From d294689bdd91049843b05df3680b7ff8c0e8d9c8 Mon Sep 17 00:00:00 2001 From: Mike Bland Date: Wed, 15 Feb 2017 10:26:28 -0500 Subject: [PATCH 07/20] exec-test: Use `printf -v` in bats_extract_line Also replaces `sed` invocation with a `while` loop, saving a subprocess. --- libexec/bats-exec-test | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/libexec/bats-exec-test b/libexec/bats-exec-test index 71ca9b0..7e86c16 100755 --- a/libexec/bats-exec-test +++ b/libexec/bats-exec-test @@ -157,13 +157,12 @@ bats_print_failed_command() { local status="$2" local filename local lineno + local failed_line + local failed_command bats_frame_filename "$frame" 'filename' bats_frame_lineno "$frame" 'lineno' - - local failed_line="$(bats_extract_line "$filename" "$lineno")" - local failed_command - + bats_extract_line "$filename" "$lineno" 'failed_line' bats_strip_string "$failed_line" 'failed_command' printf '%s' "# \`${failed_command}' " @@ -194,9 +193,15 @@ bats_frame_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() { From cf9a3b8af4a8bbb6246e321f687d89e5f654fb57 Mon Sep 17 00:00:00 2001 From: Mike Bland Date: Wed, 15 Feb 2017 10:44:11 -0500 Subject: [PATCH 08/20] exec-test: Invoke bats-preprocess directly Also, `bats-preprocess` now converts DOS/Windows CRLF line endings. --- libexec/bats-exec-test | 2 +- libexec/bats-preprocess | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/libexec/bats-exec-test b/libexec/bats-exec-test index 7e86c16..09e8a7f 100755 --- a/libexec/bats-exec-test +++ b/libexec/bats-exec-test @@ -327,7 +327,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 } diff --git a/libexec/bats-preprocess b/libexec/bats-preprocess index 04297ed..873085e 100755 --- a/libexec/bats-preprocess +++ b/libexec/bats-preprocess @@ -34,6 +34,7 @@ index=0 pattern='^ *@test *([^ ].*) *\{ *(.*)$' while IFS= read -r line; do + line="${line//$'\r'}" let index+=1 if [[ "$line" =~ $pattern ]]; then quoted_name="${BASH_REMATCH[1]}" From 741c414d6afe9e0c5be5b4435e80676ebc99df54 Mon Sep 17 00:00:00 2001 From: Mike Bland Date: Wed, 15 Feb 2017 11:01:34 -0500 Subject: [PATCH 09/20] preprocess: Use printf -v in encode_name --- libexec/bats-preprocess | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/libexec/bats-preprocess b/libexec/bats-preprocess index 873085e..6cd90eb 100755 --- a/libexec/bats-preprocess +++ b/libexec/bats-preprocess @@ -4,6 +4,7 @@ set -e encode_name() { local name="$1" local result="test_" + local hex_code if [[ ! "$name" =~ [^[:alnum:]\ _-] ]]; then name="${name//_/-5f}" @@ -21,12 +22,13 @@ 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=() @@ -40,7 +42,7 @@ while IFS= read -r line; do quoted_name="${BASH_REMATCH[1]}" body="${BASH_REMATCH[2]}" name="$(eval echo "$quoted_name")" - encoded_name="$(encode_name "$name")" + encode_name "$name" 'encoded_name' tests["${#tests[@]}"]="$encoded_name" echo "${encoded_name}() { bats_test_begin ${quoted_name} ${index}; ${body}" else From e613b31a1cde947bbe949eaf2fa1d4d99cfc15de Mon Sep 17 00:00:00 2001 From: Mike Bland Date: Wed, 15 Feb 2017 11:10:55 -0500 Subject: [PATCH 10/20] exec-test: Replace `|| { }` with `if [[ ]]; then` Somehow this is ever-so-slightly faster. --- libexec/bats-exec-test | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libexec/bats-exec-test b/libexec/bats-exec-test index 09e8a7f..1ffc746 100755 --- a/libexec/bats-exec-test +++ b/libexec/bats-exec-test @@ -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}" } From fac51df957a26d440284b839ffac69cd258cde7b Mon Sep 17 00:00:00 2001 From: Mike Bland Date: Wed, 15 Feb 2017 11:23:28 -0500 Subject: [PATCH 11/20] exec-test: Replace `caller` with `FUNCNAME`, etc. This is part of the effort to improve performance by reducing the number of command substitutions/subshells spawned by `bats_debug_trap`. Under Bash 3.2.57(1)-release on a MacBook Pro with a 2.9GHz Intel Core i5 CPU and 8GB 1867MHz DDR3 RAM, this makes `bin/bats test/` go from the following for the previous commit: 44 tests, 0 failures real 0m5.293s user 0m2.853s sys 0m2.087s to: real 0m4.319s user 0m2.559s sys 0m1.454s --- libexec/bats-exec-test | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/libexec/bats-exec-test b/libexec/bats-exec-test index 1ffc746..b27a941 100755 --- a/libexec/bats-exec-test +++ b/libexec/bats-exec-test @@ -101,16 +101,15 @@ 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 From 3ab495fda2e032b869eee86b262e651ff907c9c5 Mon Sep 17 00:00:00 2001 From: Mike Bland Date: Wed, 15 Feb 2017 12:04:03 -0500 Subject: [PATCH 12/20] preprocess: Add tests for vars, quotes in names This is in anticipation of refactoring away the `$(eval echo "$quoted_name")` command substitution. --- test/bats.bats | 14 ++++++++++++++ test/fixtures/bats/expand_var_in_test_name.bats | 3 +++ .../bats/quoted_and_unquoted_test_names.bats | 11 +++++++++++ 3 files changed, 28 insertions(+) create mode 100644 test/fixtures/bats/expand_var_in_test_name.bats create mode 100644 test/fixtures/bats/quoted_and_unquoted_test_names.bats diff --git a/test/bats.bats b/test/bats.bats index 9fca64a..60fb017 100755 --- a/test/bats.bats +++ b/test/bats.bats @@ -268,3 +268,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" ] +} diff --git a/test/fixtures/bats/expand_var_in_test_name.bats b/test/fixtures/bats/expand_var_in_test_name.bats new file mode 100644 index 0000000..8f6dec2 --- /dev/null +++ b/test/fixtures/bats/expand_var_in_test_name.bats @@ -0,0 +1,3 @@ +@test "$SUITE: test with variable in name" { + true +} diff --git a/test/fixtures/bats/quoted_and_unquoted_test_names.bats b/test/fixtures/bats/quoted_and_unquoted_test_names.bats new file mode 100644 index 0000000..706902b --- /dev/null +++ b/test/fixtures/bats/quoted_and_unquoted_test_names.bats @@ -0,0 +1,11 @@ +@test 'single-quoted name' { + true +} + +@test "double-quoted name" { + true +} + +@test unquoted { + true +} From 6beea07a0b9b0629b3ddec47899a297f82d9e8b9 Mon Sep 17 00:00:00 2001 From: Mike Bland Date: Wed, 15 Feb 2017 11:53:01 -0500 Subject: [PATCH 13/20] preprocess: Eliminate eval in subshell This is part of the effort to improve performance by reducing the number of command substitutions/subshells. Under Bash 3.2.57(1)-release on a MacBook Pro with a 2.9GHz Intel Core i5 CPU and 8GB 1867MHz DDR3 RAM, this shaves off O(0.15s) from the test suite at the previous commit, but I anticipate this effect being magnified on Windows platforms. --- libexec/bats-preprocess | 8 ++++---- test/bats.bats | 2 +- test/fixtures/bats/quoted_and_unquoted_test_names.bats | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/libexec/bats-preprocess b/libexec/bats-preprocess index 6cd90eb..30392b6 100755 --- a/libexec/bats-preprocess +++ b/libexec/bats-preprocess @@ -33,18 +33,18 @@ encode_name() { 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")" 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 diff --git a/test/bats.bats b/test/bats.bats index 60fb017..9bed998 100755 --- a/test/bats.bats +++ b/test/bats.bats @@ -280,5 +280,5 @@ fixtures bats [ $status -eq 0 ] [ "${lines[1]}" = "ok 1 single-quoted name" ] [ "${lines[2]}" = "ok 2 double-quoted name" ] - [ "${lines[3]}" = "ok 3 unquoted" ] + [ "${lines[3]}" = "ok 3 unquoted name" ] } diff --git a/test/fixtures/bats/quoted_and_unquoted_test_names.bats b/test/fixtures/bats/quoted_and_unquoted_test_names.bats index 706902b..aa460da 100644 --- a/test/fixtures/bats/quoted_and_unquoted_test_names.bats +++ b/test/fixtures/bats/quoted_and_unquoted_test_names.bats @@ -6,6 +6,6 @@ true } -@test unquoted { +@test unquoted name { true } From cdc55ad7ea7fd52ad263cda1b635d909208ca09b Mon Sep 17 00:00:00 2001 From: Mike Bland Date: Thu, 16 Mar 2017 12:14:34 -0400 Subject: [PATCH 14/20] bats: Refactor resolve_link, abs_dirname Under Bash 3.2.57(1)-release and 4.4.12(1)-release on a MacBook Pro with a 2.9GHz Intel Core i5 CPU and 8GB 1867MHz DDR3 RAM, this shaves off O(0.4s) from the current test suite. Before the change: 46 tests, 0 failures real 0m4.392s user 0m2.489s sys 0m1.467s After the change: real 0m3.980s user 0m2.312s sys 0m1.233s --- libexec/bats | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/libexec/bats b/libexec/bats index 06f8fab..8129050 100755 --- a/libexec/bats +++ b/libexec/bats @@ -26,18 +26,29 @@ help() { echo } +BATS_READLINK= + resolve_link() { - $(type -p greadlink readlink | head -1) "$1" + if [[ -z "$BATS_READLINK" ]]; then + if command -v 'greadlink' >/dev/null; then + BATS_READLINK='greadlink' + elif command -v 'readlink' >/dev/null; then + BATS_READLINK='readlink' + else + BATS_READLINK='true' + fi + fi + "$BATS_READLINK" "$1" || return 0 } abs_dirname() { - local cwd="$(pwd)" + local cwd="$PWD" local path="$1" while [ -n "$path" ]; do cd "${path%/*}" local name="${path##*/}" - path="$(resolve_link "$name" || true)" + path="$(resolve_link "$name")" done pwd From daf76c27c146598c2deba15694c6cd01b2cd82a6 Mon Sep 17 00:00:00 2001 From: Mike Bland Date: Thu, 16 Mar 2017 12:25:17 -0400 Subject: [PATCH 15/20] bats: Convert abs_dirname to use `printf -v` Under Bash 3.2.57(1)-release and 4.4.12(1)-release on a MacBook Pro with a 2.9GHz Intel Core i5 CPU and 8GB 1867MHz DDR3 RAM, this shaves off O(0.1s) from the current test suite. Before the change: 46 tests, 0 failures real 0m3.983s user 0m2.320s sys 0m1.241s After the change: real 0m3.861s user 0m2.276s sys 0m1.174s --- libexec/bats | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/libexec/bats b/libexec/bats index 8129050..5e90a9e 100755 --- a/libexec/bats +++ b/libexec/bats @@ -51,7 +51,7 @@ abs_dirname() { path="$(resolve_link "$name")" done - pwd + printf -v "$2" -- '%s' "$PWD" cd "$cwd" } @@ -63,9 +63,12 @@ expand_path() { } || echo "$1" } -BATS_LIBEXEC="$(abs_dirname "$0")" -export BATS_PREFIX="$(abs_dirname "$BATS_LIBEXEC")" -export BATS_CWD="$(abs_dirname .)" +abs_dirname "$0" 'BATS_LIBEXEC' +abs_dirname "$BATS_LIBEXEC" 'BATS_PREFIX' +abs_dirname '.' 'BATS_CWD' + +export BATS_PREFIX +export BATS_CWD export PATH="$BATS_LIBEXEC:$PATH" options=() From d4443adeb790cca11283013fedb28cc2bc12d9cb Mon Sep 17 00:00:00 2001 From: Mike Bland Date: Thu, 16 Mar 2017 13:15:38 -0400 Subject: [PATCH 16/20] bash: Refactor expand_path Under Bash 3.2.57(1)-release and 4.4.12(1)-release on a MacBook Pro with a 2.9GHz Intel Core i5 CPU and 8GB 1867MHz DDR3 RAM, this shaves off O(0.25s) from the current test suite. Before the change: 46 tests, 0 failures real 0m3.851s user 0m2.273s sys 0m1.166s After the change: real 0m3.595s user 0m2.171s sys 0m1.048s --- libexec/bats | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/libexec/bats b/libexec/bats index 5e90a9e..5cd95c6 100755 --- a/libexec/bats +++ b/libexec/bats @@ -56,11 +56,19 @@ abs_dirname() { } expand_path() { - { cd "$(dirname "$1")" 2>/dev/null - local dirname="$PWD" + local path="${1%/}" + local dirname="${path%/*}" + + if [[ "$dirname" == "$path" ]]; then + dirname="$PWD" + elif cd "$dirname" 2>/dev/null; then + dirname="$PWD" cd "$OLDPWD" - echo "$dirname/$(basename "$1")" - } || echo "$1" + else + printf '%s' "$path" + return + fi + printf -v "$2" '%s/%s' "$dirname" "${path##*/}" } abs_dirname "$0" 'BATS_LIBEXEC' @@ -127,14 +135,16 @@ fi filenames=() for filename in "${arguments[@]}"; do + expand_path "$filename" 'filename' + if [ -d "$filename" ]; then shopt -s nullglob - for suite_filename in "$(expand_path "$filename")"/*.bats; do + for suite_filename in "$filename"/*.bats; do filenames["${#filenames[@]}"]="$suite_filename" done shopt -u nullglob else - filenames["${#filenames[@]}"]="$(expand_path "$filename")" + filenames["${#filenames[@]}"]="$filename" fi done From 7bcbb2f3e9babf5ab1683c189cdafbed665e6ea9 Mon Sep 17 00:00:00 2001 From: Mike Bland Date: Thu, 16 Mar 2017 13:40:37 -0400 Subject: [PATCH 17/20] bats: Don't pipe to cat for nonpretty output Under Bash 3.2.57(1)-release and 4.4.12(1)-release on a MacBook Pro with a 2.9GHz Intel Core i5 CPU and 8GB 1867MHz DDR3 RAM, this shaves off O(0.04-0.05s) from the current test suite. Very minor, but it's a straightforward change that may provide a minor-yet-noticeable effect on Windows. Before this change: 46 tests, 0 failures real 0m3.588s user 0m2.171s sys 0m1.046s After this change: real 0m3.538s user 0m2.119s sys 0m0.941s --- libexec/bats | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/libexec/bats b/libexec/bats index 5cd95c6..7dc0c14 100755 --- a/libexec/bats +++ b/libexec/bats @@ -154,13 +154,11 @@ else command="bats-exec-suite" fi -if [ -n "$pretty" ]; then +set -o pipefail execfail +if [ -z "$pretty" ]; then + exec "$command" $count_flag "${filenames[@]}" +else extended_syntax_flag="-x" formatter="bats-format-tap-stream" -else - extended_syntax_flag="" - formatter="cat" + exec "$command" $count_flag $extended_syntax_flag "${filenames[@]}" | "$formatter" fi - -set -o pipefail execfail -exec "$command" $count_flag $extended_syntax_flag "${filenames[@]}" | "$formatter" From f5acd286129e3b68945a33da7d74ece82564ac57 Mon Sep 17 00:00:00 2001 From: Mike Bland Date: Thu, 16 Mar 2017 14:10:19 -0400 Subject: [PATCH 18/20] bats-exec-suite: Count tests w/ BATS_TEST_PATTERN Under Bash 3.2.57(1)-release and 4.4.12(1)-release on a MacBook Pro with a 2.9GHz Intel Core i5 CPU and 8GB 1867MHz DDR3 RAM, this shaves off O(0.16s) from the current test suite. Before this change: 46 tests, 0 failures real 0m3.541s user 0m2.125s sys 0m0.937s After this change: real 0m3.372s user 0m2.031s sys 0m0.894s --- libexec/bats | 1 + libexec/bats-exec-suite | 6 +++++- libexec/bats-preprocess | 3 +-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/libexec/bats b/libexec/bats index 7dc0c14..e074747 100755 --- a/libexec/bats +++ b/libexec/bats @@ -77,6 +77,7 @@ abs_dirname '.' 'BATS_CWD' export BATS_PREFIX export BATS_CWD +export BATS_TEST_PATTERN='^ *@test +(.+) +\{ *(.*)$' export PATH="$BATS_LIBEXEC:$PATH" options=() diff --git a/libexec/bats-exec-suite b/libexec/bats-exec-suite index 29ab255..96021bb 100755 --- a/libexec/bats-exec-suite +++ b/libexec/bats-exec-suite @@ -17,7 +17,11 @@ trap "kill 0; exit 1" int count=0 for filename in "$@"; do - let count+="$(bats-exec-test -c "$filename")" + while IFS= read -r line; do + if [[ "$line" =~ $BATS_TEST_PATTERN ]]; then + let count+=1 + fi + done <"$filename" done if [ -n "$count_only_flag" ]; then diff --git a/libexec/bats-preprocess b/libexec/bats-preprocess index 30392b6..8307b76 100755 --- a/libexec/bats-preprocess +++ b/libexec/bats-preprocess @@ -33,12 +33,11 @@ encode_name() { tests=() index=0 -pattern='^ *@test +(.+) +\{ *(.*)$' while IFS= read -r line; do line="${line//$'\r'}" let index+=1 - if [[ "$line" =~ $pattern ]]; then + if [[ "$line" =~ $BATS_TEST_PATTERN ]]; then name="${BASH_REMATCH[1]#[\'\"]}" name="${name%[\'\"]}" body="${BASH_REMATCH[2]}" From 8c4a86d53458920db6953d8ab613c3f7149d0e35 Mon Sep 17 00:00:00 2001 From: Mike Bland Date: Thu, 16 Mar 2017 16:04:54 -0400 Subject: [PATCH 19/20] format-tap: Refactor summary to remove subshells While the performance impact of these changes are in the noise under macOS 10.12.3 on a on a MacBook Pro with a 2.9GHz Intel Core i5 CPU and 8GB 1867MHz DDR3 RAM, eliminating these subshells makes the code more consistent. I did try removing `buffer` to eliminate yet more subshells, but the flickering of the output did prove annoying, so I'm not removing it. --- libexec/bats-format-tap-stream | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/libexec/bats-format-tap-stream b/libexec/bats-format-tap-stream index 614768f..b13fe1f 100755 --- a/libexec/bats-format-tap-stream +++ b/libexec/bats-format-tap-stream @@ -65,9 +65,15 @@ log() { } summary() { - printf "\n%d test%s" "$count" "$(plural "$count")" + printf "\n%d test" "$count" + if [[ "$count" -ne '1' ]]; then + printf 's' + fi - printf ", %d failure%s" "$failures" "$(plural "$failures")" + printf ", %d failure" "$failures" + if [[ "$failures" -ne '1' ]]; then + printf 's' + fi if [ "$skipped" -gt 0 ]; then printf ", %d skipped" "$skipped" @@ -79,7 +85,9 @@ summary() { printf_with_truncation() { local width="$1" shift - local string="$(printf "$@")" + local string + + printf -v 'string' -- "$@" if [ "${#string}" -gt "$width" ]; then printf "%s..." "${string:0:$(( $width - 4 ))}" @@ -105,18 +113,18 @@ advance() { set_color() { local color="$1" - local weight="$2" - printf "\x1B[%d;%dm" $(( 30 + $color )) "$( [ "$weight" = "bold" ] && echo 1 || echo 22 )" + local weight='22' + + if [[ "$2" == 'bold' ]]; then + weight='1' + fi + printf "\x1B[%d;%dm" $(( 30 + $color )) "$weight" } clear_color() { printf "\x1B[0m" } -plural() { - [ "$1" -eq 1 ] || echo "s" -} - _buffer="" buffer() { From ebb192e036284b5006ecac08343c72be6e93e19b Mon Sep 17 00:00:00 2001 From: Mike Bland Date: Thu, 28 Sep 2017 06:34:03 -0400 Subject: [PATCH 20/20] Fix test failures due to unquoted variables The four test cases updated in this commit were failing on my Windows virtual machine because my username contains a space. Quoting the file paths containing "$FIXTURE_ROOT" solved the problem. --- test/bats.bats | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/bats.bats b/test/bats.bats index 9bed998..44b573c 100755 --- a/test/bats.bats +++ b/test/bats.bats @@ -41,25 +41,25 @@ fixtures bats } @test "summary passing tests" { - run filter_control_sequences bats -p $FIXTURE_ROOT/passing.bats + run filter_control_sequences bats -p "$FIXTURE_ROOT/passing.bats" [ $status -eq 0 ] [ "${lines[1]}" = "1 test, 0 failures" ] } @test "summary passing and skipping tests" { - run filter_control_sequences bats -p $FIXTURE_ROOT/passing_and_skipping.bats + run filter_control_sequences bats -p "$FIXTURE_ROOT/passing_and_skipping.bats" [ $status -eq 0 ] [ "${lines[2]}" = "2 tests, 0 failures, 1 skipped" ] } @test "summary passing and failing tests" { - run filter_control_sequences bats -p $FIXTURE_ROOT/failing_and_passing.bats + run filter_control_sequences bats -p "$FIXTURE_ROOT/failing_and_passing.bats" [ $status -eq 0 ] [ "${lines[4]}" = "2 tests, 1 failure" ] } @test "summary passing, failing and skipping tests" { - run filter_control_sequences bats -p $FIXTURE_ROOT/passing_failing_and_skipping.bats + run filter_control_sequences bats -p "$FIXTURE_ROOT/passing_failing_and_skipping.bats" [ $status -eq 0 ] [ "${lines[5]}" = "3 tests, 1 failure, 1 skipped" ] }