diff --git a/docs/superpowers/contracts/wake-alarm-restore-display-test-gap-2026-06-22.json b/docs/superpowers/contracts/wake-alarm-restore-display-test-gap-2026-06-22.json new file mode 100644 index 0000000..daa2df9 --- /dev/null +++ b/docs/superpowers/contracts/wake-alarm-restore-display-test-gap-2026-06-22.json @@ -0,0 +1,15 @@ +{ + "title": "Fix real root cause of the wake_alarm coverage gap: untested _restore_display xset-found branch", + "objective": "The previous two CI-fix attempts (removing -n 4 xdist, testing under Python 3.12) both failed to fix a reproducible coverage gap on wake_alarm/_alarm_display.py:71 because they were the wrong diagnosis. The actual cause: _restore_display() was never mocked in the _block_extra_devices autouse fixture shared by test_alarm_part2/3/4.py, so every on_close()/_lock.close() call in those tests executed the REAL _restore_display(), which only reaches the xset-found branch (line 71) because the dev machine genuinely has /usr/bin/xset installed -- the CI runner does not, so it always took the xset-missing branch instead, and there was no dedicated unit test for the xset-found path at all to provide coverage independent of the environment. Success: a dedicated, hermetic unit test exists for both branches of _restore_display, and no test depends on real xset presence/absence to pass.", + "acceptance_criteria": [ + "_restore_display is added to the _block_extra_devices fixture in test_alarm_part2.py, test_alarm_part3.py, and test_alarm_part4.py, matching its sibling hardware-teardown mocks (_restore_fans, _restore_alarm_audio, turn_off_plug) that were already mocked there", + "test_alarm_display.py gains test_restore_display_runs_xset_s_on_when_present, mirroring the existing test_wake_display_runs_ddcutil_and_xset_commands pattern, mocking shutil.which to a fake path rather than depending on the real xset binary", + "Full repo test suite passes with 100% branch coverage with no -n 4/xdist parallelism change and no Python version change required -- confirming the prior two fix attempts were unnecessary for this specific gap (though the xdist removal is being kept as a separate, independently-justified reliability improvement)", + "No test in the wake_alarm suite makes a real subprocess call (xset, ddcutil, etc.) as a side effect of running on a developer's real X11 desktop" + ], + "out_of_scope": [ + "Reverting the prior -n 4 removal or Python-3.12 verification work -- both were reasonable diagnostic steps given the evidence available at the time, and removing xdist parallelism remains a valid, separately-motivated reliability improvement even though it didn't fix this particular gap", + "Auditing every other test file for the same missing-mock pattern -- scoped specifically to the _restore_display gap that was actually observed failing in CI" + ], + "verifier": "python -m pytest (full repo suite) --cov --cov-branch --cov-fail-under=100; pre-commit run --all-files; push and confirm GitHub Actions Pre-commit checks run goes green" +} diff --git a/docs/superpowers/evidence/wake-alarm-restore-display-test-gap-2026-06-22.json b/docs/superpowers/evidence/wake-alarm-restore-display-test-gap-2026-06-22.json new file mode 100644 index 0000000..4b3b982 --- /dev/null +++ b/docs/superpowers/evidence/wake-alarm-restore-display-test-gap-2026-06-22.json @@ -0,0 +1,35 @@ +{ + "intent": "Find and fix the actual root cause of a coverage gap on wake_alarm/_alarm_display.py:71 that survived two prior diagnostic fix attempts (removing xdist -n 4, testing under Python 3.12) because both were wrong guesses.", + "scope": [ + "python_pkg/wake_alarm/tests/test_alarm_part2.py, test_alarm_part3.py, test_alarm_part4.py (_block_extra_devices fixture)", + "python_pkg/wake_alarm/tests/test_alarm_display.py (new test)", + "Non-goal: any production code change -- _alarm_display.py's _restore_display is correct as-is" + ], + "changes": [ + "Added patch(\"python_pkg.wake_alarm._alarm._restore_display\") to the _block_extra_devices autouse fixture in all three test files that exercise WakeAlarm.on_close()/_lock.close() -- it was the one hardware-teardown call left unmocked there, meaning every test in those files that triggered on_close() ran the REAL _restore_display(), which calls the REAL shutil.which('xset')", + "Added test_restore_display_runs_xset_s_on_when_present to test_alarm_display.py, a dedicated hermetic test for the xset-found branch (mirrors the existing test_wake_display_runs_ddcutil_and_xset_commands pattern), so that branch's coverage no longer depends on whether the machine running the suite happens to have /usr/bin/xset installed" + ], + "verification": [ + { + "command": "which xset", + "result": "pass", + "evidence": "/usr/bin/xset -- confirms the dev machine has it, explaining why this gap was invisible locally despite being 100% reproducible on the CI runner (which does not have it)" + }, + { + "command": "python -m pytest -q (full repo suite)", + "result": "pass", + "evidence": "950 passed (949 + 1 new test), TOTAL coverage 100.00%" + }, + { + "command": "pre-commit run --all-files", + "result": "pass", + "evidence": "all hooks green" + } + ], + "risks": [ + "None to production code. Two prior commits in this session (xdist removal, the implicit 3.12 check) addressed real but ultimately unrelated reliability concerns; this commit is the one that actually closes the specific coverage gap that's been failing CI" + ], + "rollback": [ + "git revert this commit -- the gap will reappear on any machine/CI runner lacking /usr/bin/xset, and the unmocked _restore_display will resume making a real xset subprocess call as a side effect of running the wake_alarm test suite on any machine that does have it" + ] +} diff --git a/python_pkg/wake_alarm/tests/test_alarm_display.py b/python_pkg/wake_alarm/tests/test_alarm_display.py index 6dc4900..a71f74c 100644 --- a/python_pkg/wake_alarm/tests/test_alarm_display.py +++ b/python_pkg/wake_alarm/tests/test_alarm_display.py @@ -127,3 +127,20 @@ class TestDisplayHelpers: ): _restore_display() mock_run.assert_not_called() + + def test_restore_display_runs_xset_s_on_when_present(self) -> None: + """_restore_display re-enables the screensaver via xset when present.""" + with ( + patch( + "python_pkg.wake_alarm._alarm_display.shutil.which", + return_value="/usr/bin/xset", + ), + patch("python_pkg.wake_alarm._alarm_display.subprocess.run") as mock_run, + ): + _restore_display() + mock_run.assert_called_once_with( + ["/usr/bin/xset", "s", "on"], + check=False, + capture_output=True, + timeout=5, + ) diff --git a/python_pkg/wake_alarm/tests/test_alarm_part2.py b/python_pkg/wake_alarm/tests/test_alarm_part2.py index e7a7181..81b60c8 100644 --- a/python_pkg/wake_alarm/tests/test_alarm_part2.py +++ b/python_pkg/wake_alarm/tests/test_alarm_part2.py @@ -59,6 +59,7 @@ def _block_extra_devices() -> Generator[MagicMock]: patch("python_pkg.wake_alarm._alarm._restore_fans"), patch("python_pkg.wake_alarm._alarm._set_max_brightness"), patch("python_pkg.wake_alarm._alarm._wake_display"), + patch("python_pkg.wake_alarm._alarm._restore_display"), patch("python_pkg.wake_alarm._alarm._warn_if_no_real_sink"), patch("python_pkg.wake_alarm._alarm._activate_alarm_audio", return_value=None), patch("python_pkg.wake_alarm._alarm._restore_alarm_audio"), diff --git a/python_pkg/wake_alarm/tests/test_alarm_part3.py b/python_pkg/wake_alarm/tests/test_alarm_part3.py index 249c4d5..f2772f4 100644 --- a/python_pkg/wake_alarm/tests/test_alarm_part3.py +++ b/python_pkg/wake_alarm/tests/test_alarm_part3.py @@ -58,6 +58,7 @@ def _block_extra_devices() -> Generator[MagicMock]: patch("python_pkg.wake_alarm._alarm._restore_fans"), patch("python_pkg.wake_alarm._alarm._set_max_brightness"), patch("python_pkg.wake_alarm._alarm._wake_display"), + patch("python_pkg.wake_alarm._alarm._restore_display"), patch("python_pkg.wake_alarm._alarm._warn_if_no_real_sink"), patch("python_pkg.wake_alarm._alarm._activate_alarm_audio", return_value=None), patch("python_pkg.wake_alarm._alarm._restore_alarm_audio"), diff --git a/python_pkg/wake_alarm/tests/test_alarm_part4.py b/python_pkg/wake_alarm/tests/test_alarm_part4.py index b091264..31e52a5 100644 --- a/python_pkg/wake_alarm/tests/test_alarm_part4.py +++ b/python_pkg/wake_alarm/tests/test_alarm_part4.py @@ -56,6 +56,7 @@ def _block_extra_devices() -> Generator[MagicMock]: patch("python_pkg.wake_alarm._alarm._restore_fans"), patch("python_pkg.wake_alarm._alarm._set_max_brightness"), patch("python_pkg.wake_alarm._alarm._wake_display"), + patch("python_pkg.wake_alarm._alarm._restore_display"), patch("python_pkg.wake_alarm._alarm._warn_if_no_real_sink"), patch("python_pkg.wake_alarm._alarm._activate_alarm_audio", return_value=None), patch("python_pkg.wake_alarm._alarm._restore_alarm_audio"),