Skip to content

Commit ee35fc4

Browse files
authored
Improve derivative sensor tests (home-assistant#148941)
1 parent 9373bb2 commit ee35fc4

File tree

1 file changed

+85
-34
lines changed

1 file changed

+85
-34
lines changed

tests/components/derivative/test_sensor.py

Lines changed: 85 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,25 @@
2727
mock_restore_cache_with_extra_data,
2828
)
2929

30-
31-
async def test_state(hass: HomeAssistant) -> None:
30+
A1 = {"attr": "value1"}
31+
A2 = {"attr": "value2"}
32+
33+
34+
@pytest.mark.parametrize("force_update", [False, True])
35+
@pytest.mark.parametrize(
36+
"attributes",
37+
[
38+
# Same attributes, fires state report
39+
[A1, A1],
40+
# Changing attributes, fires state change with bumped last_updated
41+
[A1, A2],
42+
],
43+
)
44+
async def test_state(
45+
hass: HomeAssistant,
46+
force_update: bool,
47+
attributes: list[dict[str, Any]],
48+
) -> None:
3249
"""Test derivative sensor state."""
3350
config = {
3451
"sensor": {
@@ -45,12 +62,13 @@ async def test_state(hass: HomeAssistant) -> None:
4562
entity_id = config["sensor"]["source"]
4663
base = dt_util.utcnow()
4764
with freeze_time(base) as freezer:
48-
hass.states.async_set(entity_id, 1, {})
49-
await hass.async_block_till_done()
65+
for extra_attributes in attributes:
66+
hass.states.async_set(
67+
entity_id, 1, extra_attributes, force_update=force_update
68+
)
69+
await hass.async_block_till_done()
5070

51-
freezer.move_to(dt_util.utcnow() + timedelta(seconds=3600))
52-
hass.states.async_set(entity_id, 1, {})
53-
await hass.async_block_till_done()
71+
freezer.move_to(dt_util.utcnow() + timedelta(seconds=3600))
5472

5573
state = hass.states.get("sensor.derivative")
5674
assert state is not None
@@ -61,7 +79,24 @@ async def test_state(hass: HomeAssistant) -> None:
6179
assert state.attributes.get("unit_of_measurement") == "kW"
6280

6381

64-
async def test_no_change(hass: HomeAssistant) -> None:
82+
# Test unchanged states work both with and without max_sub_interval
83+
@pytest.mark.parametrize("extra_config", [{}, {"max_sub_interval": {"minutes": 9999}}])
84+
@pytest.mark.parametrize("force_update", [False, True])
85+
@pytest.mark.parametrize(
86+
"attributes",
87+
[
88+
# Same attributes, fires state report
89+
[A1, A1, A1, A1],
90+
# Changing attributes, fires state change with bumped last_updated
91+
[A1, A2, A1, A2],
92+
],
93+
)
94+
async def test_no_change(
95+
hass: HomeAssistant,
96+
extra_config: dict[str, Any],
97+
force_update: bool,
98+
attributes: list[dict[str, Any]],
99+
) -> None:
65100
"""Test derivative sensor state updated when source sensor doesn't change."""
66101
config = {
67102
"sensor": {
@@ -71,27 +106,21 @@ async def test_no_change(hass: HomeAssistant) -> None:
71106
"unit": "kW",
72107
"round": 2,
73108
}
109+
| extra_config
74110
}
75111

76112
assert await async_setup_component(hass, "sensor", config)
77113

78114
entity_id = config["sensor"]["source"]
79115
base = dt_util.utcnow()
80116
with freeze_time(base) as freezer:
81-
hass.states.async_set(entity_id, 0, {})
82-
await hass.async_block_till_done()
83-
84-
freezer.move_to(dt_util.utcnow() + timedelta(seconds=3600))
85-
hass.states.async_set(entity_id, 1, {})
86-
await hass.async_block_till_done()
87-
88-
freezer.move_to(dt_util.utcnow() + timedelta(seconds=3600))
89-
hass.states.async_set(entity_id, 1, {})
90-
await hass.async_block_till_done()
117+
for value, extra_attributes in zip([0, 1, 1, 1], attributes, strict=True):
118+
hass.states.async_set(
119+
entity_id, value, extra_attributes, force_update=force_update
120+
)
121+
await hass.async_block_till_done()
91122

92-
freezer.move_to(dt_util.utcnow() + timedelta(seconds=3600))
93-
hass.states.async_set(entity_id, 1, {})
94-
await hass.async_block_till_done()
123+
freezer.move_to(dt_util.utcnow() + timedelta(seconds=3600))
95124

96125
state = hass.states.get("sensor.derivative")
97126
assert state is not None
@@ -138,7 +167,7 @@ async def setup_tests(
138167
# Testing a energy sensor with non-monotonic intervals and values
139168
base = dt_util.utcnow()
140169
with freeze_time(base) as freezer:
141-
for time, value in zip(times, values, strict=False):
170+
for time, value in zip(times, values, strict=True):
142171
freezer.move_to(base + timedelta(seconds=time))
143172
hass.states.async_set(entity_id, value, {})
144173
await hass.async_block_till_done()
@@ -213,7 +242,24 @@ async def test_dataSet6(hass: HomeAssistant) -> None:
213242
await setup_tests(hass, {}, times=[0, 60], values=[0, 1 / 60], expected_state=1)
214243

215244

216-
async def test_data_moving_average_with_zeroes(hass: HomeAssistant) -> None:
245+
# Test unchanged states work both with and without max_sub_interval
246+
@pytest.mark.parametrize("extra_config", [{}, {"max_sub_interval": {"minutes": 9999}}])
247+
@pytest.mark.parametrize("force_update", [False, True])
248+
@pytest.mark.parametrize(
249+
"attributes",
250+
[
251+
# Same attributes, fires state report
252+
[A1, A1] * 10 + [A1],
253+
# Changing attributes, fires state change with bumped last_updated
254+
[A1, A2] * 10 + [A1],
255+
],
256+
)
257+
async def test_data_moving_average_with_zeroes(
258+
hass: HomeAssistant,
259+
extra_config: dict[str, Any],
260+
force_update: bool,
261+
attributes: list[dict[str, Any]],
262+
) -> None:
217263
"""Test that zeroes are properly handled within the time window."""
218264
# We simulate the following situation:
219265
# The temperature rises 1 °C per minute for 10 minutes long. Then, it
@@ -235,16 +281,21 @@ async def test_data_moving_average_with_zeroes(hass: HomeAssistant) -> None:
235281
"time_window": {"seconds": time_window},
236282
"unit_time": UnitOfTime.MINUTES,
237283
"round": 1,
238-
},
284+
}
285+
| extra_config,
239286
)
240287

