tests: do skipped tests properly
[platform/upstream/libsoup.git] / tests / xmlrpc-test.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2001-2003, Ximian, Inc.
4  */
5
6 #include "test-utils.h"
7
8 #ifdef HAVE_PHP_XMLRPC
9
10 #ifdef G_GNUC_BEGIN_IGNORE_DEPRECATIONS
11 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
12 #endif
13
14 static SoupSession *session;
15 static const char *default_uri = "http://127.0.0.1:47524/xmlrpc-server.php";
16 static const char *uri = NULL;
17 static gboolean server_test = FALSE;
18
19 static const char *const value_type[] = {
20         "BAD",
21         "int",
22         "boolean",
23         "string",
24         "double",
25         "datetime",
26         "base64",
27         "struct",
28         "array"
29 };
30
31 static gboolean
32 send_xmlrpc (const char *body, GValue *retval)
33 {
34         SoupMessage *msg;
35         GError *err = NULL;
36
37         msg = soup_message_new ("POST", uri);
38         soup_message_set_request (msg, "text/xml", SOUP_MEMORY_COPY,
39                                   body, strlen (body));
40         soup_session_send_message (session, msg);
41
42         if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
43                 debug_printf (1, "ERROR: %d %s\n", msg->status_code,
44                               msg->reason_phrase);
45                 g_object_unref (msg);
46                 return FALSE;
47         }
48
49         if (!soup_xmlrpc_parse_method_response (msg->response_body->data,
50                                                 msg->response_body->length,
51                                                 retval, &err)) {
52                 if (err) {
53                         debug_printf (1, "FAULT: %d %s\n", err->code, err->message);
54                         g_error_free (err);
55                 } else
56                         debug_printf (1, "ERROR: could not parse response\n");
57                 g_object_unref (msg);
58                 return FALSE;
59         }
60         g_object_unref (msg);
61
62         return TRUE;
63 }
64
65 static gboolean
66 do_xmlrpc (const char *method, GValue *retval, ...)
67 {
68         va_list args;
69         GValueArray *params;
70         char *body;
71         gboolean ret;
72
73         va_start (args, retval);
74         params = soup_value_array_from_args (args);
75         va_end (args);
76
77         body = soup_xmlrpc_build_method_call (method, params->values,
78                                               params->n_values);
79         g_value_array_free (params);
80         if (!body)
81                 return FALSE;
82
83         ret = send_xmlrpc (body, retval);
84         g_free (body);
85
86         return ret;
87 }
88
89 static gboolean
90 check_xmlrpc (GValue *value, GType type, ...)
91 {
92         va_list args;
93
94         if (!G_VALUE_HOLDS (value, type)) {
95                 debug_printf (1, "ERROR: could not parse response\n");
96                 g_value_unset (value);
97                 return FALSE;
98         }
99
100         va_start (args, type);
101         SOUP_VALUE_GETV (value, type, args);
102         va_end (args);
103         return TRUE;
104 }
105
106 static gboolean
107 test_sum (void)
108 {
109         GValueArray *ints;
110         int i, val, sum, result;
111         GValue retval;
112         gboolean ok;
113
114         debug_printf (1, "sum (array of int -> int): ");
115
116         ints = g_value_array_new (10);
117         for (i = sum = 0; i < 10; i++) {
118                 val = g_random_int_range (0, 100);
119                 debug_printf (2, "%s%d", i == 0 ? "[" : ", ", val);
120                 soup_value_array_append (ints, G_TYPE_INT, val);
121                 sum += val;
122         }
123         debug_printf (2, "] -> ");
124
125         ok = (do_xmlrpc ("sum", &retval,
126                         G_TYPE_VALUE_ARRAY, ints,
127                         G_TYPE_INVALID) &&
128               check_xmlrpc (&retval, G_TYPE_INT, &result));
129         g_value_array_free (ints);
130
131         if (!ok)
132                 return FALSE;
133
134         debug_printf (2, "%d: ", result);
135         debug_printf (1, "%s\n", result == sum ? "OK!" : "WRONG!");
136         return result == sum;
137 }
138
139 static gboolean
140 test_countBools (void)
141 {
142         GValueArray *bools;
143         int i, trues, falses;
144         GValue retval;
145         int ret_trues, ret_falses;
146         gboolean val, ok;
147         GHashTable *result;
148
149         debug_printf (1, "countBools (array of boolean -> struct of ints): ");
150
151         bools = g_value_array_new (10);
152         for (i = trues = falses = 0; i < 10; i++) {
153                 val = g_random_boolean ();
154                 debug_printf (2, "%s%c", i == 0 ? "[" : ", ", val ? 'T' : 'F');
155                 soup_value_array_append (bools, G_TYPE_BOOLEAN, val);
156                 if (val)
157                         trues++;
158                 else
159                         falses++;
160         }
161         debug_printf (2, "] -> ");
162
163         ok = (do_xmlrpc ("countBools", &retval,
164                          G_TYPE_VALUE_ARRAY, bools,
165                          G_TYPE_INVALID) &&
166               check_xmlrpc (&retval, G_TYPE_HASH_TABLE, &result));
167         g_value_array_free (bools);
168         if (!ok)
169                 return FALSE;
170
171         if (!soup_value_hash_lookup (result, "true", G_TYPE_INT, &ret_trues)) {
172                 debug_printf (1, "NO 'true' value in response\n");
173                 return FALSE;
174         }
175         if (!soup_value_hash_lookup (result, "false", G_TYPE_INT, &ret_falses)) {
176                 debug_printf (1, "NO 'false' value in response\n");
177                 return FALSE;
178         }
179         g_hash_table_destroy (result);
180
181         debug_printf (2, "{ true: %d, false: %d } ", ret_trues, ret_falses);
182         ok = (trues == ret_trues) && (falses == ret_falses);
183         debug_printf (1, "%s\n", ok ? "OK!" : "WRONG!");
184         return ok;
185 }
186
187 static gboolean
188 test_md5sum (void)
189 {
190         GByteArray *data, *result;
191         int i;
192         GChecksum *checksum;
193         guchar digest[16];
194         gsize digest_len = sizeof (digest);
195         GValue retval;
196         gboolean ok;
197
198         debug_printf (1, "md5sum (base64 -> base64): ");
199
200         data = g_byte_array_new ();
201         g_byte_array_set_size (data, 256);
202         for (i = 0; i < data->len; i++)
203                 data->data[i] = (char)(g_random_int_range (0, 256));
204
205         checksum = g_checksum_new (G_CHECKSUM_MD5);
206         g_checksum_update (checksum, data->data, data->len);
207         g_checksum_get_digest (checksum, digest, &digest_len);
208         g_checksum_free (checksum);
209
210         ok = (do_xmlrpc ("md5sum", &retval,
211                          SOUP_TYPE_BYTE_ARRAY, data,
212                          G_TYPE_INVALID) &&
213               check_xmlrpc (&retval, SOUP_TYPE_BYTE_ARRAY, &result));
214         g_byte_array_free (data, TRUE);
215         if (!ok)
216                 return FALSE;
217
218         if (result->len != digest_len) {
219                 debug_printf (1, "result has WRONG length (%d)\n", result->len);
220                 g_byte_array_free (result, TRUE);
221                 return FALSE;
222         }
223
224         ok = (memcmp (digest, result->data, digest_len) == 0);
225         debug_printf (1, "%s\n", ok ? "OK!" : "WRONG!");
226         g_byte_array_free (result, TRUE);
227         return ok;
228 }
229
230 static gboolean
231 test_dateChange (void)
232 {
233         GHashTable *structval;
234         SoupDate *date, *result;
235         char *timestamp;
236         GValue retval;
237         gboolean ok;
238
239         debug_printf (1, "dateChange (date, struct of ints -> time): ");
240
241         date = soup_date_new (1970 + (g_random_int_range (0, 50)),
242                               1 + g_random_int_range (0, 12),
243                               1 + g_random_int_range (0, 28),
244                               g_random_int_range (0, 24),
245                               g_random_int_range (0, 60),
246                               g_random_int_range (0, 60));
247         if (debug_level >= 2) {
248                 timestamp = soup_date_to_string (date, SOUP_DATE_ISO8601_XMLRPC);
249                 debug_printf (2, "date: %s, {", timestamp);
250                 g_free (timestamp);
251         }
252
253         structval = soup_value_hash_new ();
254
255 #define MAYBE (g_random_int_range (0, 3) != 0)
256
257         if (MAYBE) {
258                 date->year = 1970 + (g_random_int_range (0, 50));
259                 debug_printf (2, "tm_year: %d, ", date->year - 1900);
260                 soup_value_hash_insert (structval, "tm_year",
261                                         G_TYPE_INT, date->year - 1900);
262         }
263         if (MAYBE) {
264                 date->month = 1 + g_random_int_range (0, 12);
265                 debug_printf (2, "tm_mon: %d, ", date->month - 1);
266                 soup_value_hash_insert (structval, "tm_mon",
267                                         G_TYPE_INT, date->month - 1);
268         }
269         if (MAYBE) {
270                 date->day = 1 + g_random_int_range (0, 28);
271                 debug_printf (2, "tm_mday: %d, ", date->day);
272                 soup_value_hash_insert (structval, "tm_mday",
273                                         G_TYPE_INT, date->day);
274         }
275         if (MAYBE) {
276                 date->hour = g_random_int_range (0, 24);
277                 debug_printf (2, "tm_hour: %d, ", date->hour);
278                 soup_value_hash_insert (structval, "tm_hour",
279                                         G_TYPE_INT, date->hour);
280         }
281         if (MAYBE) {
282                 date->minute = g_random_int_range (0, 60);
283                 debug_printf (2, "tm_min: %d, ", date->minute);
284                 soup_value_hash_insert (structval, "tm_min",
285                                         G_TYPE_INT, date->minute);
286         }
287         if (MAYBE) {
288                 date->second = g_random_int_range (0, 60);
289                 debug_printf (2, "tm_sec: %d, ", date->second);
290                 soup_value_hash_insert (structval, "tm_sec",
291                                         G_TYPE_INT, date->second);
292         }
293
294         debug_printf (2, "} -> ");
295
296         ok = (do_xmlrpc ("dateChange", &retval,
297                          SOUP_TYPE_DATE, date,
298                          G_TYPE_HASH_TABLE, structval,
299                          G_TYPE_INVALID) &&
300               check_xmlrpc (&retval, SOUP_TYPE_DATE, &result));
301         g_hash_table_destroy (structval);
302         if (!ok) {
303                 soup_date_free (date);
304                 return FALSE;
305         }
306
307         if (debug_level >= 2) {
308                 timestamp = soup_date_to_string (result, SOUP_DATE_ISO8601_XMLRPC);
309                 debug_printf (2, "%s: ", timestamp);
310                 g_free (timestamp);
311         }
312
313         ok = ((date->year   == result->year) &&
314               (date->month  == result->month) &&
315               (date->day    == result->day) &&
316               (date->hour   == result->hour) &&
317               (date->minute == result->minute) &&
318               (date->second == result->second));
319         soup_date_free (date);
320         soup_date_free (result);
321
322         debug_printf (1, "%s\n", ok ? "OK!" : "WRONG!");
323         return ok;
324 }
325
326 static const char *const echo_strings[] = {
327         "This is a test",
328         "& so is this",
329         "and so is <this>",
330         "&amp; so is &lt;this&gt;"
331 };
332 #define N_ECHO_STRINGS G_N_ELEMENTS (echo_strings)
333
334 static const char *const echo_strings_broken[] = {
335         "This is a test",
336         " so is this",
337         "and so is this",
338         "amp; so is lt;thisgt;"
339 };
340
341 static gboolean
342 test_echo (void)
343 {
344         GValueArray *originals, *echoes;
345         GValue retval;
346         int i;
347         gboolean php_bug = FALSE;
348
349         debug_printf (1, "echo (array of string -> array of string): ");
350
351         originals = g_value_array_new (N_ECHO_STRINGS);
352         for (i = 0; i < N_ECHO_STRINGS; i++) {
353                 soup_value_array_append (originals, G_TYPE_STRING, echo_strings[i]);
354                 debug_printf (2, "%s\"%s\"", i == 0 ? "[" : ", ", echo_strings[i]);
355         }
356         debug_printf (2, "] -> ");
357
358         if (!(do_xmlrpc ("echo", &retval,
359                          G_TYPE_VALUE_ARRAY, originals,
360                          G_TYPE_INVALID) &&
361               check_xmlrpc (&retval, G_TYPE_VALUE_ARRAY, &echoes))) {
362                 g_value_array_free (originals);
363                 return FALSE;
364         }
365         g_value_array_free (originals);
366
367         if (debug_level >= 2) {
368                 for (i = 0; i < echoes->n_values; i++) {
369                         debug_printf (2, "%s\"%s\"", i == 0 ? "[" : ", ",
370                                       g_value_get_string (&echoes->values[i]));
371                 }
372                 debug_printf (2, "] -> ");
373         }
374
375         if (echoes->n_values != N_ECHO_STRINGS) {
376                 debug_printf (1, " WRONG! Wrong number of return strings");
377                 g_value_array_free (echoes);
378                 return FALSE;
379         }
380
381         for (i = 0; i < echoes->n_values; i++) {
382                 if (strcmp (echo_strings[i], g_value_get_string (&echoes->values[i])) != 0) {
383                         if (!server_test && strcmp (echo_strings_broken[i], g_value_get_string (&echoes->values[i])) == 0)
384                                 php_bug = TRUE;
385                         else {
386                                 debug_printf (1, " WRONG! Mismatch at %d\n", i + 1);
387                                 g_value_array_free (echoes);
388                                 return FALSE;
389                         }
390                 }
391         }
392
393         if (php_bug)
394                 debug_printf (1, "WRONG, but it's php's fault\n");
395         else
396                 debug_printf (1, "OK!\n");
397         g_value_array_free (echoes);
398         return TRUE;
399 }
400
401 static gboolean
402 test_ping (gboolean include_params)
403 {
404         GValueArray *params;
405         GValue retval;
406         char *request;
407         char *out;
408         gboolean ret;
409
410         debug_printf (1, "ping (void (%s) -> string): ",
411                       include_params ? "empty <params>" : "no <params>");
412
413         params = soup_value_array_new ();
414         request = soup_xmlrpc_build_method_call ("ping", params->values,
415                                                  params->n_values);
416         g_value_array_free (params);
417         if (!request)
418                 return FALSE;
419
420         if (!include_params) {
421                 char *params, *end;
422
423                 params = strstr (request, "<params/>");
424                 if (!params) {
425                         debug_printf (1, "ERROR: XML did not contain <params/>!");
426                         return FALSE;
427                 }
428                 end = params + strlen ("<params/>");
429                 memmove (params, end, strlen (end) + 1);
430         }
431
432         ret = send_xmlrpc (request, &retval);
433         g_free (request);
434
435         if (!ret || !check_xmlrpc (&retval, G_TYPE_STRING, &out))
436                 return FALSE;
437
438         if (!strcmp (out, "pong")) {
439                 debug_printf (1, "OK!\n");
440                 ret = TRUE;
441         } else {
442                 debug_printf (1, "WRONG! Bad response '%s'", out);
443                 ret = FALSE;
444         }
445
446         g_free (out);
447         return ret;
448 }
449
450 static gboolean
451 do_bad_xmlrpc (const char *body)
452 {
453         SoupMessage *msg;
454         GError *err = NULL;
455         GValue retval;
456
457         msg = soup_message_new ("POST", uri);
458         soup_message_set_request (msg, "text/xml", SOUP_MEMORY_COPY,
459                                   body, strlen (body));
460         soup_session_send_message (session, msg);
461
462         if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
463                 debug_printf (1, "ERROR: %d %s\n", msg->status_code,
464                               msg->reason_phrase);
465                 g_object_unref (msg);
466                 return FALSE;
467         }
468
469         if (!soup_xmlrpc_parse_method_response (msg->response_body->data,
470                                                 msg->response_body->length,
471                                                 &retval, &err)) {
472                 if (err) {
473                         debug_printf (1, "FAULT: %d %s (OK!)\n",
474                                       err->code, err->message);
475                         g_error_free (err);
476                         g_object_unref (msg);
477                         return TRUE;
478                 } else
479                         debug_printf (1, "ERROR: could not parse response\n");
480         } else
481                 debug_printf (1, "Unexpectedly got successful response!\n");
482
483         g_object_unref (msg);
484         return FALSE;
485 }
486
487 static gboolean
488 test_fault_malformed (void)
489 {
490         debug_printf (1, "malformed request: ");
491
492         return do_bad_xmlrpc ("<methodCall/>");
493 }
494
495 static gboolean
496 test_fault_method (void)
497 {
498         debug_printf (1, "request to non-existent method: ");
499
500         return do_bad_xmlrpc ("<methodCall><methodName>no_such_method</methodName><params><param><value><int>1</int></value></param></params></methodCall>");
501 }
502
503 static gboolean
504 test_fault_args (void)
505 {
506         debug_printf (1, "request with invalid args: ");
507
508         return do_bad_xmlrpc ("<methodCall><methodName>sum</methodName><params><param><value><int>1</int></value></param></params></methodCall>");
509 }
510
511 static GOptionEntry xmlrpc_entries[] = {
512         { "uri", 'u', 0, G_OPTION_ARG_STRING, &uri,
513           "Alternate URI for server", NULL },
514         { "server-test", 's', 0, G_OPTION_ARG_NONE, &server_test,
515           "If this is being run from xmlrpc-server-test", NULL },
516         { NULL }
517 };
518
519 int
520 main (int argc, char **argv)
521 {
522         test_init (argc, argv, xmlrpc_entries);
523
524         if (!uri) {
525                 apache_init ();
526                 uri = default_uri;
527         }
528
529         session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
530
531         if (!test_sum ())
532                 errors++;
533         if (!test_countBools ())
534                 errors++;
535         if (!test_md5sum ())
536                 errors++;
537         if (!test_dateChange ())
538                 errors++;
539         if (!test_echo ())
540                 errors++;
541         if (!test_ping (TRUE))
542                 errors++;
543         if (!test_ping (FALSE))
544                 errors++;
545         if (!test_fault_malformed ())
546                 errors++;
547         if (!test_fault_method ())
548                 errors++;
549         if (!test_fault_args ())
550                 errors++;
551
552         soup_test_session_abort_unref (session);
553
554         test_cleanup ();
555         return errors != 0;
556 }
557
558 #else /* HAVE_PHP_XMLRPC */
559
560 int
561 main (int argc, char **argv)
562 {
563         return 77; /* SKIP */
564 }
565
566 #endif