OSSP CVS Repository

ossp - Check-in [1968]
Not logged in
[Honeypot]  [Browse]  [Home]  [Login]  [Reports
[Search]  [Ticket]  [Timeline
  [Patchset]  [Tagging/Branching

Check-in Number: 1968
Date: 2002-Mar-07 15:58:41 (local)
2002-Mar-07 14:58:41 (UTC)
User:rse
Branch:
Comment: - use our standard ts.[ch] test suite library - add support for Dmalloc facility
Tickets:
Inspections:
Files:
ossp-pkg/var/Makefile.in      1.6 -> 1.7     1 inserted, 1 deleted
ossp-pkg/var/acconfig.h      1.1 -> 1.2     3 inserted, 0 deleted
ossp-pkg/var/configure.ac      1.8 -> 1.9     1 inserted, 0 deleted
ossp-pkg/var/ts.c      added-> 1.1
ossp-pkg/var/ts.h      added-> 1.1
ossp-pkg/var/var_test.c      1.43 -> 1.44     190 inserted, 134 deleted

ossp-pkg/var/Makefile.in 1.6 -> 1.7

--- 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


ossp-pkg/var/acconfig.h 1.1 -> 1.2

--- 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
+


ossp-pkg/var/configure.ac 1.8 -> 1.9

--- 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)


ossp-pkg/var/ts.c -> 1.1

*** /dev/null    Fri Apr 19 14:37:11 2024
--- -    Fri Apr 19 14:38:55 2024
***************
*** 0 ****
--- 1,463 ----
+ /*
+ **  TS - OSSP Test Suite Library
+ **  Copyright (c) 2001-2002 Ralf S. Engelschall <rse@engelschall.com>
+ **  Copyright (c) 2001-2002 The OSSP Project <http://www.ossp.org/>
+ **  Copyright (c) 2001-2002 Cable & Wireless Deutschland <http://www.cw.com/de/>
+ **
+ **  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 <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+ #include <stdarg.h>
+ 
+ #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: <available-bits> 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;
+ }
+ 


ossp-pkg/var/ts.h -> 1.1

*** /dev/null    Fri Apr 19 14:37:11 2024
--- -    Fri Apr 19 14:38:55 2024
***************
*** 0 ****
--- 1,64 ----
+ /*
+ **  TS - OSSP Test Suite Library
+ **  Copyright (c) 2001-2002 Ralf S. Engelschall <rse@engelschall.com>
+ **  Copyright (c) 2001-2002 The OSSP Project <http://www.ossp.org/>
+ **  Copyright (c) 2001-2002 Cable & Wireless Deutschland <http://www.cw.com/de/>
+ **
+ **  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_ */
+ 


ossp-pkg/var/var_test.c 1.43 -> 1.44

--- 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 <errno.h>
 #include <ctype.h>
 
-#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<bar><123><x>\"\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<bar><123><x>") != 0) {
-        fprintf(stderr, "Formatting failed: expected \"foo<bar><123><x>\", 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<bar><123><x>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 = "foo<entry1>bar";
+    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;
 }
 

CVSTrac 2.0.1