/* * This library is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This library 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 Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library. If not, see . */ #include "camel-test.h" #include #include #include #include #include #include #include /* well i dunno, doesn't seem to be in the headers but hte manpage mentions it */ /* a nonportable checking mutex for glibc, not really needed, just validates * the test harness really */ static GMutex lock; #define CAMEL_TEST_LOCK g_mutex_lock(&lock) #define CAMEL_TEST_UNLOCK g_mutex_unlock(&lock) #define CAMEL_TEST_ID (g_thread_self()) static gint setup; static gint ok; struct _stack { struct _stack *next; gint fatal; gchar *what; }; /* per-thread state */ struct _state { gchar *test; gint nonfatal; struct _stack *state; }; static GHashTable *info_table; gint camel_test_verbose; static void dump_action (GThread *thread, struct _state *s, gpointer d) { struct _stack *node; printf ("\nThread %p:\n", thread); node = s->state; if (node) { printf ("Current action:\n"); while (node) { printf ("\t%s%s\n", node->fatal?"":"[nonfatal]", node->what); node = node->next; } } printf ("\tTest: %s\n", s->test); } static void G_GNUC_NORETURN die (gint sig) { static gint indie = 0; if (!indie) { indie = 1; printf ("\n\nReceived fatal signal %d\n", sig); g_hash_table_foreach (info_table, (GHFunc) dump_action, 0); if (camel_test_verbose > 2) { printf ("Attach debugger to pid %d to debug\n", getpid ()); sleep (1000); } } _exit (1); } static struct _state * current_state (void) { struct _state *info; if (info_table == NULL) info_table = g_hash_table_new (0, 0); info = g_hash_table_lookup (info_table, CAMEL_TEST_ID); if (info == NULL) { info = g_malloc0 (sizeof (*info)); g_hash_table_insert (info_table, CAMEL_TEST_ID, info); } return info; } void camel_test_init (gint argc, gchar **argv) { struct stat st; gchar *path; gint i; setup = 1; path = g_strdup_printf ("/tmp/camel-test"); if (mkdir (path, 0700) == -1 && errno != EEXIST) abort (); if (g_stat (path, &st) == -1) abort (); if (!S_ISDIR (st.st_mode) || access (path, R_OK | W_OK | X_OK) == -1) abort (); camel_init (path, FALSE); g_free (path); info_table = g_hash_table_new (0, 0); signal (SIGSEGV, die); signal (SIGABRT, die); /* default, just say what, how well we did, unless fail, then abort */ camel_test_verbose = 1; for (i = 0; i < argc; i++) { if (argv[i][0] == '-') { switch (argv[i][1]) { case 'v': camel_test_verbose = strlen (argv[i]); break; case 'q': camel_test_verbose = 0; break; } } } } void camel_test_start (const gchar *what) { struct _state *s; CAMEL_TEST_LOCK; s = current_state (); if (!setup) camel_test_init (0, 0); ok = 1; s->test = g_strdup (what); if (camel_test_verbose > 0) { printf ("Test: %s ... ", what); fflush (stdout); } CAMEL_TEST_UNLOCK; } void camel_test_push (const gchar *what, ...) { struct _stack *node; va_list ap; gchar *text; struct _state *s; CAMEL_TEST_LOCK; s = current_state (); va_start (ap, what); text = g_strdup_vprintf (what, ap); va_end (ap); if (camel_test_verbose > 3) printf ("Start step: %s\n", text); node = g_malloc (sizeof (*node)); node->what = text; node->next = s->state; node->fatal = 1; s->state = node; CAMEL_TEST_UNLOCK; } void camel_test_pull (void) { struct _stack *node; struct _state *s; CAMEL_TEST_LOCK; s = current_state (); g_return_if_fail (s->state); if (camel_test_verbose > 3) printf ("Finish step: %s\n", s->state->what); node = s->state; s->state = node->next; if (!node->fatal) s->nonfatal--; g_free (node->what); g_free (node); CAMEL_TEST_UNLOCK; } /* where to set breakpoints */ void camel_test_break (void); void camel_test_break (void) { } void camel_test_fail (const gchar *why, ...) { va_list ap; va_start (ap, why); camel_test_failv (why, ap); va_end (ap); } void camel_test_failv (const gchar *why, va_list ap) { gchar *text; struct _state *s; CAMEL_TEST_LOCK; s = current_state (); text = g_strdup_vprintf (why, ap); if ((s->nonfatal == 0 && camel_test_verbose > 0) || (s->nonfatal && camel_test_verbose > 1)) { printf ("Failed.\n%s\n", text); camel_test_break (); } g_free (text); if ((s->nonfatal == 0 && camel_test_verbose > 0) || (s->nonfatal && camel_test_verbose > 2)) { g_hash_table_foreach (info_table, (GHFunc) dump_action, 0); } if (s->nonfatal == 0) { exit (1); } else { ok = 0; if (camel_test_verbose > 1) { printf ("Known problem (ignored):\n"); dump_action (CAMEL_TEST_ID, s, 0); } } CAMEL_TEST_UNLOCK; } void camel_test_nonfatal (const gchar *what, ...) { struct _stack *node; va_list ap; gchar *text; struct _state *s; CAMEL_TEST_LOCK; s = current_state (); va_start (ap, what); text = g_strdup_vprintf (what, ap); va_end (ap); if (camel_test_verbose > 3) printf ("Start nonfatal: %s\n", text); node = g_malloc (sizeof (*node)); node->what = text; node->next = s->state; node->fatal = 0; s->nonfatal++; s->state = node; CAMEL_TEST_UNLOCK; } void camel_test_fatal (void) { camel_test_pull (); } void camel_test_end (void) { if (camel_test_verbose > 0) { if (ok) printf ("Ok\n"); else printf ("Partial success\n"); } fflush (stdout); } /* compare strings, ignore whitespace though */ gint string_equal (const gchar *a, const gchar *b) { const gchar *ap, *bp; ap = a; bp = b; while (*ap && *bp) { while (*ap == ' ' || *ap == '\n' || *ap == '\t') ap++; while (*bp == ' ' || *bp == '\n' || *bp == '\t') bp++; a = ap; b = bp; while (*ap && *ap != ' ' && *ap != '\n' && *ap != '\t') ap++; while (*bp && *bp != ' ' && *bp != '\n' && *bp != '\t') bp++; if (ap - a != bp - a && ap - a > 0 && memcmp (a, b, ap - a) != 0) { return 0; } } return 1; }