Files
Panama/tests/quickshell/control-center-contract
T
Gabriel Brown e1faaf7a76 Drop the extension, and give the test suite a front door
Phase 6, the last of the fresh-install spec.

159 scripts lose their .sh: 110 contracts, 47 Vicinae commands, 2 compositor
contracts. A shebang and the executable bit already select the interpreter. The
extension only ever added something that had to stay in sync, and the rename
proved the point twice over in the space of an hour.

The spec's stated risk was Vicinae's script discovery. One script was renamed and
reloaded on its own before the other 46 followed; it came back as
scripts:panama.capture and all 47 resolve. What the probe turned up instead is
that the extension was never only a filename: Vicinae's command IDs embed it, so
every ID changed. Nothing in this repository refers to them, so nothing breaks.
The only trace is Vicinae's metadata.json, whose visited map had two Panama
entries that are now orphaned -- two commands lost their usage ranking and will
earn it back. Worth knowing before anyone renames these again on a machine that
has a keybind pointing at one.

Rewriting the references by exact filename missed two things it structurally
could not see: a name built from a variable, settings-$page.sh, and a glob,
-name '*.sh'. Both were in the contract that counts the generated commands, which
promptly reported 47 expected and 0 found. The mechanical part of a rename is the
part that looks finished.

The three subcommands. panama doctor fronts a health check that already existed
and already ran at the end of every install but could not be reached from a
terminal. panama upgrade re-runs the installer from anywhere. panama test runs
the suite, which had no entry point at all -- 121 files that were the main safety
net in this repository and were invisible in it.

Writing that runner found three tests nothing was running.
calendar_agenda_bridge_test, home_assistant_bridge_test and kdeconnect_bridge_test
are unittest suites without the executable bit, so no contract invoked them and
the first draft of the runner skipped them silently. All three pass, and have
passed unobserved for weeks. The runner collects *_test.py as well now, because a
runner with a blind spot is worse than no runner for the same reason a dependency
checker with one is: it reports PASS.

Six worktrees pruned. Each was re-checked rather than trusted to the spec's list,
and two needed it: panama-commands is not on feat/panama-commands but on
feat/gnome-tweaks-parity, and fix/panama-displays-review reads [ahead 3] -- ahead
of its remote, not of main, with every commit patch-equivalent to landed work.
roadmap-completion stays; it has five commits that are genuinely unlanded. The
branches are left alone: pruning a worktree costs nothing, deleting a branch is a
decision.

121 contracts pass.

Claude-Session: https://claude.ai/code/session_01NvgBuSWB5sE43yWmg21ozj
2026-08-20 21:55:55 -04:00