241288
base = dt_util.utcnow()
242289
with freeze_time(base) as freezer:
243290
last_derivative = 0
244-
for time, value in zip(times, temperature_values, strict=True):
291+
for time, value, extra_attributes in zip(
292+
times, temperature_values, attributes, strict=True
293+
):
245294
now = base + timedelta(seconds=time)
246295
freezer.move_to(now)
247-
hass.states.async_set(entity_id, value, {})
296+
hass.states.async_set(
297+
entity_id, value, extra_attributes, force_update=force_update
298+
)
248299
await hass.async_block_till_done()
249300

250301
state = hass.states.get("sensor.power")
@@ -273,7 +324,7 @@ async def test_data_moving_average_for_discrete_sensor(hass: HomeAssistant) -> N
273324
for temperature in range(30):
274325
temperature_values += [temperature] * 2 # two values per minute
275326
time_window = 600
276-
times = list(range(0, 1800 + 30, 30))
327+
times = list(range(0, 1800, 30))
277328

278329
config, entity_id = await _setup_sensor(
279330
hass,
@@ -286,7 +337,7 @@ async def test_data_moving_average_for_discrete_sensor(hass: HomeAssistant) -> N
286337

287338
base = dt_util.utcnow()
288339
with freeze_time(base) as freezer:
289-
for time, value in zip(times, temperature_values, strict=False):
340+
for time, value in zip(times, temperature_values, strict=True):
290341
now = base + timedelta(seconds=time)
291342
freezer.move_to(now)
292343
hass.states.async_set(entity_id, value, {})
@@ -330,7 +381,7 @@ def temp_function(time):
330381

331382
base = dt_util.utcnow()
332383
with freeze_time(base) as freezer:
333-
for time, value in zip(times, temperature_values, strict=False):
384+
for time, value in zip(times, temperature_values, strict=True):
334385
now = base + timedelta(seconds=time)
335386
freezer.move_to(now)
336387
hass.states.async_set(entity_id, value, {})
@@ -368,7 +419,7 @@ async def test_double_signal_after_delay(hass: HomeAssistant) -> None:
368419
base = dt_util.utcnow()
369420
previous = 0
370421
with freeze_time(base) as freezer:
371-
for time, value in zip(times, temperature_values, strict=False):
422+
for time, value in zip(times, temperature_values, strict=True):
372423
now = base + timedelta(seconds=time)
373424
freezer.move_to(now)
374425
hass.states.async_set(entity_id, value, {})
@@ -506,7 +557,7 @@ async def test_sub_intervals_with_time_window(hass: HomeAssistant) -> None:
506557
base = dt_util.utcnow()
507558
with freeze_time(base) as freezer:
508559
last_state_change = None
509-
for time, value in zip(times, values, strict=False):
560+
for time, value in zip(times, values, strict=True):
510561
now = base + timedelta(seconds=time)
511562
freezer.move_to(now)
512563
hass.states.async_set(entity_id, value, {}, force_update=True)
@@ -636,7 +687,7 @@ async def test_total_increasing_reset(hass: HomeAssistant) -> None:
636687
actual_times = []
637688
actual_values = []
638689
with freeze_time(base_time) as freezer:
639-
for time, value in zip(times, values, strict=False):
690+
for time, value in zip(times, values, strict=True):
640691
current_time = base_time + timedelta(seconds=time)
641692
freezer.move_to(current_time)
642693
hass.states.async_set(
@@ -724,7 +775,7 @@ async def test_unavailable(
724775
# Testing a energy sensor with non-monotonic intervals and values
725776
base = dt_util.utcnow()
726777
with freeze_time(base) as freezer:
727-
for time, value, expect in zip(times, values, expected_state, strict=False):
778+
for time, value, expect in zip(times, values, expected_state, strict=True):
728779
freezer.move_to(base + timedelta(seconds=time))
729780
hass.states.async_set(entity_id, value, {})
730781
await hass.async_block_till_done()
@@ -759,7 +810,7 @@ async def test_unavailable_2(
759810

760811
base = dt_util.utcnow()
761812
with freeze_time(base) as freezer:
762-
for time, value in zip(times, values, strict=False):
813+
for time, value in zip(times, values, strict=True):
763814
freezer.move_to(base + timedelta(seconds=time))
764815
hass.states.async_set(entity_id, value, {})
765816
await hass.async_block_till_done()

0 commit comments

Comments
 (0)