@@ -23,20 +23,21 @@ def __init__(
2323 """Create a new instance of the stateful tester."""
2424 self ._call_count = 0
2525 self ._batteries = batteries
26- self ._algorithm = Matryoshka (max_proposal_age = timedelta (seconds = 60.0 ))
2726 self ._system_bounds = system_bounds
27+ self .algorithm = Matryoshka (max_proposal_age = timedelta (seconds = 60.0 ))
2828
2929 def tgt_power ( # pylint: disable=too-many-arguments
3030 self ,
3131 priority : int ,
3232 power : float | None ,
3333 bounds : tuple [float | None , float | None ],
3434 expected : float | None ,
35+ creation_time : float | None = None ,
3536 must_send : bool = False ,
3637 ) -> None :
3738 """Test the target power calculation."""
3839 self ._call_count += 1
39- tgt_power = self ._algorithm .calculate_target_power (
40+ tgt_power = self .algorithm .calculate_target_power (
4041 self ._batteries ,
4142 Proposal (
4243 component_ids = self ._batteries ,
@@ -47,7 +48,9 @@ def tgt_power( # pylint: disable=too-many-arguments
4748 None if bounds [1 ] is None else Power .from_watts (bounds [1 ]),
4849 ),
4950 priority = priority ,
50- creation_time = asyncio .get_event_loop ().time (),
51+ creation_time = creation_time
52+ if creation_time is not None
53+ else asyncio .get_event_loop ().time (),
5154 ),
5255 self ._system_bounds ,
5356 must_send ,
@@ -63,7 +66,7 @@ def bounds(
6366 expected_bounds : tuple [float , float ],
6467 ) -> None :
6568 """Test the status report."""
66- report = self ._algorithm .get_status (
69+ report = self .algorithm .get_status (
6770 self ._batteries , priority , self ._system_bounds , None
6871 )
6972 if expected_power is None :
@@ -352,3 +355,67 @@ async def test_matryoshka_with_excl_3() -> None:
352355
353356 tester .tgt_power (priority = 1 , power = - 40.0 , bounds = (- 100.0 , - 35.0 ), expected = - 40.0 )
354357 tester .bounds (priority = 0 , expected_power = - 40.0 , expected_bounds = (- 100.0 , - 35.0 ))
358+
359+
360+ async def test_matryoshka_drop_old_proposals () -> None :
361+ """Tests for the power managing actor.
362+
363+ With inclusion bounds, and exclusion bounds -30.0 to 30.0.
364+ """
365+ batteries = frozenset ({2 , 5 })
366+
367+ system_bounds = _base_types .SystemBounds (
368+ timestamp = datetime .now (tz = timezone .utc ),
369+ inclusion_bounds = timeseries .Bounds (
370+ lower = Power .from_watts (- 200.0 ), upper = Power .from_watts (200.0 )
371+ ),
372+ exclusion_bounds = timeseries .Bounds (lower = Power .zero (), upper = Power .zero ()),
373+ )
374+
375+ tester = StatefulTester (batteries , system_bounds )
376+
377+ now = asyncio .get_event_loop ().time ()
378+
379+ tester .tgt_power (priority = 3 , power = 22.0 , bounds = (22.0 , 30.0 ), expected = 22.0 )
380+
381+ # When a proposal is too old and hasn't been updated, it is dropped.
382+ tester .tgt_power (
383+ priority = 2 ,
384+ power = 25.0 ,
385+ bounds = (25.0 , 50.0 ),
386+ creation_time = now - 70.0 ,
387+ expected = 25.0 ,
388+ )
389+
390+ tester .tgt_power (
391+ priority = 1 , power = 20.0 , bounds = (20.0 , 50.0 ), expected = 25.0 , must_send = True
392+ )
393+ tester .algorithm .drop_old_proposals (now )
394+ tester .tgt_power (
395+ priority = 1 , power = 20.0 , bounds = (20.0 , 50.0 ), expected = 22.0 , must_send = True
396+ )
397+
398+ # When overwritten by a newer proposal, that proposal is not dropped.
399+ tester .tgt_power (
400+ priority = 2 ,
401+ power = 25.0 ,
402+ bounds = (25.0 , 50.0 ),
403+ creation_time = now - 70.0 ,
404+ expected = 25.0 ,
405+ )
406+ tester .tgt_power (
407+ priority = 2 ,
408+ power = 25.0 ,
409+ bounds = (25.0 , 50.0 ),
410+ creation_time = now - 30.0 ,
411+ expected = 25.0 ,
412+ must_send = True ,
413+ )
414+
415+ tester .tgt_power (
416+ priority = 1 , power = 20.0 , bounds = (20.0 , 50.0 ), expected = 25.0 , must_send = True
417+ )
418+ tester .algorithm .drop_old_proposals (now )
419+ tester .tgt_power (
420+ priority = 1 , power = 20.0 , bounds = (20.0 , 50.0 ), expected = 25.0 , must_send = True
421+ )
0 commit comments