Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
ComponentPoolStatusTracker,
)
from frequenz.sdk.timeseries import ResamplerConfig, Sample3Phase
from frequenz.sdk.timeseries.ev_charger_pool import EVChargerPool, EVChargerPoolReport
from frequenz.sdk.timeseries.ev_charger_pool import EVChargerPoolReport

from ...microgrid.fixtures import _Mocks
from ...utils.component_data_streamer import MockComponentDataStreamer
Expand Down Expand Up @@ -140,9 +140,26 @@ async def _init_ev_chargers(self, mocks: _Mocks) -> None:
0.05,
)

async def _recv_reports_until(
self,
bounds_rx: Receiver[EVChargerPoolReport],
check: typing.Callable[[EVChargerPoolReport], bool],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: check -> predicate? (oh, I see this is in tests, so nitpick³)

) -> EVChargerPoolReport | None:
"""Receive reports until the given condition is met."""
max_reports = 10
ctr = 0
latest_report: EVChargerPoolReport | None = None
while ctr < max_reports:
ctr += 1
latest_report = await bounds_rx.receive()
if check(latest_report):
break

return latest_report

def _assert_report( # pylint: disable=too-many-arguments
self,
report: EVChargerPoolReport,
report: EVChargerPoolReport | None,
*,
power: float | None,
lower: float,
Expand All @@ -152,7 +169,7 @@ def _assert_report( # pylint: disable=too-many-arguments
typing.Callable[[_power_distributing.Result], bool] | None
) = None,
) -> None:
assert report.target_power == (
assert report is not None and report.target_power == (
Power.from_watts(power) if power is not None else None
)
assert report.bounds is not None
Expand All @@ -162,24 +179,6 @@ def _assert_report( # pylint: disable=too-many-arguments
assert dist_result is not None
assert expected_result_pred(dist_result)

async def _get_bounds_receiver(
self, ev_charger_pool: EVChargerPool
) -> Receiver[EVChargerPoolReport]:
bounds_rx = ev_charger_pool.power_status.new_receiver()

# Consume initial reports as chargers are initialized
expected_upper_bounds = 44160.0
max_reports = 10
ctr = 0
while ctr < max_reports:
ctr += 1
report = await bounds_rx.receive()
assert report.bounds is not None
if report.bounds.upper == Power.from_watts(expected_upper_bounds):
break

return bounds_rx

async def test_setting_power(
self,
mocks: _Mocks,
Expand All @@ -197,20 +196,21 @@ async def test_setting_power(
await self._patch_ev_pool_status(mocks, mocker)
await self._patch_power_distributing_actor(mocker)

bounds_rx = await self._get_bounds_receiver(ev_charger_pool)
bounds_rx = ev_charger_pool.power_status.new_receiver()
latest_report = await self._recv_reports_until(
bounds_rx,
lambda x: x.bounds is not None and x.bounds.upper.as_watts() == 44160.0,
)

self._assert_report(latest_report, power=None, lower=0.0, upper=44160.0)

# Check that chargers are initialized to Power.zero()
assert set_power.call_count == 4
assert all(x.args[1] == 0.0 for x in set_power.call_args_list)

self._assert_report(
await bounds_rx.receive(), power=None, lower=0.0, upper=44160.0
)

set_power.reset_mock()
await ev_charger_pool.propose_power(Power.from_watts(40000.0))
# ignore one report because it is not always immediately updated.
await bounds_rx.receive()
self._assert_report(
await bounds_rx.receive(), power=40000.0, lower=0.0, upper=44160.0
)
Expand Down
19 changes: 10 additions & 9 deletions tests/timeseries/_pv_pool/test_pv_pool_control_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ async def _fail_pv_inverters(self, fail_ids: list[int], mocks: _Mocks) -> None:

def _assert_report( # pylint: disable=too-many-arguments
self,
report: PVPoolReport,
report: PVPoolReport | None,
*,
power: float | None,
lower: float,
Expand All @@ -105,7 +105,7 @@ def _assert_report( # pylint: disable=too-many-arguments
typing.Callable[[_power_distributing.Result], bool] | None
) = None,
) -> None:
assert report.target_power == (
assert report is not None and report.target_power == (
Power.from_watts(power) if power is not None else None
)
assert report.bounds is not None
Expand All @@ -119,16 +119,19 @@ async def _recv_reports_until(
self,
bounds_rx: Receiver[PVPoolReport],
check: typing.Callable[[PVPoolReport], bool],
) -> None:
) -> PVPoolReport | None:
"""Receive reports until the given condition is met."""
max_reports = 10
ctr = 0
latest_report: PVPoolReport | None = None
while ctr < max_reports:
ctr += 1
report = await bounds_rx.receive()
if check(report):
latest_report = await bounds_rx.receive()
if check(latest_report):
break

return latest_report

async def test_setting_power( # pylint: disable=too-many-statements
self,
mocks: _Mocks,
Expand All @@ -142,13 +145,11 @@ async def test_setting_power( # pylint: disable=too-many-statements
await self._init_pv_inverters(mocks)
pv_pool = microgrid.new_pv_pool(priority=5)
bounds_rx = pv_pool.power_status.new_receiver()
await self._recv_reports_until(
latest_report = await self._recv_reports_until(
bounds_rx,
lambda x: x.bounds is not None and x.bounds.lower.as_watts() == -100000.0,
)
self._assert_report(
await bounds_rx.receive(), power=None, lower=-100000.0, upper=0.0
)
self._assert_report(latest_report, power=None, lower=-100000.0, upper=0.0)
await pv_pool.propose_power(Power.from_watts(-80000.0))
await self._recv_reports_until(
bounds_rx,
Expand Down