mirror of
https://github.com/esphome/esphome.git
synced 2026-09-24 13:34:07 +00:00
Merge remote-tracking branch 'origin/dev' into esp8266-native-build-spec
This commit is contained in:
@@ -1394,6 +1394,35 @@ def test_entity_metadata_visibility_hints() -> None:
|
||||
assert web["web_server"].visibility is advanced
|
||||
|
||||
|
||||
def test_with_visibility_remarks_keys() -> None:
|
||||
"""``with_visibility`` re-marks the named keys, preserving each field's
|
||||
default and validator, without touching the other keys or the input schema.
|
||||
"""
|
||||
base = cv.Schema(
|
||||
{
|
||||
cv.Optional("a", default=7): cv.int_,
|
||||
cv.Optional("b", visibility=cv.Visibility.ADVANCED): cv.string,
|
||||
}
|
||||
)
|
||||
promoted = cv.with_visibility(base, cv.Visibility.UI, "a")
|
||||
|
||||
pm = {str(k): k for k in promoted.schema}
|
||||
assert pm["a"].visibility is cv.Visibility.UI # re-marked
|
||||
assert pm["a"].default() == 7 # default preserved
|
||||
assert pm["b"].visibility is cv.Visibility.ADVANCED # sibling untouched
|
||||
assert promoted({}) == {"a": 7} # validator/default still applied
|
||||
|
||||
# The input schema is left untouched (no shared-marker mutation).
|
||||
assert {str(k): k for k in base.schema}["a"].visibility is None
|
||||
|
||||
|
||||
def test_with_visibility_unknown_key_raises() -> None:
|
||||
"""A key not present in the schema is a typo — fail at build time."""
|
||||
base = cv.Schema({cv.Optional("a"): cv.int_})
|
||||
with pytest.raises(ValueError, match="not in schema"):
|
||||
cv.with_visibility(base, cv.Visibility.UI, "nope")
|
||||
|
||||
|
||||
def _wrap_str(value: str) -> ESPHomeDataBase:
|
||||
"""Wrap a raw string as an ESPHomeDataBase, mimicking a YAML-loaded value."""
|
||||
return make_data_base(value)
|
||||
|
||||
@@ -85,6 +85,15 @@ class TestCallExpression:
|
||||
assert actual == 'my_function<int32_t, float>(1, "2", false)'
|
||||
|
||||
|
||||
class TestStaticCastExpression:
|
||||
def test_str(self):
|
||||
target = cg.StaticCastExpression(ct.bool_, 42)
|
||||
|
||||
actual = str(target)
|
||||
|
||||
assert actual == "static_cast<bool>(42)"
|
||||
|
||||
|
||||
class TestStructInitializer:
|
||||
def test_str(self):
|
||||
target = cg.StructInitializer(
|
||||
@@ -229,6 +238,76 @@ class TestLambdaExpression:
|
||||
)
|
||||
|
||||
|
||||
class TestCallLambda:
|
||||
"""Tests for the call_lambda() function."""
|
||||
|
||||
def test_call_lambda__return_expression_casts_to_return_type(self):
|
||||
"""A lambda body that is just a return statement reduces to the
|
||||
expression, cast to the lambda's return type."""
|
||||
lamb = cg.LambdaExpression(("return foo + 1;",), (), "", ct.bool_)
|
||||
|
||||
result = cg.call_lambda(lamb)
|
||||
|
||||
assert isinstance(result, cg.StaticCastExpression)
|
||||
assert str(result) == "static_cast<bool>(foo + 1)"
|
||||
|
||||
def test_call_lambda__return_expression_with_class_return_type_no_cast(self):
|
||||
"""A class return type is not cast, since static_cast doesn't apply
|
||||
to arbitrary class types."""
|
||||
mock_class = cg.MockObjClass("foo::Bar", parents=())
|
||||
lamb = cg.LambdaExpression(("return get_bar();",), (), "", mock_class)
|
||||
|
||||
result = cg.call_lambda(lamb)
|
||||
|
||||
assert isinstance(result, cg.RawExpression)
|
||||
assert str(result) == "get_bar()"
|
||||
|
||||
def test_call_lambda__no_return_with_parameters_calls_with_names(self):
|
||||
"""A multi-statement lambda with parameters is called with the
|
||||
parameter names as arguments."""
|
||||
lamb = cg.LambdaExpression(
|
||||
("do_something(x, y);",), ((int, "x"), (float, "y")), "=", ct.bool_
|
||||
)
|
||||
|
||||
result = cg.call_lambda(lamb)
|
||||
|
||||
assert isinstance(result, cg.CallExpression)
|
||||
assert str(result) == (
|
||||
"[=](int32_t x, float y) -> bool {\n do_something(x, y);\n}(x, y)"
|
||||
)
|
||||
|
||||
def test_call_lambda__no_return_type_raises(self):
|
||||
"""Calling a lambda with no declared return type is a developer
|
||||
error: call_lambda is only for value-returning lambdas."""
|
||||
lamb = cg.LambdaExpression(("do_something();",), (), "=")
|
||||
|
||||
with pytest.raises(AssertionError):
|
||||
cg.call_lambda(lamb)
|
||||
|
||||
def test_call_lambda__identifier_starting_with_return_is_not_a_return_statement(
|
||||
self,
|
||||
):
|
||||
"""A body that merely starts with the substring "return" (e.g. a call
|
||||
to a function named returnValue()) must not be mistaken for a return
|
||||
statement -- the match requires a word boundary after "return"."""
|
||||
lamb = cg.LambdaExpression(("returnValue();",), (), "=", ct.bool_)
|
||||
|
||||
result = cg.call_lambda(lamb)
|
||||
|
||||
assert isinstance(result, cg.CallExpression)
|
||||
assert str(result) == "[=]() -> bool {\n returnValue();\n}()"
|
||||
|
||||
def test_call_lambda__no_return_no_parameters_calls_with_no_args(self):
|
||||
"""A multi-statement lambda without parameters is called with no
|
||||
arguments."""
|
||||
lamb = cg.LambdaExpression(("do_something();",), (), "", ct.bool_)
|
||||
|
||||
result = cg.call_lambda(lamb)
|
||||
|
||||
assert isinstance(result, cg.CallExpression)
|
||||
assert str(result) == "[]() -> bool {\n do_something();\n}()"
|
||||
|
||||
|
||||
class TestLiterals:
|
||||
@pytest.mark.parametrize(
|
||||
"target, expected",
|
||||
|
||||
@@ -187,6 +187,31 @@ def test_slot_counter_emits_requested_count() -> None:
|
||||
assert _define_value("TEST_SLOT_COUNT") == "2"
|
||||
|
||||
|
||||
def test_slot_counter_keyed_emits_largest_count() -> None:
|
||||
"""Keyed requests size storage every key declares at the same capacity:
|
||||
the define is the busiest key's count, not the total over all keys."""
|
||||
request = ch.slot_counter("TEST_SLOT_COUNT_KEYED")
|
||||
request("rx_a")
|
||||
request("rx_a")
|
||||
request("rx_a")
|
||||
request("rx_b")
|
||||
assert ch.get_slot_count("TEST_SLOT_COUNT_KEYED") == 3
|
||||
ch.CORE.flush_tasks()
|
||||
assert _define_value("TEST_SLOT_COUNT_KEYED") == "3"
|
||||
|
||||
|
||||
def test_slot_counter_rejects_mixed_keyed_and_unkeyed_requests() -> None:
|
||||
"""A keyed and an unkeyed request for one define cannot be sized together."""
|
||||
request = ch.slot_counter("TEST_SLOT_COUNT_MIXED")
|
||||
request("rx_a")
|
||||
with pytest.raises(ValueError, match="TEST_SLOT_COUNT_MIXED"):
|
||||
request()
|
||||
unkeyed = ch.slot_counter("TEST_SLOT_COUNT_MIXED_2")
|
||||
unkeyed()
|
||||
with pytest.raises(ValueError, match="TEST_SLOT_COUNT_MIXED_2"):
|
||||
unkeyed("rx_a")
|
||||
|
||||
|
||||
def test_slot_counter_without_requests_emits_nothing() -> None:
|
||||
"""No requests, no job, no define — the guarded storage compiles out."""
|
||||
ch.slot_counter("TEST_SLOT_COUNT_UNUSED")
|
||||
|
||||
@@ -416,6 +416,9 @@ def test_perform_ota_no_auth(
|
||||
"Update took 14.00 seconds (prepare 2.00, upload 5.00, commit 7.00)"
|
||||
in caplog.text
|
||||
)
|
||||
# The data phase timeout must outlast the device's 105 s data timeout
|
||||
mock_socket.settimeout.assert_any_call(espota2.DATA_PHASE_TIMEOUT)
|
||||
assert espota2.DATA_PHASE_TIMEOUT > 105.0
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_time")
|
||||
|
||||
@@ -1663,7 +1663,7 @@ def test_preinstall_runs_dependency_waves(tmp_path: Path) -> None:
|
||||
{"name": "SPI"},
|
||||
]
|
||||
m.dependency_to_spec.side_effect = lambda dep: _FakeSpec(name=dep["name"])
|
||||
pf._preinstall(m, [("noise-c@0.1.24", _FakeSpec(name="noise-c"))])
|
||||
pf._preinstall(m, [("noise-c@0.1.26", _FakeSpec(name="noise-c"))])
|
||||
assert installed == ["noise-c", "libsodium"] # dep deduped, SPI left out
|
||||
# The dep wave carries its compatibility so _install searches qualified
|
||||
dep_call = m._install.call_args_list[-1]
|
||||
@@ -1683,7 +1683,7 @@ def test_preinstall_dependency_wave_skips_seen_names(tmp_path: Path) -> None:
|
||||
m._install.side_effect = lambda spec, skip_dependencies, compatibility=None: (
|
||||
installed.append(getattr(spec, "name", str(spec)))
|
||||
)
|
||||
pf._preinstall(m, [("noise-c@0.1.24", _FakeSpec(name="noise-c"))])
|
||||
pf._preinstall(m, [("noise-c@0.1.26", _FakeSpec(name="noise-c"))])
|
||||
assert installed == ["noise-c"]
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user