"""Tests for ASREngine."""
import pytest
import numpy as np
from unittest.mock import patch, MagicMock
class TestASREngine:
"""Tests for ASREngine speech recognition."""
def test_asr_no_auto_load(self, mock_onnx_model):
"""ASREngine does not load model on instantiation (lazy loading)."""
from src.localvoicemode.speech.asr import ASREngine
# Patch onnx_asr.load_model to track if it's called
with patch("onnx_asr.load_model") as mock_load:
engine = ASREngine(device="cpu")
# Model should not be loaded yet
assert engine._model is None
# load_model should NOT have been called
mock_load.assert_not_called()
def test_transcribe_calls_model(self, mock_onnx_model, mock_audio_data):
"""ASREngine.transcribe calls the underlying model."""
from src.localvoicemode.speech.asr import ASREngine
engine = ASREngine(device="cpu")
result = engine.transcribe(mock_audio_data)
assert result == "transcribed text"
mock_onnx_model.load_model.return_value.recognize.assert_called_once()
def test_transcribe_short_audio_returns_empty(self, mock_onnx_model):
"""ASREngine returns empty string for very short audio."""
from src.localvoicemode.speech.asr import ASREngine
engine = ASREngine(device="cpu")
short_audio = np.zeros(100, dtype=np.float32)
result = engine.transcribe(short_audio)
# Short audio should return empty or be handled gracefully
assert isinstance(result, str)
assert result == ""
def test_lazy_loading_triggered_by_ensure_loaded(self, mock_onnx_model):
"""ASREngine loads model when _ensure_loaded is called."""
from src.localvoicemode.speech.asr import ASREngine
engine = ASREngine(device="cpu")
assert engine._model is None
# Trigger loading
engine._ensure_loaded()
assert engine._model is not None
def test_init_with_custom_device(self, mock_onnx_model):
"""ASREngine respects device parameter."""
from src.localvoicemode.speech.asr import ASREngine
engine_cpu = ASREngine(device="cpu")
engine_cuda = ASREngine(device="cuda")
assert engine_cpu._device == "cpu"
assert engine_cuda._device == "cuda"
def test_init_with_custom_sample_rate(self, mock_onnx_model):
"""ASREngine respects sample_rate parameter."""
from src.localvoicemode.speech.asr import ASREngine
engine = ASREngine(device="cpu", sample_rate=48000)
assert engine._sample_rate == 48000
def test_transcribe_handles_dict_result(self, mock_onnx_model, mock_audio_data):
"""ASREngine handles dict result from model."""
from src.localvoicemode.speech.asr import ASREngine
# Configure mock to return dict
mock_onnx_model.load_model.return_value.recognize.return_value = {
"text": "dict result text"
}
engine = ASREngine(device="cpu")
result = engine.transcribe(mock_audio_data)
assert result == "dict result text"
def test_transcribe_handles_string_result(self, mock_onnx_model, mock_audio_data):
"""ASREngine handles string result from model."""
from src.localvoicemode.speech.asr import ASREngine
# Configure mock to return string
mock_onnx_model.load_model.return_value.recognize.return_value = " string result "
engine = ASREngine(device="cpu")
result = engine.transcribe(mock_audio_data)
# Should be stripped
assert result == "string result"
class TestASRProviders:
"""Tests for ASREngine ONNX Runtime provider selection."""
def test_get_providers_cpu(self, mock_onnx_model):
"""ASREngine returns CPU provider when device is cpu."""
from src.localvoicemode.speech.asr import ASREngine
engine = ASREngine(device="cpu")
providers = engine._get_providers()
assert providers == ["CPUExecutionProvider"]
def test_get_providers_cuda_with_available(self, mock_onnx_model, mocker):
"""ASREngine includes CUDA provider when available."""
from src.localvoicemode.speech.asr import ASREngine
# Mock onnxruntime to report CUDA available
mock_ort = MagicMock()
mock_ort.get_available_providers.return_value = [
"CUDAExecutionProvider",
"CPUExecutionProvider",
]
mocker.patch.dict("sys.modules", {"onnxruntime": mock_ort})
engine = ASREngine(device="cuda")
providers = engine._get_providers()
# Should include CUDA first, then CPU fallback
assert len(providers) == 2
assert providers[0][0] == "CUDAExecutionProvider"
assert providers[-1] == "CPUExecutionProvider"
def test_get_providers_cuda_fallback_to_cpu(self, mock_onnx_model, mocker):
"""ASREngine falls back to CPU when CUDA not available."""
from src.localvoicemode.speech.asr import ASREngine
# Mock onnxruntime to report only CPU available
mock_ort = MagicMock()
mock_ort.get_available_providers.return_value = ["CPUExecutionProvider"]
mocker.patch.dict("sys.modules", {"onnxruntime": mock_ort})
engine = ASREngine(device="cuda")
providers = engine._get_providers()
# Should only have CPU
assert providers == ["CPUExecutionProvider"]