diff --git a/tests/components/blackbird/test_media_player.py b/tests/components/blackbird/test_media_player.py index 73b40fdec97..a2f5f9914da 100644 --- a/tests/components/blackbird/test_media_player.py +++ b/tests/components/blackbird/test_media_player.py @@ -1,6 +1,5 @@ """The tests for the Monoprice Blackbird media player platform.""" from collections import defaultdict -import unittest from unittest import mock import pytest @@ -19,8 +18,6 @@ from homeassistant.components.media_player.const import ( ) from homeassistant.const import STATE_OFF, STATE_ON -import tests.common - class AttrDict(dict): """Helper class for mocking attributes.""" @@ -60,283 +57,301 @@ class MockBlackbird: self.zones[3].av = source_idx -class TestBlackbirdSchema(unittest.TestCase): - """Test Blackbird schema.""" +def test_valid_serial_schema(): + """Test valid schema.""" + valid_schema = { + "platform": "blackbird", + "port": "/dev/ttyUSB0", + "zones": { + 1: {"name": "a"}, + 2: {"name": "a"}, + 3: {"name": "a"}, + 4: {"name": "a"}, + 5: {"name": "a"}, + 6: {"name": "a"}, + 7: {"name": "a"}, + 8: {"name": "a"}, + }, + "sources": { + 1: {"name": "a"}, + 2: {"name": "a"}, + 3: {"name": "a"}, + 4: {"name": "a"}, + 5: {"name": "a"}, + 6: {"name": "a"}, + 7: {"name": "a"}, + 8: {"name": "a"}, + }, + } + PLATFORM_SCHEMA(valid_schema) - def test_valid_serial_schema(self): - """Test valid schema.""" - valid_schema = { + +def test_valid_socket_schema(): + """Test valid schema.""" + valid_schema = { + "platform": "blackbird", + "host": "192.168.1.50", + "zones": { + 1: {"name": "a"}, + 2: {"name": "a"}, + 3: {"name": "a"}, + 4: {"name": "a"}, + 5: {"name": "a"}, + }, + "sources": { + 1: {"name": "a"}, + 2: {"name": "a"}, + 3: {"name": "a"}, + 4: {"name": "a"}, + }, + } + PLATFORM_SCHEMA(valid_schema) + + +def test_invalid_schemas(): + """Test invalid schemas.""" + schemas = ( + {}, # Empty + None, # None + # Port and host used concurrently + { "platform": "blackbird", "port": "/dev/ttyUSB0", - "zones": { - 1: {"name": "a"}, - 2: {"name": "a"}, - 3: {"name": "a"}, - 4: {"name": "a"}, - 5: {"name": "a"}, - 6: {"name": "a"}, - 7: {"name": "a"}, - 8: {"name": "a"}, - }, - "sources": { - 1: {"name": "a"}, - 2: {"name": "a"}, - 3: {"name": "a"}, - 4: {"name": "a"}, - 5: {"name": "a"}, - 6: {"name": "a"}, - 7: {"name": "a"}, - 8: {"name": "a"}, - }, - } - PLATFORM_SCHEMA(valid_schema) - - def test_valid_socket_schema(self): - """Test valid schema.""" - valid_schema = { - "platform": "blackbird", "host": "192.168.1.50", - "zones": { - 1: {"name": "a"}, - 2: {"name": "a"}, - 3: {"name": "a"}, - 4: {"name": "a"}, - 5: {"name": "a"}, - }, - "sources": { - 1: {"name": "a"}, - 2: {"name": "a"}, - 3: {"name": "a"}, - 4: {"name": "a"}, - }, - } - PLATFORM_SCHEMA(valid_schema) - - def test_invalid_schemas(self): - """Test invalid schemas.""" - schemas = ( - {}, # Empty - None, # None - # Port and host used concurrently - { - "platform": "blackbird", - "port": "/dev/ttyUSB0", - "host": "192.168.1.50", - "name": "Name", - "zones": {1: {"name": "a"}}, - "sources": {1: {"name": "b"}}, - }, - # Port or host missing - { - "platform": "blackbird", - "name": "Name", - "zones": {1: {"name": "a"}}, - "sources": {1: {"name": "b"}}, - }, - # Invalid zone number - { - "platform": "blackbird", - "port": "/dev/ttyUSB0", - "name": "Name", - "zones": {11: {"name": "a"}}, - "sources": {1: {"name": "b"}}, - }, - # Invalid source number - { - "platform": "blackbird", - "port": "/dev/ttyUSB0", - "name": "Name", - "zones": {1: {"name": "a"}}, - "sources": {9: {"name": "b"}}, - }, - # Zone missing name - { - "platform": "blackbird", - "port": "/dev/ttyUSB0", - "name": "Name", - "zones": {1: {}}, - "sources": {1: {"name": "b"}}, - }, - # Source missing name - { - "platform": "blackbird", - "port": "/dev/ttyUSB0", - "name": "Name", - "zones": {1: {"name": "a"}}, - "sources": {1: {}}, - }, - ) - for value in schemas: - with pytest.raises(vol.MultipleInvalid): - PLATFORM_SCHEMA(value) + "name": "Name", + "zones": {1: {"name": "a"}}, + "sources": {1: {"name": "b"}}, + }, + # Port or host missing + { + "platform": "blackbird", + "name": "Name", + "zones": {1: {"name": "a"}}, + "sources": {1: {"name": "b"}}, + }, + # Invalid zone number + { + "platform": "blackbird", + "port": "/dev/ttyUSB0", + "name": "Name", + "zones": {11: {"name": "a"}}, + "sources": {1: {"name": "b"}}, + }, + # Invalid source number + { + "platform": "blackbird", + "port": "/dev/ttyUSB0", + "name": "Name", + "zones": {1: {"name": "a"}}, + "sources": {9: {"name": "b"}}, + }, + # Zone missing name + { + "platform": "blackbird", + "port": "/dev/ttyUSB0", + "name": "Name", + "zones": {1: {}}, + "sources": {1: {"name": "b"}}, + }, + # Source missing name + { + "platform": "blackbird", + "port": "/dev/ttyUSB0", + "name": "Name", + "zones": {1: {"name": "a"}}, + "sources": {1: {}}, + }, + ) + for value in schemas: + with pytest.raises(vol.MultipleInvalid): + PLATFORM_SCHEMA(value) -class TestBlackbirdMediaPlayer(unittest.TestCase): - """Test the media_player module.""" +@pytest.fixture +def mock_blackbird(): + """Return a mock blackbird instance.""" + return MockBlackbird() - def setUp(self): - """Set up the test case.""" - self.blackbird = MockBlackbird() - self.hass = tests.common.get_test_home_assistant() - self.hass.start() - # Note, source dictionary is unsorted! - with mock.patch( - "homeassistant.components.blackbird.media_player.get_blackbird", - new=lambda *a: self.blackbird, - ): - setup_platform( - self.hass, - { - "platform": "blackbird", - "port": "/dev/ttyUSB0", - "zones": {3: {"name": "Zone name"}}, - "sources": { - 1: {"name": "one"}, - 3: {"name": "three"}, - 2: {"name": "two"}, - }, + +@pytest.fixture +async def setup_blackbird(hass, mock_blackbird): + """Set up blackbird.""" + with mock.patch( + "homeassistant.components.blackbird.media_player.get_blackbird", + new=lambda *a: mock_blackbird, + ): + await hass.async_add_executor_job( + setup_platform, + hass, + { + "platform": "blackbird", + "port": "/dev/ttyUSB0", + "zones": {3: {"name": "Zone name"}}, + "sources": { + 1: {"name": "one"}, + 3: {"name": "three"}, + 2: {"name": "two"}, }, - lambda *args, **kwargs: None, - {}, - ) - self.hass.block_till_done() - self.media_player = self.hass.data[DATA_BLACKBIRD]["/dev/ttyUSB0-3"] - self.media_player.hass = self.hass - self.media_player.entity_id = "media_player.zone_3" - self.addCleanup(self.tear_down_cleanup) - - def tear_down_cleanup(self): - """Tear down the test case.""" - self.hass.stop() - - def test_setup_platform(self, *args): - """Test setting up platform.""" - # One service must be registered - assert self.hass.services.has_service(DOMAIN, SERVICE_SETALLZONES) - assert len(self.hass.data[DATA_BLACKBIRD]) == 1 - assert self.hass.data[DATA_BLACKBIRD]["/dev/ttyUSB0-3"].name == "Zone name" - - def test_setallzones_service_call_with_entity_id(self): - """Test set all zone source service call with entity id.""" - self.media_player.update() - assert self.media_player.name == "Zone name" - assert self.media_player.state == STATE_ON - assert self.media_player.source == "one" - - # Call set all zones service - self.hass.services.call( - DOMAIN, - SERVICE_SETALLZONES, - {"entity_id": "media_player.zone_3", "source": "three"}, - blocking=True, + }, + lambda *args, **kwargs: None, + {}, ) + await hass.async_block_till_done() - # Check that source was changed - assert self.blackbird.zones[3].av == 3 - self.media_player.update() - assert self.media_player.source == "three" - def test_setallzones_service_call_without_entity_id(self): - """Test set all zone source service call without entity id.""" - self.media_player.update() - assert self.media_player.name == "Zone name" - assert self.media_player.state == STATE_ON - assert self.media_player.source == "one" +@pytest.fixture +def media_player_entity(hass, setup_blackbird): + """Return the media player entity.""" + media_player = hass.data[DATA_BLACKBIRD]["/dev/ttyUSB0-3"] + media_player.hass = hass + media_player.entity_id = "media_player.zone_3" + return media_player - # Call set all zones service - self.hass.services.call( - DOMAIN, SERVICE_SETALLZONES, {"source": "three"}, blocking=True - ) - # Check that source was changed - assert self.blackbird.zones[3].av == 3 - self.media_player.update() - assert self.media_player.source == "three" +async def test_setup_platform(hass, setup_blackbird): + """Test setting up platform.""" + # One service must be registered + assert hass.services.has_service(DOMAIN, SERVICE_SETALLZONES) + assert len(hass.data[DATA_BLACKBIRD]) == 1 + assert hass.data[DATA_BLACKBIRD]["/dev/ttyUSB0-3"].name == "Zone name" - def test_update(self): - """Test updating values from blackbird.""" - assert self.media_player.state is None - assert self.media_player.source is None - self.media_player.update() +async def test_setallzones_service_call_with_entity_id( + hass, media_player_entity, mock_blackbird +): + """Test set all zone source service call with entity id.""" + await hass.async_add_executor_job(media_player_entity.update) + assert media_player_entity.name == "Zone name" + assert media_player_entity.state == STATE_ON + assert media_player_entity.source == "one" - assert self.media_player.state == STATE_ON - assert self.media_player.source == "one" + # Call set all zones service + await hass.services.async_call( + DOMAIN, + SERVICE_SETALLZONES, + {"entity_id": "media_player.zone_3", "source": "three"}, + blocking=True, + ) - def test_name(self): - """Test name property.""" - assert self.media_player.name == "Zone name" + # Check that source was changed + assert mock_blackbird.zones[3].av == 3 + await hass.async_add_executor_job(media_player_entity.update) + assert media_player_entity.source == "three" - def test_state(self): - """Test state property.""" - assert self.media_player.state is None - self.media_player.update() - assert self.media_player.state == STATE_ON +async def test_setallzones_service_call_without_entity_id( + mock_blackbird, hass, media_player_entity +): + """Test set all zone source service call without entity id.""" + await hass.async_add_executor_job(media_player_entity.update) + assert media_player_entity.name == "Zone name" + assert media_player_entity.state == STATE_ON + assert media_player_entity.source == "one" - self.blackbird.zones[3].power = False - self.media_player.update() - assert self.media_player.state == STATE_OFF + # Call set all zones service + await hass.services.async_call( + DOMAIN, SERVICE_SETALLZONES, {"source": "three"}, blocking=True + ) - def test_supported_features(self): - """Test supported features property.""" - assert ( - SUPPORT_TURN_ON | SUPPORT_TURN_OFF | SUPPORT_SELECT_SOURCE - == self.media_player.supported_features - ) + # Check that source was changed + assert mock_blackbird.zones[3].av == 3 + await hass.async_add_executor_job(media_player_entity.update) + assert media_player_entity.source == "three" - def test_source(self): - """Test source property.""" - assert self.media_player.source is None - self.media_player.update() - assert self.media_player.source == "one" - def test_media_title(self): - """Test media title property.""" - assert self.media_player.media_title is None - self.media_player.update() - assert self.media_player.media_title == "one" +async def test_update(hass, media_player_entity): + """Test updating values from blackbird.""" + assert media_player_entity.state is None + assert media_player_entity.source is None - def test_source_list(self): - """Test source list property.""" - # Note, the list is sorted! - assert self.media_player.source_list == ["one", "two", "three"] + await hass.async_add_executor_job(media_player_entity.update) - def test_select_source(self): - """Test source selection methods.""" - self.media_player.update() + assert media_player_entity.state == STATE_ON + assert media_player_entity.source == "one" - assert self.media_player.source == "one" - self.media_player.select_source("two") - assert self.blackbird.zones[3].av == 2 - self.media_player.update() - assert self.media_player.source == "two" +async def test_name(media_player_entity): + """Test name property.""" + assert media_player_entity.name == "Zone name" - # Trying to set unknown source. - self.media_player.select_source("no name") - assert self.blackbird.zones[3].av == 2 - self.media_player.update() - assert self.media_player.source == "two" - def test_turn_on(self): - """Testing turning on the zone.""" - self.blackbird.zones[3].power = False - self.media_player.update() - assert self.media_player.state == STATE_OFF +async def test_state(hass, media_player_entity, mock_blackbird): + """Test state property.""" + assert media_player_entity.state is None - self.media_player.turn_on() - assert self.blackbird.zones[3].power - self.media_player.update() - assert self.media_player.state == STATE_ON + await hass.async_add_executor_job(media_player_entity.update) + assert media_player_entity.state == STATE_ON - def test_turn_off(self): - """Testing turning off the zone.""" - self.blackbird.zones[3].power = True - self.media_player.update() - assert self.media_player.state == STATE_ON + mock_blackbird.zones[3].power = False + await hass.async_add_executor_job(media_player_entity.update) + assert media_player_entity.state == STATE_OFF - self.media_player.turn_off() - assert not self.blackbird.zones[3].power - self.media_player.update() - assert self.media_player.state == STATE_OFF + +async def test_supported_features(media_player_entity): + """Test supported features property.""" + assert ( + SUPPORT_TURN_ON | SUPPORT_TURN_OFF | SUPPORT_SELECT_SOURCE + == media_player_entity.supported_features + ) + + +async def test_source(hass, media_player_entity): + """Test source property.""" + assert media_player_entity.source is None + await hass.async_add_executor_job(media_player_entity.update) + assert media_player_entity.source == "one" + + +async def test_media_title(hass, media_player_entity): + """Test media title property.""" + assert media_player_entity.media_title is None + await hass.async_add_executor_job(media_player_entity.update) + assert media_player_entity.media_title == "one" + + +async def test_source_list(media_player_entity): + """Test source list property.""" + # Note, the list is sorted! + assert media_player_entity.source_list == ["one", "two", "three"] + + +async def test_select_source(hass, media_player_entity, mock_blackbird): + """Test source selection methods.""" + await hass.async_add_executor_job(media_player_entity.update) + + assert media_player_entity.source == "one" + + await media_player_entity.async_select_source("two") + assert mock_blackbird.zones[3].av == 2 + await hass.async_add_executor_job(media_player_entity.update) + assert media_player_entity.source == "two" + + # Trying to set unknown source. + await media_player_entity.async_select_source("no name") + assert mock_blackbird.zones[3].av == 2 + await hass.async_add_executor_job(media_player_entity.update) + assert media_player_entity.source == "two" + + +async def test_turn_on(hass, media_player_entity, mock_blackbird): + """Testing turning on the zone.""" + mock_blackbird.zones[3].power = False + await hass.async_add_executor_job(media_player_entity.update) + assert media_player_entity.state == STATE_OFF + + await media_player_entity.async_turn_on() + assert mock_blackbird.zones[3].power + await hass.async_add_executor_job(media_player_entity.update) + assert media_player_entity.state == STATE_ON + + +async def test_turn_off(hass, media_player_entity, mock_blackbird): + """Testing turning off the zone.""" + mock_blackbird.zones[3].power = True + await hass.async_add_executor_job(media_player_entity.update) + assert media_player_entity.state == STATE_ON + + await media_player_entity.async_turn_off() + assert not mock_blackbird.zones[3].power + await hass.async_add_executor_job(media_player_entity.update) + assert media_player_entity.state == STATE_OFF diff --git a/tests/components/universal/test_media_player.py b/tests/components/universal/test_media_player.py index 4c8b125cbce..87c3abbb65a 100644 --- a/tests/components/universal/test_media_player.py +++ b/tests/components/universal/test_media_player.py @@ -1,9 +1,8 @@ """The tests for the Universal Media player platform.""" -import asyncio from copy import copy -import unittest -from unittest.mock import patch +from unittest.mock import Mock, patch +import pytest from voluptuous.error import MultipleInvalid from homeassistant import config as hass_config @@ -21,9 +20,18 @@ from homeassistant.const import ( STATE_UNKNOWN, ) from homeassistant.core import Context, callback -from homeassistant.setup import async_setup_component, setup_component +from homeassistant.setup import async_setup_component -from tests.common import get_fixture_path, get_test_home_assistant, mock_service +from tests.common import async_mock_service, get_fixture_path + +CONFIG_CHILDREN_ONLY = { + "name": "test", + "platform": "universal", + "children": [ + media_player.ENTITY_ID_FORMAT.format("mock1"), + media_player.ENTITY_ID_FORMAT.format("mock2"), + ], +} def validate_config(config): @@ -53,64 +61,64 @@ class MockMediaPlayer(media_player.MediaPlayerEntity): self._sound_mode = None self.service_calls = { - "turn_on": mock_service( + "turn_on": async_mock_service( hass, media_player.DOMAIN, media_player.SERVICE_TURN_ON ), - "turn_off": mock_service( + "turn_off": async_mock_service( hass, media_player.DOMAIN, media_player.SERVICE_TURN_OFF ), - "mute_volume": mock_service( + "mute_volume": async_mock_service( hass, media_player.DOMAIN, media_player.SERVICE_VOLUME_MUTE ), - "set_volume_level": mock_service( + "set_volume_level": async_mock_service( hass, media_player.DOMAIN, media_player.SERVICE_VOLUME_SET ), - "media_play": mock_service( + "media_play": async_mock_service( hass, media_player.DOMAIN, media_player.SERVICE_MEDIA_PLAY ), - "media_pause": mock_service( + "media_pause": async_mock_service( hass, media_player.DOMAIN, media_player.SERVICE_MEDIA_PAUSE ), - "media_stop": mock_service( + "media_stop": async_mock_service( hass, media_player.DOMAIN, media_player.SERVICE_MEDIA_STOP ), - "media_previous_track": mock_service( + "media_previous_track": async_mock_service( hass, media_player.DOMAIN, media_player.SERVICE_MEDIA_PREVIOUS_TRACK ), - "media_next_track": mock_service( + "media_next_track": async_mock_service( hass, media_player.DOMAIN, media_player.SERVICE_MEDIA_NEXT_TRACK ), - "media_seek": mock_service( + "media_seek": async_mock_service( hass, media_player.DOMAIN, media_player.SERVICE_MEDIA_SEEK ), - "play_media": mock_service( + "play_media": async_mock_service( hass, media_player.DOMAIN, media_player.SERVICE_PLAY_MEDIA ), - "volume_up": mock_service( + "volume_up": async_mock_service( hass, media_player.DOMAIN, media_player.SERVICE_VOLUME_UP ), - "volume_down": mock_service( + "volume_down": async_mock_service( hass, media_player.DOMAIN, media_player.SERVICE_VOLUME_DOWN ), - "media_play_pause": mock_service( + "media_play_pause": async_mock_service( hass, media_player.DOMAIN, media_player.SERVICE_MEDIA_PLAY_PAUSE ), - "select_sound_mode": mock_service( + "select_sound_mode": async_mock_service( hass, media_player.DOMAIN, media_player.SERVICE_SELECT_SOUND_MODE ), - "select_source": mock_service( + "select_source": async_mock_service( hass, media_player.DOMAIN, media_player.SERVICE_SELECT_SOURCE ), - "toggle": mock_service( + "toggle": async_mock_service( hass, media_player.DOMAIN, media_player.SERVICE_TOGGLE ), - "clear_playlist": mock_service( + "clear_playlist": async_mock_service( hass, media_player.DOMAIN, media_player.SERVICE_CLEAR_PLAYLIST ), - "repeat_set": mock_service( + "repeat_set": async_mock_service( hass, media_player.DOMAIN, media_player.SERVICE_REPEAT_SET ), - "shuffle_set": mock_service( + "shuffle_set": async_mock_service( hass, media_player.DOMAIN, media_player.SERVICE_SHUFFLE_SET ), } @@ -199,826 +207,830 @@ class MockMediaPlayer(media_player.MediaPlayerEntity): self._repeat = repeat -class TestMediaPlayer(unittest.TestCase): - """Test the media_player module.""" - - def setUp(self): # pylint: disable=invalid-name - """Set up things to be run when tests are started.""" - self.hass = get_test_home_assistant() - - self.mock_mp_1 = MockMediaPlayer(self.hass, "mock1") - self.mock_mp_1.schedule_update_ha_state() - - self.mock_mp_2 = MockMediaPlayer(self.hass, "mock2") - self.mock_mp_2.schedule_update_ha_state() - - self.hass.block_till_done() - - self.mock_mute_switch_id = switch.ENTITY_ID_FORMAT.format("mute") - self.hass.states.set(self.mock_mute_switch_id, STATE_OFF) - - self.mock_state_switch_id = switch.ENTITY_ID_FORMAT.format("state") - self.hass.states.set(self.mock_state_switch_id, STATE_OFF) - - self.mock_volume_id = f"{input_number.DOMAIN}.volume_level" - self.hass.states.set(self.mock_volume_id, 0) - - self.mock_source_list_id = f"{input_select.DOMAIN}.source_list" - self.hass.states.set(self.mock_source_list_id, ["dvd", "htpc"]) - - self.mock_source_id = f"{input_select.DOMAIN}.source" - self.hass.states.set(self.mock_source_id, "dvd") - - self.mock_sound_mode_list_id = f"{input_select.DOMAIN}.sound_mode_list" - self.hass.states.set(self.mock_sound_mode_list_id, ["music", "movie"]) - - self.mock_sound_mode_id = f"{input_select.DOMAIN}.sound_mode" - self.hass.states.set(self.mock_sound_mode_id, "music") - - self.mock_shuffle_switch_id = switch.ENTITY_ID_FORMAT.format("shuffle") - self.hass.states.set(self.mock_shuffle_switch_id, STATE_OFF) - - self.mock_repeat_switch_id = switch.ENTITY_ID_FORMAT.format("repeat") - self.hass.states.set(self.mock_repeat_switch_id, STATE_OFF) - - self.config_children_only = { - "name": "test", - "platform": "universal", - "children": [ - media_player.ENTITY_ID_FORMAT.format("mock1"), - media_player.ENTITY_ID_FORMAT.format("mock2"), - ], - } - self.config_children_and_attr = { - "name": "test", - "platform": "universal", - "children": [ - media_player.ENTITY_ID_FORMAT.format("mock1"), - media_player.ENTITY_ID_FORMAT.format("mock2"), - ], - "attributes": { - "is_volume_muted": self.mock_mute_switch_id, - "volume_level": self.mock_volume_id, - "source": self.mock_source_id, - "source_list": self.mock_source_list_id, - "state": self.mock_state_switch_id, - "shuffle": self.mock_shuffle_switch_id, - "repeat": self.mock_repeat_switch_id, - "sound_mode_list": self.mock_sound_mode_list_id, - "sound_mode": self.mock_sound_mode_id, - }, - } - self.addCleanup(self.tear_down_cleanup) - - def tear_down_cleanup(self): - """Stop everything that was started.""" - self.hass.stop() - - def test_config_children_only(self): - """Check config with only children.""" - config_start = copy(self.config_children_only) - del config_start["platform"] - config_start["commands"] = {} - config_start["attributes"] = {} - - config = validate_config(self.config_children_only) - assert config_start == config - - def test_config_children_and_attr(self): - """Check config with children and attributes.""" - config_start = copy(self.config_children_and_attr) - del config_start["platform"] - config_start["commands"] = {} - - config = validate_config(self.config_children_and_attr) - assert config_start == config - - def test_config_no_name(self): - """Check config with no Name entry.""" - response = True - try: - validate_config({"platform": "universal"}) - except MultipleInvalid: - response = False - assert not response - - def test_config_bad_children(self): - """Check config with bad children entry.""" - config_no_children = {"name": "test", "platform": "universal"} - config_bad_children = {"name": "test", "children": {}, "platform": "universal"} - - config_no_children = validate_config(config_no_children) - assert [] == config_no_children["children"] - - config_bad_children = validate_config(config_bad_children) - assert [] == config_bad_children["children"] - - def test_config_bad_commands(self): - """Check config with bad commands entry.""" - config = {"name": "test", "platform": "universal"} - - config = validate_config(config) - assert {} == config["commands"] - - def test_config_bad_attributes(self): - """Check config with bad attributes.""" - config = {"name": "test", "platform": "universal"} - - config = validate_config(config) - assert {} == config["attributes"] - - def test_config_bad_key(self): - """Check config with bad key.""" - config = {"name": "test", "asdf": 5, "platform": "universal"} - - config = validate_config(config) - assert "asdf" not in config - - def test_platform_setup(self): - """Test platform setup.""" - config = {"name": "test", "platform": "universal"} - bad_config = {"platform": "universal"} - entities = [] - - def add_entities(new_entities): - """Add devices to list.""" - for dev in new_entities: - entities.append(dev) - - setup_ok = True - try: - asyncio.run_coroutine_threadsafe( - universal.async_setup_platform( - self.hass, validate_config(bad_config), add_entities - ), - self.hass.loop, - ).result() - except MultipleInvalid: - setup_ok = False - assert not setup_ok - assert len(entities) == 0 - - asyncio.run_coroutine_threadsafe( - universal.async_setup_platform( - self.hass, validate_config(config), add_entities - ), - self.hass.loop, - ).result() - assert len(entities) == 1 - assert entities[0].name == "test" - - def test_master_state(self): - """Test master state property.""" - config = validate_config(self.config_children_only) - - ump = universal.UniversalMediaPlayer(self.hass, **config) - - assert ump.master_state is None - - def test_master_state_with_attrs(self): - """Test master state property.""" - config = validate_config(self.config_children_and_attr) - - ump = universal.UniversalMediaPlayer(self.hass, **config) - - assert ump.master_state == STATE_OFF - self.hass.states.set(self.mock_state_switch_id, STATE_ON) - assert ump.master_state == STATE_ON - - def test_master_state_with_bad_attrs(self): - """Test master state property.""" - config = copy(self.config_children_and_attr) - config["attributes"]["state"] = "bad.entity_id" - config = validate_config(config) - - ump = universal.UniversalMediaPlayer(self.hass, **config) - - assert ump.master_state == STATE_OFF - - def test_active_child_state(self): - """Test active child state property.""" - config = validate_config(self.config_children_only) - - ump = universal.UniversalMediaPlayer(self.hass, **config) - ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"]) - asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() - - assert ump._child_state is None - - self.mock_mp_1._state = STATE_PLAYING - self.mock_mp_1.schedule_update_ha_state() - self.hass.block_till_done() - asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() - assert self.mock_mp_1.entity_id == ump._child_state.entity_id - - self.mock_mp_2._state = STATE_PLAYING - self.mock_mp_2.schedule_update_ha_state() - self.hass.block_till_done() - asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() - assert self.mock_mp_1.entity_id == ump._child_state.entity_id - - self.mock_mp_1._state = STATE_OFF - self.mock_mp_1.schedule_update_ha_state() - self.hass.block_till_done() - asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() - assert self.mock_mp_2.entity_id == ump._child_state.entity_id - - def test_name(self): - """Test name property.""" - config = validate_config(self.config_children_only) - - ump = universal.UniversalMediaPlayer(self.hass, **config) - - assert config["name"] == ump.name - - def test_polling(self): - """Test should_poll property.""" - config = validate_config(self.config_children_only) - - ump = universal.UniversalMediaPlayer(self.hass, **config) - - assert ump.should_poll is False - - def test_state_children_only(self): - """Test media player state with only children.""" - config = validate_config(self.config_children_only) - - ump = universal.UniversalMediaPlayer(self.hass, **config) - ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"]) - asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() - - assert ump.state, STATE_OFF - - self.mock_mp_1._state = STATE_PLAYING - self.mock_mp_1.schedule_update_ha_state() - self.hass.block_till_done() - asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() - assert ump.state == STATE_PLAYING +@pytest.fixture +async def mock_states(hass): + """Set mock states used in tests.""" + result = Mock() - def test_state_with_children_and_attrs(self): - """Test media player with children and master state.""" - config = validate_config(self.config_children_and_attr) + result.mock_mp_1 = MockMediaPlayer(hass, "mock1") + result.mock_mp_1.async_schedule_update_ha_state() - ump = universal.UniversalMediaPlayer(self.hass, **config) - ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"]) - asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() + result.mock_mp_2 = MockMediaPlayer(hass, "mock2") + result.mock_mp_2.async_schedule_update_ha_state() - assert ump.state == STATE_OFF + await hass.async_block_till_done() - self.hass.states.set(self.mock_state_switch_id, STATE_ON) - asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() - assert ump.state == STATE_ON + result.mock_mute_switch_id = switch.ENTITY_ID_FORMAT.format("mute") + hass.states.async_set(result.mock_mute_switch_id, STATE_OFF) - self.mock_mp_1._state = STATE_PLAYING - self.mock_mp_1.schedule_update_ha_state() - self.hass.block_till_done() - asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() - assert ump.state == STATE_PLAYING + result.mock_state_switch_id = switch.ENTITY_ID_FORMAT.format("state") + hass.states.async_set(result.mock_state_switch_id, STATE_OFF) - self.hass.states.set(self.mock_state_switch_id, STATE_OFF) - asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() - assert ump.state == STATE_OFF + result.mock_volume_id = f"{input_number.DOMAIN}.volume_level" + hass.states.async_set(result.mock_volume_id, 0) - def test_volume_level(self): - """Test volume level property.""" - config = validate_config(self.config_children_only) + result.mock_source_list_id = f"{input_select.DOMAIN}.source_list" + hass.states.async_set(result.mock_source_list_id, ["dvd", "htpc"]) - ump = universal.UniversalMediaPlayer(self.hass, **config) - ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"]) - asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() + result.mock_source_id = f"{input_select.DOMAIN}.source" + hass.states.async_set(result.mock_source_id, "dvd") - assert ump.volume_level is None + result.mock_sound_mode_list_id = f"{input_select.DOMAIN}.sound_mode_list" + hass.states.async_set(result.mock_sound_mode_list_id, ["music", "movie"]) - self.mock_mp_1._state = STATE_PLAYING - self.mock_mp_1.schedule_update_ha_state() - self.hass.block_till_done() - asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() - assert ump.volume_level == 0 + result.mock_sound_mode_id = f"{input_select.DOMAIN}.sound_mode" + hass.states.async_set(result.mock_sound_mode_id, "music") - self.mock_mp_1._volume_level = 1 - self.mock_mp_1.schedule_update_ha_state() - self.hass.block_till_done() - asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() - assert ump.volume_level == 1 + result.mock_shuffle_switch_id = switch.ENTITY_ID_FORMAT.format("shuffle") + hass.states.async_set(result.mock_shuffle_switch_id, STATE_OFF) - def test_media_image_url(self): - """Test media_image_url property.""" - test_url = "test_url" - config = validate_config(self.config_children_only) + result.mock_repeat_switch_id = switch.ENTITY_ID_FORMAT.format("repeat") + hass.states.async_set(result.mock_repeat_switch_id, STATE_OFF) - ump = universal.UniversalMediaPlayer(self.hass, **config) - ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"]) - asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() + return result - assert ump.media_image_url is None - self.mock_mp_1._state = STATE_PLAYING - self.mock_mp_1._media_image_url = test_url - self.mock_mp_1.schedule_update_ha_state() - self.hass.block_till_done() - 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 - # ensures ump passes through the same url without an additional proxy. - assert self.mock_mp_1.entity_picture == ump.entity_picture +@pytest.fixture +def config_children_and_attr(mock_states): + """Return configuration that references the mock states.""" + return { + "name": "test", + "platform": "universal", + "children": [ + media_player.ENTITY_ID_FORMAT.format("mock1"), + media_player.ENTITY_ID_FORMAT.format("mock2"), + ], + "attributes": { + "is_volume_muted": mock_states.mock_mute_switch_id, + "volume_level": mock_states.mock_volume_id, + "source": mock_states.mock_source_id, + "source_list": mock_states.mock_source_list_id, + "state": mock_states.mock_state_switch_id, + "shuffle": mock_states.mock_shuffle_switch_id, + "repeat": mock_states.mock_repeat_switch_id, + "sound_mode_list": mock_states.mock_sound_mode_list_id, + "sound_mode": mock_states.mock_sound_mode_id, + }, + } - def test_is_volume_muted_children_only(self): - """Test is volume muted property w/ children only.""" - config = validate_config(self.config_children_only) - ump = universal.UniversalMediaPlayer(self.hass, **config) - ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"]) - asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() +async def test_config_children_only(hass): + """Check config with only children.""" + config_start = copy(CONFIG_CHILDREN_ONLY) + del config_start["platform"] + config_start["commands"] = {} + config_start["attributes"] = {} - assert not ump.is_volume_muted + config = validate_config(CONFIG_CHILDREN_ONLY) + assert config_start == config - self.mock_mp_1._state = STATE_PLAYING - self.mock_mp_1.schedule_update_ha_state() - self.hass.block_till_done() - asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() - assert not ump.is_volume_muted - self.mock_mp_1._is_volume_muted = True - self.mock_mp_1.schedule_update_ha_state() - self.hass.block_till_done() - asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() - assert ump.is_volume_muted +async def test_config_children_and_attr(hass, config_children_and_attr): + """Check config with children and attributes.""" + config_start = copy(config_children_and_attr) + del config_start["platform"] + config_start["commands"] = {} - def test_sound_mode_list_children_and_attr(self): - """Test sound mode list property w/ children and attrs.""" - config = validate_config(self.config_children_and_attr) + config = validate_config(config_children_and_attr) + assert config_start == config - ump = universal.UniversalMediaPlayer(self.hass, **config) - assert ump.sound_mode_list == "['music', 'movie']" - - self.hass.states.set(self.mock_sound_mode_list_id, ["music", "movie", "game"]) - assert ump.sound_mode_list == "['music', 'movie', 'game']" +async def test_config_no_name(hass): + """Check config with no Name entry.""" + response = True + try: + validate_config({"platform": "universal"}) + except MultipleInvalid: + response = False + assert not response - def test_source_list_children_and_attr(self): - """Test source list property w/ children and attrs.""" - config = validate_config(self.config_children_and_attr) - ump = universal.UniversalMediaPlayer(self.hass, **config) - - assert ump.source_list == "['dvd', 'htpc']" +async def test_config_bad_children(hass): + """Check config with bad children entry.""" + config_no_children = {"name": "test", "platform": "universal"} + config_bad_children = {"name": "test", "children": {}, "platform": "universal"} - self.hass.states.set(self.mock_source_list_id, ["dvd", "htpc", "game"]) - assert ump.source_list == "['dvd', 'htpc', 'game']" - - def test_sound_mode_children_and_attr(self): - """Test sound modeproperty w/ children and attrs.""" - config = validate_config(self.config_children_and_attr) - - ump = universal.UniversalMediaPlayer(self.hass, **config) - - assert ump.sound_mode == "music" - - self.hass.states.set(self.mock_sound_mode_id, "movie") - assert ump.sound_mode == "movie" - - def test_source_children_and_attr(self): - """Test source property w/ children and attrs.""" - config = validate_config(self.config_children_and_attr) - - ump = universal.UniversalMediaPlayer(self.hass, **config) - - assert ump.source == "dvd" - - self.hass.states.set(self.mock_source_id, "htpc") - assert ump.source == "htpc" - - def test_volume_level_children_and_attr(self): - """Test volume level property w/ children and attrs.""" - config = validate_config(self.config_children_and_attr) - - ump = universal.UniversalMediaPlayer(self.hass, **config) - - assert ump.volume_level == 0 - - self.hass.states.set(self.mock_volume_id, 100) - assert ump.volume_level == 100 - - def test_is_volume_muted_children_and_attr(self): - """Test is volume muted property w/ children and attrs.""" - config = validate_config(self.config_children_and_attr) - - ump = universal.UniversalMediaPlayer(self.hass, **config) - - assert not ump.is_volume_muted - - self.hass.states.set(self.mock_mute_switch_id, STATE_ON) - assert ump.is_volume_muted - - def test_supported_features_children_only(self): - """Test supported media commands with only children.""" - config = validate_config(self.config_children_only) - - ump = universal.UniversalMediaPlayer(self.hass, **config) - ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"]) - asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() - - assert ump.supported_features == 0 - - self.mock_mp_1._supported_features = 512 - self.mock_mp_1._state = STATE_PLAYING - self.mock_mp_1.schedule_update_ha_state() - self.hass.block_till_done() - asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() - assert ump.supported_features == 512 - - def test_supported_features_children_and_cmds(self): - """Test supported media commands with children and attrs.""" - config = copy(self.config_children_and_attr) - excmd = {"service": "media_player.test", "data": {}} - config["commands"] = { - "turn_on": excmd, - "turn_off": excmd, - "volume_up": excmd, - "volume_down": excmd, - "volume_mute": excmd, - "volume_set": excmd, - "select_sound_mode": excmd, - "select_source": excmd, - "repeat_set": excmd, - "shuffle_set": excmd, - "media_play": excmd, - "media_pause": excmd, - "media_stop": excmd, - "media_next_track": excmd, - "media_previous_track": excmd, - "toggle": excmd, - "play_media": excmd, - "clear_playlist": excmd, - } - config = validate_config(config) - - ump = universal.UniversalMediaPlayer(self.hass, **config) - ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"]) - asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() - - self.mock_mp_1._state = STATE_PLAYING - self.mock_mp_1.schedule_update_ha_state() - self.hass.block_till_done() - asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() - - check_flags = ( - universal.SUPPORT_TURN_ON - | universal.SUPPORT_TURN_OFF - | universal.SUPPORT_VOLUME_STEP - | universal.SUPPORT_VOLUME_MUTE - | universal.SUPPORT_SELECT_SOUND_MODE - | universal.SUPPORT_SELECT_SOURCE - | universal.SUPPORT_REPEAT_SET - | universal.SUPPORT_SHUFFLE_SET - | universal.SUPPORT_VOLUME_SET - | universal.SUPPORT_PLAY - | universal.SUPPORT_PAUSE - | universal.SUPPORT_STOP - | universal.SUPPORT_NEXT_TRACK - | universal.SUPPORT_PREVIOUS_TRACK - | universal.SUPPORT_PLAY_MEDIA - | universal.SUPPORT_CLEAR_PLAYLIST + config_no_children = validate_config(config_no_children) + assert [] == config_no_children["children"] + + config_bad_children = validate_config(config_bad_children) + assert [] == config_bad_children["children"] + + +async def test_config_bad_commands(hass): + """Check config with bad commands entry.""" + config = {"name": "test", "platform": "universal"} + + config = validate_config(config) + assert {} == config["commands"] + + +async def test_config_bad_attributes(hass): + """Check config with bad attributes.""" + config = {"name": "test", "platform": "universal"} + + config = validate_config(config) + assert {} == config["attributes"] + + +async def test_config_bad_key(hass): + """Check config with bad key.""" + config = {"name": "test", "asdf": 5, "platform": "universal"} + + config = validate_config(config) + assert "asdf" not in config + + +async def test_platform_setup(hass): + """Test platform setup.""" + config = {"name": "test", "platform": "universal"} + bad_config = {"platform": "universal"} + entities = [] + + def add_entities(new_entities): + """Add devices to list.""" + for dev in new_entities: + entities.append(dev) + + setup_ok = True + try: + await universal.async_setup_platform( + hass, validate_config(bad_config), add_entities ) + except MultipleInvalid: + setup_ok = False + assert not setup_ok + assert len(entities) == 0 - assert check_flags == ump.supported_features + await universal.async_setup_platform(hass, validate_config(config), add_entities) + assert len(entities) == 1 + assert entities[0].name == "test" - def test_overrides(self): - """Test overrides.""" - config = copy(self.config_children_and_attr) - excmd = {"service": "test.override", "data": {}} - config["name"] = "overridden" - config["commands"] = { - "turn_on": excmd, - "turn_off": excmd, - "volume_up": excmd, - "volume_down": excmd, - "volume_mute": excmd, - "volume_set": excmd, - "select_sound_mode": excmd, - "select_source": excmd, - "repeat_set": excmd, - "shuffle_set": excmd, - "media_play": excmd, - "media_play_pause": excmd, - "media_pause": excmd, - "media_stop": excmd, - "media_next_track": excmd, - "media_previous_track": excmd, - "clear_playlist": excmd, - "play_media": excmd, - "toggle": excmd, - } - setup_component(self.hass, "media_player", {"media_player": config}) - service = mock_service(self.hass, "test", "override") - self.hass.services.call( - "media_player", - "turn_on", - service_data={"entity_id": "media_player.overridden"}, - blocking=True, - ) - assert len(service) == 1 - self.hass.services.call( - "media_player", - "turn_off", - service_data={"entity_id": "media_player.overridden"}, - blocking=True, - ) - assert len(service) == 2 - self.hass.services.call( - "media_player", - "volume_up", - service_data={"entity_id": "media_player.overridden"}, - blocking=True, - ) - assert len(service) == 3 - self.hass.services.call( - "media_player", - "volume_down", - service_data={"entity_id": "media_player.overridden"}, - blocking=True, - ) - assert len(service) == 4 - self.hass.services.call( - "media_player", - "volume_mute", - service_data={ - "entity_id": "media_player.overridden", - "is_volume_muted": True, - }, - blocking=True, - ) - assert len(service) == 5 - self.hass.services.call( - "media_player", - "volume_set", - service_data={"entity_id": "media_player.overridden", "volume_level": 1}, - blocking=True, - ) - assert len(service) == 6 - self.hass.services.call( - "media_player", - "select_sound_mode", - service_data={ - "entity_id": "media_player.overridden", - "sound_mode": "music", - }, - blocking=True, - ) - assert len(service) == 7 - self.hass.services.call( - "media_player", - "select_source", - service_data={"entity_id": "media_player.overridden", "source": "video1"}, - blocking=True, - ) - assert len(service) == 8 - self.hass.services.call( - "media_player", - "repeat_set", - service_data={"entity_id": "media_player.overridden", "repeat": "all"}, - blocking=True, - ) - assert len(service) == 9 - self.hass.services.call( - "media_player", - "shuffle_set", - service_data={"entity_id": "media_player.overridden", "shuffle": True}, - blocking=True, - ) - assert len(service) == 10 - self.hass.services.call( - "media_player", - "media_play", - service_data={"entity_id": "media_player.overridden"}, - blocking=True, - ) - assert len(service) == 11 - self.hass.services.call( - "media_player", - "media_pause", - service_data={"entity_id": "media_player.overridden"}, - blocking=True, - ) - assert len(service) == 12 - self.hass.services.call( - "media_player", - "media_stop", - service_data={"entity_id": "media_player.overridden"}, - blocking=True, - ) - assert len(service) == 13 - self.hass.services.call( - "media_player", - "media_next_track", - service_data={"entity_id": "media_player.overridden"}, - blocking=True, - ) - assert len(service) == 14 - self.hass.services.call( - "media_player", - "media_previous_track", - service_data={"entity_id": "media_player.overridden"}, - blocking=True, - ) - assert len(service) == 15 - self.hass.services.call( - "media_player", - "clear_playlist", - service_data={"entity_id": "media_player.overridden"}, - blocking=True, - ) - assert len(service) == 16 - self.hass.services.call( - "media_player", - "media_play_pause", - service_data={"entity_id": "media_player.overridden"}, - blocking=True, - ) - assert len(service) == 17 - self.hass.services.call( - "media_player", - "play_media", - service_data={ - "entity_id": "media_player.overridden", - "media_content_id": 1, - "media_content_type": "channel", - }, - blocking=True, - ) - assert len(service) == 18 - self.hass.services.call( - "media_player", - "toggle", - service_data={"entity_id": "media_player.overridden"}, - blocking=True, - ) - assert len(service) == 19 +async def test_master_state(hass): + """Test master state property.""" + config = validate_config(CONFIG_CHILDREN_ONLY) - def test_supported_features_play_pause(self): - """Test supported media commands with play_pause function.""" - config = copy(self.config_children_and_attr) - excmd = {"service": "media_player.test", "data": {"entity_id": "test"}} - config["commands"] = {"media_play_pause": excmd} - config = validate_config(config) + ump = universal.UniversalMediaPlayer(hass, **config) - ump = universal.UniversalMediaPlayer(self.hass, **config) - ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"]) - asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() + assert ump.master_state is None - self.mock_mp_1._state = STATE_PLAYING - self.mock_mp_1.schedule_update_ha_state() - self.hass.block_till_done() - asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() - check_flags = universal.SUPPORT_PLAY | universal.SUPPORT_PAUSE +async def test_master_state_with_attrs(hass, config_children_and_attr, mock_states): + """Test master state property.""" + config = validate_config(config_children_and_attr) - assert check_flags == ump.supported_features + ump = universal.UniversalMediaPlayer(hass, **config) - def test_service_call_no_active_child(self): - """Test a service call to children with no active child.""" - config = validate_config(self.config_children_and_attr) + assert ump.master_state == STATE_OFF + hass.states.async_set(mock_states.mock_state_switch_id, STATE_ON) + assert ump.master_state == STATE_ON - ump = universal.UniversalMediaPlayer(self.hass, **config) - ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"]) - asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() - self.mock_mp_1._state = STATE_OFF - self.mock_mp_1.schedule_update_ha_state() - self.mock_mp_2._state = STATE_OFF - self.mock_mp_2.schedule_update_ha_state() - self.hass.block_till_done() - asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() +async def test_master_state_with_bad_attrs(hass, config_children_and_attr): + """Test master state property.""" + config = copy(config_children_and_attr) + config["attributes"]["state"] = "bad.entity_id" + config = validate_config(config) - asyncio.run_coroutine_threadsafe(ump.async_turn_off(), self.hass.loop).result() - assert len(self.mock_mp_1.service_calls["turn_off"]) == 0 - assert len(self.mock_mp_2.service_calls["turn_off"]) == 0 + ump = universal.UniversalMediaPlayer(hass, **config) - def test_service_call_to_child(self): - """Test service calls that should be routed to a child.""" - config = validate_config(self.config_children_only) + assert ump.master_state == STATE_OFF - ump = universal.UniversalMediaPlayer(self.hass, **config) - ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"]) - asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() - self.mock_mp_2._state = STATE_PLAYING - self.mock_mp_2.schedule_update_ha_state() - self.hass.block_till_done() - asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() +async def test_active_child_state(hass, mock_states): + """Test active child state property.""" + config = validate_config(CONFIG_CHILDREN_ONLY) - asyncio.run_coroutine_threadsafe(ump.async_turn_off(), self.hass.loop).result() - assert len(self.mock_mp_2.service_calls["turn_off"]) == 1 + ump = universal.UniversalMediaPlayer(hass, **config) + ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"]) + await ump.async_update() - asyncio.run_coroutine_threadsafe(ump.async_turn_on(), self.hass.loop).result() - assert len(self.mock_mp_2.service_calls["turn_on"]) == 1 + assert ump._child_state is None - asyncio.run_coroutine_threadsafe( - ump.async_mute_volume(True), self.hass.loop - ).result() - assert len(self.mock_mp_2.service_calls["mute_volume"]) == 1 + mock_states.mock_mp_1._state = STATE_PLAYING + mock_states.mock_mp_1.async_schedule_update_ha_state() + await hass.async_block_till_done() + await ump.async_update() + assert mock_states.mock_mp_1.entity_id == ump._child_state.entity_id - asyncio.run_coroutine_threadsafe( - ump.async_set_volume_level(0.5), self.hass.loop - ).result() - assert len(self.mock_mp_2.service_calls["set_volume_level"]) == 1 + mock_states.mock_mp_2._state = STATE_PLAYING + mock_states.mock_mp_2.async_schedule_update_ha_state() + await hass.async_block_till_done() + await ump.async_update() + assert mock_states.mock_mp_1.entity_id == ump._child_state.entity_id - asyncio.run_coroutine_threadsafe( - ump.async_media_play(), self.hass.loop - ).result() - assert len(self.mock_mp_2.service_calls["media_play"]) == 1 + mock_states.mock_mp_1._state = STATE_OFF + mock_states.mock_mp_1.async_schedule_update_ha_state() + await hass.async_block_till_done() + await ump.async_update() + assert mock_states.mock_mp_2.entity_id == ump._child_state.entity_id - asyncio.run_coroutine_threadsafe( - ump.async_media_pause(), self.hass.loop - ).result() - assert len(self.mock_mp_2.service_calls["media_pause"]) == 1 - asyncio.run_coroutine_threadsafe( - ump.async_media_stop(), self.hass.loop - ).result() - assert len(self.mock_mp_2.service_calls["media_stop"]) == 1 +async def test_name(hass): + """Test name property.""" + config = validate_config(CONFIG_CHILDREN_ONLY) - asyncio.run_coroutine_threadsafe( - ump.async_media_previous_track(), self.hass.loop - ).result() - assert len(self.mock_mp_2.service_calls["media_previous_track"]) == 1 + ump = universal.UniversalMediaPlayer(hass, **config) - asyncio.run_coroutine_threadsafe( - ump.async_media_next_track(), self.hass.loop - ).result() - assert len(self.mock_mp_2.service_calls["media_next_track"]) == 1 + assert config["name"] == ump.name - asyncio.run_coroutine_threadsafe( - ump.async_media_seek(100), self.hass.loop - ).result() - assert len(self.mock_mp_2.service_calls["media_seek"]) == 1 - asyncio.run_coroutine_threadsafe( - ump.async_play_media("movie", "batman"), self.hass.loop - ).result() - assert len(self.mock_mp_2.service_calls["play_media"]) == 1 +async def test_polling(hass): + """Test should_poll property.""" + config = validate_config(CONFIG_CHILDREN_ONLY) - asyncio.run_coroutine_threadsafe(ump.async_volume_up(), self.hass.loop).result() - assert len(self.mock_mp_2.service_calls["volume_up"]) == 1 + ump = universal.UniversalMediaPlayer(hass, **config) - asyncio.run_coroutine_threadsafe( - ump.async_volume_down(), self.hass.loop - ).result() - assert len(self.mock_mp_2.service_calls["volume_down"]) == 1 + assert ump.should_poll is False - asyncio.run_coroutine_threadsafe( - ump.async_media_play_pause(), self.hass.loop - ).result() - assert len(self.mock_mp_2.service_calls["media_play_pause"]) == 1 - asyncio.run_coroutine_threadsafe( - ump.async_select_sound_mode("music"), self.hass.loop - ).result() - assert len(self.mock_mp_2.service_calls["select_sound_mode"]) == 1 +async def test_state_children_only(hass, mock_states): + """Test media player state with only children.""" + config = validate_config(CONFIG_CHILDREN_ONLY) - asyncio.run_coroutine_threadsafe( - ump.async_select_source("dvd"), self.hass.loop - ).result() - assert len(self.mock_mp_2.service_calls["select_source"]) == 1 + ump = universal.UniversalMediaPlayer(hass, **config) + ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"]) + await ump.async_update() - asyncio.run_coroutine_threadsafe( - ump.async_clear_playlist(), self.hass.loop - ).result() - assert len(self.mock_mp_2.service_calls["clear_playlist"]) == 1 + assert ump.state, STATE_OFF - asyncio.run_coroutine_threadsafe( - ump.async_set_repeat(True), self.hass.loop - ).result() - assert len(self.mock_mp_2.service_calls["repeat_set"]) == 1 + mock_states.mock_mp_1._state = STATE_PLAYING + mock_states.mock_mp_1.async_schedule_update_ha_state() + await hass.async_block_till_done() + await ump.async_update() + assert ump.state == STATE_PLAYING - asyncio.run_coroutine_threadsafe( - ump.async_set_shuffle(True), self.hass.loop - ).result() - assert len(self.mock_mp_2.service_calls["shuffle_set"]) == 1 - asyncio.run_coroutine_threadsafe(ump.async_toggle(), self.hass.loop).result() - # Delegate to turn_off - assert len(self.mock_mp_2.service_calls["turn_off"]) == 2 +async def test_state_with_children_and_attrs( + hass, config_children_and_attr, mock_states +): + """Test media player with children and master state.""" + config = validate_config(config_children_and_attr) - def test_service_call_to_command(self): - """Test service call to command.""" - config = copy(self.config_children_only) - config["commands"] = {"turn_off": {"service": "test.turn_off", "data": {}}} - config = validate_config(config) + ump = universal.UniversalMediaPlayer(hass, **config) + ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"]) + await ump.async_update() - service = mock_service(self.hass, "test", "turn_off") + assert ump.state == STATE_OFF - ump = universal.UniversalMediaPlayer(self.hass, **config) - ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"]) - asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() + hass.states.async_set(mock_states.mock_state_switch_id, STATE_ON) + await ump.async_update() + assert ump.state == STATE_ON - self.mock_mp_2._state = STATE_PLAYING - self.mock_mp_2.schedule_update_ha_state() - self.hass.block_till_done() - asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result() + mock_states.mock_mp_1._state = STATE_PLAYING + mock_states.mock_mp_1.async_schedule_update_ha_state() + await hass.async_block_till_done() + await ump.async_update() + assert ump.state == STATE_PLAYING - asyncio.run_coroutine_threadsafe(ump.async_turn_off(), self.hass.loop).result() - assert len(service) == 1 + hass.states.async_set(mock_states.mock_state_switch_id, STATE_OFF) + await ump.async_update() + assert ump.state == STATE_OFF + + +async def test_volume_level(hass, mock_states): + """Test volume level property.""" + config = validate_config(CONFIG_CHILDREN_ONLY) + + ump = universal.UniversalMediaPlayer(hass, **config) + ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"]) + await ump.async_update() + + assert ump.volume_level is None + + mock_states.mock_mp_1._state = STATE_PLAYING + mock_states.mock_mp_1.async_schedule_update_ha_state() + await hass.async_block_till_done() + await ump.async_update() + assert ump.volume_level == 0 + + mock_states.mock_mp_1._volume_level = 1 + mock_states.mock_mp_1.async_schedule_update_ha_state() + await hass.async_block_till_done() + await ump.async_update() + assert ump.volume_level == 1 + + +async def test_media_image_url(hass, mock_states): + """Test media_image_url property.""" + test_url = "test_url" + config = validate_config(CONFIG_CHILDREN_ONLY) + + ump = universal.UniversalMediaPlayer(hass, **config) + ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"]) + await ump.async_update() + + assert ump.media_image_url is None + + mock_states.mock_mp_1._state = STATE_PLAYING + mock_states.mock_mp_1._media_image_url = test_url + mock_states.mock_mp_1.async_schedule_update_ha_state() + await hass.async_block_till_done() + await ump.async_update() + # 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. + assert mock_states.mock_mp_1.entity_picture == ump.entity_picture + + +async def test_is_volume_muted_children_only(hass, mock_states): + """Test is volume muted property w/ children only.""" + config = validate_config(CONFIG_CHILDREN_ONLY) + + ump = universal.UniversalMediaPlayer(hass, **config) + ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"]) + await ump.async_update() + + assert not ump.is_volume_muted + + mock_states.mock_mp_1._state = STATE_PLAYING + mock_states.mock_mp_1.async_schedule_update_ha_state() + await hass.async_block_till_done() + await ump.async_update() + assert not ump.is_volume_muted + + mock_states.mock_mp_1._is_volume_muted = True + mock_states.mock_mp_1.async_schedule_update_ha_state() + await hass.async_block_till_done() + await ump.async_update() + assert ump.is_volume_muted + + +async def test_sound_mode_list_children_and_attr( + hass, config_children_and_attr, mock_states +): + """Test sound mode list property w/ children and attrs.""" + config = validate_config(config_children_and_attr) + + ump = universal.UniversalMediaPlayer(hass, **config) + + assert ump.sound_mode_list == "['music', 'movie']" + + hass.states.async_set( + mock_states.mock_sound_mode_list_id, ["music", "movie", "game"] + ) + assert ump.sound_mode_list == "['music', 'movie', 'game']" + + +async def test_source_list_children_and_attr( + hass, config_children_and_attr, mock_states +): + """Test source list property w/ children and attrs.""" + config = validate_config(config_children_and_attr) + + ump = universal.UniversalMediaPlayer(hass, **config) + + assert ump.source_list == "['dvd', 'htpc']" + + hass.states.async_set(mock_states.mock_source_list_id, ["dvd", "htpc", "game"]) + assert ump.source_list == "['dvd', 'htpc', 'game']" + + +async def test_sound_mode_children_and_attr( + hass, config_children_and_attr, mock_states +): + """Test sound modeproperty w/ children and attrs.""" + config = validate_config(config_children_and_attr) + + ump = universal.UniversalMediaPlayer(hass, **config) + + assert ump.sound_mode == "music" + + hass.states.async_set(mock_states.mock_sound_mode_id, "movie") + assert ump.sound_mode == "movie" + + +async def test_source_children_and_attr(hass, config_children_and_attr, mock_states): + """Test source property w/ children and attrs.""" + config = validate_config(config_children_and_attr) + + ump = universal.UniversalMediaPlayer(hass, **config) + + assert ump.source == "dvd" + + hass.states.async_set(mock_states.mock_source_id, "htpc") + assert ump.source == "htpc" + + +async def test_volume_level_children_and_attr( + hass, config_children_and_attr, mock_states +): + """Test volume level property w/ children and attrs.""" + config = validate_config(config_children_and_attr) + + ump = universal.UniversalMediaPlayer(hass, **config) + + assert ump.volume_level == 0 + + hass.states.async_set(mock_states.mock_volume_id, 100) + assert ump.volume_level == 100 + + +async def test_is_volume_muted_children_and_attr( + hass, config_children_and_attr, mock_states +): + """Test is volume muted property w/ children and attrs.""" + config = validate_config(config_children_and_attr) + + ump = universal.UniversalMediaPlayer(hass, **config) + + assert not ump.is_volume_muted + + hass.states.async_set(mock_states.mock_mute_switch_id, STATE_ON) + assert ump.is_volume_muted + + +async def test_supported_features_children_only(hass, mock_states): + """Test supported media commands with only children.""" + config = validate_config(CONFIG_CHILDREN_ONLY) + + ump = universal.UniversalMediaPlayer(hass, **config) + ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"]) + await ump.async_update() + + assert ump.supported_features == 0 + + mock_states.mock_mp_1._supported_features = 512 + mock_states.mock_mp_1._state = STATE_PLAYING + mock_states.mock_mp_1.async_schedule_update_ha_state() + await hass.async_block_till_done() + await ump.async_update() + assert ump.supported_features == 512 + + +async def test_supported_features_children_and_cmds( + hass, config_children_and_attr, mock_states +): + """Test supported media commands with children and attrs.""" + config = copy(config_children_and_attr) + excmd = {"service": "media_player.test", "data": {}} + config["commands"] = { + "turn_on": excmd, + "turn_off": excmd, + "volume_up": excmd, + "volume_down": excmd, + "volume_mute": excmd, + "volume_set": excmd, + "select_sound_mode": excmd, + "select_source": excmd, + "repeat_set": excmd, + "shuffle_set": excmd, + "media_play": excmd, + "media_pause": excmd, + "media_stop": excmd, + "media_next_track": excmd, + "media_previous_track": excmd, + "toggle": excmd, + "play_media": excmd, + "clear_playlist": excmd, + } + config = validate_config(config) + + ump = universal.UniversalMediaPlayer(hass, **config) + ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"]) + await ump.async_update() + + mock_states.mock_mp_1._state = STATE_PLAYING + mock_states.mock_mp_1.async_schedule_update_ha_state() + await hass.async_block_till_done() + await ump.async_update() + + check_flags = ( + universal.SUPPORT_TURN_ON + | universal.SUPPORT_TURN_OFF + | universal.SUPPORT_VOLUME_STEP + | universal.SUPPORT_VOLUME_MUTE + | universal.SUPPORT_SELECT_SOUND_MODE + | universal.SUPPORT_SELECT_SOURCE + | universal.SUPPORT_REPEAT_SET + | universal.SUPPORT_SHUFFLE_SET + | universal.SUPPORT_VOLUME_SET + | universal.SUPPORT_PLAY + | universal.SUPPORT_PAUSE + | universal.SUPPORT_STOP + | universal.SUPPORT_NEXT_TRACK + | universal.SUPPORT_PREVIOUS_TRACK + | universal.SUPPORT_PLAY_MEDIA + | universal.SUPPORT_CLEAR_PLAYLIST + ) + + assert check_flags == ump.supported_features + + +async def test_overrides(hass, config_children_and_attr): + """Test overrides.""" + config = copy(config_children_and_attr) + excmd = {"service": "test.override", "data": {}} + config["name"] = "overridden" + config["commands"] = { + "turn_on": excmd, + "turn_off": excmd, + "volume_up": excmd, + "volume_down": excmd, + "volume_mute": excmd, + "volume_set": excmd, + "select_sound_mode": excmd, + "select_source": excmd, + "repeat_set": excmd, + "shuffle_set": excmd, + "media_play": excmd, + "media_play_pause": excmd, + "media_pause": excmd, + "media_stop": excmd, + "media_next_track": excmd, + "media_previous_track": excmd, + "clear_playlist": excmd, + "play_media": excmd, + "toggle": excmd, + } + await async_setup_component(hass, "media_player", {"media_player": config}) + await hass.async_block_till_done() + + service = async_mock_service(hass, "test", "override") + await hass.services.async_call( + "media_player", + "turn_on", + service_data={"entity_id": "media_player.overridden"}, + blocking=True, + ) + assert len(service) == 1 + await hass.services.async_call( + "media_player", + "turn_off", + service_data={"entity_id": "media_player.overridden"}, + blocking=True, + ) + assert len(service) == 2 + await hass.services.async_call( + "media_player", + "volume_up", + service_data={"entity_id": "media_player.overridden"}, + blocking=True, + ) + assert len(service) == 3 + await hass.services.async_call( + "media_player", + "volume_down", + service_data={"entity_id": "media_player.overridden"}, + blocking=True, + ) + assert len(service) == 4 + await hass.services.async_call( + "media_player", + "volume_mute", + service_data={ + "entity_id": "media_player.overridden", + "is_volume_muted": True, + }, + blocking=True, + ) + assert len(service) == 5 + await hass.services.async_call( + "media_player", + "volume_set", + service_data={"entity_id": "media_player.overridden", "volume_level": 1}, + blocking=True, + ) + assert len(service) == 6 + await hass.services.async_call( + "media_player", + "select_sound_mode", + service_data={ + "entity_id": "media_player.overridden", + "sound_mode": "music", + }, + blocking=True, + ) + assert len(service) == 7 + await hass.services.async_call( + "media_player", + "select_source", + service_data={"entity_id": "media_player.overridden", "source": "video1"}, + blocking=True, + ) + assert len(service) == 8 + await hass.services.async_call( + "media_player", + "repeat_set", + service_data={"entity_id": "media_player.overridden", "repeat": "all"}, + blocking=True, + ) + assert len(service) == 9 + await hass.services.async_call( + "media_player", + "shuffle_set", + service_data={"entity_id": "media_player.overridden", "shuffle": True}, + blocking=True, + ) + assert len(service) == 10 + await hass.services.async_call( + "media_player", + "media_play", + service_data={"entity_id": "media_player.overridden"}, + blocking=True, + ) + assert len(service) == 11 + await hass.services.async_call( + "media_player", + "media_pause", + service_data={"entity_id": "media_player.overridden"}, + blocking=True, + ) + assert len(service) == 12 + await hass.services.async_call( + "media_player", + "media_stop", + service_data={"entity_id": "media_player.overridden"}, + blocking=True, + ) + assert len(service) == 13 + await hass.services.async_call( + "media_player", + "media_next_track", + service_data={"entity_id": "media_player.overridden"}, + blocking=True, + ) + assert len(service) == 14 + await hass.services.async_call( + "media_player", + "media_previous_track", + service_data={"entity_id": "media_player.overridden"}, + blocking=True, + ) + assert len(service) == 15 + await hass.services.async_call( + "media_player", + "clear_playlist", + service_data={"entity_id": "media_player.overridden"}, + blocking=True, + ) + assert len(service) == 16 + await hass.services.async_call( + "media_player", + "media_play_pause", + service_data={"entity_id": "media_player.overridden"}, + blocking=True, + ) + assert len(service) == 17 + await hass.services.async_call( + "media_player", + "play_media", + service_data={ + "entity_id": "media_player.overridden", + "media_content_id": 1, + "media_content_type": "channel", + }, + blocking=True, + ) + assert len(service) == 18 + await hass.services.async_call( + "media_player", + "toggle", + service_data={"entity_id": "media_player.overridden"}, + blocking=True, + ) + assert len(service) == 19 + + +async def test_supported_features_play_pause( + hass, config_children_and_attr, mock_states +): + """Test supported media commands with play_pause function.""" + config = copy(config_children_and_attr) + excmd = {"service": "media_player.test", "data": {"entity_id": "test"}} + config["commands"] = {"media_play_pause": excmd} + config = validate_config(config) + + ump = universal.UniversalMediaPlayer(hass, **config) + ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"]) + await ump.async_update() + + mock_states.mock_mp_1._state = STATE_PLAYING + mock_states.mock_mp_1.async_schedule_update_ha_state() + await hass.async_block_till_done() + await ump.async_update() + + check_flags = universal.SUPPORT_PLAY | universal.SUPPORT_PAUSE + + assert check_flags == ump.supported_features + + +async def test_service_call_no_active_child( + hass, config_children_and_attr, mock_states +): + """Test a service call to children with no active child.""" + config = validate_config(config_children_and_attr) + + ump = universal.UniversalMediaPlayer(hass, **config) + ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"]) + await ump.async_update() + + mock_states.mock_mp_1._state = STATE_OFF + mock_states.mock_mp_1.async_schedule_update_ha_state() + mock_states.mock_mp_2._state = STATE_OFF + mock_states.mock_mp_2.async_schedule_update_ha_state() + await hass.async_block_till_done() + await ump.async_update() + + await ump.async_turn_off() + assert len(mock_states.mock_mp_1.service_calls["turn_off"]) == 0 + assert len(mock_states.mock_mp_2.service_calls["turn_off"]) == 0 + + +async def test_service_call_to_child(hass, mock_states): + """Test service calls that should be routed to a child.""" + config = validate_config(CONFIG_CHILDREN_ONLY) + + ump = universal.UniversalMediaPlayer(hass, **config) + ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"]) + await ump.async_update() + + mock_states.mock_mp_2._state = STATE_PLAYING + mock_states.mock_mp_2.async_schedule_update_ha_state() + await hass.async_block_till_done() + await ump.async_update() + + await ump.async_turn_off() + assert len(mock_states.mock_mp_2.service_calls["turn_off"]) == 1 + + await ump.async_turn_on() + assert len(mock_states.mock_mp_2.service_calls["turn_on"]) == 1 + + await ump.async_mute_volume(True) + assert len(mock_states.mock_mp_2.service_calls["mute_volume"]) == 1 + + await ump.async_set_volume_level(0.5) + assert len(mock_states.mock_mp_2.service_calls["set_volume_level"]) == 1 + + await ump.async_media_play() + assert len(mock_states.mock_mp_2.service_calls["media_play"]) == 1 + + await ump.async_media_pause() + assert len(mock_states.mock_mp_2.service_calls["media_pause"]) == 1 + + await ump.async_media_stop() + assert len(mock_states.mock_mp_2.service_calls["media_stop"]) == 1 + + await ump.async_media_previous_track() + assert len(mock_states.mock_mp_2.service_calls["media_previous_track"]) == 1 + + await ump.async_media_next_track() + assert len(mock_states.mock_mp_2.service_calls["media_next_track"]) == 1 + + await ump.async_media_seek(100) + assert len(mock_states.mock_mp_2.service_calls["media_seek"]) == 1 + + await ump.async_play_media("movie", "batman") + assert len(mock_states.mock_mp_2.service_calls["play_media"]) == 1 + + await ump.async_volume_up() + assert len(mock_states.mock_mp_2.service_calls["volume_up"]) == 1 + + await ump.async_volume_down() + assert len(mock_states.mock_mp_2.service_calls["volume_down"]) == 1 + + await ump.async_media_play_pause() + assert len(mock_states.mock_mp_2.service_calls["media_play_pause"]) == 1 + + await ump.async_select_sound_mode("music") + assert len(mock_states.mock_mp_2.service_calls["select_sound_mode"]) == 1 + + await ump.async_select_source("dvd") + assert len(mock_states.mock_mp_2.service_calls["select_source"]) == 1 + + await ump.async_clear_playlist() + assert len(mock_states.mock_mp_2.service_calls["clear_playlist"]) == 1 + + await ump.async_set_repeat(True) + assert len(mock_states.mock_mp_2.service_calls["repeat_set"]) == 1 + + await ump.async_set_shuffle(True) + assert len(mock_states.mock_mp_2.service_calls["shuffle_set"]) == 1 + + await ump.async_toggle() + # Delegate to turn_off + assert len(mock_states.mock_mp_2.service_calls["turn_off"]) == 2 + + +async def test_service_call_to_command(hass, mock_states): + """Test service call to command.""" + config = copy(CONFIG_CHILDREN_ONLY) + config["commands"] = {"turn_off": {"service": "test.turn_off", "data": {}}} + config = validate_config(config) + + service = async_mock_service(hass, "test", "turn_off") + + ump = universal.UniversalMediaPlayer(hass, **config) + ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"]) + await ump.async_update() + + mock_states.mock_mp_2._state = STATE_PLAYING + mock_states.mock_mp_2.async_schedule_update_ha_state() + await hass.async_block_till_done() + await ump.async_update() + + await ump.async_turn_off() + assert len(service) == 1 async def test_state_template(hass):