Files
feresaria/scripts/soak-native.sh
T

170 lines
4.1 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
soak_seconds="${FERESE_SOAK_SECONDS:-86400}"
iteration_delay="${FERESE_SOAK_DELAY:-0.15}"
client_program="${FERESE_SOAK_CLIENT:-foot}"
feresectl_program="${FERESECTL:-target/debug/feresectl}"
malformed_client_program="${FERESE_MALFORMED_CLIENT:-target/debug/ferese-malformed-client}"
compositor_pid="${FERESE_PID:-}"
log_path="${FERESE_SOAK_LOG:-/tmp/ferese-soak-$$.log}"
if [[ -z "${WAYLAND_DISPLAY:-}" ]]; then
echo "WAYLAND_DISPLAY must name the Ferese socket" >&2
exit 2
fi
if [[ ! "$soak_seconds" =~ ^[1-9][0-9]*$ ]]; then
echo "FERESE_SOAK_SECONDS must be a positive integer" >&2
exit 2
fi
if [[ ! -x "$feresectl_program" ]]; then
cargo build -p feresectl
fi
if [[ ! -x "$malformed_client_program" ]]; then
cargo build -p ferese-malformed-client
fi
if ! command -v "$client_program" >/dev/null 2>&1; then
echo "soak client not found: $client_program" >&2
exit 2
fi
if [[ -z "$compositor_pid" ]]; then
compositor_pid="$(pgrep -n -x ferese || true)"
fi
if [[ -z "$compositor_pid" || ! -r "/proc/$compositor_pid/status" ]]; then
echo "set FERESE_PID to the running compositor process" >&2
exit 2
fi
client_pids=()
cleanup() {
local pid
for pid in "${client_pids[@]}"; do
if kill -0 "$pid" 2>/dev/null; then
kill -TERM "$pid" 2>/dev/null || true
fi
done
}
trap cleanup EXIT INT TERM
rss_kib() {
awk '/^VmRSS:/ { print $2; exit }' "/proc/$compositor_pid/status"
}
control() {
"$feresectl_program" "$@" >/dev/null
}
launch_client() {
"$client_program" >/dev/null 2>&1 &
client_pids+=("$!")
}
reap_clients() {
local pid
for pid in "${client_pids[@]}"; do
if kill -0 "$pid" 2>/dev/null; then
kill -TERM "$pid" 2>/dev/null || true
fi
wait "$pid" 2>/dev/null || true
done
client_pids=()
}
wait_for_focus() {
local attempt
local focused
for ((attempt = 0; attempt < 40; attempt += 1)); do
focused="$("$feresectl_program" get-focused-window 2>/dev/null || true)"
if [[ "$focused" != "null" && -n "$focused" ]]; then
return 0
fi
sleep 0.05
done
echo "a launched client did not receive focus" >&2
return 1
}
start_time="$SECONDS"
deadline=$((start_time + soak_seconds))
iteration=0
initial_rss="$(rss_kib)"
maximum_rss="$initial_rss"
{
echo "ferese native soak"
echo "pid=$compositor_pid display=$WAYLAND_DISPLAY client=$client_program"
echo "duration_seconds=$soak_seconds initial_rss_kib=$initial_rss"
} | tee "$log_path"
while ((SECONDS < deadline)); do
if ! kill -0 "$compositor_pid" 2>/dev/null; then
echo "FAIL compositor exited at iteration $iteration" | tee -a "$log_path"
exit 1
fi
launch_client
launch_client
wait_for_focus
control focus left
control focus right
control resize left
control resize right
control cycle-column-width
control cycle-column-width
control toggle-fullscreen
control toggle-fullscreen
control toggle-floating
control toggle-floating
control workspace 2
control workspace 1
control close
sleep "$iteration_delay"
control close
sleep "$iteration_delay"
if ((iteration % 10 == 0)); then
launch_client
wait_for_focus
kill -TERM "${client_pids[-1]}" 2>/dev/null || true
"$malformed_client_program" >>"$log_path" 2>&1
control get-outputs
fi
reap_clients
current_rss="$(rss_kib)"
if ((current_rss > maximum_rss)); then
maximum_rss="$current_rss"
fi
if ((iteration % 100 == 0)); then
printf 'iteration=%d elapsed=%d rss_kib=%d max_rss_kib=%d\n' \
"$iteration" "$((SECONDS - start_time))" "$current_rss" "$maximum_rss" \
| tee -a "$log_path"
fi
iteration=$((iteration + 1))
sleep "$iteration_delay"
done
control get-outputs
control get-workspaces
final_rss="$(rss_kib)"
printf 'PASS iterations=%d final_rss_kib=%d max_rss_kib=%d growth_kib=%d\n' \
"$iteration" "$final_rss" "$maximum_rss" "$((final_rss - initial_rss))" \
| tee -a "$log_path"