test: add native client soak harness
This commit is contained in:
1 parent
4eb456fb2b
commit
d46becfd60
3 files changed
+210
No files matched your search
@@ -28,6 +28,9 @@ Set `FERESE_TRACE_PERFORMANCE=1` to emit five-second per-output summaries under
|
||||
the `ferese::render` tracing target. Summaries include rendered frames, damaged
|
||||
pixels, average and longest render time, and DRM missed-deadline totals.
|
||||
|
||||
The native-client soak runner and its nested/DRM procedure are documented in
|
||||
[docs/native-soak-testing.md](docs/native-soak-testing.md).
|
||||
|
||||
Ferese reads `$XDG_CONFIG_HOME/ferese/config.toml`, or
|
||||
`~/.config/ferese/config.toml` when `XDG_CONFIG_HOME` is unset. New scrolling
|
||||
columns use half the viewport by default. To make every newly opened column use
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
# Native-client soak testing
|
||||
|
||||
The M6 soak runner repeatedly creates and destroys native Wayland clients while
|
||||
exercising focus, resize, column width, fullscreen, floating, and workspace
|
||||
paths. Every tenth iteration also terminates a client abruptly. The runner
|
||||
checks that the compositor and authenticated IPC socket remain responsive and
|
||||
records compositor RSS growth.
|
||||
|
||||
Build Ferese and `feresectl`, start the compositor, and run:
|
||||
|
||||
```bash
|
||||
FERESE_TRACE_PERFORMANCE=1 cargo run -p ferese -- foot
|
||||
```
|
||||
|
||||
From a terminal connected to Ferese, or from the host with the Ferese socket
|
||||
selected, run:
|
||||
|
||||
```bash
|
||||
WAYLAND_DISPLAY=wayland-1 \
|
||||
FERESE_PID="$(pgrep -n -x ferese)" \
|
||||
./scripts/soak-native.sh
|
||||
```
|
||||
|
||||
The default duration is 24 hours. A shorter validation run is:
|
||||
|
||||
```bash
|
||||
WAYLAND_DISPLAY=wayland-1 \
|
||||
FERESE_PID="$(pgrep -n -x ferese)" \
|
||||
FERESE_SOAK_SECONDS=300 \
|
||||
./scripts/soak-native.sh
|
||||
```
|
||||
|
||||
Environment controls:
|
||||
|
||||
- `FERESE_SOAK_SECONDS`: run duration, default `86400`;
|
||||
- `FERESE_SOAK_DELAY`: delay between operations, default `0.15` seconds;
|
||||
- `FERESE_SOAK_CLIENT`: native client executable, default `foot`;
|
||||
- `FERESECTL`: path to `feresectl`, default `target/debug/feresectl`;
|
||||
- `FERESE_PID`: compositor PID; automatic lookup is only a fallback;
|
||||
- `FERESE_SOAK_LOG`: result log, default `/tmp/ferese-soak-<pid>.log`.
|
||||
|
||||
Run the same workload once with the nested backend and once from a direct DRM
|
||||
session. A passing run requires the compositor to remain alive, IPC queries to
|
||||
succeed, and RSS samples to stabilize rather than grow without bound. Preserve
|
||||
the soak log and the `ferese::render` summaries with the release artifacts.
|
||||
Executable
+162
@@ -0,0 +1,162 @@
|
||||
#!/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}"
|
||||
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 ! 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
|
||||
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"
|
||||
Reference in new issue
Block a user