From 1c159479bf42a52e6510cd85fa5443073552214f Mon Sep 17 00:00:00 2001 From: Mike Bland Date: Fri, 10 Feb 2017 22:40:15 -0500 Subject: [PATCH 1/7] test: Add `go-coverage` to coverage includes Should've been part of #155. --- scripts/test | 3 ++- tests/test.bats | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/test b/scripts/test index 5736c64..a13d6df 100755 --- a/scripts/test +++ b/scripts/test @@ -26,7 +26,8 @@ # Passes all arguments through to `@go.bats_main` from `lib/bats-main`. _test_main() { local _GO_BATS_COVERAGE_INCLUDE - _GO_BATS_COVERAGE_INCLUDE=('go' 'go-core.bash' 'lib/' 'libexec/' 'scripts/') + _GO_BATS_COVERAGE_INCLUDE=('go' 'go-core.bash' 'go-template' + 'lib/' 'libexec/' 'scripts/') local _GO_COVERALLS_URL='https://coveralls.io/github/mbland/go-script-bash' . "$_GO_USE_MODULES" 'bats-main' diff --git a/tests/test.bats b/tests/test.bats index d0002d5..bc3e4ef 100644 --- a/tests/test.bats +++ b/tests/test.bats @@ -118,7 +118,7 @@ write_bats_dummy_stub_kcov_lib_and_copy_test_script() { local expected_kcov_args=( 'tests/kcov' 'tests/coverage' - 'go,go-core.bash,lib/,libexec/,scripts/' + 'go,go-core.bash,go-template,lib/,libexec/,scripts/' '/tmp/,tests/bats/' 'https://coveralls.io/github/mbland/go-script-bash' "$TEST_GO_SCRIPT" From 466cec35dd6a892e2b0179dd9a9a4042a0257ee6 Mon Sep 17 00:00:00 2001 From: Mike Bland Date: Sat, 11 Feb 2017 09:32:57 -0500 Subject: [PATCH 2/7] core, bats-main: Move `_GO_TEST_DIR` to go-core This is in anticipation of adding a `--test` option to the forthcoming `./go new` test file generator. --- go-core.bash | 3 +++ lib/bats-main | 3 --- tests/vars.bats | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/go-core.bash b/go-core.bash index f5a7540..2063c45 100755 --- a/go-core.bash +++ b/go-core.bash @@ -112,6 +112,9 @@ declare _GO_IMPORTED_MODULE_CALLERS=() # Path to the project's script directory declare _GO_SCRIPTS_DIR= +# Directory containing Bats tests, relative to `_GO_ROOTDIR` +declare -r -x _GO_TEST_DIR="${_GO_TEST_DIR:-tests}" + # Path to the main ./go script in the project's root directory declare -r -x _GO_SCRIPT="$_GO_ROOTDIR/${0##*/}" diff --git a/lib/bats-main b/lib/bats-main index 4e8f74a..00301c7 100644 --- a/lib/bats-main +++ b/lib/bats-main @@ -40,9 +40,6 @@ # make the Bats repository a submodule if so desired. The Bats version so cloned # may be set by overriding `_GO_BATS_VERSION`. -# Directory containing Bats tests, relative to `_GO_ROOTDIR` -export _GO_TEST_DIR="${_GO_TEST_DIR:-tests}" - # Directory in which kcov will be built export _GO_KCOV_DIR="${_GO_KCOV_DIR:-$_GO_TEST_DIR/kcov}" diff --git a/tests/vars.bats b/tests/vars.bats index d4ce102..1abda87 100644 --- a/tests/vars.bats +++ b/tests/vars.bats @@ -55,7 +55,7 @@ quotify_expected() { "declare -rx _GO_SCRIPT=\"$TEST_GO_SCRIPT\"" "declare -- _GO_SCRIPTS_DIR=\"$TEST_GO_SCRIPTS_DIR\"" "declare -a _GO_SEARCH_PATHS=(${search_paths[*]})" - "declare -x _GO_TEST_DIR=\"$_GO_TEST_DIR\"" + "declare -rx _GO_TEST_DIR=\"$_GO_TEST_DIR\"" "declare -rx _GO_USE_MODULES=\"$_GO_CORE_DIR/lib/internal/use\"") quotify_expected @@ -127,7 +127,7 @@ quotify_expected() { "declare -rx _GO_SCRIPT=\"$TEST_GO_SCRIPT\"" "declare -- _GO_SCRIPTS_DIR=\"$TEST_GO_SCRIPTS_DIR\"" "declare -a _GO_SEARCH_PATHS=(${search_paths[*]})" - "declare -x _GO_TEST_DIR=\"$_GO_TEST_DIR\"" + "declare -rx _GO_TEST_DIR=\"$_GO_TEST_DIR\"" "declare -rx _GO_USE_MODULES=\"$_GO_CORE_DIR/lib/internal/use\"") quotify_expected From f3d0dd90a3c3151eccc91490447b9c3dc27cfefb Mon Sep 17 00:00:00 2001 From: Mike Bland Date: Sun, 12 Feb 2017 13:30:00 -0500 Subject: [PATCH 3/7] .gitignore: Remove items, add $HOME/.config advice --- .gitignore | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index b82509d..b8eb2d2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ -.*.swp -.DS_Store +# Are your editor's temp files, OS metadata files, etc. showing up in `git +# status`? Try adding them to `$HOME/.config/git/ignore` instead. tests/bats/ tests/coverage/ tests/kcov/ From d2b0db3b98463acb1c2bba20e7396a620a20adde Mon Sep 17 00:00:00 2001 From: Mike Bland Date: Sun, 12 Feb 2017 14:05:42 -0500 Subject: [PATCH 4/7] lib/testing: Remove vestigial module import --- lib/testing/stack-trace-item | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/testing/stack-trace-item b/lib/testing/stack-trace-item index af5c956..e022937 100755 --- a/lib/testing/stack-trace-item +++ b/lib/testing/stack-trace-item @@ -5,8 +5,6 @@ # DO NOT USE THIS FILE DIRECTLY! Source `_GO_CORE_DIR/lib/testing/stack-trace` # and use the `stack_trace_item` function instead. -. "$_GO_CORE_DIR/lib/bats/helpers" - __@go.stack_trace_item() { local filepath="$1" local function_name="$2" From d5ca9cbe42bb93c39d6365f42ca33f54c3bd635b Mon Sep 17 00:00:00 2001 From: Mike Bland Date: Sun, 12 Feb 2017 15:28:27 -0500 Subject: [PATCH 5/7] complete: Fix typo in `@go.compgen` comment --- lib/complete | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/complete b/lib/complete index 816c80e..50f492a 100644 --- a/lib/complete +++ b/lib/complete @@ -15,7 +15,7 @@ # `-f` or `-d` options to generate file or directory paths. # # Arguments: -# ...: Arguments passed directly ot the builtin `compgen` +# ...: Arguments passed directly to the builtin `compgen` @go.compgen() { local add_slashes local compreply=() From 95f714abd88a053cbc4adfbd98f791ced4eff5db Mon Sep 17 00:00:00 2001 From: Mike Bland Date: Sun, 12 Feb 2017 15:31:56 -0500 Subject: [PATCH 6/7] testing/env: Add `@go.test_compgen` Regarding the claim that this new function is faster than the old hand-written loops, here are representative timings for the command: TEST_FILTER='(directories|: tab completion$)' gos test complete get/ For hand-written loops: 43 tests, 0 failures, 39 skipped real 0m7.020s user 0m3.352s sys 0m3.473s For `@go.test_compgen`: 43 tests, 0 failures, 39 skipped real 0m6.367s user 0m3.071s sys 0m3.104s Considering the fact that most of that time was spent skipping 39 tests, the result is pretty substantial. Even beyond that, it provided the insight that led to filing issue #156. --- lib/testing/environment | 16 ++++++++++++++++ tests/complete.bats | 16 ++++++---------- tests/get/file.bats | 7 +------ tests/get/git-repo.bats | 6 ++---- tests/testing/environment.bats | 25 +++++++++++++++++++++++++ 5 files changed, 50 insertions(+), 20 deletions(-) diff --git a/lib/testing/environment b/lib/testing/environment index c1cf107..34810b4 100644 --- a/lib/testing/environment +++ b/lib/testing/environment @@ -108,3 +108,19 @@ test-go() { @go.create_test_command_script "$parent.d/$subcommand" done } + +# Assigns the results of `@go.compgen` to a caller-defined test data array +# +# While more compact than writing a `while` loop by hand, it's also faster +# thanks to the fact that it disables the Bats function tracing mechanism. +# +# Arguments: +# result: Name of the caller-declared output array +# ...: Arguments passed directly to `@go.compgen` +@go.test_compgen() { + . "$_GO_CORE_DIR/lib/bats/assertions" + set "$BATS_ASSERTION_DISABLE_SHELL_OPTIONS" + . "$_GO_USE_MODULES" 'complete' 'strings' + @go.split $'\n' "$(@go.compgen "${@:2}")" "$1" + return_from_bats_assertion +} diff --git a/tests/complete.bats b/tests/complete.bats index b957bda..8e6f7a8 100644 --- a/tests/complete.bats +++ b/tests/complete.bats @@ -70,7 +70,7 @@ teardown() { while IFS= read -r item; do expected+=("${item#$TEST_GO_ROOTDIR/}") - done<<<"$(@go.compgen -d "$TEST_GO_SCRIPTS_DIR/")" + done < <(@go.compgen -d "$TEST_GO_SCRIPTS_DIR/") run "$TEST_GO_SCRIPT" complete 1 cd 'scripts/' assert_success "${expected[@]}" @@ -85,16 +85,12 @@ teardown() { touch "${files[@]/#/$TEST_GO_SCRIPTS_DIR/}" local top_level=() - local all_scripts_entries=() - local item - - while IFS= read -r item; do - top_level+=("${item#$TEST_GO_ROOTDIR/}") - done <<<"$(@go.compgen -f "$TEST_GO_ROOTDIR/")" + @go.test_compgen top_level -f "$TEST_GO_ROOTDIR/" + top_level=("${top_level[@]#$TEST_GO_ROOTDIR/}") - while IFS= read -r item; do - all_scripts_entries+=("${item#$TEST_GO_ROOTDIR/}") - done <<<"$(@go.compgen -f "$TEST_GO_SCRIPTS_DIR/")" + local all_scripts_entries=() + @go.test_compgen all_scripts_entries -f "$TEST_GO_SCRIPTS_DIR/" + all_scripts_entries=("${all_scripts_entries[@]#$TEST_GO_ROOTDIR/}") run "$TEST_GO_SCRIPT" complete 1 edit '' assert_success "${top_level[@]}" diff --git a/tests/get/file.bats b/tests/get/file.bats index 774ef7d..81b05ed 100644 --- a/tests/get/file.bats +++ b/tests/get/file.bats @@ -26,12 +26,7 @@ teardown() { assert_success '-f' local expected=() - local item - - . "$_GO_USE_MODULES" 'complete' - while IFS= read -r item; do - expected+=("$item") - done <<<"$(@go.compgen -f -- "$TEST_GO_ROOTDIR/")" + @go.test_compgen expected -f -- "$TEST_GO_ROOTDIR/" run "$TEST_GO_SCRIPT" get file --complete 1 -f assert_success "${expected[@]#$TEST_GO_ROOTDIR/}" diff --git a/tests/get/git-repo.bats b/tests/get/git-repo.bats index 4e29e9f..5dbc046 100644 --- a/tests/get/git-repo.bats +++ b/tests/get/git-repo.bats @@ -24,10 +24,8 @@ teardown() { run "$TEST_GO_SCRIPT" get git-repo --complete 1 assert_success '' - . "$_GO_USE_MODULES" 'complete' - while IFS= read -r item; do - expected+=("$item") - done <<<"$(@go.compgen -f -- "$TEST_GO_ROOTDIR/")" + local expected=() + @go.test_compgen expected -f -- "$TEST_GO_ROOTDIR/" run "$TEST_GO_SCRIPT" get git-repo --complete 2 assert_success "${expected[@]#$TEST_GO_ROOTDIR/}" diff --git a/tests/testing/environment.bats b/tests/testing/environment.bats index 8311cdd..9350319 100644 --- a/tests/testing/environment.bats +++ b/tests/testing/environment.bats @@ -93,3 +93,28 @@ teardown() { run test-go assert_success '_GO_CMD: test-go' } + +@test "$SUITE: @go.test_compgen" { + mkdir -p "$TEST_GO_ROOTDIR/lib" + printf 'foo' >"$TEST_GO_ROOTDIR/lib/foo" + printf 'bar' >"$TEST_GO_ROOTDIR/lib/bar" + printf 'baz' >"$TEST_GO_ROOTDIR/lib/baz" + + local expected=() + local item + + . "$_GO_USE_MODULES" 'complete' + while IFS= read -r item; do + expected+=("${item#$TEST_GO_ROOTDIR/}") + done < <(@go.compgen -f -- "$TEST_GO_ROOTDIR/lib/") + + export -f @go.test_compgen + @go.create_test_go_script \ + '. "$_GO_USE_MODULES" "complete"' \ + 'declare results=()' \ + '@go.test_compgen "results" -f -- lib/' \ + 'printf "%s\n" "${results[@]}"' + + run "$TEST_GO_SCRIPT" + assert_success "${expected[@]}" +} From b9152d208f5a751e5e3bd5c8f5091613d6cf66e1 Mon Sep 17 00:00:00 2001 From: Mike Bland Date: Sun, 12 Feb 2017 16:08:38 -0500 Subject: [PATCH 7/7] cmd-desc: Make `from-file` test faster The reason this test suite was so slow before was because the test cases made direct calls to the functions under test instead of wrapping them inside `TEST_GO_SCRIPT`. The functions would then call other functions, then call other functions in loops, and so on, causing Bats to do a lot of work building stack traces with every call. A sample time before this change on my macOS MacBoook Pro: 6 tests, 0 failures real 0m6.554s user 0m2.820s sys 0m3.487s A sample time after: 6 tests, 0 failures real 0m2.316s user 0m1.010s sys 0m1.204s I expect the difference to be even more dramatic on Windows. --- tests/command-descriptions/from-file.bats | 70 +++++++++++------------ 1 file changed, 33 insertions(+), 37 deletions(-) diff --git a/tests/command-descriptions/from-file.bats b/tests/command-descriptions/from-file.bats index 6ed45f4..d9e7450 100644 --- a/tests/command-descriptions/from-file.bats +++ b/tests/command-descriptions/from-file.bats @@ -5,8 +5,18 @@ load ../environment TEST_COMMAND_SCRIPT_PATH="$TEST_GO_SCRIPTS_DIR/test-command" setup() { - . 'lib/internal/command_descriptions' + test_filter + + @go.create_test_go_script \ + ". '$_GO_CORE_DIR/lib/internal/command_descriptions'" \ + 'declare __go_cmd_desc=""' \ + '"$@"' \ + 'declare result="$?"' \ + 'printf "%s\n" "$__go_cmd_desc"' \ + 'exit "$result"' +} +create_script_with_description() { local script='# # Command that does something in {{root}} # @@ -51,14 +61,14 @@ teardown() { @test "$SUITE: return error if there's an error reading" { skip_if_cannot_trigger_file_permission_failure + printf '\n' >"$TEST_COMMAND_SCRIPT_PATH" chmod ugo-r "$TEST_COMMAND_SCRIPT_PATH" - run _@go.command_summary "$TEST_COMMAND_SCRIPT_PATH" + run "$TEST_GO_SCRIPT" _@go.command_summary "$TEST_COMMAND_SCRIPT_PATH" assert_failure assert_output_matches "ERROR: problem reading $TEST_COMMAND_SCRIPT_PATH\$" - output='' - run _@go.command_description "$TEST_COMMAND_SCRIPT_PATH" + run "$TEST_GO_SCRIPT" _@go.command_description "$TEST_COMMAND_SCRIPT_PATH" assert_failure assert_output_matches "ERROR: problem reading $TEST_COMMAND_SCRIPT_PATH\$" } @@ -67,39 +77,32 @@ teardown() { @go.create_test_command_script 'test-command' \ 'echo "This script has no description"' - local __go_cmd_desc='' - _@go.command_summary "$TEST_COMMAND_SCRIPT_PATH" - assert_equal 'No description available' "$__go_cmd_desc" 'command summary' + run "$TEST_GO_SCRIPT" _@go.command_summary "$TEST_COMMAND_SCRIPT_PATH" + assert_success 'No description available' __go_cmd_desc='' - _@go.command_description "$TEST_COMMAND_SCRIPT_PATH" - assert_equal 'No description available' "$__go_cmd_desc" 'command description' + run "$TEST_GO_SCRIPT" _@go.command_description "$TEST_COMMAND_SCRIPT_PATH" + assert_success 'No description available' } @test "$SUITE: parse summary from command script" { - _GO_ROOTDIR='/foo/bar' - _@go.command_summary "$TEST_COMMAND_SCRIPT_PATH" - assert_equal 'Command that does something in /foo/bar' "$__go_cmd_desc" \ - 'command summary' + create_script_with_description + run "$TEST_GO_SCRIPT" _@go.command_summary "$TEST_COMMAND_SCRIPT_PATH" + assert_success "Command that does something in $TEST_GO_ROOTDIR" } @test "$SUITE: one-line description from command script has no trailing space" { echo '# Command that does something in {{root}}' > "$TEST_COMMAND_SCRIPT_PATH" - _GO_ROOTDIR='/foo/bar' - COLUMNS=40 - - _@go.command_description "$TEST_COMMAND_SCRIPT_PATH" - assert_equal 'Command that does something in /foo/bar' "$__go_cmd_desc" \ - 'one-line command description' + COLUMNS=40 run "$TEST_GO_SCRIPT" _@go.command_description \ + "$TEST_COMMAND_SCRIPT_PATH" + assert_success "Command that does something in $TEST_GO_ROOTDIR" } @test "$SUITE: parse description from command script" { - _GO_CMD='test-go' - _GO_ROOTDIR='/foo/bar' - COLUMNS=40 - _@go.command_description "$TEST_COMMAND_SCRIPT_PATH" + create_script_with_description + COLUMNS=40 run test-go _@go.command_description "$TEST_COMMAND_SCRIPT_PATH" - local expected='Command that does something in /foo/bar + local expected='Command that does something in TEST_GO_ROOTDIR Usage: test-go test-command [args...] @@ -125,13 +128,12 @@ Indented lines that look like tables (there are two or more adjacent spaces afte xyzzy all work and no play makes mike a Dull Boy. plugh all werk and no play makes - mike a dull Boy -' + mike a dull Boy' # With this test, I learned that you _do_ have to quote strings even inside of # '[[' and ']]' in case the strings themselves contain '[' or ']', as with # '[args...]' above. - assert_equal "$expected" "$__go_cmd_desc" 'command description' + assert_success "${expected/TEST_GO_ROOTDIR/$TEST_GO_ROOTDIR}" } @test "$SUITE: format subcommand description" { @@ -144,15 +146,9 @@ Indented lines that look like tables (there are two or more adjacent spaces afte echo The command script starts now. ' - local _GO_CMD='test-go' - local expected=("Leaf command that does something in $_GO_ROOTDIR" - '' - "Usage: $_GO_CMD root-command node-command leaf-command [args...]" - '') - local __go_cmd_desc - _@go.command_description \ + run test-go _@go.command_description \ "$TEST_GO_SCRIPTS_DIR/root-command.d/node-command.d/leaf-command" - - local IFS=$'\n' - assert_equal "${expected[*]}" "$__go_cmd_desc" + assert_success "Leaf command that does something in $TEST_GO_ROOTDIR" \ + '' \ + "Usage: test-go root-command node-command leaf-command [args...]" }