From 4353b1e62ce81c20f080bd6a4b1837fbcdfab50e Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Thu, 16 Dec 2021 21:31:37 +0100 Subject: [PATCH] Improve tests for template binary sensor (#62103) --- .../components/template/test_binary_sensor.py | 520 +++++++++++++----- 1 file changed, 372 insertions(+), 148 deletions(-) diff --git a/tests/components/template/test_binary_sensor.py b/tests/components/template/test_binary_sensor.py index 981ff63af50..0605759b16b 100644 --- a/tests/components/template/test_binary_sensor.py +++ b/tests/components/template/test_binary_sensor.py @@ -6,7 +6,7 @@ from unittest.mock import patch import pytest from homeassistant import setup -from homeassistant.components import binary_sensor +from homeassistant.components import binary_sensor, template from homeassistant.const import ( ATTR_DEVICE_CLASS, EVENT_HOMEASSISTANT_START, @@ -24,56 +24,150 @@ ON = "on" OFF = "off" -@pytest.mark.parametrize("count,domain", [(1, binary_sensor.DOMAIN)]) +@pytest.mark.parametrize("count", [1]) @pytest.mark.parametrize( - "config", + "config,domain,entity_id,name,attributes", [ - { - "binary_sensor": { - "platform": "template", - "sensors": { - "test": { - "friendly_name": "virtual thingy", - "value_template": "{{ True }}", + ( + { + "binary_sensor": { + "platform": "template", + "sensors": { + "test": { + "value_template": "{{ True }}", + } + }, + }, + }, + binary_sensor.DOMAIN, + "binary_sensor.test", + "test", + {"friendly_name": "test"}, + ), + ( + { + "template": { + "binary_sensor": { + "state": "{{ True }}", + } + }, + }, + template.DOMAIN, + "binary_sensor.unnamed_device", + "unnamed device", + {}, + ), + ], +) +async def test_setup_minimal(hass, start_ha, entity_id, name, attributes): + """Test the setup.""" + state = hass.states.get(entity_id) + assert state is not None + assert state.name == name + assert state.state == ON + assert state.attributes == attributes + + +@pytest.mark.parametrize("count", [1]) +@pytest.mark.parametrize( + "config,domain,entity_id", + [ + ( + { + "binary_sensor": { + "platform": "template", + "sensors": { + "test": { + "friendly_name": "virtual thingy", + "value_template": "{{ True }}", + "device_class": "motion", + } + }, + }, + }, + binary_sensor.DOMAIN, + "binary_sensor.test", + ), + ( + { + "template": { + "binary_sensor": { + "name": "virtual thingy", + "state": "{{ True }}", "device_class": "motion", } }, }, - }, + template.DOMAIN, + "binary_sensor.virtual_thingy", + ), ], ) -async def test_setup_legacy(hass, start_ha): +async def test_setup(hass, start_ha, entity_id): """Test the setup.""" - state = hass.states.get("binary_sensor.test") + state = hass.states.get(entity_id) assert state is not None assert state.name == "virtual thingy" assert state.state == ON assert state.attributes["device_class"] == "motion" -@pytest.mark.parametrize("count,domain", [(0, binary_sensor.DOMAIN)]) +@pytest.mark.parametrize("count", [0]) @pytest.mark.parametrize( - "config", + "config,domain", [ - {"binary_sensor": {"platform": "template"}}, - {"binary_sensor": {"platform": "template", "sensors": {"foo bar": {}}}}, - { - "binary_sensor": { - "platform": "template", - "sensors": { - "test": { - "value_template": "{{ foo }}", + # No legacy binary sensors + ( + {"binary_sensor": {"platform": "template"}}, + binary_sensor.DOMAIN, + ), + # Legacy binary sensor missing mandatory config + ( + {"binary_sensor": {"platform": "template", "sensors": {"foo bar": {}}}}, + binary_sensor.DOMAIN, + ), + # Binary sensor missing mandatory config + ( + {"template": {"binary_sensor": {}}}, + template.DOMAIN, + ), + # Legacy binary sensor with invalid device class + ( + { + "binary_sensor": { + "platform": "template", + "sensors": { + "test": { + "value_template": "{{ foo }}", + "device_class": "foobarnotreal", + } + }, + } + }, + binary_sensor.DOMAIN, + ), + # Binary sensor with invalid device class + ( + { + "template": { + "binary_sensor": { + "state": "{{ foo }}", "device_class": "foobarnotreal", } - }, - } - }, - { - "binary_sensor": { - "platform": "template", - "sensors": {"test": {"device_class": "motion"}}, - } - }, + } + }, + template.DOMAIN, + ), + # Legacy binary sensor missing mandatory config + ( + { + "binary_sensor": { + "platform": "template", + "sensors": {"test": {"device_class": "motion"}}, + } + }, + binary_sensor.DOMAIN, + ), ], ) async def test_setup_invalid_sensors(hass, count, start_ha): @@ -81,17 +175,35 @@ async def test_setup_invalid_sensors(hass, count, start_ha): assert len(hass.states.async_entity_ids("binary_sensor")) == count -@pytest.mark.parametrize("count,domain", [(1, binary_sensor.DOMAIN)]) +@pytest.mark.parametrize("count", [1]) @pytest.mark.parametrize( - "config", + "config,domain,entity_id", [ - { - "binary_sensor": { - "platform": "template", - "sensors": { - "test_template_sensor": { - "value_template": "{{ states.sensor.xyz.state }}", - "icon_template": "{% if " + ( + { + "binary_sensor": { + "platform": "template", + "sensors": { + "test_template_sensor": { + "value_template": "{{ states.sensor.xyz.state }}", + "icon_template": "{% if " + "states.binary_sensor.test_state.state == " + "'Works' %}" + "mdi:check" + "{% endif %}", + }, + }, + }, + }, + binary_sensor.DOMAIN, + "binary_sensor.test_template_sensor", + ), + ( + { + "template": { + "binary_sensor": { + "state": "{{ states.sensor.xyz.state }}", + "icon": "{% if " "states.binary_sensor.test_state.state == " "'Works' %}" "mdi:check" @@ -99,31 +211,51 @@ async def test_setup_invalid_sensors(hass, count, start_ha): }, }, }, - }, + template.DOMAIN, + "binary_sensor.unnamed_device", + ), ], ) -async def test_icon_template(hass, start_ha): +async def test_icon_template(hass, start_ha, entity_id): """Test icon template.""" - state = hass.states.get("binary_sensor.test_template_sensor") + state = hass.states.get(entity_id) assert state.attributes.get("icon") == "" hass.states.async_set("binary_sensor.test_state", "Works") await hass.async_block_till_done() - state = hass.states.get("binary_sensor.test_template_sensor") + state = hass.states.get(entity_id) assert state.attributes["icon"] == "mdi:check" -@pytest.mark.parametrize("count,domain", [(1, binary_sensor.DOMAIN)]) +@pytest.mark.parametrize("count", [1]) @pytest.mark.parametrize( - "config", + "config,domain,entity_id", [ - { - "binary_sensor": { - "platform": "template", - "sensors": { - "test_template_sensor": { - "value_template": "{{ states.sensor.xyz.state }}", - "entity_picture_template": "{% if " + ( + { + "binary_sensor": { + "platform": "template", + "sensors": { + "test_template_sensor": { + "value_template": "{{ states.sensor.xyz.state }}", + "entity_picture_template": "{% if " + "states.binary_sensor.test_state.state == " + "'Works' %}" + "/local/sensor.png" + "{% endif %}", + }, + }, + }, + }, + binary_sensor.DOMAIN, + "binary_sensor.test_template_sensor", + ), + ( + { + "template": { + "binary_sensor": { + "state": "{{ states.sensor.xyz.state }}", + "picture": "{% if " "states.binary_sensor.test_state.state == " "'Works' %}" "/local/sensor.png" @@ -131,48 +263,68 @@ async def test_icon_template(hass, start_ha): }, }, }, - }, + template.DOMAIN, + "binary_sensor.unnamed_device", + ), ], ) -async def test_entity_picture_template(hass, start_ha): +async def test_entity_picture_template(hass, start_ha, entity_id): """Test entity_picture template.""" - state = hass.states.get("binary_sensor.test_template_sensor") + state = hass.states.get(entity_id) assert state.attributes.get("entity_picture") == "" hass.states.async_set("binary_sensor.test_state", "Works") await hass.async_block_till_done() - state = hass.states.get("binary_sensor.test_template_sensor") + state = hass.states.get(entity_id) assert state.attributes["entity_picture"] == "/local/sensor.png" -@pytest.mark.parametrize("count,domain", [(1, binary_sensor.DOMAIN)]) +@pytest.mark.parametrize("count", [1]) @pytest.mark.parametrize( - "config", + "config,domain,entity_id", [ - { - "binary_sensor": { - "platform": "template", - "sensors": { - "test_template_sensor": { - "value_template": "{{ states.sensor.xyz.state }}", - "attribute_templates": { + ( + { + "binary_sensor": { + "platform": "template", + "sensors": { + "test_template_sensor": { + "value_template": "{{ states.sensor.xyz.state }}", + "attribute_templates": { + "test_attribute": "It {{ states.sensor.test_state.state }}." + }, + }, + }, + }, + }, + binary_sensor.DOMAIN, + "binary_sensor.test_template_sensor", + ), + ( + { + "template": { + "binary_sensor": { + "state": "{{ states.sensor.xyz.state }}", + "attributes": { "test_attribute": "It {{ states.sensor.test_state.state }}." }, }, }, }, - }, + template.DOMAIN, + "binary_sensor.unnamed_device", + ), ], ) -async def test_attribute_templates(hass, start_ha): +async def test_attribute_templates(hass, start_ha, entity_id): """Test attribute_templates template.""" - state = hass.states.get("binary_sensor.test_template_sensor") + state = hass.states.get(entity_id) assert state.attributes.get("test_attribute") == "It ." hass.states.async_set("sensor.test_state", "Works2") await hass.async_block_till_done() hass.states.async_set("sensor.test_state", "Works") await hass.async_block_till_done() - state = hass.states.get("binary_sensor.test_template_sensor") + state = hass.states.get(entity_id) assert state.attributes["test_attribute"] == "It Works." @@ -247,67 +399,102 @@ async def test_event(hass, start_ha): assert state.state == ON -@pytest.mark.parametrize("count,domain", [(1, binary_sensor.DOMAIN)]) @pytest.mark.parametrize( - "config", + "config,count,domain", [ - { - "binary_sensor": { - "platform": "template", - "sensors": { - "test_on": { - "friendly_name": "virtual thingy", - "value_template": "{{ states.sensor.test_state.state == 'on' }}", - "device_class": "motion", - "delay_on": 5, - }, - "test_off": { - "friendly_name": "virtual thingy", - "value_template": "{{ states.sensor.test_state.state == 'on' }}", - "device_class": "motion", - "delay_off": 5, + ( + { + "binary_sensor": { + "platform": "template", + "sensors": { + "test_on": { + "friendly_name": "virtual thingy", + "value_template": "{{ states.sensor.test_state.state == 'on' }}", + "device_class": "motion", + "delay_on": 5, + }, + "test_off": { + "friendly_name": "virtual thingy", + "value_template": "{{ states.sensor.test_state.state == 'on' }}", + "device_class": "motion", + "delay_off": 5, + }, }, }, }, - }, - { - "binary_sensor": { - "platform": "template", - "sensors": { - "test_on": { - "friendly_name": "virtual thingy", - "value_template": "{{ states.sensor.test_state.state == 'on' }}", - "device_class": "motion", - "delay_on": '{{ ({ "seconds": 10 / 2 }) }}', + 1, + binary_sensor.DOMAIN, + ), + ( + { + "template": [ + { + "binary_sensor": { + "name": "test on", + "state": "{{ states.sensor.test_state.state == 'on' }}", + "device_class": "motion", + "delay_on": 5, + }, }, - "test_off": { - "friendly_name": "virtual thingy", - "value_template": "{{ states.sensor.test_state.state == 'on' }}", - "device_class": "motion", - "delay_off": '{{ ({ "seconds": 10 / 2 }) }}', + { + "binary_sensor": { + "name": "test off", + "state": "{{ states.sensor.test_state.state == 'on' }}", + "device_class": "motion", + "delay_off": 5, + }, + }, + ] + }, + 2, + template.DOMAIN, + ), + ( + { + "binary_sensor": { + "platform": "template", + "sensors": { + "test_on": { + "friendly_name": "virtual thingy", + "value_template": "{{ states.sensor.test_state.state == 'on' }}", + "device_class": "motion", + "delay_on": '{{ ({ "seconds": 10 / 2 }) }}', + }, + "test_off": { + "friendly_name": "virtual thingy", + "value_template": "{{ states.sensor.test_state.state == 'on' }}", + "device_class": "motion", + "delay_off": '{{ ({ "seconds": 10 / 2 }) }}', + }, }, }, }, - }, - { - "binary_sensor": { - "platform": "template", - "sensors": { - "test_on": { - "friendly_name": "virtual thingy", - "value_template": "{{ states.sensor.test_state.state == 'on' }}", - "device_class": "motion", - "delay_on": '{{ ({ "seconds": states("input_number.delay")|int }) }}', - }, - "test_off": { - "friendly_name": "virtual thingy", - "value_template": "{{ states.sensor.test_state.state == 'on' }}", - "device_class": "motion", - "delay_off": '{{ ({ "seconds": states("input_number.delay")|int }) }}', + 1, + binary_sensor.DOMAIN, + ), + ( + { + "binary_sensor": { + "platform": "template", + "sensors": { + "test_on": { + "friendly_name": "virtual thingy", + "value_template": "{{ states.sensor.test_state.state == 'on' }}", + "device_class": "motion", + "delay_on": '{{ ({ "seconds": states("input_number.delay")|int }) }}', + }, + "test_off": { + "friendly_name": "virtual thingy", + "value_template": "{{ states.sensor.test_state.state == 'on' }}", + "device_class": "motion", + "delay_off": '{{ ({ "seconds": states("input_number.delay")|int }) }}', + }, }, }, }, - }, + 1, + binary_sensor.DOMAIN, + ), ], ) async def test_template_delay_on_off(hass, start_ha): @@ -349,64 +536,101 @@ async def test_template_delay_on_off(hass, start_ha): assert hass.states.get("binary_sensor.test_off").state == OFF -@pytest.mark.parametrize("count,domain", [(1, binary_sensor.DOMAIN)]) +@pytest.mark.parametrize("count", [1]) @pytest.mark.parametrize( - "config", + "config,domain,entity_id", [ - { - "binary_sensor": { - "platform": "template", - "sensors": { - "test": { - "friendly_name": "virtual thingy", - "value_template": "true", + ( + { + "binary_sensor": { + "platform": "template", + "sensors": { + "test": { + "friendly_name": "virtual thingy", + "value_template": "true", + "device_class": "motion", + "delay_off": 5, + }, + }, + }, + }, + binary_sensor.DOMAIN, + "binary_sensor.test", + ), + ( + { + "template": { + "binary_sensor": { + "name": "virtual thingy", + "state": "true", "device_class": "motion", "delay_off": 5, }, }, }, - }, + template.DOMAIN, + "binary_sensor.virtual_thingy", + ), ], ) -async def test_available_without_availability_template(hass, start_ha): +async def test_available_without_availability_template(hass, start_ha, entity_id): """Ensure availability is true without an availability_template.""" - state = hass.states.get("binary_sensor.test") + state = hass.states.get(entity_id) assert state.state != STATE_UNAVAILABLE assert state.attributes[ATTR_DEVICE_CLASS] == "motion" -@pytest.mark.parametrize("count,domain", [(1, binary_sensor.DOMAIN)]) +@pytest.mark.parametrize("count", [1]) @pytest.mark.parametrize( - "config", + "config,domain,entity_id", [ - { - "binary_sensor": { - "platform": "template", - "sensors": { - "test": { - "friendly_name": "virtual thingy", - "value_template": "true", - "device_class": "motion", - "delay_off": 5, - "availability_template": "{{ is_state('sensor.test_state','on') }}", + ( + { + "binary_sensor": { + "platform": "template", + "sensors": { + "test": { + "friendly_name": "virtual thingy", + "value_template": "true", + "device_class": "motion", + "delay_off": 5, + "availability_template": "{{ is_state('sensor.test_state','on') }}", + }, }, }, }, - }, + binary_sensor.DOMAIN, + "binary_sensor.test", + ), + ( + { + "template": { + "binary_sensor": { + "name": "virtual thingy", + "state": "true", + "device_class": "motion", + "delay_off": 5, + "availability": "{{ is_state('sensor.test_state','on') }}", + }, + }, + }, + template.DOMAIN, + "binary_sensor.virtual_thingy", + ), ], ) -async def test_availability_template(hass, start_ha): +async def test_availability_template(hass, start_ha, entity_id): """Test availability template.""" hass.states.async_set("sensor.test_state", STATE_OFF) await hass.async_block_till_done() - assert hass.states.get("binary_sensor.test").state == STATE_UNAVAILABLE + assert hass.states.get(entity_id).state == STATE_UNAVAILABLE hass.states.async_set("sensor.test_state", STATE_ON) await hass.async_block_till_done() - state = hass.states.get("binary_sensor.test") + state = hass.states.get(entity_id) assert state.state != STATE_UNAVAILABLE assert state.attributes[ATTR_DEVICE_CLASS] == "motion"