Cleanup coroutine threadsafe (#27080)

* Cleanup coroutine threadsafe

* fix lint

* Fix typing

* Fix tests

* Fix black
This commit is contained in:
Pascal Vizeli 2019-10-01 16:59:06 +02:00 committed by GitHub
parent f4a1f2809b
commit c1851a2d94
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 196 additions and 232 deletions

View file

@ -1,4 +1,5 @@
"""Tracking for bluetooth low energy devices.""" """Tracking for bluetooth low energy devices."""
import asyncio
import logging import logging
from homeassistant.helpers.event import track_point_in_utc_time from homeassistant.helpers.event import track_point_in_utc_time
@ -14,7 +15,6 @@ from homeassistant.components.device_tracker.const import (
) )
from homeassistant.const import EVENT_HOMEASSISTANT_STOP from homeassistant.const import EVENT_HOMEASSISTANT_STOP
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
from homeassistant.util.async_ import run_coroutine_threadsafe
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -89,7 +89,7 @@ def setup_scanner(hass, config, see, discovery_info=None):
# Load all known devices. # Load all known devices.
# We just need the devices so set consider_home and home range # We just need the devices so set consider_home and home range
# to 0 # to 0
for device in run_coroutine_threadsafe( for device in asyncio.run_coroutine_threadsafe(
async_load_config(yaml_path, hass, 0), hass.loop async_load_config(yaml_path, hass, 0), hass.loop
).result(): ).result():
# check if device is a valid bluetooth device # check if device is a valid bluetooth device

View file

@ -26,7 +26,6 @@ from homeassistant.components.camera import (
) )
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers import config_validation as cv from homeassistant.helpers import config_validation as cv
from homeassistant.util.async_ import run_coroutine_threadsafe
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -105,7 +104,7 @@ class GenericCamera(Camera):
def camera_image(self): def camera_image(self):
"""Return bytes of camera image.""" """Return bytes of camera image."""
return run_coroutine_threadsafe( return asyncio.run_coroutine_threadsafe(
self.async_camera_image(), self.hass.loop self.async_camera_image(), self.hass.loop
).result() ).result()

View file

@ -34,7 +34,6 @@ from homeassistant.helpers.event import async_track_state_change
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.config_validation import ENTITY_SERVICE_SCHEMA from homeassistant.helpers.config_validation import ENTITY_SERVICE_SCHEMA
from homeassistant.helpers.typing import HomeAssistantType from homeassistant.helpers.typing import HomeAssistantType
from homeassistant.util.async_ import run_coroutine_threadsafe
# mypy: allow-untyped-calls, allow-untyped-defs # mypy: allow-untyped-calls, allow-untyped-defs
@ -430,7 +429,7 @@ class Group(Entity):
mode=None, mode=None,
): ):
"""Initialize a group.""" """Initialize a group."""
return run_coroutine_threadsafe( return asyncio.run_coroutine_threadsafe(
Group.async_create_group( Group.async_create_group(
hass, hass,
name, name,
@ -546,7 +545,7 @@ class Group(Entity):
def update_tracked_entity_ids(self, entity_ids): def update_tracked_entity_ids(self, entity_ids):
"""Update the member entity IDs.""" """Update the member entity IDs."""
run_coroutine_threadsafe( asyncio.run_coroutine_threadsafe(
self.async_update_tracked_entity_ids(entity_ids), self.hass.loop self.async_update_tracked_entity_ids(entity_ids), self.hass.loop
).result() ).result()

View file

@ -39,7 +39,7 @@ from homeassistant.helpers import config_validation as cv, template
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
from homeassistant.helpers.typing import ConfigType, HomeAssistantType, ServiceDataType from homeassistant.helpers.typing import ConfigType, HomeAssistantType, ServiceDataType
from homeassistant.loader import bind_hass from homeassistant.loader import bind_hass
from homeassistant.util.async_ import run_callback_threadsafe, run_coroutine_threadsafe from homeassistant.util.async_ import run_callback_threadsafe
from homeassistant.util.logging import catch_log_exception from homeassistant.util.logging import catch_log_exception
# Loading the config flow file will register the flow # Loading the config flow file will register the flow
@ -463,7 +463,7 @@ def subscribe(
encoding: str = "utf-8", encoding: str = "utf-8",
) -> Callable[[], None]: ) -> Callable[[], None]:
"""Subscribe to an MQTT topic.""" """Subscribe to an MQTT topic."""
async_remove = run_coroutine_threadsafe( async_remove = asyncio.run_coroutine_threadsafe(
async_subscribe(hass, topic, msg_callback, qos, encoding), hass.loop async_subscribe(hass, topic, msg_callback, qos, encoding), hass.loop
).result() ).result()

View file

@ -9,7 +9,6 @@ from homeassistant.components.camera import PLATFORM_SCHEMA, Camera
from homeassistant.const import CONF_ENTITY_ID, CONF_NAME, CONF_MODE from homeassistant.const import CONF_ENTITY_ID, CONF_NAME, CONF_MODE
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import config_validation as cv from homeassistant.helpers import config_validation as cv
from homeassistant.util.async_ import run_coroutine_threadsafe
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -220,7 +219,7 @@ class ProxyCamera(Camera):
def camera_image(self): def camera_image(self):
"""Return camera image.""" """Return camera image."""
return run_coroutine_threadsafe( return asyncio.run_coroutine_threadsafe(
self.async_camera_image(), self.hass.loop self.async_camera_image(), self.hass.loop
).result() ).result()

View file

@ -64,11 +64,7 @@ from homeassistant.exceptions import (
Unauthorized, Unauthorized,
ServiceNotFound, ServiceNotFound,
) )
from homeassistant.util.async_ import ( from homeassistant.util.async_ import run_callback_threadsafe, fire_coroutine_threadsafe
run_coroutine_threadsafe,
run_callback_threadsafe,
fire_coroutine_threadsafe,
)
from homeassistant import util from homeassistant import util
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
from homeassistant.util import location, slugify from homeassistant.util import location, slugify
@ -375,7 +371,9 @@ class HomeAssistant:
def block_till_done(self) -> None: def block_till_done(self) -> None:
"""Block till all pending work is done.""" """Block till all pending work is done."""
run_coroutine_threadsafe(self.async_block_till_done(), self.loop).result() asyncio.run_coroutine_threadsafe(
self.async_block_till_done(), self.loop
).result()
async def async_block_till_done(self) -> None: async def async_block_till_done(self) -> None:
"""Block till all pending work is done.""" """Block till all pending work is done."""
@ -1168,7 +1166,7 @@ class ServiceRegistry:
Because the service is sent as an event you are not allowed to use Because the service is sent as an event you are not allowed to use
the keys ATTR_DOMAIN and ATTR_SERVICE in your service_data. the keys ATTR_DOMAIN and ATTR_SERVICE in your service_data.
""" """
return run_coroutine_threadsafe( # type: ignore return asyncio.run_coroutine_threadsafe(
self.async_call(domain, service, service_data, blocking, context), self.async_call(domain, service, service_data, blocking, context),
self._hass.loop, self._hass.loop,
).result() ).result()

View file

@ -6,7 +6,7 @@ from typing import Optional
from homeassistant.const import DEVICE_DEFAULT_NAME from homeassistant.const import DEVICE_DEFAULT_NAME
from homeassistant.core import callback, valid_entity_id, split_entity_id from homeassistant.core import callback, valid_entity_id, split_entity_id
from homeassistant.exceptions import HomeAssistantError, PlatformNotReady from homeassistant.exceptions import HomeAssistantError, PlatformNotReady
from homeassistant.util.async_ import run_callback_threadsafe, run_coroutine_threadsafe from homeassistant.util.async_ import run_callback_threadsafe
from .entity_registry import DISABLED_INTEGRATION from .entity_registry import DISABLED_INTEGRATION
from .event import async_track_time_interval, async_call_later from .event import async_track_time_interval, async_call_later
@ -220,7 +220,7 @@ class EntityPlatform:
"only inside tests or you can run into a deadlock!" "only inside tests or you can run into a deadlock!"
) )
run_coroutine_threadsafe( asyncio.run_coroutine_threadsafe(
self.async_add_entities(list(new_entities), update_before_add), self.async_add_entities(list(new_entities), update_before_add),
self.hass.loop, self.hass.loop,
).result() ).result()

