From f7ca26eef887ad7e4d1fab7efbaf4e40f9c5f5f7 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 4 Jul 2025 08:59:15 -0500 Subject: [PATCH] stress --- tests/integration/fixtures/defer_stress.yaml | 69 ++++++-------------- tests/integration/test_defer_stress.py | 15 ++++- 2 files changed, 33 insertions(+), 51 deletions(-) diff --git a/tests/integration/fixtures/defer_stress.yaml b/tests/integration/fixtures/defer_stress.yaml index 867d40ab53..9400c33f11 100644 --- a/tests/integration/fixtures/defer_stress.yaml +++ b/tests/integration/fixtures/defer_stress.yaml @@ -1,65 +1,36 @@ esphome: name: defer-stress-test +external_components: + - source: + type: local + path: EXTERNAL_COMPONENT_PATH + components: [defer_stress_component] + host: logger: level: DEBUG +defer_stress_component: + id: defer_stress + api: services: - service: run_stress_test then: - lambda: |- - static int total_defers = 0; - static int executed_defers = 0; - - ESP_LOGI("stress", "Starting defer stress test - rapid sequential defers"); - - // Reset counters - total_defers = 0; - executed_defers = 0; - - // Create a temporary component to access defer() - class TestComponent : public Component { - public: - void run_test() { - // Rapidly defer many callbacks to stress the defer mechanism - for (int batch = 0; batch < 10; batch++) { - for (int i = 0; i < 100; i++) { - int expected_id = total_defers; - this->defer([expected_id]() { - executed_defers++; - ESP_LOGV("stress", "Defer %d executed", expected_id); - }); - total_defers++; - } - // Brief yield to let other work happen - delay(1); - } - } - }; - - TestComponent test_comp; - test_comp.run_test(); - - ESP_LOGI("stress", "Scheduled %d defers", total_defers); - - // Give the main loop time to process all defers - App.scheduler.set_timeout((Component*)nullptr, nullptr, 500, []() { - ESP_LOGI("stress", "Test complete. Defers scheduled: %d, executed: %d", total_defers, executed_defers); - - // We should have executed all defers without crashing - if (executed_defers == total_defers && total_defers == 1000) { - ESP_LOGI("stress", "✓ Stress test PASSED - All %d defers executed", total_defers); - id(test_result)->trigger("passed"); - } else { - ESP_LOGE("stress", "✗ Stress test FAILED - Expected 1000 executed, got %d", executed_defers); - id(test_result)->trigger("failed"); - } - - id(test_complete)->trigger("test_finished"); - }); + id(defer_stress)->run_multi_thread_test(); + - wait_until: + lambda: |- + return id(defer_stress)->is_test_complete(); + - lambda: |- + if (id(defer_stress)->is_test_passed()) { + id(test_result)->trigger("passed"); + } else { + id(test_result)->trigger("failed"); + } + id(test_complete)->trigger("test_finished"); event: - platform: template diff --git a/tests/integration/test_defer_stress.py b/tests/integration/test_defer_stress.py index e9d6c48664..ed0ae74a08 100644 --- a/tests/integration/test_defer_stress.py +++ b/tests/integration/test_defer_stress.py @@ -1,6 +1,7 @@ """Stress test for defer() thread safety with multiple threads.""" import asyncio +from pathlib import Path from aioesphomeapi import EntityState, Event, EventInfo, UserService import pytest @@ -16,6 +17,16 @@ async def test_defer_stress( ) -> None: """Test that defer() doesn't crash when called rapidly from multiple threads.""" + # Get the absolute path to the external components directory + external_components_path = str( + Path(__file__).parent / "fixtures" / "external_components" + ) + + # Replace the placeholder in the YAML config with the actual path + yaml_config = yaml_config.replace( + "EXTERNAL_COMPONENT_PATH", external_components_path + ) + async with run_compiled(yaml_config), api_client_connected() as client: # Verify we can connect device_info = await client.device_info() @@ -79,10 +90,10 @@ async def test_defer_stress( # Wait for test completion with a longer timeout (threads run for 100ms + processing time) try: - await asyncio.wait_for(test_complete_future, timeout=10.0) + await asyncio.wait_for(test_complete_future, timeout=15.0) test_passed = await asyncio.wait_for(test_result_future, timeout=1.0) except asyncio.TimeoutError: - pytest.fail("Stress test did not complete within 10 seconds") + pytest.fail("Stress test did not complete within 15 seconds") # Verify the test passed assert test_passed is True, (