127 lines
5.6 KiB
Bash
Executable File
127 lines
5.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
set -euo pipefail
|
|
|
|
repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
preview="$repo_dir/config/dot/quickshell/modules/settings/LockScreenPreview.qml"
|
|
appearance="$repo_dir/config/dot/quickshell/modules/settings/AppearancePage.qml"
|
|
power="$repo_dir/config/dot/quickshell/modules/settings/PowerPage.qml"
|
|
privacy="$repo_dir/config/dot/quickshell/modules/settings/PrivacyPage.qml"
|
|
harness="$repo_dir/config/dot/quickshell/lock-screen-settings-harness.qml"
|
|
state_home="$(mktemp -d /tmp/panama-lock-settings-state.XXXXXX)"
|
|
shell_log="$state_home/quickshell.log"
|
|
harness_pid=""
|
|
|
|
fail() {
|
|
printf 'lock screen settings contract: %s\n' "$1" >&2
|
|
[[ -s "$shell_log" ]] && sed -n '1,160p' "$shell_log" >&2
|
|
exit 1
|
|
}
|
|
|
|
instances_for_harness() {
|
|
qs list --all 2>/dev/null | awk -v expected="$harness" '
|
|
/^Instance / { pid = "" }
|
|
/^[[:space:]]*Process ID:/ { pid = $3 }
|
|
/^[[:space:]]*Config path:/ {
|
|
path = $0
|
|
sub(/^[[:space:]]*Config path: /, "", path)
|
|
if (path == expected && pid ~ /^[0-9]+$/) print pid
|
|
}
|
|
'
|
|
}
|
|
|
|
cleanup() {
|
|
if [[ "$harness_pid" =~ ^[0-9]+$ ]] && kill -0 "$harness_pid" 2>/dev/null; then
|
|
kill "$harness_pid" 2>/dev/null || true
|
|
fi
|
|
rm -rf "$state_home"
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
[[ -f "$preview" ]] || fail 'LockScreenPreview.qml is missing'
|
|
rg -Fq 'title: "Lock screen"' "$appearance" || fail 'Appearance has no Lock screen card'
|
|
for key in lockBackgroundMode lockBlurLevel lockShowClock lockShowDate lockShowUser lockFadeOnEmpty; do
|
|
rg -Fq "setting: \"$key\"" "$appearance" || fail "Appearance does not expose $key"
|
|
! rg -Fq "setting: \"$key\"" "$power" "$privacy" \
|
|
|| fail "$key was duplicated onto Power or Privacy"
|
|
done
|
|
|
|
# ── One home for the lock timings, and a signpost where the copy used to be ──
|
|
#
|
|
# Privacy carried a whole second "Screen lock" card: the same idle timings, the
|
|
# same preferences, bound twice. Two sliders writing one value is not a
|
|
# convenience -- it is a page where the number you are looking at may not be the
|
|
# number you last set. The card is gone, and what replaced it is a pointer,
|
|
# because deleting a duplicate without saying where the original lives just
|
|
# moves the confusion.
|
|
! rg -Fq 'title: "Screen lock"' "$privacy" \
|
|
|| fail 'the duplicated Screen lock card is back on the Privacy page'
|
|
for key in lockMinutes lockMinutesBattery lockOnSleep; do
|
|
rg -Fq "setting: \"$key\"" "$power" || fail "Power does not own $key"
|
|
! rg -Fq "setting: \"$key\"" "$privacy" \
|
|
|| fail "$key is bound on Privacy as well as Power, so one preference has two controls"
|
|
done
|
|
rg -Fq 'ShellState.openSettings("power")' "$privacy" \
|
|
|| fail 'Privacy dropped the lock card without pointing anywhere, so the settings look deleted'
|
|
|
|
# The lock screen is the third card of the Background tab, after the still and
|
|
# video wallpaper cards. It belongs there because it is a picture of the
|
|
# desktop: the background is what it blurs, and the card above is what it
|
|
# takes a still frame of.
|
|
python3 - "$appearance" <<'PY' || fail 'the Lock screen card is not the last card of the Background tab'
|
|
import re
|
|
import sys
|
|
|
|
text = open(sys.argv[1], encoding="utf-8").read()
|
|
order = ['title: "Background"', 'title: "Video playback"', 'title: "Lock screen"']
|
|
positions = []
|
|
for title in order:
|
|
if title not in text:
|
|
raise SystemExit(f"Appearance has no {title} card")
|
|
positions.append(text.index(title))
|
|
if positions != sorted(positions):
|
|
raise SystemExit("the Background tab's cards are out of order")
|
|
|
|
# Each of the three is gated on the same tab, so none of them strands the
|
|
# others on a page nobody opens.
|
|
for card in re.findall(r'SettingsCard \{(.*?)\n \}', text, re.S):
|
|
for title in order:
|
|
if title in card and 'root.tab === "background"' not in card:
|
|
raise SystemExit(f"the {title} card is not on the Background tab")
|
|
PY
|
|
|
|
qs_for_harness() {
|
|
if [[ "$harness_pid" =~ ^[0-9]+$ && "${1:-}" == "ipc" ]]; then
|
|
XDG_STATE_HOME="$state_home" qs -p "$harness" ipc --pid "$harness_pid" "${@:2}"
|
|
else
|
|
XDG_STATE_HOME="$state_home" qs -p "$harness" "$@"
|
|
fi
|
|
}
|
|
|
|
before_lockers="$(pgrep -x hyprlock | sort -n || true)"
|
|
qs_for_harness --daemonize >"$shell_log" 2>&1 || fail 'isolated preview harness did not launch'
|
|
for _ in $(seq 1 60); do
|
|
harness_pid="$(instances_for_harness | head -1)"
|
|
if [[ "$harness_pid" =~ ^[0-9]+$ ]] \
|
|
&& qs_for_harness ipc show 2>/dev/null | rg -q '^target lock-screen-settings-test$'; then
|
|
break
|
|
fi
|
|
sleep 0.1
|
|
done
|
|
[[ "$harness_pid" =~ ^[0-9]+$ ]] || fail 'isolated preview harness process did not start'
|
|
|
|
status="$(qs_for_harness ipc call lock-screen-settings-test status)"
|
|
jq -e '
|
|
.screenshot == {"mode":"screenshot","wallpaperVisible":false,"blurStrength":0.8,"clockVisible":true,"dateVisible":true,"userVisible":true,"passwordVisible":true}
|
|
and .wallpaper == {"mode":"wallpaper","wallpaperVisible":true,"blurStrength":0.4,"clockVisible":false,"dateVisible":true,"userVisible":false,"passwordVisible":false}
|
|
and .solid == {"mode":"solid","wallpaperVisible":false,"blurStrength":0,"clockVisible":true,"dateVisible":false,"userVisible":true,"passwordVisible":true}
|
|
' <<<"$status" >/dev/null || fail "preview roles did not follow their settings: $status"
|
|
|
|
after_lockers="$(pgrep -x hyprlock | sort -n || true)"
|
|
[[ "$after_lockers" == "$before_lockers" ]] || fail 'preview started a real hyprlock process'
|
|
if rg -n 'ReferenceError|TypeError|Binding loop|Unable to assign|Cannot assign|Failed to load' "$shell_log"; then
|
|
fail 'preview harness emitted a QML runtime warning'
|
|
fi
|
|
|
|
printf 'lock screen settings contract: PASS\n'
|