220 lines
10 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
project_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
source_config_path="$project_root/config/dot/quickshell"
quicksettings_path="$source_config_path/modules/quicksettings"
state_home="$(mktemp -d /tmp/panama-control-center-ui.XXXXXX)"
config_path="$state_home/quickshell"
helper_log="$state_home/home-helper.log"
shell_log="$state_home/quickshell.log"
fail() {
printf 'Control Center contract: %s\n' "$1" >&2
exit 1
}
qs_for_test() {
QS_CONFIG_PATH="$config_path" XDG_STATE_HOME="$state_home" \
QS_DISABLE_CRASH_HANDLER=1 \
PANAMA_HOME_HELPER_LOG="$helper_log" \
qs -p "$config_path" "$@"
}
stop_test_shell() {
qs_for_test kill >/dev/null 2>&1 || true
for _ in $(seq 1 80); do
if ! qs_for_test list 2>/dev/null | rg '^Instance ' >/dev/null \
&& ! qs_for_test ipc show >/dev/null 2>&1; then
return 0
fi
sleep 0.1
done
return 1
}
cleanup() {
qs_for_test ipc call quicksettings close >/dev/null 2>&1 || true
qs_for_test ipc call kdeconnect reset >/dev/null 2>&1 || true
qs_for_test ipc call home-assistant reset >/dev/null 2>&1 || true
if stop_test_shell; then
rm -rf "$state_home"
else
printf 'Control Center contract: branch shell did not stop; retained %s\n' \
"$state_home" >&2
fi
}
trap cleanup EXIT
start_test_shell() {
stop_test_shell || fail 'pre-existing branch shell did not stop cleanly'
for _attempt in 1 2; do
qs_for_test --daemonize >"$shell_log" 2>&1
for _ in $(seq 1 80); do
if qs_for_test ipc show 2>/dev/null | rg '^target quicksettings$' >/dev/null; then
return
fi
sleep 0.1
done
stop_test_shell || fail 'failed branch-shell attempt did not stop cleanly'
done
sed -n '1,200p' "$shell_log" >&2
fail 'isolated branch shell did not start'
}
rg -Fq 'readonly property int controlCenterWidth: 430' \
"$source_config_path/config/Theme.qml" \
|| fail 'approved Control Center width is missing'
# The margin is the GAP alone. This used to assert the bar height plus the gap,
# which is what the code said and what made the panel open 38 pixels below a bar
# it was written to sit 2 pixels under: an exclusiveZone of 0 already places the
# surface below the bar's reserved space, so naming the bar height again counted
# it twice. The contract agreed with the code and so the bug was invisible to
# both. layer-margin-contract now holds that rule for every surface.
# Asserted as a RULE rather than a literal, and derived from two files so that
# drift in either direction fails. The Control Center used to carry its own gap
# constant set to 2 while every other popover used barGap * 2, leaving the widest
# surface in the shell hanging ten pixels higher than the date menu beside it.
# Pinning the new number here would repeat the mistake this file already records
# below: a contract that agrees with the code cannot see the code being wrong.
control_center_gap="$(rg -o 'margins\.top: (.+)$' -r '$1' "$quicksettings_path/QuickSettings.qml" | head -1)"
date_menu_gap="$(rg -o 'margins\.top: (.+)$' -r '$1' "$source_config_path/modules/datemenu/DateMenu.qml" | head -1)"
[[ -n "$control_center_gap" ]] || fail 'Control Center sets no top margin at all'
[[ "$control_center_gap" == "$date_menu_gap" ]] \
|| fail "Control Center hangs at '$control_center_gap' while the date menu uses '$date_menu_gap'"
rg -Fq 'implicitWidth: Theme.controlCenterWidth' \
"$quicksettings_path/QuickSettings.qml" \
|| fail 'Control Center window does not use its geometry token'
rg -Fq 'HomeControls' \
"$quicksettings_path/QuickSettingsPanel.qml" \
|| fail 'Home controls are not mounted'
rg -Fq 'PhoneControls' \
"$quicksettings_path/QuickSettingsPanel.qml" \
|| fail 'Phone controls are not mounted'
rg -Fq 'visible: KdeConnect.phoneReachable' \
"$source_config_path/modules/bar/StatusCluster.qml" \
|| fail 'reachable phone state is absent from the bar'
for component in ControlSectionHeader HomeBrightnessSlider HomeControls HomeTile PhoneActions PhoneControls RecentExchange; do
[[ -f "$quicksettings_path/$component.qml" ]] \
|| fail "$component is missing"
done
[[ "$(rg -c 'id: "(share|clipboard|ring|messages)"' "$quicksettings_path/PhoneActions.qml")" -eq 4 ]] \
|| fail 'Phone controls do not expose four stable action models'
rg -Fq 'columns: 4' "$quicksettings_path/PhoneControls.qml" \
|| fail 'Phone actions are not arranged in four equal columns'
if rg -Fq '.filter(' "$quicksettings_path/PhoneActions.qml"; then
fail 'Phone action columns change when a KDE capability is unavailable'
fi
rg -Fq 'SystemSettings.bluebubblesAvailable' "$quicksettings_path/PhoneControls.qml" \
|| fail 'Messages action is not independently enabled by BlueBubbles'
rg -Fq 'KdeConnect.phoneReachable' "$quicksettings_path/PhoneControls.qml" \
|| fail 'KDE action reachability behavior is missing'
[[ -f "$quicksettings_path/qmldir" ]] \
|| fail 'quick-settings module manifest is missing'
rg -Fq 'HomeBrightnessSlider 1.0 HomeBrightnessSlider.qml' \
"$quicksettings_path/qmldir" \
|| fail 'brightness slider is not registered in the quick-settings module'
rg -Fq 'PhoneActions 1.0 PhoneActions.qml' \
"$quicksettings_path/qmldir" \
|| fail 'Phone actions model is not registered in the quick-settings module'
[[ "$(rg -c '^[[:space:]]*columns: 2$' "$quicksettings_path/HomeControls.qml")" -ge 2 ]] \
|| fail 'resting and expanded Home shelves are not both two-column grids'
rg -Fq 'model: HomeAssistant.visibleEntities' "$quicksettings_path/HomeControls.qml" \
|| fail 'resting Home shelf does not use the first four selected lights'
rg -Fq 'model: HomeAssistant.selectedEntities' "$quicksettings_path/HomeControls.qml" \
|| fail 'expanded Home shelf does not use every selected light'
rg -Fq 'HomeAssistant.pendingFor(' "$quicksettings_path/HomeControls.qml" \
|| fail 'Home tiles do not receive per-light pending brightness'
rg -Fq 'HomeAssistant.setBrightness(' "$quicksettings_path/HomeControls.qml" \
|| fail 'Home brightness commits are not wired to the service'
rg -Fq 'ShellState.openSettings("home-phone")' "$quicksettings_path/HomeControls.qml" \
|| fail 'Manage in Settings does not open Home & Phone'
rg -Fq 'signal previewChanged(int value)' "$quicksettings_path/HomeBrightnessSlider.qml" \
|| fail 'brightness slider has no preview contract'
rg -Fq 'signal committed(int value)' "$quicksettings_path/HomeBrightnessSlider.qml" \
|| fail 'brightness slider has no release-commit contract'
rg -Fq 'onReleased: event =>' "$quicksettings_path/HomeBrightnessSlider.qml" \
|| fail 'brightness slider does not route pointer release through its interaction state'
rg -Fq 'root.releasePointerInteraction();' "$quicksettings_path/HomeBrightnessSlider.qml" \
|| fail 'brightness slider release is not wired to one-shot commit semantics'
rg -Fq 'onCanceled: root.cancelPointerInteraction()' "$quicksettings_path/HomeBrightnessSlider.qml" \
|| fail 'brightness slider does not restore its external value when a Flickable steals the pointer'
if rg -Fq 'preventStealing: true' "$quicksettings_path/HomeBrightnessSlider.qml"; then
fail 'brightness slider blocks the expanded shelf from stealing vertical drags'
fi
rg -Fq 'onWheel:' "$quicksettings_path/HomeBrightnessSlider.qml" \
|| fail 'brightness slider has no wheel commit path'
rg -Fq 'onCommitted: value => root.brightnessRequested(value)' "$quicksettings_path/HomeTile.qml" \
|| fail 'Home tile does not forward the slider release commit'
rg -Fq 'accessibleName: root.entity.name + " brightness"' "$quicksettings_path/HomeTile.qml" \
|| fail 'Home tile does not give its dimmer an accessory-specific accessible name'
cp -a "$source_config_path" "$config_path"
: >"$helper_log"
cat >"$config_path/scripts/panama-home-assistant" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
printf '%s\n' "$*" >>"$PANAMA_HOME_HELPER_LOG"
case "${1:-}" in
catalog)
printf '%s\n' '{"ok":false,"configured":false,"entities":[],"legacyEntityIds":[],"error":"test-helper"}'
;;
toggle|brightness)
printf '%s\n' '{"ok":false,"error":"contract-action-forbidden"}'
exit 73
;;
esac
EOF
chmod +x "$config_path/scripts/panama-home-assistant"
start_test_shell
qs_for_test ipc call home-assistant fixture ready >/dev/null
home_ready="$(qs_for_test ipc call home-assistant status)"
jq -e '.fixture == true and .visibleCount == 4 and .configuredCount == 7' \
<<<"$home_ready" >/dev/null \
|| fail 'Home fixture does not expose four resting and seven expanded accessories'
qs_for_test ipc call kdeconnect fixture reachable >/dev/null
qs_for_test ipc call quicksettings open >/dev/null
hyprctl layers | rg -q 'namespace: qs-popover-quicksettings' \
|| fail 'Control Center layer did not map'
jq -e '.open == true and .expandedSection == ""' \
<<<"$(qs_for_test ipc call quicksettings status)" >/dev/null \
|| fail 'Control Center did not open in its resting state'
qs_for_test ipc call quicksettings section home >/dev/null
jq -e '.open == true and .expandedSection == "home"' \
<<<"$(qs_for_test ipc call quicksettings status)" >/dev/null \
|| fail 'Home section did not expand'
qs_for_test ipc call quicksettings section phone >/dev/null
jq -e '.open == true and .expandedSection == "phone"' \
<<<"$(qs_for_test ipc call quicksettings status)" >/dev/null \
|| fail 'Phone did not replace Home as the expanded section'
qs_for_test ipc call quicksettings section phone >/dev/null
jq -e '.open == true and .expandedSection == ""' \
<<<"$(qs_for_test ipc call quicksettings status)" >/dev/null \
|| fail 'expanded Phone section did not collapse'
if rg '^(toggle|brightness)( |$)' "$helper_log" >&2; then
fail 'Control Center contract attempted a real Home action path'
fi
trap - EXIT
cleanup
[[ ! -e "$state_home" ]] \
|| fail 'temporary Control Center state was not removed after shell exit'
printf 'Control Center contract: PASS\n'