diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..3748f77 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,4 @@ +language: bash + +script: + - make diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2bd1bad --- /dev/null +++ b/Makefile @@ -0,0 +1,5 @@ +TESTS ?= $(wildcard tests/*.sh) + +include Makefile.test + + diff --git a/Makefile.test b/Makefile.test new file mode 100644 index 0000000..adb9548 --- /dev/null +++ b/Makefile.test @@ -0,0 +1,102 @@ +# FROM: https://github.com/box/Makefile.test +# LICENCE: Apache2 +# +# Makefile that has a convenient check target. +# It can be included from another Makefile that only has a TESTS variable +# defined like this +# +# TESTS ?= +# +# Runs the specified test executables. Prepends the test's name to each test's output +# and gives a nice summary at the end of test execution about passed failed +# tests. + +# Only bash is supported +SHELL := /bin/bash + +THIS_FILE := $(realpath $(lastword $(MAKEFILE_LIST))) +# The directory where Makefile.test (this file) resides +THIS_FILE_DIR := $(shell dirname $(THIS_FILE)) + +# FIRST_MAKEFILE may be passed from parent make to child make. If it is not +# absent, do not overwrite it. +FIRST_MAKEFILE ?= $(realpath $(firstword $(MAKEFILE_LIST))) +export FIRST_MAKEFILE + +# The directory where the Makefile, that is invoked from the command line, +# resides. That makefile would define the TESTS variable. We assume that the +# binaries defined in the TESTS variable also reside in the directory as +# the Makefile. The generated intermediate files will also go to this directory. +FIRST_MAKEFILE_DIR ?= $(shell dirname $(FIRST_MAKEFILE)) +export FIRST_MAKEFILE_DIR + +# So that the child makefiles can see the same TESTS variable. +export TESTS + +failedTestsName := .makefile_test_failed_tests +executedTestsName := .makefile_test_executed_tests +TEST_TARGETS := $(TESTS:%=TARGET_FOR_%) +export TEST_TARGETS + +# If the tests need a different environment one can append to this variable. +TEST_ENVIRONMENT = PYTHONPATH=$(THIS_FILE_DIR):$$PYTHONPATH PATH=$(THIS_FILE_DIR):$$PATH + +# TODO: Only write to intermediate files, if they exist already. +# https://unix.stackexchange.com/q/405497/212862 +# There is still a race condition here. Maybe we should use sed for appending. +define RUN_ONE_TEST +TARGET_FOR_$(1): $$(FIRST_MAKEFILE_DIR)/$(1) + +@export PATH=$$$$(pwd):$$$$PATH; \ + if [ -e $$(FIRST_MAKEFILE_DIR)/$$(executedTestsName) ]; then \ + echo $$< >> $$(FIRST_MAKEFILE_DIR)/$$(executedTestsName); \ + fi; \ + $$(TEST_ENVIRONMENT) $$< 2>&1 | sed "s/^/ [$$$$(basename $$<)] /"; test $$$${PIPESTATUS[0]} -eq 0; \ + if [ $$$$? -eq 0 ]; then \ + echo " PASSED: $$$$(basename $$<)"; \ + else \ + echo " FAILED: $$$$(basename $$<)"; \ + if [ -e $$(FIRST_MAKEFILE_DIR)/$$(failedTestsName) ]; then \ + echo $$< >> $$(FIRST_MAKEFILE_DIR)/$$(failedTestsName); \ + fi; \ + fi; +endef + +# Build the above rule to run one test, for all tests. +$(foreach currtest,$(TESTS),$(eval $(call RUN_ONE_TEST,$(currtest)))) + +# execute the tests and look at the generated temp files afterwards. +actualCheck: $(TEST_TARGETS) + +@failed_tests=$$(cat $(FIRST_MAKEFILE_DIR)/$(failedTestsName) 2> /dev/null | wc -l;); \ + executed_tests=$$(cat $(FIRST_MAKEFILE_DIR)/$(executedTestsName) 2> /dev/null | wc -l;); \ + if [ $$failed_tests -ne 0 -a $$executed_tests -ne 0 ]; then \ + echo ---------------------------------; \ + echo "Failed $$failed_tests out of $$executed_tests tests"; \ + echo ---------------------------------; \ + elif [ $$failed_tests -eq 0 ]; then \ + echo ---------------------------------; \ + echo "All $$executed_tests tests passed"; \ + echo ---------------------------------; \ + fi; \ + exit $$failed_tests; + +# A commonly used bash command to clean intermediate files. Instead of writing +# it every time re-use this variable. +RM_INTERMEDIATE_FILES := rm -f $(FIRST_MAKEFILE_DIR)/$(failedTestsName) $(FIRST_MAKEFILE_DIR)/$(executedTestsName) + +# At the start of the make, we want to start with empty intermediate files. +TRUNCATE_INTERMEDIATE_FILES := cat /dev/null > $(FIRST_MAKEFILE_DIR)/$(failedTestsName) && cat /dev/null > $(FIRST_MAKEFILE_DIR)/$(executedTestsName) + +# With trap make sure the clean step is always executed before and after the +# tests run time. Do not leave residual files in the repo. +check: + +@trap "code=\$$?; \ + $(RM_INTERMEDIATE_FILES); \ + exit \$${code};" EXIT; \ + $(TRUNCATE_INTERMEDIATE_FILES); \ + $(MAKE) -f $(THIS_FILE) actualCheck; + +all: check + +.PHONY: all check preCheck actualCheck $(TEST_TARGETS) +.DEFAULT_GOAL := all + diff --git a/README.md b/README.md index aab20d3..6e9cc5d 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,14 @@ +[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg)](LICENSE.md) +[![Build Status](https://travis-ci.com/keithy/bash-spec-2.svg?branch=master)](https://travis-ci.com/keithy/bash-spec-2) +[![GitHub issues](https://img.shields.io/github/issues/keithy/bash-spec-2.svg)](https://github.com/keithy/bash-spec-2/issues) +[![Latest Version](https://img.shields.io/github/release/keithy/bash-spec-2.svg)](https://github.com/keithy/bash-spec-2/releases) + New for 2.1 =========== #### Supporting other coding styles/formats ``` old format: describe "title" "$( ... )" -alt format: describe "title" && { ... } (most readable but predictably fails one test) +alt format: describe "title" && { ... } (most readable but variables are not locally scoped) alt format2: describe "title" && ( ... ) (compromise?) ``` #### Assertions on expressions @@ -13,7 +18,7 @@ should_succeed [[ some_expression ]] should_fail ``` -#### Cleaner support for arrays - pass by reference +#### Cleaner support for arrays and vars - pass by reference ``` expect_var varname to_be 5 @@ -24,6 +29,8 @@ expect_array arrayname to_contain 5 http://redsymbol.net/articles/unofficial-bash-strict-mode/ +#### Travis-CI + bash-spec ========= diff --git a/bash-spec-baby.sh b/bash-spec-baby.sh index 05f2611..83186c8 100644 --- a/bash-spec-baby.sh +++ b/bash-spec-baby.sh @@ -1,16 +1,17 @@ #!/usr/bin/env bash #================================================================================== -# Minimal testing framework for bash scripts. -# -# [[ some_expression ]] -# should_succeed -# [[ some_expression ]] -# should_fail -# -# bash-spec Author: Dave Nicolette -# Date: 29 Jul 2014 -# Modified by REA Group 2014 -# bash-spec-baby by keithy@consultant.com 03/2019 +## Minimal testing framework for bash scripts. +## usage: +## +## [[ some_expression ]] +## should_succeed +## [[ some_expression ]] +## should_fail +## +## bash-spec Author: Dave Nicolette +## Date: 29 Jul 2014 +## Modified by REA Group 2014 +## bash-spec-baby by keithy@consultant.com 03/2019 #================================================================================== # http://redsymbol.net/articles/unofficial-bash-strict-mode/ @@ -29,7 +30,7 @@ exec > "$result_file" function show_help { exec 1>&6 6>&- rm -f -- "$result_file" - grep "^##" $BASH_SOURCE | sed 's/^##.//' + grep "^##" $BASH_SOURCE | sed 's/^##//' } function output_results { diff --git a/bash-spec.sh b/bash-spec.sh index 7db8780..2f11dd2 100644 --- a/bash-spec.sh +++ b/bash-spec.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash ##================================================================================== ## BDD-style testing framework for bash scripts. -##. +## ## expect variable [not] to_be value Compare scalar values for equality ## expect variable [not] to_match regex Regex match ## expect array [not] to_contain value Look for a value in an array @@ -14,7 +14,7 @@ ## should_succeed ## [[ some_expression ]] ## should_fail -##. +## # Original Author: Dave Nicolette # Version 1.0.0 29 Jul 2014 # Release @@ -43,7 +43,7 @@ exec > "$result_file" function show_help { exec 1>&6 6>&- rm -f -- "$result_file" - grep "^##" $BASH_SOURCE | sed 's/^##.//' + grep "^##" $BASH_SOURCE | sed 's/^##//' } function output_results { @@ -246,8 +246,8 @@ eval set -- "$TEMP" while true; do case "$1" in h | help ) show_help; exit 0 ;; - -- ) shift ;; - * ) shift; break ;; + -- ) break ;; + * ) break ;; esac done diff --git a/test_bash-spec-altfmt.sh b/fails/test_bash-spec-altfmt.sh similarity index 99% rename from test_bash-spec-altfmt.sh rename to fails/test_bash-spec-altfmt.sh index 7da4ee6..dec2a76 100755 --- a/test_bash-spec-altfmt.sh +++ b/fails/test_bash-spec-altfmt.sh @@ -2,7 +2,7 @@ DIR="${BASH_SOURCE%/*}" if [[ ! -d "$DIR" ]]; then DIR="$PWD"; fi -. "$DIR/bash-spec.sh" +. "$DIR/../bash-spec.sh" describe "The bash version test" && { diff --git a/test_counters.sh b/fails/test_counters.sh similarity index 71% rename from test_counters.sh rename to fails/test_counters.sh index 1db3843..f84a9ac 100755 --- a/test_counters.sh +++ b/fails/test_counters.sh @@ -1,5 +1,8 @@ #! /bin/bash -. ./bash-spec.sh + +DIR="${BASH_SOURCE%/*}" +if [[ ! -d "$DIR" ]]; then DIR="$PWD"; fi +. "$DIR/../bash-spec.sh" describe "A simple test" "$( diff --git a/tests/README.md b/tests/README.md new file mode 100755 index 0000000..2db8580 --- /dev/null +++ b/tests/README.md @@ -0,0 +1,2 @@ +These files run the tests for CI, which PASS expected failures + diff --git a/tests/test_bash-altfmt.expected_fail.sh b/tests/test_bash-altfmt.expected_fail.sh new file mode 100755 index 0000000..fee521f --- /dev/null +++ b/tests/test_bash-altfmt.expected_fail.sh @@ -0,0 +1,4 @@ +# This is run in the directory context of the Makefile +./fails/test_bash-spec-altfmt.sh | grep "1 FAILED" + + diff --git a/test_bash-spec-altfmt2.sh b/tests/test_bash-spec-altfmt2.sh similarity index 99% rename from test_bash-spec-altfmt2.sh rename to tests/test_bash-spec-altfmt2.sh index 2963555..3544ae7 100755 --- a/test_bash-spec-altfmt2.sh +++ b/tests/test_bash-spec-altfmt2.sh @@ -2,7 +2,7 @@ DIR="${BASH_SOURCE%/*}" if [[ ! -d "$DIR" ]]; then DIR="$PWD"; fi -. "$DIR/bash-spec.sh" +. "$DIR/../bash-spec.sh" describe "The bash version test" && ( diff --git a/test_bash-spec.sh b/tests/test_bash-spec.sh similarity index 99% rename from test_bash-spec.sh rename to tests/test_bash-spec.sh index 8402a51..40a1ceb 100755 --- a/test_bash-spec.sh +++ b/tests/test_bash-spec.sh @@ -2,7 +2,7 @@ DIR="${BASH_SOURCE%/*}" if [[ ! -d "$DIR" ]]; then DIR="$PWD"; fi -. "$DIR/bash-spec.sh" +. "$DIR/../bash-spec.sh" describe "The bash version test" "$( diff --git a/tests/test_counters.expected_fail.sh b/tests/test_counters.expected_fail.sh new file mode 100755 index 0000000..0c3011a --- /dev/null +++ b/tests/test_counters.expected_fail.sh @@ -0,0 +1,4 @@ +# This is run in the directory context of the Makefile +./fails/test_counters.sh | grep "1 FAILED" + +