hass.tibber_prices/docs/developer/versioned_docs/version-v0.25.0b0/critical-patterns.md

10 KiB
Raw Blame History

comments
false

Critical Behavior Patterns - Testing Guide

Purpose: This documentation lists essential behavior patterns that must be tested to ensure production-quality code and prevent resource leaks.

Last Updated: 2025-11-22 Test Coverage: 41 tests implemented (100% of critical patterns)

🎯 Why Are These Tests Critical?

Home Assistant integrations run continuously in the background. Resource leaks lead to:

  • Memory Leaks: RAM usage grows over days/weeks until HA becomes unstable
  • Callback Leaks: Listeners remain registered after entity removal → CPU load increases
  • Timer Leaks: Timers continue running after unload → unnecessary background tasks
  • File Handle Leaks: Storage files remain open → system resources exhausted

Test Categories

1. Resource Cleanup (Memory Leak Prevention)

File: tests/test_resource_cleanup.py

1.1 Listener Cleanup

What is tested:

  • Time-sensitive listeners are correctly removed (async_add_time_sensitive_listener())
  • Minute-update listeners are correctly removed (async_add_minute_update_listener())
  • Lifecycle callbacks are correctly unregistered (register_lifecycle_callback())
  • Sensor cleanup removes ALL registered listeners
  • Binary sensor cleanup removes ALL registered listeners

Why critical:

  • Each registered listener holds references to Entity + Coordinator
  • Without cleanup: Entities are not freed by GC → Memory Leak
  • With 80+ sensors × 3 listener types = 240+ callbacks that must be cleanly removed

Code Locations:

  • coordinator/listeners.pyasync_add_time_sensitive_listener(), async_add_minute_update_listener()
  • coordinator/core.pyregister_lifecycle_callback()
  • sensor/core.pyasync_will_remove_from_hass()
  • binary_sensor/core.pyasync_will_remove_from_hass()

1.2 Timer Cleanup

What is tested:

  • Quarter-hour timer is cancelled and reference cleared
  • Minute timer is cancelled and reference cleared
  • Both timers are cancelled together
  • Cleanup works even when timers are None

Why critical:

  • Uncancelled timers continue running after integration unload
  • HA's async_track_utc_time_change() creates persistent callbacks
  • Without cleanup: Timers keep firing → CPU load + unnecessary coordinator updates

Code Locations:

  • coordinator/listeners.pycancel_timers()
  • coordinator/core.pyasync_shutdown()

1.3 Config Entry Cleanup

What is tested:

  • Options update listener is registered via async_on_unload()
  • Cleanup function is correctly passed to async_on_unload()

Why critical:

  • entry.add_update_listener() registers permanent callback
  • Without async_on_unload(): Listener remains active after reload → duplicate updates
  • Pattern: entry.async_on_unload(entry.add_update_listener(handler))

Code Locations:

  • coordinator/core.py__init__() (listener registration)
  • __init__.pyasync_unload_entry()

2. Cache Invalidation

File: tests/test_resource_cleanup.py

2.1 Config Cache Invalidation

What is tested:

  • DataTransformer config cache is invalidated on options change
  • PeriodCalculator config + period cache is invalidated
  • Trend calculator cache is cleared on coordinator update

Why critical:

  • Stale config → Sensors use old user settings
  • Stale period cache → Incorrect best/peak price periods
  • Stale trend cache → Outdated trend analysis

Code Locations:

  • coordinator/data_transformation.pyinvalidate_config_cache()
  • coordinator/periods.pyinvalidate_config_cache()
  • sensor/calculators/trend.pyclear_trend_cache()

3. Storage Cleanup

File: tests/test_resource_cleanup.py + tests/test_coordinator_shutdown.py

3.1 Persistent Storage Removal

What is tested:

  • Storage file is deleted on config entry removal
  • Cache is saved on shutdown (no data loss)

Why critical:

  • Without storage removal: Old files remain after uninstallation
  • Without cache save on shutdown: Data loss on HA restart
  • Storage path: .storage/tibber_prices.{entry_id}

Code Locations:

  • __init__.pyasync_remove_entry()
  • coordinator/core.pyasync_shutdown()

4. Timer Scheduling

File: tests/test_timer_scheduling.py

What is tested:

  • Quarter-hour timer is registered with correct parameters
  • Minute timer is registered with correct parameters
  • Timers can be re-scheduled (override old timer)
  • Midnight turnover detection works correctly

Why critical:

  • Wrong timer parameters → Entities update at wrong times
  • Without timer override on re-schedule → Multiple parallel timers → Performance problem

5. Sensor-to-Timer Assignment

File: tests/test_sensor_timer_assignment.py

What is tested:

  • All TIME_SENSITIVE_ENTITY_KEYS are valid entity keys
  • All MINUTE_UPDATE_ENTITY_KEYS are valid entity keys
  • Both lists are disjoint (no overlap)
  • Sensor and binary sensor platforms are checked

