From: mquinson Date: Fri, 1 Jul 2005 13:52:54 +0000 (+0000) Subject: Include the original version of the libex library. Still to be fitted to our needs X-Git-Tag: v3.3~3844 X-Git-Url: http://info.iut-bm.univ-fcomte.fr/pub/gitweb/simgrid.git/commitdiff_plain/9f0e00fec83a9a1f7053f504e48173afb146331e Include the original version of the libex library. Still to be fitted to our needs git-svn-id: svn+ssh://scm.gforge.inria.fr/svn/simgrid/simgrid/trunk@1512 48e7efb5-ca39-0410-a469-dd3cf9ba447f --- diff --git a/include/xbt/ex.h b/include/xbt/ex.h new file mode 100644 index 0000000000..11a9cd858b --- /dev/null +++ b/include/xbt/ex.h @@ -0,0 +1,251 @@ +/* +** OSSP ex - Exception Handling +** Copyright (c) 2002-2004 Ralf S. Engelschall +** Copyright (c) 2002-2004 The OSSP Project +** Copyright (c) 2002-2004 Cable & Wireless +** +** 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 + +/* 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 /* 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 /* 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 /* 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 index 0000000000..c157964312 --- /dev/null +++ b/src/xbt/ex.c @@ -0,0 +1,58 @@ +/* +** OSSP ex - Exception Handling +** Copyright (c) 2002-2004 Ralf S. Engelschall +** Copyright (c) 2002-2004 The OSSP Project +** Copyright (c) 2002-2004 Cable & Wireless +** +** 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 +#include + +#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 index 0000000000..f0491ef7df --- /dev/null +++ b/testsuite/xbt/ex_test.c @@ -0,0 +1,226 @@ +/* +** OSSP ex - Exception Handling +** Copyright (c) 2002-2004 Ralf S. Engelschall +** Copyright (c) 2002-2004 The OSSP Project +** Copyright (c) 2002-2004 Cable & Wireless +** +** 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 +#include +#include + +#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 index 0000000000..7e0e6933a9 --- /dev/null +++ b/testsuite/xbt/ex_test_ts.c @@ -0,0 +1,467 @@ +/* +** OSSP ts - Test Suite Library +** Copyright (c) 2001-2004 Ralf S. Engelschall +** Copyright (c) 2001-2004 The OSSP Project +** Copyright (c) 2001-2004 Cable & Wireless +** +** 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 +#include +#include +#include + +#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: 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 index 0000000000..8d01123157 --- /dev/null +++ b/testsuite/xbt/ex_test_ts.h @@ -0,0 +1,64 @@ +/* +** OSSP ts - Test Suite Library +** Copyright (c) 2001-2004 Ralf S. Engelschall +** Copyright (c) 2001-2004 The OSSP Project +** Copyright (c) 2001-2004 Cable & Wireless +** +** 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_ */ +