scythe/tests/e2e_test.sh
spearzificial 5988785d57 fix(cli): dispatch from argv[0] so daemon symlinks work; harden e2e run
Multicall dispatch used std::env::current_exe(), which reads /proc/self/exe
and resolves symlinks. Every installed alias therefore reported the stem
"scythe", so scythe-daemon and vrec-daemon fell through to client mode and
opened the HUD instead of starting the engine. The aliases shipped by
install.sh, the deb/rpm/flatpak packages and the Hyprland exec_cmd line were
all affected; a stale pre-consolidation target/release/scythe-daemon masked
it during local testing.

invoked_stem() now reads argv[0] and only falls back to current_exe() when
argv[0] has no usable stem. Verified against a packaged tarball: both daemon
aliases hit the running-instance guard while scythe, scythe-ui, vrec and
vrec-ui open the client.

The e2e guard also probed liveness with `scythe --status`, which calls
ensure_daemon_running() and so spawned a daemon while probing, then chased
its own tail. It now connects to the socket directly, records the running
daemon's command line and working directory, stops it gracefully, and
restores it on exit so a developer's recorder survives a test run.

Help text now reports the unified binary and marks --gtk as a deprecated
alias, since the GTK backend was retired.
2026-10-20 16:55:33 +05:30

154 lines
5.1 KiB
Bash
Executable file

#!/usr/bin/env bash
set -e
echo "============================================="
echo " Scythe Automated End-to-End Test Suite "
echo "============================================="
# Ensure binaries are compiled
echo "[1/6] Building release binaries..."
cargo build --release --quiet
SOCKET_PATH="${XDG_RUNTIME_DIR:-/tmp}/scythe.sock"
# Liveness probe with no side effects. `scythe --status` calls
# ensure_daemon_running(), so probing with it spawns a daemon and the check
# starts chasing its own tail.
daemon_alive() {
[ -S "$SOCKET_PATH" ] || return 1
python3 -c '
import socket, sys
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
s.settimeout(0.5)
try:
s.connect(sys.argv[1])
except OSError:
sys.exit(1)
finally:
s.close()
' "$SOCKET_PATH" 2>/dev/null
}
# A live daemon owns this socket. Removing the path entry while it runs leaves the
# kernel socket bound but unreachable, breaking the user's session until restart.
# Stop it gracefully, remember how it ran, and put it back when the test finishes.
PREVIOUS_DAEMON_CMD=""
PREVIOUS_DAEMON_CWD=""
if daemon_alive; then
PREVIOUS_DAEMON_CMD=$(pgrep -af 'scythe.*daemon' 2>/dev/null | head -1 | cut -d' ' -f2- || true)
PREVIOUS_DAEMON_CWD=$(readlink -f /proc/"$(pgrep -f 'scythe.*daemon' 2>/dev/null | head -1)"/cwd 2>/dev/null || true)
echo "[0/6] Existing daemon detected; stopping it for the test run..."
target/release/scythe --quit >/dev/null 2>&1 || true
for _ in $(seq 1 80); do
daemon_alive || break
sleep 0.1
done
if daemon_alive; then
echo "ERROR: A scythe daemon is running and did not stop cleanly."
echo " Refusing to unlink its socket. Stop it first: scythe --quit"
exit 1
fi
fi
rm -f "$SOCKET_PATH"
rm -f record_*.mp4 replay_*.mp4
SAVE_DIR=$(python3 -c 'import json, os; p=os.path.expanduser("~/.config/scythe/config.json"); print(os.path.expanduser(json.load(open(p))["output_directory"])) if os.path.exists(p) else print(os.path.expanduser("~/Videos/Scythe"))' 2>/dev/null || echo "$HOME/Videos/Scythe")
mkdir -p "$SAVE_DIR"
WAS_SERVICE_ACTIVE=0
if systemctl --user is-active --quiet scythe-daemon.service 2>/dev/null; then
WAS_SERVICE_ACTIVE=1
systemctl --user stop scythe-daemon.service 2>/dev/null || true
sleep 0.5
fi
echo "[2/6] Starting scythe daemon with mock capture..."
target/release/scythe daemon --mock > daemon_test.log 2>&1 &
DAEMON_PID=$!
cleanup() {
if kill -0 $DAEMON_PID 2>/dev/null; then
target/release/scythe --quit 2>/dev/null || kill -9 $DAEMON_PID 2>/dev/null || true
fi
rm -f daemon_test.log
if [ "$WAS_SERVICE_ACTIVE" -eq 1 ]; then
systemctl --user start scythe-daemon.service 2>/dev/null || true
elif [ -n "$PREVIOUS_DAEMON_CMD" ]; then
echo "Restoring the daemon that was running before the test..."
( cd "${PREVIOUS_DAEMON_CWD:-$PWD}" && setsid $PREVIOUS_DAEMON_CMD >/dev/null 2>&1 & ) || true
sleep 1
fi
}
trap cleanup EXIT
# Wait for socket to become available
echo "Waiting for daemon socket..."
for i in {1..30}; do
if [ -S "$SOCKET_PATH" ]; then
break
fi
sleep 0.1
done
if [ ! -S "$SOCKET_PATH" ]; then
echo "ERROR: Daemon IPC socket was not created!"
cat daemon_test.log
exit 1
fi
echo "Daemon is live (PID: $DAEMON_PID, Socket: $SOCKET_PATH)"
# Test 1: Start Normal Recording
echo "[3/6] Testing: Start Normal Recording..."
target/release/scythe --start
echo "Recording in progress for 3 seconds..."
sleep 3
# Test 2: Stop Normal Recording
echo "[4/6] Testing: Stop Normal Recording..."
target/release/scythe --stop
sleep 1
# Test 3: Save Instant Replay
echo "[5/6] Testing: Save Instant Replay buffer..."
target/release/scythe --save
sleep 2
# Test 4: Quit Daemon
echo "[6/6] Testing: Clean Daemon Shutdown..."
target/release/scythe --quit
wait $DAEMON_PID 2>/dev/null || true
echo "Daemon shutdown cleanly."
# Verify generated MP4 files with ffprobe
echo "============================================="
echo " Verifying Generated Recordings with ffprobe"
echo "============================================="
FOUND_ANY=0
while IFS= read -r -d '' f; do
if [ -f "$f" ]; then
FOUND_ANY=1
echo "--> Testing file: $f ($(ls -lh "$f" | awk '{print $5}'))"
# Check container and stream information
PROBE_OUT=$(ffprobe -v error -show_entries format=duration,size,bit_rate:stream=codec_name,width,height,r_frame_rate -of default=noprint_wrappers=1 "$f")
echo "$PROBE_OUT"
# Check for stream decode errors
ffmpeg -v error -i "$f" -f null - 2>&1
echo "File $f verified: VALID & NO CORRUPTION!"
echo "---------------------------------------------"
rm -f "$f"
fi
done < <(find "$SAVE_DIR" "$HOME/Videos/Scythe" "$HOME/Videos/vrec" . -maxdepth 2 -mmin -2 \( -iname "record*.mp4" -o -iname "replay*.mp4" \) -print0 2>/dev/null)
if [ $FOUND_ANY -eq 0 ]; then
echo "FAILED: No recording or replay files were produced!"
cat daemon_test.log
exit 1
fi
echo "============================================="
echo " ALL AUTOMATED TESTS PASSED SUCCESSFULLY! "
echo "============================================="