Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
63f4bdb547 | ||
|
|
1a93a788fa |
@@ -209,6 +209,8 @@ stale_json="$(jq '.[1].y = 250' "$monitor_state")"
|
|||||||
run ipc --pid "$harness_pid" call displays-test injectReadback "$stale_json" "$((generation - 1))" >/dev/null
|
run ipc --pid "$harness_pid" call displays-test injectReadback "$stale_json" "$((generation - 1))" >/dev/null
|
||||||
jq -e '.canConfirm == false' <<<"$(transaction_status)" >/dev/null \
|
jq -e '.canConfirm == false' <<<"$(transaction_status)" >/dev/null \
|
||||||
|| fail 'stale readback confirmed a newer operation'
|
|| fail 'stale readback confirmed a newer operation'
|
||||||
|
[[ "$(run ipc --pid "$harness_pid" call displays-test confirmChange)" == "false" ]] \
|
||||||
|
|| fail 'Keep accepted a display change without a generation-matched readback'
|
||||||
rm -f "$fixture/no-apply"
|
rm -f "$fixture/no-apply"
|
||||||
run ipc --pid "$harness_pid" call displays-test expireApplyVerification >/dev/null
|
run ipc --pid "$harness_pid" call displays-test expireApplyVerification >/dev/null
|
||||||
wait_for '.busy == false and .awaiting == false' >/dev/null
|
wait_for '.busy == false and .awaiting == false' >/dev/null
|
||||||
|
|||||||
@@ -335,9 +335,14 @@ done
|
|||||||
[[ "$(status | jq -r .overridden)" == "false" ]] || fail 'an unconfirmed change was written to the settings store'
|
[[ "$(status | jq -r .overridden)" == "false" ]] || fail 'an unconfirmed change was written to the settings store'
|
||||||
|
|
||||||
# ── A confirmed change is what writes ────────────────────────────────────────
|
# ── A confirmed change is what writes ────────────────────────────────────────
|
||||||
run ipc call displays-test applyScale "$target_scale" >/dev/null
|
[[ "$(run ipc call displays-test applyScale "$target_scale")" == "true" ]] \
|
||||||
[[ "$(run ipc call displays-test confirmChange)" == "false" ]] \
|
|| fail 'the confirmed-change fixture could not apply'
|
||||||
|| fail 'Keep accepted a display change before compositor readback'
|
|
||||||
|
# Hyprland can apply and read back a change faster than two IPC round trips, so
|
||||||
|
# this live test cannot reliably observe the pre-readback state. The
|
||||||
|
# fake-compositor contract deterministically holds that boundary open and
|
||||||
|
# proves Keep refuses it; this path proves a real readback eventually enables
|
||||||
|
# and persists Keep on the physical display.
|
||||||
verified=false
|
verified=false
|
||||||
for _ in $(seq 1 30); do
|
for _ in $(seq 1 30); do
|
||||||
[[ "$(status | jq -r .canConfirm)" == "true" ]] && { verified=true; break; }
|
[[ "$(status | jq -r .canConfirm)" == "true" ]] && { verified=true; break; }
|
||||||
|
|||||||
Executable
+100
@@ -0,0 +1,100 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# A search result must open the page that actually contains the setting.
|
||||||
|
#
|
||||||
|
# Every schema group routes to one page in services/SettingsSearch.qml, and the
|
||||||
|
# ownership rule in modules/settings/README.md derives a setting's owner from
|
||||||
|
# that route. Nothing checked the two agreed, and two groups had drifted:
|
||||||
|
#
|
||||||
|
# weather routed to Appearance while every weather control lived on Home, so
|
||||||
|
# searching "temperature unit" opened a page without it.
|
||||||
|
#
|
||||||
|
# vitals routed to Appearance, but the refresh interval sat on Home while the
|
||||||
|
# toggles it governs sat on Appearance -- one concept, two pages, which is
|
||||||
|
# precisely what the ownership rule forbids.
|
||||||
|
#
|
||||||
|
# Neither is visible from the code: each page is coherent on its own, and only
|
||||||
|
# following a search result reveals the mismatch.
|
||||||
|
#
|
||||||
|
# Groups whose settings are driven by bespoke UI rather than schema-bound rows
|
||||||
|
# are skipped, because there is nothing to locate.
|
||||||
|
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
schema="$repo_dir/config/dot/quickshell/config/PreferenceSchema.qml"
|
||||||
|
search="$repo_dir/config/dot/quickshell/services/SettingsSearch.qml"
|
||||||
|
pages_dir="$repo_dir/config/dot/quickshell/modules/settings"
|
||||||
|
|
||||||
|
fail() {
|
||||||
|
printf 'search routing contract: %s\n' "$1" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
routes="$(grep -oE '"[a-zA-Z]+": "[a-z-]+"' "$search" | tr -d '"' | tr ':' ' ')"
|
||||||
|
[[ -n "$routes" ]] || fail 'no group routes found -- this contract is not reading SettingsSearch correctly'
|
||||||
|
|
||||||
|
# page id -> Page component file, as SettingsShell maps them.
|
||||||
|
page_file() {
|
||||||
|
case "$1" in
|
||||||
|
home) printf 'HomePage.qml' ;;
|
||||||
|
appearance) printf 'AppearancePage.qml' ;;
|
||||||
|
displays) printf 'DisplaysPage.qml' ;;
|
||||||
|
connectivity) printf 'ConnectivityPage.qml' ;;
|
||||||
|
home-phone) printf 'HomePhonePage.qml' ;;
|
||||||
|
desktop) printf 'DesktopPage.qml' ;;
|
||||||
|
sound) printf 'SoundPage.qml' ;;
|
||||||
|
notifications) printf 'NotificationsPage.qml' ;;
|
||||||
|
screen-intelligence) printf 'ScreenIntelligencePage.qml' ;;
|
||||||
|
shortcuts) printf 'ShortcutsPage.qml' ;;
|
||||||
|
mouse) printf 'MousePage.qml' ;;
|
||||||
|
privacy) printf 'PrivacyPage.qml' ;;
|
||||||
|
region) printf 'RegionPage.qml' ;;
|
||||||
|
accounts) printf 'OnlineAccountsPage.qml' ;;
|
||||||
|
accessibility) printf 'AccessibilityPage.qml' ;;
|
||||||
|
power) printf 'PowerPage.qml' ;;
|
||||||
|
datetime) printf 'DateTimePage.qml' ;;
|
||||||
|
applications) printf 'ApplicationsPage.qml' ;;
|
||||||
|
services) printf 'HealthPage.qml' ;;
|
||||||
|
about) printf 'AboutPage.qml' ;;
|
||||||
|
*) printf '' ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
violations=0
|
||||||
|
checked=0
|
||||||
|
|
||||||
|
while read -r group page; do
|
||||||
|
[[ -n "$group" && -n "$page" ]] || continue
|
||||||
|
|
||||||
|
target="$(page_file "$page")"
|
||||||
|
[[ -n "$target" ]] || fail "group \"$group\" routes to \"$page\", which is not a known page"
|
||||||
|
[[ -r "$pages_dir/$target" ]] || fail "group \"$group\" routes to \"$page\" but $target does not exist"
|
||||||
|
|
||||||
|
# Keys belonging to this group.
|
||||||
|
keys="$(awk -v g="\"$group\"" '
|
||||||
|
/key: "/ { match($0, /key: "[a-zA-Z]+"/); k = substr($0, RSTART+6, RLENGTH-7) }
|
||||||
|
$0 ~ "group: " g { if (k != "") print k; k = "" }
|
||||||
|
' "$schema" | sort -u)"
|
||||||
|
|
||||||
|
while read -r key; do
|
||||||
|
[[ -n "$key" ]] || continue
|
||||||
|
|
||||||
|
# Only settings rendered as schema-bound rows can be located at all.
|
||||||
|
homes="$(grep -rl "setting: \"$key\"" "$pages_dir"/*Page.qml 2>/dev/null | xargs -n1 basename 2>/dev/null || true)"
|
||||||
|
[[ -n "$homes" ]] || continue
|
||||||
|
|
||||||
|
checked=$((checked + 1))
|
||||||
|
grep -qx "$target" <<<"$homes" && continue
|
||||||
|
|
||||||
|
printf ' %s (group "%s") routes to %s but appears only on: %s\n' \
|
||||||
|
"$key" "$group" "$page" "$(tr '\n' ' ' <<<"$homes")" >&2
|
||||||
|
violations=$((violations + 1))
|
||||||
|
done <<<"$keys"
|
||||||
|
done <<<"$routes"
|
||||||
|
|
||||||
|
if (( violations > 0 )); then
|
||||||
|
fail "$violations setting(s) route to a page that does not contain them, so searching for them opens the wrong page"
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf 'search routing contract: PASS (%d routed settings)\n' "$checked"
|
||||||
Reference in New Issue
Block a user