#ifndef UNITTEST_H #define UNITTEST_H #include #include #include #include #include #include typedef enum { UNITTEST_PASS, UNITTEST_FAIL, UNITTEST_ERROR, UNITTEST_SKIP, UNITTEST_XFAIL, UNITTEST_XPASS } UnittestResultType_e; typedef enum { UNITTEST_FORMAT_TEXT, UNITTEST_FORMAT_QUIET, UNITTEST_FORMAT_JSON, UNITTEST_FORMAT_XML, UNITTEST_FORMAT_HTML, UNITTEST_FORMAT_TAP } UnittestOutputFormat_e; typedef struct { char *assertion_type; char *message; int line_number; char *file_name; char *expected_str; char *actual_str; double execution_time_ms; bool passed; } UnittestAssertionInfo_t; typedef struct { char *test_name; char *test_method; char *test_class; UnittestResultType_e result_type; char *error_message; UnittestAssertionInfo_t **assertions; size_t assertion_count; size_t assertion_capacity; double execution_time_ms; int line_number; char *file_name; char *traceback; char *skip_reason; } UnittestTestResult_t; typedef struct { char *class_name; UnittestTestResult_t **results; size_t result_count; size_t result_capacity; int passed_count; int failed_count; int error_count; int skipped_count; int xfail_count; int xpass_count; double total_time_ms; } UnittestTestCase_t; typedef struct { UnittestTestCase_t **test_cases; size_t test_case_count; size_t test_case_capacity; int total_passed; int total_failed; int total_errors; int total_skipped; int total_xfail; int total_xpass; double total_suite_time_ms; char *test_suite_name; time_t start_time; } UnittestTestSuite_t; typedef struct { UnittestOutputFormat_e output_format; int verbosity; FILE *output_stream; char *output_file; char **test_names_to_run; size_t test_names_count; bool stop_on_first_failure; bool catch_exceptions; bool track_execution_time; double timeout_seconds; bool show_local_variables; bool show_full_traceback; int max_traceback_depth; char *test_pattern; bool randomize_order; int random_seed; bool use_colors; char *test_runner_name; char *test_environment; } UnittestConfig_t; UnittestConfig_t* unittest_config_create(void); void unittest_config_destroy(UnittestConfig_t *config); UnittestTestSuite_t* unittest_test_suite_create(const char *suite_name); void unittest_test_suite_add_test_case(UnittestTestSuite_t *suite, UnittestTestCase_t *test_case); void unittest_test_suite_destroy(UnittestTestSuite_t *suite); UnittestTestCase_t* unittest_test_case_create(const char *class_name); void unittest_test_case_add_result(UnittestTestCase_t *test_case, UnittestTestResult_t *result); void unittest_test_case_destroy(UnittestTestCase_t *test_case); UnittestTestResult_t* unittest_test_result_create(const char *test_class, const char *test_method, int line_number, const char *file_name); void unittest_test_result_set_skip(UnittestTestResult_t *result, const char *reason); void unittest_test_result_set_xfail(UnittestTestResult_t *result, const char *reason); void unittest_test_result_set_error(UnittestTestResult_t *result, const char *error_message, const char *traceback); void unittest_test_result_destroy(UnittestTestResult_t *result); void unittest_record_assertion(UnittestTestResult_t *result, const char *assertion_type, const char *expected_str, const char *actual_str, const char *message, int line_number, const char *file_name, bool passed); bool unittest_assert_int_equal(UnittestTestResult_t *result, int expected, int actual, const char *message, int line, const char *file); bool unittest_assert_int_not_equal(UnittestTestResult_t *result, int expected, int actual, const char *message, int line, const char *file); bool unittest_assert_int_greater(UnittestTestResult_t *result, int actual, int threshold, const char *message, int line, const char *file); bool unittest_assert_int_less(UnittestTestResult_t *result, int actual, int threshold, const char *message, int line, const char *file); bool unittest_assert_int_greater_equal(UnittestTestResult_t *result, int actual, int threshold, const char *message, int line, const char *file); bool unittest_assert_int_less_equal(UnittestTestResult_t *result, int actual, int threshold, const char *message, int line, const char *file); bool unittest_assert_long_equal(UnittestTestResult_t *result, long expected, long actual, const char *message, int line, const char *file); bool unittest_assert_long_not_equal(UnittestTestResult_t *result, long expected, long actual, const char *message, int line, const char *file); bool unittest_assert_double_equal(UnittestTestResult_t *result, double expected, double actual, double epsilon, const char *message, int line, const char *file); bool unittest_assert_double_not_equal(UnittestTestResult_t *result, double expected, double actual, double epsilon, const char *message, int line, const char *file); bool unittest_assert_string_equal(UnittestTestResult_t *result, const char *expected, const char *actual, const char *message, int line, const char *file); bool unittest_assert_string_not_equal(UnittestTestResult_t *result, const char *expected, const char *actual, const char *message, int line, const char *file); bool unittest_assert_string_contains(UnittestTestResult_t *result, const char *substring, const char *string, const char *message, int line, const char *file); bool unittest_assert_string_not_contains(UnittestTestResult_t *result, const char *substring, const char *string, const char *message, int line, const char *file); bool unittest_assert_string_starts_with(UnittestTestResult_t *result, const char *prefix, const char *string, const char *message, int line, const char *file); bool unittest_assert_string_ends_with(UnittestTestResult_t *result, const char *suffix, const char *string, const char *message, int line, const char *file); bool unittest_assert_true(UnittestTestResult_t *result, bool condition, const char *message, int line, const char *file); bool unittest_assert_false(UnittestTestResult_t *result, bool condition, const char *message, int line, const char *file); bool unittest_assert_null(UnittestTestResult_t *result, void *ptr, const char *message, int line, const char *file); bool unittest_assert_not_null(UnittestTestResult_t *result, void *ptr, const char *message, int line, const char *file); bool unittest_assert_memory_equal(UnittestTestResult_t *result, const void *expected, const void *actual, size_t length, const char *message, int line, const char *file); bool unittest_assert_array_int_equal(UnittestTestResult_t *result, const int *expected, const int *actual, size_t length, const char *message, int line, const char *file); bool unittest_assert_fail(UnittestTestResult_t *result, const char *message, int line, const char *file); bool unittest_assert_pass(UnittestTestResult_t *result, const char *message, int line, const char *file); int unittest_run_suite(UnittestTestSuite_t *suite, UnittestConfig_t *config); void unittest_generate_report(UnittestTestSuite_t *suite, UnittestConfig_t *config); void unittest_get_summary(UnittestTestSuite_t *suite, int *total, int *passed, int *failed, int *errors, int *skipped); void _unittest_format_text(UnittestTestSuite_t *suite, UnittestConfig_t *config, FILE *output); void _unittest_format_quiet(UnittestTestSuite_t *suite, UnittestConfig_t *config, FILE *output); void _unittest_format_json(UnittestTestSuite_t *suite, UnittestConfig_t *config, FILE *output); void _unittest_format_xml(UnittestTestSuite_t *suite, UnittestConfig_t *config, FILE *output); void _unittest_format_html(UnittestTestSuite_t *suite, UnittestConfig_t *config, FILE *output); void _unittest_format_tap(UnittestTestSuite_t *suite, UnittestConfig_t *config, FILE *output); double unittest_get_time_ms(void); #define UNITTEST_ASSERT_EQUAL_INT(result, expected, actual, msg) \ unittest_assert_int_equal((result), (expected), (actual), (msg), __LINE__, __FILE__) #define UNITTEST_ASSERT_NOT_EQUAL_INT(result, expected, actual, msg) \ unittest_assert_int_not_equal((result), (expected), (actual), (msg), __LINE__, __FILE__) #define UNITTEST_ASSERT_GREATER(result, actual, threshold, msg) \ unittest_assert_int_greater((result), (actual), (threshold), (msg), __LINE__, __FILE__) #define UNITTEST_ASSERT_LESS(result, actual, threshold, msg) \ unittest_assert_int_less((result), (actual), (threshold), (msg), __LINE__, __FILE__) #define UNITTEST_ASSERT_GREATER_EQUAL(result, actual, threshold, msg) \ unittest_assert_int_greater_equal((result), (actual), (threshold), (msg), __LINE__, __FILE__) #define UNITTEST_ASSERT_LESS_EQUAL(result, actual, threshold, msg) \ unittest_assert_int_less_equal((result), (actual), (threshold), (msg), __LINE__, __FILE__) #define UNITTEST_ASSERT_EQUAL_LONG(result, expected, actual, msg) \ unittest_assert_long_equal((result), (expected), (actual), (msg), __LINE__, __FILE__) #define UNITTEST_ASSERT_NOT_EQUAL_LONG(result, expected, actual, msg) \ unittest_assert_long_not_equal((result), (expected), (actual), (msg), __LINE__, __FILE__) #define UNITTEST_ASSERT_EQUAL_DOUBLE(result, expected, actual, eps, msg) \ unittest_assert_double_equal((result), (expected), (actual), (eps), (msg), __LINE__, __FILE__) #define UNITTEST_ASSERT_NOT_EQUAL_DOUBLE(result, expected, actual, eps, msg) \ unittest_assert_double_not_equal((result), (expected), (actual), (eps), (msg), __LINE__, __FILE__) #define UNITTEST_ASSERT_EQUAL_STR(result, expected, actual, msg) \ unittest_assert_string_equal((result), (expected), (actual), (msg), __LINE__, __FILE__) #define UNITTEST_ASSERT_NOT_EQUAL_STR(result, expected, actual, msg) \ unittest_assert_string_not_equal((result), (expected), (actual), (msg), __LINE__, __FILE__) #define UNITTEST_ASSERT_STRING_CONTAINS(result, substring, string, msg) \ unittest_assert_string_contains((result), (substring), (string), (msg), __LINE__, __FILE__) #define UNITTEST_ASSERT_STRING_NOT_CONTAINS(result, substring, string, msg) \ unittest_assert_string_not_contains((result), (substring), (string), (msg), __LINE__, __FILE__) #define UNITTEST_ASSERT_STRING_STARTS_WITH(result, prefix, string, msg) \ unittest_assert_string_starts_with((result), (prefix), (string), (msg), __LINE__, __FILE__) #define UNITTEST_ASSERT_STRING_ENDS_WITH(result, suffix, string, msg) \ unittest_assert_string_ends_with((result), (suffix), (string), (msg), __LINE__, __FILE__) #define UNITTEST_ASSERT_TRUE(result, condition, msg) \ unittest_assert_true((result), (condition), (msg), __LINE__, __FILE__) #define UNITTEST_ASSERT_FALSE(result, condition, msg) \ unittest_assert_false((result), (condition), (msg), __LINE__, __FILE__) #define UNITTEST_ASSERT_NULL(result, ptr, msg) \ unittest_assert_null((result), (ptr), (msg), __LINE__, __FILE__) #define UNITTEST_ASSERT_NOT_NULL(result, ptr, msg) \ unittest_assert_not_null((result), (ptr), (msg), __LINE__, __FILE__) #define UNITTEST_ASSERT_MEMORY_EQUAL(result, expected, actual, len, msg) \ unittest_assert_memory_equal((result), (expected), (actual), (len), (msg), __LINE__, __FILE__) #define UNITTEST_ASSERT_ARRAY_INT_EQUAL(result, expected, actual, len, msg) \ unittest_assert_array_int_equal((result), (expected), (actual), (len), (msg), __LINE__, __FILE__) #define UNITTEST_ASSERT_FAIL(result, msg) \ unittest_assert_fail((result), (msg), __LINE__, __FILE__) #define UNITTEST_ASSERT_PASS(result, msg) \ unittest_assert_pass((result), (msg), __LINE__, __FILE__) #define UNITTEST_BEGIN_TEST(class_name, method_name) \ UnittestTestResult_t *_unittest_result = unittest_test_result_create(class_name, method_name, __LINE__, __FILE__); \ double _unittest_start_time = unittest_get_time_ms(); #define UNITTEST_END_TEST() \ _unittest_result->execution_time_ms = unittest_get_time_ms() - _unittest_start_time; \ if (_unittest_result->result_type == UNITTEST_PASS) { \ for (size_t i = 0; i < _unittest_result->assertion_count; i++) { \ if (!_unittest_result->assertions[i]->passed) { \ _unittest_result->result_type = UNITTEST_FAIL; \ break; \ } \ } \ } \ return _unittest_result; #define UNITTEST_SKIP(reason) \ (void)_unittest_start_time; \ unittest_test_result_set_skip(_unittest_result, reason); \ return _unittest_result; #endif