Files
Captioneer/scripts/smoke/desktop-smoke.sh
T

191 lines
6.6 KiB
Bash
Raw Normal View History

#!/usr/bin/env bash
# Manual desktop smoke test.
#
# Boots a headless Wayland compositor (sway) inside the captioneer-dev
# Distrobox, routes synthesized speech through a temporary PipeWire null
# sink, runs the real captioneer-desktop binary against it, and fails on
# diagnostics errors or missing captions.
#
# Usage (from the repository root, on the host):
# ./scripts/download-models.sh # once
# ./scripts/distrobox/build.sh # produce build/captioneer-desktop
# ./scripts/smoke/desktop-smoke.sh
#
# Environment:
# CAPTIONEER_SMOKE_MODE vad (default) or fixed
# CAPTIONEER_SMOKE_SPEECH_S seconds of speech to play (default 20)
set -Eeuo pipefail
script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
repo_root="$(cd -- "$script_dir/../.." && pwd -P)"
# ---------------------------------------------------------------------------
# Host phase: prepare the Distrobox and re-execute this script inside it.
# ---------------------------------------------------------------------------
if [[ -z "${CAPTIONEER_SMOKE_INNER:-}" && -z "${CONTAINER_ID:-}" ]]; then
# shellcheck source=scripts/distrobox/common.sh
source "$repo_root/scripts/distrobox/common.sh"
prepare_distrobox
[[ -x "$repo_root/build/captioneer-desktop" ]] \
|| die "build/captioneer-desktop is missing; run ./scripts/distrobox/build.sh first"
[[ -d "$repo_root/models/parakeet-tdt-v2" ]] \
|| die "models/parakeet-tdt-v2 is missing; run ./scripts/download-models.sh first"
[[ -f "$repo_root/models/silero_vad_v5.onnx" ]] \
|| die "models/silero_vad_v5.onnx is missing; run ./scripts/download-models.sh first"
# shellcheck disable=SC2016 # Positional parameters expand inside the container.
# shellcheck disable=SC2154 # container_name is defined by common.sh above.
exec distrobox enter --no-workdir --name "$container_name" -- \
bash -lc 'CAPTIONEER_SMOKE_INNER=1 exec bash "$1"' captioneer-smoke \
"$repo_root/scripts/smoke/desktop-smoke.sh"
fi
# ---------------------------------------------------------------------------
# Inner phase: runs inside the Distrobox.
# ---------------------------------------------------------------------------
# shellcheck source=scripts/smoke/lib.sh
source "$script_dir/lib.sh"
smoke_mode="${CAPTIONEER_SMOKE_MODE:-vad}"
speech_seconds="${CAPTIONEER_SMOKE_SPEECH_S:-20}"
# On-demand smoke dependencies; kept out of common.sh so normal builds stay lean.
smoke_packages=(sway espeak-ng)
missing_packages=()
for package in "${smoke_packages[@]}"; do
rpm -q "$package" >/dev/null 2>&1 || missing_packages+=("$package")
done
if (( ${#missing_packages[@]} > 0 )); then
smoke_log "installing smoke dependencies: ${missing_packages[*]}"
sudo dnf install -y "${missing_packages[@]}"
fi
cd -- "$repo_root"
workdir="$repo_root/build/smoke/$(date +%Y%m%d-%H%M%S)"
mkdir -p "$workdir/transcripts"
smoke_log "work directory: $workdir"
trap smoke_cleanup EXIT
smoke_setup_audio "$workdir"
wayland_socket="$(smoke_start_compositor "$workdir")"
smoke_log "nested compositor ready on $wayland_socket"
# ---------------------------------------------------------------------------
# Launch the desktop application.
# ---------------------------------------------------------------------------
env \
WAYLAND_DISPLAY="$wayland_socket" \
GDK_BACKEND=wayland \
GSK_RENDERER=cairo \
./build/captioneer-desktop \
--no-config \
--start-captions=true \
--output=both \
--stderr-diagnostics=true \
--caption-log=true \
--caption-log-dir="$workdir/transcripts" \
--models-dir=models \
--mode="$smoke_mode" \
--model-auto-restart=false \
> "$workdir/captioneer.stdout.log" \
2> "$workdir/captioneer.stderr.log" &
SMOKE_APP_PID=$!
smoke_log "captioneer-desktop started (pid $SMOKE_APP_PID, mode $smoke_mode)"
stderr_log="$workdir/captioneer.stderr.log"
if ! smoke_wait_for_line "$stderr_log" "Model worker ready" 60; then
smoke_fail "model worker did not become ready within 60s"
fi
if ! smoke_wait_for_line "$stderr_log" "Capturing from:" 30; then
smoke_fail "audio capture did not start within 30s"
fi
if (( ${#SMOKE_FAILURES[@]} == 0 )); then
smoke_log "playing synthesized speech for ${speech_seconds}s"
smoke_play_speech "$workdir" "$speech_seconds"
wait "$SMOKE_PLAYER_PID" 2>/dev/null || true
SMOKE_PLAYER_PID=""
# Allow VAD finalization (500ms pause) and transcript flush to settle.
sleep 4
fi
smoke_stop_app
app_exit=0
if smoke_wait_for_exit "$SMOKE_APP_PID" 20; then
wait "$SMOKE_APP_PID" 2>/dev/null || app_exit=$?
else
smoke_fail "application did not exit within 20s of SIGINT"
kill -KILL "$SMOKE_APP_PID" 2>/dev/null || true
app_exit=137
fi
SMOKE_APP_PID=""
# ---------------------------------------------------------------------------
# Assertions.
# ---------------------------------------------------------------------------
if (( app_exit == 0 )); then
smoke_pass "clean shutdown (exit 0)"
else
smoke_fail "application exited with status $app_exit"
fi
if grep -q "Model worker ready" "$stderr_log"; then
smoke_pass "model worker started"
fi
if grep -q "Capturing from:" "$stderr_log"; then
smoke_pass "audio capture attached to a monitor source"
fi
if grep -E " ERROR " "$stderr_log" > "$workdir/errors.txt"; then
smoke_fail "diagnostics contain ERROR lines (see errors.txt)"
else
smoke_pass "no ERROR diagnostics"
fi
mapfile -t caption_files < <(find "$workdir/transcripts" -type f -size +0c 2>/dev/null)
if (( ${#caption_files[@]} > 0 )); then
smoke_pass "caption log contains recognized speech"
else
smoke_fail "no captions were recognized; inspect $workdir and consider CAPTIONEER_SMOKE_MODE=fixed"
fi
# ---------------------------------------------------------------------------
# Report.
# ---------------------------------------------------------------------------
report="$workdir/report.txt"
{
printf 'Captioneer desktop smoke report\n'
printf 'Date: %s\n' "$(date --rfc-3339=seconds)"
printf 'Mode: %s\n' "$smoke_mode"
printf 'Compositor: sway headless (%s)\n' "$wayland_socket"
printf 'Sink: %s\n' "$SMOKE_SINK_NAME"
printf 'App exit: %s\n' "$app_exit"
printf '\n--- Key diagnostics ---\n'
grep -E " (ERROR|WARNING) |Model worker ready|Capturing from:|Shutting down" \
"$stderr_log" 2>/dev/null || printf '(none)\n'
printf '\n--- Caption transcript excerpt ---\n'
if (( ${#caption_files[@]} > 0 )); then
head -n 10 "${caption_files[@]}" 2>/dev/null
else
printf '(empty)\n'
fi
printf '\n--- Result ---\n'
if (( ${#SMOKE_FAILURES[@]} == 0 )); then
printf 'PASS\n'
else
printf 'FAIL:\n'
printf ' - %s\n' "${SMOKE_FAILURES[@]}"
fi
} > "$report"
cat "$report"
smoke_log "full report: $report"
if (( ${#SMOKE_FAILURES[@]} > 0 )); then
exit 1
fi
exit 0