@@ -691,7 +691,10 @@ function bashunit::runner::run_test() {
691691 " division by 0" " cannot allocate memory" " bad file descriptor" \
692692 " segmentation fault" " illegal option" " argument list too long" \
693693 " readonly variable" " missing keyword" " killed" \
694- " cannot execute binary file" " invalid arithmetic operator" ; do
694+ " cannot execute binary file" " invalid arithmetic operator" \
695+ " ambiguous redirect" " integer expression expected" \
696+ " too many arguments" " value too great" \
697+ " not a valid identifier" " unexpected EOF" ; do
695698 if [[ " $runtime_output " == * " $error " * ]]; then
696699 runtime_error=" ${runtime_output#*: } " # Remove everything up to and including ": "
697700 runtime_error=${runtime_error// $' \n ' / } # Remove all newlines using parameter expansion
@@ -814,6 +817,18 @@ function bashunit::runner::run_test() {
814817 return
815818 fi
816819
820+ # Check for risky test (zero assertions)
821+ if [[ " $total_assertions " -eq 0 ]]; then
822+ bashunit::state::add_tests_risky
823+ if ! bashunit::env::is_failures_only_enabled; then
824+ bashunit::console_results::print_risky_test " ${label} " " $duration "
825+ fi
826+ bashunit::reports::add_test_risky " $test_file " " $label " " $duration " " $total_assertions "
827+ bashunit::runner::write_risky_result_output " $test_file " " $fn_name "
828+ bashunit::internal_log " Test risky" " $label "
829+ return
830+ fi
831+
817832 # In failures-only mode, suppress successful test output
818833 if ! bashunit::env::is_failures_only_enabled; then
819834 if [[ " $fn_name " == " $interpolated_fn_name " ]]; then
@@ -1066,6 +1081,21 @@ function bashunit::runner::write_incomplete_result_output() {
10661081 echo -e " $test_nr ) $test_file :$line_number \n$output_msg " >> " $INCOMPLETE_OUTPUT_PATH "
10671082}
10681083
1084+ function bashunit::runner::write_risky_result_output() {
1085+ local test_file=$1
1086+ local fn_name=$2
1087+
1088+ local line_number
1089+ line_number=$( bashunit::helper::get_function_line_number " $fn_name " )
1090+
1091+ local test_nr=" *"
1092+ if ! bashunit::parallel::is_enabled; then
1093+ test_nr=$( bashunit::state::get_tests_risky)
1094+ fi
1095+
1096+ echo -e " $test_nr ) $test_file :$line_number \nTest has no assertions (risky)" >> " $RISKY_OUTPUT_PATH "
1097+ }
1098+
10691099function bashunit::runner::record_file_hook_failure() {
10701100 local hook_name=" $1 "
10711101 local test_file=" $2 "
@@ -1103,15 +1133,19 @@ function bashunit::runner::execute_file_hook() {
11031133 local hook_output_file
11041134 hook_output_file=$( bashunit::temp_file " ${hook_name} _output" )
11051135
1106- # Enable errexit and errtrace to catch any failing command in the hook.
1107- # The ERR trap saves the exit status to a global variable (since return value
1108- # from trap doesn't propagate properly), disables errexit (to prevent caller
1109- # from exiting) and returns from the hook function, preventing subsequent
1110- # commands from executing.
1136+ # Enable errtrace to catch any failing command in the hook.
1137+ # Using -E (errtrace) without -e (errexit) prevents the main process from
1138+ # exiting on source failures (Bash 3.2 doesn't trigger ERR trap with -eE).
1139+ # The ERR trap saves the exit status to a global variable, cleans up shell
1140+ # options, and returns from the hook function to prevent subsequent commands
1141+ # from executing.
11111142 # Variables set before the failure are preserved since we don't use a subshell.
11121143 _BASHUNIT_HOOK_ERR_STATUS=0
1113- set -eE
1114- trap ' _BASHUNIT_HOOK_ERR_STATUS=$?; set +eE; trap - ERR; return $_BASHUNIT_HOOK_ERR_STATUS' ERR
1144+ set -E
1145+ if bashunit::env::is_strict_mode_enabled; then
1146+ set -uo pipefail
1147+ fi
1148+ trap ' _BASHUNIT_HOOK_ERR_STATUS=$?; set +Eu +o pipefail; trap - ERR; return $_BASHUNIT_HOOK_ERR_STATUS' ERR
11151149
11161150 {
11171151 " $hook_name "
@@ -1120,7 +1154,7 @@ function bashunit::runner::execute_file_hook() {
11201154 # Capture exit status from global variable and clean up
11211155 status=$_BASHUNIT_HOOK_ERR_STATUS
11221156 trap - ERR
1123- set +eE
1157+ set +Eu +o pipefail
11241158
11251159 if [[ -f " $hook_output_file " ]]; then
11261160 hook_output=" "
@@ -1204,15 +1238,19 @@ function bashunit::runner::execute_test_hook() {
12041238 local hook_output_file
12051239 hook_output_file=$( bashunit::temp_file " ${hook_name} _output" )
12061240
1207- # Enable errexit and errtrace to catch any failing command in the hook.
1208- # The ERR trap saves the exit status to a global variable (since return value
1209- # from trap doesn't propagate properly), disables errexit (to prevent caller
1210- # from exiting) and returns from the hook function, preventing subsequent
1211- # commands from executing.
1241+ # Enable errtrace to catch any failing command in the hook.
1242+ # Using -E (errtrace) without -e (errexit) prevents the subshell from
1243+ # exiting on source failures (Bash 3.2 doesn't trigger ERR trap with -eE).
1244+ # The ERR trap saves the exit status to a global variable, cleans up shell
1245+ # options, and returns from the hook function to prevent subsequent commands
1246+ # from executing.
12121247 # Variables set before the failure are preserved since we don't use a subshell.
12131248 _BASHUNIT_HOOK_ERR_STATUS=0
1214- set -eE
1215- trap ' _BASHUNIT_HOOK_ERR_STATUS=$?; set +eE; trap - ERR; return $_BASHUNIT_HOOK_ERR_STATUS' ERR
1249+ set -E
1250+ if bashunit::env::is_strict_mode_enabled; then
1251+ set -uo pipefail
1252+ fi
1253+ trap ' _BASHUNIT_HOOK_ERR_STATUS=$?; set +Eu +o pipefail; trap - ERR; return $_BASHUNIT_HOOK_ERR_STATUS' ERR
12161254
12171255 {
12181256 " $hook_name "
@@ -1221,7 +1259,7 @@ function bashunit::runner::execute_test_hook() {
12211259 # Capture exit status from global variable and clean up
12221260 status=$_BASHUNIT_HOOK_ERR_STATUS
12231261 trap - ERR
1224- set +eE
1262+ set +Eu +o pipefail
12251263
12261264 if [[ -f " $hook_output_file " ]]; then
12271265 hook_output=" "
0 commit comments