OpenBLAS/utest/ctest.h

798 lines
22 KiB
C

/* Copyright 2011-2016 Bas van den Berg
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef CTEST_H
#define CTEST_H
#if defined _WIN32 || defined __CYGWIN__
#ifndef WIN32
#define WIN32
#endif
#endif
#ifndef WIN32
#define WEAK __attribute__ ((weak))
#else
#define WEAK
#endif
#ifndef __MSC_VER
#include <inttypes.h> /* intmax_t, uintmax_t, PRI* */
#endif
#include <stddef.h> /* size_t */
typedef void (*SetupFunc)(void*);
typedef void (*TearDownFunc)(void*);
typedef void (*RunWithDataFunc)(void*);
struct ctest {
const char* ssname; // suite name
const char* ttname; // test name
void (*run)(void);
int skip;
void* data;
SetupFunc setup;
TearDownFunc teardown;
struct ctest *next;
unsigned int magic;
};
#define __FNAME(sname, tname) __ctest_##sname##_##tname##_run
#define __TNAME(sname, tname) __ctest_##sname##_##tname
#define __PNAME(sname, tname) __ctest_##sname##_##tname##_pointer
#ifdef __APPLE__
#define __CTEST_APPLE
#endif
#ifdef __MINGW32__
#undef CTEST_SEGFAULT
#endif
#if defined(_WIN32)
#if defined(__clang__)
#define __CTEST_NO_TIME
#undef CTEST_SEGFAULT
#elif defined(_MSC_VER)
#define __CTEST_MSVC
#endif
#endif
//config for MSVC compiler
#ifdef __CTEST_MSVC
#define __CTEST_NO_TIME
#define CTEST_NO_COLORS
#if __MSC_VER >= 1500
#include <inttypes.h>
#else
#include <stdint.h>
#define CTEST_NO_INTTYPES
#endif
#ifndef CTEST_ADD_TESTS_MANUALLY
#pragma section(".ctest$a")
#pragma section(".ctest$u")
#pragma section(".ctest$z")
#endif
//clear this flag for msvc
#ifdef CTEST_SEGFAULT
#undef CTEST_SEGFAULT
#endif
#ifndef __cplusplus
#define inline __inline
#endif
#endif
#ifdef CTEST_NO_JMP
#define __CTEST_NO_JMP
#endif
#define __CTEST_MAGIC (0xdeadbeef)
#ifdef CTEST_ADD_TESTS_MANUALLY
# define __Test_Section
#else
#ifdef __CTEST_APPLE
#define __Test_Section __attribute__ ((used, section ("__DATA, .ctest")))
#elif defined (__CTEST_MSVC)
#define __Test_Section __declspec( allocate(".ctest$u"))
#else
#define __Test_Section __attribute__ ((used, section (".ctest")))
#endif
#endif
#ifndef __CTEST_MSVC
#define __CTEST_STRUCT(sname, tname, _skip, __data, __setup, __teardown) \
static struct ctest __TNAME(sname, tname) = { \
.ssname=#sname, \
.ttname=#tname, \
.run = __FNAME(sname, tname), \
.skip = _skip, \
.data = __data, \
.setup = (SetupFunc)__setup, \
.teardown = (TearDownFunc)__teardown, \
.next = NULL, \
.magic = __CTEST_MAGIC}; \
static void * __PNAME(sname, tname)[2] __Test_Section = {(void*)& __TNAME(sname,tname), (void*)__CTEST_MAGIC};
#else
//for msvc
#define __CTEST_STRUCT(sname, tname, _skip, __data, __setup, __teardown) \
static struct ctest __TNAME(sname, tname) = { \
#sname, \
#tname, \
__FNAME(sname, tname), \
_skip, \
__data, \
(SetupFunc)__setup, \
(TearDownFunc)__teardown, \
NULL, \
__CTEST_MAGIC}; \
__Test_Section static void * __PNAME(sname, tname)[2]= {(void*)& __TNAME(sname,tname), (void *)__CTEST_MAGIC};
#endif
#define CTEST_DATA(sname) struct sname##_data
#define CTEST_SETUP(sname) \
void WEAK sname##_setup(struct sname##_data* data)
#define CTEST_TEARDOWN(sname) \
void WEAK sname##_teardown(struct sname##_data* data)
#define __CTEST_INTERNAL(sname, tname, _skip) \
void __FNAME(sname, tname)(void); \
__CTEST_STRUCT(sname, tname, _skip, NULL, NULL, NULL) \
void __FNAME(sname, tname)(void)
#ifdef __CTEST_APPLE
#define SETUP_FNAME(sname) NULL
#define TEARDOWN_FNAME(sname) NULL
#else
#define SETUP_FNAME(sname) sname##_setup
#define TEARDOWN_FNAME(sname) sname##_teardown
#endif
#define __CTEST2_INTERNAL(sname, tname, _skip) \
static struct sname##_data __ctest_##sname##_data; \
CTEST_SETUP(sname); \
CTEST_TEARDOWN(sname); \
void __FNAME(sname, tname)(struct sname##_data* data); \
__CTEST_STRUCT(sname, tname, _skip, &__ctest_##sname##_data, SETUP_FNAME(sname), TEARDOWN_FNAME(sname)) \
void __FNAME(sname, tname)(struct sname##_data* data)
void CTEST_LOG(const char* fmt, ...);
void CTEST_ERR(const char* fmt, ...); // doesn't return
#define CTEST(sname, tname) __CTEST_INTERNAL(sname, tname, 0)
#define CTEST_SKIP(sname, tname) __CTEST_INTERNAL(sname, tname, 1)
#define CTEST2(sname, tname) __CTEST2_INTERNAL(sname, tname, 0)
#define CTEST2_SKIP(sname, tname) __CTEST2_INTERNAL(sname, tname, 1)
#ifdef CTEST_ADD_TESTS_MANUALLY
void __ctest_addTest(struct ctest *);
#define CTEST_ADD(sname, tname) do { \
extern struct ctest __TNAME(sname, tname); \
__ctest_addTest(&__TNAME(sname, tname)); \
} while (0)
#define CTEST_ADD2(sname, tname) do { \
extern struct ctest __TNAME(sname, tname); \
__ctest_addTest(&__TNAME(sname, tname)); \
} while (0)
#endif // CTEST_ADD_TESTS_MANUALLY
void assert_str(const char* exp, const char* real, const char* caller, int line);
#define ASSERT_STR(exp, real) assert_str(exp, real, __FILE__, __LINE__)
void assert_data(const unsigned char* exp, size_t expsize,
const unsigned char* real, size_t realsize,
const char* caller, int line);
#define ASSERT_DATA(exp, expsize, real, realsize) \
assert_data(exp, expsize, real, realsize, __FILE__, __LINE__)
void assert_equal(intmax_t exp, intmax_t real, const char* caller, int line);
#define ASSERT_EQUAL(exp, real) assert_equal(exp, real, __FILE__, __LINE__)
void assert_equal_u(uintmax_t exp, uintmax_t real, const char* caller, int line);
#define ASSERT_EQUAL_U(exp, real) assert_equal_u(exp, real, __FILE__, __LINE__)
void assert_not_equal(intmax_t exp, intmax_t real, const char* caller, int line);
#define ASSERT_NOT_EQUAL(exp, real) assert_not_equal(exp, real, __FILE__, __LINE__)
void assert_not_equal_u(uintmax_t exp, uintmax_t real, const char* caller, int line);
#define ASSERT_NOT_EQUAL_U(exp, real) assert_not_equal_u(exp, real, __FILE__, __LINE__)
void assert_interval(intmax_t exp1, intmax_t exp2, intmax_t real, const char* caller, int line);
#define ASSERT_INTERVAL(exp1, exp2, real) assert_interval(exp1, exp2, real, __FILE__, __LINE__)
void assert_null(void* real, const char* caller, int line);
#define ASSERT_NULL(real) assert_null((void*)real, __FILE__, __LINE__)
void assert_not_null(const void* real, const char* caller, int line);
#define ASSERT_NOT_NULL(real) assert_not_null(real, __FILE__, __LINE__)
void assert_true(int real, const char* caller, int line);
#define ASSERT_TRUE(real) assert_true(real, __FILE__, __LINE__)
void assert_false(int real, const char* caller, int line);
#define ASSERT_FALSE(real) assert_false(real, __FILE__, __LINE__)
void assert_fail(const char* caller, int line);
#define ASSERT_FAIL() assert_fail(__FILE__, __LINE__)
/* If longjmp() is not available, integer flag will be used instead of jmp_buf.
*
* __CTEST_SETJMP() will clear the flag and return zero, and __CTEST_LONGJMP()
* will set the flag to its argument. __CTEST_ERROR_CODE() will return that flag.
*
* If longjmp() is available, jmp_buf will be used as usual and __CTEST_ERROR_CODE()
* will always return zero.
*
* You can check both __CTEST_SETJMP() and __CTEST_ERROR_CODE() return value
* to detect error in a portable way.
*/
#ifdef __CTEST_NO_JMP
# define __CTEST_JMPBUF int
# define __CTEST_ERROR_CODE(_var) (_var)
# define __CTEST_SETJMP(_var) (_var = 0)
# define __CTEST_LONGJMP(_var, _err) (_var = _err)
#else // !__CTEST_NO_JMP
# define __CTEST_JMPBUF jmp_buf
# define __CTEST_ERROR_CODE(_var) (0)
# define __CTEST_SETJMP(_var) setjmp(_var)
# define __CTEST_LONGJMP(_var, _err) longjmp(_var, _err)
#endif // __CTEST_NO_JMP
void assert_dbl_near(double exp, double real, double tol, const char* caller, int line);
#define ASSERT_DBL_NEAR(exp, real) assert_dbl_near(exp, real, 1e-4, __FILE__, __LINE__)
#define ASSERT_DBL_NEAR_TOL(exp, real, tol) assert_dbl_near(exp, real, tol, __FILE__, __LINE__)
void assert_dbl_far(double exp, double real, double tol, const char* caller, int line);
#define ASSERT_DBL_FAR(exp, real) assert_dbl_far(exp, real, 1e-4, __FILE__, __LINE__)
#define ASSERT_DBL_FAR_TOL(exp, real, tol) assert_dbl_far(exp, real, tol, __FILE__, __LINE__)
#ifdef CTEST_MAIN
#ifndef __CTEST_NO_JMP
#include <setjmp.h>
#endif
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#ifndef __CTEST_NO_TIME
#include <sys/time.h>
#endif
#include <stdint.h>
#ifdef _WIN32
#include <io.h>
#else
#include <unistd.h>
#endif
#include <stdlib.h>
#ifdef __CTEST_APPLE
#include <dlfcn.h>
#endif
static size_t ctest_errorsize;
static char* ctest_errormsg;
#define MSG_SIZE 4096
static char ctest_errorbuffer[MSG_SIZE];
static __CTEST_JMPBUF ctest_err;
static int color_output = 1;
static const char* suite_name;
static const char* test_name;
typedef int (*filter_func)(struct ctest*);
#define ANSI_BLACK "\033[0;30m"
#define ANSI_RED "\033[0;31m"
#define ANSI_GREEN "\033[0;32m"
#define ANSI_YELLOW "\033[0;33m"
#define ANSI_BLUE "\033[0;34m"
#define ANSI_MAGENTA "\033[0;35m"
#define ANSI_CYAN "\033[0;36m"
#define ANSI_GREY "\033[0;37m"
#define ANSI_DARKGREY "\033[01;30m"
#define ANSI_BRED "\033[01;31m"
#define ANSI_BGREEN "\033[01;32m"
#define ANSI_BYELLOW "\033[01;33m"
#define ANSI_BBLUE "\033[01;34m"
#define ANSI_BMAGENTA "\033[01;35m"
#define ANSI_BCYAN "\033[01;36m"
#define ANSI_WHITE "\033[01;37m"
#define ANSI_NORMAL "\033[0m"
#ifdef __CTEST_MSVC
#ifndef CTEST_ADD_TESTS_MANUALLY
__declspec(allocate(".ctest$a")) struct ctest * ctest_win_begin;
__declspec(allocate(".ctest$z")) struct ctest * ctest_win_end;
#endif
#endif
static CTEST(suite, test) { }
#define __CTEST_POINTER_NEXT(_test) (struct ctest **)((struct ctest **)(_test) + 2)
#define __CTEST_POINTER_PREV(_test) (struct ctest **)((struct ctest **)(_test) - 2)
/* First element of test list.
*/
static struct ctest * * __ctest_head_p = (struct ctest **)__PNAME(suite, test);
#ifdef CTEST_ADD_TESTS_MANUALLY
/* Last element of test list.
*/
static struct ctest *__ctest_tail = &__TNAME(suite, test);
/* Add test to linked list manually.
*/
void __ctest_addTest(struct ctest *test)
{
__ctest_tail->next = test;
__ctest_tail = test;
}
#else // !CTEST_ADD_TESTS_MANUALLY
#ifndef __CTEST_MSVC
/* Add all tests to linked list automatically.
*/
static void __ctest_linkTests(void)
{
struct ctest ** test;
struct ctest ** ctest_begin = (struct ctest **)__PNAME(suite, test);
struct ctest ** ctest_end = (struct ctest **)__PNAME(suite, test);
// find begin and end of section by comparing magics
while (1) {
struct ctest** t = __CTEST_POINTER_PREV(ctest_begin);
if (t[0] == NULL) break;
if (t[1] != (struct ctest*)__CTEST_MAGIC) break;
ctest_begin = t;
}
while (1) {
struct ctest** t = __CTEST_POINTER_NEXT(ctest_end);
if (t[0] == NULL) break;
if (t[1] != (struct ctest*)__CTEST_MAGIC) break;
ctest_end = t;
}
ctest_end = __CTEST_POINTER_NEXT(ctest_end); // end after last one
for (test = ctest_begin; test != ctest_end; test = __CTEST_POINTER_NEXT(test)) {
struct ctest ** next_p = __CTEST_POINTER_NEXT(test);
struct ctest * next;
if (next_p == ctest_end)
next = NULL;
else
next = next_p[0];
(*test)->next = next;
}
__ctest_head_p = ctest_begin;
}
#else //for msvc
static void __ctest_linkTests(void)
{
struct ctest ** ctest_start = __ctest_head_p;
struct ctest ** test;
struct ctest * cur=ctest_start[0];
for(test=&ctest_win_begin; test!=&ctest_win_end; test++){
//check
if(test[1] == (struct ctest*)__CTEST_MAGIC){
//skip the start
if((test[0]) == ctest_start[0]) continue;
cur->next = test[0];
cur=cur->next;
cur->next=NULL;
}
}
}
#endif
#endif
inline static void vprint_errormsg(const char* const fmt, va_list ap) {
// (v)snprintf returns the number that would have been written
const int ret = vsnprintf(ctest_errormsg, ctest_errorsize, fmt, ap);
if (ret < 0) {
ctest_errormsg[0] = 0x00;
} else {
const size_t size = (size_t) ret;
const size_t s = (ctest_errorsize <= size ? size -ctest_errorsize : size);
// ctest_errorsize may overflow at this point
ctest_errorsize -= s;
ctest_errormsg += s;
}
}
inline static void print_errormsg(const char* const fmt, ...) {
va_list argp;
va_start(argp, fmt);
vprint_errormsg(fmt, argp);
va_end(argp);
}
static void msg_start(const char* color, const char* title) {
if (color_output) {
print_errormsg("%s", color);
}
print_errormsg(" %s: ", title);
}
static void msg_end(void) {
if (color_output) {
print_errormsg(ANSI_NORMAL);
}
print_errormsg("\n");
}
void CTEST_LOG(const char* fmt, ...)
{
va_list argp;
msg_start(ANSI_BLUE, "LOG");
va_start(argp, fmt);
vprint_errormsg(fmt, argp);
va_end(argp);
msg_end();
}
void CTEST_ERR(const char* fmt, ...)
{
va_list argp;
msg_start(ANSI_YELLOW, "ERR");
va_start(argp, fmt);
vprint_errormsg(fmt, argp);
va_end(argp);
msg_end();
__CTEST_LONGJMP(ctest_err, 1);
}
void assert_str(const char* exp, const char* real, const char* caller, int line) {
if ((exp == NULL && real != NULL) ||
(exp != NULL && real == NULL) ||
(exp && real && strcmp(exp, real) != 0)) {
CTEST_ERR("%s:%d expected '%s', got '%s'", caller, line, exp, real);
}
}
void assert_data(const unsigned char* exp, size_t expsize,
const unsigned char* real, size_t realsize,
const char* caller, int line) {
size_t i;
if (expsize != realsize) {
#ifndef CTEST_NO_INTTYPES
CTEST_ERR("%s:%d expected %" PRIuMAX " bytes, got %" PRIuMAX, caller, line, (uintmax_t) expsize, (uintmax_t) realsize);
#else
CTEST_ERR("%s:%d expected %u bytes, got %u", caller, line, (uintmax_t) expsize, (uintmax_t) realsize);
#endif
}
for (i=0; i<expsize; i++) {
if (exp[i] != real[i]) {
#ifndef CTEST_NO_INTTYPES
CTEST_ERR("%s:%d expected 0x%02x at offset %" PRIuMAX " got 0x%02x",
#else
CTEST_ERR("%s:%d expected 0x%02x at offset %u got 0x%02x",
#endif
caller, line, exp[i], (uintmax_t) i, real[i]);
}
}
}
void assert_equal(intmax_t exp, intmax_t real, const char* caller, int line) {
if (exp != real) {
#ifndef CTEST_NO_INTTYPES
CTEST_ERR("%s:%d expected %" PRIdMAX ", got %" PRIdMAX, caller, line, exp, real);
#else
CTEST_ERR("%s:%d expected %d, got %d", caller, line, exp, real);
#endif
}
}
void assert_equal_u(uintmax_t exp, uintmax_t real, const char* caller, int line) {
if (exp != real) {
#ifndef CTEST_NO_INTTYPES
CTEST_ERR("%s:%d expected %" PRIuMAX ", got %" PRIuMAX, caller, line, exp, real);
#else
CTEST_ERR("%s:%d expected %u, got %u", caller, line, exp, real);
#endif
}
}
void assert_not_equal(intmax_t exp, intmax_t real, const char* caller, int line) {
if ((exp) == (real)) {
#ifndef CTEST_NO_INTTYPES
CTEST_ERR("%s:%d should not be %" PRIdMAX, caller, line, real);
#else
CTEST_ERR("%s:%d should not be %d", caller, line, real);
#endif
}
}
void assert_not_equal_u(uintmax_t exp, uintmax_t real, const char* caller, int line) {
if ((exp) == (real)) {
#ifndef CTEST_NO_INTTYPES
CTEST_ERR("%s:%d should not be %" PRIuMAX, caller, line, real);
#else
CTEST_ERR("%s:%d should not be %u", caller, line, real);
#endif
}
}
void assert_interval(intmax_t exp1, intmax_t exp2, intmax_t real, const char* caller, int line) {
if (real < exp1 || real > exp2) {
#ifndef CTEST_NO_INTTYPES
CTEST_ERR("%s:%d expected %" PRIdMAX "-%" PRIdMAX ", got %" PRIdMAX, caller, line, exp1, exp2, real);
#else
CTEST_ERR("%s:%d expected %d-%d, got %d", caller, line, exp1, exp2, real);
#endif
}
}
void assert_dbl_near(double exp, double real, double tol, const char* caller, int line) {
double diff = exp - real;
double absdiff = diff;
/* avoid using fabs and linking with a math lib */
if(diff < 0) {
absdiff *= -1;
}
if (absdiff > tol) {
CTEST_ERR("%s:%d expected %0.3e, got %0.3e (diff %0.3e, tol %0.3e)", caller, line, exp, real, diff, tol);
}
}
void assert_dbl_far(double exp, double real, double tol, const char* caller, int line) {
double diff = exp - real;
double absdiff = diff;
/* avoid using fabs and linking with a math lib */
if(diff < 0) {
absdiff *= -1;
}
if (absdiff <= tol) {
CTEST_ERR("%s:%d expected %0.3e, got %0.3e (diff %0.3e, tol %0.3e)", caller, line, exp, real, diff, tol);
}
}
void assert_null(void* real, const char* caller, int line) {
if ((real) != NULL) {
CTEST_ERR("%s:%d should be NULL", caller, line);
}
}
void assert_not_null(const void* real, const char* caller, int line) {
if (real == NULL) {
CTEST_ERR("%s:%d should not be NULL", caller, line);
}
}
void assert_true(int real, const char* caller, int line) {
if ((real) == 0) {
CTEST_ERR("%s:%d should be true", caller, line);
}
}
void assert_false(int real, const char* caller, int line) {
if ((real) != 0) {
CTEST_ERR("%s:%d should be false", caller, line);
}
}
void assert_fail(const char* caller, int line) {
CTEST_ERR("%s:%d shouldn't come here", caller, line);
}
static int suite_all(struct ctest* t) {
(void) t; // fix unused parameter warning
return 1;
}
static int suite_filter(struct ctest* t) {
return strncmp(suite_name, t->ssname, strlen(suite_name)) == 0;
}
static int suite_test_filter(struct ctest* t) {
int suit_match, test_match;
suit_match=(strncmp(suite_name, t->ssname, strlen(suite_name)) == 0);
test_match=(strncmp(test_name, t->ttname, strlen(test_name)) == 0);
return (suit_match & test_match);
}
#ifndef __CTEST_NO_TIME
static uint64_t getCurrentTime(void) {
struct timeval now;
gettimeofday(&now, NULL);
uint64_t now64 = (uint64_t) now.tv_sec;
now64 *= 1000000;
now64 += ((uint64_t) now.tv_usec);
return now64;
}
#endif
static void color_print(const char* color, const char* text) {
if (color_output)
printf("%s%s"ANSI_NORMAL"\n", color, text);
else
printf("%s\n", text);
}
#ifdef __CTEST_APPLE
static void *find_symbol(struct ctest *test, const char *fname)
{
size_t len = strlen(test->ssname) + 1 + strlen(fname);
char *symbol_name = (char *) malloc(len + 1);
memset(symbol_name, 0, len + 1);
snprintf(symbol_name, len + 1, "%s_%s", test->ssname, fname);
//fprintf(stderr, ">>>> dlsym: loading %s\n", symbol_name);
void *symbol = dlsym(RTLD_DEFAULT, symbol_name);
if (!symbol) {
//fprintf(stderr, ">>>> ERROR: %s\n", dlerror());
}
// returns NULL on error
free(symbol_name);
return symbol;
}
#endif
#ifdef CTEST_SEGFAULT
#include <signal.h>
static void sighandler(int signum)
{
char msg[128];
snprintf(msg, sizeof(msg), "[SIGNAL %d: %s]", signum, strsignal(signum));
color_print(ANSI_BRED, msg);
fflush(stdout);
/* "Unregister" the signal handler and send the signal back to the process
* so it can terminate as expected */
signal(signum, SIG_DFL);
kill(getpid(), signum);
}
#endif
int ctest_main(int argc, const char *argv[])
{
static int total = 0;
static int num_ok = 0;
static int num_fail = 0;
static int num_skip = 0;
static int index = 1;
static filter_func filter = suite_all;
const char* color = (num_fail) ? ANSI_BRED : ANSI_GREEN;
char results[80];
static struct ctest* test;
#ifdef CTEST_SEGFAULT
signal(SIGSEGV, sighandler);
#endif
if (argc == 2) {
suite_name = argv[1];
filter = suite_filter;
}else if (argc == 3) {
suite_name = argv[1];
test_name = argv[2];
filter = suite_test_filter;
}
#ifdef CTEST_NO_COLORS
color_output = 0;
#else
color_output = isatty(1);
#endif
#ifndef __CTEST_NO_TIME
uint64_t t1 = getCurrentTime();
#endif
#ifndef CTEST_ADD_TESTS_MANUALLY
__ctest_linkTests();
#endif
for (test = *(__ctest_head_p); test != NULL; test=test->next) {
if (test == &__ctest_suite_test) continue;
if (filter(test)) total++;
}
for (test = *(__ctest_head_p); test != NULL; test=test->next) {
if (test == &__ctest_suite_test) continue;
if (filter(test)) {
ctest_errorbuffer[0] = 0;
ctest_errorsize = MSG_SIZE-1;
ctest_errormsg = ctest_errorbuffer;
printf("TEST %d/%d %s:%s ", index, total, test->ssname, test->ttname);
fflush(stdout);
if (test->skip) {
color_print(ANSI_BYELLOW, "[SKIPPED]");
num_skip++;
} else {
int result = __CTEST_SETJMP(ctest_err);
if (result == 0) {
#ifdef __CTEST_APPLE
if (!test->setup) {
test->setup = (SetupFunc) find_symbol(test, "setup");
}
if (!test->teardown) {
test->teardown = (TearDownFunc) find_symbol(test, "teardown");
}
#endif
if (test->setup) test->setup(test->data);
if (test->data)
((RunWithDataFunc)test->run)(test->data);
else
test->run();
if (test->teardown) test->teardown(test->data);
// if we got here it's ok
#ifdef CTEST_COLOR_OK
color_print(ANSI_BGREEN, "[OK]");
#else
printf("[OK]\n");
#endif
num_ok++;
} else {
color_print(ANSI_BRED, "[FAIL]");
num_fail++;
}
if (ctest_errorsize != MSG_SIZE-1) printf("%s", ctest_errorbuffer);
}
index++;
}
}
#ifndef __CTEST_NO_TIME
uint64_t t2 = getCurrentTime();
#endif
#ifndef __CTEST_NO_TIME
sprintf(results, "RESULTS: %d tests (%d ok, %d failed, %d skipped) ran in %"PRIu64" ms", total, num_ok, num_fail, num_skip, (t2 - t1)/1000);
#else
sprintf(results, "RESULTS: %d tests (%d ok, %d failed, %d skipped)", total, num_ok, num_fail, num_skip);
#endif
color_print(color, results);
return num_fail;
}
#endif
#endif