The U7 onboarding view replaced "No accounts yet." with "Welcome to SharedInbox", causing the E2E test to spin for the full timeout budget (pumping slowly in headless CI) before failing. Fix the finder and bump per-attempt timeout from 240s → 360s and CI job ceiling from 20 min → 30 min to give the full account-add → send → verify flow room to complete. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
167 lines
6.4 KiB
Bash
Executable File
167 lines
6.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Starts Stalwart on random ports, then runs Flutter UI integration tests inside
|
|
# a virtual X server (Xvfb). Works on a local desktop and in headless CI.
|
|
# No D-Bus or keyring daemon is required — tests inject an in-memory SecureStorage.
|
|
#
|
|
# Run inside nix develop: stalwart-dev/integration_ui_test.sh
|
|
set -Eeuo pipefail
|
|
|
|
# Timing helper: prints elapsed seconds since script start with a label.
|
|
_SCRIPT_START=$(date +%s%3N)
|
|
ts() { echo "[$(( $(date +%s%3N) - _SCRIPT_START ))ms] $*"; }
|
|
|
|
export STALWART_USER_B="${STALWART_USER_B:-alice@example.com}"
|
|
export STALWART_PASS_B="${STALWART_PASS_B:-secret}"
|
|
export STALWART_USER_C="${STALWART_USER_C:-bob@example.com}"
|
|
export STALWART_PASS_C="${STALWART_PASS_C:-secret}"
|
|
export STALWART_RANDOM_PORTS=1
|
|
STALWART_TMPDIR="$(mktemp -d /tmp/stalwart-dev-XXXXXX)"
|
|
export STALWART_TMPDIR
|
|
|
|
# Isolate path_provider app data from the developer's real data directory.
|
|
TEST_HOME="$(mktemp -d /tmp/sharedinbox-test-home-XXXXXX)"
|
|
|
|
cleanup() {
|
|
kill "${STALWART_PID:-}" "${XVFB_PID:-}" 2>/dev/null || true
|
|
wait "${STALWART_PID:-}" "${XVFB_PID:-}" 2>/dev/null || true
|
|
rm -rf "$TEST_HOME"
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
command -v stalwart >/dev/null || {
|
|
echo "stalwart not in PATH."
|
|
echo "Run inside the nix dev shell:"
|
|
echo " nix develop --command stalwart-dev/integration_ui_test.sh"
|
|
exit 1
|
|
}
|
|
command -v xvfb-run >/dev/null || {
|
|
echo "xvfb-run not in PATH."
|
|
echo "Run inside the nix dev shell:"
|
|
echo " nix develop --command stalwart-dev/integration_ui_test.sh"
|
|
exit 1
|
|
}
|
|
|
|
# Reap orphan Xvfb processes from previous sessions that were killed mid-flight.
|
|
# Without this, xvfb-run's wrapper may pick a fresh display via --auto-servernum
|
|
# but the leftover Xvfb's stale /tmp/.X11-unix/X<N> socket and lock file confuse
|
|
# its cleanup, producing "kill: No such process" on exit and a non-zero status
|
|
# even when the test itself passed.
|
|
for _xvfb_pid in $(pgrep -u "$USER" -x Xvfb 2>/dev/null); do
|
|
_xvfb_display=$(tr '\0' ' ' < "/proc/${_xvfb_pid}/cmdline" 2>/dev/null \
|
|
| grep -oE ':[0-9]+' | head -1)
|
|
kill "$_xvfb_pid" 2>/dev/null || true
|
|
[ -n "$_xvfb_display" ] && {
|
|
rm -f "/tmp/.X11-unix/X${_xvfb_display#:}" "/tmp/.X${_xvfb_display#:}-lock" 2>/dev/null || true
|
|
}
|
|
done
|
|
|
|
ts "script start"
|
|
|
|
# Pre-seed spam-filter version so Stalwart does not fetch it on first boot.
|
|
mkdir -p "$STALWART_TMPDIR"
|
|
sqlite3 "${STALWART_TMPDIR}/data.sqlite" \
|
|
"CREATE TABLE IF NOT EXISTS s (k BLOB PRIMARY KEY, v BLOB NOT NULL);
|
|
INSERT OR REPLACE INTO s VALUES ('version.spam-filter', 'dev');" 2>/dev/null || true
|
|
|
|
LOGFILE="${STALWART_TMPDIR}/stalwart.log"
|
|
rm -f "$LOGFILE"
|
|
|
|
ts "stalwart start"
|
|
"$(dirname "$0")/start" >"$LOGFILE" 2>&1 &
|
|
STALWART_PID=$!
|
|
|
|
# Wait until Stalwart is accepting connections (up to 10 s).
|
|
for _i in $(seq 1 20); do
|
|
# shellcheck source=/dev/null
|
|
[ -f "${STALWART_TMPDIR}/ports.env" ] && . "${STALWART_TMPDIR}/ports.env"
|
|
grep -E "already in use" "$LOGFILE" >/dev/null 2>&1 && {
|
|
cat "$LOGFILE"; echo "Stalwart port already in use"; exit 1
|
|
}
|
|
kill -0 "$STALWART_PID" 2>/dev/null || {
|
|
cat "$LOGFILE"; echo "Stalwart process died unexpectedly"; exit 1
|
|
}
|
|
if [ -n "${STALWART_URL:-}" ] && \
|
|
curl -s --max-time 1 -o /dev/null "${STALWART_URL}/.well-known/jmap" 2>/dev/null; then
|
|
break
|
|
fi
|
|
sleep 0.5
|
|
done
|
|
|
|
[ -n "${STALWART_URL:-}" ] || { cat "$LOGFILE"; echo "Stalwart did not publish its chosen ports"; exit 1; }
|
|
curl -s --max-time 1 -o /dev/null "${STALWART_URL}/.well-known/jmap" || {
|
|
cat "$LOGFILE"; echo "Stalwart did not become ready"; exit 1
|
|
}
|
|
|
|
ts "stalwart ready — IMAP=:${STALWART_IMAP_PORT:-?} SMTP=:${STALWART_SMTP_PORT:-?}"
|
|
|
|
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
cd "$ROOT"
|
|
|
|
export STALWART_IMAP_HOST="127.0.0.1"
|
|
export STALWART_SMTP_HOST="127.0.0.1"
|
|
# Isolate app data (path_provider uses XDG_DATA_HOME on Linux) without
|
|
# overriding HOME — keeping the real HOME lets FVM reuse its cached SDK.
|
|
export XDG_DATA_HOME="$TEST_HOME"
|
|
|
|
ts "flutter test start"
|
|
|
|
# Kill any orphan sharedinbox/flutter processes left by previous CI runs.
|
|
# Stale processes can hold onto the Xvfb display, causing the new Flutter app
|
|
# to hang indefinitely during GTK initialisation without ever connecting back
|
|
# to the flutter test runner.
|
|
pkill -u "$USER" -f "sharedinbox" 2>/dev/null || true
|
|
pkill -u "$USER" -f "flutter.*integration" 2>/dev/null || true
|
|
sleep 1
|
|
|
|
# Find an unused display number.
|
|
_display=99
|
|
while [ -e "/tmp/.X${_display}-lock" ]; do _display=$((_display + 1)); done
|
|
|
|
# Manage Xvfb directly instead of via xvfb-run. xvfb-run catches SIGTERM,
|
|
# kills its children, and exits 0 — so `timeout 240 xvfb-run ...` exits 0 on
|
|
# timeout, making a stuck/timed-out test indistinguishable from a pass.
|
|
# Running Xvfb ourselves lets us capture fvm flutter test's real exit code.
|
|
# +iglx: indirect GLX so Flutter/GTK3 gets an OpenGL context via mesa software
|
|
# renderer (LIBGL_ALWAYS_SOFTWARE=1 is set in flake.nix).
|
|
Xvfb ":${_display}" -screen 0 1280x720x24 +iglx &
|
|
XVFB_PID=$!
|
|
export DISPLAY=":${_display}"
|
|
|
|
# Wait for the Xvfb Unix socket to appear (up to 5 s).
|
|
for _xi in $(seq 1 10); do
|
|
[ -S "/tmp/.X11-unix/X${_display}" ] && break
|
|
sleep 0.5
|
|
done
|
|
[ -S "/tmp/.X11-unix/X${_display}" ] || { echo "Xvfb :${_display} did not start"; exit 1; }
|
|
|
|
# Retry once: if the first attempt gets stuck in GTK/display init,
|
|
# a fresh Xvfb on a new display number usually succeeds on the second try.
|
|
_e2e_exit=0
|
|
for _attempt in 1 2; do
|
|
ts "E2E attempt $_attempt (DISPLAY=$DISPLAY)"
|
|
timeout 360 fvm flutter test integration_test/ -d linux
|
|
_e2e_exit=$?
|
|
[ $_e2e_exit -eq 0 ] && break
|
|
if [ $_attempt -lt 2 ]; then
|
|
ts "E2E attempt $_attempt failed (exit $_e2e_exit), restarting Xvfb and retrying..."
|
|
pkill -u "$USER" -f "sharedinbox" 2>/dev/null || true
|
|
# Kill the old Xvfb and start a fresh one on a new display number.
|
|
kill "${XVFB_PID:-}" 2>/dev/null || true
|
|
wait "${XVFB_PID:-}" 2>/dev/null || true
|
|
rm -f "/tmp/.X${_display}-lock" "/tmp/.X11-unix/X${_display}" 2>/dev/null || true
|
|
_display=$((_display + 1))
|
|
while [ -e "/tmp/.X${_display}-lock" ]; do _display=$((_display + 1)); done
|
|
Xvfb ":${_display}" -screen 0 1280x720x24 +iglx &
|
|
XVFB_PID=$!
|
|
export DISPLAY=":${_display}"
|
|
for _xi in $(seq 1 10); do
|
|
[ -S "/tmp/.X11-unix/X${_display}" ] && break
|
|
sleep 0.5
|
|
done
|
|
sleep 2
|
|
fi
|
|
done
|
|
[ $_e2e_exit -eq 0 ] || exit $_e2e_exit
|
|
|
|
ts "flutter test done"
|