git.lucas.co / cce-compositor
Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git

verify/escape-dismiss-test (3.7K)

 1 #!/usr/bin/env bash
 2 # escape-dismiss-test — behavioral test for the Escape-closes-status-menus arm
 3 # in keyboard_group.rs::handle_builtin_binding (7db8c03), driven end to end
 4 # through the virtual-keyboard path (fdd1209).
 5 #
 6 # Setup: a private cce-shadow instance, with status-stub mapped as a status
 7 # segment 400px tall — geometrically "expanded", i.e. an open in-surface menu.
 8 # The stub subscribes to the `dismiss` topic and shrinks to a bar strip on the
 9 # first push, printing one line per push. vkey injects Escape through
10 # zwp_virtual_keyboard_v1. Three assertions, one per gate of the arm:
11 #
12 #   1. Ctrl+Escape while expanded  -> no dismiss   (modifiers == 0 gate)
13 #   2. plain Escape while expanded -> one dismiss, stub shrinks below
14 #                                     bar_height   (the dismissal itself)
15 #   3. plain Escape after shrink   -> no dismiss   (any_expanded_status_segment
16 #                                                   gate, now closed)
17 #
18 # The compositor binary is whatever cce-shadow resolves (installed first, then
19 # the workspace target/release); extra args are passed to `cce-shadow start`,
20 # so `escape-dismiss-test --bin path/to/cce-fx` pins one explicitly.
21 
22 set -euo pipefail
23 
24 here=$(cd "$(dirname "$0")" && pwd)
25 repo=$(dirname "$here")
26 shadow="$repo/scripts/cce-shadow"
27 out=$(mktemp -d)
28 inst=""
29 
30 cleanup() {
31     [ -n "$inst" ] && "$shadow" --instance "$inst" stop >/dev/null 2>&1 || true
32     rm -rf "$out"
33 }
34 trap cleanup EXIT
35 
36 fail() {
37     echo "FAIL: $*" >&2
38     echo "--- stub log ---" >&2
39     cat "$out/stub.log" >&2 || true
40     echo "--- compositor log tail ---" >&2
41     [ -n "$inst" ] && "$shadow" --instance "$inst" logs 2>/dev/null | tail -20 >&2 || true
42     exit 1
43 }
44 
45 echo "==> building test clients"
46 cargo build --quiet --manifest-path "$here/clients/Cargo.toml"
47 bin="$here/clients/target/debug"
48 
49 echo "==> starting shadow instance"
50 inst=$("$shadow" start --new "$@" 2>&1 | sed -n "s/.*claimed instance '\([^']*\)'.*/\1/p")
51 [ -n "$inst" ] || fail "could not claim a shadow instance"
52 sc() { "$shadow" --instance "$inst" "$@"; }
53 
54 stub_h() {
55     sc ctl windows --json 2>/dev/null \
56         | jq -r 'select(.app_id == "cce-status-teststub") | .h' | head -1
57 }
58 dismisses() { grep -c '^dismiss' "$out/stub.log" 2>/dev/null || true; }
59 
60 # Predicates for wait_for. They must be functions, not `test "$(...)" = x`
61 # inline: a command substitution in wait_for's arguments would be expanded
62 # once at call time, and the loop would poll a constant.
63 stub_h_is() { [ "$(stub_h)" = "$1" ]; }
64 dismiss_count_is() { [ "$(dismisses)" = "$1" ]; }
65 
66 wait_for() { # <description> <timeout-seconds> <predicate...>
67     local desc=$1 timeout=$2 i=0
68     shift 2
69     until "$@"; do
70         i=$((i + 1))
71         [ "$i" -ge $((timeout * 5)) ] && fail "timed out waiting for $desc"
72         sleep 0.2
73     done
74 }
75 
76 echo "==> mapping expanded status stub"
77 sc run "$bin/status-stub" >"$out/stub.log" 2>&1 &
78 wait_for "stub mapped expanded (h=400)" 10 stub_h_is 400
79 
80 echo "==> gate 1: Ctrl+Escape while expanded must not dismiss"
81 sc run "$bin/vkey" mod:4 1 mod:0 >/dev/null
82 sleep 1
83 [ "$(dismisses)" = 0 ] || fail "chorded Escape dismissed the menu (got $(dismisses) dismiss lines)"
84 [ "$(stub_h)" = 400 ] || fail "stub shrank on chorded Escape"
85 
86 echo "==> gate 2: plain Escape while expanded must dismiss exactly once"
87 sc run "$bin/vkey" 1 >/dev/null
88 wait_for "dismiss push to reach the stub" 5 dismiss_count_is 1
89 wait_for "stub to shrink below bar_height" 5 stub_h_is 8
90 
91 echo "==> gate 3: plain Escape with no expanded segment must stay quiet"
92 sc run "$bin/vkey" 1 >/dev/null
93 sleep 1
94 [ "$(dismisses)" = 1 ] || fail "Escape dismissed with no menu open (got $(dismisses) dismiss lines)"
95 
96 echo "PASS: all three gates of the Escape-dismiss arm behave"