@@ -295,17 +295,25 @@ function runner::run_test() {
295295
296296 local test_execution_result=$(
297297 # shellcheck disable=SC2154
298- trap '
299- exit_code=$?
298+ trap "
299+ exit_code=\ $ ?
300300 set +e
301- state::set_test_exit_code "$exit_code"
302- runner::run_tear_down
301+ teardown_status=0
302+ runner::run_tear_down \" $test_file \" || teardown_status= \$ ?
303303 runner::clear_mocks
304304 cleanup_testcase_temp_files
305+ if [[ \$ teardown_status -ne 0 ]]; then
306+ state::set_test_exit_code \"\$ teardown_status\"
307+ else
308+ state::set_test_exit_code \"\$ exit_code\"
309+ fi
305310 state::export_subshell_context
306- ' EXIT
311+ " EXIT
307312 state::initialize_assertions_count
308- runner::run_set_up
313+ if ! runner::run_set_up " $test_file " ; then
314+ status=$?
315+ exit " $status "
316+ fi
309317
310318 # 2>&1: Redirects the std-error (FD 2) to the std-output (FD 1).
311319 # points to the original std-output.
@@ -379,17 +387,46 @@ function runner::run_test() {
379387 local test_title=" "
380388 [[ -n " $encoded_test_title " ]] && test_title=" $( helper::decode_base64 " $encoded_test_title " ) "
381389
390+ local encoded_hook_failure
391+ encoded_hook_failure=" ${test_execution_result##*## TEST_HOOK_FAILURE=} "
392+ encoded_hook_failure=" ${encoded_hook_failure%%##* } "
393+ local hook_failure=" "
394+ if [[ " $encoded_hook_failure " != " $test_execution_result " ]]; then
395+ hook_failure=" $encoded_hook_failure "
396+ fi
397+
398+ local encoded_hook_message
399+ encoded_hook_message=" ${test_execution_result##*## TEST_HOOK_MESSAGE=} "
400+ encoded_hook_message=" ${encoded_hook_message%%##* } "
401+ local hook_message=" "
402+ if [[ -n " $encoded_hook_message " ]]; then
403+ hook_message=" $( helper::decode_base64 " $encoded_hook_message " ) "
404+ fi
405+
382406 state::set_test_title " $test_title "
383407 local label
384408 label=" $( helper::normalize_test_function_name " $fn_name " " $interpolated_fn_name " ) "
385409 state::reset_test_title
386410
411+ local failure_label=" $label "
412+ local failure_function=" $fn_name "
413+ if [[ -n " $hook_failure " ]]; then
414+ failure_label=" $( helper::normalize_test_function_name " $hook_failure " ) "
415+ failure_function=" $hook_failure "
416+ fi
417+
387418 if [[ -n $runtime_error || $test_exit_code -ne 0 ]]; then
388419 state::add_tests_failed
389- console_results::print_error_test " $label " " $runtime_error "
390- reports::add_test_failed " $test_file " " $label " " $duration " " $total_assertions "
391- runner::write_failure_result_output " $test_file " " $fn_name " " $runtime_error "
392- internal_log " Test error" " $label " " $runtime_error "
420+ local error_message=" $runtime_error "
421+ if [[ -n " $hook_failure " && -n " $hook_message " ]]; then
422+ error_message=" $hook_message "
423+ elif [[ -z " $error_message " && -n " $hook_message " ]]; then
424+ error_message=" $hook_message "
425+ fi
426+ console_results::print_error_test " $failure_function " " $error_message "
427+ reports::add_test_failed " $test_file " " $failure_label " " $duration " " $total_assertions "
428+ runner::write_failure_result_output " $test_file " " $failure_function " " $error_message "
429+ internal_log " Test error" " $failure_label " " $error_message "
393430 return
394431 fi
395432
@@ -624,8 +661,9 @@ function runner::execute_file_hook() {
624661}
625662
626663function runner::run_set_up() {
664+ local _test_file=" ${1-} "
627665 internal_log " run_set_up"
628- helper::execute_function_if_exists ' set_up'
666+ runner::execute_test_hook ' set_up'
629667}
630668
631669function runner::run_set_up_before_script() {
@@ -635,8 +673,64 @@ function runner::run_set_up_before_script() {
635673}
636674
637675function runner::run_tear_down() {
676+ local _test_file=" ${1-} "
638677 internal_log " run_tear_down"
639- helper::execute_function_if_exists ' tear_down'
678+ runner::execute_test_hook ' tear_down'
679+ }
680+
681+ function runner::execute_test_hook() {
682+ local hook_name=" $1 "
683+
684+ if [[ " $( type -t " $hook_name " ) " != " function" ]]; then
685+ return 0
686+ fi
687+
688+ local hook_output=" "
689+ local status=0
690+ local hook_output_file
691+ hook_output_file=$( temp_file " ${hook_name} _output" )
692+
693+ {
694+ " $hook_name "
695+ } > " $hook_output_file " 2>&1 || status=$?
696+
697+ if [[ -f " $hook_output_file " ]]; then
698+ hook_output=$( cat " $hook_output_file " )
699+ rm -f " $hook_output_file "
700+ fi
701+
702+ if [[ $status -ne 0 ]]; then
703+ local message=" $hook_output "
704+ if [[ -n " $hook_output " ]]; then
705+ printf " %s" " $hook_output "
706+ else
707+ message=" Hook '$hook_name ' failed with exit code $status "
708+ printf " %s\n" " $message " >&2
709+ fi
710+ runner::record_test_hook_failure " $hook_name " " $message " " $status "
711+ return " $status "
712+ fi
713+
714+ if [[ -n " $hook_output " ]]; then
715+ printf " %s" " $hook_output "
716+ fi
717+
718+ return 0
719+ }
720+
721+ function runner::record_test_hook_failure() {
722+ local hook_name=" $1 "
723+ local hook_message=" $2 "
724+ local status=" $3 "
725+
726+ if [[ -n " $( state::get_test_hook_failure) " ]]; then
727+ return " $status "
728+ fi
729+
730+ state::set_test_hook_failure " $hook_name "
731+ state::set_test_hook_message " $hook_message "
732+
733+ return " $status "
640734}
641735
642736function runner::clear_mocks() {
0 commit comments