mirror of
https://github.com/esphome/esphome.git
synced 2025-09-02 03:12:20 +01:00
Fix buffer corruption in API message encoding with very verbose logging (#9249)
This commit is contained in:
89
tests/integration/fixtures/api_vv_logging.yaml
Normal file
89
tests/integration/fixtures/api_vv_logging.yaml
Normal file
@@ -0,0 +1,89 @@
|
||||
esphome:
|
||||
name: vv-logging-test
|
||||
|
||||
host:
|
||||
|
||||
api:
|
||||
|
||||
logger:
|
||||
level: VERY_VERBOSE
|
||||
# Enable VV logging for API components where the issue occurs
|
||||
logs:
|
||||
api.connection: VERY_VERBOSE
|
||||
api.service: VERY_VERBOSE
|
||||
api.proto: VERY_VERBOSE
|
||||
sensor: VERY_VERBOSE
|
||||
|
||||
# Create many sensors that update frequently to generate API traffic
|
||||
# This will cause many messages to be batched and sent, triggering the
|
||||
# code path where VV logging could cause buffer corruption
|
||||
sensor:
|
||||
- platform: template
|
||||
name: "Test Sensor 1"
|
||||
lambda: 'return millis() / 1000.0;'
|
||||
update_interval: 50ms
|
||||
unit_of_measurement: "s"
|
||||
|
||||
- platform: template
|
||||
name: "Test Sensor 2"
|
||||
lambda: 'return (millis() / 1000.0) + 10;'
|
||||
update_interval: 50ms
|
||||
unit_of_measurement: "s"
|
||||
|
||||
- platform: template
|
||||
name: "Test Sensor 3"
|
||||
lambda: 'return (millis() / 1000.0) + 20;'
|
||||
update_interval: 50ms
|
||||
unit_of_measurement: "s"
|
||||
|
||||
- platform: template
|
||||
name: "Test Sensor 4"
|
||||
lambda: 'return (millis() / 1000.0) + 30;'
|
||||
update_interval: 50ms
|
||||
unit_of_measurement: "s"
|
||||
|
||||
- platform: template
|
||||
name: "Test Sensor 5"
|
||||
lambda: 'return (millis() / 1000.0) + 40;'
|
||||
update_interval: 50ms
|
||||
unit_of_measurement: "s"
|
||||
|
||||
- platform: template
|
||||
name: "Test Sensor 6"
|
||||
lambda: 'return (millis() / 1000.0) + 50;'
|
||||
update_interval: 50ms
|
||||
unit_of_measurement: "s"
|
||||
|
||||
- platform: template
|
||||
name: "Test Sensor 7"
|
||||
lambda: 'return (millis() / 1000.0) + 60;'
|
||||
update_interval: 50ms
|
||||
unit_of_measurement: "s"
|
||||
|
||||
- platform: template
|
||||
name: "Test Sensor 8"
|
||||
lambda: 'return (millis() / 1000.0) + 70;'
|
||||
update_interval: 50ms
|
||||
unit_of_measurement: "s"
|
||||
|
||||
- platform: template
|
||||
name: "Test Sensor 9"
|
||||
lambda: 'return (millis() / 1000.0) + 80;'
|
||||
update_interval: 50ms
|
||||
unit_of_measurement: "s"
|
||||
|
||||
- platform: template
|
||||
name: "Test Sensor 10"
|
||||
lambda: 'return (millis() / 1000.0) + 90;'
|
||||
update_interval: 50ms
|
||||
unit_of_measurement: "s"
|
||||
|
||||
# Add some binary sensors too for variety
|
||||
binary_sensor:
|
||||
- platform: template
|
||||
name: "Test Binary 1"
|
||||
lambda: 'return (millis() / 1000) % 2 == 0;'
|
||||
|
||||
- platform: template
|
||||
name: "Test Binary 2"
|
||||
lambda: 'return (millis() / 1000) % 3 == 0;'
|
83
tests/integration/test_api_vv_logging.py
Normal file
83
tests/integration/test_api_vv_logging.py
Normal file
@@ -0,0 +1,83 @@
|
||||
"""Integration test for API with VERY_VERBOSE logging to verify no buffer corruption."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from typing import Any
|
||||
|
||||
from aioesphomeapi import LogLevel
|
||||
import pytest
|
||||
|
||||
from .types import APIClientConnectedFactory, RunCompiledFunction
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_api_vv_logging(
|
||||
yaml_config: str,
|
||||
run_compiled: RunCompiledFunction,
|
||||
api_client_connected: APIClientConnectedFactory,
|
||||
) -> None:
|
||||
"""Test that VERY_VERBOSE logging doesn't cause buffer corruption with API messages."""
|
||||
|
||||
# Track that we're receiving VV log messages and sensor updates
|
||||
vv_logs_received = 0
|
||||
sensor_updates_received = 0
|
||||
errors_detected = []
|
||||
|
||||
def on_log(msg: Any) -> None:
|
||||
"""Capture log messages."""
|
||||
nonlocal vv_logs_received
|
||||
# msg is a SubscribeLogsResponse object with 'message' attribute
|
||||
# The message field is always bytes
|
||||
message_text = msg.message.decode("utf-8", errors="replace")
|
||||
|
||||
# Only count VV logs specifically
|
||||
if "[VV]" in message_text:
|
||||
vv_logs_received += 1
|
||||
|
||||
# Check for assertion or error messages
|
||||
if "assert" in message_text.lower() or "error" in message_text.lower():
|
||||
errors_detected.append(message_text)
|
||||
|
||||
# Write, compile and run the ESPHome device
|
||||
async with run_compiled(yaml_config), api_client_connected() as client:
|
||||
# Subscribe to VERY_VERBOSE logs - this enables the code path that could cause corruption
|
||||
client.subscribe_logs(on_log, log_level=LogLevel.LOG_LEVEL_VERY_VERBOSE)
|
||||
|
||||
# Wait for device to be ready
|
||||
device_info = await client.device_info()
|
||||
assert device_info is not None
|
||||
assert device_info.name == "vv-logging-test"
|
||||
|
||||
# Subscribe to sensor states
|
||||
states = {}
|
||||
|
||||
def on_state(state):
|
||||
nonlocal sensor_updates_received
|
||||
sensor_updates_received += 1
|
||||
states[state.key] = state
|
||||
|
||||
client.subscribe_states(on_state)
|
||||
|
||||
# List entities to find our test sensors
|
||||
entity_info, _ = await client.list_entities_services()
|
||||
|
||||
# Count sensors
|
||||
sensor_count = sum(1 for e in entity_info if hasattr(e, "unit_of_measurement"))
|
||||
assert sensor_count >= 10, f"Expected at least 10 sensors, got {sensor_count}"
|
||||
|
||||
# Wait for sensor updates to flow with VV logging active
|
||||
# The sensors update every 50ms, so we should get many updates
|
||||
await asyncio.sleep(0.25)
|
||||
|
||||
# Verify we received both VV logs and sensor updates
|
||||
assert vv_logs_received > 0, "Expected to receive VERY_VERBOSE log messages"
|
||||
assert sensor_updates_received > 10, (
|
||||
f"Expected many sensor updates, got {sensor_updates_received}"
|
||||
)
|
||||
|
||||
# Check for any errors
|
||||
if errors_detected:
|
||||
pytest.fail(f"Errors detected during test: {errors_detected}")
|
||||
|
||||
# The test passes if we didn't hit any assertions or buffer corruption
|
Reference in New Issue
Block a user