Skip to content

Commit e6069e1

Browse files
committed
Parametrize producer fallback test
This makes it easier to see which case failed. Signed-off-by: Leandro Lucarella <[email protected]>
1 parent 7b2f9ae commit e6069e1

File tree

1 file changed

+52
-68
lines changed

1 file changed

+52
-68
lines changed

tests/timeseries/test_producer.py

Lines changed: 52 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from contextlib import AsyncExitStack
77

8+
import pytest
89
from frequenz.quantities import Power
910
from pytest_mock import MockerFixture
1011

@@ -96,7 +97,44 @@ async def test_no_producer_power(self, mocker: MockerFixture) -> None:
9697
0.0
9798
)
9899

99-
async def test_producer_fallback_formula(self, mocker: MockerFixture) -> None:
100+
@pytest.mark.parametrize(
101+
"meter_power,pv_inverter_power,chp_power,expected_power",
102+
[
103+
# Add power from meters and chp
104+
([-1.0, -2.0], [None, -200.0], [300], Power.from_watts(297.0)),
105+
([-1.0, -10], [-100.0, -200.0], [400], Power.from_watts(389.0)),
106+
# Case 2: The first meter is unavailable (None)
107+
([None, -2.0], [-100, -200.0], [400], None),
108+
# Case 3: First meter is unavailable (None). Fallback inverter provides value
109+
([None, -2.0], [-100, -200.0], [400], Power.from_watts(298.0)),
110+
([None, -2.0], [-50, -200.0], [300], Power.from_watts(248.0)),
111+
# Case 4: Both first meter and its fallback inverter are unavailable
112+
([None, -2.0], [None, -200.0], [300], Power.from_watts(298.0)),
113+
([None, -10.0], [-20.0, -200.0], [300], Power.from_watts(270.0)),
114+
# Case 5: CHP is unavailable
115+
([None, -10.0], [-20.0, -200.0], [None], Power.from_watts(-30.0)),
116+
# Case 6: Both meters are unavailable (None)
117+
([None, None], [-20.0, -200.0], [None], None),
118+
([None, None], [-20.0, -200.0], [None], Power.from_watts(-220.0)),
119+
([None, None], [None, -200.0], [None], Power.from_watts(-200.0)),
120+
# Case 7: All components are unavailable (None)
121+
([None, None], [None, None], [None], Power.from_watts(0)),
122+
([None, None], [None, None], [None], Power.from_watts(0)),
123+
([None, None], [None, None], [300.0], Power.from_watts(300.0)),
124+
([-200.0, None], [None, -100.0], [50.0], Power.from_watts(-250.0)),
125+
([-200.0, -200.0], [-10.0, -20.0], [50.0], Power.from_watts(-350.0)),
126+
# Case 8: Meter is unavailable but we already subscribed for inverter
127+
([None, -200.0], [-10.0, -100.0], [50.0], Power.from_watts(-160.0)),
128+
],
129+
)
130+
async def test_producer_fallback_formula(
131+
self,
132+
mocker: MockerFixture,
133+
meter_power: list[float | None],
134+
pv_inverter_power: list[float | None],
135+
chp_power: list[float | None],
136+
expected_power: Power | None,
137+
) -> None:
100138
"""Test the producer power formula with fallback formulas."""
101139
mockgrid = MockMicrogrid(grid_meter=False, mocker=mocker)
102140
mockgrid.add_solar_inverters(2)
@@ -108,70 +146,16 @@ async def test_producer_fallback_formula(self, mocker: MockerFixture) -> None:
108146
stack.push_async_callback(producer.stop)
109147
producer_power_receiver = producer.power.new_receiver()
110148

