Repository navigation
Expand file tree
/
Copy pathtest-linux-dictation.sh
More file actions
executable file
·131 lines (130 loc) · 5.29 KB
/
Copy pathtest-linux-dictation.sh
File metadata and controls
executable file
·131 lines (130 loc) · 5.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#!/usr/bin/env bash
set -euo pipefail
if [[ ${1:-} != --inside ]]; then
if [[ $# != 3 || $(id -u) == 0 ]]; then
echo "Usage: $0 /path/to/hex /path/to/model.gguf /path/to/jfk.wav (non-root)" >&2
exit 1
fi
for command in cc pkg-config pulseaudio pactl paplay xdotool xvfb-run timeout; do
command -v "$command" >/dev/null
done
exec timeout --kill-after=10 300 xvfb-run -a --server-args="-screen 0 1280x900x24 -noreset" \
"$0" --inside "$(realpath "$1")" "$(realpath "$2")" "$(realpath "$3")"
fi
shift
binary=$1
model=$2
audio=$3
root=$(cd -- "$(dirname -- "$0")/.." && pwd)
work=$(mktemp -d)
listener=
target=
pulse=
finish() {
status=$?
trap - EXIT INT TERM
for pid in "$listener" "$target" "$pulse"; do
if [[ -n "$pid" ]]; then kill "$pid" 2>/dev/null || true; fi
done
for ((attempt = 0; attempt < 100; attempt++)); do
if [[ -z $(jobs -pr) ]]; then break; fi
sleep 0.05
done
for pid in $(jobs -pr); do kill -KILL "$pid" 2>/dev/null || true; done
wait 2>/dev/null || true
if [[ $status != 0 ]]; then
for log in "$work"/*.log "$work"/support/logs/live.ndjson; do
if [[ -f "$log" ]]; then printf '\n%s\n' "$log"; cat "$log"; fi
done
fi
rm -rf -- "$work"
exit "$status"
}
trap finish EXIT
trap 'exit 130' INT
trap 'exit 143' TERM
export HOME="$work/home" XDG_RUNTIME_DIR="$work/runtime"
export HEX_APPLICATION_SUPPORT_DIR="$work/support" NO_COLOR=1
export PULSE_SERVER="unix:$work/pulse" PULSE_SOURCE=hex-fixture.monitor
# The window uses software Vulkan on Xvfb; only inference uses the CPU in this fixture.
export GDK_BACKEND=x11 GGML_VK_VISIBLE_DEVICES=""
unset WAYLAND_DISPLAY VK_ICD_FILENAMES
mkdir -m 700 "$HOME" "$XDG_RUNTIME_DIR" "$HEX_APPLICATION_SUPPORT_DIR"
mkdir "$HEX_APPLICATION_SUPPORT_DIR/models"
# This GTK target uses Ctrl-V. Omitting the preference from an existing settings
# file deliberately selects the legacy terminal Ctrl-Shift-V behavior.
printf '{"sound_effects":false,"paste_with_shift":false,"migrations":["streaming-english-model"]}\n' > "$HEX_APPLICATION_SUPPORT_DIR/settings.json"
ln -s "$model" "$HEX_APPLICATION_SUPPORT_DIR/models/$(basename "$model")"
printf 'pcm.!default { type pulse server "%s" }\n' "$PULSE_SERVER" > "$HOME/.asoundrc"
pulseaudio -n --daemonize=no --use-pid-file=no --exit-idle-time=-1 \
--load="module-native-protocol-unix socket=$work/pulse auth-anonymous=1" \
--load='module-null-sink sink_name=hex-fixture rate=16000 channels=1' \
>"$work/pulse.log" 2>&1 &
pulse=$!
for ((attempt = 0; attempt < 100; attempt++)); do
if pactl info >/dev/null 2>&1; then break; fi
sleep 0.05
done
pactl info >/dev/null
cc "$root/tests/fixtures/wayland-paste-target.c" $(pkg-config --cflags --libs gtk+-3.0) -o "$work/target"
"$work/target" "$work/pasted.txt" >"$work/target.log" 2>&1 &
target=$!
window=$(xdotool search --sync --name '^Hex Wayland Test$' | head -n 1)
xdotool windowfocus --sync "$window"
"$binary" service >"$work/listener.log" 2>&1 &
listener=$!
echo 'Waiting for the model and virtual microphone...'
for ((attempt = 0; attempt < 1200; attempt++)); do
kill -0 "$listener"
if grep -q 'Hex dictation is ready' "$work/listener.log"; then break; fi
sleep 0.05
done
grep -q 'Hex dictation is ready' "$work/listener.log"
# A second launch forwards to the running Hex, which opens its window; the
# complete dictation below runs beside it in the same process.
"$binary" app >"$work/app.log" 2>&1
xdotool search --sync --onlyvisible --all --pid "$listener" --name '^Hex$' >/dev/null
kill -0 "$listener"
# Holds the shortcut over the speech, then waits until the target holds
# `$1` transcripts of it.
dictate() {
xdotool windowfocus --sync "$window"
echo 'Replaying speech through the held dictation shortcut...'
xdotool keydown Alt_L keydown space
sleep 0.5
paplay --device=hex-fixture "$audio"
sleep 0.5
xdotool keyup space keyup Alt_L
echo 'Waiting for transcribed text in the focused GTK target...'
for ((attempt = 0; attempt < 1800; attempt++)); do
kill -0 "$listener"
if (($(grep -oi 'ask what you can do for your country' "$work/pasted.txt" 2>/dev/null | wc -l) >= $1)); then break; fi
sleep 0.05
done
(($(grep -oi 'ask not what your country can do for you' "$work/pasted.txt" | wc -l) == $1))
(($(grep -oi 'ask what you can do for your country' "$work/pasted.txt" | wc -l) == $1))
}
dictate 1
printf 'Captured, transcribed, and pasted: %s\n' "$(<"$work/pasted.txt")"
# The CPU never streams by default, so that dictation took the whole-clip path.
grep -Eq 'transcribed Linux dictation.*source.{0,32}offline' "$work/listener.log"
kill -TERM "$listener"
wait "$listener"
# Force a live stream on the CPU to exercise capture-time feeding, the finish,
# and (when the stream lags behind real time) the whole-clip fallback.
HEX_STREAMING=1 "$binary" service >"$work/stream.log" 2>&1 &
listener=$!
for ((attempt = 0; attempt < 1200; attempt++)); do
kill -0 "$listener"
if grep -q 'Hex dictation is ready' "$work/stream.log"; then break; fi
sleep 0.05
done
grep -q 'streaming transcription is available' "$work/stream.log"
dictate 2
grep -E 'transcribed Linux dictation.*source.{0,32}(stream|fallback)' "$work/stream.log"
kill -TERM "$listener"
wait "$listener"
listener=
outputs=$(pactl list short source-outputs)
test -z "$outputs"
echo 'Linux dictation (whole clip and live stream) beside the window and orderly shutdown passed.'