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
|