Add tests for types and functions for type conversions in templates (#100807)
Co-authored-by: Robert Resch <robert@resch.dev>pull/98968/head
parent
8ca5df6fcc
commit
35d18a9a3e
|
@ -1956,6 +1956,41 @@ def is_number(value):
|
|||
return True
|
||||
|
||||
|
||||
def _is_list(value: Any) -> bool:
|
||||
"""Return whether a value is a list."""
|
||||
return isinstance(value, list)
|
||||
|
||||
|
||||
def _is_set(value: Any) -> bool:
|
||||
"""Return whether a value is a set."""
|
||||
return isinstance(value, set)
|
||||
|
||||
|
||||
def _is_tuple(value: Any) -> bool:
|
||||
"""Return whether a value is a tuple."""
|
||||
return isinstance(value, tuple)
|
||||
|
||||
|
||||
def _to_set(value: Any) -> set[Any]:
|
||||
"""Convert value to set."""
|
||||
return set(value)
|
||||
|
||||
|
||||
def _to_tuple(value):
|
||||
"""Convert value to tuple."""
|
||||
return tuple(value)
|
||||
|
||||
|
||||
def _is_datetime(value: Any) -> bool:
|
||||
"""Return whether a value is a datetime."""
|
||||
return isinstance(value, datetime)
|
||||
|
||||
|
||||
def _is_string_like(value: Any) -> bool:
|
||||
"""Return whether a value is a string or string like object."""
|
||||
return isinstance(value, (str, bytes, bytearray))
|
||||
|
||||
|
||||
def regex_match(value, find="", ignorecase=False):
|
||||
"""Match value using regex."""
|
||||
if not isinstance(value, str):
|
||||
|
@ -2387,6 +2422,8 @@ class TemplateEnvironment(ImmutableSandboxedEnvironment):
|
|||
self.globals["max"] = min_max_from_filter(self.filters["max"], "max")
|
||||
self.globals["min"] = min_max_from_filter(self.filters["min"], "min")
|
||||
self.globals["is_number"] = is_number
|
||||
self.globals["set"] = _to_set
|
||||
self.globals["tuple"] = _to_tuple
|
||||
self.globals["int"] = forgiving_int
|
||||
self.globals["pack"] = struct_pack
|
||||
self.globals["unpack"] = struct_unpack
|
||||
|
@ -2395,6 +2432,11 @@ class TemplateEnvironment(ImmutableSandboxedEnvironment):
|
|||
self.globals["bool"] = forgiving_boolean
|
||||
self.globals["version"] = version
|
||||
self.tests["is_number"] = is_number
|
||||
self.tests["list"] = _is_list
|
||||
self.tests["set"] = _is_set
|
||||
self.tests["tuple"] = _is_tuple
|
||||
self.tests["datetime"] = _is_datetime
|
||||
self.tests["string_like"] = _is_string_like
|
||||
self.tests["match"] = regex_match
|
||||
self.tests["search"] = regex_search
|
||||
self.tests["contains"] = contains
|
||||
|
|
|
@ -7,6 +7,7 @@ import json
|
|||
import logging
|
||||
import math
|
||||
import random
|
||||
from types import MappingProxyType
|
||||
from typing import Any
|
||||
from unittest.mock import patch
|
||||
|
||||
|
@ -43,6 +44,7 @@ from homeassistant.helpers.json import json_dumps
|
|||
from homeassistant.helpers.typing import TemplateVarsType
|
||||
from homeassistant.setup import async_setup_component
|
||||
import homeassistant.util.dt as dt_util
|
||||
from homeassistant.util.read_only_dict import ReadOnlyDict
|
||||
from homeassistant.util.unit_system import UnitSystem
|
||||
|
||||
from tests.common import MockConfigEntry, async_fire_time_changed
|
||||
|
@ -475,6 +477,171 @@ def test_isnumber(hass: HomeAssistant, value, expected) -> None:
|
|||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("value", "expected"),
|
||||
[
|
||||
([1, 2], True),
|
||||
({1, 2}, False),
|
||||
({"a": 1, "b": 2}, False),
|
||||
(ReadOnlyDict({"a": 1, "b": 2}), False),
|
||||
(MappingProxyType({"a": 1, "b": 2}), False),
|
||||
("abc", False),
|
||||
(b"abc", False),
|
||||
((1, 2), False),
|
||||
(datetime(2024, 1, 1, 0, 0, 0), False),
|
||||
],
|
||||
)
|
||||
def test_is_list(hass: HomeAssistant, value: Any, expected: bool) -> None:
|
||||
"""Test is list."""
|
||||
assert (
|
||||
template.Template("{{ value is list }}", hass).async_render({"value": value})
|
||||
== expected
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("value", "expected"),
|
||||
[
|
||||
([1, 2], False),
|
||||
({1, 2}, True),
|
||||
({"a": 1, "b": 2}, False),
|
||||
(ReadOnlyDict({"a": 1, "b": 2}), False),
|
||||
(MappingProxyType({"a": 1, "b": 2}), False),
|
||||
("abc", False),
|
||||
(b"abc", False),
|
||||
((1, 2), False),
|
||||
(datetime(2024, 1, 1, 0, 0, 0), False),
|
||||
],
|
||||
)
|
||||
def test_is_set(hass: HomeAssistant, value: Any, expected: bool) -> None:
|
||||
"""Test is set."""
|
||||
assert (
|
||||
template.Template("{{ value is set }}", hass).async_render({"value": value})
|
||||
== expected
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("value", "expected"),
|
||||
[
|
||||
([1, 2], False),
|
||||
({1, 2}, False),
|
||||
({"a": 1, "b": 2}, False),
|
||||
(ReadOnlyDict({"a": 1, "b": 2}), False),
|
||||
(MappingProxyType({"a": 1, "b": 2}), False),
|
||||
("abc", False),
|
||||
(b"abc", False),
|
||||
((1, 2), True),
|
||||
(datetime(2024, 1, 1, 0, 0, 0), False),
|
||||
],
|
||||
)
|
||||
def test_is_tuple(hass: HomeAssistant, value: Any, expected: bool) -> None:
|
||||
"""Test is tuple."""
|
||||
assert (
|
||||
template.Template("{{ value is tuple }}", hass).async_render({"value": value})
|
||||
== expected
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("value", "expected"),
|
||||
[
|
||||
([1, 2], {1, 2}),
|
||||
({1, 2}, {1, 2}),
|
||||
({"a": 1, "b": 2}, {"a", "b"}),
|
||||
(ReadOnlyDict({"a": 1, "b": 2}), {"a", "b"}),
|
||||
(MappingProxyType({"a": 1, "b": 2}), {"a", "b"}),
|
||||
("abc", {"a", "b", "c"}),
|
||||
(b"abc", {97, 98, 99}),
|
||||
((1, 2), {1, 2}),
|
||||
],
|
||||
)
|
||||
def test_set(hass: HomeAssistant, value: Any, expected: bool) -> None:
|
||||
"""Test convert to set function."""
|
||||
assert (
|
||||
template.Template("{{ set(value) }}", hass).async_render({"value": value})
|
||||
== expected
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("value", "expected"),
|
||||
[
|
||||
([1, 2], (1, 2)),
|
||||
({1, 2}, (1, 2)),
|
||||
({"a": 1, "b": 2}, ("a", "b")),
|
||||
(ReadOnlyDict({"a": 1, "b": 2}), ("a", "b")),
|
||||
(MappingProxyType({"a": 1, "b": 2}), ("a", "b")),
|
||||
("abc", ("a", "b", "c")),
|
||||
(b"abc", (97, 98, 99)),
|
||||
((1, 2), (1, 2)),
|
||||
],
|
||||
)
|
||||
def test_tuple(hass: HomeAssistant, value: Any, expected: bool) -> None:
|
||||
"""Test convert to tuple function."""
|
||||
assert (
|
||||
template.Template("{{ tuple(value) }}", hass).async_render({"value": value})
|
||||
== expected
|
||||
)
|
||||
|
||||
|
||||
def test_converting_datetime_to_iterable(hass: HomeAssistant) -> None:
|
||||
"""Test converting a datetime to an iterable raises an error."""
|
||||
dt_ = datetime(2020, 1, 1, 0, 0, 0)
|
||||
with pytest.raises(TemplateError):
|
||||
template.Template("{{ tuple(value) }}", hass).async_render({"value": dt_})
|
||||
with pytest.raises(TemplateError):
|
||||
template.Template("{{ set(value) }}", hass).async_render({"value": dt_})
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("value", "expected"),
|
||||
[
|
||||
([1, 2], False),
|
||||
({1, 2}, False),
|
||||
({"a": 1, "b": 2}, False),
|
||||
(ReadOnlyDict({"a": 1, "b": 2}), False),
|
||||
(MappingProxyType({"a": 1, "b": 2}), False),
|
||||
("abc", False),
|
||||
(b"abc", False),
|
||||
((1, 2), False),
|
||||
(datetime(2024, 1, 1, 0, 0, 0), True),
|
||||
],
|
||||
)
|
||||
def test_is_datetime(hass: HomeAssistant, value, expected) -> None:
|
||||
"""Test is datetime."""
|
||||
assert (
|
||||
template.Template("{{ value is datetime }}", hass).async_render(
|
||||
{"value": value}
|
||||
)
|
||||
== expected
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("value", "expected"),
|
||||
[
|
||||
([1, 2], False),
|
||||
({1, 2}, False),
|
||||
({"a": 1, "b": 2}, False),
|
||||
(ReadOnlyDict({"a": 1, "b": 2}), False),
|
||||
(MappingProxyType({"a": 1, "b": 2}), False),
|
||||
("abc", True),
|
||||
(b"abc", True),
|
||||
((1, 2), False),
|
||||
(datetime(2024, 1, 1, 0, 0, 0), False),
|
||||
],
|
||||
)
|
||||
def test_is_string_like(hass: HomeAssistant, value, expected) -> None:
|
||||
"""Test is string_like."""
|
||||
assert (
|
||||
template.Template("{{ value is string_like }}", hass).async_render(
|
||||
{"value": value}
|
||||
)
|
||||
== expected
|
||||
)
|
||||
|
||||
|
||||
def test_rounding_value(hass: HomeAssistant) -> None:
|
||||
"""Test rounding value."""
|
||||
hass.states.async_set("sensor.temperature", 12.78)
|
||||
|
|
Loading…
Reference in New Issue