ossp-pkg/al/al_test.c
/*
** OSSP al - Assembly Line
** Copyright (c) 2002-2005 Ralf S. Engelschall <rse@engelschall.com>
** Copyright (c) 2002-2005 The OSSP Project <http://www.ossp.org/>
** Copyright (c) 2002-2005 Cable & Wireless <http://www.cw.com/>
** Copyright (c) 2002-2005 Michael van Elst <mlelstv@serpens.de>
**
** This file is part of OSSP al, an abstract datatype of a data buffer
** that can assemble, move and truncate data but avoids actual copying
** and which can be found at http://www.ossp.org/pkg/lib/al/.
**
** Permission to use, copy, modify, and distribute this software for
** any purpose with or without fee is hereby granted, provided that
** the above copyright notice and this permission notice appear in all
** copies.
**
** THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
** USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
** OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
** SUCH DAMAGE.
**
** al_test.c: assembly line library test suite
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include "ts.h"
#include "al.h"
static char label, label2, label3;
#define LABEL ((al_label_t)&label)
#define LABEL2 ((al_label_t)&label2)
#define LABEL3 ((al_label_t)&label3)
#define S(s) s, strlen(s)
#define EVAL(name,rc,rc0,block) \
ts_test_check(TS_CTX, name); \
block \
if (rc != rc0) \
ts_test_fail(TS_CTX, "%s -> %d[%s] (expected %d[%s])\n", \
name, rc, al_error(rc), rc0, al_error(rc0))
#define CHECKLEN(name, al) \
do { \
size_t good, bad; \
if (checklen(al, &good, &bad)) \
ts_test_fail(TS_CTX, "%s -> corrupted length %d (expected %d)\n", \
name, good, bad); \
} while (0)
#define EVAL0(name,block) EVAL(name,rc,AL_OK,block)
#define EVAL1(name,al,block) EVAL(name,rc,AL_OK,block); CHECKLEN(name, al);
#define CHECKLEN1(name, al, n) \
do { \
CHECKLEN(name, al); \
if (al_bytes(al) != (n)) \
ts_test_fail(TS_CTX, "%s -> invalid length %d (expected %d)\n", \
al_bytes(al), (n)); \
} while (0)
static int checklen(al_t *al, size_t *goodp, size_t *badp)
{
al_rc_t rc;
al_tx_t *tx;
al_chunk_t *cur;
size_t total, total2;
total = 0;
rc = al_txalloc(al, &tx);
if (rc != AL_OK)
return -1;
rc = al_traverse(al, 0, al_bytes(al), AL_FORWARD, NULL, tx);
if (rc != AL_OK) {
al_txfree(al, tx);
return -1;
}
while (al_traverse_next(al, tx, &cur) == AL_OK)
total += al_chunk_len(cur);
al_traverse_end(al, tx, 1);
al_txfree(al, tx);
total2 = al_bytes(al);
if (total != total2) {
*goodp = total;
*badp = total2;
return -1;
}
return 0;
}
/* test: data copy */
TS_TEST(test_al_data)
{
al_rc_t rc;
al_t *al;
EVAL0("al_create", {
rc = al_create(&al);
});
if (rc != AL_OK) return;
EVAL1("al_append_bytes", al, {
rc = al_append_bytes(al, S("Hello world\n"), NULL);
});
EVAL0("al_destroy", {
rc = al_destroy(al);
});
}
/* test: splicing */
TS_TEST(test_al_splice)
{
al_rc_t rc;
al_t *src, *ins, *dst;
int i;
EVAL0("al_create(&src)", {
rc = al_create(&src);
});
if (rc != AL_OK) return;
EVAL0("al_create(&ins)", {
rc = al_create(&ins);
});
if (rc != AL_OK) return;
EVAL0("al_create(&dst)", {
rc = al_create(&dst);
});
if (rc != AL_OK) return;
for (i=0; i<500; ++i) {
EVAL1("al_append_bytes(&src)", src, {
rc = al_append_bytes(src, S("Huhu world\n"), LABEL);
});
}
EVAL1("al_append_bytes(src)", src, {
rc = al_append_bytes(src, S("Goodbye world\n"), LABEL);
});
EVAL1("al_append_bytes(src)", src, {
rc = al_append_bytes(src, S("Goodbye world\n"), LABEL2);
});
EVAL1("al_prepend_byts(src)", src, {
rc = al_prepend_bytes(src, S("Hello world\n"), LABEL2);
});
EVAL1("al_prepend_bytes(src)", src, {
rc = al_prepend_bytes(src, S("Hello world\n"), LABEL);
});
EVAL1("al_prepend_bytes(ins)", ins, {
rc = al_prepend_bytes(ins, S("KICK "), LABEL3);
});
EVAL1("al_append_bytes(ins)", ins, {
rc = al_append_bytes(ins, S("ME\n"), LABEL3);
});
EVAL0("al_splice", {
rc = al_splice(src, 4, 80, ins, dst);
});
CHECKLEN1("al_splice(src)",src, 5480);
CHECKLEN1("al_splice(ins)",ins, 0);
CHECKLEN1("al_splice(dst)",dst, 80);
EVAL0("al_destroy(dst)", {
rc = al_destroy(dst);
});
EVAL0("al_destroy(ins)", {
rc = al_destroy(ins);
});
EVAL0("al_destroy(src)", {
rc = al_destroy(src);
});
}
/* test: labelling */
TS_TEST(test_al_label)
{
al_rc_t rc;
al_t *al;
al_tx_t *tx;
al_chunk_t *cur;
int i, k;
static struct { size_t len; int ln; } chunks[] = {
{ 5, 1 },
{ 2, 2 },
{ 2, 3 },
{ 5, 2 },
{ 14, 1 },
{ 0, 0 }
};
static al_label_t labels[] = {
NULL,
LABEL,
LABEL2,
LABEL3
};
EVAL0("al_create", {
rc = al_create(&al);
});
if (rc != AL_OK) return;
EVAL0("al_append_bytes", {
rc = al_append_bytes(al, S("Hello world. Goodbye world.\n"), LABEL);
});
/*
* 28 chars LABEL
*/
EVAL1("al_setlabel", al, {
rc = al_setlabel(al, 7, 2, NULL, LABEL3);
});
/*
* 7 chars LABEL
* 2 chars LABEL3
* 19 chars LABEL
*/
EVAL1("al_setlabel", al, {
rc = al_setlabel(al, 5, 9, LABEL, LABEL2);
});
/*
* 5 chars LABEL
* 2 chars LABEL2
* 2 chars LABEL3
* 5 chars LABEL2
* 14 chars LABEL
*/
EVAL0("al_txalloc", {
rc = al_txalloc(al, &tx);
});
EVAL0("al_traverse", {
rc = al_traverse(al, 0, al_bytes(al), AL_FORWARD, NULL, tx);
});
i = 0;
ts_test_check(TS_CTX, "al_traverse_next");
while (al_traverse_next(al, tx, &cur) == AL_OK) {
size_t n = al_chunk_len(cur);
al_label_t l = al_chunk_label(cur);
for (k=3; k>0; --k)
if (labels[k] == l)
break;
if (chunks[i].len == 0) {
ts_test_fail(TS_CTX,
"al_traverse_next: found chunk %d[L%d] (none expected)\n",
n, k);
continue;
}
if (n != chunks[i].len || k != chunks[i].ln) {
ts_test_fail(TS_CTX,
"al_traverse_next: found chunk %d[L%d] (expected %d[L%d])\n",
n, k, chunks[i].len, chunks[i].ln);
}
++i;
}
EVAL0("al_traverse_end", {
rc = al_traverse_end(al, tx, 1);
});
EVAL0("al_txfree", {
rc = al_txfree(al, tx);
});
EVAL0("al_destroy", {
rc = al_destroy(al);
});
}
/* test: attach */
static char *reclaim_ptr = NULL;
static size_t reclaim_size = 0;
static void *reclaim_u = NULL;
static int reclaim_count = 0;
static void reclaim(char *p, size_t n, void *u)
{
++reclaim_count;
if (reclaim_ptr != NULL ||
reclaim_size != 0 ||
reclaim_u != NULL)
return;
reclaim_ptr = p;
reclaim_size = n;
reclaim_u = u;
}
static const char *reclaim_result(const char *p, size_t n, void *u)
{
if (reclaim_count == 0)
return "reclaim never called";
if (reclaim_count > 1)
return "repeated call";
if (p != reclaim_ptr ||
n != reclaim_size ||
u != reclaim_u)
return "bad data for free";
return NULL;
}
TS_TEST(test_al_attach)
{
al_rc_t rc;
al_t *al;
char baf[] = "Mittendrin\n";
char *context = "CONTEXT";
const char *mess;
EVAL0("al_create", {
rc = al_create(&al);
});
if (rc != AL_OK) return;
EVAL1("al_append_bytes", al, {
rc = al_append_bytes(al, S("Hello world\n"), NULL);
});
EVAL1("al_attach_buffer", al, {
rc = al_attach_buffer(al, S(baf), NULL, reclaim, context);
});
EVAL1("al_append_bytes", al, {
rc = al_append_bytes(al, S("Goodbye world\n"), NULL);
});
EVAL0("al_destroy", {
rc = al_destroy(al);
});
mess = reclaim_result(S(baf), context);
if (mess != NULL)
ts_test_fail(TS_CTX,"buffer reclaim failed: %s\n",mess);
}
/* test: exception handling */
#ifdef WITH_EX
#include "ex.h"
TS_TEST(test_al_ex)
{
volatile al_t *al;
ex_t ex;
int caught;
ts_test_check(TS_CTX, "exception handling");
caught = 0;
al = NULL;
ex_try {
al_create(&al);
al_append_bytes(al, S("DUMMYDUMMY"), NULL);
al_splice(al, 50, 1, NULL, NULL);
}
ex_cleanup {
if (al != NULL) al_destroy(al);
}
ex_catch (ex) {
if ((al_rc_t)ex.ex_value != AL_ERR_EOF)
ts_test_fail(TS_CTX, "unexpected exception: %d\n", (al_rc_t)ex.ex_value);
caught = 1;
}
if (!caught)
ts_test_fail(TS_CTX, "expected exception not caught\n");
}
#endif
int main(int argc, char *argv[])
{
ts_suite_t *ts;
int n;
ts = ts_suite_new("OSSP al (Assembly Line)");
ts_suite_test(ts, test_al_data, "assembly line data copying");
ts_suite_test(ts, test_al_splice, "assembly line splicing");
ts_suite_test(ts, test_al_label, "assembly line labelling");
ts_suite_test(ts, test_al_attach, "assembly line buffer attach");
#ifdef WITH_EX
ts_suite_test(ts, test_al_ex, "exception handling");
#endif
n = ts_suite_run(ts);
ts_suite_free(ts);
return n;
}