HF-WS2812 Driver 0.1.0-dev
HF-WS2812 ESP32 RMT Driver
Loading...
Searching...
No Matches
TestFramework.h
Go to the documentation of this file.
1
14#pragma once
15
16// ESP-IDF C headers must be wrapped in extern "C" for C++ compatibility
17#ifdef __cplusplus
18extern "C" {
19#endif
20
21#include "esp_log.h"
22#include "esp_timer.h"
23#include "driver/gpio.h"
24#include "freertos/FreeRTOS.h"
25#include "freertos/queue.h"
26#include "freertos/semphr.h"
27#include "freertos/task.h"
28
29#ifdef __cplusplus
30}
31#endif
32
33//=============================================================================
34// GPIO14 TEST PROGRESSION INDICATOR MANAGEMENT
35//=============================================================================
36// GPIO14 test progression indicator - toggles between HIGH/LOW each time a test completes
37// providing visual feedback for test progression on oscilloscope/logic analyzer
38
39// Global GPIO14 test indicator state
40static bool g_test_progress_initialized = false;
41static bool g_test_progress_state = false;
42static constexpr gpio_num_t TEST_PROGRESS_PIN = GPIO_NUM_14;
43
44// GPIO14 test indicator functions implemented inline in this header
45
51inline bool init_test_progress_indicator() noexcept {
52 if (g_test_progress_initialized) {
53 return true; // Already initialized
54 }
55
56 // Configure GPIO14 as output
57 gpio_config_t io_conf = {
58 .pin_bit_mask = (1ULL << TEST_PROGRESS_PIN),
59 .mode = GPIO_MODE_OUTPUT,
60 .pull_up_en = GPIO_PULLUP_DISABLE,
61 .pull_down_en = GPIO_PULLDOWN_DISABLE,
62 .intr_type = GPIO_INTR_DISABLE,
63 };
64
65 esp_err_t ret = gpio_config(&io_conf);
66 if (ret != ESP_OK) {
67 const char* TAG = "TestFramework";
68 ESP_LOGE(TAG, "Failed to configure GPIO14: %s", esp_err_to_name(ret));
69 return false;
70 }
71
72 // Start with GPIO14 LOW
73 gpio_set_level(TEST_PROGRESS_PIN, 0);
74 g_test_progress_state = false;
75 g_test_progress_initialized = true;
76
77 return true;
78}
79
84inline void flip_test_progress_indicator() noexcept {
85 if (!g_test_progress_initialized) {
86 return; // Not initialized
87 }
88
89 // Toggle GPIO14 state
90 g_test_progress_state = !g_test_progress_state;
91
92 // Set GPIO level based on state
93 gpio_set_level(TEST_PROGRESS_PIN, g_test_progress_state ? 1 : 0);
94
95 const char* TAG = "TestFramework";
96 ESP_LOGI(TAG, "Test progression indicator: %s", g_test_progress_state ? "HIGH" : "LOW");
97
98 // Small delay for visual effect
99 vTaskDelay(pdMS_TO_TICKS(50));
100}
101
106inline void cleanup_test_progress_indicator() noexcept {
107 if (g_test_progress_initialized) {
108 // Ensure GPIO14 is LOW before cleanup
109 gpio_set_level(TEST_PROGRESS_PIN, 0);
110
111 // Reset GPIO configuration
112 gpio_reset_pin(TEST_PROGRESS_PIN);
113
114 g_test_progress_initialized = false;
115 g_test_progress_state = false;
116 }
117}
118
124inline void output_section_indicator(uint8_t blink_count = 5) noexcept {
125 if (!g_test_progress_initialized) {
126 return; // Not initialized
127 }
128
129 // Blink the specified number of times for section identification
130 for (uint8_t i = 0; i < blink_count; ++i) {
131 gpio_set_level(TEST_PROGRESS_PIN, 1); // HIGH
132 vTaskDelay(pdMS_TO_TICKS(50)); // ON
133 gpio_set_level(TEST_PROGRESS_PIN, 0); // LOW
134 vTaskDelay(pdMS_TO_TICKS(50)); // OFF
135
136 // // Pause between blinks (except after the last one)
137 // if (i < blink_count - 1) {
138 // vTaskDelay(pdMS_TO_TICKS(200)); // 200ms pause between blinks
139 // }
140 }
141
142 g_test_progress_state = false; // MARK THE STATE AS LOW AFTER THE BLINKING IS COMPLETED
143}
144
149inline void ensure_gpio14_initialized() noexcept {
150 if (!g_test_progress_initialized) {
152 }
153}
154
159 int total_tests = 0;
163
169 void add_result(bool passed, uint64_t execution_time) noexcept {
170 total_tests++;
171 total_execution_time_us += execution_time;
172 if (passed) {
173 passed_tests++;
174 } else {
175 failed_tests++;
176 }
177 }
178
183 float get_success_percentage() const noexcept {
184 return total_tests > 0 ? (static_cast<float>(passed_tests) / total_tests * 100.0f) : 0.0f;
185 }
186
191 float get_total_time_ms() const noexcept {
192 return total_execution_time_us / 1000.0f;
193 }
194};
195
212#define RUN_TEST(test_func) \
213 do { \
214 ensure_gpio14_initialized(); \
215 ESP_LOGI(TAG, \
216 "\n" \
217 "╔══════════════════════════════════════════════════════════════════════════════╗\n" \
218 "║ Running: " #test_func " \n" \
219 "╚══════════════════════════════════════════════════════════════════════════════╝"); \
220 uint64_t start_time = esp_timer_get_time(); \
221 bool result = test_func(); \
222 uint64_t end_time = esp_timer_get_time(); \
223 uint64_t execution_time = end_time - start_time; \
224 g_test_results.add_result(result, execution_time); \
225 if (result) { \
226 ESP_LOGI(TAG, "[SUCCESS] PASSED: " #test_func " (%.2f ms)", execution_time / 1000.0); \
227 } else { \
228 ESP_LOGE(TAG, "[FAILED] FAILED: " #test_func " (%.2f ms)", execution_time / 1000.0); \
229 } \
230 vTaskDelay(pdMS_TO_TICKS(100)); \
231 } while (0)
232
237 const char* test_name;
238 bool (*test_func)() noexcept;
240 const char* tag;
241 SemaphoreHandle_t completion_semaphore; // Add semaphore for synchronization
242};
243
247inline void test_task_trampoline(void* param) {
248 TestTaskContext* ctx = static_cast<TestTaskContext*>(param);
249 ESP_LOGI(ctx->tag,
250 "\n"
251 "╔══════════════════════════════════════════════════════════════════════════════╗\n"
252 "║ Running (task): %s \n"
253 "╚══════════════════════════════════════════════════════════════════════════════╝",
254 ctx->test_name);
255 uint64_t start_time = esp_timer_get_time();
256 bool result = ctx->test_func();
257 uint64_t end_time = esp_timer_get_time();
258 uint64_t execution_time = end_time - start_time;
259 ctx->results->add_result(result, execution_time);
260 if (result) {
261 ESP_LOGI(ctx->tag, "[SUCCESS] PASSED (task): %s (%.2f ms)", ctx->test_name,
262 execution_time / 1000.0);
263 } else {
264 ESP_LOGE(ctx->tag, "[FAILED] FAILED (task): %s (%.2f ms)", ctx->test_name,
265 execution_time / 1000.0);
266 }
267
268 // Signal completion before deleting task
269 if (ctx->completion_semaphore != nullptr) {
270 xSemaphoreGive(ctx->completion_semaphore);
271 }
272
273 vTaskDelete(nullptr);
274}
275
283#define RUN_TEST_IN_TASK(name, func, stack_size_bytes, priority) \
284 do { \
285 ensure_gpio14_initialized(); \
286 static TestTaskContext ctx; \
287 ctx.test_name = name; \
288 ctx.test_func = func; \
289 ctx.results = &g_test_results; \
290 ctx.tag = TAG; \
291 ctx.completion_semaphore = xSemaphoreCreateBinary(); \
292 if (ctx.completion_semaphore == nullptr) { \
293 ESP_LOGE(TAG, "Failed to create semaphore for test: %s", name); \
294 /* Fallback: run inline to avoid losing coverage */ \
295 RUN_TEST(func); \
296 } else { \
297 BaseType_t created = \
298 xTaskCreate(test_task_trampoline, name, (stack_size_bytes) / sizeof(StackType_t), &ctx, \
299 (priority), nullptr); \
300 if (created != pdPASS) { \
301 ESP_LOGE(TAG, "Failed to create test task: %s", name); \
302 vSemaphoreDelete(ctx.completion_semaphore); \
303 /* Fallback: run inline to avoid losing coverage */ \
304 RUN_TEST(func); \
305 } else { \
306 /* Wait for test completion using semaphore with timeout */ \
307 if (xSemaphoreTake(ctx.completion_semaphore, pdMS_TO_TICKS(30000)) == pdTRUE) { \
308 ESP_LOGI(TAG, "Test task completed: %s", name); \
309 } else { \
310 ESP_LOGW(TAG, "Test task timeout: %s", name); \
311 } \
312 vSemaphoreDelete(ctx.completion_semaphore); \
313 /* Add small delay between tests to ensure proper cleanup */ \
314 vTaskDelay(pdMS_TO_TICKS(100)); \
315 } \
316 } \
317 } while (0)
318
324inline void print_test_summary(const TestResults& test_results, const char* test_suite_name,
325 const char* tag) noexcept {
327 ESP_LOGI(tag, "\n=== %s TEST SUMMARY ===", test_suite_name);
328 ESP_LOGI(tag, "Total: %d, Passed: %d, Failed: %d, Success: %.2f%%, Time: %.2f ms",
329 test_results.total_tests, test_results.passed_tests, test_results.failed_tests,
330 test_results.get_success_percentage(), test_results.get_total_time_ms());
331
332 if (test_results.failed_tests == 0) {
333 ESP_LOGI(tag, "[SUCCESS] ALL %s TESTS PASSED!", test_suite_name);
334 } else {
335 ESP_LOGE(tag, "[FAILED] Some tests failed. Review the results above.");
336 }
337}
338
344inline void print_test_section_status(const char* tag, const char* test_suite_name) noexcept {
346 ESP_LOGI(tag, "\n");
347 ESP_LOGI(tag, "╔══════════════════════════════════════════════════════════════════════════════╗");
348 ESP_LOGI(tag, "║ %s TEST SECTION CONFIGURATION ║",
349 test_suite_name);
350 ESP_LOGI(tag, "╚══════════════════════════════════════════════════════════════════════════════╝");
351 ESP_LOGI(tag, "To modify test sections, edit the defines at the top of your test file");
352 ESP_LOGI(tag, "╔══════════════════════════════════════════════════════════════════════════════╗");
353}
354
360inline void print_test_section_header(const char* tag, const char* section_name,
361 bool enabled = true) noexcept {
362 if (enabled) {
363 ESP_LOGI(tag, "\n");
364 ESP_LOGI(tag,
365 "╔══════════════════════════════════════════════════════════════════════════════╗");
366 ESP_LOGI(tag, "║ %s ",
367 section_name);
368 ESP_LOGI(tag,
369 "╠══════════════════════════════════════════════════════════════════════════════╣");
370 } else {
371 ESP_LOGI(tag, "\n");
372 ESP_LOGI(tag,
373 "╔══════════════════════════════════════════════════════════════════════════════╗");
374 ESP_LOGI(tag, "║ %s (DISABLED) ",
375 section_name);
376 ESP_LOGI(tag,
377 "╚══════════════════════════════════════════════════════════════════════════════╝");
378 }
379}
380
386inline void print_test_section_footer(const char* tag, const char* section_name,
387 bool enabled = true) noexcept {
388 if (enabled) {
389 ESP_LOGI(tag,
390 "╚══════════════════════════════════════════════════════════════════════════════╝");
391 }
392}
411// Macro to conditionally run a test section
412#define RUN_TEST_SECTION_IF_ENABLED(define_name, section_name, ...) \
413 do { \
414 ensure_gpio14_initialized(); \
415 if (define_name) { \
416 print_test_section_header(TAG, section_name, true); \
417 __VA_ARGS__ \
418 print_test_section_footer(TAG, section_name, true); \
419 } else { \
420 print_test_section_header(TAG, section_name, false); \
421 ESP_LOGI(TAG, "Section disabled by configuration"); \
422 } \
423 } while (0)
424
425// Macro to conditionally run a single test
426#define RUN_SINGLE_TEST_IF_ENABLED(define_name, test_name, test_func, stack_size, priority) \
427 do { \
428 ensure_gpio14_initialized(); \
429 if (define_name) { \
430 RUN_TEST_IN_TASK(test_name, test_func, stack_size, priority); \
431 flip_test_progress_indicator(); \
432 } else { \
433 ESP_LOGI(TAG, "Test '%s' disabled by configuration", test_name); \
434 } \
435 } while (0)
436
437// Macro to conditionally run multiple tests in a section
438#define RUN_TEST_GROUP_IF_ENABLED(define_name, section_name, ...) \
439 RUN_TEST_SECTION_IF_ENABLED(define_name, section_name, __VA_ARGS__)
440
441// Macro to conditionally run a test section with custom progress indicator
442#define RUN_TEST_SECTION_IF_ENABLED_WITH_PROGRESS(define_name, section_name, progress_func, ...) \
443 do { \
444 ensure_gpio14_initialized(); \
445 if (define_name) { \
446 print_test_section_header(TAG, section_name, true); \
447 __VA_ARGS__ \
448 if (progress_func) \
449 progress_func(); \
450 print_test_section_footer(TAG, section_name, true); \
451 } else { \
452 print_test_section_header(TAG, section_name, false); \
453 ESP_LOGI(TAG, "Section disabled by configuration"); \
454 } \
455 } while (0)
456
457// Macro to conditionally run a test section with automatic progress indicator
458#define RUN_TEST_SECTION_IF_ENABLED_AUTO_PROGRESS(define_name, section_name, ...) \
459 RUN_TEST_SECTION_IF_ENABLED_WITH_PROGRESS(define_name, section_name, \
460 flip_test_progress_indicator, __VA_ARGS__)
461
462// Macro to conditionally run a test section with section indicator
463#define RUN_TEST_SECTION_IF_ENABLED_WITH_PATTERN(define_name, section_name, blink_count, ...) \
464 do { \
465 ensure_gpio14_initialized(); \
466 if (define_name) { \
467 print_test_section_header(TAG, section_name, true); \
468 output_section_indicator(blink_count); /* Section start indicator */ \
469 __VA_ARGS__ \
470 output_section_indicator(blink_count); /* Section end indicator */ \
471 print_test_section_footer(TAG, section_name, true); \
472 } else { \
473 print_test_section_header(TAG, section_name, false); \
474 ESP_LOGI(TAG, "Section disabled by configuration"); \
475 } \
476 } while (0)
477
void output_section_indicator(uint8_t blink_count=5) noexcept
Output section start/end indicator on GPIO14.
Definition TestFramework.h:124
void print_test_section_status(const char *tag, const char *test_suite_name) noexcept
Print standardized test summary.
Definition TestFramework.h:344
bool init_test_progress_indicator() noexcept
Initialize the test progression indicator on GPIO14.
Definition TestFramework.h:51
void ensure_gpio14_initialized() noexcept
Automatically initialize GPIO14 test indicator if not already done.
Definition TestFramework.h:149
void print_test_section_header(const char *tag, const char *section_name, bool enabled=true) noexcept
Print test section header with consistent formatting.
Definition TestFramework.h:360
void cleanup_test_progress_indicator() noexcept
Cleanup the test progression indicator GPIO.
Definition TestFramework.h:106
void print_test_section_footer(const char *tag, const char *section_name, bool enabled=true) noexcept
Print test section footer with consistent formatting.
Definition TestFramework.h:386
void flip_test_progress_indicator() noexcept
Flip the test progression indicator to show next test.
Definition TestFramework.h:84
void print_test_summary(const TestResults &test_results, const char *test_suite_name, const char *tag) noexcept
Print standardized test summary.
Definition TestFramework.h:324
void test_task_trampoline(void *param)
FreeRTOS task trampoline to execute a test with a larger dedicated stack.
Definition TestFramework.h:247
Test execution tracking and results accumulation.
Definition TestFramework.h:158
int failed_tests
Definition TestFramework.h:161
void add_result(bool passed, uint64_t execution_time) noexcept
Add test result and update statistics.
Definition TestFramework.h:169
int passed_tests
Definition TestFramework.h:160
float get_total_time_ms() const noexcept
Get total execution time in milliseconds.
Definition TestFramework.h:191
uint64_t total_execution_time_us
Definition TestFramework.h:162
float get_success_percentage() const noexcept
Calculate success percentage.
Definition TestFramework.h:183
int total_tests
Definition TestFramework.h:159
Context passed to test task trampoline.
Definition TestFramework.h:236
TestResults * results
Definition TestFramework.h:239
bool(* test_func)() noexcept
Definition TestFramework.h:238
SemaphoreHandle_t completion_semaphore
Definition TestFramework.h:241
const char * test_name
Definition TestFramework.h:237
const char * tag
Definition TestFramework.h:240