aboutsummaryrefslogtreecommitdiff
path: root/lib/test.sh
diff options
context:
space:
mode:
Diffstat (limited to 'lib/test.sh')
-rw-r--r--lib/test.sh179
1 files changed, 179 insertions, 0 deletions
diff --git a/lib/test.sh b/lib/test.sh
new file mode 100644
index 0000000..1da470b
--- /dev/null
+++ b/lib/test.sh
@@ -0,0 +1,179 @@
+. "${CLOYSTER_SCRIPT_ROOT}/lib/test/output.sh"
+
+cloyster_test_varname() {
+ if [ -n "${1}" ]; then echo "_cloyster_test_${1}"; else echo "_cloyster_test"; fi
+}
+
+cloyster_test_init() {
+ export _cloyster_test_tests_counter_count=0
+ export _cloyster_test_tests_counter_success=0
+ export _cloyster_test_tests_counter_skipped=0
+ export _cloyster_test_tests_counter_failed=0
+ export _cloyster_test_tests_counter_ignored=0
+ export _cloyster_test_tests_failed=
+ export _cloyster_test_tests_skipped=
+ export _cloyster_test_tests_success=
+ export _cloyster_test_tests_ignored=
+ export _cloyster_test_tests=
+ export _cloyster_test_groups=
+ export _cloyster_test_init=y
+}
+
+cloyster_test_add_shellcheck() {
+ ignore=
+ if ! command -v shellcheck >/dev/null; then ign=".ignore:SHELLCHECK_REQUIRED"; fi
+ _cloyster__test__shellcheck() {
+ shellcheck -e SC1008,SC2239 "${CLOYSTER_COMMAND_FILE}"
+ }
+ cloyster_test_add -a shellcheck shellcheck "_cloyster__test__shellcheck" "${ignore}"
+}
+
+cloyster_test_add() {
+ append=
+ while getopts :a _opt; do case "${_opt}" in
+ a) append=y;;
+ esac; done; shift $((OPTIND - 1))
+ id=$1
+ name=$2
+ fun=$3
+ args=$4
+ if [ -z "${id}" ]; then return 1; fi
+ if [ -z "${name}" ]; then return 1; fi
+ list=$(cloyster_test_varname "tests")
+ var=$(cloyster_test_varname "tests_${id}")
+ if [ -n "$(readvar "$var")" ]; then
+ echo "ERROR: A test '${id}' has already been defined"
+ return 1
+ fi
+ if is_true "${append}"; then _cloyster_test_tests="${name} ${_cloyster_test_tests}"; else _cloyster_test_tests="${_cloyster_test_tests} ${name}"; fi
+ setvar "${var}" "${var}"
+ setvar "${var}__name" "${name}"
+ setvar "${var}__fun" "${fun}"
+ setvar "${var}__args" "${args}"
+}
+
+cloyster_test_run() {
+ cloyster_test_log "${_green}${_bold}⚡⚡⚡ Running cloyster tests for ${_bold}${_yellow}${CLOYSTER_COMMAND}${_rst} ⚡⚡⚡"
+ cloyster_test_log "${_darkgray} (did you knew that #!/bin/sh is twenty years older than emojis ?)${_rst}\n"
+ cloyster_test_add_shellcheck #|| s=$?; _error "Failed to lol $s"; exit 1
+
+ incr() {
+ var="_cloyster_test_tests_counter_${1}"
+ val=$(($(readvar "$var") + 1))
+ setvar "$var" "$val"
+ }
+
+ _echo_block_init
+ all_start_us=$(posix_time_microseconds)
+ for id in ${_cloyster_test_tests}; do
+ incr "count"
+ name=$(readvar "_cloyster_test_tests_${id}__name")
+ args=$(readvar "_cloyster_test_tests_${id}__args")
+ line=$(readvar "_cloyster_test_tests_${id}__line")
+ fun=$(readvar "_cloyster_test_tests_${id}__fun")
+ fun="${fun:-"_test_${id}"}"
+ testlog="${_tmp_dir}/test_${id}.log"
+ _debug "Running test '${name}' line=${line} fun=${fun} args=${args}"
+ refute=; skip=; tags=; status=; precond_status=; ignore=; fatal=;
+ skip_reason=; ignore_reason= fatal_reason=
+ for arg in $args; do
+ tag=$(echo "$arg" | cut -d ":" -f 1)
+ targ=$(echo "$arg" | sed "s/^${tag}://g")
+ case $tag in
+ .refute) refute=y;;
+ .skip) skip=y; skip_reason=${targ:-.skip};;
+ .ignore) ignore=y; ignore_reason=${targ:-.ignore};;
+ .fatal) fatal=y; fatal_reason=${targ:-.fatal};;
+ .tag) if [ -n "${targ}" ]; then tags="${tags} ${targ}"; fi;;
+ *) ;;
+ esac
+ done
+
+## -- Preconditions
+if [ ! "$skip" = "y" ]; then
+ (
+ preconds=$(
+ for arg in $args; do
+ cond=$(echo "$arg" | cut -d ":" -f 2,100)
+ case $arg in
+ .if:*) if $($cond); then miss=y; break; fi;;
+ .not:*) if ! $($cond); then miss=y; break; fi;;
+ *) ;;
+ esac
+ done
+ if [ "${miss}" = "y" ]; then
+ echo "$(tput AF 105)Precondition failed: ${arg}" >> "${testlog}"
+ exit 2
+ fi
+ )
+ precond_status=$?
+ exit $precond_status
+ ) 2>> "${testlog}" >> "${testlog}"
+ precond_status=$?
+else
+ echo "$(tput AF 105)${skip_reason}" >> "${testlog}"
+fi
+
+# -- TEST SUBSHELL
+if [ "${precond_status}" = "0" ] && [ ! "${skip}" = "y" ]; then
+(
+. "${CLOYSTER_SCRIPT_ROOT}/lib/test/asserts.sh"
+_assert_reset_count
+start_us=$(posix_time_microseconds)
+(
+ "$fun"
+ s=$?
+ asserts=$(_assert_count)
+ if [ ! "${asserts}" = "0" ]; then echo "☀️ Passed ${asserts} assertions."; fi
+ exit $s
+)
+status=$?
+end_us=$(posix_time_microseconds)
+duration=$(bc -e "$end_us - $start_us")
+if is_true "$refute" && [ "${status}" = "0" ]; then echo "⚠️ Exited with code ${_bold}${status}${_rst} (refuting, expected >0)"; fi
+if is_true "$refute" && [ ! "${status}" = "0" ]; then echo "🌗 Exited with code ${status} (refuting)"; fi
+if ! is_true "$refute" && [ ! "${status}" = "0" ]; then echo "⚠️ Exited with code ${_bold}${status}${_rst} (expected 0)"; fi
+echo "$(tput AF 59)Ran in ${duration}ms${_rst}"
+exit $status
+) 2>> "${testlog}" >> "${testlog}"
+status=$?
+fi
+
+## -- TEST FINISHED
+
+ cmpstatus="$status"
+ if is_true "$refute"; then
+ if [ "$status" = "0" ]; then cmpstatus=1; else cmpstatus=0; fi
+ fi
+ if [ "$precond_status" = "0" ] && [ "$cmpstatus" = "0" ]; then
+ _echo_block success
+ _cloyster_test_tests_success="${_cloyster_test_tests_success}${id} "
+ incr "success"
+ elif [ "$precond_status" = "2" ] || [ "$skip" = "y" ]; then
+ _echo_block skipped
+ _cloyster_test_tests_skipped="${_cloyster_test_tests_skipped} ${id}"
+ incr "skipped"
+ else
+ if [ ! "$precond_status" = "0" ]; then
+ err="precondition_error:${precond_status}"
+ else
+ err="${status}"
+ fi
+ _action=failed
+ if is_true "${ignore}"; then _action="ignored"; fi
+ _echo_block "${_action}"
+ setvar "_cloyster_test_tests_${id}__status" "$err"
+ setvar "_cloyster_test_tests_${id}__ignore" "$ignore"
+ setvar "_cloyster_test_tests_${_action}" "$(readvar "_cloyster_test_tests_${_action}") ${id}"
+ incr "${_action}"
+ fi
+
+ done
+ _echo_block_flush
+ all_end_us=$(posix_time_microseconds)
+ duration=$(bc -e "$all_end_us - $all_start_us")
+
+ _test_output_results
+ echo ""
+ _test_output_overview
+}