#ifndef UCX_TEST_H
#define UCX_TEST_H
#include "common.h"
#include <stdio.h>
#include <string.h>
#include <setjmp.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifndef __FUNCTION__
#define __FUNCTION__ __func__
#endif
#if !defined(__clang__) &&
__GNUC__ >
3
#pragma GCC diagnostic ignored
"-Wclobbered"
#endif
typedef struct CxTestSuite CxTestSuite;
typedef void(*CxTest)(CxTestSuite *,
void *, cx_write_func);
typedef struct CxTestSet CxTestSet;
struct CxTestSet {
CxTest test;
CxTestSet *next;
};
struct CxTestSuite {
unsigned int success;
unsigned int failure;
const char *name;
CxTestSet *tests;
};
cx_attr_nonnull cx_attr_nodiscard cx_attr_cstr_arg(
1) cx_attr_malloc
static inline CxTestSuite* cx_test_suite_new(
const char *name) {
CxTestSuite* suite = (CxTestSuite*) malloc(
sizeof(CxTestSuite));
if (suite !=
NULL) {
suite->name = name;
suite->success =
0;
suite->failure =
0;
suite->tests =
NULL;
}
return suite;
}
CX_INLINE void cx_test_suite_free(CxTestSuite* suite) {
if (suite ==
NULL)
return;
CxTestSet *l = suite->tests;
while (l !=
NULL) {
CxTestSet *e = l;
l = l->next;
free(e);
}
free(suite);
}
cx_attr_nonnull
CX_INLINE int cx_test_register(CxTestSuite* suite, CxTest test) {
CxTestSet *t = (CxTestSet*) malloc(
sizeof(CxTestSet));
if (t) {
t->test = test;
t->next =
NULL;
if (suite->tests ==
NULL) {
suite->tests = t;
}
else {
CxTestSet *last = suite->tests;
while (last->next) {
last = last->next;
}
last->next = t;
}
return 0;
}
else {
return 1;
}
}
cx_attr_nonnull
CX_INLINE void cx_test_run(CxTestSuite *suite,
void *out_target, cx_write_func out_writer) {
if (suite->name ==
NULL) {
out_writer(
"*** Test Suite ***\n",
1,
19, out_target);
}
else {
out_writer(
"*** Test Suite : ",
1,
17, out_target);
out_writer(suite->name,
1, strlen(suite->name), out_target);
out_writer(
" ***\n",
1,
5, out_target);
}
suite->success =
0;
suite->failure =
0;
for (CxTestSet *elem = suite->tests; elem; elem = elem->next) {
elem->test(suite, out_target, out_writer);
}
out_writer(
"\nAll test completed.\n",
1,
21, out_target);
char total[
80];
int len = snprintf(
total,
80,
" Total: %u\n Success: %u\n Failure: %u\n\n",
suite->success + suite->failure, suite->success, suite->failure
);
out_writer(total,
1, len, out_target);
}
#define cx_test_run_f(suite, file) cx_test_run(suite, (
void*)file, (cx_write_func)fwrite)
#define cx_test_run_stdout(suite) cx_test_run_f(suite, stdout)
#define CX_TEST(name)
void name(CxTestSuite* _suite_,
void *_output_, cx_write_func _writefnc_)
#define CX_TEST_DO _writefnc_(
"Running ",
1,
8, _output_);\
_writefnc_(
__FUNCTION__,
1, strlen(
__FUNCTION__), _output_);\
_writefnc_(
"... ",
1,
4, _output_);\
jmp_buf _env_;\
for (
unsigned int _cx_test_loop_ =
0 ;\
_cx_test_loop_ ==
0 && !setjmp(_env_);\
_writefnc_(
"success.\n",
1,
9, _output_),\
_suite_->success++, _cx_test_loop_++)
#define CX_TEST_ASSERTM(condition,message)
if (!(condition)) { \
const char *_assert_msg_ = message; \
_writefnc_(_assert_msg_,
1, strlen(_assert_msg_), _output_); \
_writefnc_(
".\n",
1,
2, _output_); \
_suite_->failure++; \
longjmp(_env_,
1);\
} (
void)
0
#define CX_TEST_ASSERT(condition)
CX_TEST_ASSERTM(condition,
#condition " failed")
#define CX_TEST_SUBROUTINE(name,...)
void name(CxTestSuite* _suite_,\
void *_output_, cx_write_func _writefnc_, jmp_buf _env_,
__VA_ARGS__)
#define CX_TEST_CALL_SUBROUTINE(name,...) \
name(_suite_,_output_,_writefnc_,_env_,
__VA_ARGS__)
#ifdef __cplusplus
}
#endif
#endif