Index: ossp-pkg/var/Makefile.in RCS File: /v/ossp/cvs/ossp-pkg/var/Makefile.in,v rcsdiff -q -kk '-r1.6' '-r1.7' -u '/v/ossp/cvs/ossp-pkg/var/Makefile.in,v' 2>/dev/null --- Makefile.in 2002/02/28 08:48:44 1.6 +++ Makefile.in 2002/03/07 14:58:41 1.7 @@ -51,7 +51,7 @@ LIB_OBJS = var.lo TST_NAME = var_test -TST_OBJS = var_test.o +TST_OBJS = var_test.o ts.o PLY_NAME = var_play PLY_OBJS = var_play.o Index: ossp-pkg/var/acconfig.h RCS File: /v/ossp/cvs/ossp-pkg/var/Attic/acconfig.h,v rcsdiff -q -kk '-r1.1' '-r1.2' -u '/v/ossp/cvs/ossp-pkg/var/Attic/acconfig.h,v' 2>/dev/null --- acconfig.h 2002/01/30 19:37:14 1.1 +++ acconfig.h 2002/03/07 14:58:41 1.2 @@ -2,3 +2,6 @@ /* whether to use OSSP ex */ #undef WITH_EX +/* whether to use Dmalloc */ +#undef WITH_DMALLOC + Index: ossp-pkg/var/configure.ac RCS File: /v/ossp/cvs/ossp-pkg/var/configure.ac,v rcsdiff -q -kk '-r1.8' '-r1.9' -u '/v/ossp/cvs/ossp-pkg/var/configure.ac,v' 2>/dev/null --- configure.ac 2002/03/07 12:11:09 1.8 +++ configure.ac 2002/03/07 14:58:41 1.9 @@ -45,6 +45,7 @@ AC_CHECK_FUNCS(snprintf) AC_CHECK_EXTLIB([OSSP ex], ex, __ex_ctx, ex.h, [AC_DEFINE(WITH_EX)]) +AC_CHECK_EXTLIB([Dmalloc], dmalloc, dmalloc_debug, dmalloc.h, AC_DEFINE(WITH_DMALLOC)) AC_CONFIG_HEADERS(config.h) AC_CONFIG_FILES(Makefile var-config) Index: ossp-pkg/var/ts.c RCS File: /v/ossp/cvs/ossp-pkg/var/ts.c,v co -q -kk -p'1.1' '/v/ossp/cvs/ossp-pkg/var/ts.c,v' | diff -u /dev/null - -L'ossp-pkg/var/ts.c' 2>/dev/null --- ossp-pkg/var/ts.c +++ - 2024-05-03 05:35:39.815817314 +0200 @@ -0,0 +1,463 @@ +/* +** TS - OSSP Test Suite Library +** Copyright (c) 2001-2002 Ralf S. Engelschall +** Copyright (c) 2001-2002 The OSSP Project +** Copyright (c) 2001-2002 Cable & Wireless Deutschland +** +** This file is part of OSSP TS, a small test suite library which +** can be found at http://www.ossp.org/pkg/ts/. +** +** 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. +** +** ts.c: test suite library +*/ + +#include +#include +#include +#include + +#include "ts.h" + +/* embedded ring data structure library */ +#define RING_ENTRY(elem) \ + struct { elem *next; elem *prev; } +#define RING_HEAD(elem) \ + struct { elem *next; elem *prev; } +#define RING_SENTINEL(hp, elem, link) \ + (elem *)((char *)(hp) - ((size_t)(&((elem *)0)->link))) +#define RING_FIRST(hp) \ + (hp)->next +#define RING_LAST(hp) \ + (hp)->prev +#define RING_NEXT(ep, link) \ + (ep)->link.next +#define RING_PREV(ep, link) \ + (ep)->link.prev +#define RING_INIT(hp, elem, link) \ + do { RING_FIRST((hp)) = RING_SENTINEL((hp), elem, link); \ + RING_LAST((hp)) = RING_SENTINEL((hp), elem, link); } while (0) +#define RING_EMPTY(hp, elem, link) \ + (RING_FIRST((hp)) == RING_SENTINEL((hp), elem, link)) +#define RING_ELEM_INIT(ep, link) \ + do { RING_NEXT((ep), link) = (ep); \ + RING_PREV((ep), link) = (ep); } while (0) +#define RING_SPLICE_BEFORE(lep, ep1, epN, link) \ + do { RING_NEXT((epN), link) = (lep); \ + RING_PREV((ep1), link) = RING_PREV((lep), link); \ + RING_NEXT(RING_PREV((lep), link), link) = (ep1); \ + RING_PREV((lep), link) = (epN); } while (0) +#define RING_SPLICE_TAIL(hp, ep1, epN, elem, link) \ + RING_SPLICE_BEFORE(RING_SENTINEL((hp), elem, link), (ep1), (epN), link) +#define RING_INSERT_TAIL(hp, nep, elem, link) \ + RING_SPLICE_TAIL((hp), (nep), (nep), elem, link) +#define RING_FOREACH(ep, hp, elem, link) \ + for ((ep) = RING_FIRST((hp)); \ + (ep) != RING_SENTINEL((hp), elem, link); \ + (ep) = RING_NEXT((ep), link)) + +/* test suite test log */ +struct tstl_st; +typedef struct tstl_st tstl_t; +struct tstl_st { + RING_ENTRY(tstl_t) next; + char *text; + const char *file; + int line; +}; + +/* test suite test check */ +struct tstc_st; +typedef struct tstc_st tstc_t; +struct tstc_st { + RING_ENTRY(tstc_t) next; + char *title; + int failed; + const char *file; + int line; + RING_HEAD(tstl_t) logs; +}; + +/* test suite test */ +struct ts_test_st { + RING_ENTRY(ts_test_t) next; + char *title; + ts_test_cb_t func; + const char *file; + int line; + RING_HEAD(tstc_t) checks; +}; + +/* test suite */ +struct ts_suite_st { + char *title; + RING_HEAD(ts_test_t) tests; +}; + +/* minimal output-independent vprintf(3) variant which supports %{c,s,d,%} only */ +static int ts_suite_mvxprintf(char *buffer, size_t bufsize, const char *format, va_list ap) +{ + /* sufficient integer buffer: x log_10(2) + safety */ + char ibuf[((sizeof(int)*8)/3)+10]; + char *cp; + char c; + int d; + int n; + int bytes; + + if (format == NULL || ap == NULL) + return -1; + bytes = 0; + while (*format != '\0') { + if (*format == '%') { + c = *(format+1); + if (c == '%') { + /* expand "%%" */ + cp = &c; + n = sizeof(char); + } + else if (c == 'c') { + /* expand "%c" */ + c = (char)va_arg(ap, int); + cp = &c; + n = sizeof(char); + } + else if (c == 's') { + /* expand "%s" */ + if ((cp = (char *)va_arg(ap, char *)) == NULL) + cp = "(null)"; + n = strlen(cp); + } + else if (c == 'd') { + /* expand "%d" */ + d = (int)va_arg(ap, int); +#ifdef HAVE_SNPRINTF + snprintf(ibuf, sizeof(ibuf), "%d", d); /* explicitly secure */ +#else + sprintf(ibuf, "%d", d); /* implicitly secure */ +#endif + cp = ibuf; + n = strlen(cp); + } + else { + /* any other "%X" */ + cp = (char *)format; + n = 2; + } + format += 2; + } + else { + /* plain text */ + cp = (char *)format; + if ((format = strchr(cp, '%')) == NULL) + format = strchr(cp, '\0'); + n = format - cp; + } + /* perform output operation */ + if (buffer != NULL) { + if (n > bufsize) + return -1; + memcpy(buffer, cp, n); + buffer += n; + bufsize -= n; + } + bytes += n; + } + /* nul-terminate output */ + if (buffer != NULL) { + if (bufsize == 0) + return -1; + *buffer = '\0'; + } + return bytes; +} + +/* minimal vasprintf(3) variant which supports %{c,s,d} only */ +static char *ts_suite_mvasprintf(const char *format, va_list ap) +{ + char *buffer; + int n; + va_list ap2; + + if (format == NULL || ap == NULL) + return NULL; + ap2 = ap; + if ((n = ts_suite_mvxprintf(NULL, 0, format, ap)) == -1) + return NULL; + if ((buffer = (char *)malloc(n+1)) == NULL) + return NULL; + ts_suite_mvxprintf(buffer, n+1, format, ap2); + return buffer; +} + +/* minimal asprintf(3) variant which supports %{c,s,d} only */ +static char *ts_suite_masprintf(const char *format, ...) +{ + va_list ap; + char *cp; + + va_start(ap, format); + cp = ts_suite_mvasprintf(format, ap); + va_end(ap); + return cp; +} + +/* create test suite */ +ts_suite_t *ts_suite_new(const char *fmt, ...) +{ + ts_suite_t *ts; + va_list ap; + + if ((ts = (ts_suite_t *)malloc(sizeof(ts_suite_t))) == NULL) + return NULL; + va_start(ap, fmt); + ts->title = ts_suite_mvasprintf(fmt, ap); + RING_INIT(&ts->tests, ts_test_t, next); + va_end(ap); + return ts; +} + +/* add test case to test suite */ +void ts_suite_test(ts_suite_t *ts, ts_test_cb_t func, const char *fmt, ...) +{ + ts_test_t *tst; + va_list ap; + + if (ts == NULL || func == NULL || fmt == NULL) + return; + if ((tst = (ts_test_t *)malloc(sizeof(ts_test_t))) == NULL) + return; + RING_ELEM_INIT(tst, next); + va_start(ap, fmt); + tst->title = ts_suite_mvasprintf(fmt, ap); + va_end(ap); + tst->func = func; + tst->file = NULL; + tst->line = 0; + RING_INIT(&tst->checks, tstc_t, next); + RING_INSERT_TAIL(&ts->tests, tst, ts_test_t, next); + return; +} + +/* run test suite */ +int ts_suite_run(ts_suite_t *ts) +{ + ts_test_t *tst; + tstc_t *tstc; + tstl_t *tstl; + int total_tests, total_tests_suite_failed; + int total_checks, total_checks_failed; + int test_checks, test_checks_failed; + const char *file; + int line; + char *cp; + + if (ts == NULL) + return 0; + + /* init total counters */ + total_tests = 0; + total_tests_suite_failed = 0; + total_checks = 0; + total_checks_failed = 0; + + fprintf(stdout, "\n"); + fprintf(stdout, " Test Suite: %s\n", ts->title); + fprintf(stdout, " __________________________________________________________________\n"); + fprintf(stdout, "\n"); + fflush(stdout); + + /* iterate through all test cases */ + RING_FOREACH(tst, &ts->tests, ts_test_t, next) { + cp = ts_suite_masprintf(" Test: %s ........................................" + "........................................", tst->title); + cp[60] = '\0'; + fprintf(stdout, "%s", cp); + free(cp); + fflush(stdout); + + /* init test case counters */ + test_checks = 0; + test_checks_failed = 0; + + /* run the test case function */ + tst->func(tst); + + /* iterate through all performed checks to determine status */ + RING_FOREACH(tstc, &tst->checks, tstc_t, next) { + test_checks++; + if (tstc->failed) + test_checks_failed++; + } + + if (test_checks_failed > 0) { + /* some checks failed, so do detailed reporting of test case */ + fprintf(stdout, " FAILED\n"); + fprintf(stdout, " Ops, %d/%d checks failed! Detailed report follows:\n", + test_checks_failed, test_checks); + RING_FOREACH(tstc, &tst->checks, tstc_t, next) { + file = (tstc->file != NULL ? tstc->file : tst->file); + line = (tstc->line != 0 ? tstc->line : tst->line); + if (file != NULL) + fprintf(stdout, " Check: %s [%s:%d]\n", tstc->title, file, line); + else + fprintf(stdout, " Check: %s\n", tstc->title); + RING_FOREACH(tstl, &tstc->logs, tstl_t, next) { + file = (tstl->file != NULL ? tstl->file : file); + line = (tstl->line != 0 ? tstl->line : line); + if (file != NULL) + fprintf(stdout, " Log: %s [%s:%d]\n", tstl->text, file, line); + else + fprintf(stdout, " Log: %s\n", tstl->text); + } + } + } + else { + /* test case ran successfully */ + fprintf(stdout, ".... OK\n"); + } + fflush(stdout); + + /* accumulate counters */ + total_checks += test_checks; + total_tests++; + if (test_checks_failed > 0) { + total_checks_failed += test_checks_failed; + total_tests_suite_failed++; + } + } + + /* print test suite summary */ + fprintf(stdout, " __________________________________________________________________\n"); + fprintf(stdout, "\n"); + fprintf(stdout, " Test Summary: %d tests (%d ok, %d failed), %d checks (%d ok, %d failed)\n", + total_tests, (total_tests - total_tests_suite_failed), total_tests_suite_failed, + total_checks, (total_checks - total_checks_failed), total_checks_failed); + if (total_tests_suite_failed > 0) + fprintf(stdout, " Test Suite: FAILED\n"); + else + fprintf(stdout, " Test Suite: OK\n"); + fprintf(stdout, "\n"); + fflush(stdout); + + return total_checks_failed; +} + +/* destroy test suite */ +void ts_suite_free(ts_suite_t *ts) +{ + ts_test_t *tst; + tstc_t *tstc; + tstl_t *tstl; + + if (ts == NULL) + return; + RING_FOREACH(tst, &ts->tests, ts_test_t, next) { + RING_FOREACH(tstc, &tst->checks, tstc_t, next) { + RING_FOREACH(tstl, &tstc->logs, tstl_t, next) { + free(tstl->text); + } + free(tstc->title); + free(tstc); + } + free(tst->title); + free(tst); + } + free(tst->title); + free(ts); + return; +} + +/* annotate test case with file name and line number */ +ts_test_t *ts_test_ctx(ts_test_t *tst, const char *file, int line) +{ + if (tst != NULL && file != NULL) { + tst->file = file; + tst->line = line; + } + return tst; +} + +/* annotate test case with check */ +void ts_test_check(ts_test_t *tst, const char *fmt, ...) +{ + tstc_t *tstc; + va_list ap; + + if (tst == NULL || fmt == NULL) + return; + if ((tstc = (tstc_t *)malloc(sizeof(tstc_t))) == NULL) + return; + va_start(ap, fmt); + RING_ELEM_INIT(tstc, next); + tstc->title = ts_suite_mvasprintf(fmt, ap); + tstc->failed = 0; + tstc->file = tst->file; + tstc->line = tst->line; + RING_INIT(&tstc->logs, tstl_t, next); + RING_INSERT_TAIL(&tst->checks, tstc, tstc_t, next); + va_end(ap); + return; +} + +/* annotate test case with log message and failure */ +void ts_test_fail(ts_test_t *tst, const char *fmt, ...) +{ + tstc_t *tstc; + tstl_t *tstl; + va_list ap; + + if (tst == NULL || fmt == NULL) + return; + if ((tstl = (tstl_t *)malloc(sizeof(tstl_t))) == NULL) + return; + va_start(ap, fmt); + tstl->text = ts_suite_mvasprintf(fmt, ap); + tstl->file = tst->file; + tstl->line = tst->line; + RING_ELEM_INIT(tstl, next); + tstc = RING_LAST(&tst->checks); + RING_INSERT_TAIL(&tstc->logs, tstl, tstl_t, next); + tstc->failed = 1; + va_end(ap); + return; +} + +/* annotate test case with log message only */ +void ts_test_log(ts_test_t *tst, const char *fmt, ...) +{ + tstc_t *tstc; + tstl_t *tstl; + va_list ap; + + if (tst == NULL || fmt == NULL) + return; + if ((tstl = (tstl_t *)malloc(sizeof(tstl_t))) == NULL) + return; + va_start(ap, fmt); + tstl->text = ts_suite_mvasprintf(fmt, ap); + tstl->file = tst->file; + tstl->line = tst->line; + RING_ELEM_INIT(tstl, next); + tstc = RING_LAST(&tst->checks); + RING_INSERT_TAIL(&tstc->logs, tstl, tstl_t, next); + va_end(ap); + return; +} + Index: ossp-pkg/var/ts.h RCS File: /v/ossp/cvs/ossp-pkg/var/ts.h,v co -q -kk -p'1.1' '/v/ossp/cvs/ossp-pkg/var/ts.h,v' | diff -u /dev/null - -L'ossp-pkg/var/ts.h' 2>/dev/null --- ossp-pkg/var/ts.h +++ - 2024-05-03 05:35:39.818678783 +0200 @@ -0,0 +1,64 @@ +/* +** TS - OSSP Test Suite Library +** Copyright (c) 2001-2002 Ralf S. Engelschall +** Copyright (c) 2001-2002 The OSSP Project +** Copyright (c) 2001-2002 Cable & Wireless Deutschland +** +** This file is part of OSSP TS, a small test suite library which +** can be found at http://www.ossp.org/pkg/ts/. +** +** 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. +** +** ts.h: test suite library API +*/ + +#ifndef _TS_H_ +#define _TS_H_ + +/* test suite object type */ +struct ts_suite_st; +typedef struct ts_suite_st ts_suite_t; + +/* test object type */ +struct ts_test_st; +typedef struct ts_test_st ts_test_t; + +/* test callback function type */ +typedef void (*ts_test_cb_t)(ts_test_t *); + +/* test suite operations */ +ts_suite_t *ts_suite_new (const char *fmt, ...); +void ts_suite_test (ts_suite_t *s, ts_test_cb_t func, const char *fmt, ...); +int ts_suite_run (ts_suite_t *s); +void ts_suite_free (ts_suite_t *s); + +/* test operations */ +ts_test_t *ts_test_ctx (ts_test_t *t, const char *file, int line); +void ts_test_check (ts_test_t *t, const char *fmt, ...); +void ts_test_fail (ts_test_t *t, const char *fmt, ...); +void ts_test_log (ts_test_t *t, const char *fmt, ...); + +/* test suite short-cut macros */ +#define TS_TEST(name) \ + static void name(ts_test_t *_t) +#define TS_CTX \ + ts_test_ctx(_t, __FILE__, __LINE__) + +#endif /* _TS_H_ */ + Index: ossp-pkg/var/var_test.c RCS File: /v/ossp/cvs/ossp-pkg/var/var_test.c,v rcsdiff -q -kk '-r1.43' '-r1.44' -u '/v/ossp/cvs/ossp-pkg/var/var_test.c,v' 2>/dev/null --- var_test.c 2002/03/07 12:11:09 1.43 +++ var_test.c 2002/03/07 14:58:41 1.44 @@ -32,50 +32,19 @@ #include #include -#include "var.h" +#include "config.h" +#if defined(HAVE_DMALLOC_H) && defined(WITH_DMALLOC) +#include "dmalloc.h" +#endif -static var_rc_t -var_operation( - var_t *var, void *ctx, - const char *op_ptr, size_t op_len, - const char *arg_ptr, size_t arg_len, - const char *val_ptr, size_t val_len, - char **out_ptr, size_t *out_len, size_t *out_size) -{ - int i; +#include "var.h" +#include "ts.h" - if (val_ptr == NULL) { - *out_ptr = ""; - *out_len = 0; - *out_size = 0; - return VAR_OK; - } - if (op_len == 6 && strncmp(op_ptr, "return", 6) == 0) { - *out_ptr = malloc(arg_len); - *out_len = arg_len; - *out_size = arg_len; - memcpy(*out_ptr, arg_ptr, arg_len); - return VAR_OK; - } - else if (op_len == 5 && strncmp(op_ptr, "upper", 5) == 0) { - *out_ptr = malloc(val_len); - *out_len = val_len; - *out_size = val_len; - for (i = 0; i < val_len; i++) - (*out_ptr)[i] = (char)toupper((int)(val_ptr[i])); - return VAR_OK; - } - else if (op_len == 5 && strncmp(op_ptr, "lower", 5) == 0) { - *out_ptr = malloc(val_len); - *out_len = val_len; - *out_size = val_len; - for (i = 0; i < val_len; i++) - (*out_ptr)[i] = (char)tolower((int)(val_ptr[i])); - return VAR_OK; - } - else - return VAR_ERR_UNDEFINED_OPERATION; -} +/* +** +** ==== VARIABLE LOOKUP CALLBACK ==== +** +*/ struct variable { const char *name; @@ -83,7 +52,26 @@ const char *data; }; -static var_rc_t var_lookup( +static const struct variable lookup_vars[] = { + { "ARRAY", 0, "entry0" }, + { "ARRAY", 1, "entry1" }, + { "ARRAY", 2, "entry2" }, + { "ARRAY", 3, "entry3" }, + { "BAR", 0, "type" }, + { "EMPTY", 0, "" }, + { "FOO", 0, "os" }, + { "HEINZ", 0, "heinz0" }, + { "HEINZ", 1, "heinz1" }, + { "HOME", 0, "/home/regression-tests" }, + { "MULTILINE", 0, "line1\nline2\n" }, + { "NUMBER", 0, "+2" }, + { "NUMEXP", 0, "((16)%5)" }, + { "OSTYPE", 0, "regression-os" }, + { "TERM", 0, "regression-term" }, + { NULL, 0, NULL } +}; + +static var_rc_t lookup_cb( var_t *var, void *context, const char *varname, size_t name_len, int idx, const char **data, size_t *data_len, @@ -125,32 +113,67 @@ return VAR_ERR_UNDEFINED_VARIABLE; } -struct test_case { - const char *input; - const char *expected; -}; +/* +** +** ==== OPERATION CALLBACK ==== +** +*/ -int main(int argc, char **argv) +static var_rc_t +operate_cb( + var_t *var, void *ctx, + const char *op_ptr, size_t op_len, + const char *arg_ptr, size_t arg_len, + const char *val_ptr, size_t val_len, + char **out_ptr, size_t *out_len, size_t *out_size) { - const struct variable vars[] = { - { "ARRAY", 0, "entry0" }, - { "ARRAY", 1, "entry1" }, - { "ARRAY", 2, "entry2" }, - { "ARRAY", 3, "entry3" }, - { "BAR", 0, "type" }, - { "EMPTY", 0, "" }, - { "FOO", 0, "os" }, - { "HEINZ", 0, "heinz0" }, - { "HEINZ", 1, "heinz1" }, - { "HOME", 0, "/home/regression-tests" }, - { "MULTILINE", 0, "line1\nline2\n" }, - { "NUMBER", 0, "+2" }, - { "NUMEXP", 0, "((16)%5)" }, - { "OSTYPE", 0, "regression-os" }, - { "TERM", 0, "regression-term" }, - { NULL, 0, NULL } - }; + int i; + if (val_ptr == NULL) { + *out_ptr = ""; + *out_len = 0; + *out_size = 0; + return VAR_OK; + } + if (op_len == 6 && strncmp(op_ptr, "return", 6) == 0) { + *out_ptr = malloc(arg_len); + *out_len = arg_len; + *out_size = arg_len; + memcpy(*out_ptr, arg_ptr, arg_len); + return VAR_OK; + } + else if (op_len == 5 && strncmp(op_ptr, "upper", 5) == 0) { + *out_ptr = malloc(val_len); + *out_len = val_len; + *out_size = val_len; + for (i = 0; i < val_len; i++) + (*out_ptr)[i] = (char)toupper((int)(val_ptr[i])); + return VAR_OK; + } + else if (op_len == 5 && strncmp(op_ptr, "lower", 5) == 0) { + *out_ptr = malloc(val_len); + *out_len = val_len; + *out_size = val_len; + for (i = 0; i < val_len; i++) + (*out_ptr)[i] = (char)tolower((int)(val_ptr[i])); + return VAR_OK; + } + else + return VAR_ERR_UNDEFINED_OPERATION; +} + +/* +** +** ==== TEST CASES ==== +** +*/ + +TS_TEST(test_expand) +{ + struct test_case { + const char *input; + const char *expected; + }; const struct test_case tests[] = { { "${HOME}${!!}", "/home/regression-tests${!!}" }, { "$HOME", "/home/regression-tests" }, @@ -225,26 +248,11 @@ { "${HOME:%upper}", "/HOME/REGRESSION-TESTS" }, { "${HOME:%upper:%lower}", "/home/regression-tests" }, { "${EMPTY:%return($HOME)}", "/home/regression-tests" }, - { - "[${ARRAY}:${ARRAY[#]}-]", - "entry0:entry0-entry0:entry1-entry0:entry2-entry0:entry3-" - }, - { - "[${HEINZ[#]}:${ARRAY[#]}-]", - "heinz0:entry0-heinz1:entry1-:entry2-:entry3-" - }, - { - "[${HEINZ[#]}:[${ARRAY[#]}] ]", - "heinz0:entry0entry1entry2entry3 heinz1:entry0entry1entry2entry3 " - }, - { - "[${HEINZ[#]}: [${ARRAY[#]}${ARRAY[#+1]:+, }]${HEINZ[#+1]:+; }]", - "heinz0: entry0, entry1, entry2, entry3; heinz1: entry0, entry1, entry2, entry3" - }, - { - "[${ARRAY[#]}:[${ARRAY[#]},]{1,2,} ]{0,2,}", - "entry0:entry1,entry3, entry2:entry1,entry3, " - }, + { "[${ARRAY}:${ARRAY[#]}-]", "entry0:entry0-entry0:entry1-entry0:entry2-entry0:entry3-" }, + { "[${HEINZ[#]}:${ARRAY[#]}-]", "heinz0:entry0-heinz1:entry1-:entry2-:entry3-" }, + { "[${HEINZ[#]}:[${ARRAY[#]}] ]", "heinz0:entry0entry1entry2entry3 heinz1:entry0entry1entry2entry3 " }, + { "[${HEINZ[#]}: [${ARRAY[#]}${ARRAY[#+1]:+, }]${HEINZ[#+1]:+; }]", "heinz0: entry0, entry1, entry2, entry3; heinz1: entry0, entry1, entry2, entry3" }, + { "[${ARRAY[#]}:[${ARRAY[#]},]{1,2,} ]{0,2,}", "entry0:entry1,entry3, entry2:entry1,entry3, " }, }; char *tmp; size_t tmp_len; @@ -254,85 +262,133 @@ var_t *var; char *err; + ts_test_check(TS_CTX, "create environment"); if ((rc = var_create(&var)) != VAR_OK) { var_strerror(NULL, rc, &err); - printf("unable to create variable expansion context: %s (%d)\n", err, rc); - return 1; + ts_test_fail(TS_CTX, "var_create: %s (%d)", err, rc); + return; } - if ((rc = var_config(var, VAR_CONFIG_CB_VALUE, var_lookup, vars)) != VAR_OK) { + if ((rc = var_config(var, VAR_CONFIG_CB_VALUE, lookup_cb, lookup_vars)) != VAR_OK) { var_strerror(NULL, rc, &err); - printf("unable to configure variable expansion context: %s (%d)\n", err, rc); - return 1; + ts_test_fail(TS_CTX, "var_config: %s (%d)", err, rc); + return; } - if ((rc = var_config(var, VAR_CONFIG_CB_OPERATION, var_operation, NULL)) != VAR_OK) { + if ((rc = var_config(var, VAR_CONFIG_CB_OPERATION, operate_cb, NULL)) != VAR_OK) { var_strerror(NULL, rc, &err); - printf("unable to configure variable expansion context: %s (%d)\n", err, rc); - return 1; + ts_test_fail(TS_CTX, "var_config: %s (%d)", err, rc); + return; } - for (i = 0; i < sizeof(tests)/sizeof(struct test_case); ++i) { - printf("Test case #%02d: Original input is '%s'.\n", i, tests[i].input); - rc = var_unescape(var, tests[i].input, strlen(tests[i].input), buffer, sizeof(buffer), 0); + for (i = 0; i < sizeof(tests)/sizeof(struct test_case); i++) { + ts_test_check(TS_CTX, "expansion (#%d): raw input \"%s\"", i, tests[i].input); + rc = var_unescape(var, tests[i].input, strlen(tests[i].input), + buffer, sizeof(buffer), 0); if (rc != VAR_OK) { var_strerror(var, rc, &err); - printf("Test case #%02d: var_unescape() failed: %s (%d)\n", i, err, rc); - return 1; + ts_test_fail(TS_CTX, "var_unescape: %s (%d)", err, rc); + continue; } - printf("Test case #%02d: Unescaped input is '%s'.\n", i, buffer); + ts_test_check(TS_CTX, "expansion (#%d): unescaped input \"%s\"", i, buffer); rc = var_expand(var, buffer, strlen(buffer), &tmp, &tmp_len, 0); if (rc != VAR_OK) { var_strerror(var, rc, &err); - printf("Test case #%02d: var_expand() failed: %s (%d).\n", i, err, rc); - return 1; + ts_test_fail(TS_CTX, "var_expand: %s (%d)", err, rc); + continue; } - printf("Test case #%02d: Expanded output is '%s'.\n", i, tmp); + ts_test_check(TS_CTX, "expansion (#%d): expanded output \"%s\"", i, tmp); if ( tmp_len != strlen(tests[i].expected) - || tmp == NULL || memcmp(tests[i].expected, tmp, tmp_len) != 0) { - printf("Test case #%02d: Expected result '%s' but got '%s'.\n", - i, tests[i].expected, tmp); - return 1; + ts_test_fail(TS_CTX, "expected \"%s\", got \"%s\"", tests[i].expected, tmp); + free(tmp); + continue; } free(tmp); } - fprintf(stderr, "Test case: formatting \"foo\"\n"); - if ((rc = var_format(var, &tmp, 1, "foo")) != VAR_OK) { - var_strerror(var, rc, &err); - fprintf(stderr, "Formatting failed: %s (%d)\n", err, rc); - return 1; + var_destroy(var); + + return; +} + +TS_TEST(test_format) +{ + var_rc_t rc; + var_t *var; + char *err; + char *fmt; + char *exp; + char *got; + + ts_test_check(TS_CTX, "create environment"); + if ((rc = var_create(&var)) != VAR_OK) { + var_strerror(NULL, rc, &err); + ts_test_fail(TS_CTX, "var_create: %s (%d)", err, rc); + return; } - if (strcmp(tmp, "foo") != 0) { - fprintf(stderr, "Formatting failed: expected \"foo\", got \"%s\"\n", tmp); - return 1; + if ((rc = var_config(var, VAR_CONFIG_CB_VALUE, lookup_cb, lookup_vars)) != VAR_OK) { + var_strerror(NULL, rc, &err); + ts_test_fail(TS_CTX, "var_config: %s (%d)", err, rc); + return; } - free(tmp); - fprintf(stderr, "Test case: formatting \"foo<123>\"\n"); - if ((rc = var_format(var, &tmp, 1, "foo<%s><%d><%c>", "bar", 123, 'x')) != VAR_OK) { + /* check trivial formatting */ + fmt = "foo"; + exp = "foo"; + ts_test_check(TS_CTX, "formatting \"%s\"", fmt); + if ((rc = var_format(var, &got, 1, fmt)) != VAR_OK) { var_strerror(var, rc, &err); - fprintf(stderr, "Formatting failed: %s (%d)\n", err, rc); - return 1; + ts_test_fail(TS_CTX, "var_format: %s (%d)", err, rc); + return; } - if (strcmp(tmp, "foo<123>") != 0) { - fprintf(stderr, "Formatting failed: expected \"foo<123>\", got \"%s\"\n", tmp); - return 1; + if (strcmp(got, exp) != 0) { + ts_test_fail(TS_CTX, "var_format: expected \"%s\", got \"%s\"\n", exp, got); + return; + } + free(got); + + /* check real formatting */ + fmt = "foo<%s><%d><%c>quux"; + exp = "foo<123>quux"; + ts_test_check(TS_CTX, "formatting \"%s\"", fmt); + if ((rc = var_format(var, &got, 1, fmt, "bar", 123, 'x')) != VAR_OK) { + var_strerror(var, rc, &err); + ts_test_fail(TS_CTX, "var_format: %s (%d)", err, rc); + return; } - free(tmp); - - fprintf(stderr, "Test case: formatting \"foo${ARRAY[1]}bar\"\n"); - if ((rc = var_format(var, &tmp, 1, "foo${ARRAY[%d]}bar", 1)) != VAR_OK) { + if (strcmp(got, exp) != 0) { + ts_test_fail(TS_CTX, "var_format: expected \"%s\", got \"%s\"\n", exp, got); + return; + } + free(got); + + /* check combined formatting and expansion */ + fmt = "foo<${ARRAY[%d]}>bar"; + exp = "foobar"; + ts_test_check(TS_CTX, "formatting \"%s\"", fmt); + if ((rc = var_format(var, &got, 1, fmt, 1)) != VAR_OK) { var_strerror(var, rc, &err); - fprintf(stderr, "Formatting failed: %s (%d)\n", err, rc); - return 1; + ts_test_fail(TS_CTX, "var_format: %s (%d)", err, rc); + return; } - if (strcmp(tmp, "fooentry1bar") != 0) { - fprintf(stderr, "Formatting failed: expected \"fooentry1bar\", got \"%s\"\n", tmp); - return 1; + if (strcmp(got, exp) != 0) { + ts_test_fail(TS_CTX, "var_format: expected \"%s\", got \"%s\"\n", exp, got); + return; } - free(tmp); + free(got); var_destroy(var); - return 0; +} + +int main(int argc, char *argv[]) +{ + ts_suite_t *ts; + int n; + + ts = ts_suite_new("OSSP var (Variable Expansion)"); + ts_suite_test(ts, test_expand, "expansion"); + ts_suite_test(ts, test_format, "formatting"); + n = ts_suite_run(ts); + ts_suite_free(ts); + return n; }