blob: 720d590cee8dfd2e38e5890b742931544544dfc6 [file] [log] [blame]
/*
** Copyright 2004-2005 The Apache Software Foundation
**
** 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.
*/
/* at.h: TAP-compliant C utilities for the Apache::Test framework. */
#ifndef AT_H
#define AT_H
#include "apr.h"
#include "apr_file_io.h"
#include <stdarg.h>
#include <stdlib.h>
#include <setjmp.h>
typedef struct at_t at_t;
typedef struct at_report_t at_report_t;
typedef apr_status_t (*at_report_function_t)(at_report_t *r, const char *msg);
typedef void(*at_test_function_t)(at_t *t);
typedef struct at_test_t at_test_t;
struct at_test_t {
const char *name;
at_test_function_t func;
int plan;
const char *fatals;
const char *todos;
const char *skips;
};
struct at_report_t {
at_report_function_t func;
};
/* We only need one at_t struct per test suite, so lets call it *AT.
* The mnemonic we follow is that (for lowercase foo) "AT_foo(bar)"
* should be syntactically equivalent to "at_foo(AT, bar)".
*
* Terminology: test == an at_test_t,
* check == an assertion which produces TAP.
*/
#define dAT at_t *AT
struct at_t {
int current; /* current check for this test */
int prior; /* total # of checks prior to this test */
const char *name; /* name of current test */
int plan; /* total # of checks in this test */
const int *fatal; /* list of unrecoverables */
const int *todo; /* list of expected failures */
const int *skip; /* list of ignorabe assertions */
at_report_t *report ;/* handles the results of each check */
unsigned char flags; /* verbosity: concise, trace, debug, etc. */
apr_pool_t *pool; /* creator pool with end-of-test cleanup */
jmp_buf *abort; /* where fatals go to die */
};
static APR_INLINE
apr_status_t at_report(at_t *t, const char *msg) {
at_report_t *r = t->report;
return r->func(r, msg);
}
#define AT_report(msg) at_report(AT, msg)
/* The core assertion checker; the rest just wind up invoking this one. */
void at_ok(at_t *t, int is_ok, const char *label, const char *file, int line);
#define AT_ok(is_ok, label) at_ok(AT, is_ok, label, __FILE__, __LINE__)
at_t *at_create(apr_pool_t *pool, unsigned char flags, at_report_t *report);
apr_status_t at_begin(at_t *t, int total);
#define AT_begin(total) at_begin(AT, total)
apr_status_t at_run(at_t *AT, const at_test_t *test);
#define AT_run(test) at_run(AT, test)
void at_end(at_t *t);
#define AT_end() at_end(AT)
#define AT_FLAG_DEBUG(f) ((f) & 4)
#define AT_FLAG_DEBUG_ON(f) ((f) |= 4)
#define AT_FLAG_DEBUG_OFF(f) ((f) &= ~4)
#define AT_FLAG_TRACE(f) ((f) & 2)
#define AT_FLAG_TRACE_ON(f) ((f) |= 2)
#define AT_FLAG_TRACE_OFF(f) ((f) &= ~2)
#define AT_FLAG_CONCISE(f) ((f) & 1)
#define AT_FLAG_CONCISE_ON(f) ((f) |= 1)
#define AT_FLAG_CONCISE_OFF(f) ((f) &= ~1)
#define AT_debug_on() AT_FLAG_DEBUG_ON(AT->flags)
#define AT_debug_off() AT_FLAG_DEBUG_OFF(AT->flags)
#define AT_trace_on() AT_FLAG_TRACE_ON(AT->flags)
#define AT_trace_off() AT_FLAG_TRACE_OFF(AT->flags)
#define AT_concise_on() AT_FLAG_CONCISE_ON(AT->flags)
#define AT_concise_off() AT_FLAG_CONCISE_OFF(AT->flags)
/* Additional reporting utils.
These emit TAP comments, and are not "checks". */
apr_status_t at_comment(at_t *t, const char *fmt, va_list vp);
static APR_INLINE
void at_debug(at_t *t, const char *fmt, ...) {
va_list vp;
va_start(vp, fmt);
if (AT_FLAG_DEBUG(t->flags))
at_comment(t, fmt, vp);
va_end(vp);
}
static APR_INLINE
void at_trace(at_t *t, const char *fmt, ...) {
va_list vp;
va_start(vp, fmt);
if (AT_FLAG_TRACE(t->flags))
at_comment(t, fmt, vp);
va_end(vp);
}
/* These are "checks". */
static APR_INLINE
void at_check(at_t *t, int is_ok, const char *label, const char *file,
int line, const char *fmt, ...)
{
va_list vp;
va_start(vp, fmt);
if (AT_FLAG_TRACE(t->flags)) {
char format[64] = "testing: %s (%s:%d)";
at_trace(t, format, label, file, line);
if (fmt != NULL) {
char *f;
at_trace(t, " format: %s", fmt);
strcpy(format, " left: ");
strcpy(format + sizeof(" left: ") -1, fmt);
f = format + strlen(format);
strcpy(f+1, format);
*f = '\n';
memcpy(f+1, " right: ", sizeof(" right: ") -1);
at_comment(t, format, vp);
}
}
va_end(vp);
at_ok(t, is_ok, label, file, line);
}
#define AT_mem_ne(a, b, n) do { \
unsigned sz = n; \
const void *left = a, *right = b; \
char fmt[] = ", as %u-byte struct pointers"; \
char buf[256] = #a " != " #b; \
const unsigned blen = sizeof(#a " != " #b); \
apr_snprintf(buf + blen - 1, 256 - blen, fmt, sz); \
apr_snprintf(fmt, sizeof(fmt), "%%.%us", sz); \
at_check(AT, memcmp(left, right, sz), buf, __FILE__, __LINE__, \
fmt, left, right); \
} while (0) \
#define AT_mem_eq(a, b, n) do { \
unsigned sz = n; \
const void *left = a, *right = b; \
char fmt[] = ", as %u-byte struct pointers"; \
char buf[256] = #a " == " #b; \
const unsigned blen = sizeof(#a " == " #b); \
apr_snprintf(buf + blen - 1, 256 - blen , fmt, sz); \
apr_snprintf(fmt, sizeof(fmt), "%%.%us", sz); \
at_check(AT, !memcmp(left, right, sz), buf, __FILE__, __LINE__, \
fmt, left, right); \
} while (0)
#define AT_str_eq(a, b) do { \
const char *left = a, *right = b; \
at_check(AT,!strcmp(left, right), #a " == " #b ", as strings", \
__FILE__, __LINE__, "%s", left, right); \
} while (0)
#define AT_str_ne(a, b) do { \
const char *left = a, *right = b; \
at_check(AT, strcmp(left, right), #a " != " #b ", as strings", \
__FILE__, __LINE__, "%s", left, right); \
} while (0)
#define AT_ptr_eq(a, b) do { \
const void *left = a, *right = b; \
at_check(AT, left == right, #a " == " #b ", as pointers", \
__FILE__, __LINE__, "%pp", left, right); \
} while (0)
#define AT_ptr_ne(a, b) do { \
const void *left = a, *right = b; \
at_check(AT, left != right, #a " != " #b ", as pointers", \
__FILE__, __LINE__, "%pp", left, right); \
} while (0)
#define AT_int_eq(a, b) do { \
const int left = a, right = b; \
at_check(AT, left == right, #a " == " #b ", as integers", \
__FILE__, __LINE__, "%d", left, right); \
} while (0)
#define AT_int_ne(a, b) do { \
const int left = a, right = b; \
at_check(AT, left != right, #a " != " #b ", as integers", \
__FILE__, __LINE__, "%d", left, right); \
} while (0)
#define AT_is_null(a) AT_ptr_eq(a, NULL)
#define AT_not_null(a) AT_ptr_ne(a, NULL)
/* XXX these two macro checks evaluate a & b more than once, but the
* upshot is that they don't care too much about their types.
*/
#define AT_EQ(a, b, fmt) at_check(AT, ((a) == (b)), #a " == " #b,\
__FILE__, __LINE__, fmt, a, b)
#define AT_NE(a, b, fmt) at_check(AT, ((a) != (b)), #a " != " #b,\
__FILE__, __LINE__, fmt, a, b)
/* Report utilities. */
at_report_t *at_report_file_make(apr_pool_t *p, apr_file_t *f);
APR_INLINE
static at_report_t *at_report_stdout_make(apr_pool_t *p)
{
apr_file_t *out;
apr_file_open_stdout(&out, p);
return at_report_file_make(p, out);
}
void at_report_local(at_t *AT, apr_pool_t *p, const char *file, int line);
#define AT_localize(p) at_report_local(AT, p, __FILE__, __LINE__)
#endif /* AT_H */