blob: ee878bd6260811623541ac3b9a2ec15d3a5f621d [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.
*/
#include "apr_file_io.h"
#include "at.h"
#include "apr_lib.h"
#include "apr_strings.h"
#include "apr_tables.h"
#include "apr_env.h"
#include <assert.h>
apr_status_t at_begin(at_t *t, int total)
{
char buf[32];
apr_snprintf(buf, 32, "1..%d", total);
return at_report(t, buf);
}
static apr_status_t test_cleanup(void *data)
{
at_t *t = data;
if (t->current < t->plan)
return at_report(t, "Bail out!");
else
return at_report(t, "END");
}
void at_end(at_t *t)
{
apr_pool_cleanup_kill(t->pool, t, test_cleanup);
test_cleanup(t);
}
apr_status_t at_comment(at_t *t, const char *fmt, va_list vp)
{
apr_status_t s;
char buf[256], *b = buf + 2;
char *end;
int rv;
rv = apr_vsnprintf(b, 250, fmt, vp);
if (rv <= 0)
return APR_EINVAL;
end = b + rv;
buf[0] = '#';
buf[1] = ' ';
if (rv == 250) {
*end++ = '.';
*end++ = '.';
*end++ = '.';
*end = '\n';
}
else if (end[-1] != '\n') {
*end = '\n';
}
b = buf;
while (1) {
char *eol;
eol = strchr(b + 2, '\n');
assert(eol != NULL);
*eol = 0;
s = at_report(t, b);
if (s != APR_SUCCESS || eol == end)
break;
b = eol - 1;
b[0] = '#';
b[1] = ' ';
}
return s;
}
void at_ok(at_t *t, int is_ok, const char *label, const char *file, int line)
{
char format[] = "not ok %d - %s # %s (%s:%d) test %d in %s";
char *end = format + 10;
char *fmt = is_ok ? format + 4 : format;
char buf[256];
const char *comment = NULL;
int rv, is_fatal = 0, is_skip = 0, is_todo = 0;
t->current++;
if (*t->fatal == t->current) {
t->fatal++;
is_fatal = 1;
}
if (*t->skip == t->current) {
t->skip++;
is_skip = 1;
}
if (*t->todo == t->current) {
t->todo++;
is_todo = 1;
}
if (AT_FLAG_CONCISE(t->flags))
format[9] = '\0';
else if (is_ok && !AT_FLAG_TRACE(t->flags))
format[14] = '\0';
else if (is_fatal && ! is_ok)
comment = "bail";
else
comment = is_todo ? "todo" : is_skip ? "skip" : "at";
rv = apr_snprintf(buf, 256, fmt, t->current + t->prior,
label, comment, file, line, t->current, t->name);
if (rv <= 0)
exit(-1);
end = buf + rv;
if (rv == 250) {
*end++ = '.';
*end++ = '.';
*end++ = '.';
*end = '\0';
}
if (memchr(buf, '\n', rv) != NULL || at_report(t, buf) != APR_SUCCESS)
exit(-1);
if (!is_ok && is_fatal) {
if (t->abort != NULL) {
at_trace(t, "Abandon %s, omitting tests %d - %d from this run.",
t->name, t->prior + t->current + 1, t->prior + t->plan);
longjmp(*t->abort, 0);
}
else {
apr_pool_cleanup_kill(t->pool, t, test_cleanup);
at_report(t, "Bail out!");
exit(-1);
}
}
}
struct at_report_file {
at_report_t module;
apr_file_t *file;
};
static apr_status_t at_report_file_write(at_report_t *ctx, const char *msg)
{
struct at_report_file *r = (struct at_report_file *)ctx;
apr_file_t *f = r->file;
apr_size_t len = strlen(msg);
apr_status_t s;
s = apr_file_write_full(f, msg, len, &len);
if (s != APR_SUCCESS)
return s;
s = apr_file_putc('\n', f);
if (s != APR_SUCCESS)
return s;
return apr_file_flush(f);
}
at_report_t *at_report_file_make(apr_pool_t *p, apr_file_t *f)
{
struct at_report_file *r = apr_palloc(p, sizeof *r);
r->module.func = at_report_file_write;
r->file = f;
return &r->module;
}
struct at_report_local {
at_report_t module;
at_t *t;
at_report_t *saved_report;
const int *saved_fatal;
int dummy_fatal;
const char *file;
int line;
int passed;
apr_pool_t *pool;
};
static apr_status_t report_local_cleanup(void *data)
{
struct at_report_local *q = data;
dAT = q->t;
char label[32];
apr_snprintf(label, 32, "collected %d passing tests", q->passed);
AT->report = q->saved_report;
AT->fatal = q->saved_fatal;
at_ok(q->t, 1, label, q->file, q->line);
return APR_SUCCESS;
}
static apr_status_t at_report_local_write(at_report_t *ctx, const char *msg)
{
struct at_report_local *q = (struct at_report_local *)ctx;
dAT = q->t;
if (strncmp(msg, "not ok", 6) == 0) {
q->saved_report->func(q->saved_report, msg);
AT->report = q->saved_report;
AT->fatal = q->saved_fatal;
apr_pool_cleanup_kill(q->pool, q, report_local_cleanup);
at_trace(AT, "Abandon %s, omitting tests %d - %d from this run.",
AT->name, AT->prior + AT->current + 1, AT->prior + AT->plan);
longjmp(*AT->abort, 0);
}
AT->current--;
q->passed++;
return APR_SUCCESS;
}
void at_report_local(at_t *AT, apr_pool_t *p, const char *file, int line)
{
struct at_report_local *q = apr_palloc(p, sizeof *q);
q->module.func = at_report_local_write;
q->t = AT;
q->saved_report = AT->report;
q->saved_fatal = AT->fatal;
q->dummy_fatal = 0;
q->file = apr_pstrdup(p, file);
q->line = line;
q->passed = 0;
q->pool = p;
AT->fatal = &q->dummy_fatal;
AT->report = &q->module;
if (*q->saved_fatal == AT->current + 1)
q->saved_fatal++;
apr_pool_cleanup_register(p, q, report_local_cleanup,
report_local_cleanup);
}
at_t *at_create(apr_pool_t *pool, unsigned char flags, at_report_t *report)
{
at_t *t = apr_pcalloc(pool, sizeof *t);
t->flags = flags;
t->report = report;
t->pool = pool;
apr_pool_cleanup_register(pool, t, test_cleanup, test_cleanup);
return t;
}
#define AT_NELTS 4
static int* at_list(apr_pool_t *pool, const char *spec, int *list)
{
apr_array_header_t arr;
int prev, current = 0;
arr.pool = pool;
arr.elt_size = sizeof *list;
arr.nelts = 0;
arr.nalloc = AT_NELTS;
arr.elts = (char *)list;
do {
while (*spec && !apr_isdigit(*spec))
++spec;
prev = current;
current = (int)apr_strtoi64(spec, (char **)&spec, 10);
*(int *)apr_array_push(&arr) = current;
} while (prev >= current);
return (int *)arr.elts;
}
apr_status_t at_run(at_t *AT, const at_test_t *test)
{
int dummy = 0, fbuf[AT_NELTS], sbuf[AT_NELTS], tbuf[AT_NELTS];
jmp_buf j;
AT->current = 0;
AT->prior += AT->plan;
AT->name = test->name;
AT->plan = test->plan;
if (test->fatals)
AT->fatal = at_list(AT->pool, test->fatals, fbuf);
else
AT->fatal = &dummy;
if (test->skips)
AT->skip = at_list(AT->pool, test->skips, sbuf);
else
AT->skip = &dummy;
if (test->todos)
AT->todo = at_list(AT->pool, test->todos, tbuf);
else
AT->todo = &dummy;
AT->abort = &j;
if (setjmp(j) == 0) {
test->func(AT);
return APR_SUCCESS;
}
AT->abort = NULL;
return APR_EGENERAL;
}