fix: fail session validation promptly without missing tools
This commit is contained in:
@@ -196,6 +196,9 @@ Wayland rig. It verifies Recording status, non-empty segment files and metadata,
|
||||
closes the only window while recording continues, kills the game process,
|
||||
checks finalisation, reopens the app to load its library from disk, captures
|
||||
screenshots and uses Ctrl+Q during a second recording for clean shutdown.
|
||||
The script checks required tools before starting and distinguishes pending
|
||||
status from failed checks. The recovery run also passed with `PATH=/usr/bin:/bin`
|
||||
without ripgrep installed (2026-10-08); scripts use grep instead.
|
||||
The recording and library screenshots were inspected; storage totals came from
|
||||
real Matroska file sizes. The existing Linux rig also passed all nine scenarios
|
||||
after integration (2026-10-08). Its unwritable-output assertion accepts both
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user