#ifndef UNITTEST_H
#define UNITTEST_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <stdint.h>
#include <time.h>
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(result, expected, actual, msg) \
unittest_assert_int_equal((result), (expected), (actual), (msg), __LINE__, __FILE__)
#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