Why critical:

  • Wrong timer assignment → Sensors update at wrong times
  • Overlap → Duplicate updates → Performance problem

🚨 Additional Analysis (Nice-to-Have Patterns)

These patterns were analyzed and classified as not critical:

6. Async Task Management

Current Status: Fire-and-forget pattern for short tasks

  • sensor/core.py → Chart data refresh (short-lived, max 1-2 seconds)
  • coordinator/core.py → Cache storage (short-lived, max 100ms)

Why no tests needed:

  • No long-running tasks (all < 2 seconds)
  • HA's event loop handles short tasks automatically
  • Task exceptions are already logged

If needed: _chart_refresh_task tracking + cancel in async_will_remove_from_hass()

7. API Session Cleanup

Current Status: Correctly implemented

  • async_get_clientsession(hass) is used (shared session)
  • No new sessions are created
  • HA manages session lifecycle automatically

Code: api/client.py + __init__.py

8. Translation Cache Memory

Current Status: Bounded cache

  • Max ~5-10 languages × 5KB = 50KB total
  • Module-level cache without re-loading
  • Practically no memory issue

Code: const.py_TRANSLATIONS_CACHE, _STANDARD_TRANSLATIONS_CACHE

9. Coordinator Data Structure Integrity

Current Status: Manually tested via ./scripts/develop

  • Midnight turnover works correctly (observed over several days)
  • Missing keys are handled via .get() with defaults
  • 80+ sensors access coordinator.data without errors

Structure:

coordinator.data = {
    "user_data": {...},
    "priceInfo": [...],  # Flat list of all enriched intervals
    "currency": "EUR"  # Top-level for easy access
}

10. Service Response Memory

Current Status: HA's response lifecycle

  • HA automatically frees service responses after return
  • ApexCharts ~20KB response is one-time per call
  • No response accumulation in integration code

Code: services/apexcharts.py

📊 Test Coverage Status

Implemented Tests (41 total)

Category Status Tests File Coverage
Listener Cleanup 5 test_resource_cleanup.py 100%
Timer Cleanup 4 test_resource_cleanup.py 100%
Config Entry Cleanup 1 test_resource_cleanup.py 100%
Cache Invalidation 3 test_resource_cleanup.py 100%
Storage Cleanup 1 test_resource_cleanup.py 100%
Storage Persistence 2 test_coordinator_shutdown.py 100%
Timer Scheduling 8 test_timer_scheduling.py 100%
Sensor-Timer Assignment 17 test_sensor_timer_assignment.py 100%
TOTAL 41 100% (critical)

📋 Analyzed but Not Implemented (Nice-to-Have)

Category Status Rationale
Async Task Management 📋 Fire-and-forget pattern used (no long-running tasks)
API Session Cleanup Pattern correct (async_get_clientsession used)
Translation Cache Cache size bounded (~50KB max for 10 languages)
Data Structure Integrity 📋 Would add test time without finding real issues
Service Response Memory 📋 HA automatically frees service responses

Legend:

  • = Fully tested or pattern verified correct
  • 📋 = Analyzed, low priority for testing (no known issues)

🎯 Development Status

All Critical Patterns Tested

All essential memory leak prevention patterns are covered by 41 tests:

  • Listeners are correctly removed (no callback leaks)
  • Timers are cancelled (no background task leaks)
  • Config entry cleanup works (no dangling listeners)
  • Caches are invalidated (no stale data issues)
  • Storage is saved and cleaned up (no data loss)
  • Timer scheduling works correctly (no update issues)
  • Sensor-timer assignment is correct (no wrong updates)

📋 Nice-to-Have Tests (Optional)

If problems arise in the future, these tests can be added:

  1. Async Task Management - Pattern analyzed (fire-and-forget for short tasks)
  2. Data Structure Integrity - Midnight rotation manually tested
  3. Service Response Memory - HA's response lifecycle automatic

Conclusion: The integration has production-quality test coverage for all critical resource leak patterns.

🔍 How to Run Tests

# Run all resource cleanup tests (14 tests)
./scripts/test tests/test_resource_cleanup.py -v

# Run all critical pattern tests (41 tests)
./scripts/test tests/test_resource_cleanup.py tests/test_coordinator_shutdown.py \
              tests/test_timer_scheduling.py tests/test_sensor_timer_assignment.py -v

# Run all tests with coverage
./scripts/test --cov=custom_components.tibber_prices --cov-report=html

# Type checking and linting
./scripts/check

# Manual memory leak test
# 1. Start HA: ./scripts/develop
# 2. Monitor RAM: watch -n 1 'ps aux | grep home-assistant'
# 3. Reload integration multiple times (HA UI: Settings → Devices → Tibber Prices → Reload)
# 4. RAM should stabilize (not grow continuously)

📚 References