Current Status: 62.03% line coverage (163 passing, 46 ignored)
Run coverage report:
cargo llvm-cov # Terminal summary
cargo llvm-cov --lcov --output-path coverage.lcov # LCOV format
cargo llvm-cov --html && open target/llvm-cov/html/index.html # HTMLCoverage by Module:
config.rs: 88.07% ✅input/hotkey.rs: 72.31% ✅audio/capture.rs: 70.28% ✅transcription/download.rs: 60.87%tray.rs: 58.97%permissions.rs: 55.83%input/cgevent.rs: 54.17%telemetry.rs: 49.40%transcription/engine.rs: 45.26%main.rs: 0.00% (binary, not unit testable)
Expected Coverage Gaps:
Some modules have lower coverage due to external dependencies that cannot be mocked in unit tests:
- transcription/engine.rs (327 lines, 180 uncovered): Requires actual Whisper model file (~75MB). Tests are
#[ignore] - tray.rs (1,105 lines, 439 uncovered): Requires macOS main thread for Menu/TrayIcon creation. Tests are
#[ignore] - main.rs (210 lines, 210 uncovered): Binary initialization and event loop, requires full system integration
These account for 829 uncovered lines (58.5% of total 1,416 gaps). Remaining gaps are testable. Current 62.03% coverage is realistic given integration-heavy codebase.
Phase Completion:
- ✅ Phase 1: Low-hanging fruit (47% → 60%)
- ✅ Phase 2: Mock infrastructure (60% → 75%)
- ✅ Phase 3: Tray menu logic (75% → 82%)
- ✅ Phase 4: Testability refactoring (58% → 62%)
- Extracted pure configuration logic from FFI calls
- tray.rs: Menu configuration now 100% testable
- engine.rs: Sampling strategy extracted and tested
- +17 new unit tests covering previously untestable code
After building, test the full pipeline:
- Build:
cargo build --release - Run:
cargo run --release - Test hotkey: Press Ctrl+Option+Z (or configured hotkey)
- Record: Hold for 5s, speak clearly
- Verify: Text inserted at cursor
Expected latency: <2.5s total (5s recording + <2s transcription + <100ms insertion)
# Fast unit tests (no hardware)
cargo test
# Hardware tests (requires mic + permissions)
cargo test -- --ignoredKey tests:
- config.rs:237 - Config parsing with default optimization parameters
- engine.rs:327 - Long 30s recordings
- engine.rs:347 - Optimization param variations
- engine.rs:374 - Noise handling
Enable detailed logging:
RUST_LOG=whisper_hotkey=trace cargo run --releaseKey metrics (check logs):
start_recording: latency_us <50μs targetstop_recording: total_ms <50ms targetconvert_to_16khz_mono: total_us <20ms targettranscription: inference_ms <2000ms for 10s audio
# Install flamegraph
cargo install flamegraph
# Profile (run app, trigger hotkey, Ctrl+C)
sudo cargo flamegraph --release
# Open flamegraph.svg
open flamegraph.svgLook for:
- Whisper inference (should dominate)
- Audio conversion overhead (should be minimal)
- Unnecessary allocations in hot paths
# Build release
cargo build --release
# Profile allocations
instruments -t Allocations target/release/whisper-hotkey
# Profile leaks
instruments -t Leaks target/release/whisper-hotkeyTrigger multiple recordings (10+) while profiling.
Expected memory:
- Idle: ~1.5GB (model loaded)
- During transcription: +50-100MB temporary
- No leaks after recordings complete
valgrind --leak-check=full --show-leak-kinds=all target/release/whisper-hotkey- Press hotkey, don't speak, release
- Expected: Empty or minimal text
- "Hello world"
- Expected: Accurate transcription, <1.5s latency
- Read paragraph continuously
- Expected: Complete transcription, <5s latency, no crashes
- Record with background music/TV
- Expected: Partial transcription or gibberish (no crash)
- Press/release 10 times quickly (0.1s each)
- Expected: All cycles complete, no deadlocks
- Press hotkey during active transcription
- Expected: Graceful handling (queue or reject)
Test text insertion in:
- ✓ TextEdit
- ✓ VS Code
- ✓ Chrome (Gmail, Google Docs)
- ✓ Slack
- ✓ Terminal (may fail - secure input mode)
- ✓ Notes
- ✓ Messages
Check logs for insertion failures: ~/.whisper-hotkey/crash.log
Edit ~/.whisper-hotkey.toml:
[model]
threads = 8 # Try 2, 4, 8
beam_size = 1 # Try 1 (fast), 5 (balanced), 10 (accurate)Restart app, test 10s recording:
- threads=8, beam_size=1: Fastest (~1s)
- threads=4, beam_size=5: Balanced (~2s)
- threads=4, beam_size=10: Accurate (~3s)
[model]
name = "tiny" # or base, small, medium, large
path = "~/.whisper-hotkey/models/ggml-tiny.bin"Restart, verify auto-download + transcription accuracy.
| Metric | Target | Command |
|---|---|---|
| Audio start | <50μs | RUST_LOG=trace logs |
| Transcription (10s) | <2s | RUST_LOG=info logs |
| Text insertion | <100ms | Test manually |
| Idle CPU | <1% | Activity Monitor |
| Idle RAM | ~1.5GB | Activity Monitor |
| No leaks | 0 | Instruments/Valgrind |
Failing targets? Check:
- Config optimization (threads, beam_size)
- Model size (try smaller)
- Flamegraph for bottlenecks
# Check config syntax
cat ~/.whisper-hotkey.toml
# Validate manually
cargo run 2>&1 | grep -i error
# Reset config
rm ~/.whisper-hotkey.toml && cargo run# Check logs
tail -f ~/.whisper-hotkey/crash.log
# Verify model
ls -lh ~/.whisper-hotkey/models/
# Test directly
cargo test test_transcribe_short_audio -- --ignored --nocapture# Reset mic permissions
tccutil reset Microphone
# Restart app, grant permission
# Verify device
cargo test test_audio_capture_initialization -- --ignored --nocapture# Profile with Instruments
instruments -t Leaks target/release/whisper-hotkey
# Check ringbuf not growing
# Run 100 recordings, verify RSS stableGitHub Actions runs on PR/main:
cargo fmt --checkcargo clippy -- -D warningscargo test(unit only, no hardware)cargo build --release
Hardware tests (require manual run):
cargo test -- --ignoredInclude:
- macOS version (
sw_vers) - Chip (M1/M2/Intel)
- Logs (
~/.whisper-hotkey/crash.log) - Config (
~/.whisper-hotkey.toml) - Steps to reproduce
- Expected vs actual behavior
Example:
**macOS**: 14.0 (M1 Pro)
**Config**: threads=4, beam_size=5, model=small
**Issue**: 30s recording crashes with "mutex poisoned"
**Logs**: [attach crash.log]
**Reproduce**: Hold hotkey for 30s, speak continuously