diff --git a/README.md b/README.md index 9fbcc25e..11c400f9 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ To integrate Fossil Test into your project, follow these steps: # ====================== [wrap-git] url = https://github.com/fossillogic/fossil-test.git - revision = v1.1.2 + revision = v1.1.3 [provide] fossil-test = fossil_test_dep diff --git a/code/logic/fossil/test/testing.h b/code/logic/fossil/test/testing.h index 617a73f3..7dfb91c2 100644 --- a/code/logic/fossil/test/testing.h +++ b/code/logic/fossil/test/testing.h @@ -18,13 +18,22 @@ #define MAX_NAME_LENGTH 256 // Color codes -#define COLOR_RESET "\033[0m" -#define COLOR_PASS "\033[32m" // Green -#define COLOR_FAIL "\033[31m" // Red -#define COLOR_SKIP "\033[33m" // Yellow -#define COLOR_INFO "\033[34m" // Blue -#define COLOR_BDD "\033[35m" // Magenta -#define COLOR_CYAN "\033[36m" // Cyan +#define FOSSIL_TEST_COLOR_RESET "\033[0m" // Reset +#define FOSSIL_TEST_COLOR_GREEN "\033[32m" // Green +#define FOSSIL_TEST_COLOR_RED "\033[31m" // Red +#define FOSSIL_TEST_COLOR_YELLOW "\033[33m" // Yellow +#define FOSSIL_TEST_COLOR_BLUE "\033[34m" // Blue +#define FOSSIL_TEST_COLOR_MAGENTA "\033[35m" // Magenta +#define FOSSIL_TEST_COLOR_CYAN "\033[36m" // Cyan +#define FOSSIL_TEST_COLOR_WHITE "\033[97m" // White +#define FOSSIL_TEST_COLOR_PURPLE "\033[35m" // Purple +#define FOSSIL_TEST_COLOR_ORANGE "\033[38;5;208m" // Orange + +#define FOSSIL_TEST_ATTR_BOLD "\033[1m" // Bold +#define FOSSIL_TEST_ATTR_DIM "\033[2m" // Dim +#define FOSSIL_TEST_ATTR_UNDERLINE "\033[4m" // Underline +#define FOSSIL_TEST_ATTR_ITATIC "\033[3m" // Italic + #include #include @@ -54,6 +63,7 @@ typedef enum { TEST_STATUS_PASS, TEST_STATUS_FAIL, TEST_STATUS_SKIP, + TEST_STATUS_EMPTY, TEST_STATUS_TTIMEOUT } test_status_t; @@ -95,6 +105,7 @@ typedef struct fossil_test_env { int pass_count; int fail_count; int skip_count; + int empty_count; int timeout_count; int unexpected_count; double start_execution_time; @@ -229,7 +240,7 @@ void fossil_test_print_stack_trace(stack_frame_t *stack_trace); #define _FOSSIL_TEST_SKIP(test_name, message) \ test_name##_test_case.status = TEST_STATUS_SKIP; \ test_name##_test_case.failure_message = message; \ - printf(COLOR_SKIP "SKIP: %s - %s\n" COLOR_RESET, #test_name, message); \ + printf(FOSSIL_TEST_COLOR_YELLOW "SKIP: %s - %s\n" FOSSIL_TEST_COLOR_RESET, #test_name, message); \ /** * @brief Macro to define a test case. @@ -447,7 +458,7 @@ void fossil_test_print_stack_trace(stack_frame_t *stack_trace); */ #define _GIVEN(description) \ if (0) { \ - printf(COLOR_BDD "Given %s\n" COLOR_RESET, description); \ + printf(FOSSIL_TEST_COLOR_MAGENTA "Given %s\n" FOSSIL_TEST_COLOR_RESET, description); \ } /** @@ -460,7 +471,7 @@ void fossil_test_print_stack_trace(stack_frame_t *stack_trace); */ #define _WHEN(description) \ if (0) { \ - printf(COLOR_BDD "When %s\n" COLOR_RESET, description); \ + printf(FOSSIL_TEST_COLOR_MAGENTA "When %s\n" FOSSIL_TEST_COLOR_RESET, description); \ } /** @@ -473,7 +484,7 @@ void fossil_test_print_stack_trace(stack_frame_t *stack_trace); */ #define _THEN(description) \ if (0) { \ - printf(COLOR_BDD "Then %s\n" COLOR_RESET, description); \ + printf(FOSSIL_TEST_COLOR_MAGENTA "Then %s\n" FOSSIL_TEST_COLOR_RESET, description); \ } #ifdef __cplusplus diff --git a/code/logic/testing.c b/code/logic/testing.c index 352d50e3..5f59e54b 100644 --- a/code/logic/testing.c +++ b/code/logic/testing.c @@ -280,6 +280,7 @@ const char *timeout_messages[] = { #endif jmp_buf test_jump_buffer; // This will hold the jump buffer for longjmp +static int _ASSERT_COUNT = 0; // Counter for the number of assertions // Custom implementation of strdup to avoid warnings on some platforms @@ -320,7 +321,7 @@ void usage_info(void) { void version_info(void) { printf("Fossil Logic Test Framework\n"); - printf("Version: 1.1.2\n"); + printf("Version: 1.1.3\n"); printf("Author: Michael Gene Brockus (Dreamer)\n"); printf("License: Mozila Public License 2.0\n"); } @@ -457,7 +458,7 @@ void fossil_test_register_suite(fossil_test_env_t *env, test_suite_t *suite) { suite->next = env->test_suites; env->test_suites = suite; if (env->options.show_info) { - printf(COLOR_INFO "Registered test suite: %s\n" COLOR_RESET, suite->name); + printf(FOSSIL_TEST_COLOR_BLUE "Registered test suite: %s\n" FOSSIL_TEST_COLOR_RESET, suite->name); } } @@ -505,6 +506,53 @@ void fossil_test_case_teardown(test_case_t *test_case) { } } +// Run all test cases in a test suite +void fossil_test_run_suite(test_suite_t *suite, fossil_test_env_t *env) { + if (!suite) return; + + if (env->options.show_info) { + printf(FOSSIL_TEST_COLOR_BLUE "Running suite: %s\n" FOSSIL_TEST_COLOR_RESET, suite->name); + } + + if (env->options.shuffle_enabled){ + shuffle_test_cases(&suite->tests); + } + + if (env->options.reverse) { + reverse_test_cases(&suite->tests); + } + + if (suite->suite_setup_func) { + suite->suite_setup_func(); + } + + double total_execution_time = 0.0; + test_case_t *current_test = suite->tests; + while (current_test) { + fossil_test_run_case(current_test, env); + total_execution_time += current_test->execution_time; + current_test = current_test->next; + } + + if (suite->suite_teardown_func) { + suite->suite_teardown_func(); + } + + if (env->options.show_info) { + printf(FOSSIL_TEST_COLOR_CYAN "Total execution time for suite %s: %.3f seconds\n" FOSSIL_TEST_COLOR_RESET, suite->name, total_execution_time); + } +} + +// Internal function to handle assertions +void fossil_test_assert_internal(bool condition, const char *message, const char *file, int line, const char *func) { + _ASSERT_COUNT++; // Increment the assertion count + + if (!condition) { + printf(FOSSIL_TEST_COLOR_RED "Assertion failed: %s (%s:%d in %s)\n" FOSSIL_TEST_COLOR_RESET, message, file, line, func); + longjmp(test_jump_buffer, 1); // Jump back to test case failure handler + } +} + // Run an individual test case void fossil_test_run_case(test_case_t *test_case, fossil_test_env_t *env) { if (!test_case) return; @@ -517,29 +565,36 @@ void fossil_test_run_case(test_case_t *test_case, fossil_test_env_t *env) { clock_t test_start_time = clock(); clock_t timeout_limit = test_start_time + 3 * 60 * CLOCKS_PER_SEC; // 3 minutes timeout + _ASSERT_COUNT = 0; // Reset assertion count before running the test + if (setjmp(env->env) == 0) { for (int i = 0; i < env->options.repeat_count; i++) { test_case->test_func(); if (clock() > timeout_limit) { test_case->status = TEST_STATUS_TTIMEOUT; - printf(COLOR_FAIL "TIMEOUT: " COLOR_INFO " %s\n" COLOR_RESET, test_case->name); + printf(FOSSIL_TEST_COLOR_ORANGE "TIMEOUT: " FOSSIL_TEST_COLOR_BLUE " %s\n" FOSSIL_TEST_COLOR_RESET, test_case->name); break; } } } else { test_case->status = TEST_STATUS_FAIL; - printf(COLOR_FAIL "FAIL: " COLOR_INFO " %s\n", test_case->name); - printf("Failure Message: %s\n" COLOR_RESET, test_case->failure_message); + printf(FOSSIL_TEST_COLOR_RED "FAIL: " FOSSIL_TEST_COLOR_BLUE " %s\n", test_case->name); + printf("Failure Message: %s\n" FOSSIL_TEST_COLOR_RESET, test_case->failure_message); } test_case->execution_time = (double)(clock() - test_start_time) / CLOCKS_PER_SEC; + // Check if the test case is empty + if (_ASSERT_COUNT == 0) { + printf(FOSSIL_TEST_COLOR_YELLOW "WARNING: " FOSSIL_TEST_COLOR_BLUE " %s contains no assertions\n" FOSSIL_TEST_COLOR_RESET, test_case->name); + } + // Run teardown fossil_test_case_teardown(test_case); // Log result if (test_case->status == TEST_STATUS_PASS) { if (env->options.show_info) { - printf(COLOR_PASS "PASS: " COLOR_INFO " %s (%.3f seconds)\n" COLOR_RESET, test_case->name, test_case->execution_time); + printf(FOSSIL_TEST_COLOR_GREEN "PASS: " FOSSIL_TEST_COLOR_BLUE " %s (%.3f seconds)\n" FOSSIL_TEST_COLOR_RESET, test_case->name, test_case->execution_time); } } else if (test_case->status == TEST_STATUS_FAIL) { env->fail_count++; @@ -547,56 +602,13 @@ void fossil_test_run_case(test_case_t *test_case, fossil_test_env_t *env) { env->skip_count++; } else if (test_case->status == TEST_STATUS_TTIMEOUT) { env->timeout_count++; + } else if (test_case->status == TEST_STATUS_EMPTY) { + env->empty_count++; } else { env->unexpected_count++; } } -// Run all test cases in a test suite -void fossil_test_run_suite(test_suite_t *suite, fossil_test_env_t *env) { - if (!suite) return; - - if (env->options.show_info) { - printf(COLOR_INFO "Running suite: %s\n" COLOR_RESET, suite->name); - } - - if (env->options.shuffle_enabled){ - shuffle_test_cases(&suite->tests); - } - - if (env->options.reverse) { - reverse_test_cases(&suite->tests); - } - - if (suite->suite_setup_func) { - suite->suite_setup_func(); - } - - double total_execution_time = 0.0; - test_case_t *current_test = suite->tests; - while (current_test) { - fossil_test_run_case(current_test, env); - total_execution_time += current_test->execution_time; - current_test = current_test->next; - } - - if (suite->suite_teardown_func) { - suite->suite_teardown_func(); - } - - if (env->options.show_info) { - printf(COLOR_CYAN "Total execution time for suite %s: %.3f seconds\n" COLOR_RESET, suite->name, total_execution_time); - } -} - -// Internal function to handle assertions -void fossil_test_assert_internal(bool condition, const char *message, const char *file, int line, const char *func) { - if (!condition) { - printf("Assertion failed: %s (%s:%d in %s)\n", message, file, line, func); - longjmp(test_jump_buffer, 1); // Jump back to test case failure handler - } -} - void fossil_test_run_all(fossil_test_env_t *env) { test_suite_t *current_suite = env->test_suites; @@ -619,6 +631,7 @@ void fossil_test_init(fossil_test_env_t *env, int argc, char **argv) { env->pass_count = 0; env->fail_count = 0; env->skip_count = 0; + env->empty_count = 0; env->total_tests = 0; env->timeout_count = 0; env->start_execution_time = clock(); @@ -632,16 +645,16 @@ void fossil_test_message(fossil_test_env_t *env) { // Seed random number generator srand(time(NULL)); - if (env->pass_count == 0 && env->fail_count == 0 && env->skip_count == 0 && env->timeout_count == 0) { - printf(COLOR_INFO "%s\n" COLOR_RESET, sarcastic_messages[rand() % 30]); + if (env->pass_count == 0 && env->fail_count == 0 && env->skip_count == 0 && env->timeout_count == 0 && env->empty_count > 0) { + printf(FOSSIL_TEST_COLOR_YELLOW FOSSIL_TEST_ATTR_ITATIC "%s\n" FOSSIL_TEST_COLOR_RESET, sarcastic_messages[rand() % 30]); } else if (env->fail_count > 0) { - printf(COLOR_FAIL "%s\n" COLOR_RESET, humorous_messages[rand() % 30]); + printf(FOSSIL_TEST_COLOR_RED FOSSIL_TEST_ATTR_ITATIC "%s\n" FOSSIL_TEST_COLOR_RESET, humorous_messages[rand() % 30]); } else if (env->pass_count > 0) { - printf(COLOR_PASS "%s\n" COLOR_RESET, great_news_messages[rand() % 30]); + printf(FOSSIL_TEST_COLOR_GREEN FOSSIL_TEST_ATTR_ITATIC "%s\n" FOSSIL_TEST_COLOR_RESET, great_news_messages[rand() % 30]); } else if (env->timeout_count > 0) { - printf(COLOR_FAIL "%s\n" COLOR_RESET, timeout_messages[rand() % 30]); + printf(FOSSIL_TEST_COLOR_ORANGE FOSSIL_TEST_ATTR_ITATIC "%s\n" FOSSIL_TEST_COLOR_RESET, timeout_messages[rand() % 30]); } else { - printf(COLOR_INFO "Test results are in. Keep pushing, you're getting there! 💪\n" COLOR_RESET); + printf(FOSSIL_TEST_COLOR_BLUE FOSSIL_TEST_ATTR_ITATIC "Test results are in. Keep pushing, you're getting there! 💪\n" FOSSIL_TEST_COLOR_RESET); } } @@ -672,15 +685,15 @@ void fossil_test_summary(fossil_test_env_t *env) { } env->end_execution_time = clock(); - printf(COLOR_INFO "===================================================================" COLOR_RESET); - printf(COLOR_INFO "\nFossil Test Summary:\n" COLOR_RESET); - printf(COLOR_INFO "===================================================================\n" COLOR_RESET); + printf(FOSSIL_TEST_COLOR_BLUE FOSSIL_TEST_ATTR_BOLD "===================================================================" FOSSIL_TEST_COLOR_RESET); + printf(FOSSIL_TEST_COLOR_CYAN FOSSIL_TEST_ATTR_BOLD FOSSIL_TEST_ATTR_ITATIC "\nFossil Test Summary:\n" FOSSIL_TEST_COLOR_RESET); + printf(FOSSIL_TEST_COLOR_BLUE FOSSIL_TEST_ATTR_BOLD "===================================================================\n" FOSSIL_TEST_COLOR_RESET); - printf(COLOR_INFO "Passed: " COLOR_PASS " %d\n" COLOR_RESET, env->pass_count); - printf(COLOR_INFO "Failed: " COLOR_FAIL " %d\n" COLOR_RESET, env->fail_count); - printf(COLOR_INFO "Skipped: " COLOR_SKIP " %d\n" COLOR_RESET, env->skip_count); - printf(COLOR_INFO "Timeout: " COLOR_SKIP " %d\n" COLOR_RESET, env->timeout_count); - printf(COLOR_INFO "Total: %d tests\n" COLOR_RESET, env->pass_count + env->fail_count + env->skip_count); + printf(FOSSIL_TEST_COLOR_CYAN FOSSIL_TEST_ATTR_ITATIC "Passed: " FOSSIL_TEST_COLOR_ORANGE " %d\n" FOSSIL_TEST_COLOR_RESET, env->pass_count); + printf(FOSSIL_TEST_COLOR_CYAN FOSSIL_TEST_ATTR_ITATIC "Failed: " FOSSIL_TEST_COLOR_ORANGE " %d\n" FOSSIL_TEST_COLOR_RESET, env->fail_count); + printf(FOSSIL_TEST_COLOR_CYAN FOSSIL_TEST_ATTR_ITATIC "Skipped:" FOSSIL_TEST_COLOR_ORANGE " %d\n" FOSSIL_TEST_COLOR_RESET, env->skip_count); + printf(FOSSIL_TEST_COLOR_CYAN FOSSIL_TEST_ATTR_ITATIC "Timeout:" FOSSIL_TEST_COLOR_ORANGE " %d\n" FOSSIL_TEST_COLOR_RESET, env->timeout_count); + printf(FOSSIL_TEST_COLOR_CYAN FOSSIL_TEST_ATTR_ITATIC "Total: %d tests\n" FOSSIL_TEST_COLOR_RESET, env->pass_count + env->fail_count + env->skip_count); // Optionally, you could add the total execution time summary here double total_execution_time = (double)(env->end_execution_time - env->start_execution_time) / CLOCKS_PER_SEC; @@ -688,9 +701,9 @@ void fossil_test_summary(fossil_test_env_t *env) { int milliseconds = (int)((total_execution_time - seconds) * 1000); int microseconds = (int)((total_execution_time - seconds - milliseconds / 1000.0) * 1000000); - printf(COLOR_INFO "===================================================================\n" COLOR_RESET); - printf(COLOR_INFO "Execution time: (%.2d) seconds, (%.2d) milliseconds, (%.3d) microseconds\n" COLOR_RESET, seconds, milliseconds, microseconds); - printf(COLOR_INFO "===================================================================\n" COLOR_RESET); + printf(FOSSIL_TEST_COLOR_BLUE FOSSIL_TEST_ATTR_BOLD "===================================================================\n" FOSSIL_TEST_COLOR_RESET); + printf(FOSSIL_TEST_COLOR_CYAN FOSSIL_TEST_ATTR_ITATIC "Execution time: (%.2d) seconds, (%.2d) milliseconds, (%.3d) microseconds\n" FOSSIL_TEST_COLOR_RESET, seconds, milliseconds, microseconds); + printf(FOSSIL_TEST_COLOR_BLUE FOSSIL_TEST_ATTR_BOLD "===================================================================\n" FOSSIL_TEST_COLOR_RESET); fossil_test_message(env); } diff --git a/meson.build b/meson.build index 99a3a686..b3b40ee0 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project('Fossil Test', 'c', 'cpp', meson_version: '>=1.3.0', license: 'MPL-2.0', - version: '1.1.2', + version: '1.1.3', default_options: ['c_std=c11,c18', 'cpp_std=c++20']) subdir('code')