Cover aw_autostart.sh (82.93%), executing the script it generates
Some checks are pending
Pre-commit checks / pre-commit (push) Waiting to run
Shell tests / shell-tests (push) Waiting to run
Shell tests / arch-tests (push) Waiting to run

A 25-assertion suite for aw_autostart.sh, second in the hardest-first order.

The generated i3blocks status script is asserted by RUNNING it against
stubbed pacman/pgrep across all four of its branches -- uninstalled,
installed-but-stopped, running, installed-via-package -- rather than by
grepping the heredoc's text. A heredoc that emits syntactically broken bash
passes a text check and fails in front of the user.

One assertion was silently testing nothing at first: aw-qt is genuinely
installed at /usr/bin/aw-qt on this host, so the "not installed" case found
the real binary through the PREPENDED stub dir. PATH is now REPLACED for that
one case. The suite went 24-passed-1-failed -> 25-passed-0-failed, and the
gate caught it rather than the number.

Measured 68/82 = 82.93%; it STAYS on the allowlist. The residue is the
`[[ $EUID -eq 0 ]]` false branches, which are unreachable inside the jail
because the process is uid 0 there, plus the documented kcov under-report --
an `exit 44` sentinel proves create_i3blocks_status runs to completion while
its lines 145-162 report zero hits. Third lib to show it.

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 12:49:51 +02:00
parent 768e73249d
commit 26afe4390c
2 changed files with 170 additions and 0 deletions

View File

@ -0,0 +1,43 @@
{
"intent": "Add a test suite for aw_autostart.sh (score 51), the second target in the hardest-first order. It stays on the allowlist at a measured 82.93%, below the 100% bar.",
"scope": [
"linux_configuration/scripts/single_use/features/lib/tests/test_aw_autostart.sh: new, 25 assertions across all 4 functions",
"Non-goal: no change to aw_autostart.sh -- unlike dwm_config.sh, no latent bug surfaced",
"Non-goal: removing the lib from the allowlist"
],
"changes": [
"The suite asserts the GENERATED i3blocks status script by EXECUTING it against stubbed pacman/pgrep, not by grepping its text. All four of its branches are covered: uninstalled, installed-but-stopped, running, and installed-via-package. A heredoc emitting syntactically broken bash would pass a text check and fail in front of the user; executing it is what catches that.",
"setup_autostart's idempotence is asserted directly -- a second run must leave exactly one `exec --no-startup-id aw-qt` line in the i3 config.",
"test_setup's two helpers (check_activitywatch_installed / check_activitywatch_running) live in the entry script above the source line. They are redefined per-assertion inside a SUBSHELL, per the documented trap: a top-level redefinition shadows the subject's own view for every later assertion and silently drops coverage while the tests still report passing."
],
"verification": [
{
"command": "run the suite outside the jail with SCRIPT_DIR rewritten",
"result": "pass",
"evidence": "'test_aw_autostart.sh: 25 passed, 0 failed'. An earlier run was 24/1: the 'AW uninstalled' case failed because aw-qt is genuinely installed at /usr/bin/aw-qt on this host, so a PREPENDED stub dir cannot hide it from `command -v`. Fixed by REPLACING PATH for that one case -- the assertion was silently testing nothing before."
},
{
"command": "shell_coverage_jail.sh --subject .../run_all.sh <jail_args> --measure aw_autostart.sh --min 1 --fail-on-case-error",
"result": "pass",
"evidence": "aw_autostart.sh: 68/82 lines = 82.93%, and no 'warn: case exited' line -- every assertion in all five suite files passes under the gate."
},
{
"command": "temporarily insert `exit 44` after the create_i3blocks_status assertions",
"result": "pass",
"evidence": "'warn: case exited 44' fired, proving create_i3blocks_status runs to completion even though kcov reports lines 145-162 at zero hits. Third lib exhibiting the documented under-report."
},
{
"command": "shellcheck on the new suite",
"result": "pass",
"evidence": "clean, no suppressions added."
}
],
"risks": [
"Lines 22, 148-149 and 205 are the `[[ $EUID -eq 0 ]]` false branches. Inside the jail the process IS uid 0, so the non-root halves are genuinely unreachable there -- a real limitation of measuring under the jail, not a gap in the tests. Reaching them needs a non-root run, which cannot write the paths the rest of the suite asserts on.",
"82.93% is an under-report of unknown magnitude for the same reason recorded in docs/shell-split-verification.md, so the lib stays allowlisted -- the conservative direction."
],
"rollback": [
"git revert the commit; the suite disappears and aw_autostart.sh returns to being untested but still allowlisted.",
"After rollback, confirm the allowlist still contains aw_autostart.sh so the gate's view of the lib is unchanged."
]
}

