/* ** tap4embedded : http://github.com/fperrad/tap4embedded ** ** Copyright (C) 2016-2017 Francois Perrad. ** ** tap4embedded is free software; you can redistribute it and/or modify it ** under the terms of the Artistic License 2.0 */ #ifndef TAP_H #define TAP_H #ifndef TAP_PUTCHAR #include #define TAP_PUTCHAR(c) fputc((c), stdout) #define TAP_FLUSH() fflush(stdout) #endif #ifndef TAP_FLUSH #define TAP_FLUSH() #endif #if defined(__unix__) || defined(_WIN32) || defined(_WIN64) #include #define TAP_EXIT(n) exit(n) #else #define TAP_EXIT(n) #endif #if defined(__bool_true_false_are_defined) || defined(__cplusplus) #define TAP_BOOL bool #define TAP_TRUE true #define TAP_FALSE false #else #define TAP_BOOL char #define TAP_TRUE 1 #define TAP_FALSE 0 #endif #ifdef __cplusplus extern "C" { #endif extern void plan(unsigned int nb); extern void no_plan(void); extern void skip_all(const char *reason); extern void done_testing(unsigned int nb); extern void bail_out(const char *reason); extern void ok(const char *file, unsigned int line, TAP_BOOL test, const char *name); extern void todo(const char *reason, unsigned int count); extern void skip(const char *reason, unsigned int count); extern void todo_skip(const char *reason); extern void skip_rest(const char *reason); extern void diag (const char *msg); extern int exit_status (void); #define OK(test, name) ok(__FILE__, __LINE__, (test), (name)) #define NOK(test, name) ok(__FILE__, __LINE__, !(test), (name)) #define PASS(name) ok(__FILE__, __LINE__, TAP_TRUE, (name)) #define FAIL(name) ok(__FILE__, __LINE__, TAP_FALSE, (name)) #ifndef TAP_NO_IMPL typedef struct { unsigned int curr_test; unsigned int expected_tests; unsigned int todo_upto; const char *todo_reason; TAP_BOOL have_plan; TAP_BOOL no_plan; TAP_BOOL have_output_plan; TAP_BOOL done_testing; TAP_BOOL is_passing; } tap_t; tap_t tap; void putstr(const char *msg) { TAP_BOOL need_begin = TAP_FALSE; for (; *msg != '\0'; msg++) { if (need_begin) { TAP_PUTCHAR('#'); TAP_PUTCHAR(' '); need_begin = TAP_FALSE; } TAP_PUTCHAR(*msg); if (*msg == '\n') { need_begin = TAP_TRUE; } } } void putuint (unsigned int n) { char buf[8 * sizeof(unsigned int) + 1]; char *ptr = &buf[sizeof(buf) - 1]; *ptr = '\0'; do { char c = '0' + (n % 10u); --ptr; *ptr = c; n /= 10u; } while (n != 0u); putstr(ptr); } void not_yet_plan(void) { if (tap.have_plan) { putstr("You tried to plan twice\n"); TAP_FLUSH(); TAP_EXIT(-1); } } void plan(unsigned int nb) { not_yet_plan(); putstr("1.."); putuint(nb); TAP_PUTCHAR('\n'); tap.expected_tests = nb; tap.have_plan = TAP_TRUE; tap.have_output_plan = TAP_TRUE; tap.is_passing = TAP_TRUE; } void no_plan(void) { not_yet_plan(); tap.have_plan = TAP_TRUE; tap.no_plan = TAP_TRUE; tap.is_passing = TAP_TRUE; } void skip_all(const char *reason) { not_yet_plan(); putstr("1..0 # SKIP "); putstr(reason); TAP_PUTCHAR('\n'); tap.curr_test = 1u; tap.expected_tests = 1u; tap.have_plan = TAP_TRUE; tap.have_output_plan = TAP_TRUE; tap.is_passing = TAP_TRUE; TAP_FLUSH(); } void done_testing(unsigned int nb) { if (tap.done_testing) { putstr("done_testing() was already called\n"); } else { tap.done_testing = TAP_TRUE; if ((tap.expected_tests != nb) && (tap.expected_tests != 0u)) { putstr("# Looks like you planned "); putuint(tap.expected_tests); putstr(" tests but ran "); putuint(nb); TAP_PUTCHAR('.'); TAP_PUTCHAR('\n'); } else { tap.expected_tests = nb; } if (! tap.have_output_plan) { putstr("1.."); putuint(nb); TAP_PUTCHAR('\n'); tap.have_output_plan = TAP_TRUE; } if ((tap.expected_tests != tap.curr_test) || (tap.curr_test == 0u)) { tap.is_passing = TAP_FALSE; } putstr("# Done with tap4embedded.\n"); } TAP_FLUSH(); } void bail_out(const char *reason) { putstr("Bail out!"); if (reason != NULL) { TAP_PUTCHAR(' '); TAP_PUTCHAR(' '); putstr(reason); } TAP_PUTCHAR('\n'); TAP_FLUSH(); TAP_EXIT(-1); } void need_plan(void) { if (! tap.have_plan) { putstr("You tried to run a test without a plan\n"); TAP_FLUSH(); TAP_EXIT(-1); } } void ok(const char *file, unsigned int line, TAP_BOOL test, const char *name) { need_plan(); ++tap.curr_test; if (! test) { putstr("not "); } putstr("ok "); putuint(tap.curr_test); if (name != NULL) { putstr(" - "); putstr(name); } if ((tap.todo_reason != NULL) && (tap.todo_upto >= tap.curr_test)) { putstr(" # TODO # "); putstr(tap.todo_reason); } TAP_PUTCHAR('\n'); if (! test) { putstr("# Failed"); if (tap.todo_upto >= tap.curr_test) { putstr(" (TODO)"); } else { tap.is_passing = TAP_FALSE; } putstr(" test ("); putstr(file); putstr(" at line "); putuint(line); putstr(")\n"); } } void todo(const char *reason, unsigned int count) { tap.todo_upto = tap.curr_test + count; tap.todo_reason = reason; } void skip(const char *reason, unsigned int count) { unsigned int i; need_plan(); for (i = 0u; i < count; i++) { ++tap.curr_test; putstr("ok "); putuint(tap.curr_test); putstr(" - # skip"); if (reason != NULL) { TAP_PUTCHAR(' '); putstr(reason); } TAP_PUTCHAR('\n'); } } void todo_skip(const char *reason) { need_plan(); ++tap.curr_test; putstr("not ok "); putuint(tap.curr_test); putstr(" - # TODO & SKIP"); if (reason != NULL) { TAP_PUTCHAR(' '); putstr(reason); } TAP_PUTCHAR('\n'); } void skip_rest(const char *reason) { skip(reason, tap.expected_tests - tap.curr_test); } void diag (const char *msg) { TAP_PUTCHAR('#'); TAP_PUTCHAR(' '); putstr(msg); TAP_PUTCHAR('\n'); } int exit_status (void) { if (! tap.done_testing) { done_testing(tap.curr_test); } #if defined(EXIT_SUCCESS) && defined(EXIT_FAILURE) return tap.is_passing ? EXIT_SUCCESS : EXIT_FAILURE; #else return tap.is_passing ? 0 : -1; #endif } #endif #ifdef __cplusplus } #endif #endif