mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	[scheduler] Eliminate more runtime string allocations from retry (#9930)
This commit is contained in:
		| @@ -37,6 +37,15 @@ globals: | ||||
|   - id: multiple_same_name_counter | ||||
|     type: int | ||||
|     initial_value: '0' | ||||
|   - id: const_char_retry_counter | ||||
|     type: int | ||||
|     initial_value: '0' | ||||
|   - id: static_char_retry_counter | ||||
|     type: int | ||||
|     initial_value: '0' | ||||
|   - id: mixed_cancel_result | ||||
|     type: bool | ||||
|     initial_value: 'false' | ||||
|  | ||||
| # Using different component types for each test to ensure isolation | ||||
| sensor: | ||||
| @@ -229,6 +238,56 @@ script: | ||||
|               return RetryResult::RETRY; | ||||
|             }); | ||||
|  | ||||
|       # Test 8: Const char* overloads | ||||
|       - logger.log: "=== Test 8: Const char* overloads ===" | ||||
|       - lambda: |- | ||||
|           auto *component = id(simple_retry_sensor); | ||||
|  | ||||
|           // Test 8a: Direct string literal | ||||
|           App.scheduler.set_retry(component, "const_char_test", 30, 2, | ||||
|             [](uint8_t retry_countdown) { | ||||
|               id(const_char_retry_counter)++; | ||||
|               ESP_LOGI("test", "Const char retry %d", id(const_char_retry_counter)); | ||||
|               return RetryResult::DONE; | ||||
|             }); | ||||
|  | ||||
|       # Test 9: Static const char* variable | ||||
|       - logger.log: "=== Test 9: Static const char* ===" | ||||
|       - lambda: |- | ||||
|           auto *component = id(backoff_retry_sensor); | ||||
|  | ||||
|           static const char* STATIC_NAME = "static_retry_test"; | ||||
|           App.scheduler.set_retry(component, STATIC_NAME, 20, 1, | ||||
|             [](uint8_t retry_countdown) { | ||||
|               id(static_char_retry_counter)++; | ||||
|               ESP_LOGI("test", "Static const char retry %d", id(static_char_retry_counter)); | ||||
|               return RetryResult::DONE; | ||||
|             }); | ||||
|  | ||||
|           // Cancel with same static const char* | ||||
|           App.scheduler.set_timeout(component, "static_cancel", 10, []() { | ||||
|             static const char* STATIC_NAME = "static_retry_test"; | ||||
|             bool result = App.scheduler.cancel_retry(id(backoff_retry_sensor), STATIC_NAME); | ||||
|             ESP_LOGI("test", "Static cancel result: %s", result ? "true" : "false"); | ||||
|           }); | ||||
|  | ||||
|       # Test 10: Mix string and const char* cancel | ||||
|       - logger.log: "=== Test 10: Mixed string/const char* ===" | ||||
|       - lambda: |- | ||||
|           auto *component = id(immediate_done_sensor); | ||||
|  | ||||
|           // Set with std::string | ||||
|           std::string str_name = "mixed_retry"; | ||||
|           App.scheduler.set_retry(component, str_name, 40, 3, | ||||
|             [](uint8_t retry_countdown) { | ||||
|               ESP_LOGI("test", "Mixed retry - should be cancelled"); | ||||
|               return RetryResult::RETRY; | ||||
|             }); | ||||
|  | ||||
|           // Cancel with const char* | ||||
|           id(mixed_cancel_result) = App.scheduler.cancel_retry(component, "mixed_retry"); | ||||
|           ESP_LOGI("test", "Mixed cancel result: %s", id(mixed_cancel_result) ? "true" : "false"); | ||||
|  | ||||
|       # Wait for all tests to complete before reporting | ||||
|       - delay: 500ms | ||||
|  | ||||
| @@ -242,4 +301,7 @@ script: | ||||
|           ESP_LOGI("test", "Empty name retry counter: %d (expected 1-2)", id(empty_name_retry_counter)); | ||||
|           ESP_LOGI("test", "Component retry counter: %d (expected 2)", id(script_retry_counter)); | ||||
|           ESP_LOGI("test", "Multiple same name counter: %d (expected 20+)", id(multiple_same_name_counter)); | ||||
|           ESP_LOGI("test", "Const char retry counter: %d (expected 1)", id(const_char_retry_counter)); | ||||
|           ESP_LOGI("test", "Static char retry counter: %d (expected 1)", id(static_char_retry_counter)); | ||||
|           ESP_LOGI("test", "Mixed cancel result: %s (expected true)", id(mixed_cancel_result) ? "true" : "false"); | ||||
|           ESP_LOGI("test", "All retry tests completed"); | ||||
|   | ||||
| @@ -23,6 +23,9 @@ async def test_scheduler_retry_test( | ||||
|     empty_name_retry_done = asyncio.Event() | ||||
|     component_retry_done = asyncio.Event() | ||||
|     multiple_name_done = asyncio.Event() | ||||
|     const_char_done = asyncio.Event() | ||||
|     static_char_done = asyncio.Event() | ||||
|     mixed_cancel_done = asyncio.Event() | ||||
|     test_complete = asyncio.Event() | ||||
|  | ||||
|     # Track retry counts | ||||
| @@ -33,16 +36,20 @@ async def test_scheduler_retry_test( | ||||
|     empty_name_retry_count = 0 | ||||
|     component_retry_count = 0 | ||||
|     multiple_name_count = 0 | ||||
|     const_char_retry_count = 0 | ||||
|     static_char_retry_count = 0 | ||||
|  | ||||
|     # Track specific test results | ||||
|     cancel_result = None | ||||
|     empty_cancel_result = None | ||||
|     mixed_cancel_result = None | ||||
|     backoff_intervals = [] | ||||
|  | ||||
|     def on_log_line(line: str) -> None: | ||||
|         nonlocal simple_retry_count, backoff_retry_count, immediate_done_count | ||||
|         nonlocal cancel_retry_count, empty_name_retry_count, component_retry_count | ||||
|         nonlocal multiple_name_count, cancel_result, empty_cancel_result | ||||
|         nonlocal multiple_name_count, const_char_retry_count, static_char_retry_count | ||||
|         nonlocal cancel_result, empty_cancel_result, mixed_cancel_result | ||||
|  | ||||
|         # Strip ANSI color codes | ||||
|         clean_line = re.sub(r"\x1b\[[0-9;]*m", "", line) | ||||
| @@ -106,6 +113,27 @@ async def test_scheduler_retry_test( | ||||
|                 if multiple_name_count >= 20: | ||||
|                     multiple_name_done.set() | ||||
|  | ||||
|         # Const char retry test | ||||
|         elif "Const char retry" in clean_line: | ||||
|             if match := re.search(r"Const char retry (\d+)", clean_line): | ||||
|                 const_char_retry_count = int(match.group(1)) | ||||
|                 const_char_done.set() | ||||
|  | ||||
|         # Static const char retry test | ||||
|         elif "Static const char retry" in clean_line: | ||||
|             if match := re.search(r"Static const char retry (\d+)", clean_line): | ||||
|                 static_char_retry_count = int(match.group(1)) | ||||
|                 static_char_done.set() | ||||
|  | ||||
|         elif "Static cancel result:" in clean_line: | ||||
|             # This is part of test 9, but we don't track it separately | ||||
|             pass | ||||
|  | ||||
|         # Mixed cancel test | ||||
|         elif "Mixed cancel result:" in clean_line: | ||||
|             mixed_cancel_result = "true" in clean_line | ||||
|             mixed_cancel_done.set() | ||||
|  | ||||
|         # Test completion | ||||
|         elif "All retry tests completed" in clean_line: | ||||
|             test_complete.set() | ||||
| @@ -227,6 +255,40 @@ async def test_scheduler_retry_test( | ||||
|             f"Expected multiple name count >= 20 (second retry only), got {multiple_name_count}" | ||||
|         ) | ||||
|  | ||||
|         # Wait for const char retry test | ||||
|         try: | ||||
|             await asyncio.wait_for(const_char_done.wait(), timeout=1.0) | ||||
|         except TimeoutError: | ||||
|             pytest.fail( | ||||
|                 f"Const char retry test did not complete. Count: {const_char_retry_count}" | ||||
|             ) | ||||
|  | ||||
|         assert const_char_retry_count == 1, ( | ||||
|             f"Expected 1 const char retry call, got {const_char_retry_count}" | ||||
|         ) | ||||
|  | ||||
|         # Wait for static char retry test | ||||
|         try: | ||||
|             await asyncio.wait_for(static_char_done.wait(), timeout=1.0) | ||||
|         except TimeoutError: | ||||
|             pytest.fail( | ||||
|                 f"Static char retry test did not complete. Count: {static_char_retry_count}" | ||||
|             ) | ||||
|  | ||||
|         assert static_char_retry_count == 1, ( | ||||
|             f"Expected 1 static char retry call, got {static_char_retry_count}" | ||||
|         ) | ||||
|  | ||||
|         # Wait for mixed cancel test | ||||
|         try: | ||||
|             await asyncio.wait_for(mixed_cancel_done.wait(), timeout=1.0) | ||||
|         except TimeoutError: | ||||
|             pytest.fail("Mixed cancel test did not complete") | ||||
|  | ||||
|         assert mixed_cancel_result is True, ( | ||||
|             "Mixed string/const char cancel should have succeeded" | ||||
|         ) | ||||
|  | ||||
|         # Wait for test completion | ||||
|         try: | ||||
|             await asyncio.wait_for(test_complete.wait(), timeout=1.0) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user