fix: fail session validation promptly without missing tools

This commit is contained in:
2026-10-08 17:38:56 +02:00
parent b51c64eee3
commit c1cac1ba46
2 changed files with 59 additions and 26 deletions
+56 -26
View File
@@ -1,6 +1,22 @@
#!/usr/bin/env bash
# End-to-end desktop/Steam-process/session test in the private Wayland rig.
set -euo pipefail
# Check before building or starting the rig; missing tools must not look like
# a status that has simply not appeared yet.
for tool in bash cargo grep python3 ffprobe grim wtype swaymsg dirname mkdir sleep cat seq \
env dbus-run-session mktemp chmod rm basename head dbus-update-activation-environment \
pipewire wireplumber pipewire-pulse pactl sway Xwayland ffplay fusermount3; do
if ! command -v "${tool}" > /dev/null; then
echo "FAIL required tool '${tool}' is missing from PATH" >&2
exit 127
fi
done
for tool in /usr/lib/xdg-desktop-portal /usr/lib/xdg-desktop-portal-wlr; do
if [[ ! -x "${tool}" ]]; then
echo "FAIL required tool '${tool}' is missing or not executable" >&2
exit 127
fi
done
repo="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
out="${1:-${TMPDIR:-/tmp}/clipbin-session-validation}"
mkdir -p "${out}"
@@ -8,6 +24,28 @@ cargo build --locked -p clipbin-desktop --manifest-path "${repo}/Cargo.toml"
export CLIPBIN_SESSION_REPO="${repo}" CLIPBIN_SESSION_OUT="${out}"
"${repo}/scripts/rig/headless.sh" bash <<'INNER'
set -euo pipefail
# grep returns 1 for no matches; every other error must fail immediately.
wait_log() {
local pattern="$1" log="$2" minimum="$3" attempts="$4" count status attempt
for ((attempt = 0; attempt < attempts; attempt++)); do
if count=$(grep -c "${pattern}" "${log}"); then
if ((count >= minimum)); then return 0; fi
else
status=$?
if [[ ${status} != 1 ]]; then
echo "FAIL could not check '${pattern}' in ${log} (grep exited ${status})" >&2
exit 1
fi
fi
if ! kill -0 "${app}" 2>/dev/null; then
echo "FAIL desktop exited while waiting for '${pattern}'; log: ${log}" >&2
exit 1
fi
sleep 1
done
echo "FAIL timed out waiting for ${minimum} '${pattern}' entries; log: ${log}" >&2
exit 1
}
out="${CLIPBIN_SESSION_OUT}"
portal_config="${XDG_CONFIG_HOME}"
export XDG_DATA_HOME="${out}/data" XDG_CONFIG_HOME="${out}/config"
@@ -29,29 +67,17 @@ for _ in range(20):
argv = open(f'/proc/{sys.argv[1]}/cmdline', 'rb').read().split(b'\0')
assert argv[0] == b'reaper' and b'SteamLaunch' in argv and b'AppId=480' in argv, argv
PY
for _ in $(seq 30); do
if rg -q 'recorder_status=Recording' "${out}/desktop.log"; then break; fi
sleep 1
done
rg -q 'recorder_status=Recording' "${out}/desktop.log"
wait_log 'recorder_status=Recording' "${out}/desktop.log" 1 30
swaymsg '[app_id="dev.clipbin.Clipbin"] focus' > /dev/null
sleep 0.5
grim "${out}/recording.png"
if [[ "${CLIPBIN_VALIDATE_RECOVERY:-0}" == 1 ]]; then
kill "$(cat "${CLIPBIN_RIG_DIR}/xdpw.pid")"
for _ in $(seq 15); do
if rg -q 'recorder_status=Failed' "${out}/desktop.log"; then break; fi
sleep 1
done
rg -q 'recorder_status=Failed' "${out}/desktop.log"
wait_log 'recorder_status=Failed' "${out}/desktop.log" 1 15
grim "${out}/capture-failed.png"
XDG_CONFIG_HOME="${portal_config}" /usr/lib/xdg-desktop-portal-wlr > "${out}/recovered-portal.log" 2>&1 &
portal=$!
for _ in $(seq 30); do
if [[ $(rg -c 'recorder_status=Recording' "${out}/desktop.log") -ge 2 ]]; then break; fi
sleep 1
done
[[ $(rg -c 'recorder_status=Recording' "${out}/desktop.log") -ge 2 ]]
wait_log 'recorder_status=Recording' "${out}/desktop.log" 2 30
python3 - "${XDG_DATA_HOME}" <<'PY'
import pathlib,json,sys
p=max(pathlib.Path(sys.argv[1]).glob('clipbin/sessions/*/session.json'), key=lambda p:int(p.parent.name))
@@ -70,22 +96,30 @@ kill "${game}"
wait "${game}" 2>/dev/null || true
finalised=0
for _ in $(seq 20); do
if python3 - "${XDG_DATA_HOME}" 2>/dev/null <<'PY'
status=0
python3 - "${XDG_DATA_HOME}" <<'PY' || status=$?
import pathlib, json, sys
files = list(pathlib.Path(sys.argv[1]).glob('clipbin/sessions/*/session.json'))
assert files
if not files: sys.exit(2)
path = max(files, key=lambda p: int(p.parent.name))
m = json.loads(path.read_text())
assert m['app_id'] == 480 and not m['session']['active']
if m['session']['active']: sys.exit(2)
assert m['app_id'] == 480
assert m['segments'] and all(s['complete'] for s in m['segments'])
assert all((path.parent / s['path']).stat().st_size > 0 for s in m['segments'])
assert any(s['kind'] == 'Footage' for s in m['session']['spans'])
print('PASS session finalised with footage')
PY
then finalised=1; break; fi
sleep 1
case "${status}" in
0) finalised=1; break ;;
2) sleep 1 ;;
*) echo "FAIL session finalisation check exited ${status}" >&2; exit 1 ;;
esac
done
[[ ${finalised} == 1 ]]
if [[ ${finalised} != 1 ]]; then
echo "FAIL timed out waiting for session finalisation; log: ${out}/desktop.log" >&2
exit 1
fi
python3 - "${XDG_DATA_HOME}" <<'PY'
import pathlib,json,subprocess,sys
path=max(pathlib.Path(sys.argv[1]).glob('clipbin/sessions/*/session.json'), key=lambda p:int(p.parent.name))
@@ -129,11 +163,7 @@ sleep 0.5
grim "${out}/library.png"
bash -c 'exec -a reaper python3 -c "import time; time.sleep(120)" SteamLaunch AppId=480 --' &
game=$!
for _ in $(seq 30); do
if rg -q 'recorder_status=Recording' "${out}/reopened.log"; then break; fi
sleep 1
done
rg -q 'recorder_status=Recording' "${out}/reopened.log"
wait_log 'recorder_status=Recording' "${out}/reopened.log" 1 30
swaymsg '[app_id="dev.clipbin.Clipbin"] focus' > /dev/null
wtype -M ctrl -k q -m ctrl
for _ in $(seq 20); do