Skip to content
Merged
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
15 changes: 12 additions & 3 deletions go-core.bash
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,18 @@ declare _GO_PLUGINS_DIR=
# Directories containing executable plugin scripts.
declare _GO_PLUGINS_PATHS=()

# Directories that are searched for executable command scripts. After they are
# initialized, _GO_PLUGINS_PATHS and _GO_SCRIPTS_DIR will be added.
declare _GO_SEARCH_PATHS=("$_GO_CORE_DIR/libexec")
# Directories that are searched for executable command scripts.
declare _GO_SEARCH_PATHS=()

# Directory to search for command scripts prior to _GO_SEARCH_PATHS.
# Should be an absolute path. Use this for stubbing out scripts during testing
# or debugging, or for experimenting with new implementations of existing
# scripts.
declare _GO_INJECT_SEARCH_PATH="$_GO_INJECT_SEARCH_PATH"

# Directory to search for module scripts first.
# Similar to _GO_INJECT_SEARCH_PATHS above, but for `. "$_GO_USE_MODULES"`.
declare _GO_INJECT_MODULE_PATH="$_GO_INJECT_MODULE_PATH"

# Invokes printf builtin, then folds output to $COLUMNS width
#
Expand Down
6 changes: 5 additions & 1 deletion lib/internal/path
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
#! /bin/bash

