From d1f0b85a2c8b44b9de7999a00b7b955ff51621ea Mon Sep 17 00:00:00 2001 From: Jim Myhrberg Date: Sat, 15 Mar 2014 23:41:25 +0000 Subject: [PATCH] Initial commit --- Makefile | 5 ++ README.md | 83 ++++++++++++++++++++++ stub.sh | 105 ++++++++++++++++++++++++++++ test.sh | 34 +++++++++ test/assert.sh | 139 +++++++++++++++++++++++++++++++++++++ test/restore-test.sh | 47 +++++++++++++ test/stub-test.sh | 40 +++++++++++ test/stub_and_echo-test.sh | 41 +++++++++++ test/stub_and_eval-test.sh | 34 +++++++++ test/test-helper.sh | 9 +++ 10 files changed, 537 insertions(+) create mode 100644 Makefile create mode 100644 README.md create mode 100644 stub.sh create mode 100755 test.sh create mode 100644 test/assert.sh create mode 100755 test/restore-test.sh create mode 100755 test/stub-test.sh create mode 100755 test/stub_and_echo-test.sh create mode 100755 test/stub_and_eval-test.sh create mode 100644 test/test-helper.sh diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f17f19f --- /dev/null +++ b/Makefile @@ -0,0 +1,5 @@ +test: + ./test.sh + +.SILENT: +.PHONY: test diff --git a/README.md b/README.md new file mode 100644 index 0000000..315d2e8 --- /dev/null +++ b/README.md @@ -0,0 +1,83 @@ +# stub.sh + +A set of stubbing helpers for use in bash script tests. Supports stubbing and +restoring both binaries and bash functions. + + +## Usage + +Put `stub.sh` in your project, source it into your tests. For detailed +examples, why not have a look at the [tests][] for stub.sh itself? + +[tests]: https://github.com/jimeh/stub.sh/tree/master/test + +### Examples + +Stubbing binaries: + +```bash +source "stub.sh" +uname #=> Darwin +stub uname +uname #=> uname stub: +uname -r #=> uname stub: -r +restore uname +uname #=> Darwin +``` + +Stubbing bash functions: + +```bash +source "stub.sh" +my-name-is() { echo "My name is $@."; } +my-name-is Edward #=> Edward +stub my-name-is +my-name-is Edward #=> my-name-is stub: Edward +restore my-name-is +my-name-is Edward #=> Edward +``` + + +## Function Reference + +- `stub`: Basic stubbing command. Will echo a default message to STDOUT. + Arguments: + - `$1`: Name of command to stub + - `$2`: When set to "STDERR", echo to STDERR instead of STDOUT. +- `stub_and_echo`: Stub given command and echo a custom string to STDOUT. + Arguments: + - `$1`: Name of command to stub. + - `$2`: String to echo when stub is called. + - `$3`: When set to "STDERR", echo to STDERR instead of STDOUT. +- `stub_and_eval`: Stub given command and execute custom commands via eval. + Arguments: + - `$1`: Name of command to stub. + - `$2`: String to eval when stub is called. +- `restore`: Restores use of original binary/function that was stubbed. + Arguments: + - `$1`: Name of command to restore. + + +## License + +(The MIT license) + +Copyright (c) 2014 Jim Myhrberg. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/stub.sh b/stub.sh new file mode 100644 index 0000000..f96dd52 --- /dev/null +++ b/stub.sh @@ -0,0 +1,105 @@ +# !/usr/bin/env bash +# stub.sh 1.0.0 - stubbing helpers for simplifying bash script tests. +# Copyright (c) 2014 Jim Myhrberg. +# +# https://github.com/jimeh/stub.sh +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# + + +# Stub given command, echoing a default stub message. +# +# Arguments: +# - $1: Name of command to stub. +# - $2: When set to "STDERR", echo to STDERR instead of STDOUT. +# +# +stub() { + stub_and_echo "$1" "$1 stub: \$@" "$2" +} + + +# Stub given command, and echo given string. +# +# Arguments: +# - $1: Name of command to stub. +# - $2: String to echo when stub is called. +# - $3: When set to "STDERR", echo to STDERR instead of STDOUT. +# +stub_and_echo() { + if [ "$3" == "STDERR" ]; then local redirect=" 1>&2"; fi + stub_and_eval "$1" "echo \"$2\"$redirect" +} + + +# Stub given command, and executes given string with eval. +# +# Arguments: +# - $1: Name of command to stub. +# - $2: String to eval when stub is called. +# +stub_and_eval() { + local cmd="$1" + + # If stubbing a function, store non-stubbed copy of it required for restore. + if [ -n "$(command -v "$cmd")" ]; then + if [[ "$(type "$cmd" | head -1)" == *"is a function" ]]; then + local source="$(type "$cmd" | tail -n +2)" + source="${source/$cmd/non_stubbed_${cmd}}" + eval "$source" + fi + fi + + # Use a function to keep track of if the command is stubbed, as variable + # names doesn't support the "-" character, while function names do. + eval "$(echo -e "${cmd}_is_stubbed() {\n return 0\n}")" + + # Create the stub. + eval "$(echo -e "${cmd}() {\n $2\n}")" +} + + +# Restore the original command/function that was stubbed. +# +# Arguments: +# - $1: Name of command to restore. +# +restore() { + local cmd="$1" + + # Don't do anything if the command isn't currently stubbed. + if [[ "$(type "${cmd}_is_stubbed" 2>&1)" == *"not found"* ]]; then + return 0; + fi + + # Remove stub functions. + unset -f "${cmd}_is_stubbed" + unset -f "$cmd" + + # If stub was for a function, restore the original function. + if type "non_stubbed_${cmd}" &>/dev/null; then + if [[ "$(type "non_stubbed_${cmd}" | head -1)" == *"is a function" ]]; then + local source="$(type "non_stubbed_$cmd" | tail -n +2)" + source="${source/non_stubbed_${cmd}/$cmd}" + eval "$source" + unset -f "non_stubbed_${cmd}" + fi + fi +} diff --git a/test.sh b/test.sh new file mode 100755 index 0000000..656e3ac --- /dev/null +++ b/test.sh @@ -0,0 +1,34 @@ +#! /usr/bin/env bash + +resolve_link() { + $(type -p greadlink readlink | head -1) $1 +} + +abs_dirname() { + local cwd="$(pwd)" + local path="$1" + + while [ -n "$path" ]; do + cd "${path%/*}" + local name="${path##*/}" + path="$(resolve_link "$name" || true)" + done + + pwd + cd "$cwd" +} + + +testdir="$(abs_dirname "$0")/test" +testfiles="$(find "$testdir" -name "*-test.sh")" + +RET=0 +for testfile in $testfiles; do + echo "" + echo "running: ${testfile/#$(dirname "$testdir")\//}" + cd "$(dirname "$testfile")" + "$testfile" + if [ "$?" != "0" ]; then RET=1; fi +done +echo "" +exit $RET diff --git a/test/assert.sh b/test/assert.sh new file mode 100644 index 0000000..f3d03d8 --- /dev/null +++ b/test/assert.sh @@ -0,0 +1,139 @@ +#!/bin/bash +# assert.sh 1.0 - bash unit testing framework +# Copyright (C) 2009, 2010, 2011, 2012 Robert Lehmann +# +# http://github.com/lehmannro/assert.sh +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +export DISCOVERONLY=${DISCOVERONLY:-} +export DEBUG=${DEBUG:-} +export STOP=${STOP:-} +export INVARIANT=${INVARIANT:-} + +args="$(getopt -n "$0" -l verbose,help,stop,discover,invariant vhxdi $*)" \ +|| exit -1 +for arg in $args; do + case "$arg" in + -h) + echo "$0 [-vxid] [--verbose] [--stop] [--invariant] [--discover]" + echo "`sed 's/./ /g' <<< "$0"` [-h] [--help]" + exit 0;; + --help) + cat < [stdin] + (( tests_ran++ )) + [[ -n "$DISCOVERONLY" ]] && return + # printf required for formatting + printf -v expected "x${2:-}" # x required to overwrite older results + result="$(eval 2>/dev/null $1 <<< ${3:-})" + # Note: $expected is already decorated + if [[ "x$result" == "$expected" ]]; then + [[ -n "$DEBUG" ]] && echo -n . + return + fi + [[ -n "$DEBUG" ]] && echo -n X + result="$(sed -e :a -e '$!N;s/\n/\\n/;ta' <<< "$result")" + [[ -z "$result" ]] && result="nothing" || result="\"$result\"" + [[ -z "$2" ]] && expected="nothing" || expected="\"$2\"" + failure="expected $expected${_indent}got $result" + report="test #$tests_ran \"$1${3:+ <<< $3}\" failed:${_indent}$failure" + tests_errors[$tests_failed]="$report" + (( tests_failed++ )) + if [[ -n "$STOP" ]]; then + [[ -n "$DEBUG" ]] && echo + echo "$report" + exit 1 + fi +} + +assert_raises() { + # assert_raises [stdin] + (( tests_ran++ )) + [[ -n "$DISCOVERONLY" ]] && return + (eval $1 <<< ${3:-}) > /dev/null 2>&1 + status=$? + expected=${2:-0} + if [[ "$status" -eq "$expected" ]]; then + [[ -n "$DEBUG" ]] && echo -n . + return + fi + [[ -n "$DEBUG" ]] && echo -n X + failure="program terminated with code $status instead of $expected" + report="test #$tests_ran \"$1${3:+ <<< $3}\" failed:${_indent}$failure" + tests_errors[$tests_failed]="$report" + (( tests_failed++ )) + if [[ -n "$STOP" ]]; then + [[ -n "$DEBUG" ]] && echo + echo "$report" + exit 1 + fi +} + +_assert_reset diff --git a/test/restore-test.sh b/test/restore-test.sh new file mode 100755 index 0000000..d97c6ab --- /dev/null +++ b/test/restore-test.sh @@ -0,0 +1,47 @@ +#! /usr/bin/env bash +source "test-helper.sh" + +# +# restore() tests. +# + + +# Stubbing and restoring a bash function. +my-name-is() { echo "My name is $@."; } +assert "my-name-is Edward Elric" "My name is Edward Elric." + +stub "my-name-is" +assert "my-name-is Edward Elric" "my-name-is stub: Edward Elric" + +restore "my-name-is" +assert "my-name-is Edward Elric" "My name is Edward Elric." + + +# Stubbing and restoring a executable file. +actual_uname="$(uname)" +stub "uname" +assert "uname" "uname stub: " +assert "uname -a" "uname stub: -a" + +restore "uname" +assert "uname" "$actual_uname" + + +# Stubbing and restoring something that doesn't exist. +assert_raises "cowabunga-dude" 127 +stub "cowabunga-dude" +assert_raises "cowabunga-dude" 0 +restore "cowabunga-dude" +assert_raises "cowabunga-dude" 127 + + +# Attempting to restore a function that wasn't stubbed. +my-name-is() { echo "My name is $@."; } +assert "my-name-is Edward Elric" "My name is Edward Elric." + +restore "my-name-is" +assert "my-name-is Edward Elric" "My name is Edward Elric." + + +# End of tests. +assert_end "restore()" diff --git a/test/stub-test.sh b/test/stub-test.sh new file mode 100755 index 0000000..cc08a2c --- /dev/null +++ b/test/stub-test.sh @@ -0,0 +1,40 @@ +#! /usr/bin/env bash +source "test-helper.sh" + +# +# stub() tests. +# + + +# Stubbing a bash function. +my-name-is() { echo "My name is $@."; } +assert "my-name-is Edward Elric" "My name is Edward Elric." + +stub "my-name-is" +assert "my-name-is" "my-name-is stub: " +assert "my-name-is Edward" "my-name-is stub: Edward" +assert "my-name-is Edward Elric" "my-name-is stub: Edward Elric" +unset -f my-name-is + + +# Stubbing a executable file. +stub "uname" +assert "uname" "uname stub: " +assert "uname -h" "uname stub: -h" +unset -f uname + + +# Redirect stub output to STDERR. +my-name-is() { echo "My name is $@."; } +stub "my-name-is" STDERR +assert "my-name-is Edward" "" +assert "my-name-is Edward 2>&1" "my-name-is stub: Edward" + + +# Stubbing something that doesn't exist. +stub "cowabunga-dude" +assert "cowabunga-dude yeah dude" "cowabunga-dude stub: yeah dude" + + +# End of tests. +assert_end "stub()" diff --git a/test/stub_and_echo-test.sh b/test/stub_and_echo-test.sh new file mode 100755 index 0000000..fe1632a --- /dev/null +++ b/test/stub_and_echo-test.sh @@ -0,0 +1,41 @@ +#! /usr/bin/env bash +source "test-helper.sh" + +# +# stub_and_echo() tests. +# + + +# Stubbing a bash function. +my-name-is() { echo "My name is $@."; } +assert "my-name-is Edward Elric" "My name is Edward Elric." + +stub_and_echo "my-name-is" "Hohenheim" +assert "my-name-is" "Hohenheim" +assert "my-name-is Edward" "Hohenheim" +assert "my-name-is Edward Elric" "Hohenheim" +unset -f my-name-is + + +# Stubbing a executable file. +stub_and_echo "uname" "State Alchemist" +assert "uname" "State Alchemist" +assert "uname -h" "State Alchemist" +unset -f uname + + +# Redirect stub output to STDERR. +my-name-is() { echo "My name is $@."; } +stub_and_echo "my-name-is" "Hohenheim" STDERR +assert "my-name-is Edward" "" +assert "my-name-is Edward 2>&1" "Hohenheim" + + +# Stubbing something that doesn't exist. +stub_and_echo "cowabunga-dude" "Surf's up dude :D" +assert "cowabunga-dude" "Surf's up dude :D" +assert "cowabunga-dude yeah dude" "Surf's up dude :D" + + +# End of tests. +assert_end "stub_and_echo()" diff --git a/test/stub_and_eval-test.sh b/test/stub_and_eval-test.sh new file mode 100755 index 0000000..e1cba97 --- /dev/null +++ b/test/stub_and_eval-test.sh @@ -0,0 +1,34 @@ +#! /usr/bin/env bash +source "test-helper.sh" + +# +# stub_and_eval() tests. +# + + +# Stubbing a bash function. +my-name-is() { echo "My name is $@."; } +assert "my-name-is Edward Elric" "My name is Edward Elric." + +stub_and_eval "my-name-is" "date +%Y" +assert "my-name-is" "$(date +%Y)" +assert "my-name-is Edward" "$(date +%Y)" +assert "my-name-is Edward Elric" "$(date +%Y)" +unset -f my-name-is + + +# Stubbing a executable file. +stub_and_eval "uname" "date +%Y" +assert "uname" "$(date +%Y)" +assert "uname -h" "$(date +%Y)" +unset -f uname + + +# Stubbing something that doesn't exist. +stub_and_eval "cowabunga-dude" "date +%Y" +assert "cowabunga-dude" "$(date +%Y)" +assert "cowabunga-dude yeah dude" "$(date +%Y)" + + +# End of tests. +assert_end "stub_and_eval()" diff --git a/test/test-helper.sh b/test/test-helper.sh new file mode 100644 index 0000000..a13bf48 --- /dev/null +++ b/test/test-helper.sh @@ -0,0 +1,9 @@ +[ -n "$TEST_DEBUG" ] && set -x + +# Set testroot variable. +testroot="$(dirname "$BASH_SOURCE")" + +# Include assert.sh testing library. +source "$testroot/assert.sh" +source "$testroot/../stub.sh" +