Clear unity_handler.sh: 100%, off the allowlist, 105 -> 104

The first allowlist removal of the campaign this session. unity_handler.sh
measures 32/32 = 100.00% through its directory's runner, and
check_shell_coverage.sh passes it with the exemption DELETED -- the real
gate, not just the measurement.

HARNESS BUG found getting there: bash caches executable locations in a hash
table, so `command -v foo` keeps succeeding after foo is removed from the
stub dir. Every "tool is missing" test was therefore running as a "tool is
present" test, asserting the opposite of its own name while looking
plausible. Reproduced minimally: after rm, STILL FOUND; after `hash -r`,
gone.

A _t_unstub helper now removes the stub and clears the table. The two suites
committed an hour ago had the same defect and are corrected here --
test_transcribe_pkgmgr.sh goes from 15 to 18 passing assertions, so three of
its checks had been vacuous. This is exactly the failure the campaign exists
to catch, found in its own test code.

`basename` joins the restricted PATH because the SUBJECT calls it; the suite
avoids needing `tail` via a parameter expansion instead, since every binary
in that PATH is one more thing a stub's shebang can trip over.

transcribe_deps.sh (92.31%) and transcribe_pkgmgr.sh (98.00%) re-measured
unchanged and stay on the allowlist.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016sxxsDHWgdRhmdmzNMA6XM
This commit is contained in:
Krzysztof Rudnicki 2026-08-22 13:20:54 +02:00
parent ac1cf57656
commit b91be3099d
7 changed files with 219 additions and 14 deletions

View File

@ -0,0 +1,19 @@
{
"title": "Take unity_handler.sh off the shell-coverage allowlist at a measured 100%",
"objective": "Cover unity_handler.sh to 100% through its directory's runner and remove it from meta/shell-coverage-allowlist.txt -- the campaign's first removal this session -- while fixing the bash hash-cache defect that had made 'missing tool' assertions in the same directory vacuous.",
"acceptance_criteria": [
"unity_handler.sh measures 100.00% through tests/run_all.sh, the subject is_covered() builds, with --fail-on-case-error passing.",
"check_shell_coverage.sh passes for unity_handler.sh AFTER the allowlist entry is deleted -- the real gate, not just the measurement.",
"meta/shell-coverage-allowlist.txt drops from 105 to 104 entries and no entry is ever added.",
"Every 'tool is missing' assertion genuinely hides the tool: removal must clear bash's command hash table, or the assertion tests the opposite of its name.",
"The two suites committed earlier in the same directory are corrected for the same defect, and their assertion counts are re-verified rather than assumed.",
"The other two libs' measured figures are unchanged by the harness edit.",
"All three suites pass with 0 failures; shellcheck clean with no suppressions; all files within the 250-line cap."
],
"out_of_scope": [
"clean_audio_filters.sh and transcribe_venv.sh, the two libs in this directory still without suites.",
"Auditing every other suite in the repo for the same bare-`rm` unstub defect; it is recorded as a risk for a later sweep.",
"Any change to unity_handler.sh itself."
],
"verifier": "meta/scripts/shell_coverage_jail.sh --subject .../testsAndMisc-bash/lib/tests/run_all.sh --measure unity_handler.sh --min 100 --fail-on-case-error (expects 32/32 = 100.00%, exit 0), then bash meta/scripts/check_shell_coverage.sh on that path with the allowlist entry removed (expects exit 0), plus .../tests/run_all.sh reporting 15 + 18 + 18 assertions and 0 failures."
}

View File