View file

@ -1,5 +1,5 @@
"""Helpers to execute scripts.""" """Helpers to execute scripts."""
import asyncio
import logging import logging
from contextlib import suppress from contextlib import suppress
from datetime import datetime from datetime import datetime
@ -29,7 +29,7 @@ from homeassistant.helpers.event import (
from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.typing import ConfigType
from homeassistant.loader import async_get_integration from homeassistant.loader import async_get_integration
import homeassistant.util.dt as date_util import homeassistant.util.dt as date_util
from homeassistant.util.async_ import run_coroutine_threadsafe, run_callback_threadsafe from homeassistant.util.async_ import run_callback_threadsafe
# mypy: allow-untyped-calls, allow-untyped-defs, no-check-untyped-defs # mypy: allow-untyped-calls, allow-untyped-defs, no-check-untyped-defs
@ -136,7 +136,7 @@ class Script:
def run(self, variables=None, context=None): def run(self, variables=None, context=None):
"""Run script.""" """Run script."""
run_coroutine_threadsafe( asyncio.run_coroutine_threadsafe(
self.async_run(variables, context), self.hass.loop self.async_run(variables, context), self.hass.loop
).result() ).result()

View file

@ -20,7 +20,6 @@ from homeassistant.loader import async_get_integration, bind_hass
from homeassistant.util.yaml import load_yaml from homeassistant.util.yaml import load_yaml
from homeassistant.util.yaml.loader import JSON_TYPE from homeassistant.util.yaml.loader import JSON_TYPE
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.util.async_ import run_coroutine_threadsafe
from homeassistant.helpers.typing import HomeAssistantType from homeassistant.helpers.typing import HomeAssistantType
@ -42,7 +41,7 @@ def call_from_config(
hass, config, blocking=False, variables=None, validate_config=True hass, config, blocking=False, variables=None, validate_config=True
): ):
"""Call a service based on a config hash.""" """Call a service based on a config hash."""
run_coroutine_threadsafe( asyncio.run_coroutine_threadsafe(
async_call_from_config(hass, config, blocking, variables, validate_config), async_call_from_config(hass, config, blocking, variables, validate_config),
hass.loop, hass.loop,
).result() ).result()
@ -105,7 +104,7 @@ def extract_entity_ids(hass, service_call, expand_group=True):
Will convert group entity ids to the entity ids it represents. Will convert group entity ids to the entity ids it represents.
""" """
return run_coroutine_threadsafe( return asyncio.run_coroutine_threadsafe(
async_extract_entity_ids(hass, service_call, expand_group), hass.loop async_extract_entity_ids(hass, service_call, expand_group), hass.loop
).result() ).result()

View file

@ -44,7 +44,6 @@ from homeassistant.const import (
SERVICE_SELECT_OPTION, SERVICE_SELECT_OPTION,
) )
from homeassistant.core import Context, State, DOMAIN as HASS_DOMAIN from homeassistant.core import Context, State, DOMAIN as HASS_DOMAIN
from homeassistant.util.async_ import run_coroutine_threadsafe
from .typing import HomeAssistantType from .typing import HomeAssistantType
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -122,7 +121,7 @@ def reproduce_state(
blocking: bool = False, blocking: bool = False,
) -> None: ) -> None:
"""Reproduce given state.""" """Reproduce given state."""
return run_coroutine_threadsafe( # type: ignore return asyncio.run_coroutine_threadsafe(
async_reproduce_state(hass, states, blocking), hass.loop async_reproduce_state(hass, states, blocking), hass.loop
).result() ).result()

View file

@ -10,7 +10,6 @@ from homeassistant import requirements, core, loader, config as conf_util
from homeassistant.config import async_notify_setup_error from homeassistant.config import async_notify_setup_error
from homeassistant.const import EVENT_COMPONENT_LOADED, PLATFORM_FORMAT from homeassistant.const import EVENT_COMPONENT_LOADED, PLATFORM_FORMAT
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.util.async_ import run_coroutine_threadsafe
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -25,7 +24,7 @@ SLOW_SETUP_WARNING = 10
def setup_component(hass: core.HomeAssistant, domain: str, config: Dict) -> bool: def setup_component(hass: core.HomeAssistant, domain: str, config: Dict) -> bool:
"""Set up a component and all its dependencies.""" """Set up a component and all its dependencies."""
return run_coroutine_threadsafe( # type: ignore return asyncio.run_coroutine_threadsafe(
async_setup_component(hass, domain, config), hass.loop async_setup_component(hass, domain, config), hass.loop
).result() ).result()

View file

@ -7,7 +7,7 @@ from asyncio.events import AbstractEventLoop
import asyncio import asyncio
from asyncio import ensure_future from asyncio import ensure_future
from typing import Any, Union, Coroutine, Callable, Generator, TypeVar, Awaitable from typing import Any, Coroutine, Callable, TypeVar, Awaitable
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -30,20 +30,6 @@ except AttributeError:
loop.close() loop.close()
def run_coroutine_threadsafe(
coro: Union[Coroutine, Generator], loop: AbstractEventLoop
) -> concurrent.futures.Future:
"""Submit a coroutine object to a given event loop.
Return a concurrent.futures.Future to access the result.
"""
ident = loop.__dict__.get("_thread_ident")
if ident is not None and ident == threading.get_ident():
raise RuntimeError("Cannot be called from within the event loop")
return asyncio.run_coroutine_threadsafe(coro, loop)
def fire_coroutine_threadsafe(coro: Coroutine, loop: AbstractEventLoop) -> None: def fire_coroutine_threadsafe(coro: Coroutine, loop: AbstractEventLoop) -> None:
"""Submit a coroutine object to a given event loop. """Submit a coroutine object to a given event loop.

View file

@ -8,8 +8,6 @@ import threading
import traceback import traceback
from typing import Any, Callable, Coroutine, Optional from typing import Any, Callable, Coroutine, Optional
from .async_ import run_coroutine_threadsafe
class HideSensitiveDataFilter(logging.Filter): class HideSensitiveDataFilter(logging.Filter):
"""Filter API password calls.""" """Filter API password calls."""
@ -83,7 +81,9 @@ class AsyncHandler:
def _process(self) -> None: def _process(self) -> None:
"""Process log in a thread.""" """Process log in a thread."""
while True: while True:
record = run_coroutine_threadsafe(self._queue.get(), self.loop).result() record = asyncio.run_coroutine_threadsafe(
self._queue.get(), self.loop
).result()
if record is None: if record is None:
self.handler.close() self.handler.close()

View file

@ -53,7 +53,7 @@ from homeassistant.helpers import (
from homeassistant.helpers.json import JSONEncoder from homeassistant.helpers.json import JSONEncoder
from homeassistant.setup import async_setup_component, setup_component from homeassistant.setup import async_setup_component, setup_component
from homeassistant.util.unit_system import METRIC_SYSTEM from homeassistant.util.unit_system import METRIC_SYSTEM
from homeassistant.util.async_ import run_callback_threadsafe, run_coroutine_threadsafe from homeassistant.util.async_ import run_callback_threadsafe
from homeassistant.components.device_automation import ( # noqa from homeassistant.components.device_automation import ( # noqa
_async_get_device_automations as async_get_device_automations, _async_get_device_automations as async_get_device_automations,
) )
@ -92,7 +92,9 @@ def threadsafe_coroutine_factory(func):
def threadsafe(*args, **kwargs): def threadsafe(*args, **kwargs):
"""Call func threadsafe.""" """Call func threadsafe."""
hass = args[0] hass = args[0]
return run_coroutine_threadsafe(func(*args, **kwargs), hass.loop).result() return asyncio.run_coroutine_threadsafe(
func(*args, **kwargs), hass.loop
).result()
return threadsafe return threadsafe
@ -125,7 +127,7 @@ def get_test_home_assistant():
def start_hass(*mocks): def start_hass(*mocks):
"""Start hass.""" """Start hass."""
run_coroutine_threadsafe(hass.async_start(), loop).result() asyncio.run_coroutine_threadsafe(hass.async_start(), loop).result()
def stop_hass(): def stop_hass():
"""Stop hass.""" """Stop hass."""

