The audit that followed "some things in the settings app don't work". Sixty-five settings reach the compositor and five of them were checked against it. The rest were covered only by tests that read source text, which is exactly where a dead setting hides: the write no-ops, nothing fails, nothing logs, and the row simply does nothing. The sweep drives each setting through SystemSettings.commitPreference -- the entry point a settings row uses -- flips it to a value it does not hold, reads it back from the live compositor, and puts it straight back before touching the next one. Settings Panama stores itself get the same treatment against the store, since a value that fails to persist is the same dead row from the outside. Result: 61 of 63 compositor settings verified against the running compositor, and 51 stored settings round-tripped. No failures. The breakage was confined to the Applications page, which is fixed. Proven able to fail before being trusted: with commitPreference stubbed to return true without applying, 61 settings are reported; with the store stubbed to return nothing, 51 are. A one-second settle window keeps a slow read from being reported as a dead write, which it briefly was. Also here: control-center-contract asserted the literal margin expression that made the panel hang 38 pixels below the bar, so the contract and the code agreed and the bug was invisible to both. And settings-page-registry-contract is deleted -- settings-nav-contract already checked those files and more. It would have caught the Storage page omission if I had run the suite instead of a hand-picked subset. Claude-Session: https://claude.ai/code/session_01BRvzt4H8XXLPVH5MyYdk9L
106 lines
4.6 KiB
Bash
Executable File
106 lines
4.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
# Every compositor-backed setting must actually change the compositor.
|
|
#
|
|
# settings-hyprland-write-contract proves this for five settings. There are
|
|
# sixty-six, and the ones it does not reach are exactly where a dead setting
|
|
# hides: the write path no-ops, nothing fails, nothing logs, and the row simply
|
|
# does not do anything. That is what a user reports as "the settings app does
|
|
# not work", and it is not visible from any shape-checking test.
|
|
#
|
|
# Each setting is driven through SystemSettings.commitPreference -- the entry
|
|
# point a settings row uses -- flipped to a value it does not hold, read back
|
|
# from the live compositor, and put straight back before the next one is
|
|
# touched.
|
|
#
|
|
# This one talks to the running compositor on purpose. Preferences are written
|
|
# to an isolated config home so nothing lands in the real settings.json.
|
|
|
|
set -uo pipefail
|
|
|
|
repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
harness="$repo_dir/config/dot/quickshell/settings-system-harness.qml"
|
|
sweep="$repo_dir/tests/quickshell/settings_write_sweep.py"
|
|
|
|
fail() {
|
|
printf 'settings write sweep contract: %s\n' "$1" >&2
|
|
exit 1
|
|
}
|
|
|
|
[[ -r "$harness" ]] || fail 'the settings system harness is missing'
|
|
[[ -r "$sweep" ]] || fail 'the sweep is missing'
|
|
|
|
command -v qs >/dev/null 2>&1 || { printf 'settings write sweep contract: SKIP (no quickshell)\n'; exit 0; }
|
|
command -v hyprctl >/dev/null 2>&1 || { printf 'settings write sweep contract: SKIP (no compositor)\n'; exit 0; }
|
|
command -v jq >/dev/null 2>&1 || { printf 'settings write sweep contract: SKIP (no jq)\n'; exit 0; }
|
|
hyprctl -j getoption decoration:rounding >/dev/null 2>&1 \
|
|
|| { printf 'settings write sweep contract: SKIP (compositor not answering)\n'; exit 0; }
|
|
|
|
config_home="$(mktemp -d /tmp/panama-write-sweep.XXXXXX)"
|
|
|
|
qs_for_harness() {
|
|
XDG_CONFIG_HOME="$config_home" qs -p "$harness" "$@"
|
|
}
|
|
|
|
cleanup() {
|
|
qs_for_harness kill >/dev/null 2>&1 || true
|
|
rm -rf "$config_home"
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
# The sibling contract drives the SAME harness file with the compositor seam
|
|
# stubbed. Quickshell identifies an instance by config path, so an instance left
|
|
# over from that run would answer here and every write would miss the compositor
|
|
# entirely -- passing for the worst possible reason.
|
|
if qs_for_harness ipc show 2>/dev/null | grep -q '^target settings-system-test$'; then
|
|
fail 'a harness instance is already running; a stale one would answer these writes instead of the compositor'
|
|
fi
|
|
|
|
XDG_CONFIG_HOME="$config_home" PANAMA_SETTINGS_TEST_ISOLATE_COMPOSITOR=0 \
|
|
qs -p "$harness" --daemonize >/dev/null 2>&1
|
|
|
|
for _ in $(seq 1 40); do
|
|
qs_for_harness ipc show 2>/dev/null | grep -q '^target settings-system-test$' && break
|
|
sleep 0.25
|
|
done
|
|
qs_for_harness ipc show 2>/dev/null | grep -q '^target settings-system-test$' \
|
|
|| fail 'the harness did not start'
|
|
|
|
result="$(cd "$repo_dir" && XDG_CONFIG_HOME="$config_home" \
|
|
timeout 600 python3 "$sweep" "$config_home" "$harness")" \
|
|
|| fail 'the sweep did not finish'
|
|
|
|
verified="$(jq -r '.verified | length' <<<"$result")"
|
|
skipped="$(jq -r '.skipped | length' <<<"$result")"
|
|
failed="$(jq -r '.failures | length' <<<"$result")"
|
|
total="$(jq -r '.total' <<<"$result")"
|
|
|
|
if [[ "$failed" != "0" ]]; then
|
|
printf 'settings write sweep contract: %s setting(s) did not reach the compositor:\n' "$failed" >&2
|
|
jq -r '.failures[] | " \(.[0]): \(.[1])"' <<<"$result" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Skipping is legitimate for a shape with no scalar to compare, but a sweep that
|
|
# skips most of what it was pointed at is not evidence of anything.
|
|
if (( verified * 2 < total )); then
|
|
jq -r '.skipped[] | " \(.[0]): \(.[1])"' <<<"$result" >&2
|
|
fail "only $verified of $total settings were actually exercised"
|
|
fi
|
|
|
|
# ── Settings Panama stores itself ───────────────────────────────────────────
|
|
# No compositor to ask, so the question is whether the value comes back out of
|
|
# the store. A setting that silently fails to persist is the same dead row.
|
|
local_failed="$(jq -r '.localFailures | length' <<<"$result")"
|
|
local_verified="$(jq -r '.localVerified | length' <<<"$result")"
|
|
local_skipped="$(jq -r '.localSkipped | length' <<<"$result")"
|
|
|
|
if [[ "$local_failed" != "0" ]]; then
|
|
printf 'settings write sweep contract: %s stored setting(s) did not round-trip:\n' "$local_failed" >&2
|
|
jq -r '.localFailures[] | " \(.[0]): \(.[1])"' <<<"$result" >&2
|
|
exit 1
|
|
fi
|
|
|
|
printf 'settings write sweep contract: PASS (%d of %d compositor settings verified live, %d skipped; %d stored settings round-tripped, %d skipped)\n' \
|
|
"$verified" "$total" "$skipped" "$local_verified" "$local_skipped"
|