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.1 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, see <http://www.gnu.org/licenses/>. |
17 | */ |
18 | #include "config.h" |
19 | |
20 | #include <glib.h> |
21 | #include <glib-unix.h> |
22 | #include <gstdio.h> |
23 | #include <string.h> |
24 | #include <stdlib.h> |
25 | #include <unistd.h> |
26 | #include <fcntl.h> |
27 | #include <sys/wait.h> |
28 | #include <errno.h> |
29 | #include <signal.h> |
30 | |
31 | /* the read buffer size in bytes */ |
32 | #define READ_BUFFER_SIZE 4096 |
33 | |
34 | /* --- prototypes --- */ |
35 | static int main_selftest (int argc, |
36 | char **argv); |
37 | static void parse_args (gint *argc_p, |
38 | gchar ***argv_p); |
39 | |
40 | /* --- variables --- */ |
41 | static GIOChannel *ioc_report = NULL; |
42 | static gboolean gtester_quiet = FALSE; |
43 | static gboolean gtester_verbose = FALSE; |
44 | static gboolean gtester_list_tests = FALSE; |
45 | static gboolean gtester_selftest = FALSE; |
46 | static gboolean gtester_ignore_deprecation = FALSE; |
47 | static gboolean subtest_running = FALSE; |
48 | static gint subtest_exitstatus = 0; |
49 | static gboolean subtest_io_pending = FALSE; |
50 | static gboolean subtest_quiet = TRUE; |
51 | static gboolean subtest_verbose = FALSE; |
52 | static gboolean subtest_mode_fatal = TRUE; |
53 | static gboolean subtest_mode_perf = FALSE; |
54 | static gboolean subtest_mode_quick = TRUE; |
55 | static gboolean subtest_mode_undefined = TRUE; |
56 | static const gchar *subtest_seedstr = NULL; |
57 | static gchar *subtest_last_seed = NULL; |
58 | static GSList *subtest_paths = NULL; |
59 | static GSList *skipped_paths = NULL; |
60 | static GSList *subtest_args = NULL; |
61 | static gboolean testcase_open = FALSE; |
62 | static guint testcase_count = 0; |
63 | static guint testcase_fail_count = 0; |
64 | static const gchar *output_filename = NULL; |
65 | static guint log_indent = 0; |
66 | static gint log_fd = -1; |
67 | |
68 | /* --- functions --- */ |
69 | static const char* |
70 | sindent (guint n) |
71 | { |
72 | static const char spaces[] = " " ; |
73 | gsize l = sizeof (spaces) - 1; |
74 | n = MIN (n, l); |
75 | return spaces + l - n; |
76 | } |
77 | |
78 | static void G_GNUC_PRINTF (1, 2) |
79 | test_log_printfe (const char *format, |
80 | ...) |
81 | { |
82 | char *result; |
83 | int r; |
84 | va_list args; |
85 | va_start (args, format); |
86 | result = g_markup_vprintf_escaped (format, args); |
87 | va_end (args); |
88 | do |
89 | r = write (fd: log_fd, buf: result, n: strlen (s: result)); |
90 | while (r < 0 && errno == EINTR); |
91 | g_free (mem: result); |
92 | } |
93 | |
94 | static void |
95 | terminate (void) |
96 | { |
97 | kill (pid: getpid(), SIGTERM); |
98 | g_abort(); |
99 | } |
100 | |
101 | static void |
102 | testcase_close (long double duration, |
103 | gint exit_status, |
104 | guint n_forks) |
105 | { |
106 | gboolean success; |
107 | |
108 | g_return_if_fail (testcase_open > 0); |
109 | test_log_printfe (format: "%s<duration>%.6Lf</duration>\n" , sindent (n: log_indent), duration); |
110 | success = exit_status == G_TEST_RUN_SUCCESS || exit_status == G_TEST_RUN_SKIPPED; |
111 | test_log_printfe (format: "%s<status exit-status=\"%d\" n-forks=\"%d\" result=\"%s\"/>\n" , |
112 | sindent (n: log_indent), exit_status, n_forks, |
113 | success ? "success" : "failed" ); |
114 | log_indent -= 2; |
115 | test_log_printfe (format: "%s</testcase>\n" , sindent (n: log_indent)); |
116 | testcase_open--; |
117 | if (gtester_verbose) |
118 | { |
119 | switch (exit_status) |
120 | { |
121 | case G_TEST_RUN_SUCCESS: |
122 | g_print (format: "OK\n" ); |
123 | break; |
124 | case G_TEST_RUN_SKIPPED: |
125 | g_print (format: "SKIP\n" ); |
126 | break; |
127 | default: |
128 | g_print (format: "FAIL\n" ); |
129 | break; |
130 | } |
131 | } |
132 | if (!success && subtest_last_seed) |
133 | g_print (format: "GTester: last random seed: %s\n" , subtest_last_seed); |
134 | if (!success) |
135 | testcase_fail_count += 1; |
136 | if (subtest_mode_fatal && !success) |
137 | terminate(); |
138 | } |
139 | |
140 | static void |
141 | test_log_msg (GTestLogMsg *msg) |
142 | { |
143 | switch (msg->log_type) |
144 | { |
145 | guint i; |
146 | gchar **strv; |
147 | case G_TEST_LOG_NONE: |
148 | case G_TEST_LOG_START_SUITE: |
149 | case G_TEST_LOG_STOP_SUITE: |
150 | break; |
151 | case G_TEST_LOG_ERROR: |
152 | strv = g_strsplit (string: msg->strings[0], delimiter: "\n" , max_tokens: -1); |
153 | for (i = 0; strv[i]; i++) |
154 | test_log_printfe (format: "%s<error>%s</error>\n" , sindent (n: log_indent), strv[i]); |
155 | g_strfreev (str_array: strv); |
156 | break; |
157 | case G_TEST_LOG_START_BINARY: |
158 | test_log_printfe (format: "%s<binary file=\"%s\"/>\n" , sindent (n: log_indent), msg->strings[0]); |
159 | subtest_last_seed = g_strdup (str: msg->strings[1]); |
160 | test_log_printfe (format: "%s<random-seed>%s</random-seed>\n" , sindent (n: log_indent), subtest_last_seed); |
161 | break; |
162 | case G_TEST_LOG_LIST_CASE: |
163 | g_print (format: "%s\n" , msg->strings[0]); |
164 | break; |
165 | case G_TEST_LOG_START_CASE: |
166 | testcase_count++; |
167 | if (gtester_verbose) |
168 | { |
169 | gchar *sc = g_strconcat (string1: msg->strings[0], ":" , NULL); |
170 | gchar *sleft = g_strdup_printf (format: "%-68s" , sc); |
171 | g_free (mem: sc); |
172 | g_print (format: "%70s " , sleft); |
173 | g_free (mem: sleft); |
174 | } |
175 | g_return_if_fail (testcase_open == 0); |
176 | testcase_open++; |
177 | test_log_printfe (format: "%s<testcase path=\"%s\">\n" , sindent (n: log_indent), msg->strings[0]); |
178 | log_indent += 2; |
179 | break; |
180 | case G_TEST_LOG_SKIP_CASE: |
181 | if (FALSE && gtester_verbose) /* enable to debug test case skipping logic */ |
182 | { |
183 | gchar *sc = g_strconcat (string1: msg->strings[0], ":" , NULL); |
184 | gchar *sleft = g_strdup_printf (format: "%-68s" , sc); |
185 | g_free (mem: sc); |
186 | g_print (format: "%70s SKIPPED\n" , sleft); |
187 | g_free (mem: sleft); |
188 | } |
189 | test_log_printfe (format: "%s<testcase path=\"%s\" skipped=\"1\"/>\n" , sindent (n: log_indent), msg->strings[0]); |
190 | break; |
191 | case G_TEST_LOG_STOP_CASE: |
192 | testcase_close (duration: msg->nums[2], exit_status: (int) msg->nums[0], n_forks: (int) msg->nums[1]); |
193 | break; |
194 | case G_TEST_LOG_MIN_RESULT: |
195 | case G_TEST_LOG_MAX_RESULT: |
196 | test_log_printfe (format: "%s<performance minimize=\"%d\" maximize=\"%d\" value=\"%.16Lg\">\n" , |
197 | sindent (n: log_indent), msg->log_type == G_TEST_LOG_MIN_RESULT, msg->log_type == G_TEST_LOG_MAX_RESULT, msg->nums[0]); |
198 | test_log_printfe (format: "%s%s\n" , sindent (n: log_indent + 2), msg->strings[0]); |
199 | test_log_printfe (format: "%s</performance>\n" , sindent (n: log_indent)); |
200 | break; |
201 | case G_TEST_LOG_MESSAGE: |
202 | test_log_printfe (format: "%s<message>\n%s\n%s</message>\n" , sindent (n: log_indent), msg->strings[0], sindent (n: log_indent)); |
203 | break; |
204 | } |
205 | } |
206 | |
207 | static gboolean |
208 | child_report_cb (GIOChannel *source, |
209 | GIOCondition condition, |
210 | gpointer data) |
211 | { |
212 | GTestLogBuffer *tlb = data; |
213 | GIOStatus status = G_IO_STATUS_NORMAL; |
214 | gboolean first_read_eof = FALSE, first_read = TRUE; |
215 | gsize length = 0; |
216 | do |
217 | { |
218 | guint8 buffer[READ_BUFFER_SIZE]; |
219 | GError *error = NULL; |
220 | status = g_io_channel_read_chars (channel: source, buf: (gchar*) buffer, count: sizeof (buffer), bytes_read: &length, error: &error); |
221 | if (first_read && (condition & G_IO_IN)) |
222 | { |
223 | /* on some unixes (MacOS) we need to detect non-blocking fd EOF |
224 | * by an IO_IN select/poll followed by read()==0. |
225 | */ |
226 | first_read_eof = length == 0; |
227 | } |
228 | first_read = FALSE; |
229 | if (length) |
230 | { |
231 | GTestLogMsg *msg; |
232 | g_test_log_buffer_push (tbuffer: tlb, n_bytes: length, bytes: buffer); |
233 | do |
234 | { |
235 | msg = g_test_log_buffer_pop (tbuffer: tlb); |
236 | if (msg) |
237 | { |
238 | test_log_msg (msg); |
239 | g_test_log_msg_free (tmsg: msg); |
240 | } |
241 | } |
242 | while (msg); |
243 | } |
244 | g_clear_error (err: &error); |
245 | /* ignore the io channel status, which will report intermediate EOFs for non blocking fds */ |
246 | (void) status; |
247 | } |
248 | while (length > 0); |
249 | /* g_print ("LASTIOSTATE: first_read_eof=%d condition=%d\n", first_read_eof, condition); */ |
250 | if (first_read_eof || (condition & (G_IO_ERR | G_IO_HUP))) |
251 | { |
252 | /* if there's no data to read and select() reports an error or hangup, |
253 | * the fd must have been closed remotely |
254 | */ |
255 | subtest_io_pending = FALSE; |
256 | return FALSE; |
257 | } |
258 | return TRUE; /* keep polling */ |
259 | } |
260 | |
261 | static void |
262 | child_watch_cb (GPid pid, |
263 | gint status, |
264 | gpointer data) |
265 | { |
266 | g_spawn_close_pid (pid); |
267 | if (WIFEXITED (status)) /* normal exit */ |
268 | subtest_exitstatus = WEXITSTATUS (status); |
269 | else /* signal or core dump, etc */ |
270 | subtest_exitstatus = 0xffffffff; |
271 | subtest_running = FALSE; |
272 | } |
273 | |
274 | static gchar* |
275 | queue_gfree (GSList **slistp, |
276 | gchar *string) |
277 | { |
278 | *slistp = g_slist_prepend (list: *slistp, data: string); |
279 | return string; |
280 | } |
281 | |
282 | static void |
283 | unset_cloexec_fdp (gpointer fdp_data) |
284 | { |
285 | int r, *fdp = fdp_data; |
286 | do |
287 | r = fcntl (fd: *fdp, F_SETFD, 0 /* FD_CLOEXEC */); |
288 | while (r < 0 && errno == EINTR); |
289 | } |
290 | |
291 | static gboolean |
292 | launch_test_binary (const char *binary, |
293 | guint skip_tests) |
294 | { |
295 | GTestLogBuffer *tlb; |
296 | GSList *slist, *free_list = NULL; |
297 | GError *error = NULL; |
298 | int argc = 0; |
299 | const gchar **argv; |
300 | GPid pid = 0; |
301 | gint report_pipe[2] = { -1, -1 }; |
302 | guint child_report_cb_id = 0; |
303 | gboolean loop_pending; |
304 | gint i = 0; |
305 | |
306 | if (!g_unix_open_pipe (fds: report_pipe, FD_CLOEXEC, error: &error)) |
307 | { |
308 | if (subtest_mode_fatal) |
309 | g_error ("Failed to open pipe for test binary: %s: %s" , binary, error->message); |
310 | else |
311 | g_warning ("Failed to open pipe for test binary: %s: %s" , binary, error->message); |
312 | g_clear_error (err: &error); |
313 | return FALSE; |
314 | } |
315 | |
316 | /* setup argc */ |
317 | for (slist = subtest_args; slist; slist = slist->next) |
318 | argc++; |
319 | /* argc++; */ |
320 | if (subtest_quiet) |
321 | argc++; |
322 | if (subtest_verbose) |
323 | argc++; |
324 | if (!subtest_mode_fatal) |
325 | argc++; |
326 | /* Either -m=quick or -m=slow is always appended. */ |
327 | argc++; |
328 | if (subtest_mode_perf) |
329 | argc++; |
330 | if (!subtest_mode_undefined) |
331 | argc++; |
332 | if (gtester_list_tests) |
333 | argc++; |
334 | if (subtest_seedstr) |
335 | argc++; |
336 | argc++; |
337 | if (skip_tests) |
338 | argc++; |
339 | for (slist = subtest_paths; slist; slist = slist->next) |
340 | argc++; |
341 | for (slist = skipped_paths; slist; slist = slist->next) |
342 | argc++; |
343 | |
344 | /* setup argv */ |
345 | argv = g_malloc (n_bytes: (argc + 2) * sizeof(gchar *)); |
346 | argv[i++] = binary; |
347 | for (slist = subtest_args; slist; slist = slist->next) |
348 | argv[i++] = (gchar*) slist->data; |
349 | /* argv[i++] = "--debug-log"; */ |
350 | if (subtest_quiet) |
351 | argv[i++] = "--quiet" ; |
352 | if (subtest_verbose) |
353 | argv[i++] = "--verbose" ; |
354 | if (!subtest_mode_fatal) |
355 | argv[i++] = "--keep-going" ; |
356 | if (subtest_mode_quick) |
357 | argv[i++] = "-m=quick" ; |
358 | else |
359 | argv[i++] = "-m=slow" ; |
360 | if (subtest_mode_perf) |
361 | argv[i++] = "-m=perf" ; |
362 | if (!subtest_mode_undefined) |
363 | argv[i++] = "-m=no-undefined" ; |
364 | if (gtester_list_tests) |
365 | argv[i++] = "-l" ; |
366 | if (subtest_seedstr) |
367 | argv[i++] = queue_gfree (slistp: &free_list, string: g_strdup_printf (format: "--seed=%s" , subtest_seedstr)); |
368 | argv[i++] = queue_gfree (slistp: &free_list, string: g_strdup_printf (format: "--GTestLogFD=%u" , report_pipe[1])); |
369 | if (skip_tests) |
370 | argv[i++] = queue_gfree (slistp: &free_list, string: g_strdup_printf (format: "--GTestSkipCount=%u" , skip_tests)); |
371 | for (slist = subtest_paths; slist; slist = slist->next) |
372 | argv[i++] = queue_gfree (slistp: &free_list, string: g_strdup_printf (format: "-p=%s" , (gchar*) slist->data)); |
373 | for (slist = skipped_paths; slist; slist = slist->next) |
374 | argv[i++] = queue_gfree (slistp: &free_list, string: g_strdup_printf (format: "-s=%s" , (gchar*) slist->data)); |
375 | argv[i++] = NULL; |
376 | |
377 | g_spawn_async_with_pipes (NULL, /* g_get_current_dir() */ |
378 | argv: (gchar**) argv, |
379 | NULL, /* envp */ |
380 | flags: G_SPAWN_DO_NOT_REAP_CHILD, /* G_SPAWN_SEARCH_PATH */ |
381 | child_setup: unset_cloexec_fdp, user_data: &report_pipe[1], /* pre-exec callback */ |
382 | child_pid: &pid, |
383 | NULL, /* standard_input */ |
384 | NULL, /* standard_output */ |
385 | NULL, /* standard_error */ |
386 | error: &error); |
387 | g_slist_foreach (list: free_list, func: (void(*)(void*,void*)) g_free, NULL); |
388 | g_slist_free (list: free_list); |
389 | free_list = NULL; |
390 | close (fd: report_pipe[1]); |
391 | |
392 | if (!gtester_quiet) |
393 | g_print (format: "(pid=%lu)\n" , (unsigned long) pid); |
394 | |
395 | if (error) |
396 | { |
397 | close (fd: report_pipe[0]); |
398 | if (subtest_mode_fatal) |
399 | g_error ("Failed to execute test binary: %s: %s" , argv[0], error->message); |
400 | else |
401 | g_warning ("Failed to execute test binary: %s: %s" , argv[0], error->message); |
402 | g_clear_error (err: &error); |
403 | g_free (mem: argv); |
404 | return FALSE; |
405 | } |
406 | g_free (mem: argv); |
407 | |
408 | subtest_running = TRUE; |
409 | subtest_io_pending = TRUE; |
410 | tlb = g_test_log_buffer_new(); |
411 | if (report_pipe[0] >= 0) |
412 | { |
413 | ioc_report = g_io_channel_unix_new (fd: report_pipe[0]); |
414 | g_io_channel_set_flags (channel: ioc_report, flags: G_IO_FLAG_NONBLOCK, NULL); |
415 | g_io_channel_set_encoding (channel: ioc_report, NULL, NULL); |
416 | g_io_channel_set_buffered (channel: ioc_report, FALSE); |
417 | child_report_cb_id = g_io_add_watch_full (channel: ioc_report, G_PRIORITY_DEFAULT - 1, condition: G_IO_IN | G_IO_ERR | G_IO_HUP, func: child_report_cb, user_data: tlb, NULL); |
418 | g_io_channel_unref (channel: ioc_report); |
419 | } |
420 | g_child_watch_add_full (G_PRIORITY_DEFAULT + 1, pid, function: child_watch_cb, NULL, NULL); |
421 | |
422 | loop_pending = g_main_context_pending (NULL); |
423 | while (subtest_running || /* FALSE once child exits */ |
424 | subtest_io_pending || /* FALSE once ioc_report closes */ |
425 | loop_pending) /* TRUE while idler, etc are running */ |
426 | { |
427 | /* g_print ("LOOPSTATE: subtest_running=%d subtest_io_pending=%d\n", subtest_running, subtest_io_pending); */ |
428 | /* check for unexpected hangs that are not signalled on report_pipe */ |
429 | if (!subtest_running && /* child exited */ |
430 | subtest_io_pending && /* no EOF detected on report_pipe */ |
431 | !loop_pending) /* no IO events pending however */ |
432 | break; |
433 | g_main_context_iteration (NULL, TRUE); |
434 | loop_pending = g_main_context_pending (NULL); |
435 | } |
436 | |
437 | if (subtest_io_pending) |
438 | g_source_remove (tag: child_report_cb_id); |
439 | |
440 | close (fd: report_pipe[0]); |
441 | g_test_log_buffer_free (tbuffer: tlb); |
442 | |
443 | return TRUE; |
444 | } |
445 | |
446 | static void |
447 | launch_test (const char *binary) |
448 | { |
449 | gboolean success = TRUE; |
450 | GTimer *btimer = g_timer_new(); |
451 | gboolean need_restart; |
452 | |
453 | testcase_count = 0; |
454 | if (!gtester_quiet) |
455 | g_print (format: "TEST: %s... " , binary); |
456 | |
457 | retry: |
458 | test_log_printfe (format: "%s<testbinary path=\"%s\">\n" , sindent (n: log_indent), binary); |
459 | log_indent += 2; |
460 | g_timer_start (timer: btimer); |
461 | subtest_exitstatus = 0; |
462 | success &= launch_test_binary (binary, skip_tests: testcase_count); |
463 | success &= subtest_exitstatus == 0; |
464 | need_restart = testcase_open != 0; |
465 | if (testcase_open) |
466 | testcase_close (duration: 0, exit_status: -256, n_forks: 0); |
467 | g_timer_stop (timer: btimer); |
468 | test_log_printfe (format: "%s<duration>%.6f</duration>\n" , sindent (n: log_indent), g_timer_elapsed (timer: btimer, NULL)); |
469 | log_indent -= 2; |
470 | test_log_printfe (format: "%s</testbinary>\n" , sindent (n: log_indent)); |
471 | g_free (mem: subtest_last_seed); |
472 | subtest_last_seed = NULL; |
473 | if (need_restart) |
474 | { |
475 | /* restart test binary, skipping processed test cases */ |
476 | goto retry; |
477 | } |
478 | |
479 | /* count the inability to run a test as a failure */ |
480 | if (!success && testcase_count == 0) |
481 | testcase_fail_count++; |
482 | |
483 | if (!gtester_quiet) |
484 | g_print (format: "%s: %s\n" , !success ? "FAIL" : "PASS" , binary); |
485 | g_timer_destroy (timer: btimer); |
486 | if (subtest_mode_fatal && !success) |
487 | terminate(); |
488 | } |
489 | |
490 | static void |
491 | usage (gboolean just_version) |
492 | { |
493 | if (just_version) |
494 | { |
495 | g_print (format: "gtester version %d.%d.%d\n" , GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION, GLIB_MICRO_VERSION); |
496 | return; |
497 | } |
498 | g_print (format: "Usage:\n" ); |
499 | g_print (format: "gtester [OPTIONS] testprogram...\n\n" ); |
500 | /* 12345678901234567890123456789012345678901234567890123456789012345678901234567890 */ |
501 | g_print (format: "Help Options:\n" ); |
502 | g_print (format: " -h, --help Show this help message\n\n" ); |
503 | g_print (format: "Utility Options:\n" ); |
504 | g_print (format: " -v, --version Print version information\n" ); |
505 | g_print (format: " --g-fatal-warnings Make warnings fatal (abort)\n" ); |
506 | g_print (format: " -k, --keep-going Continue running after tests failed\n" ); |
507 | g_print (format: " -l List paths of available test cases\n" ); |
508 | g_print (format: " -m {perf|slow|thorough|quick} Run test cases according to mode\n" ); |
509 | g_print (format: " -m {undefined|no-undefined} Run test cases according to mode\n" ); |
510 | g_print (format: " -p=TESTPATH Only start test cases matching TESTPATH\n" ); |
511 | g_print (format: " -s=TESTPATH Skip test cases matching TESTPATH\n" ); |
512 | g_print (format: " --seed=SEEDSTRING Start tests with random seed SEEDSTRING\n" ); |
513 | g_print (format: " -o=LOGFILE Write the test log to LOGFILE\n" ); |
514 | g_print (format: " -q, --quiet Suppress per test binary output\n" ); |
515 | g_print (format: " --verbose Report success per testcase\n" ); |
516 | } |
517 | |
518 | static void |
519 | parse_args (gint *argc_p, |
520 | gchar ***argv_p) |
521 | { |
522 | guint argc = *argc_p; |
523 | gchar **argv = *argv_p; |
524 | guint i, e; |
525 | /* parse known args */ |
526 | for (i = 1; i < argc; i++) |
527 | { |
528 | if (strcmp (s1: argv[i], s2: "--g-fatal-warnings" ) == 0) |
529 | { |
530 | GLogLevelFlags fatal_mask = (GLogLevelFlags) g_log_set_always_fatal (fatal_mask: (GLogLevelFlags) G_LOG_FATAL_MASK); |
531 | fatal_mask = (GLogLevelFlags) (fatal_mask | G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL); |
532 | g_log_set_always_fatal (fatal_mask); |
533 | argv[i] = NULL; |
534 | } |
535 | else if (strcmp (s1: argv[i], s2: "--gtester-selftest" ) == 0) |
536 | { |
537 | gtester_selftest = TRUE; |
538 | argv[i] = NULL; |
539 | break; /* stop parsing regular gtester arguments */ |
540 | } |
541 | else if (strcmp (s1: argv[i], s2: "-h" ) == 0 || strcmp (s1: argv[i], s2: "--help" ) == 0) |
542 | { |
543 | usage (FALSE); |
544 | exit (status: 0); |
545 | argv[i] = NULL; |
546 | } |
547 | else if (strcmp (s1: argv[i], s2: "-v" ) == 0 || strcmp (s1: argv[i], s2: "--version" ) == 0) |
548 | { |
549 | usage (TRUE); |
550 | exit (status: 0); |
551 | argv[i] = NULL; |
552 | } |
553 | else if (strcmp (s1: argv[i], s2: "--keep-going" ) == 0 || |
554 | strcmp (s1: argv[i], s2: "-k" ) == 0) |
555 | { |
556 | subtest_mode_fatal = FALSE; |
557 | argv[i] = NULL; |
558 | } |
559 | else if (strcmp (s1: "-p" , s2: argv[i]) == 0 || strncmp (s1: "-p=" , s2: argv[i], n: 3) == 0) |
560 | { |
561 | gchar *equal = argv[i] + 2; |
562 | if (*equal == '=') |
563 | subtest_paths = g_slist_prepend (list: subtest_paths, data: equal + 1); |
564 | else if (i + 1 < argc) |
565 | { |
566 | argv[i++] = NULL; |
567 | subtest_paths = g_slist_prepend (list: subtest_paths, data: argv[i]); |
568 | } |
569 | argv[i] = NULL; |
570 | } |
571 | else if (strcmp (s1: "-s" , s2: argv[i]) == 0 || strncmp (s1: "-s=" , s2: argv[i], n: 3) == 0) |
572 | { |
573 | gchar *equal = argv[i] + 2; |
574 | if (*equal == '=') |
575 | skipped_paths = g_slist_prepend (list: skipped_paths, data: equal + 1); |
576 | else if (i + 1 < argc) |
577 | { |
578 | argv[i++] = NULL; |
579 | skipped_paths = g_slist_prepend (list: skipped_paths, data: argv[i]); |
580 | } |
581 | argv[i] = NULL; |
582 | } |
583 | else if (strcmp (s1: "--test-arg" , s2: argv[i]) == 0 || strncmp (s1: "--test-arg=" , s2: argv[i], n: 11) == 0) |
584 | { |
585 | gchar *equal = argv[i] + 10; |
586 | if (*equal == '=') |
587 | subtest_args = g_slist_prepend (list: subtest_args, data: equal + 1); |
588 | else if (i + 1 < argc) |
589 | { |
590 | argv[i++] = NULL; |
591 | subtest_args = g_slist_prepend (list: subtest_args, data: argv[i]); |
592 | } |
593 | argv[i] = NULL; |
594 | } |
595 | else if (strcmp (s1: "-o" , s2: argv[i]) == 0 || strncmp (s1: "-o=" , s2: argv[i], n: 3) == 0) |
596 | { |
597 | gchar *equal = argv[i] + 2; |
598 | if (*equal == '=') |
599 | output_filename = equal + 1; |
600 | else if (i + 1 < argc) |
601 | { |
602 | argv[i++] = NULL; |
603 | output_filename = argv[i]; |
604 | } |
605 | argv[i] = NULL; |
606 | } |
607 | else if (strcmp (s1: "-m" , s2: argv[i]) == 0 || strncmp (s1: "-m=" , s2: argv[i], n: 3) == 0) |
608 | { |
609 | gchar *equal = argv[i] + 2; |
610 | const gchar *mode = "" ; |
611 | if (*equal == '=') |
612 | mode = equal + 1; |
613 | else if (i + 1 < argc) |
614 | { |
615 | argv[i++] = NULL; |
616 | mode = argv[i]; |
617 | } |
618 | if (strcmp (s1: mode, s2: "perf" ) == 0) |
619 | subtest_mode_perf = TRUE; |
620 | else if (strcmp (s1: mode, s2: "slow" ) == 0 || strcmp (s1: mode, s2: "thorough" ) == 0) |
621 | subtest_mode_quick = FALSE; |
622 | else if (strcmp (s1: mode, s2: "quick" ) == 0) |
623 | { |
624 | subtest_mode_quick = TRUE; |
625 | subtest_mode_perf = FALSE; |
626 | } |
627 | else if (strcmp (s1: mode, s2: "undefined" ) == 0) |
628 | subtest_mode_undefined = TRUE; |
629 | else if (strcmp (s1: mode, s2: "no-undefined" ) == 0) |
630 | subtest_mode_undefined = FALSE; |
631 | else |
632 | g_error ("unknown test mode: -m %s" , mode); |
633 | argv[i] = NULL; |
634 | } |
635 | else if (strcmp (s1: "-q" , s2: argv[i]) == 0 || strcmp (s1: "--quiet" , s2: argv[i]) == 0) |
636 | { |
637 | gtester_quiet = TRUE; |
638 | gtester_verbose = FALSE; |
639 | argv[i] = NULL; |
640 | } |
641 | else if (strcmp (s1: "--verbose" , s2: argv[i]) == 0) |
642 | { |
643 | gtester_quiet = FALSE; |
644 | gtester_verbose = TRUE; |
645 | argv[i] = NULL; |
646 | } |
647 | else if (strcmp (s1: "-l" , s2: argv[i]) == 0) |
648 | { |
649 | gtester_list_tests = TRUE; |
650 | argv[i] = NULL; |
651 | } |
652 | else if (strcmp (s1: "--seed" , s2: argv[i]) == 0 || strncmp (s1: "--seed=" , s2: argv[i], n: 7) == 0) |
653 | { |
654 | gchar *equal = argv[i] + 6; |
655 | if (*equal == '=') |
656 | subtest_seedstr = equal + 1; |
657 | else if (i + 1 < argc) |
658 | { |
659 | argv[i++] = NULL; |
660 | subtest_seedstr = argv[i]; |
661 | } |
662 | argv[i] = NULL; |
663 | } |
664 | else if (strcmp (s1: "--i-know-this-is-deprecated" , s2: argv[i]) == 0) |
665 | { |
666 | gtester_ignore_deprecation = TRUE; |
667 | argv[i] = NULL; |
668 | } |
669 | } |
670 | /* collapse argv */ |
671 | e = 1; |
672 | for (i = 1; i < argc; i++) |
673 | if (argv[i]) |
674 | { |
675 | argv[e++] = argv[i]; |
676 | if (i >= e) |
677 | argv[i] = NULL; |
678 | } |
679 | *argc_p = e; |
680 | } |
681 | |
682 | int |
683 | main (int argc, |
684 | char **argv) |
685 | { |
686 | gint ui; |
687 | |
688 | g_set_prgname (prgname: argv[0]); |
689 | parse_args (argc_p: &argc, argv_p: &argv); |
690 | if (gtester_selftest) |
691 | return main_selftest (argc, argv); |
692 | |
693 | if (argc <= 1) |
694 | { |
695 | usage (FALSE); |
696 | return 1; |
697 | } |
698 | |
699 | if (!gtester_ignore_deprecation) |
700 | g_warning ("Deprecated: Since GLib 2.62, gtester and gtester-report are " |
701 | "deprecated. Port to TAP." ); |
702 | |
703 | if (output_filename) |
704 | { |
705 | int errsv; |
706 | log_fd = g_open (file: output_filename, O_WRONLY | O_CREAT | O_TRUNC, 0666); |
707 | errsv = errno; |
708 | if (log_fd < 0) |
709 | g_error ("Failed to open log file '%s': %s" , output_filename, g_strerror (errsv)); |
710 | } |
711 | |
712 | test_log_printfe (format: "<?xml version=\"1.0\"?>\n" ); |
713 | test_log_printfe (format: "<!-- Deprecated: Since GLib 2.62, gtester and " |
714 | "gtester-report are deprecated. Port to TAP. -->\n" ); |
715 | test_log_printfe (format: "%s<gtester>\n" , sindent (n: log_indent)); |
716 | log_indent += 2; |
717 | for (ui = 1; ui < argc; ui++) |
718 | { |
719 | const char *binary = argv[ui]; |
720 | launch_test (binary); |
721 | /* we only get here on success or if !subtest_mode_fatal */ |
722 | } |
723 | log_indent -= 2; |
724 | test_log_printfe (format: "%s</gtester>\n" , sindent (n: log_indent)); |
725 | |
726 | close (fd: log_fd); |
727 | |
728 | return testcase_fail_count == 0 ? EXIT_SUCCESS : EXIT_FAILURE; |
729 | } |
730 | |
731 | static void |
732 | fixture_setup (guint *fix, |
733 | gconstpointer test_data) |
734 | { |
735 | g_assert_cmphex (*fix, ==, 0); |
736 | *fix = 0xdeadbeef; |
737 | } |
738 | static void |
739 | fixture_test (guint *fix, |
740 | gconstpointer test_data) |
741 | { |
742 | g_assert_cmphex (*fix, ==, 0xdeadbeef); |
743 | g_test_message (format: "This is a test message API test message." ); |
744 | g_test_bug_base (uri_pattern: "http://www.example.com/bugtracker/" ); |
745 | g_test_bug (bug_uri_snippet: "123" ); |
746 | g_test_bug_base (uri_pattern: "http://www.example.com/bugtracker?bugnum=%s;cmd=showbug" ); |
747 | g_test_bug (bug_uri_snippet: "456" ); |
748 | } |
749 | static void |
750 | fixture_teardown (guint *fix, |
751 | gconstpointer test_data) |
752 | { |
753 | g_assert_cmphex (*fix, ==, 0xdeadbeef); |
754 | } |
755 | |
756 | static int |
757 | main_selftest (int argc, |
758 | char **argv) |
759 | { |
760 | /* gtester main() for --gtester-selftest invocations */ |
761 | g_test_init (argc: &argc, argv: &argv, NULL); |
762 | g_test_add ("/gtester/fixture-test" , guint, NULL, fixture_setup, fixture_test, fixture_teardown); |
763 | return g_test_run(); |
764 | } |
765 | |