soup-multipart-input-stream: belatedly add .h file to soup.h
[platform/upstream/libsoup.git] / tests / test-utils.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 #include "test-utils.h"
4
5 #include <glib/gprintf.h>
6
7 #include <locale.h>
8 #include <signal.h>
9
10 #ifdef HAVE_APACHE
11 static gboolean apache_running;
12 #endif
13
14 static SoupLogger *logger;
15
16 int debug_level, errors;
17 gboolean parallelize = TRUE;
18 gboolean expect_warning, tls_available;
19 static int http_debug_level;
20
21 static gboolean
22 increment_debug_level (const char *option_name, const char *value,
23                        gpointer data, GError **error)
24 {
25         debug_level++;
26         return TRUE;
27 }
28
29 static gboolean
30 increment_http_debug_level (const char *option_name, const char *value,
31                             gpointer data, GError **error)
32 {
33         http_debug_level++;
34         return TRUE;
35 }
36
37 static GOptionEntry debug_entry[] = {
38         { "debug", 'd', G_OPTION_FLAG_NO_ARG,
39           G_OPTION_ARG_CALLBACK, increment_debug_level,
40           "Enable (or increase) test-specific debugging", NULL },
41         { "parallel", 'p', G_OPTION_FLAG_REVERSE,
42           G_OPTION_ARG_NONE, &parallelize,
43           "Toggle parallelization (default is on, unless -d or -h)", NULL },
44         { "http-debug", 'h', G_OPTION_FLAG_NO_ARG,
45           G_OPTION_ARG_CALLBACK, increment_http_debug_level,
46           "Enable (or increase) HTTP-level debugging", NULL },
47         { NULL }
48 };
49
50 static void
51 quit (int sig)
52 {
53 #ifdef HAVE_APACHE
54         if (apache_running)
55                 apache_cleanup ();
56 #endif
57
58         exit (1);
59 }
60
61 static void
62 test_log_handler (const char *log_domain, GLogLevelFlags log_level,
63                   const char *message, gpointer user_data)
64 {
65         if (log_level & (G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL)) {
66                 if (expect_warning) {
67                         expect_warning = FALSE;
68                         debug_printf (2, "Got expected warning: %s\n", message);
69                         return;
70                 } else
71                         errors++;
72         }
73         g_log_default_handler (log_domain, log_level, message, user_data);
74 }
75
76 void
77 test_init (int argc, char **argv, GOptionEntry *entries)
78 {
79         GOptionContext *opts;
80         char *name;
81         GError *error = NULL;
82         GTlsBackend *tls_backend;
83
84         setlocale (LC_ALL, "");
85
86         name = strrchr (argv[0], '/');
87         if (!name++)
88                 name = argv[0];
89         if (!strncmp (name, "lt-", 3))
90                 name += 3;
91         g_set_prgname (name);
92
93         opts = g_option_context_new (NULL);
94         g_option_context_add_main_entries (opts, debug_entry, NULL);
95         if (entries)
96                 g_option_context_add_main_entries (opts, entries, NULL);
97
98         if (!g_option_context_parse (opts, &argc, &argv, &error)) {
99                 g_printerr ("Could not parse arguments: %s\n",
100                             error->message);
101                 g_printerr ("%s",
102                             g_option_context_get_help (opts, TRUE, NULL));
103                 exit (1);
104         }
105         g_option_context_free (opts);
106
107         if (debug_level > 0 || http_debug_level > 0)
108                 parallelize = !parallelize;
109
110         /* Exit cleanly on ^C in case we're valgrinding. */
111         signal (SIGINT, quit);
112
113         g_log_set_default_handler (test_log_handler, NULL);
114
115         tls_backend = g_tls_backend_get_default ();
116         tls_available = g_tls_backend_supports_tls (tls_backend);
117 }
118
119 void
120 test_cleanup (void)
121 {
122 #ifdef HAVE_APACHE
123         if (apache_running)
124                 apache_cleanup ();
125 #endif
126
127         if (logger)
128                 g_object_unref (logger);
129
130         g_main_context_unref (g_main_context_default ());
131
132         debug_printf (1, "\n");
133         if (errors) {
134                 g_print ("%s: %d error(s).%s\n",
135                          g_get_prgname (), errors,
136                          debug_level == 0 ? " Run with '-d' for details" : "");
137         } else
138                 g_print ("%s: OK\n", g_get_prgname ());
139 }
140
141 void
142 debug_printf (int level, const char *format, ...)
143 {
144         va_list args;
145
146         if (debug_level < level)
147                 return;
148
149         va_start (args, format);
150         g_vprintf (format, args);
151         va_end (args);
152 }
153
154 #ifdef HAVE_APACHE
155
156 static gboolean
157 apache_cmd (const char *cmd)
158 {
159         const char *argv[8];
160         char *cwd, *conf;
161         int status;
162         gboolean ok;
163
164         cwd = g_get_current_dir ();
165         conf = g_build_filename (cwd, "httpd.conf", NULL);
166
167         argv[0] = APACHE_HTTPD;
168         argv[1] = "-d";
169         argv[2] = cwd;
170         argv[3] = "-f";
171         argv[4] = conf;
172         argv[5] = "-k";
173         argv[6] = cmd;
174         argv[7] = NULL;
175
176         ok = g_spawn_sync (cwd, (char **)argv, NULL, 0, NULL, NULL,
177                            NULL, NULL, &status, NULL);
178         if (ok)
179                 ok = (status == 0);
180
181         g_free (cwd);
182         g_free (conf);
183
184         return ok;
185 }
186
187 void
188 apache_init (void)
189 {
190         if (!apache_cmd ("start")) {
191                 g_printerr ("Could not start apache\n");
192                 exit (1);
193         }
194         apache_running = TRUE;
195 }
196
197 void
198 apache_cleanup (void)
199 {
200         pid_t pid;
201         char *contents;
202
203         if (g_file_get_contents ("httpd.pid", &contents, NULL, NULL)) {
204                 pid = strtoul (contents, NULL, 10);
205                 g_free (contents);
206         } else
207                 pid = 0;
208
209         if (!apache_cmd ("graceful-stop"))
210                 return;
211         apache_running = FALSE;
212
213         if (pid) {
214                 while (kill (pid, 0) == 0)
215                         g_usleep (100);
216         }
217 }
218
219 #endif /* HAVE_APACHE */
220
221 SoupSession *
222 soup_test_session_new (GType type, ...)
223 {
224         va_list args;
225         const char *propname;
226         SoupSession *session;
227
228         va_start (args, type);
229         propname = va_arg (args, const char *);
230         session = (SoupSession *)g_object_new_valist (type, propname, args);
231         va_end (args);
232
233         g_object_set (G_OBJECT (session),
234                       SOUP_SESSION_SSL_CA_FILE, SRCDIR "/test-cert.pem",
235                       NULL);
236
237         if (http_debug_level && !logger) {
238                 SoupLoggerLogLevel level = MIN ((SoupLoggerLogLevel)http_debug_level, SOUP_LOGGER_LOG_BODY);
239
240                 logger = soup_logger_new (level, -1);
241         }
242
243         if (logger)
244                 soup_session_add_feature (session, SOUP_SESSION_FEATURE (logger));
245
246         return session;
247 }
248
249 void
250 soup_test_session_abort_unref (SoupSession *session)
251 {
252         g_object_add_weak_pointer (G_OBJECT (session), (gpointer *)&session);
253
254         soup_session_abort (session);
255         g_object_unref (session);
256
257         if (session) {
258                 errors++;
259                 debug_printf (1, "leaked SoupSession!\n");
260                 g_object_remove_weak_pointer (G_OBJECT (session), (gpointer *)&session);
261         }
262 }
263
264 static gpointer run_server_thread (gpointer user_data);
265
266 static SoupServer *
267 test_server_new (gboolean in_own_thread, gboolean ssl)
268 {
269         SoupServer *server;
270         GMainContext *async_context;
271         const char *ssl_cert_file, *ssl_key_file;
272         SoupAddress *addr;
273
274         async_context = in_own_thread ? g_main_context_new () : NULL;
275
276         if (ssl) {
277                 ssl_cert_file = SRCDIR "/test-cert.pem";
278                 ssl_key_file = SRCDIR "/test-key.pem";
279         } else
280                 ssl_cert_file = ssl_key_file = NULL;
281
282         addr = soup_address_new ("127.0.0.1", SOUP_ADDRESS_ANY_PORT);
283         soup_address_resolve_sync (addr, NULL);
284
285         server = soup_server_new (SOUP_SERVER_INTERFACE, addr,
286                                   SOUP_SERVER_ASYNC_CONTEXT, async_context,
287                                   SOUP_SERVER_SSL_CERT_FILE, ssl_cert_file,
288                                   SOUP_SERVER_SSL_KEY_FILE, ssl_key_file,
289                                   NULL);
290         g_object_unref (addr);
291         if (async_context)
292                 g_main_context_unref (async_context);
293
294         if (!server) {
295                 g_printerr ("Unable to create server\n");
296                 exit (1);
297         }
298
299         if (in_own_thread) {
300                 GThread *thread;
301
302                 thread = g_thread_new ("server_thread", run_server_thread, server);
303                 g_object_set_data (G_OBJECT (server), "thread", thread);
304         } else
305                 soup_server_run_async (server);
306
307         return server;
308 }
309
310 SoupServer *
311 soup_test_server_new (gboolean in_own_thread)
312 {
313         return test_server_new (in_own_thread, FALSE);
314 }
315
316 SoupServer *
317 soup_test_server_new_ssl (gboolean in_own_thread)
318 {
319         return test_server_new (in_own_thread, TRUE);
320 }
321
322 static gpointer
323 run_server_thread (gpointer user_data)
324 {
325         SoupServer *server = user_data;
326
327         soup_server_run (server);
328         return NULL;
329 }
330
331 static gboolean
332 idle_quit_server (gpointer server)
333 {
334         soup_server_quit (server);
335         return FALSE;
336 }
337
338 void
339 soup_test_server_quit_unref (SoupServer *server)
340 {
341         GThread *thread;
342
343         g_object_add_weak_pointer (G_OBJECT (server),
344                                    (gpointer *)&server);
345
346         thread = g_object_get_data (G_OBJECT (server), "thread");
347         if (thread) {
348                 soup_add_completion (soup_server_get_async_context (server),
349                                      idle_quit_server, server);
350                 g_thread_join (thread);
351         } else
352                 soup_server_quit (server);
353         g_object_unref (server);
354
355         if (server) {
356                 errors++;
357                 debug_printf (1, "leaked SoupServer!\n");
358                 g_object_remove_weak_pointer (G_OBJECT (server),
359                                               (gpointer *)&server);
360         }
361 }
362
363 typedef struct {
364         GMainLoop *loop;
365         GAsyncResult *result;
366 } AsyncAsSyncData;
367
368 static void
369 async_as_sync_callback (GObject      *object,
370                         GAsyncResult *result,
371                         gpointer      user_data)
372 {
373         AsyncAsSyncData *data = user_data;
374
375         data->result = g_object_ref (result);
376         g_main_loop_quit (data->loop);
377 }
378
379 GInputStream *
380 soup_test_request_send (SoupRequest   *req,
381                         GCancellable  *cancellable,
382                         GError       **error)
383 {
384         AsyncAsSyncData data;
385         GInputStream *stream;
386
387         if (SOUP_IS_SESSION_SYNC (soup_request_get_session (req)))
388                 return soup_request_send (req, cancellable, error);
389
390         data.loop = g_main_loop_new (g_main_context_get_thread_default (), FALSE);
391
392         soup_request_send_async (req, cancellable, async_as_sync_callback, &data);
393         g_main_loop_run (data.loop);
394
395         stream = soup_request_send_finish (req, data.result, error);
396
397         g_main_loop_unref (data.loop);
398         g_object_unref (data.result);
399
400         return stream;
401 }
402
403 gboolean
404 soup_test_request_close_stream (SoupRequest   *req,
405                                 GInputStream  *stream,
406                                 GCancellable  *cancellable,
407                                 GError       **error)
408 {
409         AsyncAsSyncData data;
410         gboolean ok;
411
412         if (SOUP_IS_SESSION_SYNC (soup_request_get_session (req)))
413                 return g_input_stream_close (stream, cancellable, error);
414
415         data.loop = g_main_loop_new (g_main_context_get_thread_default (), FALSE);
416
417         g_input_stream_close_async (stream, G_PRIORITY_DEFAULT, cancellable,
418                                     async_as_sync_callback, &data);
419         g_main_loop_run (data.loop);
420
421         ok = g_input_stream_close_finish (stream, data.result, error);
422
423         g_main_loop_unref (data.loop);
424         g_object_unref (data.result);
425
426         return ok;
427 }