111-
# Note: ProducerPowerFormula has a "nones-are-zero" rule, that says:
112-
# * if the meter value is None, it should be treated as None.
113-
# * for other components None is treated as 0.
114-
115-
# fmt: off
116-
expected_input_output: list[
117-
tuple[list[float | None], list[float | None], list[float | None], Power | None]
118-
] = [
119-
# ([pv_meter_power], [pv_inverter_power], [chp_power], expected_power)
120-
# Add power from meters and chp
121-
([-1.0, -2.0], [None, -200.0], [300], Power.from_watts(297.0)),
122-
([-1.0, -10], [-100.0, -200.0], [400], Power.from_watts(389.0)),
123-
# Case 2: The first meter is unavailable (None).
124-
# Subscribe to the fallback inverter, but return None as the result,
125-
# according to the "nones-are-zero" rule
126-
([None, -2.0], [-100, -200.0], [400], None),
127-
# Case 3: First meter is unavailable (None). Fallback inverter provides
128-
# a value.
129-
# Add second meter, first inverter and chp power
130-
([None, -2.0], [-100, -200.0], [400], Power.from_watts(298.0)),
131-
([None, -2.0], [-50, -200.0], [300], Power.from_watts(248.0)),
132-
# Case 4: Both first meter and its fallback inverter are unavailable
133-
# (None). Return 0 from failing component according to the
134-
# "nones-are-zero" rule.
135-
([None, -2.0], [None, -200.0], [300], Power.from_watts(298.0)),
136-
([None, -10.0], [-20.0, -200.0], [300], Power.from_watts(270.0)),
137-
# Case 5: CHP is unavailable. Return 0 from failing component
138-
# according to the "nones-are-zero" rule.
139-
([None, -10.0], [-20.0, -200.0], [None], Power.from_watts(-30.0)),
140-
# Case 6: Both meters are unavailable (None). Subscribe for fallback inverter
141-
([None, None], [-20.0, -200.0], [None], None),
142-
([None, None], [-20.0, -200.0], [None], Power.from_watts(-220.0)),
143-
([None, None], [None, -200.0], [None], Power.from_watts(-200.0)),
144-
# Case 7: All components are unavailable (None). Return 0 according to the
145-
# "nones-are-zero" rule.
146-
([None, None], [None, None], [None], Power.from_watts(0)),
147-
([None, None], [None, None], [None], Power.from_watts(0)),
148-
([None, None], [None, None], [300.0], Power.from_watts(300.0)),
149-
([-200.0, None], [None, -100.0], [50.0], Power.from_watts(-250.0)),
150-
([-200.0, -200.0], [-10.0, -20.0], [50.0], Power.from_watts(-350.0)),
151-
# Case 8: Meter is unavailable but we already subscribed for inverter
152-
# So don't return None in this case. Just proper formula result.
153-
([None, -200.0], [-10.0, -100.0], [50.0], Power.from_watts(-160.0)),
154-
155-
]
156-
# fmt: on
157-
158-
for idx, (
159-
meter_power,
160-
pv_inverter_power,
161-
chp_power,
162-
expected_power,
163-
) in enumerate(expected_input_output):
164-
await mockgrid.mock_resampler.send_chp_power(chp_power)
165-
await mockgrid.mock_resampler.send_meter_power(meter_power)
166-
await mockgrid.mock_resampler.send_pv_inverter_power(pv_inverter_power)
167-
mockgrid.mock_resampler.next_ts()
168-
169-
result = await producer_power_receiver.receive()
170-
assert result.value == expected_power, (
171-
f"Test case {idx} failed:"
172-
+ f" meter_power: {meter_power}"
173-
+ f" pv_inverter_power {pv_inverter_power}"
174-
+ f" chp_power {chp_power}"
175-
+ f" expected_power: {expected_power}"
176-
+ f" actual_power: {result.value}"
177-
)
149+
await mockgrid.mock_resampler.send_chp_power(chp_power)
150+
await mockgrid.mock_resampler.send_meter_power(meter_power)
151+
await mockgrid.mock_resampler.send_pv_inverter_power(pv_inverter_power)
152+
mockgrid.mock_resampler.next_ts()
153+
154+
result = await producer_power_receiver.receive()
155+
assert result.value == expected_power, (
156+
f"meter_power: {meter_power}"
157+
+ f" pv_inverter_power {pv_inverter_power}"
158+
+ f" chp_power {chp_power}"
159+
+ f" expected_power: {expected_power}"
160+
+ f" actual_power: {result.value}"
161+
)

0 commit comments

Comments
 (0)