mirror of
https://github.com/home-assistant/core.git
synced 2026-09-27 18:08:37 -04:00
Add tts entity (#91692)
* Add tts entity * Allow passing engine id to url view * Update async_resolve_engine * Add and update more tests * Fix assist pipeline tests temporarily * Move fixtures * Update notify platform * Complete legacy tests * Update media source tests * Update async_get_text_to_speech_languages * Address comment --------- Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
This commit is contained in:
co-authored by
Paulus Schoutsen
parent
458276a6a6
commit
1a18dc7425
@@ -1,4 +1,6 @@
|
||||
"""Test fixtures for voice assistant."""
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import AsyncIterable, Generator
|
||||
from typing import Any
|
||||
from unittest.mock import AsyncMock
|
||||
@@ -23,6 +25,7 @@ from tests.common import (
|
||||
mock_platform,
|
||||
)
|
||||
from tests.components.tts.conftest import ( # noqa: F401, pylint: disable=unused-import
|
||||
init_cache_dir_side_effect,
|
||||
mock_get_cache_files,
|
||||
mock_init_cache_dir,
|
||||
)
|
||||
@@ -33,11 +36,10 @@ _TRANSCRIPT = "test transcript"
|
||||
class BaseProvider:
|
||||
"""Mock STT provider."""
|
||||
|
||||
def __init__(self, hass: HomeAssistant, text: str) -> None:
|
||||
def __init__(self, text: str) -> None:
|
||||
"""Init test provider."""
|
||||
self.hass = hass
|
||||
self.text = text
|
||||
self.received = []
|
||||
self.received: list[bytes] = []
|
||||
|
||||
@property
|
||||
def supported_languages(self) -> list[str]:
|
||||
@@ -115,7 +117,7 @@ class MockTTSProvider(tts.Provider):
|
||||
return ("mp3", b"")
|
||||
|
||||
|
||||
class MockTTS:
|
||||
class MockTTS(MockPlatform):
|
||||
"""A mock TTS platform."""
|
||||
|
||||
PLATFORM_SCHEMA = tts.PLATFORM_SCHEMA
|
||||
@@ -131,15 +133,15 @@ class MockTTS:
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def mock_stt_provider(hass) -> MockSttProvider:
|
||||
async def mock_stt_provider() -> MockSttProvider:
|
||||
"""Mock STT provider."""
|
||||
return MockSttProvider(hass, _TRANSCRIPT)
|
||||
return MockSttProvider(_TRANSCRIPT)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_stt_provider_entity(hass) -> MockSttProviderEntity:
|
||||
def mock_stt_provider_entity() -> MockSttProviderEntity:
|
||||
"""Test provider entity fixture."""
|
||||
return MockSttProviderEntity(hass, _TRANSCRIPT)
|
||||
return MockSttProviderEntity(_TRANSCRIPT)
|
||||
|
||||
|
||||
class MockSttPlatform(MockPlatform):
|
||||
@@ -170,8 +172,9 @@ async def init_components(
|
||||
mock_stt_provider: MockSttProvider,
|
||||
mock_stt_provider_entity: MockSttProviderEntity,
|
||||
config_flow_fixture,
|
||||
init_cache_dir_side_effect, # noqa: F811
|
||||
mock_get_cache_files, # noqa: F811
|
||||
mock_init_cache_dir, # noqa: F811,
|
||||
mock_init_cache_dir, # noqa: F811
|
||||
):
|
||||
"""Initialize relevant components with empty configs."""
|
||||
|
||||
|
||||
+107
-12
@@ -5,30 +5,50 @@ from typing import Any
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components import media_source
|
||||
from homeassistant.components.tts import (
|
||||
CONF_LANG,
|
||||
DOMAIN as TTS_DOMAIN,
|
||||
PLATFORM_SCHEMA,
|
||||
Provider,
|
||||
TextToSpeechEntity,
|
||||
TtsAudioType,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from tests.common import MockPlatform
|
||||
|
||||
SUPPORT_LANGUAGES = ["de_CH", "de_DE", "en_GB", "en_US"]
|
||||
TEST_LANGUAGES = ["de", "en"]
|
||||
from tests.common import (
|
||||
MockConfigEntry,
|
||||
MockModule,
|
||||
MockPlatform,
|
||||
mock_integration,
|
||||
mock_platform,
|
||||
)
|
||||
|
||||
DEFAULT_LANG = "en_US"
|
||||
SUPPORT_LANGUAGES = ["de_CH", "de_DE", "en_GB", "en_US"]
|
||||
TEST_DOMAIN = "test"
|
||||
TEST_LANGUAGES = ["de", "en"]
|
||||
|
||||
|
||||
class MockProvider(Provider):
|
||||
async def get_media_source_url(hass: HomeAssistant, media_content_id: str) -> str:
|
||||
"""Get the media source url."""
|
||||
if media_source.DOMAIN not in hass.config.components:
|
||||
assert await async_setup_component(hass, media_source.DOMAIN, {})
|
||||
|
||||
resolved = await media_source.async_resolve_media(hass, media_content_id, None)
|
||||
return resolved.url
|
||||
|
||||
|
||||
class BaseProvider:
|
||||
"""Test speech API provider."""
|
||||
|
||||
def __init__(self, lang: str) -> None:
|
||||
"""Initialize test provider."""
|
||||
self._lang = lang
|
||||
self.name = "Test"
|
||||
|
||||
@property
|
||||
def default_language(self) -> str:
|
||||
@@ -59,6 +79,24 @@ class MockProvider(Provider):
|
||||
return ("mp3", b"")
|
||||
|
||||
|
||||
class MockProvider(BaseProvider, Provider):
|
||||
"""Test speech API provider."""
|
||||
|
||||
def __init__(self, lang: str) -> None:
|
||||
"""Initialize test provider."""
|
||||
super().__init__(lang)
|
||||
self.name = "Test"
|
||||
|
||||
|
||||
class MockTTSEntity(BaseProvider, TextToSpeechEntity):
|
||||
"""Test speech API provider."""
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
"""Return the name of the entity."""
|
||||
return "Test"
|
||||
|
||||
|
||||
class MockTTS(MockPlatform):
|
||||
"""A mock TTS platform."""
|
||||
|
||||
@@ -70,13 +108,9 @@ class MockTTS(MockPlatform):
|
||||
}
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self, provider: type[MockProvider] | None = None, **kwargs: Any
|
||||
) -> None:
|
||||
def __init__(self, provider: MockProvider, **kwargs: Any) -> None:
|
||||
"""Initialize."""
|
||||
super().__init__(**kwargs)
|
||||
if provider is None:
|
||||
provider = MockProvider
|
||||
self._provider = provider
|
||||
|
||||
async def async_get_engine(
|
||||
@@ -86,4 +120,65 @@ class MockTTS(MockPlatform):
|
||||
discovery_info: DiscoveryInfoType | None = None,
|
||||
) -> Provider | None:
|
||||
"""Set up a mock speech component."""
|
||||
return self._provider(config.get(CONF_LANG, DEFAULT_LANG))
|
||||
return self._provider
|
||||
|
||||
|
||||
async def mock_setup(
|
||||
hass: HomeAssistant,
|
||||
mock_provider: MockProvider,
|
||||
) -> None:
|
||||
"""Set up a test provider."""
|
||||
mock_integration(hass, MockModule(domain=TEST_DOMAIN))
|
||||
mock_platform(hass, f"{TEST_DOMAIN}.{TTS_DOMAIN}", MockTTS(mock_provider))
|
||||
|
||||
await async_setup_component(
|
||||
hass, TTS_DOMAIN, {TTS_DOMAIN: {"platform": TEST_DOMAIN}}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
async def mock_config_entry_setup(
|
||||
hass: HomeAssistant, tts_entity: MockTTSEntity
|
||||
) -> MockConfigEntry:
|
||||
"""Set up a test tts platform via config entry."""
|
||||
|
||||
async def async_setup_entry_init(
|
||||
hass: HomeAssistant, config_entry: ConfigEntry
|
||||
) -> bool:
|
||||
"""Set up test config entry."""
|
||||
await hass.config_entries.async_forward_entry_setup(config_entry, TTS_DOMAIN)
|
||||
return True
|
||||
|
||||
async def async_unload_entry_init(
|
||||
hass: HomeAssistant, config_entry: ConfigEntry
|
||||
) -> bool:
|
||||
"""Unload up test config entry."""
|
||||
await hass.config_entries.async_forward_entry_unload(config_entry, TTS_DOMAIN)
|
||||
return True
|
||||
|
||||
mock_integration(
|
||||
hass,
|
||||
MockModule(
|
||||
TEST_DOMAIN,
|
||||
async_setup_entry=async_setup_entry_init,
|
||||
async_unload_entry=async_unload_entry_init,
|
||||
),
|
||||
)
|
||||
|
||||
async def async_setup_entry_platform(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up test tts platform via config entry."""
|
||||
async_add_entities([tts_entity])
|
||||
|
||||
loaded_platform = MockPlatform(async_setup_entry=async_setup_entry_platform)
|
||||
mock_platform(hass, f"{TEST_DOMAIN}.{TTS_DOMAIN}", loaded_platform)
|
||||
|
||||
config_entry = MockConfigEntry(domain=TEST_DOMAIN)
|
||||
config_entry.add_to_hass(hass)
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
return config_entry
|
||||
|
||||
@@ -2,17 +2,28 @@
|
||||
|
||||
From http://doc.pytest.org/en/latest/example/simple.html#making-test-result-information-available-in-fixtures
|
||||
"""
|
||||
from unittest.mock import patch
|
||||
from collections.abc import Callable, Generator
|
||||
from typing import Any
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.tts import _get_cache_files
|
||||
from homeassistant.config import async_process_ha_core_config
|
||||
from homeassistant.config_entries import ConfigFlow
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .common import MockTTS
|
||||
from .common import (
|
||||
DEFAULT_LANG,
|
||||
TEST_DOMAIN,
|
||||
MockProvider,
|
||||
MockTTS,
|
||||
MockTTSEntity,
|
||||
mock_config_entry_setup,
|
||||
mock_setup,
|
||||
)
|
||||
|
||||
from tests.common import MockModule, mock_integration, mock_platform
|
||||
from tests.common import MockModule, mock_config_flow, mock_integration, mock_platform
|
||||
|
||||
|
||||
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
|
||||
@@ -37,15 +48,30 @@ def mock_get_cache_files():
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def mock_init_cache_dir():
|
||||
def mock_init_cache_dir(
|
||||
init_cache_dir_side_effect: Any,
|
||||
) -> Generator[MagicMock, None, None]:
|
||||
"""Mock the TTS cache dir in memory."""
|
||||
with patch(
|
||||
"homeassistant.components.tts._init_tts_cache_dir",
|
||||
side_effect=lambda hass, cache_dir: hass.config.path(cache_dir),
|
||||
side_effect=init_cache_dir_side_effect,
|
||||
) as mock_cache_dir:
|
||||
yield mock_cache_dir
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def init_cache_dir_side_effect(
|
||||
hass: HomeAssistant,
|
||||
) -> Callable[[HomeAssistant, str], str]:
|
||||
"""Return the cache dir."""
|
||||
|
||||
def side_effect(hass: HomeAssistant, cache_dir: str) -> str:
|
||||
"""Return the cache dir."""
|
||||
return hass.config.path(cache_dir)
|
||||
|
||||
return side_effect
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def empty_cache_dir(tmp_path, mock_init_cache_dir, mock_get_cache_files, request):
|
||||
"""Mock the TTS cache dir with empty dir."""
|
||||
@@ -89,7 +115,48 @@ async def internal_url_mock(hass: HomeAssistant) -> None:
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def mock_tts(hass: HomeAssistant) -> None:
|
||||
async def mock_tts(hass: HomeAssistant, mock_provider) -> None:
|
||||
"""Mock TTS."""
|
||||
mock_integration(hass, MockModule(domain="test"))
|
||||
mock_platform(hass, "test.tts", MockTTS())
|
||||
mock_platform(hass, "test.tts", MockTTS(mock_provider))
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_provider() -> MockProvider:
|
||||
"""Test TTS provider."""
|
||||
return MockProvider(DEFAULT_LANG)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_tts_entity() -> MockTTSEntity:
|
||||
"""Test TTS entity."""
|
||||
return MockTTSEntity(DEFAULT_LANG)
|
||||
|
||||
|
||||
class TTSFlow(ConfigFlow):
|
||||
"""Test flow."""
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def config_flow_fixture(hass: HomeAssistant) -> Generator[None, None, None]:
|
||||
"""Mock config flow."""
|
||||
mock_platform(hass, f"{TEST_DOMAIN}.config_flow")
|
||||
|
||||
with mock_config_flow(TEST_DOMAIN, TTSFlow):
|
||||
yield
|
||||
|
||||
|
||||
@pytest.fixture(name="setup")
|
||||
async def setup_fixture(
|
||||
hass: HomeAssistant,
|
||||
request: pytest.FixtureRequest,
|
||||
mock_provider: MockProvider,
|
||||
mock_tts_entity: MockTTSEntity,
|
||||
) -> None:
|
||||
"""Set up the test environment."""
|
||||
if request.param == "mock_setup":
|
||||
await mock_setup(hass, mock_provider)
|
||||
elif request.param == "mock_config_entry_setup":
|
||||
await mock_config_entry_setup(hass, mock_tts_entity)
|
||||
else:
|
||||
raise RuntimeError("Invalid setup fixture")
|
||||
|
||||
+1095
-488
File diff suppressed because it is too large
Load Diff
@@ -3,32 +3,51 @@ from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.tts import DOMAIN, Provider
|
||||
from homeassistant.components.media_player import (
|
||||
ATTR_MEDIA_CONTENT_ID,
|
||||
ATTR_MEDIA_CONTENT_TYPE,
|
||||
DOMAIN as DOMAIN_MP,
|
||||
SERVICE_PLAY_MEDIA,
|
||||
MediaType,
|
||||
)
|
||||
from homeassistant.components.tts import ATTR_MESSAGE, DOMAIN, Provider
|
||||
from homeassistant.const import ATTR_ENTITY_ID
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.discovery import async_load_platform
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from .common import MockTTS
|
||||
from .common import SUPPORT_LANGUAGES, MockProvider, MockTTS, get_media_source_url
|
||||
|
||||
from tests.common import (
|
||||
MockModule,
|
||||
assert_setup_component,
|
||||
async_mock_service,
|
||||
mock_integration,
|
||||
mock_platform,
|
||||
)
|
||||
|
||||
|
||||
class DefaultProvider(Provider):
|
||||
"""Test provider."""
|
||||
|
||||
@property
|
||||
def supported_languages(self) -> list[str]:
|
||||
"""Return a list of supported languages."""
|
||||
return SUPPORT_LANGUAGES
|
||||
|
||||
|
||||
async def test_default_provider_attributes() -> None:
|
||||
"""Test default provider properties."""
|
||||
provider = Provider()
|
||||
"""Test default provider attributes."""
|
||||
provider = DefaultProvider()
|
||||
|
||||
assert provider.hass is None
|
||||
assert provider.name is None
|
||||
assert provider.default_language is None
|
||||
assert provider.supported_languages is None
|
||||
assert provider.supported_languages == SUPPORT_LANGUAGES
|
||||
assert provider.supported_options is None
|
||||
assert provider.default_options is None
|
||||
assert provider.async_get_supported_voices("test") is None
|
||||
|
||||
|
||||
async def test_deprecated_platform(hass: HomeAssistant) -> None:
|
||||
@@ -56,8 +75,7 @@ async def test_invalid_platform(
|
||||
|
||||
|
||||
async def test_platform_setup_without_provider(
|
||||
hass: HomeAssistant,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, mock_provider: MockProvider
|
||||
) -> None:
|
||||
"""Test platform setup without provider returned."""
|
||||
|
||||
@@ -74,7 +92,7 @@ async def test_platform_setup_without_provider(
|
||||
return None
|
||||
|
||||
mock_integration(hass, MockModule(domain="bad_tts"))
|
||||
mock_platform(hass, "bad_tts.tts", BadPlatform())
|
||||
mock_platform(hass, "bad_tts.tts", BadPlatform(mock_provider))
|
||||
|
||||
await async_load_platform(
|
||||
hass,
|
||||
@@ -91,6 +109,7 @@ async def test_platform_setup_without_provider(
|
||||
async def test_platform_setup_with_error(
|
||||
hass: HomeAssistant,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
mock_provider: MockProvider,
|
||||
) -> None:
|
||||
"""Test platform setup with an error during setup."""
|
||||
|
||||
@@ -107,7 +126,7 @@ async def test_platform_setup_with_error(
|
||||
raise Exception("Setup error") # pylint: disable=broad-exception-raised
|
||||
|
||||
mock_integration(hass, MockModule(domain="bad_tts"))
|
||||
mock_platform(hass, "bad_tts.tts", BadPlatform())
|
||||
mock_platform(hass, "bad_tts.tts", BadPlatform(mock_provider))
|
||||
|
||||
await async_load_platform(
|
||||
hass,
|
||||
@@ -119,3 +138,58 @@ async def test_platform_setup_with_error(
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert "Error setting up platform: bad_tts" in caplog.text
|
||||
|
||||
|
||||
async def test_service_base_url_set(hass: HomeAssistant, mock_tts) -> None:
|
||||
"""Set up a TTS platform with ``base_url`` set and call service."""
|
||||
calls = async_mock_service(hass, DOMAIN_MP, SERVICE_PLAY_MEDIA)
|
||||
|
||||
config = {DOMAIN: {"platform": "test", "base_url": "http://fnord"}}
|
||||
|
||||
with assert_setup_component(1, DOMAIN):
|
||||
assert await async_setup_component(hass, DOMAIN, config)
|
||||
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
"test_say",
|
||||
{
|
||||
ATTR_ENTITY_ID: "media_player.something",
|
||||
ATTR_MESSAGE: "There is someone at the door.",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
assert len(calls) == 1
|
||||
assert calls[0].data[ATTR_MEDIA_CONTENT_TYPE] == MediaType.MUSIC
|
||||
assert (
|
||||
await get_media_source_url(hass, calls[0].data[ATTR_MEDIA_CONTENT_ID])
|
||||
== "http://fnord"
|
||||
"/api/tts_proxy/42f18378fd4393d18c8dd11d03fa9563c1e54491"
|
||||
"_en-us_-_test.mp3"
|
||||
)
|
||||
|
||||
|
||||
async def test_service_without_cache_config(
|
||||
hass: HomeAssistant, empty_cache_dir, mock_tts
|
||||
) -> None:
|
||||
"""Set up a TTS platform without cache."""
|
||||
calls = async_mock_service(hass, DOMAIN_MP, SERVICE_PLAY_MEDIA)
|
||||
|
||||
config = {DOMAIN: {"platform": "test", "cache": False}}
|
||||
|
||||
with assert_setup_component(1, DOMAIN):
|
||||
assert await async_setup_component(hass, DOMAIN, config)
|
||||
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
"test_say",
|
||||
{
|
||||
ATTR_ENTITY_ID: "media_player.something",
|
||||
ATTR_MESSAGE: "There is someone at the door.",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
assert len(calls) == 1
|
||||
await hass.async_block_till_done()
|
||||
assert not (
|
||||
empty_cache_dir / "42f18378fd4393d18c8dd11d03fa9563c1e54491_en-us_-_test.mp3"
|
||||
).is_file()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""Tests for TTS media source."""
|
||||
from unittest.mock import patch
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -8,33 +8,52 @@ from homeassistant.components.media_player.errors import BrowseError
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from .common import (
|
||||
DEFAULT_LANG,
|
||||
MockProvider,
|
||||
MockTTSEntity,
|
||||
mock_config_entry_setup,
|
||||
mock_setup,
|
||||
)
|
||||
|
||||
|
||||
class MSEntity(MockTTSEntity):
|
||||
"""Test speech API entity."""
|
||||
|
||||
get_tts_audio = MagicMock(return_value=("mp3", b""))
|
||||
|
||||
|
||||
class MSProvider(MockProvider):
|
||||
"""Test speech API provider."""
|
||||
|
||||
get_tts_audio = MagicMock(return_value=("mp3", b""))
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
async def mock_get_tts_audio(hass):
|
||||
async def setup_media_source(hass: HomeAssistant) -> None:
|
||||
"""Set up media source."""
|
||||
assert await async_setup_component(hass, "media_source", {})
|
||||
assert await async_setup_component(
|
||||
hass,
|
||||
"tts",
|
||||
{
|
||||
"tts": {
|
||||
"platform": "demo",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.demo.tts.DemoProvider.get_tts_audio",
|
||||
return_value=("mp3", b""),
|
||||
) as mock_get_tts:
|
||||
yield mock_get_tts
|
||||
|
||||
|
||||
async def test_browsing(hass: HomeAssistant) -> None:
|
||||
@pytest.mark.parametrize(
|
||||
("mock_provider", "mock_tts_entity"),
|
||||
[(MSProvider(DEFAULT_LANG), MSEntity(DEFAULT_LANG))],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"setup",
|
||||
[
|
||||
"mock_setup",
|
||||
"mock_config_entry_setup",
|
||||
],
|
||||
indirect=["setup"],
|
||||
)
|
||||
async def test_browsing(hass: HomeAssistant, setup: str) -> None:
|
||||
"""Test browsing TTS media source."""
|
||||
item = await media_source.async_browse_media(hass, "media-source://tts")
|
||||
|
||||
assert item is not None
|
||||
assert item.title == "Text to Speech"
|
||||
assert item.children is not None
|
||||
assert len(item.children) == 1
|
||||
assert item.can_play is False
|
||||
assert item.can_expand is True
|
||||
@@ -42,9 +61,10 @@ async def test_browsing(hass: HomeAssistant) -> None:
|
||||
item_child = await media_source.async_browse_media(
|
||||
hass, item.children[0].media_content_id
|
||||
)
|
||||
|
||||
assert item_child is not None
|
||||
assert item_child.media_content_id == item.children[0].media_content_id
|
||||
assert item_child.title == "Demo"
|
||||
assert item_child.title == "Test"
|
||||
assert item_child.children is None
|
||||
assert item_child.can_play is False
|
||||
assert item_child.can_expand is True
|
||||
@@ -52,12 +72,13 @@ async def test_browsing(hass: HomeAssistant) -> None:
|
||||
item_child = await media_source.async_browse_media(
|
||||
hass, item.children[0].media_content_id + "?message=bla"
|
||||
)
|
||||
|
||||
assert item_child is not None
|
||||
assert (
|
||||
item_child.media_content_id
|
||||
== item.children[0].media_content_id + "?message=bla"
|
||||
)
|
||||
assert item_child.title == "Demo"
|
||||
assert item_child.title == "Test"
|
||||
assert item_child.children is None
|
||||
assert item_child.can_play is False
|
||||
assert item_child.can_expand is True
|
||||
@@ -66,10 +87,14 @@ async def test_browsing(hass: HomeAssistant) -> None:
|
||||
await media_source.async_browse_media(hass, "media-source://tts/non-existing")
|
||||
|
||||
|
||||
async def test_resolving(hass: HomeAssistant, mock_get_tts_audio) -> None:
|
||||
"""Test resolving."""
|
||||
@pytest.mark.parametrize("mock_provider", [MSProvider(DEFAULT_LANG)])
|
||||
async def test_legacy_resolving(hass: HomeAssistant, mock_provider: MSProvider) -> None:
|
||||
"""Test resolving legacy provider."""
|
||||
await mock_setup(hass, mock_provider)
|
||||
mock_get_tts_audio = mock_provider.get_tts_audio
|
||||
|
||||
media = await media_source.async_resolve_media(
|
||||
hass, "media-source://tts/demo?message=Hello%20World", None
|
||||
hass, "media-source://tts/test?message=Hello%20World", None
|
||||
)
|
||||
assert media.url.startswith("/api/tts_proxy/")
|
||||
assert media.mime_type == "audio/mpeg"
|
||||
@@ -77,14 +102,14 @@ async def test_resolving(hass: HomeAssistant, mock_get_tts_audio) -> None:
|
||||
assert len(mock_get_tts_audio.mock_calls) == 1
|
||||
message, language = mock_get_tts_audio.mock_calls[0][1]
|
||||
assert message == "Hello World"
|
||||
assert language == "en"
|
||||
assert language == "en_US"
|
||||
assert mock_get_tts_audio.mock_calls[0][2]["options"] is None
|
||||
|
||||
# Pass language and options
|
||||
mock_get_tts_audio.reset_mock()
|
||||
media = await media_source.async_resolve_media(
|
||||
hass,
|
||||
"media-source://tts/demo?message=Bye%20World&language=de&voice=Paulus",
|
||||
"media-source://tts/test?message=Bye%20World&language=de&voice=Paulus",
|
||||
None,
|
||||
)
|
||||
assert media.url.startswith("/api/tts_proxy/")
|
||||
@@ -93,15 +118,62 @@ async def test_resolving(hass: HomeAssistant, mock_get_tts_audio) -> None:
|
||||
assert len(mock_get_tts_audio.mock_calls) == 1
|
||||
message, language = mock_get_tts_audio.mock_calls[0][1]
|
||||
assert message == "Bye World"
|
||||
assert language == "de"
|
||||
assert language == "de_DE"
|
||||
assert mock_get_tts_audio.mock_calls[0][2]["options"] == {"voice": "Paulus"}
|
||||
|
||||
|
||||
async def test_resolving_errors(hass: HomeAssistant) -> None:
|
||||
@pytest.mark.parametrize("mock_tts_entity", [MSEntity(DEFAULT_LANG)])
|
||||
async def test_resolving(hass: HomeAssistant, mock_tts_entity: MSEntity) -> None:
|
||||
"""Test resolving entity."""
|
||||
await mock_config_entry_setup(hass, mock_tts_entity)
|
||||
mock_get_tts_audio = mock_tts_entity.get_tts_audio
|
||||
|
||||
media = await media_source.async_resolve_media(
|
||||
hass, "media-source://tts/tts.test?message=Hello%20World", None
|
||||
)
|
||||
assert media.url.startswith("/api/tts_proxy/")
|
||||
assert media.mime_type == "audio/mpeg"
|
||||
|
||||
assert len(mock_get_tts_audio.mock_calls) == 1
|
||||
message, language = mock_get_tts_audio.mock_calls[0][1]
|
||||
assert message == "Hello World"
|
||||
assert language == "en_US"
|
||||
assert mock_get_tts_audio.mock_calls[0][2]["options"] is None
|
||||
|
||||
# Pass language and options
|
||||
mock_get_tts_audio.reset_mock()
|
||||
media = await media_source.async_resolve_media(
|
||||
hass,
|
||||
"media-source://tts/tts.test?message=Bye%20World&language=de&voice=Paulus",
|
||||
None,
|
||||
)
|
||||
assert media.url.startswith("/api/tts_proxy/")
|
||||
assert media.mime_type == "audio/mpeg"
|
||||
|
||||
assert len(mock_get_tts_audio.mock_calls) == 1
|
||||
message, language = mock_get_tts_audio.mock_calls[0][1]
|
||||
assert message == "Bye World"
|
||||
assert language == "de_DE"
|
||||
assert mock_get_tts_audio.mock_calls[0][2]["options"] == {"voice": "Paulus"}
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("mock_provider", "mock_tts_entity"),
|
||||
[(MSProvider(DEFAULT_LANG), MSEntity(DEFAULT_LANG))],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"setup",
|
||||
[
|
||||
"mock_setup",
|
||||
"mock_config_entry_setup",
|
||||
],
|
||||
indirect=["setup"],
|
||||
)
|
||||
async def test_resolving_errors(hass: HomeAssistant, setup: str) -> None:
|
||||
"""Test resolving."""
|
||||
# No message added
|
||||
with pytest.raises(media_source.Unresolvable):
|
||||
await media_source.async_resolve_media(hass, "media-source://tts/demo", None)
|
||||
await media_source.async_resolve_media(hass, "media-source://tts/test", None)
|
||||
|
||||
# Non-existing provider
|
||||
with pytest.raises(media_source.Unresolvable):
|
||||
|
||||
@@ -1,28 +1,22 @@
|
||||
"""The tests for the TTS component."""
|
||||
import pytest
|
||||
import yarl
|
||||
|
||||
import homeassistant.components.media_player as media_player
|
||||
from homeassistant.components import media_player, notify, tts
|
||||
from homeassistant.components.media_player import (
|
||||
DOMAIN as DOMAIN_MP,
|
||||
SERVICE_PLAY_MEDIA,
|
||||
)
|
||||
import homeassistant.components.notify as notify
|
||||
import homeassistant.components.tts as tts
|
||||
from homeassistant.config import async_process_ha_core_config
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from .common import MockTTSEntity, mock_config_entry_setup
|
||||
|
||||
from tests.common import assert_setup_component, async_mock_service
|
||||
|
||||
|
||||
def relative_url(url):
|
||||
"""Convert an absolute url to a relative one."""
|
||||
return str(yarl.URL(url).relative())
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
async def internal_url_mock(hass):
|
||||
async def internal_url_mock(hass: HomeAssistant) -> None:
|
||||
"""Mock internal URL of the instance."""
|
||||
await async_process_ha_core_config(
|
||||
hass,
|
||||
@@ -30,8 +24,8 @@ async def internal_url_mock(hass):
|
||||
)
|
||||
|
||||
|
||||
async def test_setup_platform(hass: HomeAssistant) -> None:
|
||||
"""Set up the tts platform ."""
|
||||
async def test_setup_legacy_platform(hass: HomeAssistant) -> None:
|
||||
"""Set up the tts notify platform ."""
|
||||
config = {
|
||||
notify.DOMAIN: {
|
||||
"platform": "tts",
|
||||
@@ -46,7 +40,23 @@ async def test_setup_platform(hass: HomeAssistant) -> None:
|
||||
assert hass.services.has_service(notify.DOMAIN, "tts_test")
|
||||
|
||||
|
||||
async def test_setup_component_and_test_service(hass: HomeAssistant) -> None:
|
||||
async def test_setup_platform(hass: HomeAssistant) -> None:
|
||||
"""Set up the tts notify platform ."""
|
||||
config = {
|
||||
notify.DOMAIN: {
|
||||
"platform": "tts",
|
||||
"name": "tts_test",
|
||||
"entity_id": "tts.test",
|
||||
"media_player": "media_player.demo",
|
||||
}
|
||||
}
|
||||
with assert_setup_component(1, notify.DOMAIN):
|
||||
assert await async_setup_component(hass, notify.DOMAIN, config)
|
||||
|
||||
assert hass.services.has_service(notify.DOMAIN, "tts_test")
|
||||
|
||||
|
||||
async def test_setup_legacy_service(hass: HomeAssistant) -> None:
|
||||
"""Set up the demo platform and call service."""
|
||||
calls = async_mock_service(hass, DOMAIN_MP, SERVICE_PLAY_MEDIA)
|
||||
|
||||
@@ -80,3 +90,38 @@ async def test_setup_component_and_test_service(hass: HomeAssistant) -> None:
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert len(calls) == 1
|
||||
|
||||
|
||||
async def test_setup_service(
|
||||
hass: HomeAssistant, mock_tts_entity: MockTTSEntity
|
||||
) -> None:
|
||||
"""Set up platform and call service."""
|
||||
calls = async_mock_service(hass, DOMAIN_MP, SERVICE_PLAY_MEDIA)
|
||||
|
||||
config = {
|
||||
notify.DOMAIN: {
|
||||
"platform": "tts",
|
||||
"name": "tts_test",
|
||||
"entity_id": "tts.test",
|
||||
"media_player": "media_player.demo",
|
||||
"language": "en",
|
||||
},
|
||||
}
|
||||
|
||||
await mock_config_entry_setup(hass, mock_tts_entity)
|
||||
|
||||
with assert_setup_component(1, notify.DOMAIN):
|
||||
assert await async_setup_component(hass, notify.DOMAIN, config)
|
||||
|
||||
await hass.services.async_call(
|
||||
notify.DOMAIN,
|
||||
"tts_test",
|
||||
{
|
||||
tts.ATTR_MESSAGE: "There is someone at the door.",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert len(calls) == 1
|
||||
|
||||
Reference in New Issue
Block a user