Skip to content

Commit 72178a2

Browse files
authored
Add BatteryStatus tests to ensure it recovers automatically after issues (#522)
This PR also does: 1. Improve usability of MockMicrogrid 2. Update PowerDistributor tests to use MockMicrogrid 3. And some small bug fixes and code quality improvements
2 parents 8c9bcae + d38764e commit 72178a2

File tree

7 files changed

+734
-317
lines changed

7 files changed

+734
-317
lines changed

src/frequenz/sdk/actor/power_distributing/_battery_status.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ class _BlockingStatus:
8181
max_duration_sec: float
8282

8383
def __post_init__(self) -> None:
84+
assert self.min_duration_sec <= self.max_duration_sec, (
85+
f"Minimum blocking duration ({self.min_duration_sec}) cannot be greater "
86+
f"than maximum blocking duration ({self.max_duration_sec})"
87+
)
8488
self.last_blocking_duration_sec: float = self.min_duration_sec
8589
self.blocked_until: Optional[datetime] = None
8690

@@ -335,9 +339,25 @@ async def _run(
335339
self._handle_status_set_power_result(selected.value)
336340

337341
elif selected_from(selected, battery_timer):
342+
if (
343+
datetime.now(tz=timezone.utc)
344+
- self._battery.last_msg_timestamp
345+
) < timedelta(seconds=self._max_data_age):
346+
# This means that we have received data from the battery
347+
# since the timer triggered, but the timer event arrived
348+
# late, so we can ignore it.
349+
continue
338350
self._handle_status_battery_timer()
339351

340352
elif selected_from(selected, inverter_timer):
353+
if (
354+
datetime.now(tz=timezone.utc)
355+
- self._inverter.last_msg_timestamp
356+
) < timedelta(seconds=self._max_data_age):
357+
# This means that we have received data from the inverter
358+
# since the timer triggered, but the timer event arrived
359+
# late, so we can ignore it.
360+
continue
341361
self._handle_status_inverter_timer()
342362

343363
else:

tests/actor/test_battery_pool_status.py

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import asyncio
66
from typing import Set
77

8-
import pytest
98
from frequenz.channels import Broadcast
109
from pytest_mock import MockerFixture
1110

@@ -14,41 +13,30 @@
1413
BatteryStatus,
1514
)
1615
from frequenz.sdk.microgrid.component import ComponentCategory
16+
from tests.timeseries.mock_microgrid import MockMicrogrid
1717

18-
from ..utils.mock_microgrid_client import MockMicrogridClient
19-
from .test_battery_status import battery_data, component_graph, inverter_data
18+
from .test_battery_status import battery_data, inverter_data
2019

2120

2221
# pylint: disable=protected-access
2322
class TestBatteryPoolStatus:
2423
"""Tests for BatteryPoolStatus"""
2524

26-
@pytest.fixture
27-
async def mock_microgrid(self, mocker: MockerFixture) -> MockMicrogridClient:
28-
"""Create and initialize mock microgrid
29-
30-
Args:
31-
mocker: pytest mocker
32-
33-
Returns:
34-
MockMicrogridClient
35-
"""
36-
components, connections = component_graph()
37-
microgrid = MockMicrogridClient(components, connections)
38-
microgrid.initialize(mocker)
39-
return microgrid
40-
41-
async def test_batteries_status(self, mock_microgrid: MockMicrogridClient) -> None:
25+
async def test_batteries_status(self, mocker: MockerFixture) -> None:
4226
"""Basic tests for BatteryPoolStatus.
4327
4428
BatteryStatusTracker is more tested in its own unit tests.
4529
4630
Args:
4731
mock_microgrid: mock microgrid client
4832
"""
33+
mock_microgrid = MockMicrogrid(grid_side_meter=True)
34+
mock_microgrid.add_batteries(3)
35+
await mock_microgrid.start(mocker)
36+
4937
batteries = {
5038
battery.component_id
51-
for battery in mock_microgrid.component_graph.components(
39+
for battery in mock_microgrid.mock_client.component_graph.components(
5240
component_category={ComponentCategory.BATTERY}
5341
)
5442
}
@@ -67,22 +55,34 @@ async def test_batteries_status(self, mock_microgrid: MockMicrogridClient) -> No
6755

6856
batteries_list = list(batteries)
6957

70-
await mock_microgrid.send(battery_data(component_id=batteries_list[0]))
58+
await mock_microgrid.mock_client.send(
59+
battery_data(component_id=batteries_list[0])
60+
)
7161
await asyncio.sleep(0.1)
7262
assert batteries_status.get_working_batteries(batteries) == expected_working
7363

7464
expected_working.add(batteries_list[0])
75-
await mock_microgrid.send(inverter_data(component_id=batteries_list[0] - 1))
65+
await mock_microgrid.mock_client.send(
66+
inverter_data(component_id=batteries_list[0] - 1)
67+
)
7668
await asyncio.sleep(0.1)
7769
assert batteries_status.get_working_batteries(batteries) == expected_working
7870
msg = await asyncio.wait_for(battery_status_recv.receive(), timeout=0.2)
7971
assert msg == batteries_status._current_status
8072

81-
await mock_microgrid.send(inverter_data(component_id=batteries_list[1] - 1))
82-
await mock_microgrid.send(battery_data(component_id=batteries_list[1]))
73+
await mock_microgrid.mock_client.send(
74+
inverter_data(component_id=batteries_list[1] - 1)
75+
)
76+
await mock_microgrid.mock_client.send(
77+
battery_data(component_id=batteries_list[1])
78+
)
8379

84-
await mock_microgrid.send(inverter_data(component_id=batteries_list[2] - 1))
85-
await mock_microgrid.send(battery_data(component_id=batteries_list[2]))
80+
await mock_microgrid.mock_client.send(
81+
inverter_data(component_id=batteries_list[2] - 1)
82+
)
83+
await mock_microgrid.mock_client.send(
84+
battery_data(component_id=batteries_list[2])
85+
)
8686

8787
expected_working = set(batteries_list)
8888
await asyncio.sleep(0.1)
@@ -91,15 +91,15 @@ async def test_batteries_status(self, mock_microgrid: MockMicrogridClient) -> No
9191
assert msg == batteries_status._current_status
9292

9393
await batteries_status.update_status(
94-
succeed_batteries={106}, failed_batteries={206, 306}
94+
succeed_batteries={9}, failed_batteries={19, 29}
9595
)
9696
await asyncio.sleep(0.1)
97-
assert batteries_status.get_working_batteries(batteries) == {106}
97+
assert batteries_status.get_working_batteries(batteries) == {9}
9898

9999
await batteries_status.update_status(
100-
succeed_batteries={106, 206}, failed_batteries=set()
100+
succeed_batteries={9, 19}, failed_batteries=set()
101101
)
102102
await asyncio.sleep(0.1)
103-
assert batteries_status.get_working_batteries(batteries) == {106, 206}
103+
assert batteries_status.get_working_batteries(batteries) == {9, 19}
104104

105105
await batteries_status.stop()

0 commit comments

Comments
 (0)