Improve cloud tts tests (#106427)

This commit is contained in:
Martin Hjelmare 2023-12-27 08:48:07 +01:00 committed by GitHub
parent 2cc6fd1afb
commit 59a01da0ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 148 additions and 47 deletions

View File

@ -152,6 +152,11 @@ def mock_tts_cache_dir_autouse(mock_tts_cache_dir):
return mock_tts_cache_dir
@pytest.fixture(autouse=True)
def tts_mutagen_mock_fixture_autouse(tts_mutagen_mock):
"""Mock writing tags."""
@pytest.fixture(autouse=True)
def mock_user_data():
"""Mock os module."""

View File

@ -1,23 +1,35 @@
"""Tests for cloud tts."""
from unittest.mock import Mock
from collections.abc import Callable, Coroutine
from http import HTTPStatus
from typing import Any
from unittest.mock import AsyncMock, MagicMock
from hass_nabucasa import voice
from hass_nabucasa.voice import MAP_VOICE, VoiceError
import pytest
import voluptuous as vol
from homeassistant.components.cloud import const, tts
from homeassistant.components.cloud import DOMAIN, const, tts
from homeassistant.components.tts import DOMAIN as TTS_DOMAIN
from homeassistant.components.tts.helper import get_engine_instance
from homeassistant.config import async_process_ha_core_config
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component
from tests.typing import ClientSessionGenerator
@pytest.fixture
def cloud_with_prefs(cloud_prefs):
"""Return a cloud mock with prefs."""
return Mock(client=Mock(prefs=cloud_prefs))
@pytest.fixture(autouse=True)
async def internal_url_mock(hass: HomeAssistant) -> None:
"""Mock internal URL of the instance."""
await async_process_ha_core_config(
hass,
{"internal_url": "http://example.local:8123"},
)
def test_default_exists() -> None:
"""Test our default language exists."""
assert const.DEFAULT_TTS_DEFAULT_VOICE in voice.MAP_VOICE
assert const.DEFAULT_TTS_DEFAULT_VOICE in MAP_VOICE
def test_schema() -> None:
@ -42,54 +54,138 @@ def test_schema() -> None:
tts.PLATFORM_SCHEMA({"platform": "cloud"})
@pytest.mark.parametrize(
("engine_id", "platform_config"),
[
(
DOMAIN,
None,
),
(
DOMAIN,
{
"platform": DOMAIN,
"service_name": "yaml",
"language": "fr-FR",
"gender": "female",
},
),
],
)
async def test_prefs_default_voice(
hass: HomeAssistant, cloud_with_prefs, cloud_prefs
hass: HomeAssistant,
cloud: MagicMock,
set_cloud_prefs: Callable[[dict[str, Any]], Coroutine[Any, Any, None]],
engine_id: str,
platform_config: dict[str, Any] | None,
) -> None:
"""Test cloud provider uses the preferences."""
assert cloud_prefs.tts_default_voice == ("en-US", "female")
tts_info = {"platform_loaded": Mock()}
provider_pref = await tts.async_get_engine(
Mock(data={const.DOMAIN: cloud_with_prefs}), None, tts_info
)
provider_conf = await tts.async_get_engine(
Mock(data={const.DOMAIN: cloud_with_prefs}),
{"language": "fr-FR", "gender": "female"},
None,
)
assert provider_pref.default_language == "en-US"
assert provider_pref.default_options == {"gender": "female", "audio_output": "mp3"}
assert provider_conf.default_language == "fr-FR"
assert provider_conf.default_options == {"gender": "female", "audio_output": "mp3"}
await cloud_prefs.async_update(tts_default_voice=("nl-NL", "male"))
assert await async_setup_component(hass, "homeassistant", {})
assert await async_setup_component(hass, TTS_DOMAIN, {TTS_DOMAIN: platform_config})
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
await hass.async_block_till_done()
assert provider_pref.default_language == "nl-NL"
assert provider_pref.default_options == {"gender": "male", "audio_output": "mp3"}
assert provider_conf.default_language == "fr-FR"
assert provider_conf.default_options == {"gender": "female", "audio_output": "mp3"}
assert cloud.client.prefs.tts_default_voice == ("en-US", "female")
on_start_callback = cloud.register_on_start.call_args[0][0]
await on_start_callback()
engine = get_engine_instance(hass, engine_id)
assert engine is not None
# The platform config provider will be overridden by the discovery info provider.
assert engine.default_language == "en-US"
assert engine.default_options == {"gender": "female", "audio_output": "mp3"}
await set_cloud_prefs({"tts_default_voice": ("nl-NL", "male")})
await hass.async_block_till_done()
assert engine.default_language == "nl-NL"
assert engine.default_options == {"gender": "male", "audio_output": "mp3"}
async def test_provider_properties(cloud_with_prefs) -> None:
async def test_provider_properties(
hass: HomeAssistant,
cloud: MagicMock,
) -> None:
"""Test cloud provider."""
tts_info = {"platform_loaded": Mock()}
provider = await tts.async_get_engine(
Mock(data={const.DOMAIN: cloud_with_prefs}), None, tts_info
)
assert provider.supported_options == ["gender", "voice", "audio_output"]
assert "nl-NL" in provider.supported_languages
assert tts.Voice(
"ColetteNeural", "ColetteNeural"
) in provider.async_get_supported_voices("nl-NL")
assert await async_setup_component(hass, "homeassistant", {})
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
await hass.async_block_till_done()
on_start_callback = cloud.register_on_start.call_args[0][0]
await on_start_callback()
engine = get_engine_instance(hass, DOMAIN)
assert engine is not None
assert engine.supported_options == ["gender", "voice", "audio_output"]
assert "nl-NL" in engine.supported_languages
supported_voices = engine.async_get_supported_voices("nl-NL")
assert supported_voices is not None
assert tts.Voice("ColetteNeural", "ColetteNeural") in supported_voices
supported_voices = engine.async_get_supported_voices("missing_language")
assert supported_voices is None
async def test_get_tts_audio(cloud_with_prefs) -> None:
@pytest.mark.parametrize(
("data", "expected_url_suffix"),
[
({"platform": DOMAIN}, DOMAIN),
({"engine_id": DOMAIN}, DOMAIN),
],
)
@pytest.mark.parametrize(
("mock_process_tts_return_value", "mock_process_tts_side_effect"),
[
(b"", None),
(None, VoiceError("Boom!")),
],
)
async def test_get_tts_audio(
hass: HomeAssistant,
hass_client: ClientSessionGenerator,
cloud: MagicMock,
data: dict[str, Any],
expected_url_suffix: str,
mock_process_tts_return_value: bytes | None,
mock_process_tts_side_effect: Exception | None,
) -> None:
"""Test cloud provider."""
tts_info = {"platform_loaded": Mock()}
provider = await tts.async_get_engine(
Mock(data={const.DOMAIN: cloud_with_prefs}), None, tts_info
mock_process_tts = AsyncMock(
return_value=mock_process_tts_return_value,
side_effect=mock_process_tts_side_effect,
)
assert provider.supported_options == ["gender", "voice", "audio_output"]
assert "nl-NL" in provider.supported_languages
cloud.voice.process_tts = mock_process_tts
assert await async_setup_component(hass, "homeassistant", {})
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
await hass.async_block_till_done()
on_start_callback = cloud.register_on_start.call_args[0][0]
await on_start_callback()
client = await hass_client()
url = "/api/tts_get_url"
data |= {"message": "There is someone at the door."}
req = await client.post(url, json=data)
assert req.status == HTTPStatus.OK
response = await req.json()
assert response == {
"url": (
"http://example.local:8123/api/tts_proxy/"
"42f18378fd4393d18c8dd11d03fa9563c1e54491"
f"_en-us_e09b5a0968_{expected_url_suffix}.mp3"
),
"path": (
"/api/tts_proxy/42f18378fd4393d18c8dd11d03fa9563c1e54491"
f"_en-us_e09b5a0968_{expected_url_suffix}.mp3"
),
}
await hass.async_block_till_done()
assert mock_process_tts.call_count == 1
assert mock_process_tts.call_args is not None
assert mock_process_tts.call_args.kwargs["text"] == "There is someone at the door."
assert mock_process_tts.call_args.kwargs["language"] == "en-US"
assert mock_process_tts.call_args.kwargs["gender"] == "female"
assert mock_process_tts.call_args.kwargs["output"] == "mp3"