1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * Copyright (C) 2000-2009 Jeffrey Stedfast
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <glib.h>
26 #include <stdlib.h>
27 #include <pthread.h>
28 #include "testsuite.h"
29
30
31 static struct _stack *stack = NULL;
32 static pthread_key_t key;
33 static int total_errors = 0;
34 static int total_tests = 0;
35 int verbose = 0;
36
37 enum {
38 TEST,
39 CHECK
40 };
41
42 typedef enum {
43 UNKNOWN,
44 PASSED,
45 WARNING,
46 FAILED
47 } status_t;
48
49 struct _stack {
50 struct _stack *next;
51 char *message;
52 int type;
53 union {
54 struct {
55 int failures;
56 int warnings;
57 int passed;
58 } test;
59 struct {
60 status_t status;
61 } check;
62 } v;
63 };
64
65 static void
key_data_destroy(void * user_data)66 key_data_destroy (void *user_data)
67 {
68 ExceptionEnv *stack = user_data;
69 ExceptionEnv *parent;
70
71 while (stack != NULL) {
72 parent = stack->parent;
73 g_free (stack);
74 stack = parent;
75 }
76 }
77
78 void
testsuite_init(int argc,char ** argv)79 testsuite_init (int argc, char **argv)
80 {
81 int i, j;
82
83 for (i = 1; i < argc; i++) {
84 if (argv[i][0] == '-' && argv[i][1] != '-') {
85 for (j = 1; argv[i][j]; j++) {
86 if (argv[i][j] == 'v')
87 verbose++;
88 }
89 }
90 }
91
92 pthread_key_create (&key, key_data_destroy);
93 }
94
95 int
testsuite_exit(void)96 testsuite_exit (void)
97 {
98 if (total_errors > 0)
99 return total_errors;
100 else if (total_tests > 0)
101 return 0;
102
103 return EXIT_FAILURE;
104 }
105
106
107 void
testsuite_printf(FILE * out,int verbosity,const char * fmt,...)108 testsuite_printf (FILE *out, int verbosity, const char *fmt, ...)
109 {
110 va_list ap;
111
112 if (verbose < verbosity)
113 return;
114
115 va_start (ap, fmt);
116 vfprintf (out, fmt, ap);
117 va_end (ap);
118 }
119
120 void
testsuite_vprintf(FILE * out,int verbosity,const char * fmt,va_list args)121 testsuite_vprintf (FILE *out, int verbosity, const char *fmt, va_list args)
122 {
123 if (verbose < verbosity)
124 return;
125
126 vfprintf (out, fmt, args);
127 }
128
129 void
testsuite_start(const char * test)130 testsuite_start (const char *test)
131 {
132 struct _stack *s;
133
134 s = g_new (struct _stack, 1);
135 s->next = stack;
136 stack = s;
137
138 s->type = TEST;
139 s->message = g_strdup (test);
140 s->v.test.failures = 0;
141 s->v.test.warnings = 0;
142 s->v.test.passed = 0;
143 }
144
145 void
testsuite_end(void)146 testsuite_end (void)
147 {
148 struct _stack *s = stack;
149
150 if (verbose > 0) {
151 fputs ("Testing ", stdout);
152 fputs (s->message, stdout);
153
154 if (stack->v.test.failures > 0) {
155 fprintf (stdout, ": failed (%d errors, %d warnings)\n",
156 stack->v.test.failures,
157 stack->v.test.warnings);
158 } else if (stack->v.test.warnings > 0) {
159 fprintf (stdout, ": passed (%d warnings)\n",
160 stack->v.test.warnings);
161 } else if (stack->v.test.passed > 0) {
162 fputs (": passed\n", stdout);
163 } else {
164 fputs (": no tests performed\n", stdout);
165 }
166 }
167
168 stack = stack->next;
169 g_free (s->message);
170 g_free (s);
171 }
172
173 void
testsuite_check(const char * checking,...)174 testsuite_check (const char *checking, ...)
175 {
176 struct _stack *s;
177 va_list ap;
178
179 g_assert (stack != NULL && stack->type == TEST);
180
181 s = g_new (struct _stack, 1);
182 s->next = stack;
183 stack = s;
184
185 s->type = CHECK;
186 s->v.check.status = UNKNOWN;
187
188 va_start (ap, checking);
189 s->message = g_strdup_vprintf (checking, ap);
190 va_end (ap);
191 }
192
193 static void
testsuite_check_pop(void)194 testsuite_check_pop (void)
195 {
196 struct _stack *s = stack;
197
198 g_assert (stack != NULL && stack->type == CHECK);
199
200 if (verbose > 1) {
201 fputs ("Checking ", stdout);
202 fputs (s->message, stdout);
203 fputs ("... ", stdout);
204
205 switch (stack->v.check.status) {
206 case PASSED:
207 fputs ("PASSED\n", stdout);
208 break;
209 case WARNING:
210 fputs ("WARNING\n", stdout);
211 break;
212 case FAILED:
213 fputs ("FAILED\n", stdout);
214 break;
215 default:
216 g_assert_not_reached ();
217 }
218 }
219
220 stack = stack->next;
221 g_free (s->message);
222 g_free (s);
223 }
224
225 void
testsuite_check_failed(const char * fmt,...)226 testsuite_check_failed (const char *fmt, ...)
227 {
228 va_list ap;
229
230 g_assert (stack != NULL && stack->type == CHECK);
231
232 if (verbose > 2) {
233 va_start (ap, fmt);
234 vfprintf (stderr, fmt, ap);
235 va_end (ap);
236 fputc ('\n', stderr);
237 }
238
239 stack->v.check.status = FAILED;
240
241 testsuite_check_pop ();
242
243 stack->v.test.failures++;
244 total_errors++;
245 total_tests++;
246 }
247
248 void
testsuite_check_warn(const char * fmt,...)249 testsuite_check_warn (const char *fmt, ...)
250 {
251 va_list ap;
252
253 g_assert (stack != NULL && stack->type == CHECK);
254
255 if (verbose > 2) {
256 va_start (ap, fmt);
257 vfprintf (stderr, fmt, ap);
258 va_end (ap);
259 fputc ('\n', stderr);
260 }
261
262 stack->v.check.status = WARNING;
263
264 testsuite_check_pop ();
265
266 stack->v.test.warnings++;
267 total_tests++;
268 }
269
270 void
testsuite_check_passed(void)271 testsuite_check_passed (void)
272 {
273 g_assert (stack != NULL && stack->type == CHECK);
274
275 stack->v.check.status = PASSED;
276
277 testsuite_check_pop ();
278
279 stack->v.test.passed++;
280 total_tests++;
281 }
282
283 int
testsuite_total_errors(void)284 testsuite_total_errors (void)
285 {
286 return total_errors;
287 }
288
289
290 Exception *
exception_new(const char * fmt,...)291 exception_new (const char *fmt, ...)
292 {
293 Exception *ex;
294 va_list ap;
295
296 ex = g_new (Exception, 1);
297 va_start (ap, fmt);
298 ex->message = g_strdup_vprintf (fmt, ap);
299 va_end (ap);
300
301 return ex;
302 }
303
304 void
exception_free(Exception * ex)305 exception_free (Exception *ex)
306 {
307 g_free (ex->message);
308 g_free (ex);
309 }
310
311 void
_try(ExceptionEnv * env)312 _try (ExceptionEnv *env)
313 {
314 ExceptionEnv *penv;
315
316 penv = pthread_getspecific (key);
317
318 env->parent = penv;
319 env->ex = NULL;
320
321 pthread_setspecific (key, env);
322 }
323
324 void
throw(Exception * ex)325 throw (Exception *ex)
326 {
327 ExceptionEnv *env;
328
329 if (!(env = pthread_getspecific (key))) {
330 fprintf (stderr, "Uncaught exception: %s\n", ex->message);
331 abort ();
332 }
333
334 env->ex = ex;
335 pthread_setspecific (key, env->parent);
336 longjmp (env->env, 1);
337 }
338
339
340 #ifdef BUILD
main(int argc,char ** argv)341 int main (int argc, char **argv)
342 {
343 testsuite_init (argc, argv);
344
345 testsuite_start ("test-suite implementation");
346
347 testsuite_check ("exceptions work");
348 try {
349 if (TRUE)
350 throw (exception_new ("ApplicationException"));
351 testsuite_check_passed ();
352 } catch (ex) {
353 testsuite_check_failed ("Caught %s", ex->message);
354 } finally;
355
356 #if 0
357 /* try */ {
358 ExceptionEnv __env = { NULL, };
359 if (setjmp (__env.env) == 0) {
360 if (TRUE) {
361 __env.ex = exception_new ("ApplicationException");
362 longjmp (__env.env, 1);
363 }
364 testsuite_check_passed ();
365 } /* catch */ else {
366 Exception *ex = __env.ex;
367 if (ex != NULL) {
368 testsuite_check_failed (ex->message);
369 } /* finally */ }
370 exception_free (__env.ex);
371 };
372 #endif
373
374 testsuite_end ();
375
376 return testsuite_exit ();
377 }
378 #endif
379