@ -0,0 +1,51 @@
{
"intent": "Cover unity_handler.sh to a measured 100% and take it OFF the shell-coverage allowlist -- the first entry removed this session, 105 -> 104. Fix the bash hash-cache bug found doing it, which had silently weakened the two suites committed just before.",
"scope": [
".../testsAndMisc-bash/lib/tests/test_unity_handler.sh: new, 18 assertions across all 4 functions",
".../tests/transcribe_harness.sh: new _t_unstub helper; basename added to the restricted PATH",
".../tests/test_transcribe_deps.sh and test_transcribe_pkgmgr.sh: switched to _t_unstub",
"meta/shell-coverage-allowlist.txt: unity_handler.sh removed",
"Non-goal: no change to any subject; no latent bug surfaced in unity_handler.sh"
],
"changes": [
"HARNESS BUG, found by an assertion that would not go green: bash caches executable locations in a hash table, so `command -v foo` keeps SUCCEEDING after foo is deleted from the stub dir. Every 'tool is missing' test therefore ran as a 'tool is present' test -- asserting the opposite of its own name while looking plausible. Reproduced minimally: after `rm`, STILL FOUND; after `hash -r`, gone.",
"The fix is a _t_unstub helper that removes the stub and clears the table, applied to all three suites in the directory. The two committed an hour earlier were affected and are corrected here; test_transcribe_pkgmgr.sh goes 15 -> 18 passing assertions as a result, so three of its checks had been vacuous.",
"`basename` was added to the restricted PATH because the SUBJECT calls it, unlike bash/env which the stubs' shebangs need. The suite avoids needing `tail` by taking the last line with a parameter expansion instead: every binary in that PATH is another thing a shebang can trip over.",
"The generated .desktop file is asserted on its CONTENT (Exec and MimeType lines), and xdg-mime registration on the BASENAME it is given -- a full path there would silently fail to register."
],
"verification": [
{
"command": "shell_coverage_jail.sh --subject .../tests/run_all.sh --measure unity_handler.sh --min 100 --fail-on-case-error",
"result": "pass",
"evidence": "unity_handler.sh: 32/32 lines = 100.00%, exit 0 at --min 100. Measured through run_all.sh, the subject is_covered() builds."
},
{
"command": "bash meta/scripts/check_shell_coverage.sh .../unity_handler.sh (AFTER removing it from the allowlist)",
"result": "pass",
"evidence": "'every checked shell library meets the coverage bar', exit 0. The lib passes the REAL gate with no exemption -- properly cleared, not merely measured."
},
{
"command": ".../lib/tests/run_all.sh",
"result": "pass",
"evidence": "15 + 18 + 18 = 51 assertions, 0 failures, 'All transcribe lib test files passed.'"
},
{
"command": "minimal repro of the hash cache: stub a command, run it, rm it, then command -v",
"result": "pass",
"evidence": "'STILL FOUND (hash cache)' before `hash -r`, 'gone after hash -r'. This is what makes _t_unstub necessary rather than cosmetic."
},
{
"command": "re-measure the other two libs after the harness change",
"result": "pass",
"evidence": "transcribe_deps.sh 48/52 = 92.31% and transcribe_pkgmgr.sh 49/50 = 98.00%, both unchanged. Both stay on the allowlist."
}
],
"risks": [
"Any other suite in the repo that removes a stub with a bare `rm` has the same latent defect: its 'missing tool' branch is not being tested. The features/lib suites use a jail and mostly add rather than remove stubs, but this is worth a sweep before the gate is armed.",
"The restricted PATH must list every binary the subjects use. A missing one surfaces as 'command not found' mid-suite rather than as a clear assertion failure."
],
"rollback": [
"git revert the commit; unity_handler.sh returns to the allowlist and the three suites return to bare `rm` unstubbing.",
"After rollback, confirm the allowlist is back to 105 entries so the gate's view is unchanged."
]
}

View File

