Files
Panama/tests/hypr/prefs-fallback-contract.sh
T
Gabriel Brown d96863b687 Convert British spellings to American across the repo
colour -> color, behaviour -> behavior, centre -> center, favourite ->
favorite, and about twenty other pairs, applied consistently across
comments, docs, error/UI copy, and a handful of QML identifiers that
used the British spelling as their actual name: SystemSettings'
serialiseValue/serialiseTable/normaliseGradient, Displays'
normaliseModes, Wallpaper's normalisePolicy, SettingsBackup's
serialiseHomeState, DateTime's ntpSynchronised property, Clipboard's
_normalise helper, and ShortcutCapture's cancelled signal (with its
onCancelled handler in ShortcutsPage.qml). Every call site and the two
tests that assert on the literal source text (settings-ownership and
settings-backup-live contracts) were updated in lockstep.

Left untouched: config/dot/espanso/match/packages/misspell-en/ is a
vendored third-party autocorrect dictionary -- its entries are typo
corrections, not our prose, and rewriting them would fight the
package's own purpose (and any future re-sync from upstream).

The already-American `favorites` property (Home page pinned
accessories) was never actually misspelled -- only nearby comments and
error strings said "favourites" -- so no data migration was needed
there.

Claude-Session: https://claude.ai/code/session_01E6TJUAh41HaP25MVHWkhRZ
2026-08-19 08:07:55 -04:00

129 lines
6.5 KiB
Bash
Executable File

#!/usr/bin/env bash
# config/dot/hypr/prefs.lua is read at config time by looks.lua, input.lua, and
# monitors.lua. It is therefore the one piece of Panama that can cost the user a
# working compositor rather than merely a working feature.
#
# This contract pins the only behavior that matters: whatever is in the
# settings file -- including nothing, garbage, or values of the wrong type --
# the config still parses, and every setting either takes the stored value or
# falls back to the value shipped in the Lua.
set -euo pipefail
repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
hypr_dir="$repo_dir/config/dot/hypr"
work="$(mktemp -d /tmp/panama-prefs-contract.XXXXXX)"
fail() {
printf 'prefs fallback contract: %s\n' "$1" >&2
exit 1
}
cleanup() {
rm -rf "$work"
}
trap cleanup EXIT
# Exercise prefs.lua directly rather than through a full compositor launch: the
# question is what the module returns, and Hyprland's own parse is covered by
# the --verify-config case at the end.
probe() {
local settings="$1"
mkdir -p "$work/config/panama"
if [[ "$settings" == "__absent__" ]]; then
rm -f "$work/config/panama/settings.json"
else
printf '%s' "$settings" >"$work/config/panama/settings.json"
fi
XDG_CONFIG_HOME="$work/config" lua -e "
package.path = '$hypr_dir/?.lua;' .. package.path
local ok, prefs = pcall(require, 'prefs')
if not ok then
print('LOAD_ERROR ' .. tostring(prefs))
os.exit(0)
end
print(string.format(
'gapsOut=%s rounding=%s blur=%s layout=%s hdr=%s',
tostring(prefs.get('gapsOut', 10)),
tostring(prefs.get('windowRounding', 18)),
tostring(prefs.get('blurEnabled', true)),
tostring(prefs.get('keyboardLayout', 'us')),
tostring(prefs.getInt('autoHdr', 1))))
" 2>&1
}
shipped='gapsOut=10 rounding=18 blur=true layout=us hdr=1'
# ── No file at all: the normal first run ─────────────────────────────────────
result="$(probe '__absent__')"
[[ "$result" == "$shipped" ]] || fail "an absent settings file did not yield shipped defaults: $result"
# ── Empty file ───────────────────────────────────────────────────────────────
result="$(probe '')"
[[ "$result" == "$shipped" ]] || fail "an empty settings file did not yield shipped defaults: $result"
# ── Whitespace only ──────────────────────────────────────────────────────────
result="$(probe '
')"
[[ "$result" == "$shipped" ]] || fail "a whitespace-only file did not yield shipped defaults: $result"
# ── Truncated mid-object, the shape a crashed write leaves behind ────────────
result="$(probe '{ "gapsOut": 24, "windowRounding":')"
[[ "$result" == "$shipped" ]] || fail "a truncated file did not yield shipped defaults: $result"
# ── Not JSON at all ──────────────────────────────────────────────────────────
result="$(probe 'gaps_out = 24')"
[[ "$result" == "$shipped" ]] || fail "a non-JSON file did not yield shipped defaults: $result"
# ── A JSON array rather than an object ───────────────────────────────────────
result="$(probe '[1, 2, 3]')"
[[ "$result" == "$shipped" ]] || fail "a top-level array did not yield shipped defaults: $result"
# ── Wrong types: each bad value falls back on its own ────────────────────────
result="$(probe '{"gapsOut": "wide", "windowRounding": 24, "blurEnabled": 3, "keyboardLayout": 7}')"
[[ "$result" == 'gapsOut=10 rounding=24 blur=true layout=us hdr=1' ]] \
|| fail "wrong-typed values did not fall back per key: $result"
# ── Good values are actually used ────────────────────────────────────────────
result="$(probe '{"gapsOut": 24, "windowRounding": 6, "blurEnabled": false, "keyboardLayout": "us,de", "autoHdr": false}')"
[[ "$result" == 'gapsOut=24 rounding=6 blur=false layout=us,de hdr=0' ]] \
|| fail "stored values were not applied: $result"
# ── A boolean maps onto an integer option, matching SystemSettings.hyprValue ─
result="$(probe '{"autoHdr": true}')"
[[ "$result" == 'gapsOut=10 rounding=18 blur=true layout=us hdr=1' ]] \
|| fail "getInt did not convert a boolean: $result"
# ── Escapes and nesting do not break the reader ──────────────────────────────
result="$(probe '{"note": "a \"quoted\" value\nwith escapes", "nested": {"a": [1, 2, {"b": null}]}, "gapsOut": 12}')"
[[ "$result" == 'gapsOut=12 rounding=18 blur=true layout=us hdr=1' ]] \
|| fail "a file with escapes and nesting was not parsed: $result"
# ── The real config parses in every one of those states ─────────────────────
# This is the case that actually protects the desktop: prefs.lua returning
# defaults is only useful if Hyprland still accepts the config around it.
#
# Hyprland resolves its own config from $HOME/.config/hypr regardless of
# XDG_CONFIG_HOME, while prefs.lua honors XDG_CONFIG_HOME. That asymmetry is
# what makes this loop useful rather than vacuous: the *real* looks.lua and
# input.lua are parsed against a *fixture* settings file. The probe cases above
# already establish that prefs.lua reads the fixture and not the live file.
for settings in '__absent__' '' '{ "gapsOut": 24, "windowRounding":' 'gaps_out = 24' '{"gapsOut": "wide"}' \
'{"gapsOut": 24, "windowRounding": 6, "blurEnabled": false, "borderSize": 0, "keyboardLayout": "us,de"}'; do
if [[ "$settings" == "__absent__" ]]; then
rm -f "$work/config/panama/settings.json"
else
mkdir -p "$work/config/panama"
printf '%s' "$settings" >"$work/config/panama/settings.json"
fi
output="$(XDG_CONFIG_HOME="$work/config" Hyprland --verify-config 2>&1 || true)"
grep -q 'config ok' <<<"$output" \
|| fail "Hyprland rejected the config with settings=<$settings>: $(tail -5 <<<"$output")"
done
trap - EXIT
cleanup
printf 'prefs fallback contract: PASS\n'