#!/usr/bin/env bash # The preference store is derived from config/PreferenceSchema.qml rather than # restating each key. This contract pins the properties that derivation is # supposed to buy, so that a future change cannot quietly reintroduce the # hand-maintained variant: # # * every schema key round-trips through disk # * out-of-range numbers are clamped, not stored raw and not rejected # * unknown keys in the file are preserved across a write # * set() refuses a key that is not in the schema # * reset() restores *every* schema default with no hand-written list # * a corrupt file yields shipped defaults rather than a broken shell set -euo pipefail repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" harness="$repo_dir/config/dot/quickshell/preference-schema-harness.qml" state_home="$(mktemp -d /tmp/panama-schema-state.XXXXXX)" config_home="$(mktemp -d /tmp/panama-schema-config.XXXXXX)" store="$config_home/panama/settings.json" fail() { printf 'preference schema contract: %s\n' "$1" >&2 exit 1 } qs_for_harness() { XDG_STATE_HOME="$state_home" XDG_CONFIG_HOME="$config_home" qs -p "$harness" "$@" } cleanup() { qs_for_harness kill >/dev/null 2>&1 || true } trap cleanup EXIT start_harness() { XDG_STATE_HOME="$state_home" XDG_CONFIG_HOME="$config_home" qs -p "$harness" --daemonize >/dev/null for _ in $(seq 1 40); do if qs_for_harness ipc show 2>/dev/null | rg -q '^target preference-schema-test$'; then return fi sleep 0.1 done fail 'test IPC target did not start' } stop_harness() { qs_for_harness kill >/dev/null 2>&1 || true for _ in $(seq 1 40); do # A bare `return` would propagate the failed `ipc show` status, which # under `set -e` ends the whole contract instead of the function. qs_for_harness ipc show >/dev/null 2>&1 || return 0 sleep 0.1 done fail 'test shell did not stop cleanly' } wait_for_store() { for _ in $(seq 1 40); do [[ -f "$store" ]] && jq -e . "$store" >/dev/null 2>&1 && return sleep 0.1 done fail 'preferences file was not written' } # ── A fresh store reports schema defaults ──────────────────────────────────── start_harness key_count="$(qs_for_harness ipc call preference-schema-test keyCount)" [[ "$key_count" -gt 0 ]] || fail 'schema is empty' initial="$(qs_for_harness ipc call preference-schema-test dump | jq -cS .)" defaults="$(qs_for_harness ipc call preference-schema-test defaults | jq -cS .)" [[ "$initial" == "$defaults" ]] || fail "a fresh store did not report schema defaults: $initial" # ── Out-of-range numbers are clamped, not stored raw ───────────────────────── qs_for_harness ipc call preference-schema-test applyJson \ '{"dockHideDelayMs": 99999, "nightLightTemperature": 100, "focusDurationMinutes": 45}' >/dev/null clamped="$(qs_for_harness ipc call preference-schema-test dump)" [[ "$(jq -r .dockHideDelayMs <<<"$clamped")" == "2000" ]] \ || fail "an above-range value was not clamped to the schema maximum: $(jq -r .dockHideDelayMs <<<"$clamped")" [[ "$(jq -r .nightLightTemperature <<<"$clamped")" == "2000" ]] \ || fail "a below-range value was not clamped to the schema minimum: $(jq -r .nightLightTemperature <<<"$clamped")" # ── An unknown key is refused rather than silently accepted ────────────────── verdict="$(qs_for_harness ipc call preference-schema-test applyJson '{"__not_a_setting__": 1}')" [[ "$(jq -r .__not_a_setting__ <<<"$verdict")" == "false" ]] || fail 'set() accepted a key outside the schema' qs_for_harness ipc call preference-schema-test dump | jq -e 'has("__not_a_setting__") | not' >/dev/null \ || fail 'a key outside the schema entered the store' # ── An out-of-range enum value is refused ──────────────────────────────────── verdict="$(qs_for_harness ipc call preference-schema-test applyJson '{"vrrPolicy": 7}')" [[ "$(jq -r .vrrPolicy <<<"$verdict")" == "false" ]] || fail 'set() accepted an enum value outside its options' # ── Every schema key round-trips across a restart ──────────────────────────── qs_for_harness ipc call preference-schema-test applyJson \ '{"use24Hour": true, "showSeconds": false, "showWeekday": false, "showCpu": false, "showMemory": false, "showGpu": false, "dockAutohide": false, "dockRevealDelayMs": 75, "dockHideDelayMs": 400, "focusDurationMinutes": 25, "autoHdr": false, "vrrPolicy": 0, "directScanoutPolicy": 0, "nightLightEnabled": true, "nightLightAutomatic": true, "nightLightTemperature": 4100, "lastPage": "desktop"}' >/dev/null wait_for_store before="$(qs_for_harness ipc call preference-schema-test dump | jq -cS .)" [[ "$before" != "$defaults" ]] || fail 'the fixture did not change anything' stop_harness start_harness after="" for _ in $(seq 1 40); do after="$(qs_for_harness ipc call preference-schema-test dump | jq -cS .)" [[ "$after" == "$before" ]] && break sleep 0.1 done [[ "$after" == "$before" ]] || fail "values did not survive a restart: $after" # ── A key this build does not know is carried through, not dropped ─────────── stop_harness jq '. + {"__future_setting__": "keep me"}' "$store" >"$store.tmp" && mv "$store.tmp" "$store" start_harness qs_for_harness ipc call preference-schema-test applyJson '{"dockHideDelayMs": 500}' >/dev/null for _ in $(seq 1 40); do jq -e '.__future_setting__ == "keep me" and .dockHideDelayMs == 500' "$store" >/dev/null 2>&1 && break sleep 0.1 done jq -e '.__future_setting__ == "keep me"' "$store" >/dev/null \ || fail 'a setting from a newer build was dropped on write' # ── reset() restores every schema key, with no hand-maintained list ────────── qs_for_harness ipc call preference-schema-test reset >/dev/null restored="" for _ in $(seq 1 40); do restored="$(qs_for_harness ipc call preference-schema-test dump | jq -cS .)" [[ "$restored" == "$defaults" ]] && break sleep 0.1 done [[ "$restored" == "$defaults" ]] || fail "reset did not restore every schema default: $restored" jq -e '.__future_setting__ == "keep me"' "$store" >/dev/null \ || fail 'reset discarded a setting it does not own' # ── A corrupt file degrades to defaults instead of breaking the shell ──────── stop_harness printf '{ this is not json' >"$store" start_harness corrupt="$(qs_for_harness ipc call preference-schema-test dump | jq -cS .)" [[ "$corrupt" == "$defaults" ]] || fail "a corrupt store did not fall back to defaults: $corrupt" trap - EXIT cleanup printf 'preference schema contract: PASS\n'