diff --git a/tests/components/axis/test_binary_sensor.py b/tests/components/axis/test_binary_sensor.py index ca3e984c993..d70d55e0d1e 100644 --- a/tests/components/axis/test_binary_sensor.py +++ b/tests/components/axis/test_binary_sensor.py @@ -1,12 +1,11 @@ """Axis binary sensor platform tests.""" -from unittest.mock import Mock - -from homeassistant import config_entries from homeassistant.components import axis import homeassistant.components.binary_sensor as binary_sensor from homeassistant.setup import async_setup_component +from .test_device import NAME, setup_axis_integration + EVENTS = [ { "operation": "Initialized", @@ -24,52 +23,6 @@ EVENTS = [ }, ] -ENTRY_CONFIG = { - axis.CONF_DEVICE: { - axis.config_flow.CONF_HOST: "1.2.3.4", - axis.config_flow.CONF_USERNAME: "user", - axis.config_flow.CONF_PASSWORD: "pass", - axis.config_flow.CONF_PORT: 80, - }, - axis.config_flow.CONF_MAC: "1234ABCD", - axis.config_flow.CONF_MODEL: "model", - axis.config_flow.CONF_NAME: "model 0", -} - -ENTRY_OPTIONS = { - axis.CONF_CAMERA: False, - axis.CONF_EVENTS: True, - axis.CONF_TRIGGER_TIME: 0, -} - - -async def setup_device(hass): - """Load the Axis binary sensor platform.""" - from axis import AxisDevice - - loop = Mock() - - config_entry = config_entries.ConfigEntry( - 1, - axis.DOMAIN, - "Mock Title", - ENTRY_CONFIG, - "test", - config_entries.CONN_CLASS_LOCAL_PUSH, - system_options={}, - options=ENTRY_OPTIONS, - ) - device = axis.AxisNetworkDevice(hass, config_entry) - device.api = AxisDevice(loop=loop, **config_entry.data[axis.CONF_DEVICE]) - hass.data[axis.DOMAIN] = {device.serial: device} - device.api.enable_events(event_callback=device.async_event_callback) - - await hass.config_entries.async_forward_entry_setup(config_entry, "binary_sensor") - # To flush out the service call to update the group - await hass.async_block_till_done() - - return device - async def test_platform_manually_configured(hass): """Test that nothing happens when platform is manually configured.""" @@ -85,25 +38,25 @@ async def test_platform_manually_configured(hass): async def test_no_binary_sensors(hass): """Test that no sensors in Axis results in no sensor entities.""" - await setup_device(hass) + await setup_axis_integration(hass) - assert len(hass.states.async_all()) == 0 + assert not hass.states.async_entity_ids("binary_sensor") async def test_binary_sensors(hass): """Test that sensors are loaded properly.""" - device = await setup_device(hass) + device = await setup_axis_integration(hass) for event in EVENTS: device.api.stream.event.manage_event(event) await hass.async_block_till_done() - assert len(hass.states.async_all()) == 2 + assert len(hass.states.async_entity_ids("binary_sensor")) == 2 - pir = hass.states.get("binary_sensor.model_0_pir_0") + pir = hass.states.get(f"binary_sensor.{NAME}_pir_0") assert pir.state == "off" - assert pir.name == "model 0 PIR 0" + assert pir.name == f"{NAME} PIR 0" - vmd4 = hass.states.get("binary_sensor.model_0_vmd4_camera1profile1") + vmd4 = hass.states.get(f"binary_sensor.{NAME}_vmd4_camera1profile1") assert vmd4.state == "on" - assert vmd4.name == "model 0 VMD4 Camera1Profile1" + assert vmd4.name == f"{NAME} VMD4 Camera1Profile1" diff --git a/tests/components/axis/test_camera.py b/tests/components/axis/test_camera.py index 67ca7e3690a..5cbc5e993ca 100644 --- a/tests/components/axis/test_camera.py +++ b/tests/components/axis/test_camera.py @@ -1,57 +1,10 @@ """Axis camera platform tests.""" -from unittest.mock import Mock - -from homeassistant import config_entries from homeassistant.components import axis import homeassistant.components.camera as camera from homeassistant.setup import async_setup_component -ENTRY_CONFIG = { - axis.CONF_DEVICE: { - axis.config_flow.CONF_HOST: "1.2.3.4", - axis.config_flow.CONF_USERNAME: "user", - axis.config_flow.CONF_PASSWORD: "pass", - axis.config_flow.CONF_PORT: 80, - }, - axis.config_flow.CONF_MAC: "1234ABCD", - axis.config_flow.CONF_MODEL: "model", - axis.config_flow.CONF_NAME: "model 0", -} - -ENTRY_OPTIONS = { - axis.CONF_CAMERA: False, - axis.CONF_EVENTS: True, - axis.CONF_TRIGGER_TIME: 0, -} - - -async def setup_device(hass): - """Load the Axis binary sensor platform.""" - from axis import AxisDevice - - loop = Mock() - - config_entry = config_entries.ConfigEntry( - 1, - axis.DOMAIN, - "Mock Title", - ENTRY_CONFIG, - "test", - config_entries.CONN_CLASS_LOCAL_PUSH, - system_options={}, - options=ENTRY_OPTIONS, - ) - device = axis.AxisNetworkDevice(hass, config_entry) - device.api = AxisDevice(loop=loop, **config_entry.data[axis.CONF_DEVICE]) - hass.data[axis.DOMAIN] = {device.serial: device} - device.api.enable_events(event_callback=device.async_event_callback) - - await hass.config_entries.async_forward_entry_setup(config_entry, "camera") - # To flush out the service call to update the group - await hass.async_block_till_done() - - return device +from .test_device import NAME, setup_axis_integration async def test_platform_manually_configured(hass): @@ -68,12 +21,10 @@ async def test_platform_manually_configured(hass): async def test_camera(hass): """Test that Axis camera platform is loaded properly.""" - await setup_device(hass) + await setup_axis_integration(hass) - await hass.async_block_till_done() + assert len(hass.states.async_entity_ids("camera")) == 1 - assert len(hass.states.async_all()) == 1 - - cam = hass.states.get("camera.model_0") + cam = hass.states.get(f"camera.{NAME}") assert cam.state == "idle" - assert cam.name == "model 0" + assert cam.name == NAME diff --git a/tests/components/axis/test_config_flow.py b/tests/components/axis/test_config_flow.py index a29c270e0b8..e542ef0534f 100644 --- a/tests/components/axis/test_config_flow.py +++ b/tests/components/axis/test_config_flow.py @@ -60,7 +60,7 @@ async def test_flow_works(hass): ) assert result["type"] == "create_entry" - assert result["title"] == "{} - {}".format("prodnbr", "serialnumber") + assert result["title"] == f"prodnbr - serialnumber" assert result["data"] == { axis.CONF_DEVICE: { config_flow.CONF_HOST: "1.2.3.4", diff --git a/tests/components/axis/test_device.py b/tests/components/axis/test_device.py index a9f38cc4f3a..58ebed60681 100644 --- a/tests/components/axis/test_device.py +++ b/tests/components/axis/test_device.py @@ -1,173 +1,180 @@ """Test Axis device.""" -from unittest.mock import Mock, patch +from copy import deepcopy +from asynctest import Mock, patch +import axis as axislib import pytest -from homeassistant.components.axis import device, errors -from homeassistant.components.axis.camera import AxisCamera +from homeassistant import config_entries +from homeassistant.components import axis -from tests.common import MockConfigEntry, mock_coro +MAC = "00408C12345" +MODEL = "model" +NAME = "name" DEVICE_DATA = { - device.CONF_HOST: "1.2.3.4", - device.CONF_USERNAME: "username", - device.CONF_PASSWORD: "password", - device.CONF_PORT: 1234, + axis.device.CONF_HOST: "1.2.3.4", + axis.device.CONF_USERNAME: "username", + axis.device.CONF_PASSWORD: "password", + axis.device.CONF_PORT: 1234, } -ENTRY_OPTIONS = {device.CONF_CAMERA: True, device.CONF_EVENTS: True} +ENTRY_OPTIONS = {axis.device.CONF_CAMERA: True, axis.device.CONF_EVENTS: True} ENTRY_CONFIG = { - device.CONF_DEVICE: DEVICE_DATA, - device.CONF_MAC: "mac", - device.CONF_MODEL: "model", - device.CONF_NAME: "name", + axis.device.CONF_DEVICE: DEVICE_DATA, + axis.device.CONF_MAC: MAC, + axis.device.CONF_MODEL: MODEL, + axis.device.CONF_NAME: NAME, } +DEFAULT_BRAND = """root.Brand.Brand=AXIS +root.Brand.ProdFullName=AXIS M1065-LW Network Camera +root.Brand.ProdNbr=M1065-LW +root.Brand.ProdShortName=AXIS M1065-LW +root.Brand.ProdType=Network Camera +root.Brand.ProdVariant= +root.Brand.WebURL=http://www.axis.com +""" -async def test_device_setup(): - """Successful setup.""" - hass = Mock() - entry = Mock() - entry.data = ENTRY_CONFIG - entry.options = ENTRY_OPTIONS - api = Mock() +DEFAULT_PORTS = """root.Input.NbrOfInputs=1 +root.IOPort.I0.Configurable=no +root.IOPort.I0.Direction=input +root.IOPort.I0.Input.Name=PIR sensor +root.IOPort.I0.Input.Trig=closed +root.Output.NbrOfOutputs=0 +""" - axis_device = device.AxisNetworkDevice(hass, entry) - axis_device.start = Mock() +DEFAULT_PROPERTIES = """root.Properties.API.HTTP.Version=3 +root.Properties.API.Metadata.Metadata=yes +root.Properties.API.Metadata.Version=1.0 +root.Properties.Firmware.BuildDate=Feb 15 2019 09:42 +root.Properties.Firmware.BuildNumber=26 +root.Properties.Firmware.Version=9.10.1 +root.Properties.Image.Format=jpeg,mjpeg,h264 +root.Properties.Image.NbrOfViews=2 +root.Properties.Image.Resolution=1920x1080,1280x960,1280x720,1024x768,1024x576,800x600,640x480,640x360,352x240,320x240 +root.Properties.Image.Rotation=0,180 +root.Properties.System.SerialNumber=ACCC12345678 +""" - assert axis_device.host == DEVICE_DATA[device.CONF_HOST] - assert axis_device.model == ENTRY_CONFIG[device.CONF_MODEL] - assert axis_device.name == ENTRY_CONFIG[device.CONF_NAME] - assert axis_device.serial == ENTRY_CONFIG[device.CONF_MAC] - with patch.object(device, "get_device", return_value=mock_coro(api)): - assert await axis_device.async_setup() is True - - assert axis_device.api is api - assert len(hass.config_entries.async_forward_entry_setup.mock_calls) == 3 - assert hass.config_entries.async_forward_entry_setup.mock_calls[0][1] == ( - entry, - "camera", - ) - assert hass.config_entries.async_forward_entry_setup.mock_calls[1][1] == ( - entry, - "binary_sensor", - ) - assert hass.config_entries.async_forward_entry_setup.mock_calls[2][1] == ( - entry, - "switch", +async def setup_axis_integration( + hass, + config=ENTRY_CONFIG, + options=ENTRY_OPTIONS, + brand=DEFAULT_BRAND, + ports=DEFAULT_PORTS, + properties=DEFAULT_PROPERTIES, +): + """Create the Axis device.""" + config_entry = config_entries.ConfigEntry( + version=1, + domain=axis.DOMAIN, + title="Mock Title", + data=deepcopy(config), + source="test", + connection_class=config_entries.CONN_CLASS_LOCAL_PUSH, + system_options={}, + options=deepcopy(options), + entry_id="1", ) + def mock_request(self, method, path, json=None): + if method == "get": + if path == "/axis-cgi/param.cgi?action=list&group=root.Brand": + return brand + if path in [ + "/axis-cgi/param.cgi?action=list&group=root.Input", + "/axis-cgi/param.cgi?action=list&group=root.IOPort", + "/axis-cgi/param.cgi?action=list&group=root.Output", + ]: + return ports + if path == "/axis-cgi/param.cgi?action=list&group=root.Properties": + return properties -async def test_device_signal_new_address(hass): - """Successful setup.""" - entry = MockConfigEntry( - domain=device.DOMAIN, data=ENTRY_CONFIG, options=ENTRY_OPTIONS - ) + return None - api = Mock() - api.vapix.get_param.return_value = "1234" - - axis_device = device.AxisNetworkDevice(hass, entry) - hass.data[device.DOMAIN] = {axis_device.serial: axis_device} - - with patch.object(device, "get_device", return_value=mock_coro(api)), patch.object( - AxisCamera, "_new_address" - ) as new_address_mock: - await axis_device.async_setup() - await hass.async_block_till_done() - - assert len(hass.states.async_all()) == 1 - assert len(axis_device.listeners) == 2 - - entry.data[device.CONF_DEVICE][device.CONF_HOST] = "2.3.4.5" - hass.config_entries.async_update_entry(entry, data=entry.data) + with patch("axis.vapix.Vapix.request", new=mock_request), patch( + "axis.AxisDevice.start", return_value=True + ): + await axis.async_setup_entry(hass, config_entry) await hass.async_block_till_done() - assert axis_device.host == "2.3.4.5" - assert axis_device.api.config.host == "2.3.4.5" - assert len(new_address_mock.mock_calls) == 1 + hass.config_entries._entries.append(config_entry) + + return hass.data[axis.DOMAIN].get(config[axis.CONF_MAC]) + + +async def test_device_setup(hass): + """Successful setup.""" + with patch( + "homeassistant.config_entries.ConfigEntries.async_forward_entry_setup", + return_value=True, + ) as forward_entry_setup: + device = await setup_axis_integration(hass) + + entry = device.config_entry + + assert len(forward_entry_setup.mock_calls) == 3 + assert forward_entry_setup.mock_calls[0][1] == (entry, "camera") + assert forward_entry_setup.mock_calls[1][1] == (entry, "binary_sensor") + assert forward_entry_setup.mock_calls[2][1] == (entry, "switch") + + assert device.host == DEVICE_DATA[axis.device.CONF_HOST] + assert device.model == ENTRY_CONFIG[axis.device.CONF_MODEL] + assert device.name == ENTRY_CONFIG[axis.device.CONF_NAME] + assert device.serial == ENTRY_CONFIG[axis.device.CONF_MAC] + + +async def test_update_address(hass): + """Test update address works.""" + device = await setup_axis_integration(hass) + assert device.api.config.host == "1.2.3.4" + + await hass.config_entries.flow.async_init( + axis.DOMAIN, + data={ + "host": "2.3.4.5", + "port": 80, + "hostname": "name", + "properties": {"macaddress": MAC}, + }, + context={"source": "zeroconf"}, + ) + await hass.async_block_till_done() + + assert device.api.config.host == "2.3.4.5" async def test_device_unavailable(hass): """Successful setup.""" - entry = MockConfigEntry( - domain=device.DOMAIN, data=ENTRY_CONFIG, options=ENTRY_OPTIONS - ) - - api = Mock() - api.vapix.get_param.return_value = "1234" - - axis_device = device.AxisNetworkDevice(hass, entry) - hass.data[device.DOMAIN] = {axis_device.serial: axis_device} - - with patch.object(device, "get_device", return_value=mock_coro(api)), patch.object( - device, "async_dispatcher_send" - ) as mock_dispatcher: - await axis_device.async_setup() - await hass.async_block_till_done() - - axis_device.async_connection_status_callback(status=False) - - assert not axis_device.available - assert len(mock_dispatcher.mock_calls) == 1 + device = await setup_axis_integration(hass) + device.async_connection_status_callback(status=False) + assert not device.available async def test_device_reset(hass): """Successfully reset device.""" - entry = MockConfigEntry( - domain=device.DOMAIN, data=ENTRY_CONFIG, options=ENTRY_OPTIONS - ) - - api = Mock() - api.vapix.get_param.return_value = "1234" - - axis_device = device.AxisNetworkDevice(hass, entry) - hass.data[device.DOMAIN] = {axis_device.serial: axis_device} - - with patch.object(device, "get_device", return_value=mock_coro(api)): - await axis_device.async_setup() - await hass.async_block_till_done() - - await axis_device.async_reset() - - assert len(api.stop.mock_calls) == 1 - assert len(hass.states.async_all()) == 0 - assert len(axis_device.listeners) == 0 + device = await setup_axis_integration(hass) + result = await device.async_reset() + assert result is True -async def test_device_not_accessible(): +async def test_device_not_accessible(hass): """Failed setup schedules a retry of setup.""" - hass = Mock() - hass.data = dict() - entry = Mock() - entry.data = ENTRY_CONFIG - entry.options = ENTRY_OPTIONS - - axis_device = device.AxisNetworkDevice(hass, entry) - with patch.object( - device, "get_device", side_effect=errors.CannotConnect - ), pytest.raises(device.ConfigEntryNotReady): - await axis_device.async_setup() - - assert not hass.helpers.event.async_call_later.mock_calls + axis.device, "get_device", side_effect=axis.errors.CannotConnect + ), pytest.raises(axis.device.ConfigEntryNotReady): + await setup_axis_integration(hass) + assert hass.data[axis.DOMAIN] == {} -async def test_device_unknown_error(): +async def test_device_unknown_error(hass): """Unknown errors are handled.""" - hass = Mock() - entry = Mock() - entry.data = ENTRY_CONFIG - entry.options = ENTRY_OPTIONS - - axis_device = device.AxisNetworkDevice(hass, entry) - - with patch.object(device, "get_device", side_effect=Exception): - assert await axis_device.async_setup() is False - - assert not hass.helpers.event.async_call_later.mock_calls + with patch.object(axis.device, "get_device", side_effect=Exception): + await setup_axis_integration(hass) + assert hass.data[axis.DOMAIN] == {} async def test_new_event_sends_signal(hass): @@ -175,9 +182,9 @@ async def test_new_event_sends_signal(hass): entry = Mock() entry.data = ENTRY_CONFIG - axis_device = device.AxisNetworkDevice(hass, entry) + axis_device = axis.device.AxisNetworkDevice(hass, entry) - with patch.object(device, "async_dispatcher_send") as mock_dispatch_send: + with patch.object(axis.device, "async_dispatcher_send") as mock_dispatch_send: axis_device.async_event_callback(action="add", event_id="event") await hass.async_block_till_done() @@ -191,7 +198,7 @@ async def test_shutdown(): entry = Mock() entry.data = ENTRY_CONFIG - axis_device = device.AxisNetworkDevice(hass, entry) + axis_device = axis.device.AxisNetworkDevice(hass, entry) axis_device.api = Mock() axis_device.shutdown(None) @@ -199,39 +206,25 @@ async def test_shutdown(): assert len(axis_device.api.stop.mock_calls) == 1 -async def test_get_device(hass): - """Successful call.""" - with patch("axis.param_cgi.Params.update_brand", return_value=mock_coro()), patch( - "axis.param_cgi.Params.update_properties", return_value=mock_coro() - ), patch("axis.port_cgi.Ports.update", return_value=mock_coro()): - assert await device.get_device(hass, DEVICE_DATA) - - async def test_get_device_fails(hass): """Device unauthorized yields authentication required error.""" - import axis - with patch( - "axis.param_cgi.Params.update_brand", side_effect=axis.Unauthorized - ), pytest.raises(errors.AuthenticationRequired): - await device.get_device(hass, DEVICE_DATA) + "axis.param_cgi.Params.update_brand", side_effect=axislib.Unauthorized + ), pytest.raises(axis.errors.AuthenticationRequired): + await axis.device.get_device(hass, DEVICE_DATA) async def test_get_device_device_unavailable(hass): """Device unavailable yields cannot connect error.""" - import axis - with patch( - "axis.param_cgi.Params.update_brand", side_effect=axis.RequestError - ), pytest.raises(errors.CannotConnect): - await device.get_device(hass, DEVICE_DATA) + "axis.param_cgi.Params.update_brand", side_effect=axislib.RequestError + ), pytest.raises(axis.errors.CannotConnect): + await axis.device.get_device(hass, DEVICE_DATA) async def test_get_device_unknown_error(hass): """Device yield unknown error.""" - import axis - with patch( - "axis.param_cgi.Params.update_brand", side_effect=axis.AxisException - ), pytest.raises(errors.AuthenticationRequired): - await device.get_device(hass, DEVICE_DATA) + "axis.param_cgi.Params.update_brand", side_effect=axislib.AxisException + ), pytest.raises(axis.errors.AuthenticationRequired): + await axis.device.get_device(hass, DEVICE_DATA) diff --git a/tests/components/axis/test_switch.py b/tests/components/axis/test_switch.py index 406e3170ab2..844cfedf7fe 100644 --- a/tests/components/axis/test_switch.py +++ b/tests/components/axis/test_switch.py @@ -2,11 +2,12 @@ from unittest.mock import Mock, call as mock_call -from homeassistant import config_entries from homeassistant.components import axis import homeassistant.components.switch as switch from homeassistant.setup import async_setup_component +from .test_device import NAME, setup_axis_integration + EVENTS = [ { "operation": "Initialized", @@ -26,52 +27,6 @@ EVENTS = [ }, ] -ENTRY_CONFIG = { - axis.CONF_DEVICE: { - axis.config_flow.CONF_HOST: "1.2.3.4", - axis.config_flow.CONF_USERNAME: "user", - axis.config_flow.CONF_PASSWORD: "pass", - axis.config_flow.CONF_PORT: 80, - }, - axis.config_flow.CONF_MAC: "1234ABCD", - axis.config_flow.CONF_MODEL: "model", - axis.config_flow.CONF_NAME: "model 0", -} - -ENTRY_OPTIONS = { - axis.CONF_CAMERA: False, - axis.CONF_EVENTS: True, - axis.CONF_TRIGGER_TIME: 0, -} - - -async def setup_device(hass): - """Load the Axis switch platform.""" - from axis import AxisDevice - - loop = Mock() - - config_entry = config_entries.ConfigEntry( - 1, - axis.DOMAIN, - "Mock Title", - ENTRY_CONFIG, - "test", - config_entries.CONN_CLASS_LOCAL_PUSH, - system_options={}, - options=ENTRY_OPTIONS, - ) - device = axis.AxisNetworkDevice(hass, config_entry) - device.api = AxisDevice(loop=loop, **config_entry.data[axis.CONF_DEVICE]) - hass.data[axis.DOMAIN] = {device.serial: device} - device.api.enable_events(event_callback=device.async_event_callback) - - await hass.config_entries.async_forward_entry_setup(config_entry, "switch") - # To flush out the service call to update the group - await hass.async_block_till_done() - - return device - async def test_platform_manually_configured(hass): """Test that nothing happens when platform is manually configured.""" @@ -84,14 +39,15 @@ async def test_platform_manually_configured(hass): async def test_no_switches(hass): """Test that no output events in Axis results in no switch entities.""" - await setup_device(hass) + await setup_axis_integration(hass) assert not hass.states.async_entity_ids("switch") async def test_switches(hass): """Test that switches are loaded properly.""" - device = await setup_device(hass) + device = await setup_axis_integration(hass) + device.api.vapix.ports = {"0": Mock(), "1": Mock()} device.api.vapix.ports["0"].name = "Doorbell" device.api.vapix.ports["1"].name = "" @@ -100,24 +56,24 @@ async def test_switches(hass): device.api.stream.event.manage_event(event) await hass.async_block_till_done() - assert len(hass.states.async_all()) == 3 + assert len(hass.states.async_entity_ids("switch")) == 2 - relay_0 = hass.states.get("switch.model_0_doorbell") + relay_0 = hass.states.get(f"switch.{NAME}_doorbell") assert relay_0.state == "off" - assert relay_0.name == "model 0 Doorbell" + assert relay_0.name == f"{NAME} Doorbell" - relay_1 = hass.states.get("switch.model_0_relay_1") + relay_1 = hass.states.get(f"switch.{NAME}_relay_1") assert relay_1.state == "on" - assert relay_1.name == "model 0 Relay 1" + assert relay_1.name == f"{NAME} Relay 1" device.api.vapix.ports["0"].action = Mock() await hass.services.async_call( - "switch", "turn_on", {"entity_id": "switch.model_0_doorbell"}, blocking=True + "switch", "turn_on", {"entity_id": f"switch.{NAME}_doorbell"}, blocking=True ) await hass.services.async_call( - "switch", "turn_off", {"entity_id": "switch.model_0_doorbell"}, blocking=True + "switch", "turn_off", {"entity_id": f"switch.{NAME}_doorbell"}, blocking=True ) assert device.api.vapix.ports["0"].action.call_args_list == [