From 458391ee2b7d1192a2cb06733b0203da84ff4a1e Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Thu, 29 Feb 2024 05:14:50 +0100 Subject: [PATCH] Axis improve coverage binary tests (#111758) * Parametrize binary sensor tests * Add test coverage to the different *guard apps * Add object analytics tests --- tests/components/axis/conftest.py | 43 ++++--- tests/components/axis/const.py | 46 ++++++- tests/components/axis/test_binary_sensor.py | 127 ++++++++++++++++---- 3 files changed, 171 insertions(+), 45 deletions(-) diff --git a/tests/components/axis/conftest.py b/tests/components/axis/conftest.py index 8de16ee7990..0e83f9007bf 100644 --- a/tests/components/axis/conftest.py +++ b/tests/components/axis/conftest.py @@ -21,6 +21,8 @@ from homeassistant.const import ( from .const import ( API_DISCOVERY_RESPONSE, + APP_AOA_RESPONSE, + APP_VMD4_RESPONSE, APPLICATIONS_LIST_RESPONSE, BASIC_DEVICE_INFO_RESPONSE, BRAND_RESPONSE, @@ -36,7 +38,6 @@ from .const import ( PTZ_RESPONSE, STREAM_PROFILES_RESPONSE, VIEW_AREAS_RESPONSE, - VMD4_RESPONSE, ) from tests.common import MockConfigEntry @@ -105,88 +106,90 @@ def default_request_fixture( """Mock default Vapix requests responses.""" def __mock_default_requests(host): - path = f"http://{host}:80" + respx_mock(base_url=f"http://{host}:80") if host != DEFAULT_HOST: - respx.post(f"{path}/axis-cgi/apidiscovery.cgi").respond( + respx.post("/axis-cgi/apidiscovery.cgi").respond( json=API_DISCOVERY_RESPONSE, ) - respx.post(f"{path}/axis-cgi/basicdeviceinfo.cgi").respond( + respx.post("/axis-cgi/basicdeviceinfo.cgi").respond( json=BASIC_DEVICE_INFO_RESPONSE, ) - respx.post(f"{path}/axis-cgi/io/portmanagement.cgi").respond( + respx.post("/axis-cgi/io/portmanagement.cgi").respond( json=port_management_payload, ) - respx.post(f"{path}/axis-cgi/mqtt/client.cgi").respond( + respx.post("/axis-cgi/mqtt/client.cgi").respond( json=MQTT_CLIENT_RESPONSE, ) - respx.post(f"{path}/axis-cgi/streamprofile.cgi").respond( + respx.post("/axis-cgi/streamprofile.cgi").respond( json=STREAM_PROFILES_RESPONSE, ) - respx.post(f"{path}/axis-cgi/viewarea/info.cgi").respond( - json=VIEW_AREAS_RESPONSE - ) + respx.post("/axis-cgi/viewarea/info.cgi").respond(json=VIEW_AREAS_RESPONSE) respx.post( - f"{path}/axis-cgi/param.cgi", + "/axis-cgi/param.cgi", data={"action": "list", "group": "root.Brand"}, ).respond( text=BRAND_RESPONSE, headers={"Content-Type": "text/plain"}, ) respx.post( - f"{path}/axis-cgi/param.cgi", + "/axis-cgi/param.cgi", data={"action": "list", "group": "root.Image"}, ).respond( text=IMAGE_RESPONSE, headers={"Content-Type": "text/plain"}, ) respx.post( - f"{path}/axis-cgi/param.cgi", + "/axis-cgi/param.cgi", data={"action": "list", "group": "root.Input"}, ).respond( text=PORTS_RESPONSE, headers={"Content-Type": "text/plain"}, ) respx.post( - f"{path}/axis-cgi/param.cgi", + "/axis-cgi/param.cgi", data={"action": "list", "group": "root.IOPort"}, ).respond( text=param_ports_payload, headers={"Content-Type": "text/plain"}, ) respx.post( - f"{path}/axis-cgi/param.cgi", + "/axis-cgi/param.cgi", data={"action": "list", "group": "root.Output"}, ).respond( text=PORTS_RESPONSE, headers={"Content-Type": "text/plain"}, ) respx.post( - f"{path}/axis-cgi/param.cgi", + "/axis-cgi/param.cgi", data={"action": "list", "group": "root.Properties"}, ).respond( text=param_properties_payload, headers={"Content-Type": "text/plain"}, ) respx.post( - f"{path}/axis-cgi/param.cgi", + "/axis-cgi/param.cgi", data={"action": "list", "group": "root.PTZ"}, ).respond( text=PTZ_RESPONSE, headers={"Content-Type": "text/plain"}, ) respx.post( - f"{path}/axis-cgi/param.cgi", + "/axis-cgi/param.cgi", data={"action": "list", "group": "root.StreamProfile"}, ).respond( text=STREAM_PROFILES_RESPONSE, headers={"Content-Type": "text/plain"}, ) - respx.post(f"{path}/axis-cgi/applications/list.cgi").respond( + respx.post("/axis-cgi/applications/list.cgi").respond( text=APPLICATIONS_LIST_RESPONSE, headers={"Content-Type": "text/xml"}, ) - respx.post(f"{path}/local/vmd/control.cgi").respond(json=VMD4_RESPONSE) + respx.post("/local/fenceguard/control.cgi").respond(json=APP_VMD4_RESPONSE) + respx.post("/local/loiteringguard/control.cgi").respond(json=APP_VMD4_RESPONSE) + respx.post("/local/motionguard/control.cgi").respond(json=APP_VMD4_RESPONSE) + respx.post("/local/vmd/control.cgi").respond(json=APP_VMD4_RESPONSE) + respx.post("/local/objectanalytics/control.cgi").respond(json=APP_AOA_RESPONSE) return __mock_default_requests diff --git a/tests/components/axis/const.py b/tests/components/axis/const.py index df1d0aa4529..8dffac5866a 100644 --- a/tests/components/axis/const.py +++ b/tests/components/axis/const.py @@ -35,7 +35,11 @@ API_DISCOVERY_PORT_MANAGEMENT = { } APPLICATIONS_LIST_RESPONSE = """ + + + + """ BASIC_DEVICE_INFO_RESPONSE = { @@ -95,7 +99,7 @@ PORT_MANAGEMENT_RESPONSE = { }, } -VMD4_RESPONSE = { +APP_VMD4_RESPONSE = { "apiVersion": "1.4", "method": "getConfiguration", "context": CONTEXT, @@ -108,6 +112,46 @@ VMD4_RESPONSE = { }, } +APP_AOA_RESPONSE = { + "apiVersion": "1.0", + "context": "Axis library", + "data": { + "devices": [{"id": 1, "rotation": 180, "type": "camera"}], + "metadataOverlay": [], + "perspectives": [], + "scenarios": [ + { + "devices": [{"id": 1}], + "filters": [ + {"distance": 5, "type": "distanceSwayingObject"}, + {"time": 1, "type": "timeShortLivedLimit"}, + {"height": 3, "type": "sizePercentage", "width": 3}, + ], + "id": 1, + "name": "Scenario 1", + "objectClassifications": [], + "perspectives": [], + "presets": [], + "triggers": [ + { + "type": "includeArea", + "vertices": [ + [-0.97, -0.97], + [-0.97, 0.97], + [0.97, 0.97], + [0.97, -0.97], + ], + } + ], + "type": "motion", + }, + ], + "status": {}, + }, + "method": "getConfiguration", +} + + BRAND_RESPONSE = """root.Brand.Brand=AXIS root.Brand.ProdFullName=AXIS M1065-LW Network Camera root.Brand.ProdNbr=M1065-LW diff --git a/tests/components/axis/test_binary_sensor.py b/tests/components/axis/test_binary_sensor.py index 45fd8fe2f6c..d928d3beaa9 100644 --- a/tests/components/axis/test_binary_sensor.py +++ b/tests/components/axis/test_binary_sensor.py @@ -1,4 +1,6 @@ """Axis binary sensor platform tests.""" +import pytest + from homeassistant.components.axis.const import DOMAIN as AXIS_DOMAIN from homeassistant.components.binary_sensor import ( DOMAIN as BINARY_SENSOR_DOMAIN, @@ -30,23 +32,10 @@ async def test_no_binary_sensors(hass: HomeAssistant, setup_config_entry) -> Non assert not hass.states.async_entity_ids(BINARY_SENSOR_DOMAIN) -async def test_binary_sensors( +async def test_unsupported_binary_sensors( hass: HomeAssistant, setup_config_entry, mock_rtsp_event ) -> None: - """Test that sensors are loaded properly.""" - mock_rtsp_event( - topic="tns1:Device/tnsaxis:Sensor/PIR", - data_type="state", - data_value="0", - source_name="sensor", - source_idx="0", - ) - mock_rtsp_event( - topic="tnsaxis:CameraApplicationPlatform/VMD/Camera1Profile1", - data_type="active", - data_value="1", - ) - # Unsupported event + """Test that unsupported sensors are not loaded.""" mock_rtsp_event( topic="tns1:PTZController/tnsaxis:PTZPresets/Channel_1", data_type="on_preset", @@ -56,14 +45,104 @@ async def test_binary_sensors( ) await hass.async_block_till_done() - assert len(hass.states.async_entity_ids(BINARY_SENSOR_DOMAIN)) == 2 + assert len(hass.states.async_entity_ids(BINARY_SENSOR_DOMAIN)) == 0 - pir = hass.states.get(f"{BINARY_SENSOR_DOMAIN}.{NAME}_pir_0") - assert pir.state == STATE_OFF - assert pir.name == f"{NAME} PIR 0" - assert pir.attributes["device_class"] == BinarySensorDeviceClass.MOTION - vmd4 = hass.states.get(f"{BINARY_SENSOR_DOMAIN}.{NAME}_vmd4_profile_1") - assert vmd4.state == STATE_ON - assert vmd4.name == f"{NAME} VMD4 Profile 1" - assert vmd4.attributes["device_class"] == BinarySensorDeviceClass.MOTION +@pytest.mark.parametrize( + ("event", "entity"), + [ + ( + { + "topic": "tns1:Device/tnsaxis:Sensor/PIR", + "data_type": "state", + "data_value": "0", + "source_name": "sensor", + "source_idx": "0", + }, + { + "id": f"{BINARY_SENSOR_DOMAIN}.{NAME}_pir_0", + "state": STATE_OFF, + "name": f"{NAME} PIR 0", + "device_class": BinarySensorDeviceClass.MOTION, + }, + ), + ( + { + "topic": "tnsaxis:CameraApplicationPlatform/FenceGuard/Camera1Profile1", + "data_type": "active", + "data_value": "1", + }, + { + "id": f"{BINARY_SENSOR_DOMAIN}.{NAME}_fence_guard_profile_1", + "state": STATE_ON, + "name": f"{NAME} Fence Guard Profile 1", + "device_class": BinarySensorDeviceClass.MOTION, + }, + ), + ( + { + "topic": "tnsaxis:CameraApplicationPlatform/MotionGuard/Camera1Profile1", + "data_type": "active", + "data_value": "1", + }, + { + "id": f"{BINARY_SENSOR_DOMAIN}.{NAME}_motion_guard_profile_1", + "state": STATE_ON, + "name": f"{NAME} Motion Guard Profile 1", + "device_class": BinarySensorDeviceClass.MOTION, + }, + ), + ( + { + "topic": "tnsaxis:CameraApplicationPlatform/LoiteringGuard/Camera1Profile1", + "data_type": "active", + "data_value": "1", + }, + { + "id": f"{BINARY_SENSOR_DOMAIN}.{NAME}_loitering_guard_profile_1", + "state": STATE_ON, + "name": f"{NAME} Loitering Guard Profile 1", + "device_class": BinarySensorDeviceClass.MOTION, + }, + ), + ( + { + "topic": "tnsaxis:CameraApplicationPlatform/VMD/Camera1Profile1", + "data_type": "active", + "data_value": "1", + }, + { + "id": f"{BINARY_SENSOR_DOMAIN}.{NAME}_vmd4_profile_1", + "state": STATE_ON, + "name": f"{NAME} VMD4 Profile 1", + "device_class": BinarySensorDeviceClass.MOTION, + }, + ), + ( + { + "topic": "tnsaxis:CameraApplicationPlatform/ObjectAnalytics/Device1Scenario1", + "data_type": "active", + "data_value": "1", + }, + { + "id": f"{BINARY_SENSOR_DOMAIN}.{NAME}_object_analytics_scenario_1", + "state": STATE_ON, + "name": f"{NAME} Object Analytics Scenario 1", + "device_class": BinarySensorDeviceClass.MOTION, + }, + ), + ], +) +async def test_binary_sensors( + hass: HomeAssistant, setup_config_entry, mock_rtsp_event, event, entity +) -> None: + """Test that sensors are loaded properly.""" + mock_rtsp_event(**event) + await hass.async_block_till_done() + + assert len(hass.states.async_entity_ids(BINARY_SENSOR_DOMAIN)) == 1 + + state = hass.states.get(entity["id"]) + assert state.state == entity["state"] + assert state.name == entity["name"] + assert state.attributes["device_class"] == entity["device_class"]