|
4 | 4 | """Tests for the Matryoshka power manager algorithm.""" |
5 | 5 |
|
6 | 6 | import asyncio |
| 7 | +import re |
7 | 8 | from datetime import datetime, timedelta, timezone |
8 | 9 |
|
| 10 | +import pytest |
9 | 11 | from frequenz.quantities import Power |
10 | 12 |
|
11 | 13 | from frequenz.sdk import timeseries |
@@ -36,13 +38,14 @@ def tgt_power( # pylint: disable=too-many-arguments,too-many-positional-argumen |
36 | 38 | expected: float | None, |
37 | 39 | creation_time: float | None = None, |
38 | 40 | must_send: bool = False, |
| 41 | + batteries: frozenset[int] | None = None, |
39 | 42 | ) -> None: |
40 | 43 | """Test the target power calculation.""" |
41 | 44 | self._call_count += 1 |
42 | 45 | tgt_power = self.algorithm.calculate_target_power( |
43 | | - self._batteries, |
| 46 | + self._batteries if batteries is None else batteries, |
44 | 47 | Proposal( |
45 | | - component_ids=self._batteries, |
| 48 | + component_ids=self._batteries if batteries is None else batteries, |
46 | 49 | source_id=f"actor-{priority}", |
47 | 50 | preferred_power=None if power is None else Power.from_watts(power), |
48 | 51 | bounds=timeseries.Bounds( |
@@ -368,6 +371,7 @@ async def test_matryoshka_drop_old_proposals() -> None: |
368 | 371 | With inclusion bounds, and exclusion bounds -30.0 to 30.0. |
369 | 372 | """ |
370 | 373 | batteries = frozenset({2, 5}) |
| 374 | + overlapping_batteries = frozenset({5, 8}) |
371 | 375 |
|
372 | 376 | system_bounds = _base_types.SystemBounds( |
373 | 377 | timestamp=datetime.now(tz=timezone.utc), |
@@ -424,3 +428,58 @@ async def test_matryoshka_drop_old_proposals() -> None: |
424 | 428 | tester.tgt_power( |
425 | 429 | priority=1, power=20.0, bounds=(20.0, 50.0), expected=25.0, must_send=True |
426 | 430 | ) |
| 431 | + |
| 432 | + # When all proposals are too old, they are dropped, and the buckets are dropped as |
| 433 | + # well. After that, sending a request for a different but overlapping bucket will |
| 434 | + # succeed. And it will fail until then. |
| 435 | + with pytest.raises( |
| 436 | + NotImplementedError, |
| 437 | + match=re.escape( |
| 438 | + "PowerManagingActor: component IDs frozenset({8, 5}) are already " |
| 439 | + + "part of another bucket. Overlapping buckets are not yet supported." |
| 440 | + ), |
| 441 | + ): |
| 442 | + tester.tgt_power( |
| 443 | + priority=1, |
| 444 | + power=25.0, |
| 445 | + bounds=(25.0, 50.0), |
| 446 | + expected=25.0, |
| 447 | + must_send=True, |
| 448 | + batteries=overlapping_batteries, |
| 449 | + ) |
| 450 | + |
| 451 | + tester.tgt_power( |
| 452 | + priority=1, |
| 453 | + power=25.0, |
| 454 | + bounds=(25.0, 50.0), |
| 455 | + creation_time=now - 70.0, |
| 456 | + expected=25.0, |
| 457 | + must_send=True, |
| 458 | + ) |
| 459 | + tester.tgt_power( |
| 460 | + priority=2, |
| 461 | + power=25.0, |
| 462 | + bounds=(25.0, 50.0), |
| 463 | + creation_time=now - 70.0, |
| 464 | + expected=25.0, |
| 465 | + must_send=True, |
| 466 | + ) |
| 467 | + tester.tgt_power( |
| 468 | + priority=3, |
| 469 | + power=25.0, |
| 470 | + bounds=(25.0, 50.0), |
| 471 | + creation_time=now - 70.0, |
| 472 | + expected=25.0, |
| 473 | + must_send=True, |
| 474 | + ) |
| 475 | + |
| 476 | + tester.algorithm.drop_old_proposals(now) |
| 477 | + |
| 478 | + tester.tgt_power( |
| 479 | + priority=1, |
| 480 | + power=25.0, |
| 481 | + bounds=(25.0, 50.0), |
| 482 | + expected=25.0, |
| 483 | + must_send=True, |
| 484 | + batteries=overlapping_batteries, |
| 485 | + ) |
0 commit comments