152 lines
7.8 KiB
Bash
Executable File
152 lines
7.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
# Two behaviours the settings rows depend on:
|
|
#
|
|
# commitPreference(key, value)
|
|
# One entry point for every row. A compositor-backed key must reach Hyprland
|
|
# and be confirmed before it is stored; a local key is written directly.
|
|
# Rows bind a schema key and call this, so they never need to know which
|
|
# kind they hold -- and a row must not be able to store a value the
|
|
# compositor rejected.
|
|
#
|
|
# restoreDefaults()
|
|
# Panama keeps user state in more than one file. Resetting only the schema
|
|
# store would leave a customised Home accessory arrangement in place while
|
|
# claiming to have restored Panama's defaults. That is worse than having no
|
|
# reset at all, because it is silent.
|
|
|
|
set -euo pipefail
|
|
|
|
repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
harness="$repo_dir/config/dot/quickshell/settings-system-harness.qml"
|
|
system_settings="$repo_dir/config/dot/quickshell/services/SystemSettings.qml"
|
|
wallpaper_service="$repo_dir/config/dot/quickshell/services/Wallpaper.qml"
|
|
|
|
# Preferences are committed to $XDG_CONFIG_HOME, and the Home store lives under
|
|
# $XDG_STATE_HOME. Both are isolated so this contract cannot touch the real
|
|
# desktop's settings; the compositor is the live one and is restored below.
|
|
config_home="$(mktemp -d /tmp/panama-commit-config.XXXXXX)"
|
|
state_home="$(mktemp -d /tmp/panama-commit-state.XXXXXX)"
|
|
|
|
fail() {
|
|
printf 'settings commit/reset contract: %s\n' "$1" >&2
|
|
exit 1
|
|
}
|
|
|
|
rg -Fq 'HomePreferences.resetHomeDefaults();' "$system_settings" \
|
|
|| fail 'restoreDefaults does not use the durable Home reset boundary'
|
|
if rg -q 'HomePreferences\.(favorites|initialized)\s*=' "$system_settings"; then
|
|
fail 'restoreDefaults mutates Home aliases instead of using resetHomeDefaults'
|
|
fi
|
|
rg -Fq 'root.protectDisplays(protectedDisplays)' "$system_settings" \
|
|
|| fail 'restoreDefaults can apply unconfirmed display geometry during reload'
|
|
rg -Fq 'Keybinds.applyReload();' "$system_settings" \
|
|
|| fail 'restoreDefaults does not replay shipped keybindings'
|
|
rg -Fq 'root.applyWallpaper(String(DesktopPreferences.get("wallpaperPath") ?? ""));' "$system_settings" \
|
|
|| fail 'restoreDefaults does not visibly reapply the shipped wallpaper'
|
|
rg -Fq 'const effectivePath = path === "" ? root.shippedPath : path;' "$wallpaper_service" \
|
|
|| fail 'clearing wallpaper preference leaves the old image visible'
|
|
rg -Fq 'property string storedValue:' "$wallpaper_service" \
|
|
|| fail 'the shipped wallpaper cannot remain represented by the default empty preference'
|
|
|
|
qs_for_harness() {
|
|
XDG_CONFIG_HOME="$config_home" XDG_STATE_HOME="$state_home" qs -p "$harness" "$@"
|
|
}
|
|
|
|
original_rounding="$(hyprctl -j getoption decoration:rounding | jq -r .int)"
|
|
original_gaps="$(hyprctl -j getoption general:gaps_out | jq -r .css | awk '{print $1}')"
|
|
|
|
restore() {
|
|
hyprctl eval "hl.config({ decoration = { rounding = $original_rounding }, general = { gaps_out = $original_gaps } })" >/dev/null 2>&1 || true
|
|
qs_for_harness kill >/dev/null 2>&1 || true
|
|
rm -rf "$config_home" "$state_home"
|
|
}
|
|
trap restore EXIT
|
|
|
|
XDG_CONFIG_HOME="$config_home" XDG_STATE_HOME="$state_home" qs -p "$harness" --daemonize >/dev/null
|
|
for _ in $(seq 1 40); do
|
|
qs_for_harness ipc show 2>/dev/null | rg -q '^target settings-system-test$' && break
|
|
sleep 0.1
|
|
done
|
|
qs_for_harness ipc show 2>/dev/null | rg -q '^target settings-system-test$' || fail 'test IPC target did not start'
|
|
|
|
# ── A local key is stored directly ───────────────────────────────────────────
|
|
[[ "$(qs_for_harness ipc call settings-system-test commit showSeconds false)" == "true" ]] \
|
|
|| fail 'commitPreference refused a local key'
|
|
[[ "$(qs_for_harness ipc call settings-system-test stored showSeconds)" == "false" ]] \
|
|
|| fail 'a local key was not stored'
|
|
|
|
# ── A compositor key reaches Hyprland, then is stored ────────────────────────
|
|
target_rounding=$(( original_rounding == 11 ? 13 : 11 ))
|
|
[[ "$(qs_for_harness ipc call settings-system-test commit windowRounding "$target_rounding")" == "true" ]] \
|
|
|| fail 'commitPreference refused a compositor key'
|
|
|
|
for _ in $(seq 1 40); do
|
|
[[ "$(hyprctl -j getoption decoration:rounding | jq -r .int)" == "$target_rounding" ]] && break
|
|
sleep 0.1
|
|
done
|
|
[[ "$(hyprctl -j getoption decoration:rounding | jq -r .int)" == "$target_rounding" ]] \
|
|
|| fail "a compositor-backed commit did not reach Hyprland (rounding=$(hyprctl -j getoption decoration:rounding | jq -r .int))"
|
|
[[ "$(qs_for_harness ipc call settings-system-test stored windowRounding)" == "$target_rounding" ]] \
|
|
|| fail 'a verified compositor commit was not stored'
|
|
|
|
# ── A value the schema rejects is never stored ───────────────────────────────
|
|
before="$(qs_for_harness ipc call settings-system-test stored windowRounding)"
|
|
[[ "$(qs_for_harness ipc call settings-system-test commit windowRounding 9999)" == "true" ]] \
|
|
|| fail 'an out-of-range value should be clamped by the schema, not refused outright'
|
|
[[ "$(qs_for_harness ipc call settings-system-test stored windowRounding)" != "9999" ]] \
|
|
|| fail 'an out-of-range value was stored unclamped'
|
|
|
|
[[ "$(qs_for_harness ipc call settings-system-test commit __not_a_setting__ 1)" == "false" ]] \
|
|
|| fail 'commitPreference accepted a key outside the schema'
|
|
|
|
# ── Reset spans every store, not just the schema one ─────────────────────────
|
|
qs_for_harness ipc call settings-system-test seedHome >/dev/null
|
|
qs_for_harness ipc call settings-system-test commit dockHideDelayMs 900 >/dev/null
|
|
display_fixture='{"DP-2":{"mode":"4500x3000@60","scale":1.5,"transform":0}}'
|
|
[[ "$(qs_for_harness ipc call settings-system-test commit displays "$display_fixture")" == "true" ]] \
|
|
|| fail 'the protected display fixture did not apply'
|
|
sleep 0.4
|
|
|
|
home_before="$(qs_for_harness ipc call settings-system-test homeState)"
|
|
jq -e '.count == 1 and .initialized == true' <<<"$home_before" >/dev/null \
|
|
|| fail "the Home fixture did not apply: $home_before"
|
|
[[ "$(qs_for_harness ipc call settings-system-test stored dockHideDelayMs)" == "900" ]] \
|
|
|| fail 'the dock fixture did not apply'
|
|
|
|
[[ "$(qs_for_harness ipc call settings-system-test restoreDefaults)" == "true" ]] \
|
|
|| fail 'restoreDefaults refused a safe reset'
|
|
sleep 0.6
|
|
|
|
reset_state="$(qs_for_harness ipc call settings-system-test resetState)"
|
|
jq -e '.calls == [
|
|
"display.block:true",
|
|
"display.protect",
|
|
"keybinds.reload",
|
|
"wallpaper.set:",
|
|
"display.block:false"
|
|
] and .displayBlocked == false' <<<"$reset_state" >/dev/null \
|
|
|| fail "reset did not safely replay non-reactive state: $reset_state"
|
|
|
|
[[ "$(qs_for_harness ipc call settings-system-test stored dockHideDelayMs)" == "250" ]] \
|
|
|| fail 'reset did not restore a schema default'
|
|
[[ "$(qs_for_harness ipc call settings-system-test stored displays | jq -cS .)" == "$(jq -cS . <<<"$display_fixture")" ]] \
|
|
|| fail 'reset replaced confirmed display geometry without confirmation'
|
|
|
|
home_after="$(qs_for_harness ipc call settings-system-test homeState)"
|
|
jq -e '.count == 0 and .initialized == false' <<<"$home_after" >/dev/null \
|
|
|| fail "reset left the Home accessory store customised: $home_after"
|
|
|
|
# Resetting a stored value does not by itself tell Hyprland anything, so the
|
|
# reset must re-apply compositor-backed defaults too.
|
|
for _ in $(seq 1 40); do
|
|
[[ "$(hyprctl -j getoption decoration:rounding | jq -r .int)" == "18" ]] && break
|
|
sleep 0.1
|
|
done
|
|
[[ "$(hyprctl -j getoption decoration:rounding | jq -r .int)" == "18" ]] \
|
|
|| fail "reset did not re-apply the compositor default (rounding=$(hyprctl -j getoption decoration:rounding | jq -r .int))"
|
|
|
|
trap - EXIT
|
|
restore
|
|
printf 'settings commit/reset contract: PASS\n'
|