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