View file

@ -17,7 +17,6 @@ from homeassistant.components.camera.const import DOMAIN, PREF_PRELOAD_STREAM
from homeassistant.components.camera.prefs import CameraEntityPreferences from homeassistant.components.camera.prefs import CameraEntityPreferences
from homeassistant.components.websocket_api.const import TYPE_RESULT from homeassistant.components.websocket_api.const import TYPE_RESULT
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.util.async_ import run_coroutine_threadsafe
from tests.common import ( from tests.common import (
get_test_home_assistant, get_test_home_assistant,
@ -110,7 +109,7 @@ class TestGetImage:
"""Grab an image from camera entity.""" """Grab an image from camera entity."""
self.hass.start() self.hass.start()
image = run_coroutine_threadsafe( image = asyncio.run_coroutine_threadsafe(
camera.async_get_image(self.hass, "camera.demo_camera"), self.hass.loop camera.async_get_image(self.hass, "camera.demo_camera"), self.hass.loop
).result() ).result()
@ -123,7 +122,7 @@ class TestGetImage:
"homeassistant.helpers.entity_component.EntityComponent." "get_entity", "homeassistant.helpers.entity_component.EntityComponent." "get_entity",
return_value=None, return_value=None,
), pytest.raises(HomeAssistantError): ), pytest.raises(HomeAssistantError):
run_coroutine_threadsafe( asyncio.run_coroutine_threadsafe(
camera.async_get_image(self.hass, "camera.demo_camera"), self.hass.loop camera.async_get_image(self.hass, "camera.demo_camera"), self.hass.loop
).result() ).result()
@ -133,7 +132,7 @@ class TestGetImage:
"homeassistant.components.camera.Camera.async_camera_image", "homeassistant.components.camera.Camera.async_camera_image",
side_effect=asyncio.TimeoutError, side_effect=asyncio.TimeoutError,
), pytest.raises(HomeAssistantError): ), pytest.raises(HomeAssistantError):
run_coroutine_threadsafe( asyncio.run_coroutine_threadsafe(
camera.async_get_image(self.hass, "camera.demo_camera"), self.hass.loop camera.async_get_image(self.hass, "camera.demo_camera"), self.hass.loop
).result() ).result()
@ -143,7 +142,7 @@ class TestGetImage:
"homeassistant.components.camera.Camera.async_camera_image", "homeassistant.components.camera.Camera.async_camera_image",
return_value=mock_coro(None), return_value=mock_coro(None),
), pytest.raises(HomeAssistantError): ), pytest.raises(HomeAssistantError):
run_coroutine_threadsafe( asyncio.run_coroutine_threadsafe(
camera.async_get_image(self.hass, "camera.demo_camera"), self.hass.loop camera.async_get_image(self.hass, "camera.demo_camera"), self.hass.loop
).result() ).result()

View file

