#!/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"