Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Include the original version of the libex library. Still to be fitted to our needs
authormquinson <mquinson@48e7efb5-ca39-0410-a469-dd3cf9ba447f>
Fri, 1 Jul 2005 13:52:54 +0000 (13:52 +0000)
committermquinson <mquinson@48e7efb5-ca39-0410-a469-dd3cf9ba447f>
Fri, 1 Jul 2005 13:52:54 +0000 (13:52 +0000)
git-svn-id: svn+ssh://scm.gforge.inria.fr/svn/simgrid/simgrid/trunk@1512 48e7efb5-ca39-0410-a469-dd3cf9ba447f

include/xbt/ex.h [new file with mode: 0644]
src/xbt/ex.c [new file with mode: 0644]
testsuite/xbt/ex_test.c [new file with mode: 0644]
testsuite/xbt/ex_test_ts.c [new file with mode: 0644]
testsuite/xbt/ex_test_ts.h [new file with mode: 0644]

diff --git a/include/xbt/ex.h b/include/xbt/ex.h
new file mode 100644 (file)
index 0000000..11a9cd8
--- /dev/null
@@ -0,0 +1,251 @@
+/*
+**  OSSP ex - Exception Handling
+**  Copyright (c) 2002-2004 Ralf S. Engelschall <rse@engelschall.com>
+**  Copyright (c) 2002-2004 The OSSP Project <http://www.ossp.org/>
+**  Copyright (c) 2002-2004 Cable & Wireless <http://www.cw.com/>
+**
+**  This file is part of OSSP ex, an exception handling library
+**  which can be found at http://www.ossp.org/pkg/lib/ex/.
+**
+**  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.
+**
+**  ex.h: exception handling (pre-processor part)
+*/
+
+#ifndef __EX_H__
+#define __EX_H__
+
+/* required ISO-C standard facilities */
+#include <stdio.h>
+
+/* convenience define */
+#ifndef NULL
+#define NULL (void *)0
+#endif
+
+/* determine how the current function name can be fetched */
+#if (   defined(__STDC__) \
+     && defined(__STDC_VERSION__) \
+     && __STDC_VERSION__ >= 199901L)
+#define __EX_FUNC__ __func__      /* ISO-C99 compliant */
+#elif (   defined(__GNUC__) \
+       && defined(__GNUC_MINOR__) \
+       && (   __GNUC__ > 2 \
+           || (__GNUC__ == 2 && __GNUC_MINOR__ >= 8)))
+#define __EX_FUNC__ __FUNCTION__  /* gcc >= 2.8 */
+#else
+#define __EX_FUNC__ "#NA#"        /* not available */
+#endif
+
+/* the machine context */
+#if defined(__EX_MCTX_MCSC__)
+#include <ucontext.h>            /* POSIX.1 ucontext(3) */
+#define __ex_mctx_struct         ucontext_t uc;
+#define __ex_mctx_save(mctx)     (getcontext(&(mctx)->uc) == 0)
+#define __ex_mctx_restored(mctx) /* noop */
+#define __ex_mctx_restore(mctx)  (void)setcontext(&(mctx)->uc)
+
+#elif defined(__EX_MCTX_SSJLJ__)
+#include <setjmp.h>              /* POSIX.1 sigjmp_buf(3) */
+#define __ex_mctx_struct         sigjmp_buf jb;
+#define __ex_mctx_save(mctx)     (sigsetjmp((mctx)->jb, 1) == 0)
+#define __ex_mctx_restored(mctx) /* noop */
+#define __ex_mctx_restore(mctx)  (void)siglongjmp((mctx)->jb, 1)
+
+#elif defined(__EX_MCTX_SJLJ__) || !defined(__EX_MCTX_CUSTOM__)
+#include <setjmp.h>              /* ISO-C jmp_buf(3) */
+#define __ex_mctx_struct         jmp_buf jb;
+#define __ex_mctx_save(mctx)     (setjmp((mctx)->jb) == 0)
+#define __ex_mctx_restored(mctx) /* noop */
+#define __ex_mctx_restore(mctx)  (void)longjmp((mctx)->jb, 1)
+#endif
+
+/* declare the machine context type */
+typedef struct { __ex_mctx_struct } __ex_mctx_t;
+
+/* declare the exception type (public) */
+typedef struct {
+    /* throw value */
+    void       *ex_class;
+    void       *ex_object;
+    void       *ex_value;
+    /* throw point */
+    const char *ex_file;
+    int         ex_line;
+    const char *ex_func;
+} ex_t;
+
+/* declare the context type (private) */
+typedef struct {
+    __ex_mctx_t  *ctx_mctx;     /* permanent machine context of enclosing try/catch */
+    int           ctx_deferred; /* permanent flag whether exception is deferred */
+    int           ctx_deferring;/* permanent counter of exception deferring level */
+    int           ctx_defer;    /* temporary flag for exception deferring macro */
+    int           ctx_shielding;/* permanent counter of exception shielding level */
+    int           ctx_shield;   /* temporary flag for exception shielding macro */
+    int           ctx_caught;   /* temporary flag whether exception was caught */
+    volatile ex_t ctx_ex;       /* temporary exception storage */
+} ex_ctx_t;
+
+/* the static and dynamic initializers for a context structure */
+#define EX_CTX_INITIALIZER \
+    { NULL, 0, 0, 0, 0, 0, 0, { NULL, NULL, NULL, NULL, 0, NULL } }
+#define EX_CTX_INITIALIZE(ctx) \
+    do { \
+        (ctx)->ctx_mctx         = NULL; \
+        (ctx)->ctx_deferred     = 0;    \
+        (ctx)->ctx_deferring    = 0;    \
+        (ctx)->ctx_defer        = 0;    \
+        (ctx)->ctx_shielding    = 0;    \
+        (ctx)->ctx_shield       = 0;    \
+        (ctx)->ctx_caught       = 0;    \
+        (ctx)->ctx_ex.ex_class  = NULL; \
+        (ctx)->ctx_ex.ex_object = NULL; \
+        (ctx)->ctx_ex.ex_value  = NULL; \
+        (ctx)->ctx_ex.ex_file   = NULL; \
+        (ctx)->ctx_ex.ex_line   = 0;    \
+        (ctx)->ctx_ex.ex_func   = NULL; \
+    } while (0)
+
+/* the exception context */
+typedef ex_ctx_t *(*ex_ctx_cb_t)(void);
+extern ex_ctx_cb_t __ex_ctx;
+extern ex_ctx_t *__ex_ctx_default(void);
+
+/* the termination handler */
+typedef void (*ex_term_cb_t)(ex_t *);
+extern ex_term_cb_t __ex_terminate;
+extern void __ex_terminate_default(ex_t *e);
+
+/* the block for trying execution */
+#define ex_try \
+    { \
+        ex_ctx_t *__ex_ctx_ptr = __ex_ctx(); \
+        int __ex_cleanup = 0; \
+        __ex_mctx_t *__ex_mctx_en; \
+        __ex_mctx_t __ex_mctx_me; \
+        __ex_mctx_en = __ex_ctx_ptr->ctx_mctx; \
+        __ex_ctx_ptr->ctx_mctx = &__ex_mctx_me; \
+        if (__ex_mctx_save(&__ex_mctx_me)) { \
+            if (1)
+
+/* the optional(!) block for cleanup */
+#define ex_cleanup \
+            else { \
+            } \
+            __ex_ctx_ptr->ctx_caught = 0; \
+        } \
+        else { \
+            __ex_mctx_restored(&__ex_mctx_me); \
+            __ex_ctx_ptr->ctx_caught = 1; \
+        } \
+        __ex_ctx_ptr->ctx_mctx = __ex_mctx_en; \
+        __ex_cleanup = 1; \
+        if (1) { \
+            if (1)
+
+/* the block for catching an exception */
+#define ex_catch(e) \
+            else { \
+            } \
+            if (!(__ex_cleanup)) \
+                __ex_ctx_ptr->ctx_caught = 0; \
+        } \
+        else { \
+            if (!(__ex_cleanup)) { \
+                __ex_mctx_restored(&__ex_mctx_me); \
+                __ex_ctx_ptr->ctx_caught = 1; \
+            } \
+        } \
+        __ex_ctx_ptr->ctx_mctx = __ex_mctx_en; \
+    } \
+    if (   !(__ex_ctx()->ctx_caught) \
+        || ((e) = __ex_ctx()->ctx_ex, 0)) { \
+    } \
+    else
+
+/* the throwing of a new exception */
+#define ex_throw(c,o,v) \
+    ((   __ex_ctx()->ctx_shielding > 0 \
+      || (__ex_ctx()->ctx_deferring > 0 && __ex_ctx()->ctx_deferred == 1)) ? 0 : \
+     (__ex_ctx()->ctx_ex.ex_class  = (void *)(c), \
+      __ex_ctx()->ctx_ex.ex_object = (void *)(o), \
+      __ex_ctx()->ctx_ex.ex_value  = (void *)(v), \
+      __ex_ctx()->ctx_ex.ex_file   = __FILE__, \
+      __ex_ctx()->ctx_ex.ex_line   = __LINE__, \
+      __ex_ctx()->ctx_ex.ex_func   = __EX_FUNC__, \
+      __ex_ctx()->ctx_deferred     = 1, \
+      (__ex_ctx()->ctx_deferring > 0 ? 0 : \
+       (__ex_ctx()->ctx_mctx == NULL \
+        ? (__ex_terminate((ex_t *)&(__ex_ctx()->ctx_ex)), -1) \
+        : (__ex_mctx_restore(__ex_ctx()->ctx_mctx), 1) ))))
+
+/* the re-throwing of an already caught exception */
+#define ex_rethrow \
+    ((   __ex_ctx()->ctx_shielding > 0 \
+      || __ex_ctx()->ctx_deferring > 0) ? 0 : \
+      (  __ex_ctx()->ctx_mctx == NULL \
+       ? (__ex_terminate((ex_t *)&(__ex_ctx()->ctx_ex)), -1) \
+       : (__ex_mctx_restore(__ex_ctx()->ctx_mctx), 1) ))
+
+/* shield an operation from exception handling */
+#define ex_shield \
+    for (__ex_ctx()->ctx_shielding++, \
+         __ex_ctx()->ctx_shield =  1; \
+         __ex_ctx()->ctx_shield == 1; \
+         __ex_ctx()->ctx_shield =  0, \
+         __ex_ctx()->ctx_shielding--)
+
+/* defer immediate exception handling */
+#define ex_defer \
+    for (((__ex_ctx()->ctx_deferring)++ == 0 ? __ex_ctx()->ctx_deferred = 0 : 0), \
+         __ex_ctx()->ctx_defer =  1;  \
+         __ex_ctx()->ctx_defer == 1;  \
+         __ex_ctx()->ctx_defer =  0,  \
+         ((--(__ex_ctx()->ctx_deferring) == 0 && __ex_ctx()->ctx_deferred == 1) ? ex_rethrow : 0))
+
+/* exception handling tests */
+#define ex_catching \
+    (__ex_ctx()->ctx_mctx != NULL)
+#define ex_shielding \
+    (__ex_ctx()->ctx_shielding > 0)
+#define ex_deferring \
+    (__ex_ctx()->ctx_deferring > 0)
+
+/* optional namespace mapping */
+#if defined(__EX_NS_UCCXX__)
+#define Try      ex_try
+#define Cleanup  ex_cleanup
+#define Catch    ex_catch
+#define Throw    ex_throw
+#define Rethrow  ex_rethrow
+#define Shield   ex_shield
+#define Defer    ex_defer
+#elif defined(__EX_NS_CXX__) || (!defined(__cplusplus) && !defined(__EX_NS_CUSTOM__))
+#define try      ex_try
+#define cleanup  ex_cleanup
+#define catch    ex_catch
+#define throw    ex_throw
+#define rethrow  ex_rethrow
+#define shield   ex_shield
+#define defer    ex_defer
+#endif
+
+#endif /* __EX_H__ */
+
diff --git a/src/xbt/ex.c b/src/xbt/ex.c
new file mode 100644 (file)
index 0000000..c157964
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+**  OSSP ex - Exception Handling
+**  Copyright (c) 2002-2004 Ralf S. Engelschall <rse@engelschall.com>
+**  Copyright (c) 2002-2004 The OSSP Project <http://www.ossp.org/>
+**  Copyright (c) 2002-2004 Cable & Wireless <http://www.cw.com/>
+**
+**  This file is part of OSSP ex, an exception handling library
+**  which can be found at http://www.ossp.org/pkg/lib/ex/.
+**
+**  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.
+**
+**  ex.c: exception handling (compiler part)
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "xbt/ex.h"
+
+/* default __ex_ctx callback function */
+ex_ctx_t *__ex_ctx_default(void)
+{
+    static ex_ctx_t ctx = EX_CTX_INITIALIZER;
+
+    return &ctx;
+}
+
+/* default __ex_terminate callback function */
+void __ex_terminate_default(ex_t *e)
+{
+    fprintf(stderr,
+            "**EX: UNCAUGHT EXCEPTION: "
+            "class=0x%lx object=0x%lx value=0x%lx [%s:%d@%s]\n",
+            (long)((e)->ex_class), (long)((e)->ex_object), (long)((e)->ex_value),
+            (e)->ex_file, (e)->ex_line, (e)->ex_func);
+    abort();
+}
+
+/* the externally visible API */
+ex_ctx_cb_t  __ex_ctx       = &__ex_ctx_default;
+ex_term_cb_t __ex_terminate = &__ex_terminate_default;
+
diff --git a/testsuite/xbt/ex_test.c b/testsuite/xbt/ex_test.c
new file mode 100644 (file)
index 0000000..f0491ef
--- /dev/null
@@ -0,0 +1,226 @@
+/*
+**  OSSP ex - Exception Handling
+**  Copyright (c) 2002-2004 Ralf S. Engelschall <rse@engelschall.com>
+**  Copyright (c) 2002-2004 The OSSP Project <http://www.ossp.org/>
+**  Copyright (c) 2002-2004 Cable & Wireless <http://www.cw.com/>
+**
+**  This file is part of OSSP ex, an exception handling library
+**  which can be found at http://www.ossp.org/pkg/lib/ex/.
+**
+**  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.
+**
+**  ex_test.c: exception handling test suite
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include "ex_test_ts.h"
+#include "xbt/ex.h"
+
+TS_TEST(test_controlflow)
+{
+    ex_t ex;
+    volatile int n;
+
+    ts_test_check(TS_CTX, "basic nested control flow");
+    n = 1;
+    ex_try {
+        if (n != 1)
+            ts_test_fail(TS_CTX, "M1: n=%d (!= 1)", n);
+        n++;
+        ex_try {
+            if (n != 2)
+                ts_test_fail(TS_CTX, "M2: n=%d (!= 2)", n);
+            n++;
+            ex_throw(0, 0, 0);
+        }
+        ex_catch (ex) {
+            if (n != 3)
+                ts_test_fail(TS_CTX, "M3: n=%d (!= 1)", n);
+            n++;
+            ex_rethrow;
+        }
+        ts_test_fail(TS_CTX, "MX: n=%d (expected: not reached)", n);
+    }
+    ex_catch (ex) {
+        if (n != 4)
+            ts_test_fail(TS_CTX, "M4: n=%d (!= 4)", n);
+        n++;
+    }
+    if (n != 5)
+        ts_test_fail(TS_CTX, "M5: n=%d (!= 5)", n);
+}
+
+TS_TEST(test_value)
+{
+    ex_t ex;
+
+    ex_try {
+        ex_throw(1, 2, 3);
+    }
+    ex_catch (ex) {
+        ts_test_check(TS_CTX, "exception value passing");
+        if (ex.ex_class != (void *)1)
+            ts_test_fail(TS_CTX, "ex_class=0x%lx (!= 1)", (long)ex.ex_class);
+        if (ex.ex_object != (void *)2)
+            ts_test_fail(TS_CTX, "ex_object=0x%lx (!= 2)", (long)ex.ex_object);
+        if (ex.ex_value != (void *)3)
+            ts_test_fail(TS_CTX, "ex_value=0x%lx (!= 3)", (long)ex.ex_value);
+    }
+}
+
+TS_TEST(test_variables)
+{
+    ex_t ex;
+    int r1, r2;
+    volatile int v1, v2;
+
+    r1 = r2 = v1 = v2 = 1234;
+    ex_try {
+        r2 = 5678;
+        v2 = 5678;
+        ex_throw(0, 0, 0);
+    }
+    ex_catch (ex) {
+        ts_test_check(TS_CTX, "variable preservation");
+        if (r1 != 1234)
+            ts_test_fail(TS_CTX, "r1=%d (!= 1234)", r1);
+        if (v1 != 1234)
+            ts_test_fail(TS_CTX, "v1=%d (!= 1234)", v1);
+        /* r2 is allowed to be destroyed because not volatile */
+        if (v2 != 5678)
+            ts_test_fail(TS_CTX, "v2=%d (!= 5678)", v2);
+    }
+}
+
+TS_TEST(test_defer)
+{
+    ex_t ex;
+    volatile int i1 = 0;
+    volatile int i2 = 0;
+    volatile int i3 = 0;
+
+    ts_test_check(TS_CTX, "exception deferring");
+    if (ex_deferring)
+        ts_test_fail(TS_CTX, "unexpected deferring scope");
+    ex_try {
+        ex_defer {
+            if (!ex_deferring)
+                ts_test_fail(TS_CTX, "unexpected non-deferring scope");
+            ex_defer {
+                i1 = 1;
+                ex_throw(0, 0, 4711);
+                i2 = 2;
+                ex_throw(0, 0, 0);
+                i3 = 3;
+                ex_throw(0, 0, 0);
+            }
+            ex_throw(0, 0, 0);
+        }
+        ts_test_fail(TS_CTX, "unexpected not occurred deferred throwing");
+    }
+    ex_catch (ex) {
+        if ((long)ex.ex_value != (long)4711)
+            ts_test_fail(TS_CTX, "caught exception with value %d, expected 4711", (long)ex.ex_value);
+    }
+    if (i1 != 1)
+        ts_test_fail(TS_CTX, "v.i1 not set (expected 1, got %d)", i1);
+    if (i2 != 2)
+        ts_test_fail(TS_CTX, "v.i2 not set (expected 2, got %d)", i2);
+    if (i3 != 3)
+        ts_test_fail(TS_CTX, "v.i3 not set (expected 3, got %d)", i3);
+}
+
+TS_TEST(test_shield)
+{
+    ex_t ex;
+
+    ts_test_check(TS_CTX, "exception shielding");
+    if (ex_shielding)
+        ts_test_fail(TS_CTX, "unexpected shielding scope");
+    if (ex_catching)
+        ts_test_fail(TS_CTX, "unexpected catching scope");
+    ex_try {
+        ex_shield {
+            if (!ex_shielding)
+                ts_test_fail(TS_CTX, "unexpected non-shielding scope");
+            ex_throw(0, 0, 0);
+        }
+        if (ex_shielding)
+            ts_test_fail(TS_CTX, "unexpected shielding scope");
+        if (!ex_catching)
+            ts_test_fail(TS_CTX, "unexpected non-catching scope");
+    }
+    ex_catch (ex) {
+        ts_test_fail(TS_CTX, "unexpected exception catched");
+        if (ex_catching)
+            ts_test_fail(TS_CTX, "unexpected catching scope");
+    }
+    if (ex_catching)
+        ts_test_fail(TS_CTX, "unexpected catching scope");
+}
+
+TS_TEST(test_cleanup)
+{
+    ex_t ex;
+    volatile int v1;
+    int c;
+
+    ts_test_check(TS_CTX, "cleanup handling");
+
+    v1 = 1234;
+    c = 0;
+    ex_try {
+        v1 = 5678;
+        ex_throw(1, 2, 3);
+    }
+    ex_cleanup {
+        if (v1 != 5678)
+            ts_test_fail(TS_CTX, "v1 = %d (!= 5678)", v1);
+        c = 1;
+    }
+    ex_catch (ex) {
+        if (v1 != 5678)
+            ts_test_fail(TS_CTX, "v1 = %d (!= 5678)", v1);
+        if (!(ex.ex_class == (void *)1 && ex.ex_object == (void *)2 && ex.ex_value == (void *)3))
+            ts_test_fail(TS_CTX, "unexpected exception contents");
+    }
+    if (!c)
+        ts_test_fail(TS_CTX, "ex_cleanup not executed");
+}
+
+int main(int argc, char *argv[])
+{
+    ts_suite_t *ts;
+    int n;
+
+    ts = ts_suite_new("OSSP ex (Exception Handling)");
+    ts_suite_test(ts, test_controlflow, "basic nested control flow");
+    ts_suite_test(ts, test_value,       "exception value passing");
+    ts_suite_test(ts, test_variables,   "variable value preservation");
+    ts_suite_test(ts, test_defer,       "exception deferring");
+    ts_suite_test(ts, test_shield,      "exception shielding");
+    ts_suite_test(ts, test_cleanup,     "cleanup handling");
+    n = ts_suite_run(ts);
+    ts_suite_free(ts);
+    return n;
+}
+
diff --git a/testsuite/xbt/ex_test_ts.c b/testsuite/xbt/ex_test_ts.c
new file mode 100644 (file)
index 0000000..7e0e693
--- /dev/null
@@ -0,0 +1,467 @@
+/*
+**  OSSP ts - Test Suite Library
+**  Copyright (c) 2001-2004 Ralf S. Engelschall <rse@engelschall.com>
+**  Copyright (c) 2001-2004 The OSSP Project <http://www.ossp.org/>
+**  Copyright (c) 2001-2004 Cable & Wireless <http://www.cw.com/>
+**
+**  This file is part of OSSP ts, a small test suite library which
+**  can be found at http://www.ossp.org/pkg/lib/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 "ex_test_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))
+#define RING_FOREACH_LA(ep, epT, hp, elem, link) \
+    for ((ep)  = RING_FIRST((hp)), (epT) = RING_NEXT((ep), link); \
+         (ep) != RING_SENTINEL((hp), elem, link); \
+         (ep)  = (epT), (epT) = RING_NEXT((epT), 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)
+        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 = (char*)"(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)
+        return NULL;
+    va_copy(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, *tstT;
+    tstc_t *tstc, *tstcT;
+    tstl_t *tstl, *tstlT;
+
+    if (ts == NULL)
+        return;
+    RING_FOREACH_LA(tst, tstT, &ts->tests, ts_test_t, next) {
+        RING_FOREACH_LA(tstc, tstcT, &tst->checks, tstc_t, next) {
+            RING_FOREACH_LA(tstl, tstlT, &tstc->logs, tstl_t, next) {
+                free(tstl->text);
+            }
+            free(tstc->title);
+            free(tstc);
+        }
+        free(tst->title);
+        free(tst);
+    }
+    free(ts->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;
+}
+
diff --git a/testsuite/xbt/ex_test_ts.h b/testsuite/xbt/ex_test_ts.h
new file mode 100644 (file)
index 0000000..8d01123
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+**  OSSP ts - Test Suite Library
+**  Copyright (c) 2001-2004 Ralf S. Engelschall <rse@engelschall.com>
+**  Copyright (c) 2001-2004 The OSSP Project <http://www.ossp.org/>
+**  Copyright (c) 2001-2004 Cable & Wireless <http://www.cw.com/>
+**
+**  This file is part of OSSP ts, a small test suite library which
+**  can be found at http://www.ossp.org/pkg/lib/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_ */
+