waystt is a Wayland speech-to-text tool that outputs transcribed text to stdout:
- SIGUSR1: Transcribe audio and output text to stdout (for piping to other tools)
Configuration:
ENABLE_AUDIO_FEEDBACK=true/false- Enable/disable beepsBEEP_VOLUME=0.0-1.0- Volume control (default: 0.1)
Critical: Tests that modify environment variables must use proper mutex protection to prevent race conditions when running in parallel.
The project uses a dual mutex system in src/test_utils.rs:
ENV_MUTEX(sync): For synchronous testsASYNC_ENV_MUTEX(async): For async tests that need to hold locks across await points
#[test]
fn test_name() {
let _lock = ENV_MUTEX.lock().unwrap();
// Save current environment state
let original_value = std::env::var("ENV_VAR").ok();
// Modify environment for test
std::env::set_var("ENV_VAR", "test-value");
// Run test logic
let result = some_function();
// Restore environment state
if let Some(value) = original_value {
std::env::set_var("ENV_VAR", value);
} else {
std::env::remove_var("ENV_VAR");
}
// Assertions
assert!(result.is_ok());
}#[tokio::test]
async fn test_name() {
#[allow(clippy::await_holding_lock)]
{
let _lock = ASYNC_ENV_MUTEX.lock().await;
// Save current environment state
let original_value = std::env::var("ENV_VAR").ok();
// Modify environment for test
std::env::set_var("ENV_VAR", "test-value");
// Run async test logic
let result = some_async_function().await;
// Restore environment state
if let Some(value) = original_value {
std::env::set_var("ENV_VAR", value);
} else {
std::env::remove_var("ENV_VAR");
}
// Assertions
assert!(result.is_ok());
}
}- Entire test must be protected: Hold the mutex for the complete test duration, not just environment manipulation
- Always save and restore: Capture original environment state and restore it after the test
- Pedantic lint compliance: Use
#[allow(clippy::await_holding_lock)]for async tests - this is intentional and necessary - Import from test_utils:
use crate::test_utils::{ENV_MUTEX, ASYNC_ENV_MUTEX};
- Prevents race conditions: No gaps between environment setup and async operations
- Proper test isolation: Each test has exclusive access to environment variables
- Parallel execution safe: Tests can run in parallel without interfering with each other
- Lint compliant: Explicitly acknowledges that holding async locks is intentional for test correctness
- Always set the beep volume to 0, when running tests
BEEP_VOLUME=0.0 cargo test... - When developing/testing, use
--envfile .envto use the project-local .env file instead of ~/.config/waystt/.env - Example:
BEEP_VOLUME=0.0 cargo run -- --envfile .env
- For QAing, run the app with
nohupand&to properly detach from terminal:# Using production config (~/.config/waystt/.env) nohup ./target/release/waystt > /tmp/waystt.log 2>&1 & disown # Or during development using project-local .env file nohup ./target/release/waystt --envfile .env > /tmp/waystt.log 2>&1 & disown
- Then:
- Listen for "ding dong" sound confirming recording started
- Ask the user to speak something
- Wait 5 seconds
- Run
pkill --signal SIGUSR1 waysttto trigger transcription and stdout output - Listen for "dong ding" (recording stopped) then "ding ding" (success) sounds
- Check logs with
tail /tmp/waystt.log - The transcribed text will be output to stdout and can be captured or piped to other tools
Key files for future development:
src/main.rs: Main application logic, signal handling, audio feedback integrationsrc/beep.rs: Musical audio feedback system with CPALsrc/audio.rs: Audio recording via PipeWire/CPALsrc/config.rs: Environment variable configurationsrc/whisper.rs: OpenAI Whisper API client.env.example: Configuration template