1#! /bin/sh 2# vim:et:ft=sh:sts=2:sw=2 3# 4# Unit test suite runner. 5# 6# Copyright 2008-2020 Kate Ward. All Rights Reserved. 7# Released under the Apache 2.0 license. 8# 9# Author: kate.ward@forestent.com (Kate Ward) 10# https://github.com/kward/shlib 11# 12# This script runs all the unit tests that can be found, and generates a nice 13# report of the tests. 14# 15### Sample usage: 16# 17# Run all tests for all shells. 18# $ ./test_runner 19# 20# Run all tests for single shell. 21# $ ./test_runner -s /bin/bash 22# 23# Run single test for all shells. 24# $ ./test_runner -t shunit_asserts_test.sh 25# 26# Run single test for single shell. 27# $ ./test_runner -s /bin/bash -t shunit_asserts_test.sh 28# 29### ShellCheck (http://www.shellcheck.net/) 30# Disable source following. 31# shellcheck disable=SC1090,SC1091 32# expr may be antiquated, but it is the only solution in some cases. 33# shellcheck disable=SC2003 34# $() are not fully portable (POSIX != portable). 35# shellcheck disable=SC2006 36 37# Return if test_runner already loaded. 38[ -z "${RUNNER_LOADED:-}" ] || return 0 39RUNNER_LOADED=0 40 41RUNNER_ARGV0=`basename "$0"` 42RUNNER_SHELLS='/bin/sh ash /bin/bash /bin/dash /bin/ksh /bin/mksh /bin/zsh' 43RUNNER_TEST_SUFFIX='_test.sh' 44true; RUNNER_TRUE=$? 45false; RUNNER_FALSE=$? 46 47runner_warn() { echo "runner:WARN $*" >&2; } 48runner_error() { echo "runner:ERROR $*" >&2; } 49runner_fatal() { echo "runner:FATAL $*" >&2; exit 1; } 50 51runner_usage() { 52 echo "usage: ${RUNNER_ARGV0} [-e key=val ...] [-s shell(s)] [-t test(s)]" 53} 54 55_runner_tests() { echo ./*${RUNNER_TEST_SUFFIX} |sed 's#\./##g'; } 56_runner_testName() { 57 # shellcheck disable=SC1117 58 _runner_testName_=`expr "${1:-}" : "\(.*\)${RUNNER_TEST_SUFFIX}"` 59 if [ -n "${_runner_testName_}" ]; then 60 echo "${_runner_testName_}" 61 else 62 echo 'unknown' 63 fi 64 unset _runner_testName_ 65} 66 67main() { 68 # Find and load versions library. 69 for _runner_dir_ in . ${LIB_DIR:-lib}; do 70 if [ -r "${_runner_dir_}/versions" ]; then 71 _runner_lib_dir_="${_runner_dir_}" 72 break 73 fi 74 done 75 [ -n "${_runner_lib_dir_}" ] || runner_fatal 'Unable to find versions library.' 76 . "${_runner_lib_dir_}/versions" || runner_fatal 'Unable to load versions library.' 77 unset _runner_dir_ _runner_lib_dir_ 78 79 # Process command line flags. 80 env='' 81 while getopts 'e:hs:t:' opt; do 82 case ${opt} in 83 e) # set an environment variable 84 key=`expr "${OPTARG}" : '\([^=]*\)='` 85 val=`expr "${OPTARG}" : '[^=]*=\(.*\)'` 86 # shellcheck disable=SC2166 87 if [ -z "${key}" -o -z "${val}" ]; then 88 runner_usage 89 exit 1 90 fi 91 eval "${key}='${val}'" 92 eval "export ${key}" 93 env="${env:+${env} }${key}" 94 ;; 95 h) runner_usage; exit 0 ;; # help output 96 s) shells=${OPTARG} ;; # list of shells to run 97 t) tests=${OPTARG} ;; # list of tests to run 98 *) runner_usage; exit 1 ;; 99 esac 100 done 101 shift "`expr ${OPTIND} - 1`" 102 103 # Fill shells and/or tests. 104 shells=${shells:-${RUNNER_SHELLS}} 105 [ -z "${tests}" ] && tests=`_runner_tests` 106 107 # Error checking. 108 if [ -z "${tests}" ]; then 109 runner_error 'no tests found to run; exiting' 110 exit 1 111 fi 112 113 cat <<EOF 114#------------------------------------------------------------------------------ 115# System data. 116# 117 118$ uname -mprsv 119`uname -mprsv` 120 121OS Name: `versions_osName` 122OS Version: `versions_osVersion` 123 124### Test run info. 125shells: ${shells} 126tests: ${tests} 127EOF 128for key in ${env}; do 129 eval "echo \"${key}=\$${key}\"" 130done 131 132# Run tests. 133runner_passing_=${RUNNER_TRUE} 134for shell in ${shells}; do 135 echo 136 137 cat <<EOF 138 139#------------------------------------------------------------------------------ 140# Running the test suite with ${shell}. 141# 142EOF 143 144 # Check for existence of shell. 145 shell_bin=${shell} 146 shell_name='' 147 shell_present=${RUNNER_FALSE} 148 case ${shell} in 149 ash) 150 shell_bin=`command -v busybox` 151 [ $? -eq "${RUNNER_TRUE}" ] && shell_present="${RUNNER_TRUE}" 152 shell_bin="${shell_bin:+${shell_bin} }ash" 153 shell_name=${shell} 154 ;; 155 *) 156 [ -x "${shell_bin}" ] && shell_present="${RUNNER_TRUE}" 157 shell_name=`basename "${shell}"` 158 ;; 159 esac 160 if [ "${shell_present}" -eq "${RUNNER_FALSE}" ]; then 161 runner_warn "unable to run tests with the ${shell_name} shell" 162 continue 163 fi 164 165 shell_version=`versions_shellVersion "${shell}"` 166 167 echo "shell name: ${shell_name}" 168 echo "shell version: ${shell_version}" 169 170 # Execute the tests. 171 for t in ${tests}; do 172 echo 173 echo "--- Executing the '`_runner_testName "${t}"`' test suite. ---" 174 # ${shell_bin} needs word splitting. 175 # shellcheck disable=SC2086 176 ( exec ${shell_bin} "./${t}" 2>&1; ) 177 shell_passing=$? 178 if [ "${shell_passing}" -ne "${RUNNER_TRUE}" ]; then 179 runner_warn "${shell_bin} not passing" 180 fi 181 test "${runner_passing_}" -eq ${RUNNER_TRUE} -a ${shell_passing} -eq ${RUNNER_TRUE} 182 runner_passing_=$? 183 done 184 done 185 return ${runner_passing_} 186} 187 188# Execute main() if this is run in standalone mode (i.e. not from a unit test). 189if [ -z "${SHUNIT_VERSION}" ]; then 190 main "$@" 191fi 192