292 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			292 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
|  | /*
 | ||
|  | **  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 <stdio.h>
 | ||
|  | #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 <stdlib.h>
 | ||
|  | #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
 |