1 | /* This file is part of GLib |
2 | * |
3 | * Copyright (C) 2010 Sven Herzberg |
4 | * |
5 | * This work is provided "as is"; redistribution and modification |
6 | * in whole or in part, in any medium, physical or electronic is |
7 | * permitted without restriction. |
8 | * |
9 | * This work 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. |
12 | * |
13 | * In no event shall the authors or contributors be liable for any |
14 | * direct, indirect, incidental, special, exemplary, or consequential |
15 | * damages (including, but not limited to, procurement of substitute |
16 | * goods or services; loss of use, data, or profits; or business |
17 | * interruption) however caused and on any theory of liability, whether |
18 | * in contract, strict liability, or tort (including negligence or |
19 | * otherwise) arising in any way out of the use of this software, even |
20 | * if advised of the possibility of such damage. |
21 | */ |
22 | |
23 | #include <errno.h> /* errno */ |
24 | #include <glib.h> |
25 | #ifdef G_OS_UNIX |
26 | #include <unistd.h> /* pipe() */ |
27 | #endif |
28 | #ifdef G_OS_WIN32 |
29 | #include <io.h> |
30 | #include <fcntl.h> |
31 | #define pipe(fds) _pipe(fds, 4096, _O_BINARY) |
32 | #endif |
33 | |
34 | static const char *argv0; |
35 | |
36 | static void |
37 | debug (void) |
38 | { |
39 | if (g_test_subprocess ()) |
40 | g_debug ("this is a regular g_debug() from the test suite" ); |
41 | } |
42 | |
43 | static void |
44 | info (void) |
45 | { |
46 | if (g_test_subprocess ()) |
47 | g_info ("this is a regular g_info from the test suite" ); |
48 | } |
49 | |
50 | static void |
51 | message (void) |
52 | { |
53 | if (g_test_subprocess ()) |
54 | g_message ("this is a regular g_message() from the test suite" ); |
55 | } |
56 | |
57 | static void |
58 | warning (void) |
59 | { |
60 | if (g_test_subprocess ()) |
61 | g_warning ("this is a regular g_warning() from the test suite" ); |
62 | } |
63 | |
64 | static void |
65 | critical (void) |
66 | { |
67 | if (g_test_subprocess ()) |
68 | g_critical ("this is a regular g_critical() from the test suite" ); |
69 | } |
70 | |
71 | static void |
72 | error (void) |
73 | { |
74 | if (g_test_subprocess ()) |
75 | g_error ("this is a regular g_error() from the test suite" ); |
76 | } |
77 | |
78 | static void |
79 | gtest_message (void) |
80 | { |
81 | if (g_test_subprocess ()) |
82 | g_test_message (format: "this is a regular g_test_message() from the test suite" ); |
83 | } |
84 | |
85 | static gboolean |
86 | test_message_cb1 (GIOChannel * channel, |
87 | GIOCondition condition, |
88 | gpointer user_data) |
89 | { |
90 | GIOStatus status; |
91 | guchar buf[512]; |
92 | gsize read_bytes = 0; |
93 | |
94 | g_assert_cmpuint (condition, ==, G_IO_IN); |
95 | |
96 | for (status = g_io_channel_read_chars (channel, buf: (gchar*)buf, count: sizeof (buf), bytes_read: &read_bytes, NULL); |
97 | status == G_IO_STATUS_NORMAL; |
98 | status = g_io_channel_read_chars (channel, buf: (gchar*)buf, count: sizeof (buf), bytes_read: &read_bytes, NULL)) |
99 | { |
100 | g_test_log_buffer_push (tbuffer: user_data, n_bytes: read_bytes, bytes: buf); |
101 | } |
102 | |
103 | g_assert_cmpuint (status, ==, G_IO_STATUS_AGAIN); |
104 | |
105 | return TRUE; |
106 | } |
107 | |
108 | static void |
109 | test_message_cb2 (GPid pid, |
110 | gint status, |
111 | gpointer user_data) |
112 | { |
113 | g_spawn_close_pid (pid); |
114 | |
115 | g_main_loop_quit (loop: user_data); |
116 | } |
117 | |
118 | static void |
119 | test_message (void) |
120 | { |
121 | gchar* argv[] = { |
122 | (gchar*)argv0, |
123 | NULL, |
124 | "--GTestSubprocess" , |
125 | "-p" , "/glib/testing/protocol/gtest-message" , |
126 | "-p" , "/glib/testing/protocol/message" , |
127 | "-p" , "/glib/testing/protocol/debug" , |
128 | NULL |
129 | }; |
130 | GTestLogBuffer* tlb; |
131 | GTestLogMsg * msg; |
132 | GIOChannel * channel; |
133 | GMainLoop * loop; |
134 | GError * error = NULL; |
135 | gulong child_source; |
136 | gulong io_source; |
137 | GPid pid = 0; |
138 | int pipes[2]; |
139 | int passed = 0; |
140 | int messages = 0; |
141 | const char * line_term; |
142 | int line_term_len; |
143 | |
144 | if (0 > pipe (pipedes: pipes)) |
145 | { |
146 | int errsv = errno; |
147 | g_error ("error creating pipe: %s" , g_strerror (errsv)); |
148 | } |
149 | |
150 | argv[1] = g_strdup_printf (format: "--GTestLogFD=%u" , pipes[1]); |
151 | |
152 | if (!g_spawn_async (NULL, |
153 | argv, NULL, |
154 | flags: G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_LEAVE_DESCRIPTORS_OPEN | |
155 | G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL, |
156 | NULL, NULL, child_pid: &pid, |
157 | error: &error)) |
158 | { |
159 | g_error ("error spawning the test: %s" , error->message); |
160 | } |
161 | |
162 | tlb = g_test_log_buffer_new (); |
163 | loop = g_main_loop_new (NULL, FALSE); |
164 | |
165 | #ifdef G_OS_WIN32 |
166 | channel = g_io_channel_win32_new_fd (pipes[0]); |
167 | #else |
168 | channel = g_io_channel_unix_new (fd: pipes[0]); |
169 | #endif |
170 | g_io_channel_set_close_on_unref (channel, TRUE); |
171 | g_io_channel_set_encoding (channel, NULL, NULL); |
172 | g_io_channel_set_buffered (channel, FALSE); |
173 | g_io_channel_set_flags (channel, flags: G_IO_FLAG_NONBLOCK, NULL); |
174 | g_assert (g_io_channel_get_line_term (channel, NULL) == NULL); |
175 | g_io_channel_set_line_term (channel, line_term: "\n" , length: 1); |
176 | line_term = g_io_channel_get_line_term (channel, length: &line_term_len); |
177 | g_assert_cmpint (*line_term, ==, '\n'); |
178 | g_assert_cmpint (line_term_len, ==, 1); |
179 | |
180 | g_assert (g_io_channel_get_close_on_unref (channel)); |
181 | g_assert (g_io_channel_get_encoding (channel) == NULL); |
182 | g_assert (!g_io_channel_get_buffered (channel)); |
183 | |
184 | io_source = g_io_add_watch (channel, condition: G_IO_IN, func: test_message_cb1, user_data: tlb); |
185 | child_source = g_child_watch_add (pid, function: test_message_cb2, data: loop); |
186 | |
187 | g_main_loop_run (loop); |
188 | |
189 | test_message_cb1 (channel, condition: G_IO_IN, user_data: tlb); |
190 | |
191 | g_test_expect_message (log_domain: "GLib" , log_level: G_LOG_LEVEL_CRITICAL, pattern: "Source ID*" ); |
192 | g_assert (!g_source_remove (child_source)); |
193 | g_test_assert_expected_messages (); |
194 | g_assert (g_source_remove (io_source)); |
195 | g_io_channel_unref (channel); |
196 | |
197 | for (msg = g_test_log_buffer_pop (tbuffer: tlb); |
198 | msg; |
199 | msg = g_test_log_buffer_pop (tbuffer: tlb)) |
200 | { |
201 | switch (msg->log_type) |
202 | { |
203 | case G_TEST_LOG_START_BINARY: |
204 | case G_TEST_LOG_START_CASE: |
205 | case G_TEST_LOG_START_SUITE: |
206 | case G_TEST_LOG_STOP_SUITE: |
207 | /* ignore */ |
208 | break; |
209 | case G_TEST_LOG_STOP_CASE: |
210 | passed++; |
211 | break; |
212 | case G_TEST_LOG_MESSAGE: |
213 | { |
214 | gchar const* known_messages[] = { |
215 | "this is a regular g_test_message() from the test suite" , |
216 | "GLib-MESSAGE: this is a regular g_message() from the test suite" , |
217 | "GLib-DEBUG: this is a regular g_debug() from the test suite" |
218 | }; |
219 | g_assert_cmpint (messages, <, G_N_ELEMENTS (known_messages)); |
220 | g_assert_cmpstr (msg->strings[0], ==, known_messages[messages]); |
221 | messages++; |
222 | } |
223 | break; |
224 | case G_TEST_LOG_ERROR: |
225 | g_assert_not_reached (); |
226 | break; |
227 | default: |
228 | g_error ("unexpected log message type: %s" , g_test_log_type_name (msg->log_type)); |
229 | } |
230 | g_test_log_msg_free (tmsg: msg); |
231 | } |
232 | |
233 | g_assert_cmpint (passed, ==, 3); |
234 | g_assert_cmpint (messages, ==, 3); |
235 | |
236 | g_free (mem: argv[1]); |
237 | g_main_loop_unref (loop); |
238 | g_test_log_buffer_free (tbuffer: tlb); |
239 | } |
240 | |
241 | static void |
242 | test_error (void) |
243 | { |
244 | gchar* tests[] = { |
245 | "/glib/testing/protocol/warning" , |
246 | "/glib/testing/protocol/critical" , |
247 | "/glib/testing/protocol/error" |
248 | }; |
249 | gsize i; |
250 | int messages = 0; |
251 | |
252 | for (i = 0; i < G_N_ELEMENTS (tests); i++) |
253 | { |
254 | gchar* argv[] = { |
255 | (gchar*)argv0, |
256 | NULL, |
257 | "--GTestSubprocess" , |
258 | "-p" , tests[i], |
259 | NULL |
260 | }; |
261 | GTestLogBuffer* tlb; |
262 | GTestLogMsg * msg; |
263 | GIOChannel * channel; |
264 | GMainLoop * loop; |
265 | GError * error = NULL; |
266 | gulong child_source; |
267 | gulong io_source; |
268 | GPid pid = 0; |
269 | int pipes[2]; |
270 | |
271 | if (0 > pipe (pipedes: pipes)) |
272 | { |
273 | int errsv = errno; |
274 | g_error ("error creating pipe: %s" , g_strerror (errsv)); |
275 | } |
276 | |
277 | argv[1] = g_strdup_printf (format: "--GTestLogFD=%u" , pipes[1]); |
278 | |
279 | if (!g_spawn_async (NULL, |
280 | argv, NULL, |
281 | flags: G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_LEAVE_DESCRIPTORS_OPEN | |
282 | G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL, |
283 | NULL, NULL, child_pid: &pid, |
284 | error: &error)) |
285 | { |
286 | g_error ("error spawning the test: %s" , error->message); |
287 | } |
288 | |
289 | tlb = g_test_log_buffer_new (); |
290 | loop = g_main_loop_new (NULL, FALSE); |
291 | |
292 | #ifdef G_OS_WIN32 |
293 | channel = g_io_channel_win32_new_fd (pipes[0]); |
294 | #else |
295 | channel = g_io_channel_unix_new (fd: pipes[0]); |
296 | #endif |
297 | g_io_channel_set_close_on_unref (channel, TRUE); |
298 | g_io_channel_set_encoding (channel, NULL, NULL); |
299 | g_io_channel_set_buffered (channel, FALSE); |
300 | g_io_channel_set_flags (channel, flags: G_IO_FLAG_NONBLOCK, NULL); |
301 | |
302 | io_source = g_io_add_watch (channel, condition: G_IO_IN, func: test_message_cb1, user_data: tlb); |
303 | child_source = g_child_watch_add (pid, function: test_message_cb2, data: loop); |
304 | |
305 | g_main_loop_run (loop); |
306 | |
307 | test_message_cb1 (channel, condition: G_IO_IN, user_data: tlb); |
308 | |
309 | g_test_expect_message (log_domain: "GLib" , log_level: G_LOG_LEVEL_CRITICAL, pattern: "Source ID*" ); |
310 | g_assert (!g_source_remove (child_source)); |
311 | g_test_assert_expected_messages (); |
312 | g_assert (g_source_remove (io_source)); |
313 | g_io_channel_unref (channel); |
314 | |
315 | for (msg = g_test_log_buffer_pop (tbuffer: tlb); |
316 | msg; |
317 | msg = g_test_log_buffer_pop (tbuffer: tlb)) |
318 | { |
319 | switch (msg->log_type) |
320 | { |
321 | case G_TEST_LOG_START_BINARY: |
322 | case G_TEST_LOG_START_CASE: |
323 | case G_TEST_LOG_START_SUITE: |
324 | case G_TEST_LOG_STOP_SUITE: |
325 | /* ignore */ |
326 | break; |
327 | case G_TEST_LOG_STOP_CASE: |
328 | case G_TEST_LOG_MESSAGE: |
329 | g_assert_not_reached (); |
330 | break; |
331 | case G_TEST_LOG_ERROR: |
332 | { |
333 | gchar const* known_messages[] = { |
334 | "GLib-FATAL-WARNING: this is a regular g_warning() from the test suite" , |
335 | "GLib-FATAL-CRITICAL: this is a regular g_critical() from the test suite" , |
336 | "GLib-FATAL-ERROR: this is a regular g_error() from the test suite" |
337 | }; |
338 | g_assert_cmpint (messages, <, G_N_ELEMENTS (known_messages)); |
339 | g_assert_cmpstr (msg->strings[0], ==, known_messages[messages]); |
340 | messages++; |
341 | } |
342 | break; |
343 | default: |
344 | g_error ("unexpected log message type: %s" , g_test_log_type_name (msg->log_type)); |
345 | } |
346 | g_test_log_msg_free (tmsg: msg); |
347 | } |
348 | |
349 | g_free (mem: argv[1]); |
350 | g_main_loop_unref (loop); |
351 | g_test_log_buffer_free (tbuffer: tlb); |
352 | } |
353 | |
354 | g_assert_cmpint (messages, ==, 3); |
355 | } |
356 | |
357 | int |
358 | main (int argc, |
359 | char**argv) |
360 | { |
361 | argv0 = argv[0]; |
362 | |
363 | g_test_init (argc: &argc, argv: &argv, NULL); |
364 | |
365 | /* we use ourself as the testcase, these are the ones we need internally */ |
366 | g_test_add_func (testpath: "/glib/testing/protocol/debug" , test_func: debug); |
367 | g_test_add_func (testpath: "/glib/testing/protocol/info" , test_func: info); |
368 | g_test_add_func (testpath: "/glib/testing/protocol/message" , test_func: message); |
369 | g_test_add_func (testpath: "/glib/testing/protocol/warning" , test_func: warning); |
370 | g_test_add_func (testpath: "/glib/testing/protocol/critical" , test_func: critical); |
371 | g_test_add_func (testpath: "/glib/testing/protocol/error" , test_func: error); |
372 | g_test_add_func (testpath: "/glib/testing/protocol/gtest-message" , test_func: gtest_message); |
373 | |
374 | /* these are the real tests */ |
375 | g_test_add_func (testpath: "/glib/testing/protocol/test-message" , test_func: test_message); |
376 | g_test_add_func (testpath: "/glib/testing/protocol/test-error" , test_func: test_error); |
377 | |
378 | return g_test_run (); |
379 | } |
380 | |
381 | /* vim:set et sw=2 cino=t0,f0,(0,{s,>2s,n-1s,^-1s,e2s: */ |
382 | |