View File

@ -0,0 +1,127 @@
#!/usr/bin/env bash
# Tests for aw_autostart.sh — ActivityWatch's autostart wiring and self-checks.
#
# The subject writes an XDG .desktop entry, appends an `exec` line to the i3
# config, and generates an i3blocks status script. All of it lands under
# $USER_HOME, which the suite points at a throwaway dir -- so unlike its
# siblings this one does not need root paths. It still runs under the jail
# with the rest of the directory's suites; see jail_args beside this file.
#
# The generated i3blocks script is asserted by EXECUTING it against stubbed
# pacman/pgrep, not by grepping its text: "AW on" vs "AW off" is the thing
# that reaches the status bar, and a heredoc that emits syntactically broken
# bash would pass a text check and fail in front of the user.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=./features_harness.sh
. "$SCRIPT_DIR/features_harness.sh"
_t_setup_env
trap _t_teardown EXIT
# The globals aw_autostart.sh inherits from setup_activitywatch.sh.
USER_HOME="$TEST_TMPDIR/home"
ACTUAL_USER="$(id -un)"
mkdir -p "$USER_HOME/.config/i3"
# test_setup calls these two; they live in the entry script, above the source
# line. Redefinable per-assertion, so each definition goes in a subshell -- a
# top-level redefinition would shadow the subject's own view for every later
# assertion.
check_activitywatch_installed() { return 1; }
check_activitywatch_running() { return 1; }
# shellcheck source=../aw_autostart.sh
. "$FEATURES_LIB_DIR/aw_autostart.sh"
# --- setup_autostart: no i3 config present ----------------------------------
rm -f "$USER_HOME/.config/i3/config"
no_i3_out="$(setup_autostart)"
desktop="$USER_HOME/.config/autostart/activitywatch.desktop"
_t_file_has "$desktop" 'Exec=aw-qt' "the XDG autostart entry execs aw-qt"
_t_file_has "$desktop" 'X-GNOME-Autostart-enabled=true' "the XDG entry enables GNOME autostart"
_t_has "$no_i3_out" 'i3 config not found' "setup_autostart reports a missing i3 config"
# --- setup_autostart: appends to an existing i3 config ----------------------
printf '# my i3 config\n' >"$USER_HOME/.config/i3/config"
added_out="$(setup_autostart)"
_t_file_has "$USER_HOME/.config/i3/config" 'exec --no-startup-id aw-qt' "setup_autostart appends the i3 exec line"
_t_has "$added_out" 'Added ActivityWatch to i3 config' "setup_autostart reports the i3 entry was added"
# --- setup_autostart: idempotent -- a second run must not duplicate ---------
again_out="$(setup_autostart)"
_t_eq "1" "$(grep -c 'exec --no-startup-id aw-qt' "$USER_HOME/.config/i3/config")" \
"a second run does not duplicate the i3 exec line"
_t_has "$again_out" 'already exists in i3 config' "setup_autostart reports the entry already exists"
# --- create_i3blocks_status -------------------------------------------------
status_out="$(create_i3blocks_status)"
status_script="$USER_HOME/.config/i3blocks/activitywatch_status.sh"
_t_eq "0" "$([[ -x $status_script ]] && echo 0 || echo 1)" "the i3blocks status script is executable"
_t_has "$status_out" 'interval=10' "the printed i3blocks block sets interval=10"
# --- the GENERATED script actually works ------------------------------------
# Not installed: pacman -Qi fails and aw-qt is absent from PATH.
# PATH is REPLACED, not prepended, for this one case: aw-qt is genuinely
# installed at /usr/bin/aw-qt on the dev host, so a prepended stub dir cannot
# hide it -- `command -v aw-qt` would find the real one and the case would
# silently assert nothing. The generated script needs only coreutils here.
_t_stub pgrep
printf '#!/usr/bin/env bash\nexit 1\n' >"$TEST_TMPDIR/bin/pacman"
chmod +x "$TEST_TMPDIR/bin/pacman"
uninstalled="$(PATH="$TEST_TMPDIR/bin:/usr/bin/coreutils-only" "$status_script")"
_t_has "$uninstalled" 'AW uninstalled' "the status script reports 'AW uninstalled' with no package and no binary"
# Installed via the aw-qt binary, but no process running: pgrep must FAIL.
printf '#!/usr/bin/env bash\nexit 0\n' >"$TEST_TMPDIR/bin/aw-qt"
chmod +x "$TEST_TMPDIR/bin/aw-qt"
printf '#!/usr/bin/env bash\nexit 1\n' >"$TEST_TMPDIR/bin/pgrep"
chmod +x "$TEST_TMPDIR/bin/pgrep"
off_out="$("$status_script")"
_t_has "$off_out" 'AW off' "the status script reports 'AW off' when installed but not running"
# Installed and running.
printf '#!/usr/bin/env bash\nexit 0\n' >"$TEST_TMPDIR/bin/pgrep"
chmod +x "$TEST_TMPDIR/bin/pgrep"
on_out="$("$status_script")"
_t_has "$on_out" 'AW on' "the status script reports 'AW on' when the process is found"
# Installed via the pacman package rather than the binary.
rm -f "$TEST_TMPDIR/bin/aw-qt"
printf '#!/usr/bin/env bash\nexit 0\n' >"$TEST_TMPDIR/bin/pacman"
chmod +x "$TEST_TMPDIR/bin/pacman"
pkg_out="$("$status_script")"
_t_has "$pkg_out" 'AW on' "the status script accepts the pacman package as proof of install"
# --- test_setup: everything present -----------------------------------------
present_out="$(
check_activitywatch_installed() { return 0; }
check_activitywatch_running() { return 0; }
test_setup
)"
_t_has "$present_out" '✓ ActivityWatch is installed' "test_setup reports an installed ActivityWatch"
_t_has "$present_out" '✓ ActivityWatch is running' "test_setup reports a running ActivityWatch"
_t_has "$present_out" '✓ XDG autostart file exists' "test_setup finds the XDG autostart file"
_t_has "$present_out" '✓ i3 autostart configured' "test_setup finds the i3 autostart line"
_t_has "$present_out" '✓ i3blocks status script created' "test_setup finds the i3blocks script"
# --- test_setup: nothing present --------------------------------------------
rm -f "$desktop" "$status_script" "$USER_HOME/.config/i3/config"
absent_out="$(
check_activitywatch_installed() { return 1; }
check_activitywatch_running() { return 1; }
test_setup
)"
_t_has "$absent_out" '✗ ActivityWatch is not installed' "test_setup reports a missing ActivityWatch"
_t_has "$absent_out" '✗ ActivityWatch is not running' "test_setup reports a stopped ActivityWatch"
_t_has "$absent_out" '✗ XDG autostart file missing' "test_setup reports the missing XDG file"
_t_has "$absent_out" '! i3 autostart may not be configured' "test_setup reports the missing i3 line"
_t_has "$absent_out" '✗ i3blocks status script missing' "test_setup reports the missing i3blocks script"
# --- show_instructions ------------------------------------------------------
instructions="$(show_instructions)"
_t_has "$instructions" 'http://localhost:5600' "show_instructions gives the web interface URL"
_t_has "$instructions" 'Super+Shift+R' "show_instructions gives the i3 reload shortcut"
_t_report "test_aw_autostart.sh"