"""Tests for i2p_stat.rate — Rate tracking.""" import time from i2p_stat.rate import Rate, RateSummaryListener class TestRate: def test_create_rate(self): r = Rate(60000) assert r.period == 60000 assert r.get_current_event_count() == 0 assert r.get_lifetime_event_count() == 0 def test_add_data(self): r = Rate(60000) r.add_data(100) assert r.get_current_event_count() == 1 assert r.get_current_total_value() == 100.0 assert r.get_lifetime_event_count() == 1 assert r.get_lifetime_total_value() == 100.0 def test_add_multiple(self): r = Rate(60000) r.add_data(10) r.add_data(20) r.add_data(30) assert r.get_current_event_count() == 3 assert r.get_current_total_value() == 60.0 assert r.get_lifetime_event_count() == 3 def test_coalesce_shifts_current_to_last(self): # Use very short period for test r = Rate(100) # 100ms period r.add_data(50) r.add_data(50) time.sleep(0.15) # Wait > period r.coalesce() assert r.get_current_event_count() == 0 assert r.get_last_event_count() > 0 assert r.get_last_total_value() > 0 def test_coalesce_too_early_is_noop(self): r = Rate(60000) # 60s period r.add_data(100) r.coalesce() # Should be a no-op (< period - SLACK) assert r.get_current_event_count() == 1 # Still in current assert r.get_last_event_count() == 0 def test_average_value(self): r = Rate(100) r.add_data(10) r.add_data(30) time.sleep(0.15) r.coalesce() avg = r.get_average_value() # Average should be approximately (10+30)/2 = 20 assert avg > 0 def test_lifetime_average(self): r = Rate(60000) r.add_data(10) r.add_data(20) r.add_data(30) assert r.get_lifetime_average_value() == 20.0 def test_extreme_tracks_highest(self): r = Rate(100) # Period 1: value 100 r.add_data(100) time.sleep(0.15) r.coalesce() # Period 2: value 50 r.add_data(50) time.sleep(0.15) r.coalesce() # Extreme should still be from period 1 assert r.get_extreme_total_value() >= r.get_last_total_value() def test_invalid_period_raises(self): try: Rate(0) assert False, "Should have raised" except ValueError: pass try: Rate(-1) assert False, "Should have raised" except ValueError: pass def test_avg_or_lifetime_avg_fallback(self): r = Rate(60000) r.add_data(100) # No coalesce, so last is empty — should fall back to lifetime avg = r.get_avg_or_lifetime_avg() assert avg == 100.0 def test_lifetime_periods(self): r = Rate(100) time.sleep(0.25) assert r.get_lifetime_periods() >= 2 def test_saturation(self): r = Rate(100) r.add_data(10, 50) # 50ms event in 100ms period time.sleep(0.15) r.coalesce() sat = r.get_last_event_saturation() assert 0 < sat <= 1.0 class TestRateSummaryListener: def test_listener_called_on_coalesce(self): r = Rate(100) calls = [] class TestListener(RateSummaryListener): def add(self, total_value, event_count, total_event_time, period): calls.append((total_value, event_count, period)) r.set_summary_listener(TestListener()) r.add_data(42) time.sleep(0.15) r.coalesce() assert len(calls) == 1 assert calls[0][2] == 100 # period