2025-12-04 22:38:05 +01:00
|
|
|
#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);
|
|
|
|
|
|
2025-12-04 23:00:10 +01:00
|
|
|
#define UNITTEST_ASSERT_EQUAL(result, expected, actual, msg) \
|
|
|
|
|
unittest_assert_int_equal((result), (expected), (actual), (msg), __LINE__, __FILE__)
|
|
|
|
|
|
2025-12-04 22:38:05 +01:00
|
|
|
#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
|