AtlatestRepositorytessera

tessera / tree / scriptstest-release-smoke

1#!/usr/bin/env bash
2# Run only on an isolated build/test host. Never inherits a user's desktop.
3set -euo pipefail
4tools=$(cd "$(dirname "$0")" && pwd)
5fixtures=$(realpath "${3:?fixture directory}")
6binary=$(realpath "${1:?binary}")
7out=$(realpath -m "${2:?receipts directory}")
8mkdir -p "$out"
9scratch=$(mktemp -d /tmp/tessera-release-smoke.XXXXXX)
10chmod 700 "$scratch"
11server= parent= client=
12cleanup() {
13 local status=$?
14 for child in "$client" "$server" "$parent"; do
15 [[ -z $child ]] || { kill -TERM "$child" 2>/dev/null || true; wait "$child" 2>/dev/null || true; }
16 done
17 if [[ $status != 0 ]]; then tail -40 "$out/server.log" "$out/parent.log" 2>/dev/null || true; fi
18 rm -rf "$scratch"
20trap cleanup EXIT
21unset DISPLAY WAYLAND_DISPLAY WAYLAND_SOCKET SWAYSOCK DBUS_SESSION_BUS_ADDRESS TMUX \
22 SIGIL_WM_LIBRARY TESSERA_LIBRARY TESSERA_CONFIG TESSERA_DATA_DIR TESSERA_THEME_DIR \
23 TESSERA_THEME_RUNTIME_DIR TESSERA_LOGIN_SESSION TESSERA_CLIENT_ENV_FILE
24export HOME=$scratch/home XDG_CONFIG_HOME=$scratch/config XDG_CACHE_HOME=$scratch/cache
25export XDG_DATA_HOME=$scratch/data XDG_RUNTIME_DIR=$scratch/run
26export TESSERA_REPL=$scratch/run/repl TESSERA_SOCKET=release-smoke
27export TESSERA_THEME_LIVE=tessera
28export PULSE_SERVER=unix:$scratch/no-audio
29mkdir -p "$HOME" "$XDG_CONFIG_HOME" "$XDG_CACHE_HOME" "$XDG_DATA_HOME" "$XDG_RUNTIME_DIR"
30chmod 700 "$XDG_RUNTIME_DIR"
31cd "$scratch"
32if env -u XDG_RUNTIME_DIR "$binary" start > "$out/missing-runtime.log" 2>&1; then exit 1; fi
33grep -q 'Tessera: XDG_RUNTIME_DIR is missing' "$out/missing-runtime.log"
34"$binary" --version > "$out/version.txt"
35"$binary" --help > "$out/help.txt"
36"$binary" theme list > "$out/palettes.txt"
37grep -q orpiment "$out/palettes.txt"
38if "$binary" msg '(+ 1 2)' > "$out/no-msg-alias.log" 2>&1; then exit 1; else [[ $? == 2 ]]; fi
39if "$binary" eval > "$out/eval-usage.log" 2>&1; then exit 1; else [[ $? == 2 ]]; fi
40if "$binary" eval '(+ 1 2)' > "$out/missing-socket.log" 2>&1; then exit 1; else [[ $? == 1 ]]; fi
41weston --backend=headless --renderer=pixman --socket=parent --width=1280 --height=720 \
42 --idle-time=0 --log="$out/parent.log" >/dev/null 2>&1 & parent=$!
43for ((i=0;i<200;i++)); do [[ ! -S $XDG_RUNTIME_DIR/parent ]] || break; sleep .05; done
44[[ -S $XDG_RUNTIME_DIR/parent ]]
45export WAYLAND_DISPLAY=$XDG_RUNTIME_DIR/parent
46unset WLR_BACKENDS WLR_RENDERER TESSERA_MODIFIER
47export SIGIL_WLROOTS_TEST_INPUT=1 TESSERA_MODIFIER=Super
48# Plain desktop invocation must exit without a compositor or asset extraction.
49"$binary" > "$out/desktop-help.txt" 2>&1
50grep -q "tessera start" "$out/desktop-help.txt"
51! grep -q "WAYLAND_SOCKET=" "$out/desktop-help.txt"
52export TESSERA_LOGIN_SESSION=1 TESSERA_SOCKET=parent TESSERA_REPL=$XDG_RUNTIME_DIR/parent-repl
53export WLR_SCENE_DISABLE_DIRECT_SCANOUT=1
54"$binary" start > "$out/server.log" 2>&1 & server=$!
55for ((i=0;i<400;i++)); do
56 endpoint=$(sed -n 's/^TESSERA_REPL=//p' "$out/server.log" | tail -n1)
57 [[ -z $endpoint || ! -S $endpoint ]] || break
58 kill -0 "$server"; sleep .05
59done
60export TESSERA_REPL=$endpoint
61export TESSERA_SOCKET=$(sed -n 's/^WAYLAND_SOCKET=//p' "$out/server.log" | tail -n1)
62[[ $TESSERA_SOCKET != parent && $TESSERA_REPL != "$XDG_RUNTIME_DIR/parent-repl" ]]
63[[ -S $TESSERA_REPL && $(stat -c %a "$TESSERA_REPL") == 600 ]]
64evaluate() { "$binary" eval "$1"; }
65[[ $(evaluate '(+ 1 2)') == 3 ]]
66expected_commit=$(git -C "$tools/.." rev-parse HEAD)
67[[ $(evaluate '(begin (import (sigil meta)) (app-commit))') == "\"$expected_commit\"" ]]
68printf 'RUNTIME_APP_COMMIT=%s\n' "$expected_commit" > "$out/app-commit.txt"
69for args in 'volume 55' 'brightness 80' 'mic muted' 'keyboard-layout us'; do
70 read -r kind value <<< "$args"
71 [[ $("$binary" osd "$kind" "$value") == '#t' ]]
72done
73for args in 'unknown 50' 'brightness 101' 'volume -1' 'mic bad'; do
74 read -r kind value <<< "$args"
75 if "$binary" osd "$kind" "$value" > "$out/osd-invalid-$kind.log" 2>&1; then exit 1; else [[ $? == 2 ]]; fi
76done
77if evaluate '(error "release error probe")' > "$out/expression-error.log" 2>&1; then exit 1; fi
78bash "$tools/test-repl-command" "$binary" "$out/repl"
79"$fixtures/repl-terminal" "$binary" > "$out/repl-terminal.txt"
80grep -q 'REPL_TERMINAL=PASS' "$out/repl-terminal.txt"
81WAYLAND_DISPLAY=$TESSERA_SOCKET timeout --foreground 30 "$fixtures/security-context-client" check native > "$out/security-context.log" 2>&1
82cat "$out/security-context.log"
83[[ $(evaluate '(and
84 (= (length (module-exports (find-module (quote (tessera api))))) 79)
85 (every (lambda (name) (procedure? (%module-lookup name)))
86 (module-exports (find-module (quote (tessera api)))))
87 (every (lambda (name) (not (%module-lookup name)))
88 (quote (tessera-config tessera-run-command! tessera-reload!
89 tessera-host-native host-snapshot start-host! stop-host!))))') == '#t' ]]
90bash "$tools/test-release-bound-keys" "$binary" "$fixtures" "$out" "$scratch" "$server"
91evaluate '(define (release-live-value) 1)' > "$out/redefine.log"
92[[ $(evaluate '(release-live-value)') == 1 ]]
93evaluate '(define (release-live-value) 2)' >> "$out/redefine.log"
94[[ $(evaluate '(release-live-value)') == 2 ]]
95evaluate '(output-box (focused-output))' > "$out/output-box.txt"
96evaluate '(background-command)' > "$out/background.txt"
97! grep -q 'Tessera: unpacking bundled wallpapers' "$out/missing-runtime.log" "$out/server.log"
98# A binary-only HOME must produce native-size images asynchronously.
99wait_background() {
100 local recipe=$1 palette=$2 seed=$3 receipt=$4
101 for ((attempt=0; attempt<400; attempt++)); do
102 evaluate '(background-command)' > "$receipt"
103 if grep -Fq "/v1/$recipe/$palette/seed-$seed/" "$receipt" &&
104 [[ -n $(find "$XDG_DATA_HOME/tessera/backgrounds/v1/$recipe/$palette/seed-$seed" \
105 -name '*.png' -type f -print -quit 2>/dev/null) ]]; then return 0; fi
106 kill -0 "$server"; sleep .1
107 done
108 printf 'RELEASE_BACKGROUND_TIMEOUT=%s/%s/%s\n' "$recipe" "$palette" "$seed" >&2
109 return 1
111wait_background facets orpiment 1 "$out/cold-background.txt"
112# A burst is one pending theme operation, with bounded public action admission.
113[[ $(evaluate '(begin (for-each (lambda (index) (next-wallpaper!)) (iota 500)) #t)') == '#t' ]]
114wait_background drift orpiment 1 "$out/burst-background.txt"
115evaluate '(define release-overflow-before (length (problems)))' > "$out/overflow-before.txt"
116[[ $(evaluate '(let ((accepted 0) (rejected 0))
117 (for-each (lambda (index)
118 (if (focus-workspace! "1") (set! accepted (+ accepted 1)) (set! rejected (+ rejected 1))))
119 (iota 500))
120 (list accepted rejected))') == '(64 436)' ]]
121[[ $(evaluate '(and (> (length (problems)) release-overflow-before)
122 (any (lambda (problem) (string-contains? (format "~a" problem) "rejected 436")) (problems)))') == '#t' ]]
123[[ $(evaluate '(begin (apply-theme! "smalti") (set-wallpaper! "drift" 7) #t)') == '#t' ]]
124wait_background drift smalti 7 "$out/merged-background.txt"
125[[ $(evaluate '(equal? (palette-id (theme-palette)) "smalti")') == '#t' ]]
126[[ $(evaluate "(set-wallpaper! 'drift seed: 42 params: '((density . 0.7)))") == '#t' ]]
127wait_background drift smalti 42 "$out/parameter-background.txt"
128[[ $(evaluate "(= (cdr (assq 'density (theme-wallpaper-params))) 0.7)") == '#t' ]]
129[[ $(evaluate "(save-wallpaper! \"$out/api-export.png\")") == '#t' ]]
130for ((attempt=0; attempt<200; attempt++)); do
131 [[ ! -f $out/api-export.png ]] || break
132 kill -0 "$server"; sleep .1
133done
134native_cache=$(find "$XDG_DATA_HOME/tessera/backgrounds/v1/drift/smalti/seed-42" -name 1280x720.png)
135node "$tools/../test/check-wallpaper-export.mjs" "$out/api-export.png" "$native_cache" drift smalti 42 1280 720 '(density . 0.7)'
136[[ $(evaluate "(save-wallpaper! \"$out/api-sized-export.png\" width: 256 height: 144)") == '#t' ]]
137for ((attempt=0; attempt<200; attempt++)); do
138 [[ ! -f $out/api-sized-export.png ]] || break
139 kill -0 "$server"; sleep .1
140done
141node "$tools/../test/check-wallpaper-export.mjs" "$out/api-sized-export.png" - drift smalti 42 256 144 '(density . 0.7)'
142"$binary" wallpaper save "$out/cli-export.png" --size 320x180 > "$out/cli-export.txt"
143node "$tools/../test/check-wallpaper-export.mjs" "$out/cli-export.png" - drift smalti 42 320 180 '(density . 0.7)'
144[[ $(evaluate '(begin (apply-theme! "orpiment" "facets" 1) #t)') == '#t' ]]
145wait_background facets orpiment 1 "$out/restored-background.txt"
146"$binary" theme apply workbench > "$out/apply-workbench.log" 2>&1
147wait_background facets workbench 1 "$out/workbench-background.txt"
148[[ $(evaluate '(= (config-focused-border (current-config)) #xff8800ff)') == '#t' ]]
149"$binary" theme apply orpiment --wallpaper drift > "$out/apply-drift-a.log" 2>&1
150wait_background drift orpiment 23 "$out/drift-a-background.txt"
151"$binary" theme apply workbench > "$out/apply-drift-b.log" 2>&1
152wait_background drift workbench 23 "$out/drift-b-background.txt"
153"$binary" theme wallpaper facets > "$out/wallpaper-facets.log" 2>&1
154[[ $(evaluate '(palette-id (theme-palette))') == '"workbench"' ]]
155wait_background facets workbench 1 "$out/wallpaper-facets-background.txt"
156"$binary" theme preview lichen --wallpaper drift > "$out/preview-wallpaper.log" 2>&1
157wait_background drift lichen 23 "$out/preview-wallpaper-background.txt"
158"$binary" theme revert > "$out/revert-wallpaper.log" 2>&1
159[[ $(evaluate '(theme-wallpaper)') == '"facets"' ]]
160"$binary" theme wallpaper facets --seed -3 > "$out/wallpaper-seed.log" 2>&1
161wait_background facets workbench -3 "$out/seed-background.txt"
162[[ $(evaluate '(theme-wallpaper-seed)') == -3 ]]
163"$binary" wallpaper render drift --palette current --seed 0 --size 512x320 \
164 --output "$scratch/repeat-a.png" > "$out/render-a.txt"
165"$binary" wallpaper render drift --palette current --seed 0 --size 512x320 \
166 --output "$scratch/repeat-b.png" > "$out/render-b.txt"
167cmp "$scratch/repeat-a.png" "$scratch/repeat-b.png"
168"$binary" wallpaper render drift --palette current --seed 1 --size 512x320 \
169 --output "$scratch/changed.png" > "$out/render-changed.txt"
170! cmp -s "$scratch/repeat-a.png" "$scratch/changed.png"
171find "$XDG_DATA_HOME/tessera/backgrounds" -type f -name '*.png' \
172 -exec sha256sum {} + > "$out/assets.sha256"
173"$binary" theme apply lichen > "$out/apply.log" 2>&1
174[[ $(evaluate '(= (config-focused-border (current-config)) (config-focused-border (stock-config-for (theme-palette))))') == '#t' ]]
175evaluate '(config-focused-border (current-config))' > "$out/lichen-border.txt"
176"$binary" theme preview smalti > "$out/preview.log" 2>&1
177[[ $(evaluate '(= (config-focused-border (current-config)) (config-focused-border (stock-config-for (theme-palette))))') == '#t' ]]
178"$binary" theme revert > "$out/revert.log" 2>&1
179"$binary" theme status > "$out/status.txt"
180[[ $(evaluate '(config-focused-border (current-config))') == "$(cat "$out/lichen-border.txt")" ]]
181export RELEASE_SMOKE_WAYLAND_DISPLAY=$TESSERA_SOCKET
182TESSERA_RELEASE_SMOKE=1 bash "$tools/test-float-snapping" "$binary" "$fixtures" "$out" "$scratch"
183# The final executable must use its registered native ABI, with no WM plugin.
184if grep -E '/gnu/store/|/work/|libsigil-wm|bootstrap' "/proc/$server/maps" > "$out/forbidden-maps.txt"; then
185 # The executable itself may reside in /work; permit exactly its pathname.
186 grep -vF "$binary" "$out/forbidden-maps.txt" > "$out/forbidden-maps-other.txt" || true
187 [[ ! -s $out/forbidden-maps-other.txt ]]
188fi
189cp "/proc/$server/maps" "$out/runtime-maps.txt"
190evaluate '(run-command! (quote (exit)))' > "$out/exit.txt" || true
191for ((i=0;i<200;i++)); do ! kill -0 "$server" 2>/dev/null && break; sleep .05; done
192if kill -0 "$server" 2>/dev/null; then kill -TERM "$server"; fi
193wait "$server"
194server=
195# A cold process must load a user config in the same environment as IPC.
196# Deliberately omit (sigil process): getenv is in the shared user vocabulary.
197export TESSERA_CONFIG=$scratch/cold-config.sgl
198cat > "$TESSERA_CONFIG" <<'CONFIG'
199(import (tessera config))
200(define release-config-home (getenv "HOME"))
201(unless release-config-home (error "cold config needs HOME"))
202(config-with default-config focused-border: #x123456ff
203 startup: '() session-helpers: '())
204CONFIG
205# Retain the generated cache across a fresh process and config load.
206"$binary" start > "$out/warm-server.log" 2>&1 & server=$!
207for ((i=0;i<400;i++)); do
208 endpoint=$(sed -n 's/^TESSERA_REPL=//p' "$out/warm-server.log" | tail -n1)
209 [[ -z $endpoint || ! -S $endpoint ]] || break
210 kill -0 "$server"; sleep .05
211done
212export TESSERA_REPL=$endpoint
213export TESSERA_SOCKET=$(sed -n 's/^WAYLAND_SOCKET=//p' "$out/warm-server.log" | tail -n1)
214[[ $TESSERA_SOCKET != parent && $TESSERA_REPL != "$XDG_RUNTIME_DIR/parent-repl" ]]
215[[ -S $TESSERA_REPL ]]
216[[ $(evaluate '(+ 2 3)') == 5 ]]
217grep -q 'TESSERA_CONFIG_LOADED=' "$out/warm-server.log"
218! grep -q 'configuration .* not used:' "$out/warm-server.log"
219[[ $(evaluate '(equal? release-config-home (getenv "HOME"))') == '#t' ]]
220[[ $(evaluate '(= (config-focused-border (current-config)) #x123456ff)') == '#t' ]]
221[[ $(evaluate '(apply-config! (load-config (getenv "TESSERA_CONFIG")))') == '#t' ]]
222! grep -q 'Tessera: unpacking bundled wallpapers' "$out/warm-server.log"
223evaluate '(run-command! (quote (exit)))' > "$out/warm-exit.txt" || true
224for ((i=0;i<200;i++)); do ! kill -0 "$server" 2>/dev/null && break; sleep .05; done
225if kill -0 "$server" 2>/dev/null; then kill -TERM "$server"; fi
226wait "$server"
227server=
228! grep -q 'event handler failed' "$out/server.log" "$out/warm-server.log"
229bash "$tools/test-repl-startup" "$binary" "$out/repl-startup" "$fixtures"
230printf 'RELEASE_NESTED_SMOKE=PASS\n' | tee "$out/PASS"