#!/usr/bin/env bash # # Run one or more tests. # # Command-line usage to run all tests. # # ./run-tests # # To run only one test, run "tests/test-name". # # Usage within a test as a template. Source run-tests to get functions, export # any necessary variables, then call runTest. # # #!/usr/bin/env bash # cd "${0%/*}" || exit 1 # . ../run-tests # # export template="This is a template" # export expected="This is a template" # runTest # # When used within the test, you control various aspects with environment # variables or functions. # # - The content passed into mo is either the variable "$template" or the output # of the function called template. # - The expected result is either "$expected" or the function called expected. # - The expected return code is "$returnCode" and defaults to 0. # - The arguments to pass to mo is the array "${arguments[@]}" and defaults to (). # # When $MO_DEBUG is set to a non-empty value, the test does not run, but mo is # simply executed directly. This allows for calling mo in the same manner as # the test but does not buffer output nor expect the output to match the # expected. # # When $MO_DEBUG_TEST is set to a non-empty value, the expected and actual # results are shown using "declare -p" to provide an easier time seeing the # differences, especially with whitespace. testCase() { echo "Input: $1" echo "Expected: $2" } indirect() { unset -v "$1" printf -v "$1" '%s' "$2" } getValue() { local name temp len hardSpace name=$2 hardSpace=" " if declare -f "$name" &> /dev/null; then temp=$("$name"; echo -n "$hardSpace") len=$((${#temp} - 1)) if [[ "${temp:$len}" == "$hardSpace" ]]; then temp=${temp:0:$len} fi else temp=${!name} fi local "$1" && indirect "$1" "$temp" } runTest() ( local testTemplate testExpected testActual hardSpace len testReturnCode testFail hardSpace=" " . ../mo getValue testTemplate template getValue testExpected expected if [[ -n "${MO_DEBUG:-}" ]]; then echo -n "$testTemplate" | mo ${arguments[@]+"${arguments[@]}"} 2>&1 return $? fi testActual=$(echo -n "$testTemplate" | mo ${arguments[@]+"${arguments[@]}"} 2>&1; echo -n "$hardSpace$?") testReturnCode=${testActual##*$hardSpace} testActual=${testActual%$hardSpace*} testFail=false if [[ "$testActual" != "$testExpected" ]]; then echo "Failure" echo "Expected:" echo "$testExpected" echo "Actual:" echo "$testActual" if [[ -n "${MO_DEBUG_TEST-}" ]]; then declare -p testExpected # Align the two declare outputs echo -n " " declare -p testActual fi testFail=true fi if [[ "$testReturnCode" != "$returnCode" ]]; then echo "Expected return code $returnCode, but got $testReturnCode" testFail=true fi if [[ "$testFail" == "true" ]]; then return 1 fi return 0 ) runTestFile() ( local file=$1 echo "Test: $file" "$file" ) runTests() ( PASS=0 FAIL=0 if [[ $# -gt 0 ]]; then for TEST in "$@"; do runTestFile "$TEST" && PASS=$((PASS + 1)) || FAIL=$((FAIL + 1)) done else cd "${0%/*}" for TEST in tests/*; do if [[ -f "$TEST" ]]; then runTestFile "$TEST" && PASS=$((PASS + 1)) || FAIL=$((FAIL + 1)) fi done fi echo "" echo "Pass: $PASS" echo "Fail: $FAIL" if [[ $FAIL -gt 0 ]]; then exit 1 fi ) # Clear test related variables template="Template not defined" expected="Expected not defined" returnCode=0 arguments=() # If sourced, load functions. # If executed, perform the actions as expected. if [[ "$0" == "${BASH_SOURCE[0]}" ]] || [[ -z "${BASH_SOURCE[0]}" ]]; then runTests ${@+"${@}"} fi