/*
** Licensed to the Apache Software Foundation (ASF) under one or more
** contributor license agreements. See the NOTICE file distributed with
** this work for additional information regarding copyright ownership.
** The ASF licenses this file to You 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>
#include "apr_strings.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 *skips;
const char *todos;
};
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 *skip; /* list of ignorabe assertions */
const int *todo; /* list of expected failures */
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[32] = "testing: %s (%s:%d)";
at_trace(t, format, label, file, line);
if (fmt != NULL) {
char *f;
apr_snprintf(format, sizeof format, " format: %s", fmt);
at_trace(t, "%s", format);
memcpy(format, " left:", 8);
f = format + strlen(format);
apr_snprintf(f, sizeof format - strlen(format), "\n right: %s", fmt);
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)
static APR_INLINE
void at_skip(at_t *t, int n, const char *reason, const char *file, int line) {
char buf[256];
while (n-- > 0) {
++t->current;
apr_snprintf(buf, 256, "ok %d - %s (%d) #skipped: %s (%s:%d)",
t->current + t->prior, t->name, t->current, reason, file, line);
at_report(t, buf);
}
}
#define AT_skip(n, reason) at_skip(AT, n, reason, __FILE__, __LINE__)
/* 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 */