#ifndef UCX_TEST_H
#define UCX_TEST_H
#include <stdlib.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
#ifndef UCX_COMMON_H
typedef size_t (*cx_write_func)(
const void *,
size_t,
size_t,
void *
);
#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;
};
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;
}
static inline void cx_test_suite_free(CxTestSuite* suite) {
CxTestSet *l = suite->tests;
while (l != NULL) {
CxTestSet *e = l;
l = l->next;
free(e);
}
free(suite);
}
static 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;
}
}
static 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