test(gtk): live D-Bus/portal comparison against the original Ghostty
Adds scripts/live-dbus-test.sh (`just live-dbus`): each scenario runs the installed Ghostty 1.3.1 and the Rust build on a private dbus-run-session bus inside a private headless weston (the user's session bus is never written to), with Python/Gio mocks for org.freedesktop.Notifications, the GlobalShortcuts/Settings portal and org.freedesktop.systemd1, plus a real xdg-desktop-portal run. Scenarios: single-instance (+detect), +new-window with arguments, notifications, global shortcuts, color-scheme settings, cgroup scope creation, URL opening. tests/live_dbus.rs compares the Rust build against goldens recorded from the original (skips when tools or a fresh production build are missing). Two Rust bugs found and fixed (no upstream deviation): - GlobalShortcuts CreateSession/BindShortcuts payloads used the wrong D-Bus signatures ((v), double-boxed values), which a real portal rejects; signature tests added. - CreateSession was sent twice at startup; the config is now bound only after the D-Bus connection is set, as in Zig. All nine scenarios now behave identically to the original (docs/rewrite/notes/live-dbus.md). Test Plan: - just live-dbus - cargo test --test live_dbus (needs target/vp/production/ghostty)
This commit is contained in:
1 parent
5cb5bbada2
commit
39865d0ae1
25 files changed
+1278
-31
No files matched your search
Executable
+120
@@ -0,0 +1,120 @@
|
||||
#!/usr/bin/env bash
|
||||
# Live D-Bus / portal comparison of original Ghostty and the Rust build.
|
||||
#
|
||||
# scripts/live-dbus-test.sh [scenario...]
|
||||
#
|
||||
# Scenarios: single-instance single-instance-detect notifications shortcuts settings cgroup openuri openurl-click real-portal
|
||||
# (default: all). Each scenario runs once per binary on a *private* session
|
||||
# bus (dbus-run-session with its own config: only our service dir, no user
|
||||
# services) inside a private headless weston; mock services
|
||||
# (scripts/live-dbus/mock.py) answer org.freedesktop.Notifications,
|
||||
# org.freedesktop.systemd1 and the portal interfaces Ghostty uses. The
|
||||
# user's session bus, portal and desktop are never touched.
|
||||
#
|
||||
# The original's normalized result (D-Bus traffic + observable effects) is the
|
||||
# oracle; the Rust result must be identical. Exit status 1 on any difference,
|
||||
# 0 when everything matches or the needed tools are missing (skip).
|
||||
#
|
||||
# env: RUST_BIN (default target/vp/production/ghostty: debug builds use a different app id), REF_BIN (default /usr/bin/ghostty),
|
||||
# OUT (default target/live-dbus), KEEP=1 keeps the weston log.
|
||||
set -u
|
||||
ROOT=$(cd "$(dirname "$0")/.." && pwd)
|
||||
RUST_BIN=${RUST_BIN:-$ROOT/target/vp/production/ghostty}
|
||||
REF_BIN=${REF_BIN:-/usr/bin/ghostty}
|
||||
OUT=${OUT:-$ROOT/target/live-dbus}
|
||||
LD=$ROOT/scripts/live-dbus
|
||||
SCEN=${*:-single-instance single-instance-detect notifications shortcuts settings cgroup openuri openurl-click real-portal}
|
||||
|
||||
for t in weston dbus-run-session busctl python3 xdotool; do
|
||||
command -v $t >/dev/null || { echo "live-dbus: SKIP ($t missing)"; exit 0; }
|
||||
done
|
||||
python3 -c 'import gi; gi.require_version("Gio","2.0")' 2>/dev/null || { echo "live-dbus: SKIP (python gi missing)"; exit 0; }
|
||||
[ -x "$RUST_BIN" ] || [ "${LABELS:-}" = ref ] || { echo "live-dbus: SKIP (no rust build at $RUST_BIN)"; exit 0; }
|
||||
|
||||
mkdir -p "$OUT"
|
||||
export XDG_RUNTIME_DIR=${VP_RUNTIME_DIR:-/tmp/vp-runtime-$UID}
|
||||
mkdir -p -m700 "$XDG_RUNTIME_DIR" /tmp/.X11-unix 2>/dev/null
|
||||
WORK=$(mktemp -d "$OUT/work.XXXX")
|
||||
TAG=ldb$$
|
||||
SOCK=ldb$$
|
||||
cat >"$WORK/weston.ini" <<INI
|
||||
[core]
|
||||
shell=desktop-shell.so
|
||||
xwayland=true
|
||||
[output]
|
||||
name=headless
|
||||
mode=1200x800
|
||||
[shell]
|
||||
panel-position=none
|
||||
background-color=0xff202020
|
||||
INI
|
||||
__EGL_VENDOR_LIBRARY_FILENAMES=/usr/share/glvnd/egl_vendor.d/50_mesa.json XWAYLAND_NO_GLAMOR=1 \
|
||||
timeout 3000 weston --debug --backend=headless --renderer=gl --socket=$SOCK -c "$WORK/weston.ini" \
|
||||
>"$WORK/weston.log" 2>&1 &
|
||||
WPID=$!
|
||||
cleanup() {
|
||||
kill $WPID 2>/dev/null
|
||||
for p in /proc/[0-9]*; do
|
||||
grep -qa "LDB_TAG=$TAG" "$p/environ" 2>/dev/null && kill -9 "${p#/proc/}" 2>/dev/null
|
||||
done
|
||||
wait 2>/dev/null
|
||||
[ -n "${KEEP:-}" ] || rm -rf "$WORK"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
for _ in $(seq 50); do [ -S "$XDG_RUNTIME_DIR/$SOCK" ] && break; sleep 0.1; done
|
||||
[ -S "$XDG_RUNTIME_DIR/$SOCK" ] || { echo "live-dbus: SKIP (weston did not start)"; cat "$WORK/weston.log"; exit 0; }
|
||||
sleep 1
|
||||
XDISPLAY=$(grep -o "display :[0-9]*" "$WORK/weston.log" | tail -1 | sed 's/display //')
|
||||
|
||||
rc=0
|
||||
RUST_ONLY=" openurl-click " # the original cannot be driven (no input injection): golden file only
|
||||
cut_bus() { sed '/^== bus traffic/,$d' "$1"; }
|
||||
run_label() { # scenario label bin
|
||||
local s=$1 label=$2 bin=$3
|
||||
local d=$OUT/$s/$label; rm -rf "$d"; mkdir -p "$d"
|
||||
local run=$WORK/run-$s-$label; mkdir -p "$run/services"
|
||||
# Private bus: only our service dir (activation must start the binary
|
||||
# under test, not /usr/bin/ghostty), no user services.
|
||||
cat >"$run/session.conf" <<CONF
|
||||
<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
|
||||
<busconfig><type>session</type><keep_umask/><listen>unix:tmpdir=$XDG_RUNTIME_DIR</listen><auth>EXTERNAL</auth>
|
||||
<servicedir>$run/services</servicedir>
|
||||
<policy context="default"><allow send_destination="*" eavesdrop="true"/><allow eavesdrop="true"/><allow own="*"/></policy>
|
||||
</busconfig>
|
||||
CONF
|
||||
printf '[D-BUS Service]\nName=com.mitchellh.ghostty\nExec=%s --gtk-single-instance=true --initial-window=false\n' "$bin" \
|
||||
>"$run/services/com.mitchellh.ghostty.service"
|
||||
LDB_LABEL=$label LDB_TAG=$TAG WAYLAND_DISPLAY=$SOCK DISPLAY=$XDISPLAY LDB_BIN=$bin LDB_RUN=$run LDB_ROOT=$ROOT \
|
||||
timeout 240 dbus-run-session --config-file="$run/session.conf" -- \
|
||||
bash "$LD/scenario.sh" "$s" "$bin" "$d" >"$d/driver.log" 2>&1
|
||||
LDB_RUN=$run LDB_ROOT=$ROOT python3 "$LD/normalize.py" "$d" >"$d/result.txt" 2>"$d/normalize.err"
|
||||
}
|
||||
for s in $SCEN; do
|
||||
golden=$LD/golden/$s.txt
|
||||
mkdir -p "$OUT/$s"
|
||||
have_ref=0
|
||||
case "$RUST_ONLY" in *" $s "*) ;; *) [ -x "$REF_BIN" ] && [ "${LABELS:-ref rust}" != rust ] && have_ref=1 ;; esac
|
||||
if [ $have_ref = 1 ]; then run_label "$s" ref "$REF_BIN"; fi
|
||||
if [ -x "$RUST_BIN" ] && [ "${LABELS:-rust}" != ref ]; then
|
||||
if [ $have_ref = 1 ] || [ -f "$golden" ] || [ -n "${UPDATE_GOLDEN:-}" ]; then
|
||||
run_label "$s" rust "$RUST_BIN"
|
||||
fi
|
||||
fi
|
||||
[ "${LABELS:-}" = ref ] && continue
|
||||
ok=1
|
||||
if grep -qs '^ERROR' "$OUT"/$s/*/facts.txt; then echo "live-dbus: FAIL $s (scenario error, see $OUT/$s/*/facts.txt)"; rc=1; continue; fi
|
||||
if [ $have_ref = 1 ] && [ -f "$OUT/$s/rust/result.txt" ]; then
|
||||
if diff -u "$OUT/$s/ref/result.txt" "$OUT/$s/rust/result.txt" >"$OUT/$s/diff.txt"; then :; else
|
||||
echo "live-dbus: FAIL $s (original vs Rust)"; cat "$OUT/$s/diff.txt"; ok=0; fi
|
||||
if [ -n "${UPDATE_GOLDEN:-}" ] || [ ! -f "$golden" ]; then cut_bus "$OUT/$s/ref/result.txt" >"$golden"; fi
|
||||
fi
|
||||
if [ -f "$golden" ] && [ -f "$OUT/$s/rust/result.txt" ]; then
|
||||
if [ -n "${UPDATE_GOLDEN:-}" ] && [ $have_ref = 0 ]; then cut_bus "$OUT/$s/rust/result.txt" >"$golden"; fi
|
||||
if ! diff -u "$golden" <(cut_bus "$OUT/$s/rust/result.txt") >"$OUT/$s/diff-golden.txt"; then
|
||||
echo "live-dbus: FAIL $s (Rust vs recorded original, golden/$s.txt)"; cat "$OUT/$s/diff-golden.txt"; ok=0; fi
|
||||
fi
|
||||
if [ $ok = 1 ]; then
|
||||
echo "live-dbus: PASS $s$([ $have_ref = 1 ] && echo ' (original + golden)' || echo ' (golden only)')"
|
||||
else rc=1; fi
|
||||
done
|
||||
exit $rc
|
||||
Reference in new issue
Block a user