gtester.c: adapted to become a rudimentary test binary launcher.
[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 <string.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24
25 /* the read buffer size in bytes */
26 #define READ_BUFFER_SIZE 4096
27
28 /* --- prototypes --- */
29 static void     parse_args      (gint           *argc_p,
30                                  gchar        ***argv_p);
31
32 /* --- variables --- */
33 static GIOChannel  *ioc_report = NULL;
34 static gboolean     subtest_running = FALSE;
35 static gboolean     subtest_io_pending = FALSE;
36 static gboolean     gtester_quiet = FALSE;
37 static gboolean     gtester_verbose = FALSE;
38 static gboolean     gtester_list_tests = FALSE;
39 static gboolean     subtest_mode_fatal = FALSE;
40 static gboolean     subtest_mode_perf = FALSE;
41 static gboolean     subtest_mode_quick = TRUE;
42 static const gchar *subtest_seedstr = NULL;
43 static GSList      *subtest_paths = NULL;
44 static const gchar *outpu_filename = NULL;
45
46 /* --- functions --- */
47 static gboolean
48 child_report_cb (GIOChannel  *source,
49                  GIOCondition condition,
50                  gpointer     data)
51 {
52   GIOStatus status = G_IO_STATUS_NORMAL;
53
54   while (status == G_IO_STATUS_NORMAL)
55     {
56       gchar buffer[READ_BUFFER_SIZE];
57       gsize length = 0;
58       GError *error = NULL;
59       status = g_io_channel_read_chars (source, buffer, sizeof (buffer), &length, &error);
60       switch (status)
61         {
62         case G_IO_STATUS_NORMAL:
63           write (2, buffer, length); /* passthrough child's stdout */
64           break;
65         case G_IO_STATUS_AGAIN:
66           /* retry later */
67           break;
68         case G_IO_STATUS_ERROR:
69           /* ignore, child closed fd or similar g_warning ("Error while reading data: %s", error->message); */
70           /* fall through into EOF */
71         case G_IO_STATUS_EOF:
72           subtest_io_pending = FALSE;
73           return FALSE;
74         }
75       g_clear_error (&error);
76     }
77   return TRUE;
78 }
79
80 static void
81 child_watch_cb (GPid     pid,
82                 gint     status,
83                 gpointer data)
84 {
85   g_spawn_close_pid (pid);
86   subtest_running = FALSE;
87 }
88
89 static gchar*
90 queue_gfree (GSList **slistp,
91              gchar   *string)
92 {
93   *slistp = g_slist_prepend (*slistp, string);
94   return string;
95 }
96
97 static void
98 launch_test (const char *binary)
99 {
100   GSList *slist, *free_list = NULL;
101   GError *error = NULL;
102   const gchar *argv[20 + g_slist_length (subtest_paths)];
103   GPid pid = 0;
104   gint i = 0, child_report = -1;
105
106   /* setup argv */
107   argv[i++] = binary;
108   // argv[i++] = "--quiet";
109   if (!subtest_mode_fatal)
110     argv[i++] = "--keep-going";
111   if (subtest_mode_quick)
112     argv[i++] = "-m=quick";
113   else
114     argv[i++] = "-m=slow";
115   if (subtest_mode_perf)
116     argv[i++] = "-m=perf";
117   if (subtest_seedstr)
118     argv[i++] = queue_gfree (&free_list, g_strdup_printf ("--seed=%s", subtest_seedstr));
119   for (slist = subtest_paths; slist; slist = slist->next)
120     argv[i++] = queue_gfree (&free_list, g_strdup_printf ("-p=%s", (gchar*) slist->data));
121   argv[i++] = NULL;
122
123   /* child_report will be used to capture logging information from the
124    * child binary. for the moment, we just use it to replicate stdout.
125    */
126
127   g_spawn_async_with_pipes (NULL, /* g_get_current_dir() */
128                             (gchar**) argv,
129                             NULL, /* envp */
130                             G_SPAWN_DO_NOT_REAP_CHILD, /* G_SPAWN_SEARCH_PATH */
131                             NULL, NULL, /* child_setup, user_data */
132                             &pid,
133                             NULL,       /* standard_input */
134                             &child_report, /* standard_output */
135                             NULL,       /* standard_error */
136                             &error);
137   g_slist_foreach (free_list, (void(*)(void*,void*)) g_free, NULL);
138   g_slist_free (free_list);
139   free_list = NULL;
140
141   if (error)
142     {
143       if (subtest_mode_fatal)
144         g_error ("Failed to execute test binary: %s: %s", argv[0], error->message);
145       else
146         g_warning ("Failed to execute test binary: %s: %s", argv[0], error->message);
147       g_clear_error (&error);
148       return;
149     }
150   subtest_running = TRUE;
151   subtest_io_pending = TRUE;
152
153   if (child_report >= 0)
154     {
155       ioc_report = g_io_channel_unix_new (child_report);
156       g_io_channel_set_flags (ioc_report, G_IO_FLAG_NONBLOCK, NULL);
157       g_io_add_watch_full (ioc_report, G_PRIORITY_DEFAULT - 1, G_IO_IN | G_IO_ERR | G_IO_HUP, child_report_cb, NULL, NULL);
158       g_io_channel_unref (ioc_report);
159     }
160   g_child_watch_add_full (G_PRIORITY_DEFAULT + 1, pid, child_watch_cb, NULL, NULL);
161
162   while (subtest_running ||             /* FALSE once child exits */
163          subtest_io_pending ||          /* FALSE once ioc_report closes */
164          g_main_context_pending (NULL)) /* TRUE while idler, etc are running */
165     g_main_context_iteration (NULL, TRUE);
166 }
167
168 static void
169 usage (gboolean just_version)
170 {
171   if (just_version)
172     {
173       g_print ("gtester version %d.%d.%d\n", GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION, GLIB_MICRO_VERSION);
174       return;
175     }
176   g_print ("Usage: gtester [OPTIONS] testprogram...\n");
177   /*        12345678901234567890123456789012345678901234567890123456789012345678901234567890 */
178   g_print ("Options:\n");
179   g_print ("  -h, --help                  show this help message\n");
180   g_print ("  -v, --version               print version informations\n");
181   g_print ("  --g-fatal-warnings          make warnings fatal (abort)\n");
182   g_print ("  -k, --keep-going            continue running after tests failed\n");
183   g_print ("  -l                          list paths of available test cases\n");
184   g_print ("  -m=perf, -m=slow, -m=quick  run test cases in mode perf, slow or quick (default)\n");
185   g_print ("  -p=TESTPATH                 only start test cases matching TESTPATH\n");
186   g_print ("  --seed=SEEDSTRING           start all tests with random number seed SEEDSTRING\n");
187   g_print ("  -o=LOGFILE                  write the test log to LOGFILE\n");
188   g_print ("  -q, --quiet                 suppress unnecessary output\n");
189   g_print ("  --verbose                   produce additional output\n");
190 }
191
192 static void
193 parse_args (gint    *argc_p,
194             gchar ***argv_p)
195 {
196   guint argc = *argc_p;
197   gchar **argv = *argv_p;
198   guint i, e;
199   /* parse known args */
200   for (i = 1; i < argc; i++)
201     {
202       if (strcmp (argv[i], "--g-fatal-warnings") == 0)
203         {
204           GLogLevelFlags fatal_mask = (GLogLevelFlags) g_log_set_always_fatal ((GLogLevelFlags) G_LOG_FATAL_MASK);
205           fatal_mask = (GLogLevelFlags) (fatal_mask | G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL);
206           g_log_set_always_fatal (fatal_mask);
207           argv[i] = NULL;
208         }
209       else if (strcmp (argv[i], "-h") == 0 || strcmp (argv[i], "--help") == 0)
210         {
211           usage (FALSE);
212           exit (0);
213           argv[i] = NULL;
214         }
215       else if (strcmp (argv[i], "-v") == 0 || strcmp (argv[i], "--version") == 0)
216         {
217           usage (TRUE);
218           exit (0);
219           argv[i] = NULL;
220         }
221       else if (strcmp (argv[i], "--keep-going") == 0 ||
222                strcmp (argv[i], "-k") == 0)
223         {
224           subtest_mode_fatal = FALSE;
225           argv[i] = NULL;
226         }
227       else if (strcmp ("-p", argv[i]) == 0 || strncmp ("-p=", argv[i], 3) == 0)
228         {
229           gchar *equal = argv[i] + 2;
230           if (*equal == '=')
231             subtest_paths = g_slist_prepend (subtest_paths, equal + 1);
232           else if (i + 1 < argc)
233             {
234               argv[i++] = NULL;
235               subtest_paths = g_slist_prepend (subtest_paths, argv[i]);
236             }
237           argv[i] = NULL;
238         }
239       else if (strcmp ("-o", argv[i]) == 0 || strncmp ("-o=", argv[i], 3) == 0)
240         {
241           gchar *equal = argv[i] + 2;
242           if (*equal == '=')
243             outpu_filename = equal + 1;
244           else if (i + 1 < argc)
245             {
246               argv[i++] = NULL;
247               outpu_filename = argv[i];
248             }
249           argv[i] = NULL;
250         }
251       else if (strcmp ("-m", argv[i]) == 0 || strncmp ("-m=", argv[i], 3) == 0)
252         {
253           gchar *equal = argv[i] + 2;
254           const gchar *mode = "";
255           if (*equal == '=')
256             mode = equal + 1;
257           else if (i + 1 < argc)
258             {
259               argv[i++] = NULL;
260               mode = argv[i];
261             }
262           if (strcmp (mode, "perf") == 0)
263             subtest_mode_perf = TRUE;
264           else if (strcmp (mode, "slow") == 0)
265             subtest_mode_quick = FALSE;
266           else if (strcmp (mode, "quick") == 0)
267             {
268               subtest_mode_quick = TRUE;
269               subtest_mode_perf = FALSE;
270             }
271           else
272             g_error ("unknown test mode: -m %s", mode);
273           argv[i] = NULL;
274         }
275       else if (strcmp ("-q", argv[i]) == 0 || strcmp ("--quiet", argv[i]) == 0)
276         {
277           gtester_quiet = TRUE;
278           gtester_verbose = FALSE;
279           argv[i] = NULL;
280         }
281       else if (strcmp ("--verbose", argv[i]) == 0)
282         {
283           gtester_quiet = FALSE;
284           gtester_verbose = TRUE;
285           argv[i] = NULL;
286         }
287       else if (strcmp ("-l", argv[i]) == 0)
288         {
289           gtester_list_tests = TRUE;
290           argv[i] = NULL;
291         }
292       else if (strcmp ("--seed", argv[i]) == 0 || strncmp ("--seed=", argv[i], 7) == 0)
293         {
294           gchar *equal = argv[i] + 6;
295           if (*equal == '=')
296             subtest_seedstr = equal + 1;
297           else if (i + 1 < argc)
298             {
299               argv[i++] = NULL;
300               subtest_seedstr = argv[i];
301             }
302           argv[i] = NULL;
303         }
304     }
305   /* collapse argv */
306   e = 1;
307   for (i = 1; i < argc; i++)
308     if (argv[i])
309       {
310         argv[e++] = argv[i];
311         if (i >= e)
312           argv[i] = NULL;
313       }
314   *argc_p = e;
315 }
316
317 int
318 main (int    argc,
319       char **argv)
320 {
321   guint ui;
322
323   g_set_prgname (argv[0]);
324   parse_args (&argc, &argv);
325
326   if (argc <= 1)
327     {
328       usage (FALSE);
329       return 1;
330     }
331
332   for (ui = 1; ui < argc; ui++)
333     {
334       const char *binary = argv[ui];
335       launch_test (binary);
336     }
337
338   /* we only get here on success or if !subtest_mode_fatal */
339   return 0;
340 }