1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * Copyright 2008 Red Hat, Inc.
4 * Copyright 2015, Collabora ltd.
5 */
6
7 #include "test-utils.h"
8
9 static char *uri;
10
11 static GVariant *
parse_params(SoupMessage * msg,SoupXMLRPCParams * params,const char * signature)12 parse_params (SoupMessage *msg, SoupXMLRPCParams *params, const char *signature)
13 {
14 GVariant *args;
15 GError *error = NULL;
16
17 args = soup_xmlrpc_params_parse (params, signature, &error);
18 if (!args) {
19 soup_xmlrpc_message_set_fault (msg,
20 SOUP_XMLRPC_FAULT_SERVER_ERROR_INVALID_METHOD_PARAMETERS,
21 "Wrong method signature: expected %s: %s",
22 signature, error->message);
23 }
24
25 return args;
26 }
27
28 static void
do_sum(SoupMessage * msg,SoupXMLRPCParams * params)29 do_sum (SoupMessage *msg, SoupXMLRPCParams *params)
30 {
31 GVariant *args;
32 GVariant *child;
33 GVariantIter iter;
34 double sum = 0.0, val;
35
36 if (!(args = parse_params (msg, params, "(ad)")))
37 return;
38
39 child = g_variant_get_child_value (args, 0);
40
41 g_variant_iter_init (&iter, child);
42 while (g_variant_iter_loop (&iter, "d", &val))
43 sum += val;
44
45 soup_xmlrpc_message_set_response (msg, g_variant_new_double (sum), NULL);
46
47 g_variant_unref (args);
48 g_variant_unref (child);
49 }
50
51 static void
do_countBools(SoupMessage * msg,SoupXMLRPCParams * params)52 do_countBools (SoupMessage *msg, SoupXMLRPCParams *params)
53 {
54 GVariant *args;
55 GVariant *child;
56 GVariantIter iter;
57 gboolean val;
58 int trues = 0, falses = 0;
59 GVariantDict dict;
60
61 if (!(args = parse_params (msg, params, "(ab)")))
62 return;
63
64 child = g_variant_get_child_value (args, 0);
65
66 g_variant_iter_init (&iter, child);
67 while (g_variant_iter_loop (&iter, "b", &val)) {
68 if (val)
69 trues++;
70 else
71 falses++;
72 }
73
74 g_variant_dict_init (&dict, NULL);
75 g_variant_dict_insert (&dict, "true", "i", trues);
76 g_variant_dict_insert (&dict, "false", "i", falses);
77
78 soup_xmlrpc_message_set_response (msg, g_variant_dict_end (&dict), NULL);
79
80 g_variant_unref (args);
81 g_variant_unref (child);
82 }
83
84 static void
do_md5sum(SoupMessage * msg,SoupXMLRPCParams * params)85 do_md5sum (SoupMessage *msg, SoupXMLRPCParams *params)
86 {
87 GVariant *args;
88 GVariant *child;
89 GChecksum *checksum;
90 GByteArray *digest;
91 gsize digest_len = 16;
92
93 if (!(args = parse_params (msg, params, "(ay)")))
94 return;
95
96 child = g_variant_get_child_value (args, 0);
97
98 checksum = g_checksum_new (G_CHECKSUM_MD5);
99 g_checksum_update (checksum,
100 g_variant_get_data (child),
101 g_variant_get_size (child));
102 digest = g_byte_array_new ();
103 g_byte_array_set_size (digest, digest_len);
104 g_checksum_get_digest (checksum, digest->data, &digest_len);
105 g_checksum_free (checksum);
106
107 soup_xmlrpc_message_set_response (msg,
108 g_variant_new_from_data (G_VARIANT_TYPE_BYTESTRING,
109 digest->data, digest_len,
110 TRUE, NULL, NULL),
111 NULL);
112 g_byte_array_free (digest, TRUE);
113 g_variant_unref (child);
114 g_variant_unref (args);
115 }
116
117
118 static void
do_dateChange(SoupMessage * msg,SoupXMLRPCParams * params)119 do_dateChange (SoupMessage *msg, SoupXMLRPCParams *params)
120 {
121 GVariant *args;
122 GVariant *timestamp;
123 SoupDate *date;
124 GVariant *arg;
125 int val;
126 GError *error = NULL;
127
128 if (!(args = parse_params (msg, params, "(va{si})")))
129 return;
130
131 g_variant_get (args, "(v@a{si})", ×tamp, &arg);
132
133 date = soup_xmlrpc_variant_get_datetime (timestamp, &error);
134 if (!date) {
135 soup_xmlrpc_message_set_fault (msg,
136 SOUP_XMLRPC_FAULT_SERVER_ERROR_INVALID_METHOD_PARAMETERS,
137 "%s", error->message);
138 g_clear_error (&error);
139 goto fail;
140 }
141
142 if (g_variant_lookup (arg, "tm_year", "i", &val))
143 date->year = val + 1900;
144 if (g_variant_lookup (arg, "tm_mon", "i", &val))
145 date->month = val + 1;
146 if (g_variant_lookup (arg, "tm_mday", "i", &val))
147 date->day = val;
148 if (g_variant_lookup (arg, "tm_hour", "i", &val))
149 date->hour = val;
150 if (g_variant_lookup (arg, "tm_min", "i", &val))
151 date->minute = val;
152 if (g_variant_lookup (arg, "tm_sec", "i", &val))
153 date->second = val;
154
155 soup_xmlrpc_message_set_response (msg,
156 soup_xmlrpc_variant_new_datetime (date),
157 NULL);
158
159 soup_date_free (date);
160
161 fail:
162 g_variant_unref (args);
163 g_variant_unref (arg);
164 g_variant_unref (timestamp);
165 }
166
167 static void
do_echo(SoupMessage * msg,SoupXMLRPCParams * params)168 do_echo (SoupMessage *msg, SoupXMLRPCParams *params)
169 {
170 GVariant *args;
171 GVariant *child;
172
173 if (!(args = parse_params (msg, params, "(as)")))
174 return;
175
176 child = g_variant_get_child_value (args, 0);
177 soup_xmlrpc_message_set_response (msg, child, NULL);
178 g_variant_unref (args);
179 g_variant_unref (child);
180 }
181
182 static void
do_ping(SoupMessage * msg,SoupXMLRPCParams * params)183 do_ping (SoupMessage *msg, SoupXMLRPCParams *params)
184 {
185 GVariant *args;
186
187 if (!(args = parse_params (msg, params, "()")))
188 return;
189
190 soup_xmlrpc_message_set_response (msg, g_variant_new_string ("pong"), NULL);
191 g_variant_unref (args);
192 }
193
194 static void
server_callback(SoupServer * server,SoupMessage * msg,const char * path,GHashTable * query,SoupClientContext * context,gpointer data)195 server_callback (SoupServer *server, SoupMessage *msg,
196 const char *path, GHashTable *query,
197 SoupClientContext *context, gpointer data)
198 {
199 char *method_name;
200 SoupXMLRPCParams *params;
201 GError *error = NULL;
202
203 if (msg->method != SOUP_METHOD_POST) {
204 soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
205 return;
206 }
207
208 soup_message_set_status (msg, SOUP_STATUS_OK);
209
210 method_name = soup_xmlrpc_parse_request (msg->request_body->data,
211 msg->request_body->length,
212 ¶ms, &error);
213 if (!method_name) {
214 soup_xmlrpc_message_set_fault (msg, SOUP_XMLRPC_FAULT_PARSE_ERROR_NOT_WELL_FORMED,
215 "Could not parse method call: %s", error->message);
216 g_clear_error (&error);
217 return;
218 }
219
220 if (!strcmp (method_name, "sum"))
221 do_sum (msg, params);
222 else if (!strcmp (method_name, "countBools"))
223 do_countBools (msg, params);
224 else if (!strcmp (method_name, "md5sum"))
225 do_md5sum (msg, params);
226 else if (!strcmp (method_name, "dateChange"))
227 do_dateChange (msg, params);
228 else if (!strcmp (method_name, "echo"))
229 do_echo (msg, params);
230 else if (!strcmp (method_name, "ping"))
231 do_ping (msg, params);
232 else {
233 soup_xmlrpc_message_set_fault (msg, SOUP_XMLRPC_FAULT_SERVER_ERROR_REQUESTED_METHOD_NOT_FOUND,
234 "Unknown method %s", method_name);
235 }
236
237 g_free (method_name);
238 soup_xmlrpc_params_free (params);
239 }
240
241 static gboolean
run_xmlrpc_test(char ** argv,char ** stdout_out,char ** stderr_out,GError ** error)242 run_xmlrpc_test (char **argv,
243 char **stdout_out,
244 char **stderr_out,
245 GError **error)
246 {
247 gboolean ok;
248 int status;
249
250 argv[0] = g_test_build_filename (G_TEST_BUILT, "xmlrpc-test", NULL);
251 ok = g_spawn_sync (NULL, argv, NULL, 0, NULL, NULL,
252 stdout_out, stderr_out, &status,
253 error);
254 g_free (argv[0]);
255
256 if (!ok)
257 return FALSE;
258
259 return g_spawn_check_exit_status (status, error);
260 }
261
262 static void
do_one_xmlrpc_test(gconstpointer data)263 do_one_xmlrpc_test (gconstpointer data)
264 {
265 const char *path = data;
266 char *argv[12];
267 char *stdout_out, *stderr_out;
268 GError *error = NULL;
269 int arg;
270
271 argv[0] = NULL;
272 argv[1] = "-S";
273 argv[2] = "-U";
274 argv[3] = uri;
275 argv[4] = "-q";
276 argv[5] = "-p";
277 argv[6] = (char *) path;
278
279 for (arg = 0; arg < debug_level && arg < 3; arg++)
280 argv[arg + 7] = "-d";
281 argv[arg + 7] = NULL;
282
283 run_xmlrpc_test (argv, &stdout_out, &stderr_out, &error);
284 if (stdout_out) {
285 g_print ("%s", stdout_out);
286 g_free (stdout_out);
287 }
288 if (stderr_out) {
289 g_printerr ("%s", stderr_out);
290 g_free (stderr_out);
291 }
292
293 if ( g_error_matches (error, G_SPAWN_EXIT_ERROR, 1)
294 || g_error_matches (error, G_SPAWN_EXIT_ERROR, 77))
295 g_test_fail ();
296 else
297 g_assert_no_error (error);
298 g_clear_error (&error);
299 }
300
301 gboolean run_tests = TRUE;
302
303 static GOptionEntry no_test_entry[] = {
304 { "no-tests", 'n', G_OPTION_FLAG_REVERSE,
305 G_OPTION_ARG_NONE, &run_tests,
306 "Don't run tests, just run the test server", NULL },
307 { NULL }
308 };
309
310 int
main(int argc,char ** argv)311 main (int argc, char **argv)
312 {
313 SoupServer *server;
314 SoupURI *server_uri;
315 int ret;
316
317 test_init (argc, argv, no_test_entry);
318
319 server = soup_test_server_new (run_tests ? SOUP_TEST_SERVER_IN_THREAD : SOUP_TEST_SERVER_DEFAULT);
320 soup_server_add_handler (server, "/xmlrpc-server.php",
321 server_callback, NULL, NULL);
322 server_uri = soup_test_server_get_uri (server, "http", NULL);
323 soup_uri_set_path (server_uri, "/xmlrpc-server.php");
324 uri = soup_uri_to_string (server_uri, FALSE);
325
326 if (run_tests) {
327 char *out, **tests, *path;
328 char *list_argv[4];
329 GError *error = NULL;
330 int i;
331
332 list_argv[0] = NULL;
333 list_argv[1] = "-S";
334 list_argv[2] = "-l";
335 list_argv[3] = NULL;
336
337 if (!run_xmlrpc_test (list_argv, &out, NULL, &error)) {
338 g_printerr ("'xmlrpc-test -l' failed: %s\n", error->message);
339 g_error_free (error);
340 return 1;
341 }
342
343 tests = g_strsplit (out, "\n", -1);
344 g_free (out);
345
346 for (i = 0; tests[i] && *tests[i]; i++) {
347 /* GLib >= 2.62 defaults to TAP output for tests, and
348 * this adds TAP diagnostics "#..." and the test count
349 * "1..N", even in the output of "some-test -l".
350 * Ignore those. */
351 if (tests[i][0] == '#' || g_str_has_prefix (tests[i], "1.."))
352 continue;
353
354 g_assert_true (g_str_has_prefix (tests[i], "/xmlrpc/"));
355 path = g_strdup_printf ("/xmlrpc-server/%s", tests[i] + strlen ("/xmlrpc/"));
356 g_test_add_data_func (path, tests[i], do_one_xmlrpc_test);
357 g_free (path);
358 }
359
360 ret = g_test_run ();
361
362 g_strfreev (tests);
363 } else {
364 GMainLoop *loop;
365
366 g_print ("Listening on port %d\n", server_uri->port);
367
368 loop = g_main_loop_new (NULL, TRUE);
369 g_main_loop_run (loop);
370 g_main_loop_unref (loop);
371
372 ret = 0;
373 }
374
375 soup_test_server_quit_unref (server);
376 soup_uri_free (server_uri);
377 g_free (uri);
378 if (run_tests)
379 test_cleanup ();
380 return ret;
381 }
382