Test: Harden manifest validation

This commit is contained in:
Gabriel Brown
2026-08-26 22:00:52 -04:00
parent 1192ad64dc
commit 43d1e15858
2 changed files with 87 additions and 35 deletions
+59 -29
View File
@@ -17,66 +17,83 @@ hermetic tests/hypr/window-rules-contract
hermetic tests/hypr/workspace-rules-contract hermetic tests/hypr/workspace-rules-contract
hermetic tests/quickshell/accent-controls-contract hermetic tests/quickshell/accent-controls-contract
hermetic tests/quickshell/accessibility-contract hermetic tests/quickshell/accessibility-contract
# Pushes a fixture privacy state into the running shell and opens the activity\npanel over the desktop. # Pushes a fixture privacy state into the running shell and opens the activity
# panel over the desktop.
live-compositor,live-desktop tests/quickshell/activity-indicator-contract live-compositor,live-desktop tests/quickshell/activity-indicator-contract
# Pushes fixture privacy states and Signal Glass events into the running shell,\nand dismisses the live capsule. # Pushes fixture privacy states and Signal Glass events into the running shell,
# and dismisses the live capsule.
live-desktop tests/quickshell/activity-state-contract live-desktop tests/quickshell/activity-state-contract
# Reads the host GNOME accent enum through gsettings to verify Panama's accent\nmapping. # Reads the host GNOME accent enum through gsettings to verify Panama's accent
# mapping.
live-host tests/quickshell/adwaita-accent-contract live-host tests/quickshell/adwaita-accent-contract
hermetic tests/quickshell/agent-usage-contract hermetic tests/quickshell/agent-usage-contract
hermetic tests/quickshell/app-library-contract hermetic tests/quickshell/app-library-contract
# Reads the host PipeWire application-volume state through the live Quickshell\nservice. # Reads the host PipeWire application-volume state through the live Quickshell
# service.
live-host tests/quickshell/application-volume-contract live-host tests/quickshell/application-volume-contract
hermetic tests/quickshell/applications-settings-contract hermetic tests/quickshell/applications-settings-contract
hermetic tests/quickshell/bar-visibility-contract hermetic tests/quickshell/bar-visibility-contract
hermetic tests/quickshell/battery-contract hermetic tests/quickshell/battery-contract
hermetic tests/quickshell/bluetooth-discovery-contract hermetic tests/quickshell/bluetooth-discovery-contract
hermetic tests/quickshell/brightness-helper-contract hermetic tests/quickshell/brightness-helper-contract
# Opens the live agenda popover and notification centre, and starts a real focus\nsession on the running shell. # Opens the live agenda popover and notification centre, and starts a real focus
# session on the running shell.
live-compositor,live-desktop tests/quickshell/calendar-agenda-contract live-compositor,live-desktop tests/quickshell/calendar-agenda-contract
# Queries the host calendar helper for real configured sources and upcoming\nevents. # Queries the host calendar helper for real configured sources and upcoming
# events.
live-host tests/quickshell/calendar-agenda-helper-contract live-host tests/quickshell/calendar-agenda-helper-contract
hermetic tests/quickshell/calendar_agenda_bridge_test.py hermetic tests/quickshell/calendar_agenda_bridge_test.py
# Opens and closes the cheatsheet overlay on the running shell. # Opens and closes the cheatsheet overlay on the running shell.
live-compositor,live-desktop tests/quickshell/cheatsheet-contract live-compositor,live-desktop tests/quickshell/cheatsheet-contract
# Reads the host Quickshell networking type metadata to verify the live\nNetworkManager boundary. # Reads the host Quickshell networking type metadata to verify the live
# NetworkManager boundary.
live-host tests/quickshell/connectivity-contract live-host tests/quickshell/connectivity-contract
hermetic tests/quickshell/containers-contract hermetic tests/quickshell/containers-contract
# Maps the Control Center and reads its live compositor layer to verify the\nwindow is present. # Maps the Control Center and reads its live compositor layer to verify the
# window is present.
live-compositor,live-desktop tests/quickshell/control-center-contract live-compositor,live-desktop tests/quickshell/control-center-contract
# Maps the Control Center service surface and reads the live compositor while\nexercising its IPC services. # Maps the Control Center service surface and reads the live compositor while
# exercising its IPC services.
live-compositor,live-desktop tests/quickshell/control-center-services-contract live-compositor,live-desktop tests/quickshell/control-center-services-contract
hermetic tests/quickshell/curated-events-policy-contract hermetic tests/quickshell/curated-events-policy-contract
hermetic tests/quickshell/declared-assets-contract hermetic tests/quickshell/declared-assets-contract
hermetic tests/quickshell/declared-dependencies-contract hermetic tests/quickshell/declared-dependencies-contract
hermetic tests/quickshell/default-apps-contract hermetic tests/quickshell/default-apps-contract
# Reads the host xdg-mime default handlers for the configured application\nfamilies. # Reads the host xdg-mime default handlers for the configured application
# families.
live-host tests/quickshell/default-apps-family-contract live-host tests/quickshell/default-apps-family-contract
# Changes the real default browser through the default-apps helper -- xdg-mime\nand xdg-settings defaults on this machine -- and puts it back. # Changes the real default browser through the default-apps helper -- xdg-mime
# and xdg-settings defaults on this machine -- and puts it back.
live-desktop tests/quickshell/default-apps-roles-contract live-desktop tests/quickshell/default-apps-roles-contract
hermetic tests/quickshell/desktop-style-contract hermetic tests/quickshell/desktop-style-contract
# Reads the host disk and filesystem snapshot through the production disks\nhelper. # Reads the host disk and filesystem snapshot through the production disks
# helper.
live-host tests/quickshell/disks-contract live-host tests/quickshell/disks-contract
# Maps the display-arrangement surface and measures its live desktop geometry. # Maps the display-arrangement surface and measures its live desktop geometry.
live-desktop tests/quickshell/display-arrangement-contract live-desktop tests/quickshell/display-arrangement-contract
hermetic tests/quickshell/display-layout-contract hermetic tests/quickshell/display-layout-contract
hermetic tests/quickshell/display-transaction-contract hermetic tests/quickshell/display-transaction-contract
# Changes the real monitor's mode, position, scale and rotation through the live\ncompositor, restoring the display it started from. # Changes the real monitor's mode, position, scale and rotation through the live
# compositor, restoring the display it started from.
live-compositor,live-desktop tests/quickshell/displays-contract live-compositor,live-desktop tests/quickshell/displays-contract
hermetic tests/quickshell/dock-pins-contract hermetic tests/quickshell/dock-pins-contract
# Reads the live dock window geometry to verify its mapped position. # Reads the live dock window geometry to verify its mapped position.
live-desktop tests/quickshell/dock-position-contract live-desktop tests/quickshell/dock-position-contract
# Reads Hyprland option descriptions from the live compositor to verify enum\nmappings. # Reads Hyprland option descriptions from the live compositor to verify enum
# mappings.
live-compositor tests/quickshell/enum-hypr-map-contract live-compositor tests/quickshell/enum-hypr-map-contract
hermetic tests/quickshell/fingerprint-contract hermetic tests/quickshell/fingerprint-contract
# Reads the host firewall state through the production firewall helper. # Reads the host firewall state through the production firewall helper.
live-host tests/quickshell/firewall-contract live-host tests/quickshell/firewall-contract
hermetic tests/quickshell/focus-modes-contract hermetic tests/quickshell/focus-modes-contract
# Starts, pauses, reveals and ends a real focus session, and opens Mission\nControl on the running shell. # Starts, pauses, reveals and ends a real focus session, and opens Mission
# Control on the running shell.
live-compositor,live-desktop tests/quickshell/focus-session-contract live-compositor,live-desktop tests/quickshell/focus-session-contract
# Restarts panama-quickshell.service (or `qs kill`s the shell when unsupervised)\nwith a focus session in flight; an interrupted run leaves caffeine latched on. # Restarts panama-quickshell.service (or `qs kill`s the shell when unsupervised)
# with a focus session in flight; an interrupted run leaves caffeine latched on.
live-desktop tests/quickshell/focus-session-expiry live-desktop tests/quickshell/focus-session-expiry
# Restarts panama-quickshell.service (or `qs kill`s the shell when unsupervised)\nmid-session to prove a paused focus session survives it. # Restarts panama-quickshell.service (or `qs kill`s the shell when unsupervised)
# mid-session to prove a paused focus session survives it.
live-desktop tests/quickshell/focus-session-restart live-desktop tests/quickshell/focus-session-restart
# Reads the host gaming and graphics state through the production helper. # Reads the host gaming and graphics state through the production helper.
live-host tests/quickshell/gaming-contract live-host tests/quickshell/gaming-contract
@@ -106,7 +123,8 @@ hermetic tests/quickshell/lock-screen-settings-contract
hermetic tests/quickshell/lock-screen-theme-contract hermetic tests/quickshell/lock-screen-theme-contract
hermetic tests/quickshell/manual-contract hermetic tests/quickshell/manual-contract
hermetic tests/quickshell/migrations-contract hermetic tests/quickshell/migrations-contract
# Maps the My Home Settings surface and reads the live compositor during the\nsettings flow. # Maps the My Home Settings surface and reads the live compositor during the
# settings flow.
live-compositor,live-desktop tests/quickshell/my-home-settings-contract live-compositor,live-desktop tests/quickshell/my-home-settings-contract
hermetic tests/quickshell/network-tools-contract hermetic tests/quickshell/network-tools-contract
hermetic tests/quickshell/notification-app-rules-contract hermetic tests/quickshell/notification-app-rules-contract
@@ -115,12 +133,14 @@ hermetic tests/quickshell/osd-helper-contract
hermetic tests/quickshell/osd-model-contract hermetic tests/quickshell/osd-model-contract
hermetic tests/quickshell/osd-ui-contract hermetic tests/quickshell/osd-ui-contract
hermetic tests/quickshell/overview-keyboard-contract hermetic tests/quickshell/overview-keyboard-contract
# Spawns a real kitty window, moves it between your workspaces and into the\nscratchpad, and opens the overview. # Spawns a real kitty window, moves it between your workspaces and into the
# scratchpad, and opens the overview.
live-compositor,live-desktop tests/quickshell/overview-live-actions live-compositor,live-desktop tests/quickshell/overview-live-actions
# Opens the overview on the running shell and types a search into it. # Opens the overview on the running shell and types a search into it.
live-compositor,live-desktop tests/quickshell/overview-search-contract live-compositor,live-desktop tests/quickshell/overview-search-contract
hermetic tests/quickshell/overview-thumbnail-contract hermetic tests/quickshell/overview-thumbnail-contract
# Queries the live Quickshell overview-actions IPC surface for window action\navailability. # Queries the live Quickshell overview-actions IPC surface for window action
# availability.
live-host tests/quickshell/overview-window-actions-contract live-host tests/quickshell/overview-window-actions-contract
hermetic tests/quickshell/palette-contract hermetic tests/quickshell/palette-contract
hermetic tests/quickshell/panama-action-contract hermetic tests/quickshell/panama-action-contract
@@ -133,7 +153,8 @@ hermetic tests/quickshell/panama-doctor-contract
hermetic tests/quickshell/per-screen-surface-contract hermetic tests/quickshell/per-screen-surface-contract
hermetic tests/quickshell/permissions-contract hermetic tests/quickshell/permissions-contract
hermetic tests/quickshell/phone-messages-contract hermetic tests/quickshell/phone-messages-contract
# Maps the Phone page test-shell surface and reads the live compositor during\nthe page flow. # Maps the Phone page test-shell surface and reads the live compositor during
# the page flow.
live-compositor,live-desktop tests/quickshell/phone-page-contract live-compositor,live-desktop tests/quickshell/phone-page-contract
# Reads the host polkit-agent installation and session service configuration. # Reads the host polkit-agent installation and session service configuration.
live-host tests/quickshell/polkit-agent-contract live-host tests/quickshell/polkit-agent-contract
@@ -151,7 +172,8 @@ hermetic tests/quickshell/qmldir-registration-contract
live-compositor tests/quickshell/schema-hypr-shape-contract live-compositor tests/quickshell/schema-hypr-shape-contract
# Opens and closes the live overview. # Opens and closes the live overview.
live-compositor,live-desktop tests/quickshell/scratchpad-shelf-contract live-compositor,live-desktop tests/quickshell/scratchpad-shelf-contract
# Opens the live screen-intelligence and capture overlays and runs an analysis\nthrough them. # Opens the live screen-intelligence and capture overlays and runs an analysis
# through them.
live-desktop tests/quickshell/screen-intelligence-contract live-desktop tests/quickshell/screen-intelligence-contract
hermetic tests/quickshell/screen-intelligence-helper-contract hermetic tests/quickshell/screen-intelligence-helper-contract
hermetic tests/quickshell/search-routing-contract hermetic tests/quickshell/search-routing-contract
@@ -162,24 +184,30 @@ hermetic tests/quickshell/settings-buttons-contract
hermetic tests/quickshell/settings-commit-reset-contract hermetic tests/quickshell/settings-commit-reset-contract
hermetic tests/quickshell/settings-docs-contract hermetic tests/quickshell/settings-docs-contract
hermetic tests/quickshell/settings-hardcoded-values-contract hermetic tests/quickshell/settings-hardcoded-values-contract
# Flips real compositor policy -- gaps, blur, inactive opacity, keyboard layout\n-- on the live compositor and restores it. # Flips real compositor policy -- gaps, blur, inactive opacity, keyboard layout
# -- on the live compositor and restores it.
live-compositor,live-desktop tests/quickshell/settings-hyprland-write-contract live-compositor,live-desktop tests/quickshell/settings-hyprland-write-contract
hermetic tests/quickshell/settings-idiom-contract hermetic tests/quickshell/settings-idiom-contract
hermetic tests/quickshell/settings-jump-contract hermetic tests/quickshell/settings-jump-contract
hermetic tests/quickshell/settings-nav-contract hermetic tests/quickshell/settings-nav-contract
hermetic tests/quickshell/settings-ownership-contract hermetic tests/quickshell/settings-ownership-contract
# Maps Settings pages through a test shell and reads the live compositor during\npage routing. # Maps Settings pages through a test shell and reads the live compositor during
# page routing.
live-compositor,live-desktop tests/quickshell/settings-pages-contract live-compositor,live-desktop tests/quickshell/settings-pages-contract
hermetic tests/quickshell/settings-preferences-contract hermetic tests/quickshell/settings-preferences-contract
hermetic tests/quickshell/settings-search-contract hermetic tests/quickshell/settings-search-contract
hermetic tests/quickshell/settings-sidebar-layout-contract hermetic tests/quickshell/settings-sidebar-layout-contract
hermetic tests/quickshell/settings-sync-contract hermetic tests/quickshell/settings-sync-contract
# Applies compositor policy through the production write path against the live\ncompositor, and writes preferences to the real settings store (no isolated\nconfig home). # Applies compositor policy through the production write path against the live
# compositor, and writes preferences to the real settings store (no isolated
# config home).
live-compositor,live-desktop tests/quickshell/settings-system-contract live-compositor,live-desktop tests/quickshell/settings-system-contract
hermetic tests/quickshell/settings-titlebar-contract hermetic tests/quickshell/settings-titlebar-contract
# Opens the real Settings window, routes it between pages, and closes it through\nthe compositor. # Opens the real Settings window, routes it between pages, and closes it through
# the compositor.
live-compositor,live-desktop tests/quickshell/settings-window-contract live-compositor,live-desktop tests/quickshell/settings-window-contract
# Flips every compositor-backed setting to a value it does not hold, one at a\ntime, on the live compositor. # Flips every compositor-backed setting to a value it does not hold, one at a
# time, on the live compositor.
live-compositor,live-desktop tests/quickshell/settings-write-sweep-contract live-compositor,live-desktop tests/quickshell/settings-write-sweep-contract
# Reads the host sharing-service snapshot through the production helper. # Reads the host sharing-service snapshot through the production helper.
live-host tests/quickshell/sharing-contract live-host tests/quickshell/sharing-contract
@@ -192,7 +220,8 @@ hermetic tests/quickshell/sound-defaults-contract
# Reads the host PipeWire device state through Quickshell's live audio service. # Reads the host PipeWire device state through Quickshell's live audio service.
live-host tests/quickshell/sound-page-contract live-host tests/quickshell/sound-page-contract
hermetic tests/quickshell/sound-routing-contract hermetic tests/quickshell/sound-routing-contract
# Reads the host SSH-agent and key-management availability while keeping test\nkeys in a throwaway home. # Reads the host SSH-agent and key-management availability while keeping test
# keys in a throwaway home.
live-host tests/quickshell/ssh-keys-contract live-host tests/quickshell/ssh-keys-contract
# Publishes fixture events into the live capsule and toggles Do Not Disturb. # Publishes fixture events into the live capsule and toggles Do Not Disturb.
live-desktop tests/quickshell/status-events-contract live-desktop tests/quickshell/status-events-contract
@@ -223,7 +252,8 @@ hermetic tests/setup/crash-watch-contract
hermetic tests/setup/desktop-first-contract hermetic tests/setup/desktop-first-contract
hermetic tests/setup/dictation-contract hermetic tests/setup/dictation-contract
hermetic tests/setup/dotfile-classification-contract hermetic tests/setup/dotfile-classification-contract
# Contacts the configured package sources through dnf and Flathub availability\nchecks. # Contacts the configured package sources through dnf and Flathub availability
# checks.
network tests/setup/extras-contract network tests/setup/extras-contract
hermetic tests/setup/firefox-chrome-contract hermetic tests/setup/firefox-chrome-contract
hermetic tests/setup/hardware-contract hermetic tests/setup/hardware-contract
+28 -6
View File
@@ -21,7 +21,7 @@ discover_contracts() {
validate_manifest() { validate_manifest() {
local candidate="$1" local candidate="$1"
local -n expected_contracts="$2" local -n expected_contracts="$2"
local line capabilities path extra previous_was_comment=0 local line capabilities path extra previous_comment="" previous_was_comment=0
local -a capability_list=() local -a capability_list=()
local -A manifest_paths=() capability_counts=() local -A manifest_paths=() capability_counts=()
local previous_path="" local previous_path=""
@@ -31,11 +31,15 @@ validate_manifest() {
while IFS= read -r line || [[ -n "$line" ]]; do while IFS= read -r line || [[ -n "$line" ]]; do
if [[ "$line" =~ ^[[:space:]]*# ]]; then if [[ "$line" =~ ^[[:space:]]*# ]]; then
previous_comment="${line#*#}"
previous_comment="${previous_comment#"${previous_comment%%[![:space:]]*}"}"
previous_comment="${previous_comment%"${previous_comment##*[![:space:]]}"}"
previous_was_comment=1 previous_was_comment=1
continue continue
fi fi
if [[ "$line" =~ ^[[:space:]]*$ ]]; then if [[ "$line" =~ ^[[:space:]]*$ ]]; then
previous_comment=""
previous_was_comment=0 previous_was_comment=0
continue continue
fi fi
@@ -43,6 +47,7 @@ validate_manifest() {
IFS=$' \t' read -r capabilities path extra <<<"$line" IFS=$' \t' read -r capabilities path extra <<<"$line"
if [[ -z "${capabilities:-}" || -z "${path:-}" || -n "${extra:-}" ]]; then if [[ -z "${capabilities:-}" || -z "${path:-}" || -n "${extra:-}" ]]; then
validation_note "manifest line is not exactly two fields: $line" validation_note "manifest line is not exactly two fields: $line"
previous_comment=""
previous_was_comment=0 previous_was_comment=0
continue continue
fi fi
@@ -81,9 +86,14 @@ validate_manifest() {
validation_note "hermetic must appear alone on $path" validation_note "hermetic must appear alone on $path"
fi fi
if [[ "$capabilities" != hermetic && "$previous_was_comment" -ne 1 ]]; then if [[ "$capabilities" != hermetic ]]; then
validation_note "$path is non-hermetic but lacks a directly preceding comment" if [[ "$previous_was_comment" -ne 1 ]]; then
validation_note "$path is non-hermetic but lacks a directly preceding comment"
elif [[ -z "$previous_comment" ]]; then
validation_note "$path is non-hermetic but lacks a non-empty directly preceding comment"
fi
fi fi
previous_comment=""
previous_was_comment=0 previous_was_comment=0
done < "$candidate" done < "$candidate"
@@ -108,8 +118,12 @@ validate_manifest() {
return 0 return 0
} }
cleanup_fixture() {
[[ -n "${fixture:-}" ]] && rm -f -- "$fixture"
}
run_parser_fixture() { run_parser_fixture() {
local label="$1" expected_message="$2" contents="$3" output fixture local label="$1" expected_message="$2" contents="$3" output fixture=""
shift 3 shift 3
local -A fixture_paths=() local -A fixture_paths=()
local fixture_path local fixture_path
@@ -118,13 +132,18 @@ run_parser_fixture() {
done done
fixture="$(mktemp)" fixture="$(mktemp)"
trap cleanup_fixture EXIT
trap 'cleanup_fixture; exit 130' INT
trap 'cleanup_fixture; exit 143' TERM
printf '%s' "$contents" > "$fixture" printf '%s' "$contents" > "$fixture"
if output="$(validate_manifest "$fixture" fixture_paths 2>&1)"; then if output="$(validate_manifest "$fixture" fixture_paths 2>&1)"; then
printf 'contract manifest: parser fixture %s unexpectedly passed\n' "$label" >&2 printf 'contract manifest: parser fixture %s unexpectedly passed\n' "$label" >&2
rm -f "$fixture" cleanup_fixture
trap - EXIT INT TERM
return 1 return 1
fi fi
rm -f "$fixture" cleanup_fixture
trap - EXIT INT TERM
if ! grep -Fq "$expected_message" <<<"$output"; then if ! grep -Fq "$expected_message" <<<"$output"; then
printf 'contract manifest: parser fixture %s did not name %q: %s\n' \ printf 'contract manifest: parser fixture %s did not name %q: %s\n' \
@@ -149,6 +168,9 @@ run_parser_fixtures() {
run_parser_fixture uncommented-non-hermetic \ run_parser_fixture uncommented-non-hermetic \
'tests/a is non-hermetic but lacks a directly preceding comment' \ 'tests/a is non-hermetic but lacks a directly preceding comment' \
$'network tests/a\n' tests/a || return 1 $'network tests/a\n' tests/a || return 1
run_parser_fixture blank-comment \
'tests/a is non-hermetic but lacks a non-empty directly preceding comment' \
$'#\nnetwork tests/a\n' tests/a || return 1
} }
[[ -r "$manifest" ]] || { [[ -r "$manifest" ]] || {