From 68a96c9b27f8f53fe51d4ddcd0c284c1ed3f0644 Mon Sep 17 00:00:00 2001 From: Chemaclass Date: Sat, 25 Jul 2026 15:42:36 +0200 Subject: [PATCH 1/2] test(learn): cover the rest of the non-interactive core learn.sh is 1213 lines with 6 tests over 4 functions, which is why a real duplication cleanup there could not be attempted safely -- a refactor had nothing to catch it. Covers print_menu, show_progress, reset_progress, create_example_file and run_lesson_test (pass and fail paths): 4 functions to 9, 6 tests to 14. Three things the sandbox needed: - stdin from /dev/null so the 'Press Enter to continue' prompts hit EOF instead of blocking, plus '|| true' at the call sites because that read returns non-zero and the sandbox runs under set -e - BASHUNIT_ROOT_DIR set INSIDE the child (run_lesson_test shells out through it); it is readonly in the caller, so a prefix assignment aborts - the colour palette stubbed empty, since learn.sh renders colour but does not source colors.sh; pulling the real chain in would couple a unit test to source order Verified non-vacuous by mutation: dropping the rm in reset_progress and the chmod in create_example_file each fail their test. --- tests/unit/learn_test.sh | 101 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 100 insertions(+), 1 deletion(-) diff --git a/tests/unit/learn_test.sh b/tests/unit/learn_test.sh index 064cd87b..aadedf41 100644 --- a/tests/unit/learn_test.sh +++ b/tests/unit/learn_test.sh @@ -14,15 +14,28 @@ function _learn_in_sandbox() { # Resolve the repo root from this test file, not $BASHUNIT_ROOT_DIR: under # `build.sh --verify` the running binary's root dir has no src/ (#834). root_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" + # stdin is /dev/null so the `read -p "Press Enter to continue..."` prompts in + # the interactive paths hit EOF and return instead of blocking the suite. + # BASHUNIT_ROOT_DIR is set INSIDE the child (run_lesson_test shells out to the + # entrypoint through it): it is readonly in the caller, so a `VAR=x cmd` prefix + # assignment would abort with "readonly variable". ( cd "$sandbox" && HOME="$sandbox" bash -c ' set -euo pipefail + BASHUNIT_ROOT_DIR="'"$root_dir"'" + export BASHUNIT_ROOT_DIR GREP="$(command -v grep)" + # learn.sh renders coloured output but does not source colors.sh. Stub + # the palette to empty rather than pulling in the real chain + # (str -> globals -> env -> colors), which would couple this unit test to + # source order and make the assertions match escape codes. + _BASHUNIT_COLOR_BOLD="" _BASHUNIT_COLOR_DEFAULT="" _BASHUNIT_COLOR_FAILED="" + _BASHUNIT_COLOR_FAINT="" _BASHUNIT_COLOR_INCOMPLETE="" _BASHUNIT_COLOR_PASSED="" # shellcheck source=/dev/null source "'"$root_dir"'/src/learn.sh" '"$1"' - ' + ' /dev/null || true + if ! bashunit::learn::is_completed "lesson_4"; then echo cleared; fi + ')" + + assert_same "cleared" "$out" +} + +function test_learn_reset_progress_is_safe_without_a_progress_file() { + local out + out="$(_learn_in_sandbox ' + bashunit::learn::reset_progress >/dev/null || true + echo safe + ')" + + assert_same "safe" "$out" +} + +function test_learn_create_example_file_writes_and_makes_it_executable() { + local out + out="$(_learn_in_sandbox ' + bashunit::learn::create_example_file "sample_test.sh" "#!/usr/bin/env bash" >/dev/null || true + [ -f sample_test.sh ] && echo exists + [ -x sample_test.sh ] && echo executable + cat sample_test.sh + ')" + + assert_same $'exists\nexecutable\n#!/usr/bin/env bash' "$out" +} + +function test_learn_run_lesson_test_marks_the_lesson_completed_when_it_passes() { + local out + out="$(_learn_in_sandbox ' + printf "#!/usr/bin/env bash\n\nfunction test_ok() {\n assert_same 1 1\n}\n" >pass_test.sh + bashunit::learn::run_lesson_test "pass_test.sh" "2" >/dev/null 2>&1 || true + if bashunit::learn::is_completed "lesson_2"; then echo completed; fi + ')" + + assert_same "completed" "$out" +} + +function test_learn_run_lesson_test_does_not_mark_the_lesson_when_it_fails() { + local out + out="$(_learn_in_sandbox ' + printf "#!/usr/bin/env bash\n\nfunction test_bad() {\n assert_same 1 2\n}\n" >fail_test.sh + bashunit::learn::run_lesson_test "fail_test.sh" "5" >/dev/null 2>&1 || true + if ! bashunit::learn::is_completed "lesson_5"; then echo not_completed; fi + ')" + + assert_same "not_completed" "$out" +} From dff686dcb9ffb05fe3bf5dd37f6b78e25c6544be Mon Sep 17 00:00:00 2001 From: Chemaclass Date: Sat, 25 Jul 2026 15:47:15 +0200 Subject: [PATCH 2/2] test(learn): tolerate a non-zero return from the output-only calls Under --strict the test shell runs set -e, so a non-zero return from the sandbox aborted the test before its assertion. show_progress and print_menu are inspected for their output, not their status, so the call sites tolerate it -- matching what the interactive ones already do for the read-at-EOF case. Verified on real Bash 3.0 in Docker with --strict, and locally with --strict --parallel. --- tests/unit/learn_test.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/unit/learn_test.sh b/tests/unit/learn_test.sh index aadedf41..775a490c 100644 --- a/tests/unit/learn_test.sh +++ b/tests/unit/learn_test.sh @@ -110,7 +110,7 @@ function test_learn_cleanup_is_safe_without_init() { function test_learn_print_menu_lists_every_lesson_and_the_controls() { local out - out="$(_learn_in_sandbox 'bashunit::learn::print_menu')" + out="$(_learn_in_sandbox 'bashunit::learn::print_menu || true')" assert_contains "1." "$out" assert_contains "10." "$out" @@ -119,7 +119,7 @@ function test_learn_print_menu_lists_every_lesson_and_the_controls() { function test_learn_show_progress_without_a_progress_file() { local out - out="$(_learn_in_sandbox 'bashunit::learn::show_progress')" + out="$(_learn_in_sandbox 'bashunit::learn::show_progress || true')" assert_contains "No progress yet" "$out" } @@ -128,7 +128,7 @@ function test_learn_show_progress_reflects_completed_lessons() { local out out="$(_learn_in_sandbox ' bashunit::learn::mark_completed "lesson_1" - bashunit::learn::show_progress + bashunit::learn::show_progress || true ')" assert_contains "Your Progress" "$out"