HF Interface Wrapper 0.1.0-dev
Embedded C++ hardware abstraction layer
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 "freertos/FreeRTOS.h"
24#include "freertos/queue.h"
25#include "freertos/semphr.h"
26#include "freertos/task.h"
27
28#ifdef __cplusplus
29}
30#endif
31
32// Include GPIO types for proper functionality
33#include "HardwareTypes.h"
34
35// Include the full EspGpio header for GPIO14 functionality
36#include "mcu/esp32/EspGpio.h"
37
38//=============================================================================
39// GPIO14 TEST PROGRESSION INDICATOR MANAGEMENT
40//=============================================================================
41// GPIO14 test progression indicator - toggles between HIGH/LOW each time a test completes
42// providing visual feedback for test progression on oscilloscope/logic analyzer
43
44// Global GPIO14 test indicator instance
45static EspGpio* g_test_progress_gpio = nullptr;
46static bool g_test_progress_state = false;
47static constexpr int TEST_PROGRESS_PIN = 14;
48
49// GPIO14 test indicator functions implemented inline in this header
50
56inline bool init_test_progress_indicator() noexcept {
58 return true; // Already initialized
59 }
60
61 // Create GPIO14 instance for test progression indicator
67
69 return false; // Failed to allocate
70 }
71
72 // Initialize the GPIO pin
75 g_test_progress_gpio = nullptr;
76 return false;
77 }
78
79 // Start with GPIO14 LOW
82
83 return true;
84}
85
90inline void flip_test_progress_indicator() noexcept {
92 return; // Not initialized
93 }
94
95 // Toggle GPIO14 state
97
99 g_test_progress_gpio->SetActive(); // Set HIGH
100 } else {
101 g_test_progress_gpio->SetInactive(); // Set LOW
102 }
103
104 const char* TAG = "TestFramework";
105 ESP_LOGI(TAG, "Test progression indicator: %s", g_test_progress_state ? "HIGH" : "LOW");
106
107 // Small delay for visual effect
108 vTaskDelay(pdMS_TO_TICKS(50));
109}
110
115inline void cleanup_test_progress_indicator() noexcept {
117 // Ensure GPIO14 is LOW before cleanup
119
120 // Delete the GPIO instance
122 g_test_progress_gpio = nullptr;
123 g_test_progress_state = false;
124 }
125}
126
132inline void output_section_indicator(uint8_t blink_count = 5) noexcept {
134 return; // Not initialized
135 }
136
137 // Blink the specified number of times for section identification
138 for (uint8_t i = 0; i < blink_count; ++i) {
140 vTaskDelay(pdMS_TO_TICKS(50)); // ON
142 vTaskDelay(pdMS_TO_TICKS(50)); // OFF
143
144 // // Pause between blinks (except after the last one)
145 // if (i < blink_count - 1) {
146 // vTaskDelay(pdMS_TO_TICKS(200)); // 200ms pause between blinks
147 // }
148 }
149
150 g_test_progress_state = false; // MARK THE STATE AS LOW AFTER THE BLINKING IS COMPLETED
151}
152
157inline void ensure_gpio14_initialized() noexcept {
160 }
161}
162
167 int total_tests = 0;
171
177 void add_result(bool passed, uint64_t execution_time) noexcept {
178 total_tests++;
179 total_execution_time_us += execution_time;
180 if (passed) {
181 passed_tests++;
182 } else {
183 failed_tests++;
184 }
185 }
186
191 double get_success_percentage() const noexcept {
192 return total_tests > 0 ? (static_cast<double>(passed_tests) / total_tests * 100.0) : 0.0;
193 }
194
199 double get_total_time_ms() const noexcept {
200 return total_execution_time_us / 1000.0;
201 }
202};
203
220#define RUN_TEST(test_func) \
221 do { \
222 ensure_gpio14_initialized(); \
223 ESP_LOGI(TAG, \
224 "\n" \
225 "╔══════════════════════════════════════════════════════════════════════════════╗\n" \
226 "║ Running: " #test_func " \n" \
227 "╚══════════════════════════════════════════════════════════════════════════════╝"); \
228 uint64_t start_time = esp_timer_get_time(); \
229 bool result = test_func(); \
230 uint64_t end_time = esp_timer_get_time(); \
231 uint64_t execution_time = end_time - start_time; \
232 g_test_results.add_result(result, execution_time); \
233 if (result) { \
234 ESP_LOGI(TAG, "[SUCCESS] PASSED: " #test_func " (%.2f ms)", execution_time / 1000.0); \
235 } else { \
236 ESP_LOGE(TAG, "[FAILED] FAILED: " #test_func " (%.2f ms)", execution_time / 1000.0); \
237 } \
238 vTaskDelay(pdMS_TO_TICKS(100)); \
239 } while (0)
240
245 const char* test_name;
246 bool (*test_func)() noexcept;
248 const char* tag;
249 SemaphoreHandle_t completion_semaphore; // Add semaphore for synchronization
250};
251
255inline void test_task_trampoline(void* param) {
256 TestTaskContext* ctx = static_cast<TestTaskContext*>(param);
257 ESP_LOGI(ctx->tag,
258 "\n"
259 "╔══════════════════════════════════════════════════════════════════════════════╗\n"
260 "║ Running (task): %s \n"
261 "╚══════════════════════════════════════════════════════════════════════════════╝",
262 ctx->test_name);
263 uint64_t start_time = esp_timer_get_time();
264 bool result = ctx->test_func();
265 uint64_t end_time = esp_timer_get_time();
266 uint64_t execution_time = end_time - start_time;
267 ctx->results->add_result(result, execution_time);
268 if (result) {
269 ESP_LOGI(ctx->tag, "[SUCCESS] PASSED (task): %s (%.2f ms)", ctx->test_name,
270 execution_time / 1000.0);
271 } else {
272 ESP_LOGE(ctx->tag, "[FAILED] FAILED (task): %s (%.2f ms)", ctx->test_name,
273 execution_time / 1000.0);
274 }
275
276 // Signal completion before deleting task
277 if (ctx->completion_semaphore != nullptr) {
278 xSemaphoreGive(ctx->completion_semaphore);
279 }
280
281 vTaskDelete(nullptr);
282}
283
291#define RUN_TEST_IN_TASK(name, func, stack_size_bytes, priority) \
292 do { \
293 ensure_gpio14_initialized(); \
294 static TestTaskContext ctx; \
295 ctx.test_name = name; \
296 ctx.test_func = func; \
297 ctx.results = &g_test_results; \
298 ctx.tag = TAG; \
299 ctx.completion_semaphore = xSemaphoreCreateBinary(); \
300 if (ctx.completion_semaphore == nullptr) { \
301 ESP_LOGE(TAG, "Failed to create semaphore for test: %s", name); \
302 /* Fallback: run inline to avoid losing coverage */ \
303 RUN_TEST(func); \
304 } else { \
305 BaseType_t created = \
306 xTaskCreate(test_task_trampoline, name, (stack_size_bytes) / sizeof(StackType_t), &ctx, \
307 (priority), nullptr); \
308 if (created != pdPASS) { \
309 ESP_LOGE(TAG, "Failed to create test task: %s", name); \
310 vSemaphoreDelete(ctx.completion_semaphore); \
311 /* Fallback: run inline to avoid losing coverage */ \
312 RUN_TEST(func); \
313 } else { \
314 /* Wait for test completion using semaphore with timeout */ \
315 if (xSemaphoreTake(ctx.completion_semaphore, pdMS_TO_TICKS(30000)) == pdTRUE) { \
316 ESP_LOGI(TAG, "Test task completed: %s", name); \
317 } else { \
318 ESP_LOGW(TAG, "Test task timeout: %s", name); \
319 } \
320 vSemaphoreDelete(ctx.completion_semaphore); \
321 /* Add small delay between tests to ensure proper cleanup */ \
322 vTaskDelay(pdMS_TO_TICKS(100)); \
323 } \
324 } \
325 } while (0)
326
333inline void print_test_summary(const TestResults& test_results, const char* test_suite_name,
334 const char* tag) noexcept {
336 ESP_LOGI(tag, "\n=== %s TEST SUMMARY ===", test_suite_name);
337 ESP_LOGI(tag, "Total: %d, Passed: %d, Failed: %d, Success: %.2f%%, Time: %.2f ms",
338 test_results.total_tests, test_results.passed_tests, test_results.failed_tests,
339 test_results.get_success_percentage(), test_results.get_total_time_ms());
340
341 if (test_results.failed_tests == 0) {
342 ESP_LOGI(tag, "[SUCCESS] ALL %s TESTS PASSED!", test_suite_name);
343 } else {
344 ESP_LOGE(tag, "[FAILED] Some tests failed. Review the results above.");
345 }
346}
347
353inline void print_test_section_status(const char* tag, const char* test_suite_name) noexcept {
355 ESP_LOGI(tag, "\n");
356 ESP_LOGI(tag, "╔══════════════════════════════════════════════════════════════════════════════╗");
357 ESP_LOGI(tag, "║ %s TEST SECTION CONFIGURATION ║",
358 test_suite_name);
359 ESP_LOGI(tag, "╚══════════════════════════════════════════════════════════════════════════════╝");
360 ESP_LOGI(tag, "To modify test sections, edit the defines at the top of your test file");
361 ESP_LOGI(tag, "╔══════════════════════════════════════════════════════════════════════════════╗");
362}
363
370inline void print_test_section_header(const char* tag, const char* section_name,
371 bool enabled = true) noexcept {
372 if (enabled) {
373 ESP_LOGI(tag, "\n");
374 ESP_LOGI(tag,
375 "╔══════════════════════════════════════════════════════════════════════════════╗");
376 ESP_LOGI(tag, "║ %s ",
377 section_name);
378 ESP_LOGI(tag,
379 "╠══════════════════════════════════════════════════════════════════════════════╣");
380 } else {
381 ESP_LOGI(tag, "\n");
382 ESP_LOGI(tag,
383 "╔══════════════════════════════════════════════════════════════════════════════╗");
384 ESP_LOGI(tag, "║ %s (DISABLED) ",
385 section_name);
386 ESP_LOGI(tag,
387 "╚══════════════════════════════════════════════════════════════════════════════╝");
388 }
389}
390
397inline void print_test_section_footer(const char* tag, const char* section_name,
398 bool enabled = true) noexcept {
399 if (enabled) {
400 ESP_LOGI(tag,
401 "╚══════════════════════════════════════════════════════════════════════════════╝");
402 }
403}
422// Macro to conditionally run a test section
423#define RUN_TEST_SECTION_IF_ENABLED(define_name, section_name, ...) \
424 do { \
425 ensure_gpio14_initialized(); \
426 if (define_name) { \
427 print_test_section_header(TAG, section_name, true); \
428 __VA_ARGS__ \
429 print_test_section_footer(TAG, section_name, true); \
430 } else { \
431 print_test_section_header(TAG, section_name, false); \
432 ESP_LOGI(TAG, "Section disabled by configuration"); \
433 } \
434 } while (0)
435
436// Macro to conditionally run a single test
437#define RUN_SINGLE_TEST_IF_ENABLED(define_name, test_name, test_func, stack_size, priority) \
438 do { \
439 ensure_gpio14_initialized(); \
440 if (define_name) { \
441 RUN_TEST_IN_TASK(test_name, test_func, stack_size, priority); \
442 flip_test_progress_indicator(); \
443 } else { \
444 ESP_LOGI(TAG, "Test '%s' disabled by configuration", test_name); \
445 } \
446 } while (0)
447
448// Macro to conditionally run multiple tests in a section
449#define RUN_TEST_GROUP_IF_ENABLED(define_name, section_name, ...) \
450 RUN_TEST_SECTION_IF_ENABLED(define_name, section_name, __VA_ARGS__)
451
452// Macro to conditionally run a test section with custom progress indicator
453#define RUN_TEST_SECTION_IF_ENABLED_WITH_PROGRESS(define_name, section_name, progress_func, ...) \
454 do { \
455 ensure_gpio14_initialized(); \
456 if (define_name) { \
457 print_test_section_header(TAG, section_name, true); \
458 __VA_ARGS__ \
459 if (progress_func) \
460 progress_func(); \
461 print_test_section_footer(TAG, section_name, true); \
462 } else { \
463 print_test_section_header(TAG, section_name, false); \
464 ESP_LOGI(TAG, "Section disabled by configuration"); \
465 } \
466 } while (0)
467
468// Macro to conditionally run a test section with automatic progress indicator
469#define RUN_TEST_SECTION_IF_ENABLED_AUTO_PROGRESS(define_name, section_name, ...) \
470 RUN_TEST_SECTION_IF_ENABLED_WITH_PROGRESS(define_name, section_name, \
471 flip_test_progress_indicator, __VA_ARGS__)
472
473// Macro to conditionally run a test section with section indicator
474#define RUN_TEST_SECTION_IF_ENABLED_WITH_PATTERN(define_name, section_name, blink_count, ...) \
475 do { \
476 ensure_gpio14_initialized(); \
477 if (define_name) { \
478 print_test_section_header(TAG, section_name, true); \
479 output_section_indicator(blink_count); /* Section start indicator */ \
480 __VA_ARGS__ \
481 output_section_indicator(blink_count); /* Section end indicator */ \
482 print_test_section_footer(TAG, section_name, true); \
483 } else { \
484 print_test_section_header(TAG, section_name, false); \
485 ESP_LOGI(TAG, "Section disabled by configuration"); \
486 } \
487 } while (0)
488
static const char * TAG
Definition EspBluetooth.cpp:30
Advanced MCU-specific implementation of the unified BaseGpio class with ESP32C6/ESP-IDF v5....
Platform-agnostic hardware type definitions for the HardFOC system.
static bool g_test_progress_state
Definition TestFramework.h:46
void output_section_indicator(uint8_t blink_count=5) noexcept
Output section start/end indicator on GPIO14.
Definition TestFramework.h:132
void print_test_section_status(const char *tag, const char *test_suite_name) noexcept
Print standardized test section status.
Definition TestFramework.h:353
bool init_test_progress_indicator() noexcept
Initialize the test progression indicator on GPIO14.
Definition TestFramework.h:56
static EspGpio * g_test_progress_gpio
Definition TestFramework.h:45
static constexpr int TEST_PROGRESS_PIN
Definition TestFramework.h:47
void ensure_gpio14_initialized() noexcept
Automatically initialize GPIO14 test indicator if not already done.
Definition TestFramework.h:157
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:370
void cleanup_test_progress_indicator() noexcept
Cleanup the test progression indicator GPIO.
Definition TestFramework.h:115
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:397
void flip_test_progress_indicator() noexcept
Flip the test progression indicator to show next test.
Definition TestFramework.h:90
void print_test_summary(const TestResults &test_results, const char *test_suite_name, const char *tag) noexcept
Print standardized test summary.
Definition TestFramework.h:333
void test_task_trampoline(void *param)
FreeRTOS task trampoline to execute a test with a larger dedicated stack.
Definition TestFramework.h:255
hf_gpio_err_t SetInactive() noexcept
Set the GPIO to inactive state.
Definition BaseGpio.h:920
bool EnsureInitialized() noexcept
Ensures the pin is initialized (lazy initialization).
Definition BaseGpio.h:790
hf_gpio_err_t SetActive() noexcept
Set the GPIO to active state.
Definition BaseGpio.h:905
Advanced MCU-specific implementation of unified BaseGpio with ESP32C6/ESP-IDF v5.5+ features.
Definition EspGpio.h:65
@ HF_GPIO_DIRECTION_OUTPUT
Pin configured as output.
@ HF_GPIO_PULL_MODE_DOWN
Internal pull-down resistor enabled.
@ HF_GPIO_OUTPUT_MODE_PUSH_PULL
Push-pull output (strong high and low)
@ HF_GPIO_ACTIVE_HIGH
Active state is electrical high.
Test execution tracking and results accumulation.
Definition TestFramework.h:166
int failed_tests
Definition TestFramework.h:169
void add_result(bool passed, uint64_t execution_time) noexcept
Add test result and update statistics.
Definition TestFramework.h:177
int passed_tests
Definition TestFramework.h:168
double get_total_time_ms() const noexcept
Get total execution time in milliseconds.
Definition TestFramework.h:199
uint64_t total_execution_time_us
Definition TestFramework.h:170
double get_success_percentage() const noexcept
Calculate success percentage.
Definition TestFramework.h:191
int total_tests
Definition TestFramework.h:167
Context passed to test task trampoline.
Definition TestFramework.h:244
TestResults * results
Definition TestFramework.h:247
bool(* test_func)() noexcept
Definition TestFramework.h:246
SemaphoreHandle_t completion_semaphore
Definition TestFramework.h:249
const char * test_name
Definition TestFramework.h:245
const char * tag
Definition TestFramework.h:248