Files
StarPilot/openpilot/starpilot/audio/tests/test_backend_selection.py
T

68 lines
3.0 KiB
Python

from types import SimpleNamespace
from unittest.mock import Mock, patch
import pytest
from openpilot.selfdrive.ui import soundd
from openpilot.starpilot.audio import native_playback
def device():
return SimpleNamespace(_terminate=Mock(), _initialize=Mock(), OutputStream=Mock(return_value=object()))
def daemon():
obj = soundd.Soundd.__new__(soundd.Soundd)
obj.native_audio = None
return obj
def test_host_backend_reports_python_without_attempting_native():
obj, sd = daemon(), device()
with patch.object(soundd, 'PC', True), patch.object(native_playback, 'NativePlayback') as ctor:
assert soundd.Soundd.get_stream.__wrapped__(obj, sd) is sd.OutputStream.return_value
assert (obj.audio_backend, obj.audio_fallback_reason) == ('python', 'host')
ctor.assert_not_called()
assert sd.OutputStream.call_args.kwargs == {'channels': 1, 'samplerate': 48000, 'callback': obj.callback, 'blocksize': 4096}
def test_native_backend_reports_native_without_python_stream():
obj, sd = daemon(), device()
playback = Mock()
with patch.object(soundd, 'PC', False), patch.object(native_playback, 'NativePlayback', return_value=playback):
assert soundd.Soundd.get_stream.__wrapped__(obj, sd) is playback.stream.return_value
assert obj.native_audio is playback
assert (obj.audio_backend, obj.audio_fallback_reason) == ('native', None)
sd.OutputStream.assert_not_called()
playback.close.assert_not_called()
@pytest.mark.parametrize('exception', (OSError, AttributeError, ValueError, MemoryError))
def test_constructor_failure_preserves_python_audio_and_bounded_reason(exception):
obj, sd = daemon(), device()
with patch.object(soundd, 'PC', False), patch.object(native_playback, 'NativePlayback', side_effect=exception('private arbitrary detail')):
assert soundd.Soundd.get_stream.__wrapped__(obj, sd) is sd.OutputStream.return_value
assert obj.native_audio is None
assert (obj.audio_backend, obj.audio_fallback_reason) == ('python', 'initialization:' + exception.__name__)
@pytest.mark.parametrize('exception', (AttributeError, TypeError, ValueError))
def test_native_stream_unsupported_preserves_close_and_fallback(exception):
obj, sd = daemon(), device()
playback = Mock()
playback.stream.side_effect = exception('private arbitrary detail')
with patch.object(soundd, 'PC', False), patch.object(native_playback, 'NativePlayback', return_value=playback):
assert soundd.Soundd.get_stream.__wrapped__(obj, sd) is sd.OutputStream.return_value
playback.close.assert_called_once_with()
assert (obj.audio_backend, obj.audio_fallback_reason) == ('python', 'stream:' + exception.__name__)
def test_native_backend_unhandled_failure_closes_and_remains_an_error():
obj, sd = daemon(), device()
playback = Mock()
playback.stream.side_effect = OSError('backend open failed')
with patch.object(soundd, 'PC', False), patch.object(native_playback, 'NativePlayback', return_value=playback), pytest.raises(OSError):
soundd.Soundd.get_stream.__wrapped__(obj, sd)
playback.close.assert_called_once_with()
sd.OutputStream.assert_not_called()