@ -38,7 +38,7 @@ _t_has "$present_out" 'System deps present' "install_system_deps reports a satis
_t_lacks "$(_t_calls)" 'pacman' "nothing is installed when ffmpeg and espeak-ng are both present"
# --- offline with a missing dep exits 5 -------------------------------------
rm -f "$TEST_TMPDIR/bin/ffmpeg"
_t_unstub ffmpeg
offline_rc=0
(
OFFLINE=1
@ -47,7 +47,7 @@ offline_rc=0
_t_eq "5" "$offline_rc" "a missing dep in offline mode exits 5"
# --- no sudo means the install is skipped, not failed -----------------------
rm -f "$TEST_TMPDIR/bin/sudo"
_t_unstub sudo
_t_stub pacman
no_sudo_out="$(install_system_deps 2>&1)"
_t_has "$no_sudo_out" 'sudo not found' "a missing sudo skips the install instead of failing"
@ -63,7 +63,7 @@ _t_has "$pac_calls" 'ffmpeg' "the pacman branch installs the missing ffmpeg"
_t_lacks "$pac_calls" 'espeak-ng' "the pacman branch omits espeak-ng, which is already present"
# --- the apt branch uses Debian package names and refreshes first -----------
rm -f "$TEST_TMPDIR/bin/pacman"
_t_unstub pacman
_t_stub apt-get
_t_reset_calls
install_system_deps >/dev/null 2>&1
@ -72,28 +72,28 @@ _t_has "$apt_calls" 'apt-get update -y' "the apt branch refreshes the package li
_t_has "$apt_calls" 'python3-venv' "the apt branch asks for python3-venv, not python-virtualenv"
# --- the dnf branch ---------------------------------------------------------
rm -f "$TEST_TMPDIR/bin/apt-get"
_t_unstub apt-get
_t_stub dnf
_t_reset_calls
install_system_deps >/dev/null 2>&1
_t_has "$(_t_calls)" 'dnf install -y python3-venv' "the dnf branch installs python3-venv"
# --- the yum branch ---------------------------------------------------------
rm -f "$TEST_TMPDIR/bin/dnf"
_t_unstub dnf
_t_stub yum
_t_reset_calls
install_system_deps >/dev/null 2>&1
_t_has "$(_t_calls)" 'yum install -y python3-venv' "the yum branch installs python3-venv"
# --- the zypper branch uses its own versioned python names ------------------
rm -f "$TEST_TMPDIR/bin/yum"
_t_unstub yum
_t_stub zypper
_t_reset_calls
install_system_deps >/dev/null 2>&1
_t_has "$(_t_calls)" 'python311-virtualenv' "the zypper branch asks for python311-virtualenv"
# --- an unknown package manager warns rather than guessing ------------------
rm -f "$TEST_TMPDIR/bin/zypper"
_t_unstub zypper
unknown_out="$(install_system_deps 2>&1)"
_t_has "$unknown_out" 'Unknown package manager' "an unrecognised manager warns instead of installing"

View File

@ -76,7 +76,9 @@ _t_has "$no_sudo_out" 'sudo not found' "ensure_cuda_runtime reports a missing su
# sudo is a pass-through so the manager stub below records the real argv.
printf '#!/usr/bin/env bash\nexec "$@"\n' >"$TEST_TMPDIR/bin/sudo"
chmod +x "$TEST_TMPDIR/bin/sudo"
rm -f "$TEST_TMPDIR/bin/apt-get" "$TEST_TMPDIR/bin/dnf" "$TEST_TMPDIR/bin/yum"
_t_unstub apt-get
_t_unstub dnf
_t_unstub yum
_t_reset_calls
pac_rc=0
(ensure_cuda_runtime) >/dev/null 2>&1 || pac_rc=$?
@ -90,27 +92,28 @@ _t_reset_calls
_t_has "$(_t_calls)" 'apt-get install -y nvidia-cuda-toolkit' "the apt branch installs the CUDA toolkit"
# --- ensure_cuda_runtime: dnf and yum share one branch ----------------------
rm -f "$TEST_TMPDIR/bin/apt-get"
_t_unstub apt-get
_t_stub dnf
_t_reset_calls
(ensure_cuda_runtime) >/dev/null 2>&1 || true
_t_has "$(_t_calls)" 'dnf install -y cuda cudnn' "the dnf branch installs cuda and cudnn"
rm -f "$TEST_TMPDIR/bin/dnf"
_t_unstub dnf
_t_stub yum
_t_reset_calls
(ensure_cuda_runtime) >/dev/null 2>&1 || true
_t_has "$(_t_calls)" 'yum install -y cuda cudnn' "the shared dnf|yum branch also drives yum"
# --- ensure_cuda_runtime: the zypper branch ---------------------------------
rm -f "$TEST_TMPDIR/bin/yum" "$TEST_TMPDIR/bin/pacman"
_t_unstub yum
_t_unstub pacman
_t_stub zypper
_t_reset_calls
(ensure_cuda_runtime) >/dev/null 2>&1 || true
_t_has "$(_t_calls)" 'zypper install -y cuda cudnn' "the zypper branch installs cuda and cudnn"
# --- ensure_cuda_runtime: an unknown manager warns --------------------------
rm -f "$TEST_TMPDIR/bin/zypper"
_t_unstub zypper
unknown_out="$( (ensure_cuda_runtime) 2>&1)" || true
_t_has "$unknown_out" 'cannot install CUDA automatically' "an unknown package manager warns rather than guessing"

View File

@ -0,0 +1,123 @@
#!/usr/bin/env bash
# Tests for unity_handler.sh — the unityhub:// URL handler registration.
#
# No jail: the only write goes to $DESKTOP_DIR, pointed at a throwaway dir,
# and every external (update-desktop-database, xdg-mime, xdg-open) is
# intercepted by the harness's PATH stub dir.
#
# The generated .desktop file is asserted on its CONTENT: the MimeType and
# Exec lines are what make the handler work, and a file that exists with the
# wrong Exec is exactly the bug a presence check would pass.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=./transcribe_harness.sh
. "$SCRIPT_DIR/transcribe_harness.sh"
_t_setup_env
trap _t_teardown EXIT
# Globals the entry script defines above its source line.
DESKTOP_DIR="$TEST_TMPDIR/applications"
RUN_TEST=false
mkdir -p "$DESKTOP_DIR"
log_info() { printf '[INFO] %s\n' "$*"; }
log_ok() { printf '[OK] %s\n' "$*"; }
log_warn() { printf '[WARN] %s\n' "$*"; }
log_error() { printf '[ERR] %s\n' "$*"; }
# shellcheck source=../unity_handler.sh
. "$TRANSCRIBE_LIB_DIR/unity_handler.sh"
# --- create_handler_desktop -------------------------------------------------
create_out="$(create_handler_desktop '/opt/unityhub/unityhub %u')"
desktop_file="${create_out##*$'\n'}"
_t_eq "$DESKTOP_DIR/unityhub-url-handler.desktop" "$desktop_file" \
"create_handler_desktop returns the path it wrote"
desktop_body="$(cat "$desktop_file")"
_t_has "$desktop_body" 'Exec=/opt/unityhub/unityhub %u' \
"the Exec line carries the command it was given"
_t_has "$desktop_body" 'x-scheme-handler/unityhub;x-scheme-handler/unity;' \
"both URL schemes are declared in MimeType"
_t_has "$desktop_body" 'NoDisplay=true' "the entry is hidden from menus"
# --- register_mime_handler: both tools present ------------------------------
_t_stub update-desktop-database
_t_stub xdg-mime
_t_reset_calls
register_mime_handler "$desktop_file" >/dev/null
mime_calls="$(_t_calls)"
_t_has "$mime_calls" 'update-desktop-database' "the desktop database is refreshed"
_t_has "$mime_calls" 'default unityhub-url-handler.desktop x-scheme-handler/unityhub' \
"xdg-mime registers the unityhub scheme by BASENAME, not full path"
_t_has "$mime_calls" 'unityhub-url-handler.desktop x-scheme-handler/unity' \
"xdg-mime also registers the bare unity scheme"
# --- register_mime_handler: no update-desktop-database is survivable --------
_t_unstub update-desktop-database
no_db_out="$(register_mime_handler "$desktop_file" 2>&1)"
_t_has "$no_db_out" 'update-desktop-database not found' \
"a missing update-desktop-database is reported"
_t_has "$no_db_out" 'MIME handler registered' "registration still completes without it"
# --- register_mime_handler: no xdg-mime is FATAL ----------------------------
# Without xdg-mime nothing is registered, so this must fail rather than
# report success -- the distinction the else branch exists to make.
_t_unstub xdg-mime
no_mime_rc=0
register_mime_handler "$desktop_file" >/dev/null 2>&1 || no_mime_rc=$?
_t_eq "1" "$no_mime_rc" "a missing xdg-mime returns non-zero rather than claiming success"
# --- verify_registration: the handler matches -------------------------------
cat >"$TEST_TMPDIR/bin/xdg-mime" <<'XDGMIME'
#!/usr/bin/env bash
printf 'unityhub-url-handler.desktop\n'
XDGMIME
chmod +x "$TEST_TMPDIR/bin/xdg-mime"
match_out="$(verify_registration "$desktop_file" 2>&1)"
_t_has "$match_out" 'correctly set to unityhub-url-handler.desktop' \
"verify_registration confirms a correctly registered handler"
# --- verify_registration: a foreign handler warns ---------------------------
cat >"$TEST_TMPDIR/bin/xdg-mime" <<'XDGMIME'
#!/usr/bin/env bash
printf 'some-other-app.desktop\n'
XDGMIME
chmod +x "$TEST_TMPDIR/bin/xdg-mime"
wrong_out="$(verify_registration "$desktop_file" 2>&1)"
_t_has "$wrong_out" 'not set to' "verify_registration warns when another app owns the scheme"
_t_has "$wrong_out" 'some-other-app.desktop' "the warning names the handler actually registered"
# --- verify_registration: nothing registered at all -------------------------
cat >"$TEST_TMPDIR/bin/xdg-mime" <<'XDGMIME'
#!/usr/bin/env bash
exit 1
XDGMIME
chmod +x "$TEST_TMPDIR/bin/xdg-mime"
none_out="$(verify_registration "$desktop_file" 2>&1)"
_t_has "$none_out" '<none>' "an unset scheme is reported as <none> rather than blank"
# --- maybe_test_open: disabled prints the manual command --------------------
manual_out="$(maybe_test_open 2>&1)"
_t_has "$manual_out" 'test manually with' "RUN_TEST=false explains how to test by hand"
# --- maybe_test_open: enabled invokes xdg-open ------------------------------
_t_stub xdg-open
_t_reset_calls
opened_out="$(
RUN_TEST=true
maybe_test_open 2>&1
)"
_t_has "$(_t_calls)" 'unityhub://v1/editor-signin' "RUN_TEST=true opens the test link"
_t_has "$opened_out" 'Test link invoked' "the run reports that the link was invoked"
# --- maybe_test_open: enabled but xdg-open missing --------------------------
_t_unstub xdg-open
no_open_out="$(
RUN_TEST=true
maybe_test_open 2>&1
)"
_t_has "$no_open_out" 'xdg-open not found' "a missing xdg-open warns instead of failing"
_t_report "test_unity_handler.sh"

