diff --git a/tests/components/cast/conftest.py b/tests/components/cast/conftest.py index 4c17cf74af9..901f7fb74fb 100644 --- a/tests/components/cast/conftest.py +++ b/tests/components/cast/conftest.py @@ -17,13 +17,7 @@ def dial_mock(): @pytest.fixture() def castbrowser_mock(): """Mock pychromecast CastBrowser.""" - return MagicMock() - - -@pytest.fixture() -def castbrowser_constructor_mock(): - """Mock pychromecast CastBrowser constructor.""" - return MagicMock() + return MagicMock(spec=pychromecast.discovery.CastBrowser) @pytest.fixture() @@ -32,37 +26,29 @@ def mz_mock(): return MagicMock() -@pytest.fixture() -def pycast_mock(castbrowser_mock, castbrowser_constructor_mock): - """Mock pychromecast.""" - pycast_mock = MagicMock() - pycast_mock.IDLE_APP_ID = pychromecast.IDLE_APP_ID - pycast_mock.IGNORE_CEC = [] - pycast_mock.const = pychromecast.const - pycast_mock.discovery.CastBrowser = castbrowser_constructor_mock - pycast_mock.discovery.CastBrowser.return_value = castbrowser_mock - pycast_mock.discovery.AbstractCastListener = ( - pychromecast.discovery.AbstractCastListener - ) - return pycast_mock - - @pytest.fixture() def quick_play_mock(): """Mock pychromecast quick_play.""" return MagicMock() +@pytest.fixture() +def get_chromecast_mock(): + """Mock pychromecast get_chromecast_from_cast_info.""" + return MagicMock() + + @pytest.fixture(autouse=True) -def cast_mock(dial_mock, mz_mock, pycast_mock, quick_play_mock): +def cast_mock( + dial_mock, mz_mock, quick_play_mock, castbrowser_mock, get_chromecast_mock +): """Mock pychromecast.""" + ignore_cec_orig = list(pychromecast.IGNORE_CEC) + with patch( - "homeassistant.components.cast.media_player.pychromecast", pycast_mock - ), patch( - "homeassistant.components.cast.discovery.pychromecast", pycast_mock - ), patch( - "homeassistant.components.cast.helpers.dial", dial_mock - ), patch( + "homeassistant.components.cast.discovery.pychromecast.discovery.CastBrowser", + castbrowser_mock, + ), patch("homeassistant.components.cast.helpers.dial", dial_mock), patch( "homeassistant.components.cast.media_player.MultizoneManager", return_value=mz_mock, ), patch( @@ -71,5 +57,10 @@ def cast_mock(dial_mock, mz_mock, pycast_mock, quick_play_mock): ), patch( "homeassistant.components.cast.media_player.quick_play", quick_play_mock, + ), patch( + "homeassistant.components.cast.media_player.pychromecast.get_chromecast_from_cast_info", + get_chromecast_mock, ): yield + + pychromecast.IGNORE_CEC = list(ignore_cec_orig) diff --git a/tests/components/cast/test_config_flow.py b/tests/components/cast/test_config_flow.py index 7c3fb774722..1ad89c7a8e5 100644 --- a/tests/components/cast/test_config_flow.py +++ b/tests/components/cast/test_config_flow.py @@ -245,7 +245,7 @@ async def test_option_flow(hass, parameter_data): assert dict(config_entry.data) == expected_data -async def test_known_hosts(hass, castbrowser_mock, castbrowser_constructor_mock): +async def test_known_hosts(hass, castbrowser_mock): """Test known hosts is passed to pychromecasts.""" result = await hass.config_entries.flow.async_init( "cast", context={"source": config_entries.SOURCE_USER} @@ -257,12 +257,9 @@ async def test_known_hosts(hass, castbrowser_mock, castbrowser_constructor_mock) await hass.async_block_till_done() config_entry = hass.config_entries.async_entries("cast")[0] - assert castbrowser_mock.start_discovery.call_count == 1 - castbrowser_constructor_mock.assert_called_once_with( - ANY, ANY, ["192.168.0.1", "192.168.0.2"] - ) + assert castbrowser_mock.return_value.start_discovery.call_count == 1 + castbrowser_mock.assert_called_once_with(ANY, ANY, ["192.168.0.1", "192.168.0.2"]) castbrowser_mock.reset_mock() - castbrowser_constructor_mock.reset_mock() result = await hass.config_entries.options.async_init(config_entry.entry_id) result = await hass.config_entries.options.async_configure( @@ -272,8 +269,8 @@ async def test_known_hosts(hass, castbrowser_mock, castbrowser_constructor_mock) await hass.async_block_till_done() - castbrowser_mock.start_discovery.assert_not_called() - castbrowser_constructor_mock.assert_not_called() - castbrowser_mock.host_browser.update_hosts.assert_called_once_with( + castbrowser_mock.return_value.start_discovery.assert_not_called() + castbrowser_mock.assert_not_called() + castbrowser_mock.return_value.host_browser.update_hosts.assert_called_once_with( ["192.168.0.11", "192.168.0.12"] ) diff --git a/tests/components/cast/test_media_player.py b/tests/components/cast/test_media_player.py index 6c63d3bcc2b..3690cb7a2e0 100644 --- a/tests/components/cast/test_media_player.py +++ b/tests/components/cast/test_media_player.py @@ -46,6 +46,13 @@ FakeUUID = UUID("57355bce-9364-4aa6-ac1e-eb849dccf9e2") FakeUUID2 = UUID("57355bce-9364-4aa6-ac1e-eb849dccf9e4") FakeGroupUUID = UUID("57355bce-9364-4aa6-ac1e-eb849dccf9e3") +FAKE_HOST_SERVICE = pychromecast.discovery.ServiceInfo( + pychromecast.const.SERVICE_TYPE_HOST, ("127.0.0.1", 8009) +) +FAKE_MDNS_SERVICE = pychromecast.discovery.ServiceInfo( + pychromecast.const.SERVICE_TYPE_MDNS, "the-service" +) + def get_fake_chromecast(info: ChromecastInfo): """Generate a Fake Chromecast object with the specified arguments.""" @@ -56,7 +63,7 @@ def get_fake_chromecast(info: ChromecastInfo): def get_fake_chromecast_info( - host="192.168.178.42", port=8009, uuid: UUID | None = FakeUUID + host="192.168.178.42", port=8009, service=None, uuid: UUID | None = FakeUUID ): """Generate a Fake ChromecastInfo with the specified arguments.""" @@ -81,10 +88,14 @@ def get_fake_chromecast_info( ) return super().__eq__(other) + if service is None: + service = pychromecast.discovery.ServiceInfo( + pychromecast.const.SERVICE_TYPE_HOST, (host, port) + ) return ExtendedChromecastInfo( host=host, port=port, - services={"the-service"}, + services={service}, uuid=uuid, model_name="Chromecast", friendly_name="Speaker", @@ -136,10 +147,12 @@ async def async_setup_cast_internal_discovery(hass, config=None): discovery_callback = cast_browser.call_args[0][0].add_cast remove_callback = cast_browser.call_args[0][0].remove_cast - def discover_chromecast(service_name: str, info: ChromecastInfo) -> None: + def discover_chromecast( + service: pychromecast.discovery.ServiceInfo, info: ChromecastInfo + ) -> None: """Discover a chromecast device.""" browser.devices[info.uuid] = pychromecast.discovery.CastInfo( - {service_name}, + {service}, info.uuid, info.model_name, info.friendly_name, @@ -148,7 +161,7 @@ async def async_setup_cast_internal_discovery(hass, config=None): info.cast_type, info.manufacturer, ) - discovery_callback(info.uuid, service_name) + discovery_callback(info.uuid, "") def remove_chromecast(service_name: str, info: ChromecastInfo) -> None: """Remove a chromecast device.""" @@ -194,9 +207,8 @@ async def async_setup_media_player_cast(hass: HomeAssistant, info: ChromecastInf discovery_callback = cast_browser.call_args[0][0].add_cast - service_name = "the-service" browser.devices[info.uuid] = pychromecast.discovery.CastInfo( - {service_name}, + {FAKE_MDNS_SERVICE}, info.uuid, info.model_name, info.friendly_name, @@ -205,7 +217,7 @@ async def async_setup_media_player_cast(hass: HomeAssistant, info: ChromecastInf info.cast_type, info.manufacturer, ) - discovery_callback(info.uuid, service_name) + discovery_callback(info.uuid, FAKE_MDNS_SERVICE[1]) await hass.async_block_till_done() await hass.async_block_till_done() @@ -214,7 +226,7 @@ async def async_setup_media_player_cast(hass: HomeAssistant, info: ChromecastInf def discover_chromecast(service_name: str, info: ChromecastInfo) -> None: """Discover a chromecast device.""" browser.devices[info.uuid] = pychromecast.discovery.CastInfo( - {service_name}, + {FAKE_MDNS_SERVICE}, info.uuid, info.model_name, info.friendly_name, @@ -223,7 +235,7 @@ async def async_setup_media_player_cast(hass: HomeAssistant, info: ChromecastInf info.cast_type, info.manufacturer, ) - discovery_callback(info.uuid, service_name) + discovery_callback(info.uuid, FAKE_MDNS_SERVICE[1]) return chromecast, discover_chromecast @@ -250,16 +262,16 @@ def get_status_callbacks(chromecast_mock, mz_mock=None): async def test_start_discovery_called_once(hass, castbrowser_mock): """Test pychromecast.start_discovery called exactly once.""" await async_setup_cast(hass) - assert castbrowser_mock.start_discovery.call_count == 1 + assert castbrowser_mock.return_value.start_discovery.call_count == 1 await async_setup_cast(hass) - assert castbrowser_mock.start_discovery.call_count == 1 + assert castbrowser_mock.return_value.start_discovery.call_count == 1 async def test_internal_discovery_callback_fill_out_fail(hass): """Test internal discovery automatically filling out information.""" discover_cast, _, _ = await async_setup_cast_internal_discovery(hass) - info = get_fake_chromecast_info(host="host1") + info = get_fake_chromecast_info(host="host1", service=FAKE_MDNS_SERVICE) zconf = get_fake_zconf(host="host1", port=8009) full_info = ( info # attr.evolve(info, model_name="", friendly_name="Speaker", uuid=FakeUUID) @@ -275,7 +287,7 @@ async def test_internal_discovery_callback_fill_out_fail(hass): signal = MagicMock() async_dispatcher_connect(hass, "cast_discovered", signal) - discover_cast("the-service", info) + discover_cast(FAKE_MDNS_SERVICE, info) await hass.async_block_till_done() # when called with incomplete info, it should use HTTP to get missing @@ -286,7 +298,7 @@ async def test_internal_discovery_callback_fill_out_fail(hass): async def test_internal_discovery_callback_fill_out_group(hass): """Test internal discovery automatically filling out information.""" discover_cast, _, _ = await async_setup_cast_internal_discovery(hass) - info = get_fake_chromecast_info(host="host1", port=12345) + info = get_fake_chromecast_info(host="host1", port=12345, service=FAKE_MDNS_SERVICE) zconf = get_fake_zconf(host="host1", port=12345) full_info = attr.evolve( info, @@ -306,7 +318,7 @@ async def test_internal_discovery_callback_fill_out_group(hass): signal = MagicMock() async_dispatcher_connect(hass, "cast_discovered", signal) - discover_cast("the-service", info) + discover_cast(FAKE_MDNS_SERVICE, info) await hass.async_block_till_done() # when called with incomplete info, it should use HTTP to get missing @@ -318,12 +330,12 @@ async def test_stop_discovery_called_on_stop(hass, castbrowser_mock): """Test pychromecast.stop_discovery called on shutdown.""" # start_discovery should be called with empty config await async_setup_cast(hass, {}) - assert castbrowser_mock.start_discovery.call_count == 1 + assert castbrowser_mock.return_value.start_discovery.call_count == 1 # stop discovery should be called on shutdown hass.bus.async_fire(EVENT_HOMEASSISTANT_STOP) await hass.async_block_till_done() - assert castbrowser_mock.stop_discovery.call_count == 1 + assert castbrowser_mock.return_value.stop_discovery.call_count == 1 async def test_create_cast_device_without_uuid(hass): @@ -362,7 +374,12 @@ async def test_manual_cast_chromecasts_uuid(hass): "homeassistant.components.cast.discovery.ChromeCastZeroconf.get_zeroconf", return_value=zconf_2, ): - discover_cast("service2", cast_2) + discover_cast( + pychromecast.discovery.ServiceInfo( + pychromecast.const.SERVICE_TYPE_MDNS, "service2" + ), + cast_2, + ) await hass.async_block_till_done() await hass.async_block_till_done() # having tasks that add jobs assert add_dev1.call_count == 0 @@ -371,7 +388,12 @@ async def test_manual_cast_chromecasts_uuid(hass): "homeassistant.components.cast.discovery.ChromeCastZeroconf.get_zeroconf", return_value=zconf_1, ): - discover_cast("service1", cast_1) + discover_cast( + pychromecast.discovery.ServiceInfo( + pychromecast.const.SERVICE_TYPE_MDNS, "service1" + ), + cast_1, + ) await hass.async_block_till_done() await hass.async_block_till_done() # having tasks that add jobs assert add_dev1.call_count == 1 @@ -390,7 +412,12 @@ async def test_auto_cast_chromecasts(hass): "homeassistant.components.cast.discovery.ChromeCastZeroconf.get_zeroconf", return_value=zconf_1, ): - discover_cast("service2", cast_2) + discover_cast( + pychromecast.discovery.ServiceInfo( + pychromecast.const.SERVICE_TYPE_MDNS, "service2" + ), + cast_2, + ) await hass.async_block_till_done() await hass.async_block_till_done() # having tasks that add jobs assert add_dev1.call_count == 1 @@ -399,13 +426,18 @@ async def test_auto_cast_chromecasts(hass): "homeassistant.components.cast.discovery.ChromeCastZeroconf.get_zeroconf", return_value=zconf_2, ): - discover_cast("service1", cast_1) + discover_cast( + pychromecast.discovery.ServiceInfo( + pychromecast.const.SERVICE_TYPE_MDNS, "service1" + ), + cast_1, + ) await hass.async_block_till_done() await hass.async_block_till_done() # having tasks that add jobs assert add_dev1.call_count == 2 -async def test_discover_dynamic_group(hass, dial_mock, pycast_mock, caplog): +async def test_discover_dynamic_group(hass, dial_mock, get_chromecast_mock, caplog): """Test dynamic group does not create device or entity.""" cast_1 = get_fake_chromecast_info(host="host_1", port=23456, uuid=FakeUUID) cast_2 = get_fake_chromecast_info(host="host_2", port=34567, uuid=FakeUUID2) @@ -421,7 +453,7 @@ async def test_discover_dynamic_group(hass, dial_mock, pycast_mock, caplog): tmp2.uuid = FakeUUID2 dial_mock.get_multizone_status.return_value.dynamic_groups = [tmp1, tmp2] - pycast_mock.get_chromecast_from_cast_info.assert_not_called() + get_chromecast_mock.assert_not_called() discover_cast, remove_cast, add_dev1 = await async_setup_cast_internal_discovery( hass ) @@ -431,11 +463,16 @@ async def test_discover_dynamic_group(hass, dial_mock, pycast_mock, caplog): "homeassistant.components.cast.discovery.ChromeCastZeroconf.get_zeroconf", return_value=zconf_1, ): - discover_cast("service", cast_1) + discover_cast( + pychromecast.discovery.ServiceInfo( + pychromecast.const.SERVICE_TYPE_MDNS, "service" + ), + cast_1, + ) await hass.async_block_till_done() await hass.async_block_till_done() # having tasks that add jobs - pycast_mock.get_chromecast_from_cast_info.assert_called() - pycast_mock.get_chromecast_from_cast_info.reset_mock() + get_chromecast_mock.assert_called() + get_chromecast_mock.reset_mock() assert add_dev1.call_count == 0 assert reg.async_get_entity_id("media_player", "cast", cast_1.uuid) is None @@ -444,11 +481,16 @@ async def test_discover_dynamic_group(hass, dial_mock, pycast_mock, caplog): "homeassistant.components.cast.discovery.ChromeCastZeroconf.get_zeroconf", return_value=zconf_2, ): - discover_cast("service", cast_2) + discover_cast( + pychromecast.discovery.ServiceInfo( + pychromecast.const.SERVICE_TYPE_MDNS, "service" + ), + cast_2, + ) await hass.async_block_till_done() await hass.async_block_till_done() # having tasks that add jobs - pycast_mock.get_chromecast_from_cast_info.assert_called() - pycast_mock.get_chromecast_from_cast_info.reset_mock() + get_chromecast_mock.assert_called() + get_chromecast_mock.reset_mock() assert add_dev1.call_count == 0 assert reg.async_get_entity_id("media_player", "cast", cast_1.uuid) is None @@ -457,10 +499,15 @@ async def test_discover_dynamic_group(hass, dial_mock, pycast_mock, caplog): "homeassistant.components.cast.discovery.ChromeCastZeroconf.get_zeroconf", return_value=zconf_1, ): - discover_cast("service", cast_1) + discover_cast( + pychromecast.discovery.ServiceInfo( + pychromecast.const.SERVICE_TYPE_MDNS, "service" + ), + cast_1, + ) await hass.async_block_till_done() await hass.async_block_till_done() # having tasks that add jobs - pycast_mock.get_chromecast_from_cast_info.assert_not_called() + get_chromecast_mock.assert_not_called() assert add_dev1.call_count == 0 assert reg.async_get_entity_id("media_player", "cast", cast_1.uuid) is None @@ -471,7 +518,12 @@ async def test_discover_dynamic_group(hass, dial_mock, pycast_mock, caplog): "homeassistant.components.cast.discovery.ChromeCastZeroconf.get_zeroconf", return_value=zconf_1, ): - remove_cast("service", cast_1) + remove_cast( + pychromecast.discovery.ServiceInfo( + pychromecast.const.SERVICE_TYPE_MDNS, "service" + ), + cast_1, + ) await hass.async_block_till_done() await hass.async_block_till_done() # having tasks that add jobs @@ -492,7 +544,12 @@ async def test_update_cast_chromecasts(hass): "homeassistant.components.cast.discovery.ChromeCastZeroconf.get_zeroconf", return_value=zconf_1, ): - discover_cast("service1", cast_1) + discover_cast( + pychromecast.discovery.ServiceInfo( + pychromecast.const.SERVICE_TYPE_MDNS, "service1" + ), + cast_1, + ) await hass.async_block_till_done() await hass.async_block_till_done() # having tasks that add jobs assert add_dev1.call_count == 1 @@ -501,7 +558,12 @@ async def test_update_cast_chromecasts(hass): "homeassistant.components.cast.discovery.ChromeCastZeroconf.get_zeroconf", return_value=zconf_2, ): - discover_cast("service2", cast_2) + discover_cast( + pychromecast.discovery.ServiceInfo( + pychromecast.const.SERVICE_TYPE_MDNS, "service2" + ), + cast_2, + ) await hass.async_block_till_done() await hass.async_block_till_done() # having tasks that add jobs assert add_dev1.call_count == 1 @@ -1406,7 +1468,7 @@ async def test_entry_setup_empty_config(hass: HomeAssistant): assert config_entry.data["ignore_cec"] == [] -async def test_entry_setup_single_config(hass: HomeAssistant, pycast_mock): +async def test_entry_setup_single_config(hass: HomeAssistant): """Test deprecated yaml config with a single config media_player.""" await async_setup_component( hass, "cast", {"cast": {"media_player": {"uuid": "bla", "ignore_cec": "cast1"}}} @@ -1417,10 +1479,10 @@ async def test_entry_setup_single_config(hass: HomeAssistant, pycast_mock): assert config_entry.data["uuid"] == ["bla"] assert config_entry.data["ignore_cec"] == ["cast1"] - assert pycast_mock.IGNORE_CEC == ["cast1"] + assert pychromecast.IGNORE_CEC == ["cast1"] -async def test_entry_setup_list_config(hass: HomeAssistant, pycast_mock): +async def test_entry_setup_list_config(hass: HomeAssistant): """Test deprecated yaml config with multiple media_players.""" await async_setup_component( hass, @@ -1439,4 +1501,4 @@ async def test_entry_setup_list_config(hass: HomeAssistant, pycast_mock): config_entry = hass.config_entries.async_entries("cast")[0] assert set(config_entry.data["uuid"]) == {"bla", "blu"} assert set(config_entry.data["ignore_cec"]) == {"cast1", "cast2", "cast3"} - assert set(pycast_mock.IGNORE_CEC) == {"cast1", "cast2", "cast3"} + assert set(pychromecast.IGNORE_CEC) == {"cast1", "cast2", "cast3"}