c7df2a2a25dbd65d9ada5f69316ed3b57c7f15f1
[platform/upstream/glib.git] / glib / gtestframework.c
1 /* GLib testing framework examples
2  * Copyright (C) 2007 Tim Janik
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19 #include "config.h"
20 #include "gtestframework.h"
21 #include <sys/types.h>
22 #include <sys/wait.h>
23 #include <fcntl.h>
24 #include <string.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <errno.h>
28 #include <signal.h>
29 #ifdef HAVE_SYS_SELECT_H
30 #include <sys/select.h>
31 #endif /* HAVE_SYS_SELECT_H */
32
33 /* --- structures --- */
34 struct GTestCase
35 {
36   gchar  *name;
37   guint   fixture_size;
38   void (*fixture_setup) (void*);
39   void (*fixture_test) (void*);
40   void (*fixture_teardown) (void*);
41 };
42 struct GTestSuite
43 {
44   gchar  *name;
45   GSList *suites;
46   GSList *cases;
47 };
48
49 /* --- prototypes --- */
50 static void                     test_run_seed           (const gchar *rseed);
51 static void                     test_trap_clear         (void);
52
53 /* --- variables --- */
54 static int         test_stdmsg = 1;
55 static gboolean    test_mode_quick = TRUE;
56 static gboolean    test_mode_perf = FALSE;
57 static gboolean    test_mode_fatal = TRUE;
58 static gboolean    g_test_initialized = FALSE;
59 static gboolean    g_test_run_once = TRUE;
60 static gboolean    test_run_quiet = FALSE;
61 static gboolean    test_run_list = FALSE;
62 static gchar      *test_run_output = NULL;
63 static gchar      *test_run_seedstr = NULL;
64 static GRand      *test_run_rand = NULL;
65 static gchar      *test_run_name = "";
66 static GTimer     *test_run_timer = NULL;
67 static GTimer     *test_user_timer = NULL;
68 static double      test_user_stamp = 0;
69 static GSList     *test_paths = NULL;
70 static GTestSuite *test_suite_root = NULL;
71 static GSList     *test_run_free_queue = NULL;
72 static int         test_trap_last_status = 0;
73 static int         test_trap_last_pid = 0;
74 static char       *test_trap_last_stdout = NULL;
75 static char       *test_trap_last_stderr = NULL;
76
77 /* --- functions --- */
78 static void
79 parse_args (gint    *argc_p,
80             gchar ***argv_p)
81 {
82   guint argc = *argc_p;
83   gchar **argv = *argv_p;
84   guint i, e;
85   /* parse known args */
86   for (i = 1; i < argc; i++)
87     {
88       if (strcmp (argv[i], "--g-fatal-warnings") == 0)
89         {
90           GLogLevelFlags fatal_mask = (GLogLevelFlags) g_log_set_always_fatal ((GLogLevelFlags) G_LOG_FATAL_MASK);
91           fatal_mask = (GLogLevelFlags) (fatal_mask | G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL);
92           g_log_set_always_fatal (fatal_mask);
93           argv[i] = NULL;
94         }
95       else if (strcmp (argv[i], "--keep-going") == 0 ||
96                strcmp (argv[i], "-k") == 0)
97         {
98           test_mode_fatal = FALSE;
99           argv[i] = NULL;
100         }
101       else if (strcmp ("-p", argv[i]) == 0 || strncmp ("-p=", argv[i], 3) == 0)
102         {
103           gchar *equal = argv[i] + 2;
104           if (*equal == '=')
105             test_paths = g_slist_prepend (test_paths, equal + 1);
106           else if (i + 1 < argc)
107             {
108               argv[i++] = NULL;
109               test_paths = g_slist_prepend (test_paths, argv[i]);
110             }
111           argv[i] = NULL;
112         }
113       else if (strcmp ("-o", argv[i]) == 0 || strncmp ("-o=", argv[i], 3) == 0)
114         {
115           gchar *equal = argv[i] + 2;
116           if (*equal == '=')
117             test_run_output = equal + 1;
118           else if (i + 1 < argc)
119             {
120               argv[i++] = NULL;
121               test_run_output = argv[i];
122             }
123           argv[i] = NULL;
124         }
125       else if (strcmp ("-m", argv[i]) == 0 || strncmp ("-m=", argv[i], 3) == 0)
126         {
127           gchar *equal = argv[i] + 2;
128           const gchar *mode = "";
129           if (*equal == '=')
130             mode = equal + 1;
131           else if (i + 1 < argc)
132             {
133               argv[i++] = NULL;
134               mode = argv[i];
135             }
136           if (strcmp (mode, "perf") == 0)
137             test_mode_perf = TRUE;
138           else if (strcmp (mode, "slow") == 0)
139             test_mode_quick = FALSE;
140           else if (strcmp (mode, "quick") == 0)
141             {
142               test_mode_quick = TRUE;
143               test_mode_perf = FALSE;
144             }
145           else
146             g_error ("unknown test mode: -m %s", mode);
147           argv[i] = NULL;
148         }
149       else if (strcmp ("-q", argv[i]) == 0 || strcmp ("--quiet", argv[i]) == 0)
150         {
151           test_run_quiet = TRUE;
152           argv[i] = NULL;
153         }
154       else if (strcmp ("-l", argv[i]) == 0)
155         {
156           test_run_list = TRUE;
157           argv[i] = NULL;
158         }
159       else if (strcmp ("--seed", argv[i]) == 0 || strncmp ("--seed=", argv[i], 7) == 0)
160         {
161           gchar *equal = argv[i] + 6;
162           if (*equal == '=')
163             test_run_seedstr = equal + 1;
164           else if (i + 1 < argc)
165             {
166               argv[i++] = NULL;
167               test_run_seedstr = argv[i];
168             }
169           argv[i] = NULL;
170         }
171     }
172   /* collapse argv */
173   e = 1;
174   for (i = 1; i < argc; i++)
175     if (argv[i])
176       {
177         argv[e++] = argv[i];
178         if (i >= e)
179           argv[i] = NULL;
180       }
181   *argc_p = e;
182 }
183
184 void
185 g_test_init (int            *argc,
186              char         ***argv,
187              ...)
188 {
189   static char seedstr[4 + 4 * 8 + 1];
190   va_list args;
191   gpointer vararg1;
192   g_return_if_fail (argc != NULL);
193   g_return_if_fail (argv != NULL);
194   g_return_if_fail (g_test_initialized == FALSE);
195   g_test_initialized = TRUE;
196
197   va_start (args, argv);
198   vararg1 = va_arg (args, gpointer); /* reserved for future extensions */
199   va_end (args);
200   g_return_if_fail (vararg1 == NULL);
201
202   /* setup random seed string */
203   g_snprintf (seedstr, sizeof (seedstr), "R02S%08x%08x%08x%08x", g_random_int(), g_random_int(), g_random_int(), g_random_int());
204   test_run_seedstr = seedstr;
205
206   /* parse args, sets up mode, changes seed, etc. */
207   parse_args (argc, argv);
208
209   /* verify GRand reliability, needed for reliable seeds */
210   if (1)
211     {
212       GRand *rg = g_rand_new_with_seed (0xc8c49fb6);
213       guint32 t1 = g_rand_int (rg), t2 = g_rand_int (rg), t3 = g_rand_int (rg), t4 = g_rand_int (rg);
214       // g_print ("GRand-current: 0x%x 0x%x 0x%x 0x%x\n", t1, t2, t3, t4);
215       if (t1 != 0xfab39f9b || t2 != 0xb948fb0e || t3 != 0x3d31be26 || t4 != 0x43a19d66)
216         g_warning ("random numbers are not GRand-2.2 compatible, seeds may be broken (check $G_RANDOM_VERSION)");
217       g_rand_free (rg);
218     }
219
220   /* check rand seed */
221   test_run_seed (test_run_seedstr);
222   if (test_run_seedstr == seedstr)
223     g_printerr ("NOTE: random-seed: %s\n", test_run_seedstr);
224
225   /* misc setups */
226   test_run_timer = g_timer_new();
227 }
228
229 static void
230 test_run_seed (const gchar *rseed)
231 {
232   guint seed_failed = 0;
233   if (test_run_rand)
234     g_rand_free (test_run_rand);
235   test_run_rand = NULL;
236   while (strchr (" \t\v\r\n\f", *rseed))
237     rseed++;
238   if (strncmp (rseed, "R02S", 4) == 0)  // seed for random generator 02 (GRand-2.2)
239     {
240       const char *s = rseed + 4;
241       if (strlen (s) >= 32)             // require 4 * 8 chars
242         {
243           guint32 seedarray[4];
244           gchar *p, hexbuf[9] = { 0, };
245           memcpy (hexbuf, s + 0, 8);
246           seedarray[0] = g_ascii_strtoull (hexbuf, &p, 16);
247           seed_failed += p != NULL && *p != 0;
248           memcpy (hexbuf, s + 8, 8);
249           seedarray[1] = g_ascii_strtoull (hexbuf, &p, 16);
250           seed_failed += p != NULL && *p != 0;
251           memcpy (hexbuf, s + 16, 8);
252           seedarray[2] = g_ascii_strtoull (hexbuf, &p, 16);
253           seed_failed += p != NULL && *p != 0;
254           memcpy (hexbuf, s + 24, 8);
255           seedarray[3] = g_ascii_strtoull (hexbuf, &p, 16);
256           seed_failed += p != NULL && *p != 0;
257           if (!seed_failed)
258             {
259               test_run_rand = g_rand_new_with_seed_array (seedarray, 4);
260               return;
261             }
262         }
263     }
264   g_error ("Unknown or invalid random seed: %s", rseed);
265 }
266
267 gint32
268 g_test_rand_int (void)
269 {
270   return g_rand_int (test_run_rand);
271 }
272
273 gint32
274 g_test_rand_int_range (gint32          begin,
275                        gint32          end)
276 {
277   return g_rand_int_range (test_run_rand, begin, end);
278 }
279
280 double
281 g_test_rand_double (void)
282 {
283   return g_rand_double (test_run_rand);
284 }
285
286 double
287 g_test_rand_double_range (double          range_start,
288                           double          range_end)
289 {
290   return g_rand_double_range (test_run_rand, range_start, range_end);
291 }
292
293 void
294 g_test_timer_start (void)
295 {
296   if (!test_user_timer)
297     test_user_timer = g_timer_new();
298   test_user_stamp = 0;
299   g_timer_start (test_user_timer);
300 }
301
302 double
303 g_test_timer_elapsed (void)
304 {
305   test_user_stamp = test_user_timer ? g_timer_elapsed (test_user_timer, NULL) : 0;
306   return test_user_stamp;
307 }
308
309 double
310 g_test_timer_last (void)
311 {
312   return test_user_stamp;
313 }
314
315 GTestSuite*
316 g_test_get_root (void)
317 {
318   if (!test_suite_root)
319     {
320       test_suite_root = g_test_create_suite ("root");
321       g_free (test_suite_root->name);
322       test_suite_root->name = g_strdup ("");
323     }
324   return test_suite_root;
325 }
326
327 int
328 g_test_run (void)
329 {
330   return g_test_run_suite (g_test_get_root());
331 }
332
333 GTestCase*
334 g_test_create_case (const char     *test_name,
335                     gsize           data_size,
336                     void          (*data_setup) (void),
337                     void          (*data_test) (void),
338                     void          (*data_teardown) (void))
339 {
340   g_return_val_if_fail (test_name != NULL, NULL);
341   g_return_val_if_fail (strchr (test_name, '/') == NULL, NULL);
342   g_return_val_if_fail (test_name[0] != 0, NULL);
343   g_return_val_if_fail (data_test != NULL, NULL);
344   GTestCase *tc = g_slice_new0 (GTestCase);
345   tc->name = g_strdup (test_name);
346   tc->fixture_size = data_size;
347   tc->fixture_setup = (void*) data_setup;
348   tc->fixture_test = (void*) data_test;
349   tc->fixture_teardown = (void*) data_teardown;
350   return tc;
351 }
352
353 void
354 g_test_add_vtable (const char     *testpath,
355                    gsize           data_size,
356                    void          (*data_setup)    (void),
357                    void          (*fixture_test_func) (void),
358                    void          (*data_teardown) (void))
359 {
360   gchar **segments;
361   guint ui;
362   GTestSuite *suite;
363
364   g_return_if_fail (testpath != NULL);
365   g_return_if_fail (testpath[0] == '/');
366   g_return_if_fail (fixture_test_func != NULL);
367
368   suite = g_test_get_root();
369   segments = g_strsplit (testpath, "/", -1);
370   for (ui = 0; segments[ui] != NULL; ui++)
371     {
372       const char *seg = segments[ui];
373       gboolean islast = segments[ui + 1] == NULL;
374       if (islast && !seg[0])
375         g_error ("invalid test case path: %s", testpath);
376       else if (!seg[0])
377         continue;       // initial or duplicate slash
378       else if (!islast)
379         {
380           GTestSuite *csuite = g_test_create_suite (seg);
381           g_test_suite_add_suite (suite, csuite);
382           suite = csuite;
383         }
384       else /* islast */
385         {
386           GTestCase *tc = g_test_create_case (seg, data_size, data_setup, fixture_test_func, data_teardown);
387           g_test_suite_add (suite, tc);
388         }
389     }
390   g_strfreev (segments);
391 }
392
393 void
394 g_test_add_func (const char     *testpath,
395                  void          (*test_func) (void))
396 {
397   g_return_if_fail (testpath != NULL);
398   g_return_if_fail (testpath[0] == '/');
399   g_return_if_fail (test_func != NULL);
400   g_test_add_vtable (testpath, 0, NULL, test_func, NULL);
401 }
402
403 GTestSuite*
404 g_test_create_suite (const char *suite_name)
405 {
406   g_return_val_if_fail (suite_name != NULL, NULL);
407   g_return_val_if_fail (strchr (suite_name, '/') == NULL, NULL);
408   g_return_val_if_fail (suite_name[0] != 0, NULL);
409   GTestSuite *ts = g_slice_new0 (GTestSuite);
410   ts->name = g_strdup (suite_name);
411   return ts;
412 }
413
414 void
415 g_test_suite_add (GTestSuite     *suite,
416                   GTestCase      *test_case)
417 {
418   g_return_if_fail (suite != NULL);
419   g_return_if_fail (test_case != NULL);
420   suite->cases = g_slist_prepend (suite->cases, test_case);
421 }
422
423 void
424 g_test_suite_add_suite (GTestSuite     *suite,
425                         GTestSuite     *nestedsuite)
426 {
427   g_return_if_fail (suite != NULL);
428   g_return_if_fail (nestedsuite != NULL);
429   suite->suites = g_slist_prepend (suite->suites, nestedsuite);
430 }
431
432 void
433 g_test_queue_free (gpointer gfree_pointer)
434 {
435   if (gfree_pointer)
436     test_run_free_queue = g_slist_prepend (test_run_free_queue, gfree_pointer);
437 }
438
439 static int
440 test_case_run (GTestCase *tc)
441 {
442   gchar *old_name;
443   g_timer_start (test_run_timer);
444   old_name = test_run_name;
445   test_run_name = g_strconcat (old_name, "/", tc->name, NULL);
446   if (test_run_list)
447     g_print ("%s\n", test_run_name);
448   else
449     {
450       if (!test_run_quiet)
451         g_print ("%s: ", test_run_name);
452       void *fixture = g_malloc0 (tc->fixture_size);
453       test_run_seed (test_run_seedstr);
454       if (tc->fixture_setup)
455         tc->fixture_setup (fixture);
456       tc->fixture_test (fixture);
457       test_trap_clear();
458       while (test_run_free_queue)
459         {
460           gpointer freeme = test_run_free_queue->data;
461           test_run_free_queue = g_slist_delete_link (test_run_free_queue, test_run_free_queue);
462           g_free (freeme);
463         }
464       if (tc->fixture_teardown)
465         tc->fixture_teardown (fixture);
466       g_free (fixture);
467       if (!test_run_quiet)
468         g_print ("OK\n");
469     }
470   g_free (test_run_name);
471   test_run_name = old_name;
472   g_timer_stop (test_run_timer);
473   /* FIXME: need reporting here */
474   return 0;
475 }
476
477 static int
478 g_test_run_suite_internal (GTestSuite *suite,
479                            const char *path)
480 {
481   guint n_bad = 0, n_good = 0, bad_suite = 0, l;
482   gchar *rest, *old_name = test_run_name;
483   GSList *slist, *reversed;
484   g_return_val_if_fail (suite != NULL, -1);
485   while (path[0] == '/')
486     path++;
487   l = strlen (path);
488   rest = strchr (path, '/');
489   l = rest ? MIN (l, rest - path) : l;
490   test_run_name = suite->name[0] == 0 ? g_strdup (test_run_name) : g_strconcat (old_name, "/", suite->name, NULL);
491   reversed = g_slist_reverse (g_slist_copy (suite->cases));
492   for (slist = reversed; slist; slist = slist->next)
493     {
494       GTestCase *tc = slist->data;
495       guint n = l ? strlen (tc->name) : 0;
496       if (l == n && strncmp (path, tc->name, n) == 0)
497         {
498           n_good++;
499           n_bad += test_case_run (tc) != 0;
500         }
501     }
502   g_slist_free (reversed);
503   reversed = g_slist_reverse (g_slist_copy (suite->suites));
504   for (slist = reversed; slist; slist = slist->next)
505     {
506       GTestSuite *ts = slist->data;
507       guint n = l ? strlen (ts->name) : 0;
508       if (l == n && strncmp (path, ts->name, n) == 0)
509         bad_suite += g_test_run_suite_internal (ts, rest ? rest : "") != 0;
510     }
511   g_slist_free (reversed);
512   g_free (test_run_name);
513   test_run_name = old_name;
514   return n_bad || bad_suite;
515 }
516
517 int
518 g_test_run_suite (GTestSuite *suite)
519 {
520   guint n_bad = 0;
521   g_return_val_if_fail (g_test_initialized == TRUE, -1);
522   g_return_val_if_fail (g_test_run_once == TRUE, -1);
523   g_test_run_once = FALSE;
524   if (!test_paths)
525     test_paths = g_slist_prepend (test_paths, "");
526   while (test_paths)
527     {
528       const char *rest, *path = test_paths->data;
529       guint l, n;
530       test_paths = g_slist_delete_link (test_paths, test_paths);
531       while (path[0] == '/')
532         path++;
533       rest = strchr (path, '/');
534       l = strlen (path);
535       l = rest ? MIN (l, rest - path) : l;
536       n = l ? strlen (suite->name) : 0;
537       if (l == n && strncmp (path, suite->name, n) == 0)
538         n_bad += 0 != g_test_run_suite_internal (suite, rest ? rest : "");
539     }
540   return n_bad;
541 }
542
543 void
544 g_assertion_message (const char     *domain,
545                      const char     *file,
546                      int             line,
547                      const char     *func,
548                      const char     *message)
549 {
550   char lstr[32];
551   g_snprintf (lstr, 32, "%d", line);
552   char *s = g_strconcat (domain ? domain : "", domain && domain[0] ? ":" : "",
553                          file, ":", lstr, ":",
554                          func, func[0] ? ":" : "",
555                          " ", message, NULL);
556   g_printerr ("**\n** %s\n", s);
557   g_free (s);
558   abort();
559 }
560
561 void
562 g_assertion_message_expr (const char     *domain,
563                           const char     *file,
564                           int             line,
565                           const char     *func,
566                           const char     *expr)
567 {
568   char *s = g_strconcat ("assertion failed: (", expr, ")", NULL);
569   g_assertion_message (domain, file, line, func, s);
570   g_free (s);
571 }
572
573 void
574 g_assertion_message_cmpnum (const char     *domain,
575                             const char     *file,
576                             int             line,
577                             const char     *func,
578                             const char     *expr,
579                             long double     arg1,
580                             const char     *cmp,
581                             long double     arg2,
582                             char            numtype)
583 {
584   char *s = NULL;
585   switch (numtype)
586     {
587     case 'i':   s = g_strdup_printf ("assertion failed (%s): (%.0Lf %s %.0Lf)", expr, arg1, cmp, arg2); break;
588     case 'x':   s = g_strdup_printf ("assertion failed (%s): (0x%08Lx %s 0x%08Lx)", expr, (guint64) arg1, cmp, (guint64) arg2); break;
589     case 'f':   s = g_strdup_printf ("assertion failed (%s): (%.9Lg %s %.9Lg)", expr, arg1, cmp, arg2); break;
590       /* ideally use: floats=%.7g double=%.17g */
591     }
592   g_assertion_message (domain, file, line, func, s);
593   g_free (s);
594 }
595
596 void
597 g_assertion_message_cmpstr (const char     *domain,
598                             const char     *file,
599                             int             line,
600                             const char     *func,
601                             const char     *expr,
602                             const char     *arg1,
603                             const char     *cmp,
604                             const char     *arg2)
605 {
606   char *a1, *a2, *s, *t1 = NULL, *t2 = NULL;
607   a1 = arg1 ? g_strconcat ("\"", t1 = g_strescape (arg1, NULL), "\"", NULL) : g_strdup ("NULL");
608   a2 = arg2 ? g_strconcat ("\"", t2 = g_strescape (arg2, NULL), "\"", NULL) : g_strdup ("NULL");
609   g_free (t1);
610   g_free (t2);
611   s = g_strdup_printf ("assertion failed (%s): (%s %s %s)", expr, a1, cmp, a2);
612   g_free (a1);
613   g_free (a2);
614   g_assertion_message (domain, file, line, func, s);
615   g_free (s);
616 }
617
618 int
619 g_strcmp0 (const char     *str1,
620            const char     *str2)
621 {
622   if (!str1)
623     return -(str1 != str2);
624   if (!str2)
625     return str1 != str2;
626   return strcmp (str1, str2);
627 }
628
629 static int /* 0 on success */
630 kill_child (int  pid,
631             int *status,
632             int  patience)
633 {
634   int wr;
635   if (patience >= 3)    /* try graceful reap */
636     {
637       if (waitpid (pid, status, WNOHANG) > 0)
638         return 0;
639     }
640   if (patience >= 2)    /* try SIGHUP */
641     {
642       kill (pid, SIGHUP);
643       if (waitpid (pid, status, WNOHANG) > 0)
644         return 0;
645       g_usleep (20 * 1000); /* give it some scheduling/shutdown time */
646       if (waitpid (pid, status, WNOHANG) > 0)
647         return 0;
648       g_usleep (50 * 1000); /* give it some scheduling/shutdown time */
649       if (waitpid (pid, status, WNOHANG) > 0)
650         return 0;
651       g_usleep (100 * 1000); /* give it some scheduling/shutdown time */
652       if (waitpid (pid, status, WNOHANG) > 0)
653         return 0;
654     }
655   if (patience >= 1)    /* try SIGTERM */
656     {
657       kill (pid, SIGTERM);
658       if (waitpid (pid, status, WNOHANG) > 0)
659         return 0;
660       g_usleep (200 * 1000); /* give it some scheduling/shutdown time */
661       if (waitpid (pid, status, WNOHANG) > 0)
662         return 0;
663       g_usleep (400 * 1000); /* give it some scheduling/shutdown time */
664       if (waitpid (pid, status, WNOHANG) > 0)
665         return 0;
666     }
667   /* finish it off */
668   kill (pid, SIGKILL);
669   do
670     wr = waitpid (pid, status, 0);
671   while (wr < 0 && errno == EINTR);
672   return wr;
673 }
674
675 static inline int
676 g_string_must_read (GString *gstring,
677                     int      fd)
678 {
679 #define STRING_BUFFER_SIZE     4096
680   char buf[STRING_BUFFER_SIZE];
681   gssize bytes;
682  again:
683   bytes = read (fd, buf, sizeof (buf));
684   if (bytes == 0)
685     return 0; /* EOF, calling this function assumes data is available */
686   else if (bytes > 0)
687     {
688       g_string_append_len (gstring, buf, bytes);
689       return 1;
690     }
691   else if (bytes < 0 && errno == EINTR)
692     goto again;
693   else /* bytes < 0 */
694     {
695       g_warning ("failed to read() from child process (%d): %s", test_trap_last_pid, g_strerror (errno));
696       return 1; /* ignore error after warning */
697     }
698 }
699
700 static inline void
701 g_string_write_out (GString *gstring,
702                     int      outfd,
703                     int     *stringpos)
704 {
705   if (*stringpos < gstring->len)
706     {
707       int r;
708       do
709         r = write (outfd, gstring->str + *stringpos, gstring->len - *stringpos);
710       while (r < 0 && errno == EINTR);
711       *stringpos += MAX (r, 0);
712     }
713 }
714
715 static int
716 sane_dup2 (int fd1,
717            int fd2)
718 {
719   int ret;
720   do
721     ret = dup2 (fd1, fd2);
722   while (ret < 0 && errno == EINTR);
723   return ret;
724 }
725
726 static void
727 test_trap_clear (void)
728 {
729   test_trap_last_status = 0;
730   test_trap_last_pid = 0;
731   g_free (test_trap_last_stdout);
732   test_trap_last_stdout = NULL;
733   g_free (test_trap_last_stderr);
734   test_trap_last_stderr = NULL;
735 }
736
737 static guint64
738 test_time_stamp (void)
739 {
740   GTimeVal tv;
741   guint64 stamp;
742   g_get_current_time (&tv);
743   stamp = tv.tv_sec;
744   stamp = stamp * 1000000 + tv.tv_usec;
745   return stamp;
746 }
747
748 gboolean
749 g_test_trap_fork (guint64        usec_timeout,
750                   GTestTrapFlags test_trap_flags)
751 {
752   int stdout_pipe[2] = { -1, -1 };
753   int stderr_pipe[2] = { -1, -1 };
754   int stdtst_pipe[2] = { -1, -1 };
755   test_trap_clear();
756   if (pipe (stdout_pipe) < 0 || pipe (stderr_pipe) < 0 || pipe (stdtst_pipe) < 0)
757     g_error ("failed to create pipes to fork test program: %s", g_strerror (errno));
758   signal (SIGCHLD, SIG_DFL);
759   test_trap_last_pid = fork ();
760   if (test_trap_last_pid < 0)
761     g_error ("failed to fork test program: %s", g_strerror (errno));
762   if (test_trap_last_pid == 0)  /* child */
763     {
764       int fd0 = -1;
765       close (stdout_pipe[0]);
766       close (stderr_pipe[0]);
767       close (stdtst_pipe[0]);
768       if (!(test_trap_flags & G_TEST_TRAP_INHERIT_STDIN))
769         fd0 = open ("/dev/null", O_RDONLY);
770       if (sane_dup2 (stdout_pipe[1], 1) < 0 || sane_dup2 (stderr_pipe[1], 2) < 0 || (fd0 >= 0 && sane_dup2 (fd0, 0) < 0))
771         g_error ("failed to dup2() in forked test program: %s", g_strerror (errno));
772       if (fd0 >= 3)
773         close (fd0);
774       if (stdout_pipe[1] >= 3)
775         close (stdout_pipe[1]);
776       if (stderr_pipe[1] >= 3)
777         close (stderr_pipe[1]);
778       test_stdmsg = stdtst_pipe[1];
779       return TRUE;
780     }
781   else                          /* parent */
782     {
783       GString *sout = g_string_new (NULL);
784       GString *serr = g_string_new (NULL);
785       GString *stst = g_string_new (NULL);
786       guint64 sstamp;
787       int soutpos = 0, serrpos = 0, ststpos = 0, wr, need_wait = TRUE;
788       close (stdout_pipe[1]);
789       close (stderr_pipe[1]);
790       close (stdtst_pipe[1]);
791       sstamp = test_time_stamp();
792       /* read data until we get EOF on all pipes */
793       while (stdout_pipe[0] >= 0 || stderr_pipe[0] >= 0 || stdtst_pipe[0] > 0)
794         {
795           fd_set fds;
796           struct timeval tv;
797           FD_ZERO (&fds);
798           if (stdout_pipe[0] >= 0)
799             FD_SET (stdout_pipe[0], &fds);
800           if (stderr_pipe[0] >= 0)
801             FD_SET (stderr_pipe[0], &fds);
802           if (stdtst_pipe[0] >= 0)
803             FD_SET (stdtst_pipe[0], &fds);
804           tv.tv_sec = 0;
805           tv.tv_usec = MIN (usec_timeout ? usec_timeout : 1000000, 100 * 1000); // sleep at most 0.5 seconds to catch clock skews, etc.
806           int ret = select (MAX (MAX (stdout_pipe[0], stderr_pipe[0]), stdtst_pipe[0]) + 1, &fds, NULL, NULL, &tv);
807           if (ret < 0 && errno != EINTR)
808             {
809               g_warning ("Unexpected error in select() while reading from child process (%d): %s", test_trap_last_pid, g_strerror (errno));
810               break;
811             }
812           if (stdout_pipe[0] >= 0 && FD_ISSET (stdout_pipe[0], &fds) &&
813               g_string_must_read (sout, stdout_pipe[0]) == 0)
814             {
815               close (stdout_pipe[0]);
816               stdout_pipe[0] = -1;
817             }
818           if (stderr_pipe[0] >= 0 && FD_ISSET (stderr_pipe[0], &fds) &&
819               g_string_must_read (serr, stderr_pipe[0]) == 0)
820             {
821               close (stderr_pipe[0]);
822               stderr_pipe[0] = -1;
823             }
824           if (stdtst_pipe[0] >= 0 && FD_ISSET (stdtst_pipe[0], &fds) &&
825               g_string_must_read (stst, stdtst_pipe[0]) == 0)
826             {
827               close (stdtst_pipe[0]);
828               stdtst_pipe[0] = -1;
829             }
830           if (!(test_trap_flags & G_TEST_TRAP_SILENCE_STDOUT))
831             g_string_write_out (sout, 1, &soutpos);
832           if (!(test_trap_flags & G_TEST_TRAP_SILENCE_STDERR))
833             g_string_write_out (serr, 2, &serrpos);
834           if (TRUE) // FIXME: needs capturing into log file
835             g_string_write_out (stst, 1, &ststpos);
836           if (usec_timeout)
837             {
838               guint64 nstamp = test_time_stamp();
839               int status = 0;
840               sstamp = MIN (sstamp, nstamp); // guard against backwards clock skews
841               if (usec_timeout < nstamp - sstamp)
842                 {
843                   /* timeout reached, need to abort the child now */
844                   kill_child (test_trap_last_pid, &status, 3);
845                   test_trap_last_status = 1024; /* timeout */
846                   if (0 && WIFSIGNALED (status))
847                     g_printerr ("%s: child timed out and received: %s\n", G_STRFUNC, g_strsignal (WTERMSIG (status)));
848                   need_wait = FALSE;
849                   break;
850                 }
851             }
852         }
853       close (stdout_pipe[0]);
854       close (stderr_pipe[0]);
855       close (stdtst_pipe[0]);
856       if (need_wait)
857         {
858           int status = 0;
859           do
860             wr = waitpid (test_trap_last_pid, &status, 0);
861           while (wr < 0 && errno == EINTR);
862           if (WIFEXITED (status)) /* normal exit */
863             test_trap_last_status = WEXITSTATUS (status); /* 0..255 */
864           else if (WIFSIGNALED (status))
865             test_trap_last_status = (WTERMSIG (status) << 12); /* signalled */
866           else /* WCOREDUMP (status) */
867             test_trap_last_status = 512; /* coredump */
868         }
869       test_trap_last_stdout = g_string_free (sout, FALSE);
870       test_trap_last_stderr = g_string_free (serr, FALSE);
871       g_string_free (stst, TRUE);
872       return FALSE;
873     }
874 }
875
876 gboolean
877 g_test_trap_has_passed (void)
878 {
879   return test_trap_last_status == 0; /* exit_status == 0 && !signal && !coredump */
880 }
881
882 gboolean
883 g_test_trap_reached_timeout (void)
884 {
885   return 0 != (test_trap_last_status & 1024); /* timeout flag */
886 }
887
888 void
889 g_test_trap_assertions (const char     *domain,
890                         const char     *file,
891                         int             line,
892                         const char     *func,
893                         gboolean        must_pass,
894                         gboolean        must_fail,
895                         const char     *stdout_pattern,
896                         const char     *stderr_pattern)
897 {
898   if (test_trap_last_pid == 0)
899     g_error ("child process failed to exit after g_test_trap_fork() and before g_test_trap_assert*()");
900   if (must_pass && !g_test_trap_has_passed())
901     {
902       char *msg = g_strdup_printf ("child process (%d) of test trap failed unexpectedly", test_trap_last_pid);
903       g_assertion_message (domain, file, line, func, msg);
904       g_free (msg);
905     }
906   if (must_fail && g_test_trap_has_passed())
907     {
908       char *msg = g_strdup_printf ("child process (%d) did not fail as expected", test_trap_last_pid);
909       g_assertion_message (domain, file, line, func, msg);
910       g_free (msg);
911     }
912   if (stdout_pattern && !g_pattern_match_simple (stdout_pattern, test_trap_last_stdout))
913     {
914       char *msg = g_strdup_printf ("stdout of child process (%d) failed to match: %s", test_trap_last_pid, stdout_pattern);
915       g_assertion_message (domain, file, line, func, msg);
916       g_free (msg);
917     }
918   if (stderr_pattern && !g_pattern_match_simple (stderr_pattern, test_trap_last_stderr))
919     {
920       char *msg = g_strdup_printf ("stderr of child process (%d) failed to match: %s", test_trap_last_pid, stderr_pattern);
921       g_assertion_message (domain, file, line, func, msg);
922       g_free (msg);
923     }
924 }