libexec/shellspec-runner.sh
#!/bin/sh
set -eu
# shellcheck disable=SC3044
shopt -u verbose_errexit 2>/dev/null ||:
# shellcheck source=lib/libexec/runner.sh
. "${SHELLSPEC_LIB:-./lib}/libexec/runner.sh"
start_profiler() {
[ "$SHELLSPEC_PROFILER" ] || return 0
$SHELLSPEC_SHELL "$SHELLSPEC_LIBEXEC/shellspec-profiler.sh" &
} 2>/dev/null
stop_profiler() {
[ "$SHELLSPEC_PROFILER" ] || return 0
if [ -e "$SHELLSPEC_PROFILER_SIGNAL" ]; then
rm "$SHELLSPEC_PROFILER_SIGNAL"
fi
}
cleanup() {
"$SHELLSPEC_TRAP" '' INT
set -- "$SHELLSPEC_TMPBASE" && SHELLSPEC_TMPBASE=''
[ "$SHELLSPEC_KEEP_TMPDIR" ] && return 0
[ "$1" ] || return 0
{ rmtempdir "$1" & } 2>/dev/null
}
interrupt() {
"$SHELLSPEC_TRAP" '' TERM # Workaround for posh: Prevent display 'Terminated'.
stop_profiler
reporter_pid=''
read_pid_file reporter_pid "$SHELLSPEC_REPORTER_PID" 0
[ "$reporter_pid" ] && sleep_wait signal 0 "$reporter_pid" 2>/dev/null
signal TERM 0 2>/dev/null &&:
cleanup
exit 130
}
precheck() {
export VERSION="$SHELLSPEC_VERSION"
export SHELL_VERSION="$SHELLSPEC_SHELL_VERSION"
export SHELL_TYPE="$SHELLSPEC_SHELL_TYPE"
eval "set -- $1"
[ $# -gt 0 ] || return 0
status_file=$SHELLSPEC_PRECHECKER_STATUS
for module; do
import_path=''
resolve_module_path import_path "$module"
if [ ! -r "$import_path" ]; then
echo "Unable to load the required module '$module': $import_path" >&2
exit 1
fi
set -- "$@" "$import_path"
shift
done
prechecker="$SHELLSPEC_LIBEXEC/shellspec-prechecker.sh"
# shellcheck disable=SC2086
$SHELLSPEC_SHELL "$prechecker" --warn-fd=3 --status-file="$status_file" "$@"
}
executor() {
start_profiler
executor="$SHELLSPEC_LIBEXEC/shellspec-executor.sh"
# shellcheck disable=SC2086
$SHELLSPEC_TIME $SHELLSPEC_SHELL "$executor" "$@" 3>&2 2>"$SHELLSPEC_TIME_LOG"
eval "stop_profiler; return $?"
}
reporter() {
$SHELLSPEC_SHELL "$SHELLSPEC_LIBEXEC/shellspec-reporter.sh" "$@"
}
error_handler() {
error_count=0
while IFS= read -r line; do
# shellcheck disable=SC2004
error_count=$(($error_count + 1))
error "$line"
done
[ "$error_count" -eq 0 ] || exit "$SHELLSPEC_ERROR_EXIT_CODE"
}
"$SHELLSPEC_TRAP" 'interrupt' INT
"$SHELLSPEC_TRAP" ':' TERM
trap 'cleanup' EXIT
check_formatters() {
eval "set -- $1"
for module; do
module_exists "${module}_formatter" && continue
abort "The specified formatter '$module' is not found."
done
}
check_formatters "$SHELLSPEC_FORMATTER $SHELLSPEC_GENERATORS"
for p; do
[ -f "$p" ] || continue
is_specfile "$p" && continue
abort "File '$p' cannot be executed because it does not match the pattern '$SHELLSPEC_PATTERN'."
done
if [ "$SHELLSPEC_REPAIR" ]; then
if [ -e "$SHELLSPEC_QUICK_FILE" ]; then
SHELLSPEC_QUICK=1
else
warn "Quick Mode is disabled. Run with --quick option first."
exit
fi
fi
if [ "$SHELLSPEC_QUICK" ]; then
if ! [ -e "$SHELLSPEC_QUICK_FILE" ]; then
if ( : > "$SHELLSPEC_QUICK_FILE" ) 2>/dev/null; then
warn "Quick Mode is automatically enabled." \
"If you want disable it, delete '$SHELLSPEC_QUICK_FILE'."
else
warn "Failed to enable Quick Mode " \
"due to failed to create '$SHELLSPEC_QUICK_FILE'."
fi
fi
if [ -e "$SHELLSPEC_QUICK_FILE" ]; then
count=$# line='' last_line=''
while read_quickfile line state "$SHELLSPEC_REPAIR"; do
[ "$last_line" = "$line" ] && continue || last_line=$line
match_quick_data "$line" "$@" && set -- "$@" "$line"
done < "$SHELLSPEC_QUICK_FILE"
if [ "$#" -gt "$count" ] && shift "$count"; then
warn "Run only not-passed examples the last time they ran."
export SHELLSPEC_PATTERN="*"
elif [ "$SHELLSPEC_REPAIR" ]; then
warn "No failed examples were found."
exit
fi
fi
fi
quick_mode='' info='' info_extra=$SHELLSPEC_INFO
[ -e "$SHELLSPEC_QUICK_FILE" ] && quick_mode="<quick mode> "
[ "$SHELLSPEC_QUICK" ] && info="${info}--quick "
[ "$SHELLSPEC_REPAIR" ] && info="${info}--repair "
if [ "$SHELLSPEC_FAIL_FAST_COUNT" ]; then
info="${info}--fail-fast $SHELLSPEC_FAIL_FAST_COUNT " && info="${info% 1 } "
fi
[ "$SHELLSPEC_WORKERS" -gt 0 ] && info="${info}--jobs $SHELLSPEC_WORKERS "
[ "$SHELLSPEC_DRYRUN" ] && info="${info}--dry-run "
[ "$SHELLSPEC_XTRACE" ] && info="${info}--trace${SHELLSPEC_XTRACE_ONLY:+-only} "
[ "$SHELLSPEC_RANDOM" ] && info="${info}--random $SHELLSPEC_RANDOM "
[ "$info" ] && info="{${info% }}"
SHELLSPEC_INFO="${quick_mode}${info}${info_extra:+ }${info_extra}"
mktempdir "$SHELLSPEC_TMPBASE"
if [ "$SHELLSPEC_KEEP_TMPDIR" ]; then
warn "Keeping temporary directory."
warn "Manually delete: rm -rf \"$SHELLSPEC_TMPBASE\""
fi
noexec_check="$SHELLSPEC_TMPBASE/.shellspec-check-executable"
echo '#!/bin/sh' > "$noexec_check"
"$SHELLSPEC_CHMOD" +x "$noexec_check"
if ! "$noexec_check" 2>/dev/null; then
export SHELLSPEC_NOEXEC_TMPDIR=1
warn "Some features will not work properly because files under" \
"the tmp directory (mounted with noexec option?) cannot be executed."
fi
if [ "$SHELLSPEC_BANNER" ]; then
if [ -s "$SHELLSPEC_BANNER_FILE" ]; then
cat "$SHELLSPEC_BANNER_FILE"
elif [ -s "$SHELLSPEC_BANNER_FILE.md" ]; then
cat "$SHELLSPEC_BANNER_FILE.md"
fi
fi
if [ "${SHELLSPEC_RANDOM:-}" ]; then
export SHELLSPEC_LIST="$SHELLSPEC_RANDOM"
exec="$SHELLSPEC_LIBEXEC/shellspec-list.sh"
eval "$SHELLSPEC_SHELL" "\"$exec\"" ${1+'"$@"'} >"$SHELLSPEC_INFILE"
set -- -
fi
{
env=$( ( ( ( ( _do() { set +e; (set -e; "$@" ); echo "exit_status=$?" >&9; }
_do precheck "$SHELLSPEC_REQUIRES" >&8
) 2>&1 | while IFS= read -r line; do error "$line"; done >&2
) 3>&1 | while IFS= read -r line; do warn "$line"; done >&2
) 4>&1 | while IFS= read -r line; do info "$line"; done >&8
) 9>&1 )
eval "$env"
} 8>&1
if [ "$exit_status" -ne 0 ] || [ -s "$SHELLSPEC_PRECHECKER_STATUS" ]; then
exit "$exit_status"
fi
# I want to process with non-blocking output
# and the stdout of runner streams to the reporter
# and capture stderr both of the runner and the reporter
# and the stderr streams to error hander
# and also handle both exit status. As a result of
set +e
( ( ( ( ( set -e; exec 3>&- 4>&- 5>&-; executor "$@" ); echo $? >&5; ) \
| ( set -e; reporter "$@" ) >&3; echo $? >&5 ) 2>&1 \
| ( set -e; error_handler ) >&4; echo $? >&5 ) 5>&1 \
| (
read -r xs1; read -r xs2; read -r xs3
xs='' error='' msg="Aborted with status code"
for i in "$xs1" "$xs2" "$xs3"; do
case $i in
0) continue ;;
"$SHELLSPEC_FAILURE_EXIT_CODE") [ "$xs" ] || xs=$i ;;
"$SHELLSPEC_ERROR_EXIT_CODE") xs=$i error=1 && break ;;
*) [ "${xs#"$SHELLSPEC_FAILURE_EXIT_CODE"}" ] || xs=$i; error=1
esac
done
if [ "$error" ]; then
error "$msg [executor: $xs1] [reporter: $xs2] [error handler: $xs3]"
fi
set_exit_status "${xs:-0}"
)
) 3>&1 4>&2
exit_status=$?
case $exit_status in
0) ;; # Running specs exit with successfully.
"$SHELLSPEC_FAILURE_EXIT_CODE") ;; # Running specs exit with failure.
*) error "Fatal error occurred, terminated with exit status $exit_status."
esac
exit "$exit_status"