diff --git a/README.md b/README.md index 11c400f9..6586efa6 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.3 + revision = v1.1.4 [provide] fossil-test = fossil_test_dep diff --git a/code/logic/fossil/test/marking.h b/code/logic/fossil/test/marking.h index 04f0212e..d7f77fbd 100644 --- a/code/logic/fossil/test/marking.h +++ b/code/logic/fossil/test/marking.h @@ -24,6 +24,14 @@ extern "C" { #endif +/** + * @brief Structure to hold the benchmark statistics. + * + * This structure contains various fields to manage the statistics for a benchmark, + * including the name of the benchmark, the start and end times, the number of samples, + * the total duration, the minimum and maximum durations, and a flag to indicate if the + * benchmark is currently running. + */ typedef struct { const char* name; clock_t start_time; diff --git a/code/logic/fossil/test/testing.h b/code/logic/fossil/test/testing.h index 7dfb91c2..2b88a425 100644 --- a/code/logic/fossil/test/testing.h +++ b/code/logic/fossil/test/testing.h @@ -48,7 +48,35 @@ extern "C" { #endif -// Define the structure to hold parsed options +/** + * @struct fossil_options + * @brief Structure to hold the configuration options for the test environment. + * + * This structure contains various fields to manage the configuration options for + * the test environment, including flags to show version and help information, as + * well as options to reverse the order of tests, repeat tests, and shuffle tests. + * + * @var fossil_options::show_version + * Flag to show version information. + * + * @var fossil_options::show_help + * Flag to show help information. + * + * @var fossil_options::show_info + * Flag to show additional information. + * + * @var fossil_options::reverse + * Flag to reverse the order of tests. + * + * @var fossil_options::repeat_enabled + * Flag to enable repeating tests. + * + * @var fossil_options::repeat_count + * Number of times to repeat tests. + * + * @var fossil_options::shuffle_enabled + * Flag to enable shuffling of tests. + */ typedef struct { bool show_version; bool show_help; @@ -59,6 +87,13 @@ typedef struct { bool shuffle_enabled; } fossil_options_t; +/** + * @enum test_status + * @brief Enumeration to represent the status of a test. + * + * This enumeration defines the possible status values for a test, including + * pass, fail, skip, empty, and timeout. + */ typedef enum { TEST_STATUS_PASS, TEST_STATUS_FAIL, @@ -67,37 +102,128 @@ typedef enum { TEST_STATUS_TTIMEOUT } test_status_t; -// Stack frame structure for tracking function call details during failures -typedef struct stack_frame { - const char *func; - const char *file; - int line; - struct stack_frame *next; -} stack_frame_t; - -// Test case structure +/** + * @struct test_case + * @brief Structure to hold a test case. + * + * This structure contains fields to hold information about a test case, including + * the name, test function, setup function, teardown function, status, failure message, + * execution time, and a pointer to the next test case. + * + * @var test_case::name + * Test case name + * + * @var test_case::test_func + * Pointer to test function + * + * @var test_case::setup_func + * Pointer to setup function (optional) + * + * @var test_case::teardown_func + * Pointer to teardown function (optional) + * + * @var test_case::status + * Test status (pass, fail, skip) + * + * @var test_case::failure_message + * Failure message (if any) + * + * @var test_case::execution_time + * Execution time of the test + * + * @var test_case::next + * Pointer to next test case in the list + */ typedef struct test_case { - const char *name; // Test case name - void (*test_func)(void); // Pointer to test function - void (*setup_func)(void); // Pointer to setup function (optional) - void (*teardown_func)(void); // Pointer to teardown function (optional) - test_status_t status; // Test status (pass, fail, skip) - const char *failure_message; // Failure message (if any) - stack_frame_t *stack_trace; // Stack trace for failures - double execution_time; // Execution time of the test - struct test_case *next; // Pointer to next test case in the list + const char *name; + void (*test_func)(void); + void (*setup_func)(void); + void (*teardown_func)(void); + test_status_t status; + const char *failure_message; + double execution_time; + struct test_case *next; } test_case_t; -// Test suite structure +/** + * @struct test_suite + * @brief Structure to hold a test suite. + * + * This structure contains fields to hold information about a test suite, including + * the name, suite setup function, suite teardown function, total execution time, + * list of test cases, and a pointer to the next test suite. + * + * @var test_suite::name + * Suite name + * + * @var test_suite::suite_setup_func + * Pointer to suite setup function (optional) + * + * @var test_suite::suite_teardown_func + * Pointer to suite teardown function (optional) + * + * @var test_suite::total_execution_time + * Total execution time of all test cases + * + * @var test_suite::tests + * List of test cases + * + * @var test_suite::next + * Pointer to next suite in the list + */ typedef struct test_suite { - const char *name; // Suite name - void (*suite_setup_func)(void); // Suite setup function (optional) - void (*suite_teardown_func)(void); // Suite teardown function (optional) - double total_execution_time; // Total execution time of all test cases - test_case_t *tests; // List of test cases - struct test_suite *next; // Pointer to next suite in the list + const char *name; + void (*suite_setup_func)(void); + void (*suite_teardown_func)(void); + double total_execution_time; + test_case_t *tests; + struct test_suite *next; } test_suite_t; +/** + * @struct fossil_test_env + * @brief Structure to hold the environment for fossil tests. + * + * This structure contains various fields to manage and track the state of + * test execution, including options, counts of different test outcomes, + * execution times, and a list of test suites. + * + * @var fossil_test_env::options + * Configuration options for the fossil test environment. + * + * @var fossil_test_env::env + * Environment buffer for handling non-local jumps (e.g., setjmp/longjmp). + * + * @var fossil_test_env::total_tests + * Total number of tests to be executed. + * + * @var fossil_test_env::pass_count + * Count of tests that have passed. + * + * @var fossil_test_env::fail_count + * Count of tests that have failed. + * + * @var fossil_test_env::skip_count + * Count of tests that have been skipped. + * + * @var fossil_test_env::empty_count + * Count of tests that are empty (i.e., no test cases). + * + * @var fossil_test_env::timeout_count + * Count of tests that have timed out. + * + * @var fossil_test_env::unexpected_count + * Count of tests that have encountered unexpected errors. + * + * @var fossil_test_env::start_execution_time + * Timestamp marking the start of test execution. + * + * @var fossil_test_env::end_execution_time + * Timestamp marking the end of test execution. + * + * @var fossil_test_env::test_suites + * Pointer to the list of test suites to be executed. + */ typedef struct fossil_test_env { fossil_options_t options; jmp_buf env; @@ -180,13 +306,16 @@ void fossil_test_run_case(test_case_t *test_case, fossil_test_env_t *env); void fossil_test_run_suite(test_suite_t *suite, fossil_test_env_t *env); /** - * @brief Asserts a condition and prints a message if the assertion fails. + * @brief Internal function to handle assertions with anomaly detection. * - * @param condition The condition to assert. - * @param message The message to print if the assertion fails. - * @param file The file where the assertion failed. - * @param line The line number where the assertion failed. - * @param func The function where the assertion failed. + * This function is used internally by the test framework to handle assertions + * and detect duplicate assertions. It is not intended to be called directly. + * + * @param condition The condition to check. + * @param message The message to display if the condition is false. + * @param file The file name where the assertion occurred. + * @param line The line number where the assertion occurred. + * @param func The function name where the assertion occurred. */ void fossil_test_assert_internal(bool condition, const char *message, const char *file, int line, const char *func); @@ -211,13 +340,6 @@ void fossil_test_summary(fossil_test_env_t *env); */ void fossil_test_run_all(fossil_test_env_t *env); -/** - * @brief Prints the stack trace. - * - * @param stack_trace The stack trace to print. - */ -void fossil_test_print_stack_trace(stack_frame_t *stack_trace); - // ***************************************************************************** // Macro definitions // ***************************************************************************** @@ -262,7 +384,6 @@ void fossil_test_print_stack_trace(stack_frame_t *stack_trace); nullptr, \ TEST_STATUS_PASS, \ nullptr, \ - nullptr, \ 0.0, \ nullptr \ }; \ @@ -277,7 +398,6 @@ void fossil_test_print_stack_trace(stack_frame_t *stack_trace); .teardown_func = NULL, \ .status = TEST_STATUS_PASS, \ .failure_message = NULL, \ - .stack_trace = NULL, \ .execution_time = 0.0, \ .next = NULL \ }; \ diff --git a/code/logic/testing.c b/code/logic/testing.c index 5f59e54b..c6f17c9a 100644 --- a/code/logic/testing.c +++ b/code/logic/testing.c @@ -308,22 +308,24 @@ fossil_options_t init_options(void) { } void usage_info(void) { - printf("Usage: fossil [options] [command]\n"); - printf("Options:\n"); - printf(" --version\t\tDisplays the current version of Fossil Test\n"); - printf(" --help\t\tShows help message with usage\n"); - printf(" --info\t\tDisplays detailed information about the test run.\n"); - printf("Commands:\n"); - printf(" reverse [enable|disable]\tEnables or disables reverse order of test execution\n"); - printf(" repeat [count]\t\tRepeats the test suite a specified number of times\n"); - printf(" shuffle [enable|disable]\tEnables or disables shuffling of test execution order\n"); + printf(FOSSIL_TEST_COLOR_CYAN FOSSIL_TEST_ATTR_ITATIC "Usage: fossil [options] [command]\n" FOSSIL_TEST_COLOR_RESET); + printf(FOSSIL_TEST_COLOR_BLUE FOSSIL_TEST_ATTR_BOLD "===================================================================\n" FOSSIL_TEST_COLOR_RESET); + printf(FOSSIL_TEST_COLOR_CYAN FOSSIL_TEST_ATTR_BOLD "Options:\n" FOSSIL_TEST_COLOR_RESET); + printf(FOSSIL_TEST_COLOR_CYAN FOSSIL_TEST_ATTR_ITATIC " --version\t\tDisplays the current version of Fossil Test\n" FOSSIL_TEST_COLOR_RESET); + printf(FOSSIL_TEST_COLOR_CYAN FOSSIL_TEST_ATTR_ITATIC " --help\t\tShows help message with usage\n" FOSSIL_TEST_COLOR_RESET); + printf(FOSSIL_TEST_COLOR_CYAN FOSSIL_TEST_ATTR_ITATIC " --info\t\tDisplays detailed information about the test run.\n" FOSSIL_TEST_COLOR_RESET); + printf(FOSSIL_TEST_COLOR_CYAN FOSSIL_TEST_ATTR_BOLD "Commands:\n" FOSSIL_TEST_COLOR_RESET); + printf(FOSSIL_TEST_COLOR_CYAN FOSSIL_TEST_ATTR_ITATIC " reverse [enable|disable]\tEnables or disables reverse order of test execution\n" FOSSIL_TEST_COLOR_RESET); + printf(FOSSIL_TEST_COLOR_CYAN FOSSIL_TEST_ATTR_ITATIC " repeat [count]\t\tRepeats the test suite a specified number of times\n" FOSSIL_TEST_COLOR_RESET); + printf(FOSSIL_TEST_COLOR_CYAN FOSSIL_TEST_ATTR_ITATIC " shuffle [enable|disable]\tEnables or disables shuffling of test execution order\n" FOSSIL_TEST_COLOR_RESET); + printf(FOSSIL_TEST_COLOR_BLUE FOSSIL_TEST_ATTR_BOLD "===================================================================\n" FOSSIL_TEST_COLOR_RESET); } void version_info(void) { - printf("Fossil Logic Test Framework\n"); - printf("Version: 1.1.3\n"); - printf("Author: Michael Gene Brockus (Dreamer)\n"); - printf("License: Mozila Public License 2.0\n"); + printf(FOSSIL_TEST_COLOR_BLUE FOSSIL_TEST_ATTR_BOLD "Fossil Logic Test Framework\n"); + printf(FOSSIL_TEST_COLOR_CYAN FOSSIL_TEST_ATTR_ITATIC "Version: 1.1.4\n"); + printf(FOSSIL_TEST_COLOR_CYAN FOSSIL_TEST_ATTR_ITATIC "Author: Michael Gene Brockus (Dreamer)\n"); + printf(FOSSIL_TEST_COLOR_CYAN FOSSIL_TEST_ATTR_ITATIC "License: Mozila Public License 2.0\n"); } // Parse command-line arguments @@ -543,12 +545,35 @@ void fossil_test_run_suite(test_suite_t *suite, fossil_test_env_t *env) { } } -// Internal function to handle assertions +// Internal function to handle assertions with anomaly detection void fossil_test_assert_internal(bool condition, const char *message, const char *file, int line, const char *func) { + static const char *last_message = NULL; // Store the last assertion message + static const char *last_file = NULL; // Store the last file name + static int last_line = 0; // Store the last line number + static const char *last_func = NULL; // Store the last function name + static int anomaly_count = 0; // Counter for anomaly detection + _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); + // Check if the current assertion is the same as the last one + if (last_message && strcmp(last_message, message) == 0 && + last_file && strcmp(last_file, file) == 0 && + last_line == line && + last_func && strcmp(last_func, func) == 0) { + anomaly_count++; + printf(FOSSIL_TEST_COLOR_YELLOW "Duplicate assertion detected: %s (%s:%d in %s) [Anomaly Count: %d]\n" FOSSIL_TEST_COLOR_RESET, message, file, line, func, anomaly_count); + } else { + anomaly_count = 0; // Reset anomaly count for new assertion + printf(FOSSIL_TEST_COLOR_RED "Assertion failed: %s (%s:%d in %s)\n" FOSSIL_TEST_COLOR_RESET, message, file, line, func); + } + + // Update the last assertion details + last_message = message; + last_file = file; + last_line = line; + last_func = func; + longjmp(test_jump_buffer, 1); // Jump back to test case failure handler } } @@ -707,11 +732,3 @@ void fossil_test_summary(fossil_test_env_t *env) { fossil_test_message(env); } - -void fossil_test_print_stack_trace(stack_frame_t *stack_trace) { - stack_frame_t *current_frame = stack_trace; - while (current_frame) { - printf(" at %s (%s:%d)\n", current_frame->func, current_frame->file, current_frame->line); - current_frame = current_frame->next; - } -} diff --git a/meson.build b/meson.build index b3b40ee0..558119e2 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.3', + version: '1.1.4', default_options: ['c_std=c11,c18', 'cpp_std=c++20']) subdir('code')