diff --git a/tests/test_bootstrap.py b/tests/test_bootstrap.py index f9c8647bd6e..9599e249f40 100644 --- a/tests/test_bootstrap.py +++ b/tests/test_bootstrap.py @@ -1067,3 +1067,48 @@ async def test_bootstrap_does_not_preload_stage_1_integrations() -> None: # as a side effect of importing the pre-imports for integration in bootstrap.STAGE_1_INTEGRATIONS: assert f"homeassistant.components.{integration}" not in decoded_stdout + + +@pytest.mark.parametrize("load_registries", [False]) +async def test_cancellation_does_not_leak_upward_from_async_setup( + hass: HomeAssistant, + caplog: pytest.LogCaptureFixture, + enable_custom_integrations: None, +) -> None: + """Test setting up an integration that raises asyncio.CancelledError.""" + await bootstrap.async_setup_multi_components( + hass, {"test_package_raises_cancelled_error"}, {} + ) + await hass.async_block_till_done() + + assert ( + "Error during setup of component test_package_raises_cancelled_error" + in caplog.text + ) + + +@pytest.mark.parametrize("load_registries", [False]) +async def test_cancellation_does_not_leak_upward_from_async_setup_entry( + hass: HomeAssistant, + caplog: pytest.LogCaptureFixture, + enable_custom_integrations: None, +) -> None: + """Test setting up an integration that raises asyncio.CancelledError.""" + entry = MockConfigEntry( + domain="test_package_raises_cancelled_error_config_entry", data={} + ) + entry.add_to_hass(hass) + await bootstrap.async_setup_multi_components( + hass, {"test_package_raises_cancelled_error_config_entry"}, {} + ) + await hass.async_block_till_done() + + await bootstrap.async_setup_multi_components(hass, {"test_package"}, {}) + await hass.async_block_till_done() + assert ( + "Error setting up entry Mock Title for test_package_raises_cancelled_error_config_entry" + in caplog.text + ) + + assert "test_package" in hass.config.components + assert "test_package_raises_cancelled_error_config_entry" in hass.config.components diff --git a/tests/testing_config/custom_components/test_package_raises_cancelled_error/__init__.py b/tests/testing_config/custom_components/test_package_raises_cancelled_error/__init__.py new file mode 100644 index 00000000000..e77df90a00b --- /dev/null +++ b/tests/testing_config/custom_components/test_package_raises_cancelled_error/__init__.py @@ -0,0 +1,8 @@ +"""Provide a mock package component.""" +import asyncio + + +async def async_setup(hass, config): + """Mock a successful setup.""" + asyncio.current_task().cancel() + await asyncio.sleep(0) diff --git a/tests/testing_config/custom_components/test_package_raises_cancelled_error/manifest.json b/tests/testing_config/custom_components/test_package_raises_cancelled_error/manifest.json new file mode 100644 index 00000000000..930c4d2ae9e --- /dev/null +++ b/tests/testing_config/custom_components/test_package_raises_cancelled_error/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "test_package_raises_cancelled_error", + "name": "Test Package that raises asyncio.CancelledError", + "documentation": "http://test-package.io", + "requirements": [], + "dependencies": [], + "codeowners": [], + "config_flow": false, + "version": "1.2.3" +} diff --git a/tests/testing_config/custom_components/test_package_raises_cancelled_error_config_entry/__init__.py b/tests/testing_config/custom_components/test_package_raises_cancelled_error_config_entry/__init__.py new file mode 100644 index 00000000000..1283e79f21b --- /dev/null +++ b/tests/testing_config/custom_components/test_package_raises_cancelled_error_config_entry/__init__.py @@ -0,0 +1,13 @@ +"""Provide a mock package component.""" +import asyncio + + +async def async_setup(hass, config): + """Mock a successful setup.""" + return True + + +async def async_setup_entry(hass, entry): + """Mock an unsuccessful entry setup.""" + asyncio.current_task().cancel() + await asyncio.sleep(0) diff --git a/tests/testing_config/custom_components/test_package_raises_cancelled_error_config_entry/config_flow.py b/tests/testing_config/custom_components/test_package_raises_cancelled_error_config_entry/config_flow.py new file mode 100644 index 00000000000..7277bac343d --- /dev/null +++ b/tests/testing_config/custom_components/test_package_raises_cancelled_error_config_entry/config_flow.py @@ -0,0 +1,17 @@ +"""Config flow.""" + +from homeassistant.config_entries import ConfigFlow +from homeassistant.core import HomeAssistant + + +class MockConfigFlow( + ConfigFlow, domain="test_package_raises_cancelled_error_config_entry" +): + """Mock config flow.""" + + pass + + +async def _async_has_devices(hass: HomeAssistant) -> bool: + """Return if there are devices that can be discovered.""" + return True diff --git a/tests/testing_config/custom_components/test_package_raises_cancelled_error_config_entry/manifest.json b/tests/testing_config/custom_components/test_package_raises_cancelled_error_config_entry/manifest.json new file mode 100644 index 00000000000..2ce303ca687 --- /dev/null +++ b/tests/testing_config/custom_components/test_package_raises_cancelled_error_config_entry/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "test_package_raises_cancelled_error_config_entry", + "name": "Test Package that raises asyncio.CancelledError in async_setup_entry", + "documentation": "http://test-package.io", + "requirements": [], + "dependencies": [], + "codeowners": [], + "config_flow": true, + "version": "1.2.3" +}