@ -1,4 +1,5 @@
"""The tests for the notify.group platform.""" """The tests for the notify.group platform."""
import asyncio
import unittest import unittest
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
@ -6,7 +7,6 @@ from homeassistant.setup import setup_component
import homeassistant.components.notify as notify import homeassistant.components.notify as notify
import homeassistant.components.group.notify as group import homeassistant.components.group.notify as group
import homeassistant.components.demo.notify as demo import homeassistant.components.demo.notify as demo
from homeassistant.util.async_ import run_coroutine_threadsafe
from tests.common import assert_setup_component, get_test_home_assistant from tests.common import assert_setup_component, get_test_home_assistant
@ -43,7 +43,7 @@ class TestNotifyGroup(unittest.TestCase):
}, },
) )
self.service = run_coroutine_threadsafe( self.service = asyncio.run_coroutine_threadsafe(
group.async_get_service( group.async_get_service(
self.hass, self.hass,
{ {
@ -70,7 +70,7 @@ class TestNotifyGroup(unittest.TestCase):
def test_send_message_with_data(self): def test_send_message_with_data(self):
"""Test sending a message with to a notify group.""" """Test sending a message with to a notify group."""
run_coroutine_threadsafe( asyncio.run_coroutine_threadsafe(
self.service.async_send_message( self.service.async_send_message(
"Hello", title="Test notification", data={"hello": "world"} "Hello", title="Test notification", data={"hello": "world"}
), ),

View file

@ -1,5 +1,6 @@
"""The tests for Core components.""" """The tests for Core components."""
# pylint: disable=protected-access # pylint: disable=protected-access
import asyncio
import unittest import unittest
from unittest.mock import patch, Mock from unittest.mock import patch, Mock
@ -27,7 +28,6 @@ from homeassistant.components.homeassistant import (
import homeassistant.helpers.intent as intent import homeassistant.helpers.intent as intent
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity from homeassistant.helpers import entity
from homeassistant.util.async_ import run_coroutine_threadsafe
from tests.common import ( from tests.common import (
get_test_home_assistant, get_test_home_assistant,
@ -111,7 +111,7 @@ class TestComponentsCore(unittest.TestCase):
def setUp(self): def setUp(self):
"""Set up things to be run when tests are started.""" """Set up things to be run when tests are started."""
self.hass = get_test_home_assistant() self.hass = get_test_home_assistant()
assert run_coroutine_threadsafe( assert asyncio.run_coroutine_threadsafe(
async_setup_component(self.hass, "homeassistant", {}), self.hass.loop async_setup_component(self.hass, "homeassistant", {}), self.hass.loop
).result() ).result()

View file

@ -10,7 +10,6 @@ from homeassistant.const import (
STATE_OFF, STATE_OFF,
STATE_IDLE, STATE_IDLE,
) )
from homeassistant.util.async_ import run_coroutine_threadsafe
from tests.common import get_test_home_assistant from tests.common import get_test_home_assistant
@ -162,21 +161,23 @@ class TestAsyncMediaPlayer(unittest.TestCase):
def test_volume_up(self): def test_volume_up(self):
"""Test the volume_up helper function.""" """Test the volume_up helper function."""
assert self.player.volume_level == 0 assert self.player.volume_level == 0
run_coroutine_threadsafe( asyncio.run_coroutine_threadsafe(
self.player.async_set_volume_level(0.5), self.hass.loop self.player.async_set_volume_level(0.5), self.hass.loop
).result() ).result()
assert self.player.volume_level == 0.5 assert self.player.volume_level == 0.5
run_coroutine_threadsafe(self.player.async_volume_up(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(
self.player.async_volume_up(), self.hass.loop
).result()
assert self.player.volume_level == 0.6 assert self.player.volume_level == 0.6
def test_volume_down(self): def test_volume_down(self):
"""Test the volume_down helper function.""" """Test the volume_down helper function."""
assert self.player.volume_level == 0 assert self.player.volume_level == 0
run_coroutine_threadsafe( asyncio.run_coroutine_threadsafe(
self.player.async_set_volume_level(0.5), self.hass.loop self.player.async_set_volume_level(0.5), self.hass.loop
).result() ).result()
assert self.player.volume_level == 0.5 assert self.player.volume_level == 0.5
run_coroutine_threadsafe( asyncio.run_coroutine_threadsafe(
self.player.async_volume_down(), self.hass.loop self.player.async_volume_down(), self.hass.loop
).result() ).result()
assert self.player.volume_level == 0.4 assert self.player.volume_level == 0.4
@ -184,11 +185,11 @@ class TestAsyncMediaPlayer(unittest.TestCase):
def test_media_play_pause(self): def test_media_play_pause(self):
"""Test the media_play_pause helper function.""" """Test the media_play_pause helper function."""
assert self.player.state == STATE_OFF assert self.player.state == STATE_OFF
run_coroutine_threadsafe( asyncio.run_coroutine_threadsafe(
self.player.async_media_play_pause(), self.hass.loop self.player.async_media_play_pause(), self.hass.loop
).result() ).result()
assert self.player.state == STATE_PLAYING assert self.player.state == STATE_PLAYING
run_coroutine_threadsafe( asyncio.run_coroutine_threadsafe(
self.player.async_media_play_pause(), self.hass.loop self.player.async_media_play_pause(), self.hass.loop
).result() ).result()
assert self.player.state == STATE_PAUSED assert self.player.state == STATE_PAUSED
@ -196,9 +197,13 @@ class TestAsyncMediaPlayer(unittest.TestCase):
def test_toggle(self): def test_toggle(self):
"""Test the toggle helper function.""" """Test the toggle helper function."""
assert self.player.state == STATE_OFF assert self.player.state == STATE_OFF
run_coroutine_threadsafe(self.player.async_toggle(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(
self.player.async_toggle(), self.hass.loop
).result()
assert self.player.state == STATE_ON assert self.player.state == STATE_ON
run_coroutine_threadsafe(self.player.async_toggle(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(
self.player.async_toggle(), self.hass.loop
).result()
assert self.player.state == STATE_OFF assert self.player.state == STATE_OFF
@ -219,7 +224,9 @@ class TestSyncMediaPlayer(unittest.TestCase):
assert self.player.volume_level == 0 assert self.player.volume_level == 0
self.player.set_volume_level(0.5) self.player.set_volume_level(0.5)
assert self.player.volume_level == 0.5 assert self.player.volume_level == 0.5
run_coroutine_threadsafe(self.player.async_volume_up(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(
self.player.async_volume_up(), self.hass.loop
).result()
assert self.player.volume_level == 0.7 assert self.player.volume_level == 0.7
def test_volume_down(self): def test_volume_down(self):
@ -227,7 +234,7 @@ class TestSyncMediaPlayer(unittest.TestCase):
assert self.player.volume_level == 0 assert self.player.volume_level == 0
self.player.set_volume_level(0.5) self.player.set_volume_level(0.5)
assert self.player.volume_level == 0.5 assert self.player.volume_level == 0.5
run_coroutine_threadsafe( asyncio.run_coroutine_threadsafe(
self.player.async_volume_down(), self.hass.loop self.player.async_volume_down(), self.hass.loop
).result() ).result()
assert self.player.volume_level == 0.3 assert self.player.volume_level == 0.3
@ -235,11 +242,11 @@ class TestSyncMediaPlayer(unittest.TestCase):
def test_media_play_pause(self): def test_media_play_pause(self):
"""Test the media_play_pause helper function.""" """Test the media_play_pause helper function."""
assert self.player.state == STATE_OFF assert self.player.state == STATE_OFF
run_coroutine_threadsafe( asyncio.run_coroutine_threadsafe(
self.player.async_media_play_pause(), self.hass.loop self.player.async_media_play_pause(), self.hass.loop
).result() ).result()
assert self.player.state == STATE_PLAYING assert self.player.state == STATE_PLAYING
run_coroutine_threadsafe( asyncio.run_coroutine_threadsafe(
self.player.async_media_play_pause(), self.hass.loop self.player.async_media_play_pause(), self.hass.loop
).result() ).result()
assert self.player.state == STATE_PAUSED assert self.player.state == STATE_PAUSED
@ -247,7 +254,11 @@ class TestSyncMediaPlayer(unittest.TestCase):
def test_toggle(self): def test_toggle(self):
"""Test the toggle helper function.""" """Test the toggle helper function."""
assert self.player.state == STATE_OFF assert self.player.state == STATE_OFF
run_coroutine_threadsafe(self.player.async_toggle(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(
self.player.async_toggle(), self.hass.loop
).result()
assert self.player.state == STATE_ON assert self.player.state == STATE_ON
run_coroutine_threadsafe(self.player.async_toggle(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(
self.player.async_toggle(), self.hass.loop
).result()
assert self.player.state == STATE_OFF assert self.player.state == STATE_OFF

View file

@ -5,7 +5,6 @@ import aiohttp
import homeassistant.components.rest.switch as rest import homeassistant.components.rest.switch as rest
from homeassistant.setup import setup_component from homeassistant.setup import setup_component
from homeassistant.util.async_ import run_coroutine_threadsafe
from homeassistant.helpers.template import Template from homeassistant.helpers.template import Template
from tests.common import get_test_home_assistant, assert_setup_component from tests.common import get_test_home_assistant, assert_setup_component
@ -23,14 +22,14 @@ class TestRestSwitchSetup:
def test_setup_missing_config(self): def test_setup_missing_config(self):
"""Test setup with configuration missing required entries.""" """Test setup with configuration missing required entries."""
assert not run_coroutine_threadsafe( assert not asyncio.run_coroutine_threadsafe(
rest.async_setup_platform(self.hass, {"platform": "rest"}, None), rest.async_setup_platform(self.hass, {"platform": "rest"}, None),
self.hass.loop, self.hass.loop,
).result() ).result()
def test_setup_missing_schema(self): def test_setup_missing_schema(self):
"""Test setup with resource missing schema.""" """Test setup with resource missing schema."""
assert not run_coroutine_threadsafe( assert not asyncio.run_coroutine_threadsafe(
rest.async_setup_platform( rest.async_setup_platform(
self.hass, {"platform": "rest", "resource": "localhost"}, None self.hass, {"platform": "rest", "resource": "localhost"}, None
), ),
@ -40,7 +39,7 @@ class TestRestSwitchSetup:
def test_setup_failed_connect(self, aioclient_mock): def test_setup_failed_connect(self, aioclient_mock):
"""Test setup when connection error occurs.""" """Test setup when connection error occurs."""
aioclient_mock.get("http://localhost", exc=aiohttp.ClientError) aioclient_mock.get("http://localhost", exc=aiohttp.ClientError)
assert not run_coroutine_threadsafe( assert not asyncio.run_coroutine_threadsafe(
rest.async_setup_platform( rest.async_setup_platform(
self.hass, {"platform": "rest", "resource": "http://localhost"}, None self.hass, {"platform": "rest", "resource": "http://localhost"}, None
), ),
@ -50,7 +49,7 @@ class TestRestSwitchSetup:
def test_setup_timeout(self, aioclient_mock): def test_setup_timeout(self, aioclient_mock):
"""Test setup when connection timeout occurs.""" """Test setup when connection timeout occurs."""
aioclient_mock.get("http://localhost", exc=asyncio.TimeoutError()) aioclient_mock.get("http://localhost", exc=asyncio.TimeoutError())
assert not run_coroutine_threadsafe( assert not asyncio.run_coroutine_threadsafe(
rest.async_setup_platform( rest.async_setup_platform(
self.hass, {"platform": "rest", "resource": "http://localhost"}, None self.hass, {"platform": "rest", "resource": "http://localhost"}, None
), ),
@ -131,7 +130,9 @@ class TestRestSwitch:
def test_turn_on_success(self, aioclient_mock): def test_turn_on_success(self, aioclient_mock):
"""Test turn_on.""" """Test turn_on."""
aioclient_mock.post(self.resource, status=200) aioclient_mock.post(self.resource, status=200)
run_coroutine_threadsafe(self.switch.async_turn_on(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(
self.switch.async_turn_on(), self.hass.loop
).result()
assert self.body_on.template == aioclient_mock.mock_calls[-1][2].decode() assert self.body_on.template == aioclient_mock.mock_calls[-1][2].decode()
assert self.switch.is_on assert self.switch.is_on
@ -139,7 +140,9 @@ class TestRestSwitch:
def test_turn_on_status_not_ok(self, aioclient_mock): def test_turn_on_status_not_ok(self, aioclient_mock):
"""Test turn_on when error status returned.""" """Test turn_on when error status returned."""
aioclient_mock.post(self.resource, status=500) aioclient_mock.post(self.resource, status=500)
run_coroutine_threadsafe(self.switch.async_turn_on(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(
self.switch.async_turn_on(), self.hass.loop
).result()
assert self.body_on.template == aioclient_mock.mock_calls[-1][2].decode() assert self.body_on.template == aioclient_mock.mock_calls[-1][2].decode()
assert self.switch.is_on is None assert self.switch.is_on is None
@ -147,14 +150,18 @@ class TestRestSwitch:
def test_turn_on_timeout(self, aioclient_mock): def test_turn_on_timeout(self, aioclient_mock):
"""Test turn_on when timeout occurs.""" """Test turn_on when timeout occurs."""
aioclient_mock.post(self.resource, status=500) aioclient_mock.post(self.resource, status=500)
run_coroutine_threadsafe(self.switch.async_turn_on(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(
self.switch.async_turn_on(), self.hass.loop
).result()
assert self.switch.is_on is None assert self.switch.is_on is None
def test_turn_off_success(self, aioclient_mock): def test_turn_off_success(self, aioclient_mock):
"""Test turn_off.""" """Test turn_off."""
aioclient_mock.post(self.resource, status=200) aioclient_mock.post(self.resource, status=200)
run_coroutine_threadsafe(self.switch.async_turn_off(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(
self.switch.async_turn_off(), self.hass.loop
).result()
assert self.body_off.template == aioclient_mock.mock_calls[-1][2].decode() assert self.body_off.template == aioclient_mock.mock_calls[-1][2].decode()
assert not self.switch.is_on assert not self.switch.is_on
@ -162,7 +169,9 @@ class TestRestSwitch:
def test_turn_off_status_not_ok(self, aioclient_mock): def test_turn_off_status_not_ok(self, aioclient_mock):
"""Test turn_off when error status returned.""" """Test turn_off when error status returned."""
aioclient_mock.post(self.resource, status=500) aioclient_mock.post(self.resource, status=500)
run_coroutine_threadsafe(self.switch.async_turn_off(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(
self.switch.async_turn_off(), self.hass.loop
).result()
assert self.body_off.template == aioclient_mock.mock_calls[-1][2].decode() assert self.body_off.template == aioclient_mock.mock_calls[-1][2].decode()
assert self.switch.is_on is None assert self.switch.is_on is None
@ -170,34 +179,44 @@ class TestRestSwitch:
def test_turn_off_timeout(self, aioclient_mock): def test_turn_off_timeout(self, aioclient_mock):
"""Test turn_off when timeout occurs.""" """Test turn_off when timeout occurs."""
aioclient_mock.post(self.resource, exc=asyncio.TimeoutError()) aioclient_mock.post(self.resource, exc=asyncio.TimeoutError())
run_coroutine_threadsafe(self.switch.async_turn_on(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(
self.switch.async_turn_on(), self.hass.loop
).result()
assert self.switch.is_on is None assert self.switch.is_on is None
def test_update_when_on(self, aioclient_mock): def test_update_when_on(self, aioclient_mock):
"""Test update when switch is on.""" """Test update when switch is on."""
aioclient_mock.get(self.resource, text=self.body_on.template) aioclient_mock.get(self.resource, text=self.body_on.template)
run_coroutine_threadsafe(self.switch.async_update(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(
self.switch.async_update(), self.hass.loop
).result()
assert self.switch.is_on assert self.switch.is_on
def test_update_when_off(self, aioclient_mock): def test_update_when_off(self, aioclient_mock):
"""Test update when switch is off.""" """Test update when switch is off."""
aioclient_mock.get(self.resource, text=self.body_off.template) aioclient_mock.get(self.resource, text=self.body_off.template)
run_coroutine_threadsafe(self.switch.async_update(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(
self.switch.async_update(), self.hass.loop
).result()
assert not self.switch.is_on assert not self.switch.is_on
def test_update_when_unknown(self, aioclient_mock): def test_update_when_unknown(self, aioclient_mock):
"""Test update when unknown status returned.""" """Test update when unknown status returned."""
aioclient_mock.get(self.resource, text="unknown status") aioclient_mock.get(self.resource, text="unknown status")
run_coroutine_threadsafe(self.switch.async_update(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(
self.switch.async_update(), self.hass.loop
).result()
assert self.switch.is_on is None assert self.switch.is_on is None
def test_update_timeout(self, aioclient_mock): def test_update_timeout(self, aioclient_mock):
"""Test update when timeout occurs.""" """Test update when timeout occurs."""
aioclient_mock.get(self.resource, exc=asyncio.TimeoutError()) aioclient_mock.get(self.resource, exc=asyncio.TimeoutError())
run_coroutine_threadsafe(self.switch.async_update(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(
self.switch.async_update(), self.hass.loop
).result()
assert self.switch.is_on is None assert self.switch.is_on is None

View file

@ -1,4 +1,5 @@
"""The tests for the Universal Media player platform.""" """The tests for the Universal Media player platform."""
import asyncio
from copy import copy from copy import copy
import unittest import unittest
@ -10,7 +11,6 @@ import homeassistant.components.input_number as input_number
import homeassistant.components.input_select as input_select import homeassistant.components.input_select as input_select
import homeassistant.components.media_player as media_player import homeassistant.components.media_player as media_player
import homeassistant.components.universal.media_player as universal import homeassistant.components.universal.media_player as universal
from homeassistant.util.async_ import run_coroutine_threadsafe
from tests.common import mock_service, get_test_home_assistant from tests.common import mock_service, get_test_home_assistant
@ -298,7 +298,7 @@ class TestMediaPlayer(unittest.TestCase):
setup_ok = True setup_ok = True
try: try:
run_coroutine_threadsafe( asyncio.run_coroutine_threadsafe(
universal.async_setup_platform( universal.async_setup_platform(
self.hass, validate_config(bad_config), add_entities self.hass, validate_config(bad_config), add_entities
), ),
@ -309,7 +309,7 @@ class TestMediaPlayer(unittest.TestCase):
assert not setup_ok assert not setup_ok
assert 0 == len(entities) assert 0 == len(entities)
run_coroutine_threadsafe( asyncio.run_coroutine_threadsafe(
universal.async_setup_platform( universal.async_setup_platform(
self.hass, validate_config(config), add_entities self.hass, validate_config(config), add_entities
), ),
@ -369,26 +369,26 @@ class TestMediaPlayer(unittest.TestCase):
ump = universal.UniversalMediaPlayer(self.hass, **config) ump = universal.UniversalMediaPlayer(self.hass, **config)
ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"]) ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"])
run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
assert ump._child_state is None assert ump._child_state is None
self.mock_mp_1._state = STATE_PLAYING self.mock_mp_1._state = STATE_PLAYING
self.mock_mp_1.schedule_update_ha_state() self.mock_mp_1.schedule_update_ha_state()
self.hass.block_till_done() self.hass.block_till_done()
run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
assert self.mock_mp_1.entity_id == ump._child_state.entity_id assert self.mock_mp_1.entity_id == ump._child_state.entity_id
self.mock_mp_2._state = STATE_PLAYING self.mock_mp_2._state = STATE_PLAYING
self.mock_mp_2.schedule_update_ha_state() self.mock_mp_2.schedule_update_ha_state()
self.hass.block_till_done() self.hass.block_till_done()
run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
assert self.mock_mp_1.entity_id == ump._child_state.entity_id assert self.mock_mp_1.entity_id == ump._child_state.entity_id
self.mock_mp_1._state = STATE_OFF self.mock_mp_1._state = STATE_OFF
self.mock_mp_1.schedule_update_ha_state() self.mock_mp_1.schedule_update_ha_state()
self.hass.block_till_done() self.hass.block_till_done()
run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
assert self.mock_mp_2.entity_id == ump._child_state.entity_id assert self.mock_mp_2.entity_id == ump._child_state.entity_id
def test_name(self): def test_name(self):
@ -413,14 +413,14 @@ class TestMediaPlayer(unittest.TestCase):
ump = universal.UniversalMediaPlayer(self.hass, **config) ump = universal.UniversalMediaPlayer(self.hass, **config)
ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"]) ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"])
run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
assert ump.state, STATE_OFF assert ump.state, STATE_OFF
self.mock_mp_1._state = STATE_PLAYING self.mock_mp_1._state = STATE_PLAYING
self.mock_mp_1.schedule_update_ha_state() self.mock_mp_1.schedule_update_ha_state()
self.hass.block_till_done() self.hass.block_till_done()
run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
assert STATE_PLAYING == ump.state assert STATE_PLAYING == ump.state
def test_state_with_children_and_attrs(self): def test_state_with_children_and_attrs(self):
@ -429,22 +429,22 @@ class TestMediaPlayer(unittest.TestCase):
ump = universal.UniversalMediaPlayer(self.hass, **config) ump = universal.UniversalMediaPlayer(self.hass, **config)
ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"]) ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"])
run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
assert STATE_OFF == ump.state assert STATE_OFF == ump.state
self.hass.states.set(self.mock_state_switch_id, STATE_ON) self.hass.states.set(self.mock_state_switch_id, STATE_ON)
run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
assert STATE_ON == ump.state assert STATE_ON == ump.state
self.mock_mp_1._state = STATE_PLAYING self.mock_mp_1._state = STATE_PLAYING
self.mock_mp_1.schedule_update_ha_state() self.mock_mp_1.schedule_update_ha_state()
self.hass.block_till_done() self.hass.block_till_done()
run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
assert STATE_PLAYING == ump.state assert STATE_PLAYING == ump.state
self.hass.states.set(self.mock_state_switch_id, STATE_OFF) self.hass.states.set(self.mock_state_switch_id, STATE_OFF)
run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
assert STATE_OFF == ump.state assert STATE_OFF == ump.state
def test_volume_level(self): def test_volume_level(self):
@ -453,20 +453,20 @@ class TestMediaPlayer(unittest.TestCase):
ump = universal.UniversalMediaPlayer(self.hass, **config) ump = universal.UniversalMediaPlayer(self.hass, **config)
ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"]) ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"])
run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
assert ump.volume_level is None assert ump.volume_level is None
self.mock_mp_1._state = STATE_PLAYING self.mock_mp_1._state = STATE_PLAYING
self.mock_mp_1.schedule_update_ha_state() self.mock_mp_1.schedule_update_ha_state()
self.hass.block_till_done() self.hass.block_till_done()
run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
assert 0 == ump.volume_level assert 0 == ump.volume_level
self.mock_mp_1._volume_level = 1 self.mock_mp_1._volume_level = 1
self.mock_mp_1.schedule_update_ha_state() self.mock_mp_1.schedule_update_ha_state()
self.hass.block_till_done() self.hass.block_till_done()
run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
assert 1 == ump.volume_level assert 1 == ump.volume_level
def test_media_image_url(self): def test_media_image_url(self):
@ -476,7 +476,7 @@ class TestMediaPlayer(unittest.TestCase):
ump = universal.UniversalMediaPlayer(self.hass, **config) ump = universal.UniversalMediaPlayer(self.hass, **config)
ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"]) ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"])
run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
assert ump.media_image_url is None assert ump.media_image_url is None
@ -484,7 +484,7 @@ class TestMediaPlayer(unittest.TestCase):
self.mock_mp_1._media_image_url = test_url self.mock_mp_1._media_image_url = test_url
self.mock_mp_1.schedule_update_ha_state() self.mock_mp_1.schedule_update_ha_state()
self.hass.block_till_done() self.hass.block_till_done()
run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
# mock_mp_1 will convert the url to the api proxy url. This test # mock_mp_1 will convert the url to the api proxy url. This test
# ensures ump passes through the same url without an additional proxy. # ensures ump passes through the same url without an additional proxy.
assert self.mock_mp_1.entity_picture == ump.entity_picture assert self.mock_mp_1.entity_picture == ump.entity_picture
@ -495,20 +495,20 @@ class TestMediaPlayer(unittest.TestCase):
ump = universal.UniversalMediaPlayer(self.hass, **config) ump = universal.UniversalMediaPlayer(self.hass, **config)
ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"]) ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"])
run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
assert not ump.is_volume_muted assert not ump.is_volume_muted
self.mock_mp_1._state = STATE_PLAYING self.mock_mp_1._state = STATE_PLAYING
self.mock_mp_1.schedule_update_ha_state() self.mock_mp_1.schedule_update_ha_state()
self.hass.block_till_done() self.hass.block_till_done()
run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
assert not ump.is_volume_muted assert not ump.is_volume_muted
self.mock_mp_1._is_volume_muted = True self.mock_mp_1._is_volume_muted = True
self.mock_mp_1.schedule_update_ha_state() self.mock_mp_1.schedule_update_ha_state()
self.hass.block_till_done() self.hass.block_till_done()
run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
assert ump.is_volume_muted assert ump.is_volume_muted
def test_source_list_children_and_attr(self): def test_source_list_children_and_attr(self):
@ -561,7 +561,7 @@ class TestMediaPlayer(unittest.TestCase):
ump = universal.UniversalMediaPlayer(self.hass, **config) ump = universal.UniversalMediaPlayer(self.hass, **config)
ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"]) ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"])
run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
assert 0 == ump.supported_features assert 0 == ump.supported_features
@ -569,7 +569,7 @@ class TestMediaPlayer(unittest.TestCase):
self.mock_mp_1._state = STATE_PLAYING self.mock_mp_1._state = STATE_PLAYING
self.mock_mp_1.schedule_update_ha_state() self.mock_mp_1.schedule_update_ha_state()
self.hass.block_till_done() self.hass.block_till_done()
run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
assert 512 == ump.supported_features assert 512 == ump.supported_features
def test_supported_features_children_and_cmds(self): def test_supported_features_children_and_cmds(self):
@ -590,12 +590,12 @@ class TestMediaPlayer(unittest.TestCase):
ump = universal.UniversalMediaPlayer(self.hass, **config) ump = universal.UniversalMediaPlayer(self.hass, **config)
ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"]) ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"])
run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
self.mock_mp_1._state = STATE_PLAYING self.mock_mp_1._state = STATE_PLAYING
self.mock_mp_1.schedule_update_ha_state() self.mock_mp_1.schedule_update_ha_state()
self.hass.block_till_done() self.hass.block_till_done()
run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
check_flags = ( check_flags = (
universal.SUPPORT_TURN_ON universal.SUPPORT_TURN_ON
@ -615,16 +615,16 @@ class TestMediaPlayer(unittest.TestCase):
ump = universal.UniversalMediaPlayer(self.hass, **config) ump = universal.UniversalMediaPlayer(self.hass, **config)
ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"]) ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"])
run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
self.mock_mp_1._state = STATE_OFF self.mock_mp_1._state = STATE_OFF
self.mock_mp_1.schedule_update_ha_state() self.mock_mp_1.schedule_update_ha_state()
self.mock_mp_2._state = STATE_OFF self.mock_mp_2._state = STATE_OFF
self.mock_mp_2.schedule_update_ha_state() self.mock_mp_2.schedule_update_ha_state()
self.hass.block_till_done() self.hass.block_till_done()
run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
run_coroutine_threadsafe(ump.async_turn_off(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(ump.async_turn_off(), self.hass.loop).result()
assert 0 == len(self.mock_mp_1.service_calls["turn_off"]) assert 0 == len(self.mock_mp_1.service_calls["turn_off"])
assert 0 == len(self.mock_mp_2.service_calls["turn_off"]) assert 0 == len(self.mock_mp_2.service_calls["turn_off"])
@ -634,67 +634,85 @@ class TestMediaPlayer(unittest.TestCase):
ump = universal.UniversalMediaPlayer(self.hass, **config) ump = universal.UniversalMediaPlayer(self.hass, **config)
ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"]) ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"])
run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
self.mock_mp_2._state = STATE_PLAYING self.mock_mp_2._state = STATE_PLAYING
self.mock_mp_2.schedule_update_ha_state() self.mock_mp_2.schedule_update_ha_state()
self.hass.block_till_done() self.hass.block_till_done()
run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
run_coroutine_threadsafe(ump.async_turn_off(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(ump.async_turn_off(), self.hass.loop).result()
assert 1 == len(self.mock_mp_2.service_calls["turn_off"]) assert 1 == len(self.mock_mp_2.service_calls["turn_off"])
run_coroutine_threadsafe(ump.async_turn_on(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(ump.async_turn_on(), self.hass.loop).result()
assert 1 == len(self.mock_mp_2.service_calls["turn_on"]) assert 1 == len(self.mock_mp_2.service_calls["turn_on"])
run_coroutine_threadsafe(ump.async_mute_volume(True), self.hass.loop).result() asyncio.run_coroutine_threadsafe(
ump.async_mute_volume(True), self.hass.loop
).result()
assert 1 == len(self.mock_mp_2.service_calls["mute_volume"]) assert 1 == len(self.mock_mp_2.service_calls["mute_volume"])
run_coroutine_threadsafe( asyncio.run_coroutine_threadsafe(
ump.async_set_volume_level(0.5), self.hass.loop ump.async_set_volume_level(0.5), self.hass.loop
).result() ).result()
assert 1 == len(self.mock_mp_2.service_calls["set_volume_level"]) assert 1 == len(self.mock_mp_2.service_calls["set_volume_level"])
run_coroutine_threadsafe(ump.async_media_play(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(
ump.async_media_play(), self.hass.loop
).result()
assert 1 == len(self.mock_mp_2.service_calls["media_play"]) assert 1 == len(self.mock_mp_2.service_calls["media_play"])
run_coroutine_threadsafe(ump.async_media_pause(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(
ump.async_media_pause(), self.hass.loop
).result()
assert 1 == len(self.mock_mp_2.service_calls["media_pause"]) assert 1 == len(self.mock_mp_2.service_calls["media_pause"])
run_coroutine_threadsafe( asyncio.run_coroutine_threadsafe(
ump.async_media_previous_track(), self.hass.loop ump.async_media_previous_track(), self.hass.loop
).result() ).result()
assert 1 == len(self.mock_mp_2.service_calls["media_previous_track"]) assert 1 == len(self.mock_mp_2.service_calls["media_previous_track"])
run_coroutine_threadsafe(ump.async_media_next_track(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(
ump.async_media_next_track(), self.hass.loop
).result()
assert 1 == len(self.mock_mp_2.service_calls["media_next_track"]) assert 1 == len(self.mock_mp_2.service_calls["media_next_track"])
run_coroutine_threadsafe(ump.async_media_seek(100), self.hass.loop).result() asyncio.run_coroutine_threadsafe(
ump.async_media_seek(100), self.hass.loop
).result()
assert 1 == len(self.mock_mp_2.service_calls["media_seek"]) assert 1 == len(self.mock_mp_2.service_calls["media_seek"])
run_coroutine_threadsafe( asyncio.run_coroutine_threadsafe(
ump.async_play_media("movie", "batman"), self.hass.loop ump.async_play_media("movie", "batman"), self.hass.loop
).result() ).result()
assert 1 == len(self.mock_mp_2.service_calls["play_media"]) assert 1 == len(self.mock_mp_2.service_calls["play_media"])
run_coroutine_threadsafe(ump.async_volume_up(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(ump.async_volume_up(), self.hass.loop).result()
assert 1 == len(self.mock_mp_2.service_calls["volume_up"]) assert 1 == len(self.mock_mp_2.service_calls["volume_up"])
run_coroutine_threadsafe(ump.async_volume_down(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(
ump.async_volume_down(), self.hass.loop
).result()
assert 1 == len(self.mock_mp_2.service_calls["volume_down"]) assert 1 == len(self.mock_mp_2.service_calls["volume_down"])
run_coroutine_threadsafe(ump.async_media_play_pause(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(
ump.async_media_play_pause(), self.hass.loop
).result()
assert 1 == len(self.mock_mp_2.service_calls["media_play_pause"]) assert 1 == len(self.mock_mp_2.service_calls["media_play_pause"])
run_coroutine_threadsafe( asyncio.run_coroutine_threadsafe(
ump.async_select_source("dvd"), self.hass.loop ump.async_select_source("dvd"), self.hass.loop
).result() ).result()
assert 1 == len(self.mock_mp_2.service_calls["select_source"]) assert 1 == len(self.mock_mp_2.service_calls["select_source"])
run_coroutine_threadsafe(ump.async_clear_playlist(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(
ump.async_clear_playlist(), self.hass.loop
).result()
assert 1 == len(self.mock_mp_2.service_calls["clear_playlist"]) assert 1 == len(self.mock_mp_2.service_calls["clear_playlist"])
run_coroutine_threadsafe(ump.async_set_shuffle(True), self.hass.loop).result() asyncio.run_coroutine_threadsafe(
ump.async_set_shuffle(True), self.hass.loop
).result()
assert 1 == len(self.mock_mp_2.service_calls["shuffle_set"]) assert 1 == len(self.mock_mp_2.service_calls["shuffle_set"])
def test_service_call_to_command(self): def test_service_call_to_command(self):
@ -707,12 +725,12 @@ class TestMediaPlayer(unittest.TestCase):
ump = universal.UniversalMediaPlayer(self.hass, **config) ump = universal.UniversalMediaPlayer(self.hass, **config)
ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"]) ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"])
run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
self.mock_mp_2._state = STATE_PLAYING self.mock_mp_2._state = STATE_PLAYING
self.mock_mp_2.schedule_update_ha_state() self.mock_mp_2.schedule_update_ha_state()
self.hass.block_till_done() self.hass.block_till_done()
run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
run_coroutine_threadsafe(ump.async_turn_off(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(ump.async_turn_off(), self.hass.loop).result()
assert 1 == len(service) assert 1 == len(service)

View file

@ -1,9 +1,9 @@
"""The tests for the uptime sensor platform.""" """The tests for the uptime sensor platform."""
import asyncio
import unittest import unittest
from unittest.mock import patch from unittest.mock import patch
from datetime import timedelta from datetime import timedelta
from homeassistant.util.async_ import run_coroutine_threadsafe
from homeassistant.setup import setup_component from homeassistant.setup import setup_component
from homeassistant.components.uptime.sensor import UptimeSensor from homeassistant.components.uptime.sensor import UptimeSensor
from tests.common import get_test_home_assistant from tests.common import get_test_home_assistant
@ -46,11 +46,15 @@ class TestUptimeSensor(unittest.TestCase):
assert sensor.unit_of_measurement == "days" assert sensor.unit_of_measurement == "days"
new_time = sensor.initial + timedelta(days=1) new_time = sensor.initial + timedelta(days=1)
with patch("homeassistant.util.dt.now", return_value=new_time): with patch("homeassistant.util.dt.now", return_value=new_time):
run_coroutine_threadsafe(sensor.async_update(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(
sensor.async_update(), self.hass.loop
).result()
assert sensor.state == 1.00 assert sensor.state == 1.00
new_time = sensor.initial + timedelta(days=111.499) new_time = sensor.initial + timedelta(days=111.499)
with patch("homeassistant.util.dt.now", return_value=new_time): with patch("homeassistant.util.dt.now", return_value=new_time):
run_coroutine_threadsafe(sensor.async_update(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(
sensor.async_update(), self.hass.loop
).result()
assert sensor.state == 111.50 assert sensor.state == 111.50
def test_uptime_sensor_hours_output(self): def test_uptime_sensor_hours_output(self):
@ -59,11 +63,15 @@ class TestUptimeSensor(unittest.TestCase):
assert sensor.unit_of_measurement == "hours" assert sensor.unit_of_measurement == "hours"
new_time = sensor.initial + timedelta(hours=16) new_time = sensor.initial + timedelta(hours=16)
with patch("homeassistant.util.dt.now", return_value=new_time): with patch("homeassistant.util.dt.now", return_value=new_time):
run_coroutine_threadsafe(sensor.async_update(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(
sensor.async_update(), self.hass.loop
).result()
assert sensor.state == 16.00 assert sensor.state == 16.00
new_time = sensor.initial + timedelta(hours=72.499) new_time = sensor.initial + timedelta(hours=72.499)
with patch("homeassistant.util.dt.now", return_value=new_time): with patch("homeassistant.util.dt.now", return_value=new_time):
run_coroutine_threadsafe(sensor.async_update(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(
sensor.async_update(), self.hass.loop
).result()
assert sensor.state == 72.50 assert sensor.state == 72.50
def test_uptime_sensor_minutes_output(self): def test_uptime_sensor_minutes_output(self):
@ -72,9 +80,13 @@ class TestUptimeSensor(unittest.TestCase):
assert sensor.unit_of_measurement == "minutes" assert sensor.unit_of_measurement == "minutes"
new_time = sensor.initial + timedelta(minutes=16) new_time = sensor.initial + timedelta(minutes=16)
with patch("homeassistant.util.dt.now", return_value=new_time): with patch("homeassistant.util.dt.now", return_value=new_time):
run_coroutine_threadsafe(sensor.async_update(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(
sensor.async_update(), self.hass.loop
).result()
assert sensor.state == 16.00 assert sensor.state == 16.00
new_time = sensor.initial + timedelta(minutes=12.499) new_time = sensor.initial + timedelta(minutes=12.499)
with patch("homeassistant.util.dt.now", return_value=new_time): with patch("homeassistant.util.dt.now", return_value=new_time):
run_coroutine_threadsafe(sensor.async_update(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(
sensor.async_update(), self.hass.loop
).result()
assert sensor.state == 12.50 assert sensor.state == 12.50

View file

@ -15,7 +15,6 @@ import pytest
import homeassistant.core as ha import homeassistant.core as ha
from homeassistant.exceptions import InvalidEntityFormatError, InvalidStateError from homeassistant.exceptions import InvalidEntityFormatError, InvalidStateError
from homeassistant.util.async_ import run_coroutine_threadsafe
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
from homeassistant.util.unit_system import METRIC_SYSTEM from homeassistant.util.unit_system import METRIC_SYSTEM
from homeassistant.const import ( from homeassistant.const import (
@ -191,7 +190,7 @@ class TestHomeAssistant(unittest.TestCase):
for _ in range(3): for _ in range(3):
self.hass.add_job(test_coro()) self.hass.add_job(test_coro())
run_coroutine_threadsafe( asyncio.run_coroutine_threadsafe(
asyncio.wait(self.hass._pending_tasks), loop=self.hass.loop asyncio.wait(self.hass._pending_tasks), loop=self.hass.loop
).result() ).result()
@ -216,7 +215,9 @@ class TestHomeAssistant(unittest.TestCase):
yield from asyncio.sleep(0) yield from asyncio.sleep(0)
yield from asyncio.sleep(0) yield from asyncio.sleep(0)
run_coroutine_threadsafe(wait_finish_callback(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(
wait_finish_callback(), self.hass.loop
).result()
assert len(self.hass._pending_tasks) == 2 assert len(self.hass._pending_tasks) == 2
self.hass.block_till_done() self.hass.block_till_done()
@ -239,7 +240,9 @@ class TestHomeAssistant(unittest.TestCase):
for _ in range(2): for _ in range(2):
self.hass.add_job(test_executor) self.hass.add_job(test_executor)
run_coroutine_threadsafe(wait_finish_callback(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(
wait_finish_callback(), self.hass.loop
).result()
assert len(self.hass._pending_tasks) == 2 assert len(self.hass._pending_tasks) == 2
self.hass.block_till_done() self.hass.block_till_done()
@ -263,7 +266,9 @@ class TestHomeAssistant(unittest.TestCase):
for _ in range(2): for _ in range(2):
self.hass.add_job(test_callback) self.hass.add_job(test_callback)
run_coroutine_threadsafe(wait_finish_callback(), self.hass.loop).result() asyncio.run_coroutine_threadsafe(
wait_finish_callback(), self.hass.loop
).result()
self.hass.block_till_done() self.hass.block_till_done()

View file

@ -9,43 +9,6 @@ import pytest
from homeassistant.util import async_ as hasync from homeassistant.util import async_ as hasync
@patch("asyncio.coroutines.iscoroutine")
@patch("concurrent.futures.Future")
@patch("threading.get_ident")
def test_run_coroutine_threadsafe_from_inside_event_loop(
mock_ident, _, mock_iscoroutine
):
"""Testing calling run_coroutine_threadsafe from inside an event loop."""
coro = MagicMock()
loop = MagicMock()
loop._thread_ident = None
mock_ident.return_value = 5
mock_iscoroutine.return_value = True
hasync.run_coroutine_threadsafe(coro, loop)
assert len(loop.call_soon_threadsafe.mock_calls) == 1
loop._thread_ident = 5
mock_ident.return_value = 5
mock_iscoroutine.return_value = True
with pytest.raises(RuntimeError):
hasync.run_coroutine_threadsafe(coro, loop)
assert len(loop.call_soon_threadsafe.mock_calls) == 1
loop._thread_ident = 1
mock_ident.return_value = 5
mock_iscoroutine.return_value = False
with pytest.raises(TypeError):
hasync.run_coroutine_threadsafe(coro, loop)
assert len(loop.call_soon_threadsafe.mock_calls) == 1
loop._thread_ident = 1
mock_ident.return_value = 5
mock_iscoroutine.return_value = True
hasync.run_coroutine_threadsafe(coro, loop)
assert len(loop.call_soon_threadsafe.mock_calls) == 2
@patch("asyncio.coroutines.iscoroutine") @patch("asyncio.coroutines.iscoroutine")
@patch("concurrent.futures.Future") @patch("concurrent.futures.Future")
@patch("threading.get_ident") @patch("threading.get_ident")
@ -187,49 +150,6 @@ class RunThreadsafeTests(TestCase):
finally: finally:
future.done() or future.cancel() future.done() or future.cancel()
def test_run_coroutine_threadsafe(self):
"""Test coroutine submission from a thread to an event loop."""
future = self.loop.run_in_executor(None, self.target_coroutine)
result = self.loop.run_until_complete(future)
self.assertEqual(result, 3)
def test_run_coroutine_threadsafe_with_exception(self):
"""Test coroutine submission from thread to event loop on exception."""
future = self.loop.run_in_executor(None, self.target_coroutine, True)
with self.assertRaises(RuntimeError) as exc_context:
self.loop.run_until_complete(future)
self.assertIn("Fail!", exc_context.exception.args)
def test_run_coroutine_threadsafe_with_invalid(self):
"""Test coroutine submission from thread to event loop on invalid."""
callback = lambda: self.target_coroutine(invalid=True) # noqa
future = self.loop.run_in_executor(None, callback)
with self.assertRaises(ValueError) as exc_context:
self.loop.run_until_complete(future)
self.assertIn("Invalid!", exc_context.exception.args)
def test_run_coroutine_threadsafe_with_timeout(self):
"""Test coroutine submission from thread to event loop on timeout."""
callback = lambda: self.target_coroutine(timeout=0) # noqa
future = self.loop.run_in_executor(None, callback)
with self.assertRaises(asyncio.TimeoutError):
self.loop.run_until_complete(future)
self.run_briefly(self.loop)
# Check that there's no pending task (add has been cancelled)
if sys.version_info[:2] >= (3, 7):
all_tasks = asyncio.all_tasks
else:
all_tasks = asyncio.Task.all_tasks
for task in all_tasks(self.loop):
self.assertTrue(task.done())
def test_run_coroutine_threadsafe_task_cancelled(self):
"""Test coroutine submission from tread to event loop on cancel."""
callback = lambda: self.target_coroutine(cancel=True) # noqa
future = self.loop.run_in_executor(None, callback)
with self.assertRaises(asyncio.CancelledError):
self.loop.run_until_complete(future)
def test_run_callback_threadsafe(self): def test_run_callback_threadsafe(self):
"""Test callback submission from a thread to an event loop.""" """Test callback submission from a thread to an event loop."""
future = self.loop.run_in_executor(None, self.target_callback) future = self.loop.run_in_executor(None, self.target_callback)