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"
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;
52 if (g_test_progress_initialized) {
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,
65 esp_err_t ret = gpio_config(&io_conf);
67 const char* TAG =
"TestFramework";
68 ESP_LOGE(TAG,
"Failed to configure GPIO14: %s", esp_err_to_name(ret));
73 gpio_set_level(TEST_PROGRESS_PIN, 0);
74 g_test_progress_state =
false;
75 g_test_progress_initialized =
true;
85 if (!g_test_progress_initialized) {
90 g_test_progress_state = !g_test_progress_state;
93 gpio_set_level(TEST_PROGRESS_PIN, g_test_progress_state ? 1 : 0);
95 const char* TAG =
"TestFramework";
96 ESP_LOGI(TAG,
"Test progression indicator: %s", g_test_progress_state ?
"HIGH" :
"LOW");
99 vTaskDelay(pdMS_TO_TICKS(50));
107 if (g_test_progress_initialized) {
109 gpio_set_level(TEST_PROGRESS_PIN, 0);
112 gpio_reset_pin(TEST_PROGRESS_PIN);
114 g_test_progress_initialized =
false;
115 g_test_progress_state =
false;
125 if (!g_test_progress_initialized) {
130 for (uint8_t i = 0; i < blink_count; ++i) {
131 gpio_set_level(TEST_PROGRESS_PIN, 1);
132 vTaskDelay(pdMS_TO_TICKS(50));
133 gpio_set_level(TEST_PROGRESS_PIN, 0);
134 vTaskDelay(pdMS_TO_TICKS(50));
142 g_test_progress_state =
false;
150 if (!g_test_progress_initialized) {
169 void add_result(
bool passed, uint64_t execution_time)
noexcept {
212#define RUN_TEST(test_func) \
214 ensure_gpio14_initialized(); \
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); \
226 ESP_LOGI(TAG, "[SUCCESS] PASSED: " #test_func " (%.2f ms)", execution_time / 1000.0); \
228 ESP_LOGE(TAG, "[FAILED] FAILED: " #test_func " (%.2f ms)", execution_time / 1000.0); \
230 vTaskDelay(pdMS_TO_TICKS(100)); \
251 "╔══════════════════════════════════════════════════════════════════════════════╗\n"
252 "║ Running (task): %s \n"
253 "╚══════════════════════════════════════════════════════════════════════════════╝",
255 uint64_t start_time = esp_timer_get_time();
257 uint64_t end_time = esp_timer_get_time();
258 uint64_t execution_time = end_time - start_time;
261 ESP_LOGI(ctx->
tag,
"[SUCCESS] PASSED (task): %s (%.2f ms)", ctx->
test_name,
262 execution_time / 1000.0);
264 ESP_LOGE(ctx->
tag,
"[FAILED] FAILED (task): %s (%.2f ms)", ctx->
test_name,
265 execution_time / 1000.0);
273 vTaskDelete(
nullptr);
283#define RUN_TEST_IN_TASK(name, func, stack_size_bytes, priority) \
285 ensure_gpio14_initialized(); \
286 static TestTaskContext ctx; \
287 ctx.test_name = name; \
288 ctx.test_func = func; \
289 ctx.results = &g_test_results; \
291 ctx.completion_semaphore = xSemaphoreCreateBinary(); \
292 if (ctx.completion_semaphore == nullptr) { \
293 ESP_LOGE(TAG, "Failed to create semaphore for test: %s", name); \
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); \
307 if (xSemaphoreTake(ctx.completion_semaphore, pdMS_TO_TICKS(30000)) == pdTRUE) { \
308 ESP_LOGI(TAG, "Test task completed: %s", name); \
310 ESP_LOGW(TAG, "Test task timeout: %s", name); \
312 vSemaphoreDelete(ctx.completion_semaphore); \
314 vTaskDelay(pdMS_TO_TICKS(100)); \
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());
332 if (test_results.failed_tests == 0) {
333 ESP_LOGI(tag,
"[SUCCESS] ALL %s TESTS PASSED!", test_suite_name);
335 ESP_LOGE(tag,
"[FAILED] Some tests failed. Review the results above.");
347 ESP_LOGI(tag,
"╔══════════════════════════════════════════════════════════════════════════════╗");
348 ESP_LOGI(tag,
"║ %s TEST SECTION CONFIGURATION ║",
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,
"╔══════════════════════════════════════════════════════════════════════════════╗");
361 bool enabled =
true) noexcept {
365 "╔══════════════════════════════════════════════════════════════════════════════╗");
366 ESP_LOGI(tag,
"║ %s ",
369 "╠══════════════════════════════════════════════════════════════════════════════╣");
373 "╔══════════════════════════════════════════════════════════════════════════════╗");
374 ESP_LOGI(tag,
"║ %s (DISABLED) ",
377 "╚══════════════════════════════════════════════════════════════════════════════╝");
387 bool enabled =
true) noexcept {
390 "╚══════════════════════════════════════════════════════════════════════════════╝");
412#define RUN_TEST_SECTION_IF_ENABLED(define_name, section_name, ...) \
414 ensure_gpio14_initialized(); \
416 print_test_section_header(TAG, section_name, true); \
418 print_test_section_footer(TAG, section_name, true); \
420 print_test_section_header(TAG, section_name, false); \
421 ESP_LOGI(TAG, "Section disabled by configuration"); \
426#define RUN_SINGLE_TEST_IF_ENABLED(define_name, test_name, test_func, stack_size, priority) \
428 ensure_gpio14_initialized(); \
430 RUN_TEST_IN_TASK(test_name, test_func, stack_size, priority); \
431 flip_test_progress_indicator(); \
433 ESP_LOGI(TAG, "Test '%s' disabled by configuration", test_name); \
438#define RUN_TEST_GROUP_IF_ENABLED(define_name, section_name, ...) \
439 RUN_TEST_SECTION_IF_ENABLED(define_name, section_name, __VA_ARGS__)
442#define RUN_TEST_SECTION_IF_ENABLED_WITH_PROGRESS(define_name, section_name, progress_func, ...) \
444 ensure_gpio14_initialized(); \
446 print_test_section_header(TAG, section_name, true); \
450 print_test_section_footer(TAG, section_name, true); \
452 print_test_section_header(TAG, section_name, false); \
453 ESP_LOGI(TAG, "Section disabled by configuration"); \
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__)
463#define RUN_TEST_SECTION_IF_ENABLED_WITH_PATTERN(define_name, section_name, blink_count, ...) \
465 ensure_gpio14_initialized(); \
467 print_test_section_header(TAG, section_name, true); \
468 output_section_indicator(blink_count); \
470 output_section_indicator(blink_count); \
471 print_test_section_footer(TAG, section_name, true); \
473 print_test_section_header(TAG, section_name, false); \
474 ESP_LOGI(TAG, "Section disabled by configuration"); \
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