View File

@ -70,7 +70,7 @@ _t_setup_env() {
# shebang, and env resolves it through the RESTRICTED PATH. Without it each
# stub dies with "env: 'bash': No such file or directory" and the branch it
# was standing in for is silently never taken.
for tool in bash cat chmod cp env grep ln mkdir mktemp printf rm sed; do
for tool in basename bash cat chmod cp env grep ln mkdir mktemp printf rm sed; do
if command -v "$tool" >/dev/null 2>&1; then
ln -sf "$(command -v "$tool")" "$TEST_TMPDIR/coreutils/$tool"
fi
@ -94,6 +94,16 @@ _t_stub_failing() {
chmod +x "$TEST_TMPDIR/bin/$1"
}
# Remove a stub AND forget it. bash caches executable locations in a hash
# table, so `command -v foo` keeps succeeding after foo is deleted until the
# table is cleared -- which silently turns every "tool is missing" test into a
# "tool is present" test that asserts the opposite of its own name. Verified:
# without the `hash -r` the removed stub is STILL FOUND.
_t_unstub() {
rm -f "$TEST_TMPDIR/bin/$1"
hash -r
}
_t_calls() {
if [[ -f "$TEST_TMPDIR/calls.log" ]]; then
cat "$TEST_TMPDIR/calls.log"

View File

@ -101,7 +101,6 @@ linux_configuration/scripts/single_use/misc/testsAndMisc-bash/lib/clean_audio_fi
linux_configuration/scripts/single_use/misc/testsAndMisc-bash/lib/transcribe_deps.sh
linux_configuration/scripts/single_use/misc/testsAndMisc-bash/lib/transcribe_pkgmgr.sh
linux_configuration/scripts/single_use/misc/testsAndMisc-bash/lib/transcribe_venv.sh
linux_configuration/scripts/single_use/misc/testsAndMisc-bash/lib/unity_handler.sh
meta/lib/lint_full_tools.sh
meta/lib/lint_output.sh
meta/scripts/lib/disk_cleanup_common.sh