if [[ "${#_GO_SEARCH_PATHS[@]}" -eq 1 ]]; then
if [[ "${#_GO_SEARCH_PATHS[@]}" -eq 0 ]]; then
if [[ -n "$_GO_INJECT_SEARCH_PATH" ]]; then
_GO_SEARCH_PATHS+=("$_GO_INJECT_SEARCH_PATH")
fi
_GO_SEARCH_PATHS+=("$_GO_CORE_DIR/libexec")
if [[ -d "$_GO_SCRIPTS_DIR/plugins" ]]; then
_GO_PLUGINS_DIR="$_GO_SCRIPTS_DIR/plugins"
_GO_PLUGINS_PATHS=("$_GO_PLUGINS_DIR" "$_GO_PLUGINS_DIR"/*/bin)
Expand Down
11 changes: 10 additions & 1 deletion lib/internal/use
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
# The precedence for discovering modules is (with examples from the "Directory
# structure" example from README.md):
#
# - the `_GO_INJECT_MODULE_PATH` directory
# - the `lib/` directory of the framework (`scripts/go-script-bash/lib`)
# - the `lib/` directory of installed plugins (`scripts/plugins/*/lib`)
# - the `lib/` directory in your project scripts directory (`scripts/lib`)
Expand Down Expand Up @@ -80,7 +81,15 @@ for __go_module_name in "$@"; do

# Prevent self- and circular importing by registering name before sourcing.
_GO_IMPORTED_MODULES+=("$__go_module_name")
__go_module_file="$_GO_CORE_DIR/lib/$__go_module_name"
__go_module_file=''

if [[ -n "$_GO_INJECT_MODULE_PATH" ]]; then
__go_module_file="$_GO_INJECT_MODULE_PATH/$__go_module_name"
if [[ ! -f "$__go_module_file" ]]; then
__go_module_file=''
fi
fi
__go_module_file="${__go_module_file:-$_GO_CORE_DIR/lib/$__go_module_name}"

if [[ ! -f "$__go_module_file" ]]; then
# Convert <plugin>/<module> to plugins/<plugin>/lib/<module>
Expand Down
57 changes: 25 additions & 32 deletions lib/testing/stubbing
Original file line number Diff line number Diff line change
@@ -1,43 +1,36 @@
#! /bin/bash
#
# Helper functions for creating test stubs for core framework elements

# Replace a module in `_GO_CORE_DIR/lib` with a stub implementation
# Helper functions for creating test stubs for command and module scripts
#
# THIS IS POTENTIALLY DANGEROUS and you MUST call
# `@go.restore_stubbed_core_modules` in your `teardown` function if you use it!
# Useful when you need to model command script or module behavior, but don't
# want it to do real work or want to avoid a potentially complicated test setup.
#
# Useful when you need to model core module behavior, but don't want it to do
# real work or want to avoid a potentially complicated test setup.
# This will assign and export values for `_GO_INJECT_SEARCH_PATH` and
# `_GO_INJECT_MODULE_PATH`.
#
# Arguments:
# module_name: Name of the module to stub from `_GO_CORE_DIR/lib`
# ...: Lines comprising the stubbed module implementation
@go.create_core_module_stub() {
local module_path="$_GO_CORE_DIR/lib/$1"
shift
# You must import `_GO_CORE_DIR/lib/testing/environment` before importing this
# script, and make sure to call `@go.remove_test_go_rootdir` from `teardown`.

if [[ ! -f "$module_path" ]]; then
echo "No such core module: $module_path" >&2
return 1
fi
export _GO_INJECT_SEARCH_PATH="$TEST_GO_ROOTDIR/test-bin"
export _GO_INJECT_MODULE_PATH="$TEST_GO_ROOTDIR/test-lib"

cp "$module_path"{,.stubbed}
echo '#! /bin/bash' > "$module_path"
printf '%s\n' "$@" >>"$module_path"
chmod 600 "$module_path"
# Creates a stub command script implementation in `_GO_INJECT_SEARCH_PATH`
#
# Arguments:
# module_name: Name of the command script to stub
# ...: Lines comprising the stubbed command script implementation
@go.create_command_script_test_stub() {
local script_path="$_GO_INJECT_SEARCH_PATH/$1"
create_bats_test_script "${script_path#$BATS_TEST_ROOTDIR/}" "${@:2}"
}

# Restore all core modules stubbed by `@go.create_core_module_stub`
# Creates a stub module implementation in `_GO_INJECT_MODULE_PATH`
#
# YOU MUST CALL THIS FROM TEARDOWN IF YOU USE `@go.create_core_module_stub`!
@go.restore_stubbed_core_modules() {
local module
local stubbed_modules=("$_GO_CORE_DIR/lib"/*.stubbed)

if [[ "${stubbed_modules#$_GO_CORE_DIR/lib/}" != '*.stubbed' ]]; then
for module in "${stubbed_modules[@]}"; do
mv "$module" "${module%.stubbed}"
done
fi
# Arguments:
# module_name: Name of the module to stub
# ...: Lines comprising the stubbed module implementation
@go.create_module_test_stub() {
local module_path="$_GO_INJECT_MODULE_PATH/$1"
create_bats_test_script "${module_path#$BATS_TEST_ROOTDIR/}" "${@:2}"
chmod 600 "$module_path"
}
27 changes: 27 additions & 0 deletions tests/core/inject-paths.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#! /usr/bin/env bats

load ../environment

setup() {
test_filter
}

teardown() {
@go.remove_test_go_rootdir
}

@test "$SUITE: use _GO_INJECT_SEARCH_PATH to stub out builtin command" {
@go.create_test_go_script '@go "$@"'
create_bats_test_script 'test/help' 'printf "INJECTED\n"'

_GO_INJECT_SEARCH_PATH="$BATS_TEST_ROOTDIR/test" run "$TEST_GO_SCRIPT" help
assert_success 'INJECTED'
}

@test "$SUITE: use _GO_INJECT_MODULE_PATH to stub out builtin module" {
@go.create_test_go_script '. "$_GO_USE_MODULES" "$@"'
create_bats_test_script 'test/log' 'printf "INJECTED\n"'

_GO_INJECT_MODULE_PATH="$BATS_TEST_ROOTDIR/test" run "$TEST_GO_SCRIPT" log
assert_success 'INJECTED'
}
3 changes: 1 addition & 2 deletions tests/test.bats
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ load environment
load "$_GO_CORE_DIR/lib/testing/stubbing"

teardown() {
@go.restore_stubbed_core_modules
@go.remove_test_go_rootdir
}

Expand Down Expand Up @@ -104,7 +103,7 @@ write_bats_dummy_stub_kcov_lib_and_copy_test_script() {
create_bats_test_script "tests/bats/libexec/bats"

# Stub the kcov lib to assert it's called correctly.
@go.create_core_module_stub 'kcov-ubuntu' \
@go.create_module_test_stub 'kcov-ubuntu' \
"run_kcov() { IFS=\$'\n'; echo \"\$*\"; }"

if [[ ! -d "$TEST_GO_SCRIPTS_DIR" ]]; then
Expand Down
33 changes: 10 additions & 23 deletions tests/testing/stubbing.bats
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,16 @@ teardown() {
@go.remove_test_go_rootdir
}

@test "$SUITE: create_core_module_stub and restore_stubbed_core_modules" {
[ -e "$_GO_CORE_DIR/lib/log" ]
[ ! -e "$_GO_CORE_DIR/lib/log.stubbed" ]
@go.create_core_module_stub 'log' 'echo Hello, World!'
[ -e "$_GO_CORE_DIR/lib/log.stubbed" ]
[ -e "$_GO_CORE_DIR/lib/log" ]

@go.create_test_go_script '. "$_GO_USE_MODULES" log'
run "$TEST_GO_SCRIPT"

@go.restore_stubbed_core_modules
[ ! -e "$_GO_CORE_DIR/lib/log.stubbed" ]
[ -e "$_GO_CORE_DIR/lib/log" ]
assert_success 'Hello, World!'
}

@test "$SUITE: restore_stubbed_core_modules does nothing if no stubs exist" {
run @go.restore_stubbed_core_modules
assert_success ''
@test "$SUITE: create_command_script_test_stub to stub out builtin command" {
@go.create_test_go_script '@go "$@"'
@go.create_command_script_test_stub 'help' 'printf "INJECTED\n"'
run "$TEST_GO_SCRIPT" help
assert_success 'INJECTED'
}

@test "$SUITE: create_core_module_stub aborts if module unknown" {
[ ! -e "$_GO_CORE_DIR/lib/foobar" ]
run @go.create_core_module_stub 'foobar' 'echo Hello, World!'
assert_failure "No such core module: $_GO_CORE_DIR/lib/foobar"
@test "$SUITE: create_module_test_stub to stub out builtin module" {
@go.create_test_go_script '. "$_GO_USE_MODULES" "$@"'
@go.create_module_test_stub 'log' 'printf "INJECTED\n"'
run "$TEST_GO_SCRIPT" log
assert_success 'INJECTED'
}
35 changes: 23 additions & 12 deletions tests/vars.bats
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ quotify_expected() {
"declare -rx _GO_CORE_VERSION=\"$_GO_CORE_VERSION\""
"declare -x _GO_COVERALLS_URL=\"$_GO_COVERALLS_URL\""
'declare -a _GO_IMPORTED_MODULES=()'
'declare -- _GO_INJECT_MODULE_PATH=""'
'declare -- _GO_INJECT_SEARCH_PATH=""'
"declare -x _GO_KCOV_DIR=\"$_GO_KCOV_DIR\""
'declare -- _GO_PLUGINS_DIR=""'
'declare -a _GO_PLUGINS_PATHS=()'
Expand All @@ -60,33 +62,40 @@ quotify_expected() {

@test "$SUITE: all _GO_* variables for Bash subcommand contain values" {
@go.create_test_command_script 'test-command.d/test-subcommand' \
'. "$_GO_USE_MODULES" "complete" "format"' \
'. "$_GO_USE_MODULES" "module_0" "module_1"' \
'@go vars'

mkdir -p "$TEST_GO_ROOTDIR/lib"
printf '' >"$TEST_GO_ROOTDIR/lib/module_0"
printf '' >"$TEST_GO_ROOTDIR/lib/module_1"

mkdir "$TEST_GO_PLUGINS_DIR"
mkdir "$TEST_GO_PLUGINS_DIR/plugin"{0,1,2}
mkdir "$TEST_GO_PLUGINS_DIR/plugin"{0,1,2}"/bin"

run "$TEST_GO_SCRIPT" test-command test-subcommand foo bar 'baz quux' xyzzy
# Note that defining the `_GO_INJECT_*_PATH` variables before the `run`
# command causes them to be exported.
_GO_INJECT_SEARCH_PATH="$TEST_GO_ROOTDIR/bin" \
_GO_INJECT_MODULE_PATH="$TEST_GO_ROOTDIR/lib" \
run "$TEST_GO_SCRIPT" test-command test-subcommand foo bar 'baz quux' xyzzy
assert_success

local cmd_argv=('[0]="foo"' '[1]="bar"' '[2]="baz quux"' '[3]="xyzzy"')
local plugins_paths=("[0]=\"$TEST_GO_PLUGINS_DIR\""
"[1]=\"$TEST_GO_PLUGINS_DIR/plugin0/bin\""
"[2]=\"$TEST_GO_PLUGINS_DIR/plugin1/bin\""
"[3]=\"$TEST_GO_PLUGINS_DIR/plugin2/bin\"")
local search_paths=("[0]=\"$_GO_CORE_DIR/libexec\""
"[1]=\"$TEST_GO_PLUGINS_DIR\""
"[2]=\"$TEST_GO_PLUGINS_DIR/plugin0/bin\""
"[3]=\"$TEST_GO_PLUGINS_DIR/plugin1/bin\""
"[4]=\"$TEST_GO_PLUGINS_DIR/plugin2/bin\""
"[5]=\"$TEST_GO_SCRIPTS_DIR\"")
local search_paths=("[0]=\"$TEST_GO_ROOTDIR/bin\""
"[1]=\"$_GO_CORE_DIR/libexec\""
"[2]=\"$TEST_GO_PLUGINS_DIR\""
"[3]=\"$TEST_GO_PLUGINS_DIR/plugin0/bin\""
"[4]=\"$TEST_GO_PLUGINS_DIR/plugin1/bin\""
"[5]=\"$TEST_GO_PLUGINS_DIR/plugin2/bin\""
"[6]=\"$TEST_GO_SCRIPTS_DIR\"")

# Note that the `format` module imports `strings` and `validation`.
local expected_modules=('[0]="complete"'
'[1]="format"'
'[2]="strings"'
'[3]="validation"')
local expected_modules=('[0]="module_0"'
'[1]="module_1"')
local expected=("declare -x _GO_BATS_COVERAGE_DIR=\"$_GO_BATS_COVERAGE_DIR\""
"declare -x _GO_BATS_DIR=\"$_GO_BATS_DIR\""
"declare -x _GO_BATS_PATH=\"$_GO_BATS_PATH\""
Expand All @@ -101,6 +110,8 @@ quotify_expected() {
"declare -rx _GO_CORE_VERSION=\"$_GO_CORE_VERSION\""
"declare -x _GO_COVERALLS_URL=\"$_GO_COVERALLS_URL\""
"declare -a _GO_IMPORTED_MODULES=(${expected_modules[*]})"
"declare -x _GO_INJECT_MODULE_PATH=\"$TEST_GO_ROOTDIR/lib\""
"declare -x _GO_INJECT_SEARCH_PATH=\"$TEST_GO_ROOTDIR/bin\""
"declare -x _GO_KCOV_DIR=\"$_GO_KCOV_DIR\""
"declare -- _GO_PLUGINS_DIR=\"$TEST_GO_PLUGINS_DIR\""
"declare -a _GO_PLUGINS_PATHS=(${plugins_paths[*]})"
Expand Down