@@ -208,6 +208,7 @@ run_teardown() {
208208trap run_teardown EXIT
209209
210210TOTAL=0
211+ PASSED=0
211212SKIPPED=0
212213# Set as the last statement of each branch that runs tests, and checked before
213214# the summary. A shell expansion error (a bad arithmetic expansion, say) unwinds
@@ -247,6 +248,7 @@ should_skip() {
247248 fi
248249 if [ " $1 " == " $EXCLUDE " ]; then
249250 echo " Test '$1 ' is excluded. Skipping."
251+ TOTAL=$(( TOTAL+ 1 ))
250252 SKIPPED=$(( SKIPPED+ 1 ))
251253 return 0
252254 fi
@@ -261,6 +263,19 @@ matches() {
261263 [ -z " $MATCH " ] || [ " $1 " == " $MATCH " ]
262264}
263265
266+ # Counts each named test as skipped, for tests that need a local daemon on a run
267+ # against an external host. Takes the names rather than a number so the count
268+ # cannot drift from the tests it stands in for.
269+ skip_local_only () {
270+ local test
271+ for test in " $@ " ; do
272+ if matches " $test " ; then
273+ TOTAL=$(( TOTAL+ 1 ))
274+ SKIPPED=$(( SKIPPED+ 1 ))
275+ fi
276+ done
277+ }
278+
264279# Collect the directories above $1 that wolfSSHd's StrictModes check will
265280# reject: a group or world writable directory anywhere above authorized_keys
266281# makes the daemon refuse to read it, unless the directory is sticky. This
@@ -351,6 +366,7 @@ run_test() {
351366 SKIPPED=$(( SKIPPED+ 1 ))
352367 elif [ " $RESULT " == 0 ]; then
353368 printf " PASSED\n"
369+ PASSED=$(( PASSED+ 1 ))
354370 else
355371 printf " FAILED!\n"
356372 cat stdout.txt
402418 TOTAL=$(( TOTAL+ 1 ))
403419 if grep -q " group or world readable" strictmodes_log.txt; then
404420 printf " PASSED\n"
421+ PASSED=$(( PASSED+ 1 ))
405422 else
406423 printf " FAILED!\n"
407424 cat strictmodes_log.txt
460477 grep -q " but this build cannot enforce it" upn_nofpki_log.txt &&
461478 ! grep -q " Refusing to load" upn_nofpki_log.txt; then
462479 printf " PASSED\n"
480+ PASSED=$(( PASSED+ 1 ))
463481 else
464482 printf " FAILED!\n"
465483 cat upn_nofpki_log.txt
@@ -519,6 +537,7 @@ run_strictmodes_authkeys_negative_test() {
519537 after=${after:- 0}
520538 if [ " $result " != 0 ] && [ " $after " -gt " $before " ]; then
521539 printf " PASSED\n"
540+ PASSED=$(( PASSED+ 1 ))
522541 else
523542 printf " FAILED! (expected StrictModes rejection: client exit=%s, new log matches=%s)\n" \
524543 " $result " " $(( after - before)) "
655674 rm -rf " $HK_WORK "
656675 printf " PASSED (mode and owner cases skipped, built with "
657676 printf " WOLFSSH_NO_HOSTKEY_PERMS)\n"
677+ PASSED=$(( PASSED+ 1 ))
658678 return
659679 fi
660680
674694
675695 rm -rf " $HK_WORK "
676696 printf " PASSED\n"
697+ PASSED=$(( PASSED+ 1 ))
677698}
678699
679700# Run the tests. There is one path whether or not --match was given: every test
@@ -712,15 +733,15 @@ run_hostkey_perm_check
712733# rejected. Reads the local ./log.txt, so only when we own the daemon.
713734if [ " $USING_LOCAL_HOST " == 1 ]; then
714735 run_test " sshd_pubkey_reject_test.sh"
715- elif matches " sshd_pubkey_reject_test.sh " ; then
716- SKIPPED= $(( SKIPPED + 1 ))
736+ else
737+ skip_local_only " sshd_pubkey_reject_test.sh "
717738fi
718739
719740# exercise the authorized_keys StrictModes path against the running sshd
720741if [ " $USING_LOCAL_HOST " == 1 ]; then
721742 run_strictmodes_authkeys_negative_test
722- elif matches " strictmodes_authkeys_negative " ; then
723- SKIPPED= $(( SKIPPED + 1 ))
743+ else
744+ skip_local_only " strictmodes_authkeys_negative "
724745fi
725746
726747if [ " $USING_LOCAL_HOST " == 1 ]; then
@@ -729,27 +750,32 @@ if [ "$USING_LOCAL_HOST" == 1 ]; then
729750fi
730751
731752# these tests require setting up an sshd
753+ local_sshd_tests=(
754+ " sshd_forcedcmd_test.sh"
755+ " sshd_match_overlap_test.sh"
756+ " sshd_window_full_test.sh"
757+ " sshd_stderr_eof_test.sh"
758+ " sshd_empty_password_test.sh"
759+ " sshd_permitroot_test.sh"
760+ " sshd_permitroot_prohibit_password.sh"
761+ " sshd_permitroot_forced_cmd.sh"
762+ " strictmodes_hostkey_negative"
763+ " upn_unenforceable_negative"
764+ " sshd_login_grace_test.sh"
765+ " sshd_privdrop_fail_test.sh"
766+ " sshd_chroot_fail_test.sh"
767+ )
732768if [ " $USING_LOCAL_HOST " == 1 ]; then
733- run_test " sshd_forcedcmd_test.sh"
734- run_test " sshd_match_overlap_test.sh"
735- run_test " sshd_window_full_test.sh"
736- run_test " sshd_stderr_eof_test.sh"
737- run_test " sshd_empty_password_test.sh"
738- run_test " sshd_permitroot_test.sh"
739- run_test " sshd_permitroot_prohibit_password.sh"
740- run_test " sshd_permitroot_forced_cmd.sh"
741- run_strictmodes_negative_test
742- run_upn_unenforceable_negative_test
743- run_test " sshd_login_grace_test.sh"
744- run_test " sshd_privdrop_fail_test.sh"
745- run_test " sshd_chroot_fail_test.sh"
769+ for test in " ${local_sshd_tests[@]} " ; do
770+ case " $test " in
771+ strictmodes_hostkey_negative) run_strictmodes_negative_test ;;
772+ upn_unenforceable_negative) run_upn_unenforceable_negative_test ;;
773+ * ) run_test " $test " ;;
774+ esac
775+ done
746776else
747777 printf " Skipping tests that need to setup local SSHD\n"
748- # the thirteen calls above; with --match at most one of them was going to run,
749- # and should_skip() has already counted it if it was excluded
750- if [ -z " $MATCH " ]; then
751- SKIPPED=$(( SKIPPED+ 13 ))
752- fi
778+ skip_local_only " ${local_sshd_tests[@]} "
753779fi
754780
755781# these tests run with X509 sshd-config loaded. The matches() guard keeps a
768794# negative test: a certificate UPN realm outside AuthorizedUPNDomains must
769795# be rejected. Needs the dedicated bad-domain config, so only runs when we
770796# control the local daemon.
771- if [ " $USING_LOCAL_HOST " == 1 ] && matches " sshd_x509_upn_fail.sh" ; then
797+ if [ " $USING_LOCAL_HOST " != 1 ]; then
798+ skip_local_only " sshd_x509_upn_fail.sh"
799+ elif matches " sshd_x509_upn_fail.sh" ; then
772800 start_wolfsshd " sshd_config_test_x509_upn_bad"
773801 run_test " sshd_x509_upn_fail.sh"
774802 printf " Shutting down test wolfSSHd\n"
780808# sshd_config_test_mldsa has no other host key, so an ML-DSA-less build
781809# cannot start the daemon; check out here, not in the test script. The check
782810# is the composite, not the umbrella: the ECDSA half can be missing on its own.
783- if [ " $USING_LOCAL_HOST " == 1 ] && matches " sshd_mldsa_composite_test.sh" ; then
811+ if [ " $USING_LOCAL_HOST " != 1 ]; then
812+ skip_local_only " sshd_mldsa_composite_test.sh"
813+ elif matches " sshd_mldsa_composite_test.sh" ; then
784814 if wolfssh_has MLDSA87_ES384; then
785815 start_wolfsshd " sshd_config_test_mldsa"
786816 run_test " sshd_mldsa_composite_test.sh"
799829# client when present.
800830if [ " $USING_LOCAL_HOST " == 1 ]; then
801831 run_test " sshd_ossh_cert_test.sh"
832+ else
833+ skip_local_only " sshd_ossh_cert_test.sh"
802834fi
803835RUN_COMPLETE=1
804836}
@@ -809,14 +841,35 @@ if [ "$RUN_COMPLETE" != 1 ]; then
809841fi
810842
811843# A --match that reached no call site ran nothing and skipped nothing, which
812- # would otherwise report a clean run. The local-only tests are unreachable
813- # against an external host, so a name that is valid on a local run selects
814- # nothing here; say so rather than exit 0 on an empty run.
844+ # would otherwise add up and report a clean "0 run". Every name is counted
845+ # somewhere -- the local-only ones as skipped against an external host -- so
846+ # this catches a call site that stopped counting; say so rather than exit 0 on
847+ # an empty run.
815848if [ -n " $MATCH " ] && [ " $TOTAL " -eq 0 ]; then
816849 printf " ERROR: --match %s selected no test that could run here\n" " $MATCH "
817850 exit 1
818851fi
819852
820- printf " All tests ran, $TOTAL passed, $SKIPPED skipped\n"
853+ # TOTAL counts every test that was reached, skips included, so it is a "ran"
854+ # count and not a pass count. Print the three separately, and assert they add
855+ # up: a skip site that bumps one counter and not the other then fails the run
856+ # instead of quietly printing a wrong number.
857+ printf " All tests ran, %d run, %d passed, %d skipped\n" \
858+ " $TOTAL " " $PASSED " " $SKIPPED "
859+
860+ if [ " $(( PASSED + SKIPPED)) " -ne " $TOTAL " ]; then
861+ printf " ERROR: counter mismatch (ran %d, passed %d, skipped %d)\n" \
862+ " $TOTAL " " $PASSED " " $SKIPPED "
863+ exit 1
864+ fi
865+
866+ # Without --match every known test is reached once, run or skipped, so a
867+ # name no call site reaches -- one dropped from local_sshd_tests, say --
868+ # shows up here as a short count.
869+ KNOWN=$(( ${# test_cases[@]} + ${# extra_test_cases[@]} ))
870+ if [ -z " $MATCH " ] && [ " $TOTAL " -ne " $KNOWN " ]; then
871+ printf " ERROR: %d tests known but %d reached\n" " $KNOWN " " $TOTAL "
872+ exit 1
873+ fi
821874
822875exit 0
0 commit comments