/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * Copyright (C) 2000-2007 Jeffrey Stedfast * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "testsuite.h" static struct _stack *stack = NULL; static pthread_key_t key; static int total_errors = 0; static int total_tests = 0; int verbose = 0; enum { TEST, CHECK }; typedef enum { UNKNOWN, PASSED, WARNING, FAILED } status_t; struct _stack { struct _stack *next; char *message; int type; union { struct { int failures; int warnings; int passed; } test; struct { status_t status; } check; } v; }; static void key_data_destroy (void *user_data) { ExceptionEnv *stack = user_data; ExceptionEnv *parent; while (stack != NULL) { parent = stack->parent; g_free (stack); stack = parent; } } void testsuite_init (int argc, char **argv) { int i, j; for (i = 1; i < argc; i++) { if (argv[i][0] == '-' && argv[i][1] != '-') { for (j = 1; argv[i][j]; j++) { if (argv[i][j] == 'v') verbose++; } } } pthread_key_create (&key, key_data_destroy); } int testsuite_exit (void) { if (total_errors > 0) return total_errors; else if (total_tests > 0) return 0; return EXIT_FAILURE; } void testsuite_printf (FILE *out, int verbosity, const char *fmt, ...) { va_list ap; if (verbose < verbosity) return; va_start (ap, fmt); vfprintf (out, fmt, ap); va_end (ap); } void testsuite_vprintf (FILE *out, int verbosity, const char *fmt, va_list args) { if (verbose < verbosity) return; vfprintf (out, fmt, args); } void testsuite_start (const char *test) { struct _stack *s; s = g_new (struct _stack, 1); s->next = stack; stack = s; s->type = TEST; s->message = g_strdup (test); s->v.test.failures = 0; s->v.test.warnings = 0; s->v.test.passed = 0; } void testsuite_end (void) { struct _stack *s = stack; if (verbose > 0) { fputs ("Testing ", stdout); fputs (s->message, stdout); if (stack->v.test.failures > 0) { fprintf (stdout, ": failed (%d errors, %d warnings)\n", stack->v.test.failures, stack->v.test.warnings); } else if (stack->v.test.warnings > 0) { fprintf (stdout, ": passed (%d warnings)\n", stack->v.test.warnings); } else if (stack->v.test.passed > 0) { fputs (": passed\n", stdout); } else { fputs (": no tests performed\n", stdout); } } stack = stack->next; g_free (s->message); g_free (s); } void testsuite_check (const char *checking, ...) { struct _stack *s; va_list ap; g_assert (stack != NULL && stack->type == TEST); s = g_new (struct _stack, 1); s->next = stack; stack = s; s->type = CHECK; s->v.check.status = UNKNOWN; va_start (ap, checking); s->message = g_strdup_vprintf (checking, ap); va_end (ap); } static void testsuite_check_pop (void) { struct _stack *s = stack; g_assert (stack != NULL && stack->type == CHECK); if (verbose > 1) { fputs ("Checking ", stdout); fputs (s->message, stdout); fputs ("... ", stdout); switch (stack->v.check.status) { case PASSED: fputs ("PASSED\n", stdout); break; case WARNING: fputs ("WARNING\n", stdout); break; case FAILED: fputs ("FAILED\n", stdout); break; default: g_assert_not_reached (); } } stack = stack->next; g_free (s->message); g_free (s); } void testsuite_check_failed (const char *fmt, ...) { va_list ap; g_assert (stack != NULL && stack->type == CHECK); if (verbose > 2) { va_start (ap, fmt); vfprintf (stderr, fmt, ap); va_end (ap); fputc ('\n', stderr); } stack->v.check.status = FAILED; testsuite_check_pop (); stack->v.test.failures++; total_errors++; total_tests++; } void testsuite_check_warn (const char *fmt, ...) { va_list ap; g_assert (stack != NULL && stack->type == CHECK); if (verbose > 2) { va_start (ap, fmt); vfprintf (stderr, fmt, ap); va_end (ap); fputc ('\n', stderr); } stack->v.check.status = WARNING; testsuite_check_pop (); stack->v.test.warnings++; total_tests++; } void testsuite_check_passed (void) { g_assert (stack != NULL && stack->type == CHECK); stack->v.check.status = PASSED; testsuite_check_pop (); stack->v.test.passed++; total_tests++; } Exception * exception_new (const char *fmt, ...) { Exception *ex; va_list ap; ex = g_new (Exception, 1); va_start (ap, fmt); ex->message = g_strdup_vprintf (fmt, ap); va_end (ap); return ex; } void exception_free (Exception *ex) { g_free (ex->message); g_free (ex); } void _try (ExceptionEnv *env) { ExceptionEnv *penv; penv = pthread_getspecific (key); env->parent = penv; env->ex = NULL; pthread_setspecific (key, env); } void throw (Exception *ex) { ExceptionEnv *env; if (!(env = pthread_getspecific (key))) { fprintf (stderr, "Uncaught exception: %s\n", ex->message); abort (); } env->ex = ex; pthread_setspecific (key, env->parent); longjmp (env->env, 1); } #ifdef BUILD int main (int argc, char **argv) { testsuite_init (argc, argv); testsuite_start ("test-suite implementation"); testsuite_check ("exceptions work"); try { if (TRUE) throw (exception_new ("ApplicationException")); testsuite_check_passed (); } catch (ex) { testsuite_check_failed ("Caught %s", ex->message); } finally; #if 0 /* try */ { ExceptionEnv __env = { NULL, }; if (setjmp (__env.env) == 0) { if (TRUE) { __env.ex = exception_new ("ApplicationException"); longjmp (__env.env, 1); } testsuite_check_passed (); } /* catch */ else { Exception *ex = __env.ex; if (ex != NULL) { testsuite_check_failed (ex->message); } /* finally */ } exception_free (__env.ex); }; #endif testsuite_end (); return testsuite_exit (); } #endif