gtester: implemented XML logging.
[platform/upstream/glib.git] / glib / gtester.c
1 /* GLib testing framework runner
2  * Copyright (C) 2007 Sven Herzberg
3  * Copyright (C) 2007 Tim Janik
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library 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 GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20 #include <glib.h>
21 #include <gstdio.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25 #include <fcntl.h>
26 #include <errno.h>
27 #include <signal.h>
28
29 /* the read buffer size in bytes */
30 #define READ_BUFFER_SIZE 4096
31
32 /* --- prototypes --- */
33 static void     parse_args      (gint           *argc_p,
34                                  gchar        ***argv_p);
35
36 /* --- variables --- */
37 static GIOChannel  *ioc_report = NULL;
38 static gboolean     gtester_quiet = FALSE;
39 static gboolean     gtester_verbose = FALSE;
40 static gboolean     gtester_list_tests = FALSE;
41 static gboolean     subtest_running = FALSE;
42 static gboolean     subtest_io_pending = FALSE;
43 static gboolean     subtest_faill = TRUE;
44 static gboolean     subtest_quiet = TRUE;
45 static gboolean     subtest_verbose = FALSE;
46 static gboolean     subtest_mode_fatal = FALSE;
47 static gboolean     subtest_mode_perf = FALSE;
48 static gboolean     subtest_mode_quick = TRUE;
49 static const gchar *subtest_seedstr = NULL;
50 static GSList      *subtest_paths = NULL;
51 static gboolean     testcase_open = FALSE;
52 static const gchar *output_filename = NULL;
53 static guint        log_indent = 0;
54 static gint         log_fd = -1;
55
56 /* --- functions --- */
57 static const char*
58 sindent (guint n)
59 {
60   static const char spaces[] = "                                                                                                    ";
61   int l = sizeof (spaces);
62   n = MIN (n, l - 1);
63   return spaces + l - n;
64 }
65
66 static void G_GNUC_PRINTF (1, 2)
67 test_log_printfe (const char *format,
68                   ...)
69 {
70   char *result;
71   int r;
72   va_list args;
73   va_start (args, format);
74   result = g_markup_vprintf_escaped (format, args);
75   va_end (args);
76   do
77     r = write (log_fd, result, strlen (result));
78   while (r < 0 && errno == EINTR);
79   g_free (result);
80 }
81
82 static void
83 test_log_msg (GTestLogMsg *msg)
84 {
85   switch (msg->log_type)
86     {
87     case G_TEST_LOG_NONE:
88       break;
89     case G_TEST_LOG_ERROR:
90       g_printerr ("%s\n", msg->strings[0]);
91       break;
92     case G_TEST_LOG_START_BINARY:
93       test_log_printfe ("%s<binary file=\"%s\"/>\n", sindent (log_indent), msg->strings[0]);
94       test_log_printfe ("%s<random-seed>%s</random-seed>\n", sindent (log_indent), msg->strings[1]);
95       break;
96     case G_TEST_LOG_LIST_CASE:
97       g_print ("%s\n", msg->strings[0]);
98       break;
99     case G_TEST_LOG_START_CASE:
100       if (gtester_verbose)
101         {
102           gchar *sc = g_strconcat (msg->strings[0], ":", NULL);
103           gchar *sleft = g_strdup_printf ("%-68s", sc);
104           g_free (sc);
105           g_print ("%70s ", sleft);
106           g_free (sleft);
107         }
108       g_return_if_fail (testcase_open == 0);
109       testcase_open++;
110       test_log_printfe ("%s<testcase path=\"%s\">\n", sindent (log_indent), msg->strings[0]);
111       log_indent += 2;
112       break;
113     case G_TEST_LOG_STOP_CASE:
114       g_return_if_fail (testcase_open > 0);
115       test_log_printfe ("%s<duration>%.6Lf</duration>\n", sindent (log_indent), msg->nums[2]);
116       test_log_printfe ("%s<status exit-status=\"%d\" n-forks=\"%d\"/>\n",
117                         sindent (log_indent), (int) msg->nums[0], (int) msg->nums[1]);
118       log_indent -= 2;
119       test_log_printfe ("%s</testcase>\n", sindent (log_indent));
120       testcase_open--;
121       if (gtester_verbose)
122         g_print ("OK\n");
123       break;
124     case G_TEST_LOG_MIN_RESULT:
125     case G_TEST_LOG_MAX_RESULT:
126       test_log_printfe ("%s<performance minimize=\"%d\" maximize=\"%d\" value=\"%.16Lg\">\n",
127                         sindent (log_indent), msg->log_type == G_TEST_LOG_MIN_RESULT, msg->log_type == G_TEST_LOG_MAX_RESULT, msg->nums[0]);
128       test_log_printfe ("%s%s\n", sindent (log_indent + 2), msg->strings[0]);
129       test_log_printfe ("%s</performance>\n", sindent (log_indent));
130       break;
131     }
132 }
133
134 static gboolean
135 child_report_cb (GIOChannel  *source,
136                  GIOCondition condition,
137                  gpointer     data)
138 {
139   GTestLogBuffer *tlb = data;
140   GIOStatus status = G_IO_STATUS_NORMAL;
141   gsize length = 0;
142   do
143     {
144       guint8 buffer[READ_BUFFER_SIZE];
145       GError *error = NULL;
146       status = g_io_channel_read_chars (source, (gchar*) buffer, sizeof (buffer), &length, &error);
147       if (length)
148         {
149           GTestLogMsg *msg;
150           g_test_log_buffer_push (tlb, length, buffer);
151           do
152             {
153               msg = g_test_log_buffer_pop (tlb);
154               if (msg)
155                 {
156                   test_log_msg (msg);
157                   g_test_log_msg_free (msg);
158                 }
159             }
160           while (msg);
161         }
162       g_clear_error (&error);
163       /* ignore the io channel status, which seems to be bogus especially for non blocking fds */
164       (void) status;
165     }
166   while (length > 0);
167   if (condition & (G_IO_ERR | G_IO_HUP))
168     {
169       /* if there's no data to read and select() reports an error or hangup,
170        * the fd must have been closed remotely
171        */
172       subtest_io_pending = FALSE;
173       return FALSE;
174     }
175   return TRUE; /* keep polling */
176 }
177
178 static void
179 child_watch_cb (GPid     pid,
180                 gint     status,
181                 gpointer data)
182 {
183   g_spawn_close_pid (pid);
184   subtest_running = FALSE;
185 }
186
187 static gchar*
188 queue_gfree (GSList **slistp,
189              gchar   *string)
190 {
191   *slistp = g_slist_prepend (*slistp, string);
192   return string;
193 }
194
195 static void
196 unset_cloexec_fdp (gpointer fdp_data)
197 {
198   int r, *fdp = fdp_data;
199   do
200     r = fcntl (*fdp, F_SETFD, 0 /* FD_CLOEXEC */);
201   while (r < 0 && errno == EINTR);
202 }
203
204 static gboolean
205 launch_test_binary (const char *binary)
206 {
207   GTestLogBuffer *tlb;
208   GSList *slist, *free_list = NULL;
209   GError *error = NULL;
210   const gchar *argv[20 + g_slist_length (subtest_paths)];
211   GPid pid = 0;
212   gint report_pipe[2] = { -1, -1 };
213   gint i = 0;
214
215   if (pipe (report_pipe) < 0)
216     {
217       if (subtest_mode_fatal)
218         g_error ("Failed to open pipe for test binary: %s: %s", binary, g_strerror (errno));
219       else
220         g_warning ("Failed to open pipe for test binary: %s: %s", binary, g_strerror (errno));
221       return FALSE;
222     }
223
224   /* setup argv */
225   argv[i++] = binary;
226   if (subtest_quiet)
227     argv[i++] = "--quiet";
228   if (subtest_verbose)
229     argv[i++] = "--verbose";
230   // argv[i++] = "--debug-log";
231   argv[i++] = queue_gfree (&free_list, g_strdup_printf ("--GTestLogFD=%u", report_pipe[1]));
232   if (!subtest_mode_fatal)
233     argv[i++] = "--keep-going";
234   if (subtest_mode_quick)
235     argv[i++] = "-m=quick";
236   else
237     argv[i++] = "-m=slow";
238   if (subtest_mode_perf)
239     argv[i++] = "-m=perf";
240   if (subtest_seedstr)
241     argv[i++] = queue_gfree (&free_list, g_strdup_printf ("--seed=%s", subtest_seedstr));
242   for (slist = subtest_paths; slist; slist = slist->next)
243     argv[i++] = queue_gfree (&free_list, g_strdup_printf ("-p=%s", (gchar*) slist->data));
244   if (gtester_list_tests)
245     argv[i++] = "-l";
246   argv[i++] = NULL;
247
248   g_spawn_async_with_pipes (NULL, /* g_get_current_dir() */
249                             (gchar**) argv,
250                             NULL, /* envp */
251                             G_SPAWN_DO_NOT_REAP_CHILD, /* G_SPAWN_SEARCH_PATH */
252                             unset_cloexec_fdp, &report_pipe[1], /* pre-exec callback */
253                             &pid,
254                             NULL,       /* standard_input */
255                             NULL,       /* standard_output */
256                             NULL,       /* standard_error */
257                             &error);
258   g_slist_foreach (free_list, (void(*)(void*,void*)) g_free, NULL);
259   g_slist_free (free_list);
260   free_list = NULL;
261   close (report_pipe[1]);
262
263   if (!gtester_quiet)
264     g_print ("(pid=%lu)\n", (unsigned long) pid);
265
266   if (error)
267     {
268       close (report_pipe[0]);
269       if (subtest_mode_fatal)
270         g_error ("Failed to execute test binary: %s: %s", argv[0], error->message);
271       else
272         g_warning ("Failed to execute test binary: %s: %s", argv[0], error->message);
273       g_clear_error (&error);
274       return FALSE;
275     }
276
277   subtest_running = TRUE;
278   subtest_io_pending = TRUE;
279   tlb = g_test_log_buffer_new();
280   if (report_pipe[0] >= 0)
281     {
282       ioc_report = g_io_channel_unix_new (report_pipe[0]);
283       g_io_channel_set_flags (ioc_report, G_IO_FLAG_NONBLOCK, NULL);
284       g_io_channel_set_encoding (ioc_report, NULL, NULL);
285       g_io_channel_set_buffered (ioc_report, FALSE);
286       g_io_add_watch_full (ioc_report, G_PRIORITY_DEFAULT - 1, G_IO_IN | G_IO_ERR | G_IO_HUP, child_report_cb, tlb, NULL);
287       g_io_channel_unref (ioc_report);
288     }
289   g_child_watch_add_full (G_PRIORITY_DEFAULT + 1, pid, child_watch_cb, NULL, NULL);
290
291   while (subtest_running ||             /* FALSE once child exits */
292          subtest_io_pending ||          /* FALSE once ioc_report closes */
293          g_main_context_pending (NULL)) /* TRUE while idler, etc are running */
294     g_main_context_iteration (NULL, TRUE);
295
296   close (report_pipe[0]);
297   g_test_log_buffer_free (tlb);
298
299   return TRUE;
300 }
301
302 static void
303 launch_test (const char *binary)
304 {
305   gboolean success;
306   GTimer *btimer = g_timer_new();
307   subtest_faill = FALSE;
308   if (!gtester_quiet)
309     g_print ("TEST: %s... ", binary);
310   test_log_printfe ("%s<testbinary path=\"%s\">\n", sindent (log_indent), binary);
311   log_indent += 2;
312   g_timer_start (btimer);
313   success = launch_test_binary (binary);
314   g_timer_stop (btimer);
315   test_log_printfe ("%s<duration>%.6f</duration>\n", sindent (log_indent), g_timer_elapsed (btimer, NULL));
316   log_indent -= 2;
317   test_log_printfe ("%s</testbinary>\n", sindent (log_indent));
318   if (!gtester_quiet)
319     g_print ("%s: %s\n", subtest_faill || !success ? "FAIL" : "PASS", binary);
320   g_timer_destroy (btimer);
321 }
322
323 static void
324 usage (gboolean just_version)
325 {
326   if (just_version)
327     {
328       g_print ("gtester version %d.%d.%d\n", GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION, GLIB_MICRO_VERSION);
329       return;
330     }
331   g_print ("Usage: gtester [OPTIONS] testprogram...\n");
332   /*        12345678901234567890123456789012345678901234567890123456789012345678901234567890 */
333   g_print ("Options:\n");
334   g_print ("  -h, --help                  show this help message\n");
335   g_print ("  -v, --version               print version informations\n");
336   g_print ("  --g-fatal-warnings          make warnings fatal (abort)\n");
337   g_print ("  -k, --keep-going            continue running after tests failed\n");
338   g_print ("  -l                          list paths of available test cases\n");
339   g_print ("  -m=perf, -m=slow, -m=quick  run test cases in mode perf, slow or quick (default)\n");
340   g_print ("  -p=TESTPATH                 only start test cases matching TESTPATH\n");
341   g_print ("  --seed=SEEDSTRING           start all tests with random number seed SEEDSTRING\n");
342   g_print ("  -o=LOGFILE                  write the test log to LOGFILE\n");
343   g_print ("  -q, --quiet                 suppress per test binary output\n");
344   g_print ("  --verbose                   report success per testcase\n");
345 }
346
347 static void
348 parse_args (gint    *argc_p,
349             gchar ***argv_p)
350 {
351   guint argc = *argc_p;
352   gchar **argv = *argv_p;
353   guint i, e;
354   /* parse known args */
355   for (i = 1; i < argc; i++)
356     {
357       if (strcmp (argv[i], "--g-fatal-warnings") == 0)
358         {
359           GLogLevelFlags fatal_mask = (GLogLevelFlags) g_log_set_always_fatal ((GLogLevelFlags) G_LOG_FATAL_MASK);
360           fatal_mask = (GLogLevelFlags) (fatal_mask | G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL);
361           g_log_set_always_fatal (fatal_mask);
362           argv[i] = NULL;
363         }
364       else if (strcmp (argv[i], "-h") == 0 || strcmp (argv[i], "--help") == 0)
365         {
366           usage (FALSE);
367           exit (0);
368           argv[i] = NULL;
369         }
370       else if (strcmp (argv[i], "-v") == 0 || strcmp (argv[i], "--version") == 0)
371         {
372           usage (TRUE);
373           exit (0);
374           argv[i] = NULL;
375         }
376       else if (strcmp (argv[i], "--keep-going") == 0 ||
377                strcmp (argv[i], "-k") == 0)
378         {
379           subtest_mode_fatal = FALSE;
380           argv[i] = NULL;
381         }
382       else if (strcmp ("-p", argv[i]) == 0 || strncmp ("-p=", argv[i], 3) == 0)
383         {
384           gchar *equal = argv[i] + 2;
385           if (*equal == '=')
386             subtest_paths = g_slist_prepend (subtest_paths, equal + 1);
387           else if (i + 1 < argc)
388             {
389               argv[i++] = NULL;
390               subtest_paths = g_slist_prepend (subtest_paths, argv[i]);
391             }
392           argv[i] = NULL;
393         }
394       else if (strcmp ("-o", argv[i]) == 0 || strncmp ("-o=", argv[i], 3) == 0)
395         {
396           gchar *equal = argv[i] + 2;
397           if (*equal == '=')
398             output_filename = equal + 1;
399           else if (i + 1 < argc)
400             {
401               argv[i++] = NULL;
402               output_filename = argv[i];
403             }
404           argv[i] = NULL;
405         }
406       else if (strcmp ("-m", argv[i]) == 0 || strncmp ("-m=", argv[i], 3) == 0)
407         {
408           gchar *equal = argv[i] + 2;
409           const gchar *mode = "";
410           if (*equal == '=')
411             mode = equal + 1;
412           else if (i + 1 < argc)
413             {
414               argv[i++] = NULL;
415               mode = argv[i];
416             }
417           if (strcmp (mode, "perf") == 0)
418             subtest_mode_perf = TRUE;
419           else if (strcmp (mode, "slow") == 0)
420             subtest_mode_quick = FALSE;
421           else if (strcmp (mode, "quick") == 0)
422             {
423               subtest_mode_quick = TRUE;
424               subtest_mode_perf = FALSE;
425             }
426           else
427             g_error ("unknown test mode: -m %s", mode);
428           argv[i] = NULL;
429         }
430       else if (strcmp ("-q", argv[i]) == 0 || strcmp ("--quiet", argv[i]) == 0)
431         {
432           gtester_quiet = TRUE;
433           gtester_verbose = FALSE;
434           argv[i] = NULL;
435         }
436       else if (strcmp ("--verbose", argv[i]) == 0)
437         {
438           gtester_quiet = FALSE;
439           gtester_verbose = TRUE;
440           argv[i] = NULL;
441         }
442       else if (strcmp ("-l", argv[i]) == 0)
443         {
444           gtester_list_tests = TRUE;
445           argv[i] = NULL;
446         }
447       else if (strcmp ("--seed", argv[i]) == 0 || strncmp ("--seed=", argv[i], 7) == 0)
448         {
449           gchar *equal = argv[i] + 6;
450           if (*equal == '=')
451             subtest_seedstr = equal + 1;
452           else if (i + 1 < argc)
453             {
454               argv[i++] = NULL;
455               subtest_seedstr = argv[i];
456             }
457           argv[i] = NULL;
458         }
459     }
460   /* collapse argv */
461   e = 1;
462   for (i = 1; i < argc; i++)
463     if (argv[i])
464       {
465         argv[e++] = argv[i];
466         if (i >= e)
467           argv[i] = NULL;
468       }
469   *argc_p = e;
470 }
471
472 int
473 main (int    argc,
474       char **argv)
475 {
476   guint ui;
477
478   /* some unices need SA_RESTART for SIGCHLD to return -EAGAIN for io.
479    * we must fiddle with sigaction() *before* glib is used, otherwise
480    * we could revoke signal hanmdler setups from glib initialization code.
481    */
482   if (TRUE)
483     {
484       struct sigaction sa;
485       struct sigaction osa;
486       sa.sa_handler = SIG_DFL;
487       sigfillset (&sa.sa_mask);
488       sa.sa_flags = SA_RESTART;
489       sigaction (SIGCHLD, &sa, &osa);
490     }
491
492   g_set_prgname (argv[0]);
493   parse_args (&argc, &argv);
494
495   if (argc <= 1)
496     {
497       usage (FALSE);
498       return 1;
499     }
500
501   if (output_filename)
502     {
503       log_fd = g_open (output_filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
504       if (log_fd < 0)
505         g_error ("Failed to open log file '%s': %s", output_filename, g_strerror (errno));
506     }
507
508   test_log_printfe ("<?xml version=\"1.0\"?>\n");
509   test_log_printfe ("%s<gtester>\n", sindent (log_indent));
510   log_indent += 2;
511   for (ui = 1; ui < argc; ui++)
512     {
513       const char *binary = argv[ui];
514       launch_test (binary);
515       /* we only get here on success or if !subtest_mode_fatal */
516     }
517   log_indent -= 2;
518   test_log_printfe ("%s</gtester>\n", sindent (log_indent));
519
520   close (log_fd);
521
522   return 0;
523 }