/* ** OSSP al - Assembly Line ** Copyright (c) 2002-2005 Ralf S. Engelschall ** Copyright (c) 2002-2005 The OSSP Project ** Copyright (c) 2002-2005 Cable & Wireless ** Copyright (c) 2002-2005 Michael van Elst ** ** 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 #include #include #include #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; }