diff options
-rw-r--r-- | tests/Makefile.am | 4 | ||||
-rw-r--r-- | tests/xmlrpc-old-server-test.c | 2 | ||||
-rw-r--r-- | tests/xmlrpc-server-test.c | 363 | ||||
-rw-r--r-- | tests/xmlrpc-test.c | 719 |
4 files changed, 1086 insertions, 2 deletions
diff --git a/tests/Makefile.am b/tests/Makefile.am index f2f9f0c4..33f29df5 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -43,7 +43,9 @@ test_programs = \ uri-parsing \ websocket-test \ xmlrpc-old-server-test \ - xmlrpc-old-test + xmlrpc-old-test \ + xmlrpc-server-test \ + xmlrpc-test test_extra_programs = \ ntlm-test-helper \ diff --git a/tests/xmlrpc-old-server-test.c b/tests/xmlrpc-old-server-test.c index eb73f570..3d3ba992 100644 --- a/tests/xmlrpc-old-server-test.c +++ b/tests/xmlrpc-old-server-test.c @@ -355,7 +355,7 @@ main (int argc, char **argv) } else { GMainLoop *loop; - g_print ("Listening on port %d\n", soup_server_get_port (server)); + g_print ("Listening on port %d\n", server_uri->port); loop = g_main_loop_new (NULL, TRUE); g_main_loop_run (loop); diff --git a/tests/xmlrpc-server-test.c b/tests/xmlrpc-server-test.c new file mode 100644 index 00000000..a2ef9e41 --- /dev/null +++ b/tests/xmlrpc-server-test.c @@ -0,0 +1,363 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright 2008 Red Hat, Inc. + * Copyright 2015, Collabora ltd. + */ + +#include "test-utils.h" + +static char *uri; + +static GVariant * +parse_params (SoupMessage *msg, SoupXMLRPCParams *params, const char *signature) +{ + GVariant *args; + GError *error = NULL; + + args = soup_xmlrpc_params_parse (params, signature, &error); + if (!args) { + soup_xmlrpc_message_set_fault (msg, + SOUP_XMLRPC_FAULT_SERVER_ERROR_INVALID_METHOD_PARAMETERS, + "Wrong method signature: expected %s: %s", + signature, error->message); + } + + return args; +} + +static void +do_sum (SoupMessage *msg, SoupXMLRPCParams *params) +{ + GVariant *args; + GVariant *child; + GVariantIter iter; + int sum = 0, val; + + if (!(args = parse_params (msg, params, "(ai)"))) + return; + + child = g_variant_get_child_value (args, 0); + + g_variant_iter_init (&iter, child); + while (g_variant_iter_loop (&iter, "i", &val)) + sum += val; + + soup_xmlrpc_message_set_response (msg, g_variant_new_int32 (sum), NULL); + + g_variant_unref (args); + g_variant_unref (child); +} + +static void +do_countBools (SoupMessage *msg, SoupXMLRPCParams *params) +{ + GVariant *args; + GVariant *child; + GVariantIter iter; + gboolean val; + int trues = 0, falses = 0; + GVariantDict dict; + + if (!(args = parse_params (msg, params, "(ab)"))) + return; + + child = g_variant_get_child_value (args, 0); + + g_variant_iter_init (&iter, child); + while (g_variant_iter_loop (&iter, "b", &val)) { + if (val) + trues++; + else + falses++; + } + + g_variant_dict_init (&dict, NULL); + g_variant_dict_insert (&dict, "true", "i", trues); + g_variant_dict_insert (&dict, "false", "i", falses); + + soup_xmlrpc_message_set_response (msg, g_variant_dict_end (&dict), NULL); + + g_variant_unref (args); + g_variant_unref (child); +} + +static void +do_md5sum (SoupMessage *msg, SoupXMLRPCParams *params) +{ + GVariant *args; + GVariant *child; + GChecksum *checksum; + GByteArray *digest; + gsize digest_len = 16; + + if (!(args = parse_params (msg, params, "(ay)"))) + return; + + child = g_variant_get_child_value (args, 0); + + checksum = g_checksum_new (G_CHECKSUM_MD5); + g_checksum_update (checksum, + g_variant_get_data (child), + g_variant_get_size (child)); + digest = g_byte_array_new (); + g_byte_array_set_size (digest, digest_len); + g_checksum_get_digest (checksum, digest->data, &digest_len); + g_checksum_free (checksum); + + soup_xmlrpc_message_set_response (msg, + g_variant_new_from_data (G_VARIANT_TYPE_BYTESTRING, + digest->data, digest_len, + TRUE, NULL, NULL), + NULL); + g_byte_array_free (digest, TRUE); + g_variant_unref (child); + g_variant_unref (args); +} + + +static void +do_dateChange (SoupMessage *msg, SoupXMLRPCParams *params) +{ + GVariant *args; + time_t timestamp; + SoupDate *date; + GVariant *arg; + int val; + + if (!(args = parse_params (msg, params, "(xa{si})"))) + return; + + g_variant_get (args, "(x@a{si})", ×tamp, &arg); + + date = soup_date_new_from_time_t (timestamp); + + if (g_variant_lookup (arg, "tm_year", "i", &val)) + date->year = val + 1900; + if (g_variant_lookup (arg, "tm_mon", "i", &val)) + date->month = val + 1; + if (g_variant_lookup (arg, "tm_mday", "i", &val)) + date->day = val; + if (g_variant_lookup (arg, "tm_hour", "i", &val)) + date->hour = val; + if (g_variant_lookup (arg, "tm_min", "i", &val)) + date->minute = val; + if (g_variant_lookup (arg, "tm_sec", "i", &val)) + date->second = val; + + soup_xmlrpc_message_set_response (msg, + soup_xmlrpc_new_datetime (soup_date_to_time_t (date)), + NULL); + + soup_date_free (date); + g_variant_unref (args); + g_variant_unref (arg); +} + +static void +do_echo (SoupMessage *msg, SoupXMLRPCParams *params) +{ + GVariant *args; + GVariant *child; + + if (!(args = parse_params (msg, params, "(as)"))) + return; + + child = g_variant_get_child_value (args, 0); + soup_xmlrpc_message_set_response (msg, child, NULL); + g_variant_unref (args); + g_variant_unref (child); +} + +static void +do_ping (SoupMessage *msg, SoupXMLRPCParams *params) +{ + GVariant *args; + + if (!(args = parse_params (msg, params, "()"))) + return; + + soup_xmlrpc_message_set_response (msg, g_variant_new_string ("pong"), NULL); + g_variant_unref (args); +} + +static void +server_callback (SoupServer *server, SoupMessage *msg, + const char *path, GHashTable *query, + SoupClientContext *context, gpointer data) +{ + char *method_name; + SoupXMLRPCParams *params; + GError *error = NULL; + + if (msg->method != SOUP_METHOD_POST) { + soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED); + return; + } + + soup_message_set_status (msg, SOUP_STATUS_OK); + + method_name = soup_xmlrpc_parse_request (msg->request_body->data, + msg->request_body->length, + ¶ms, &error); + if (!method_name) { + soup_xmlrpc_message_set_fault (msg, SOUP_XMLRPC_FAULT_PARSE_ERROR_NOT_WELL_FORMED, + "Could not parse method call: %s", error->message); + g_clear_error (&error); + return; + } + + if (!strcmp (method_name, "sum")) + do_sum (msg, params); + else if (!strcmp (method_name, "countBools")) + do_countBools (msg, params); + else if (!strcmp (method_name, "md5sum")) + do_md5sum (msg, params); + else if (!strcmp (method_name, "dateChange")) + do_dateChange (msg, params); + else if (!strcmp (method_name, "echo")) + do_echo (msg, params); + else if (!strcmp (method_name, "ping")) + do_ping (msg, params); + else { + soup_xmlrpc_message_set_fault (msg, SOUP_XMLRPC_FAULT_SERVER_ERROR_REQUESTED_METHOD_NOT_FOUND, + "Unknown method %s", method_name); + } + + g_free (method_name); + soup_xmlrpc_params_free (params); +} + +static gboolean +run_xmlrpc_test (char **argv, + char **stdout_out, + char **stderr_out, + GError **error) +{ + gboolean ok; + int status; + + argv[0] = g_test_build_filename (G_TEST_BUILT, "xmlrpc-test", NULL); + ok = g_spawn_sync (NULL, argv, NULL, 0, NULL, NULL, + stdout_out, stderr_out, &status, + error); + g_free (argv[0]); + + if (!ok) + return FALSE; + + return g_spawn_check_exit_status (status, error); +} + +static void +do_one_xmlrpc_test (gconstpointer data) +{ + const char *path = data; + char *argv[12]; + char *stdout_out, *stderr_out; + GError *error = NULL; + int arg; + + argv[0] = NULL; + argv[1] = "-S"; + argv[2] = "-U"; + argv[3] = uri; + argv[4] = "-q"; + argv[5] = "-p"; + argv[6] = (char *) path; + + for (arg = 0; arg < debug_level && arg < 3; arg++) + argv[arg + 7] = "-d"; + argv[arg + 7] = NULL; + + run_xmlrpc_test (argv, &stdout_out, &stderr_out, &error); + if (stdout_out) { + g_print ("%s", stdout_out); + g_free (stdout_out); + } + if (stderr_out) { + g_printerr ("%s", stderr_out); + g_free (stderr_out); + } + + if ( g_error_matches (error, G_SPAWN_EXIT_ERROR, 1) + || g_error_matches (error, G_SPAWN_EXIT_ERROR, 77)) + g_test_fail (); + else + g_assert_no_error (error); + g_clear_error (&error); +} + +gboolean run_tests = TRUE; + +static GOptionEntry no_test_entry[] = { + { "no-tests", 'n', G_OPTION_FLAG_REVERSE, + G_OPTION_ARG_NONE, &run_tests, + "Don't run tests, just run the test server", NULL }, + { NULL } +}; + +int +main (int argc, char **argv) +{ + SoupServer *server; + SoupURI *server_uri; + int ret; + + test_init (argc, argv, no_test_entry); + + server = soup_test_server_new (run_tests ? SOUP_TEST_SERVER_IN_THREAD : SOUP_TEST_SERVER_DEFAULT); + soup_server_add_handler (server, "/xmlrpc-server.php", + server_callback, NULL, NULL); + server_uri = soup_test_server_get_uri (server, "http", NULL); + soup_uri_set_path (server_uri, "/xmlrpc-server.php"); + uri = soup_uri_to_string (server_uri, FALSE); + + if (run_tests) { + char *out, **tests, *path; + char *list_argv[4]; + GError *error = NULL; + int i; + + list_argv[0] = NULL; + list_argv[1] = "-S"; + list_argv[2] = "-l"; + list_argv[3] = NULL; + + if (!run_xmlrpc_test (list_argv, &out, NULL, &error)) { + g_printerr ("'xmlrpc-test -l' failed: %s\n", error->message); + g_error_free (error); + return 1; + } + + tests = g_strsplit (out, "\n", -1); + g_free (out); + + for (i = 0; tests[i] && *tests[i]; i++) { + g_assert_true (g_str_has_prefix (tests[i], "/xmlrpc/")); + path = g_strdup_printf ("/xmlrpc-server/%s", tests[i] + strlen ("/xmlrpc/")); + g_test_add_data_func (path, tests[i], do_one_xmlrpc_test); + g_free (path); + } + + ret = g_test_run (); + + g_strfreev (tests); + } else { + GMainLoop *loop; + + g_print ("Listening on port %d\n", server_uri->port); + + loop = g_main_loop_new (NULL, TRUE); + g_main_loop_run (loop); + g_main_loop_unref (loop); + + ret = 0; + } + + soup_test_server_quit_unref (server); + soup_uri_free (server_uri); + g_free (uri); + if (run_tests) + test_cleanup (); + return ret; +} diff --git a/tests/xmlrpc-test.c b/tests/xmlrpc-test.c new file mode 100644 index 00000000..b9bcb654 --- /dev/null +++ b/tests/xmlrpc-test.c @@ -0,0 +1,719 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright 2001-2003, Ximian, Inc. + * Copyright 2015, Collabora ltd. + */ + +#include "test-utils.h" + +static SoupSession *session; +static const char *default_uri = "http://127.0.0.1:47524/xmlrpc-server.php"; +static const char *uri = NULL; +static gboolean server_test = FALSE; + +#ifdef HAVE_PHP_XMLRPC +#define SOUP_TEST_SKIP_IF_NO_XMLRPC_SERVER +#else +#define SOUP_TEST_SKIP_IF_NO_XMLRPC_SERVER \ + G_STMT_START { \ + if (!server_test) { \ + g_test_skip ("php-xmlrpc is not available"); \ + return; \ + } \ + } G_STMT_END +#endif + +static const char *const value_type[] = { + "BAD", + "int", + "boolean", + "string", + "double", + "datetime", + "base64", + "struct", + "array" +}; + +static gboolean +send_xmlrpc (const char *body, const char *signature, GVariant **retval) +{ + SoupMessage *msg; + GError *err = NULL; + + msg = soup_message_new ("POST", uri); + soup_message_set_request (msg, "text/xml", SOUP_MEMORY_COPY, + body, strlen (body)); + soup_session_send_message (session, msg); + + soup_test_assert_message_status (msg, SOUP_STATUS_OK); + + *retval = soup_xmlrpc_parse_response (msg->response_body->data, + msg->response_body->length, + signature, &err); + if (!*retval) { + if (err->domain == SOUP_XMLRPC_FAULT) + soup_test_assert (FALSE, "FAULT: %d %s\n", err->code, err->message); + else + soup_test_assert (FALSE, "ERROR: %s\n", err->message); + g_error_free (err); + g_object_unref (msg); + return FALSE; + } + + return TRUE; +} + +static gboolean +do_xmlrpc (const char *method, GVariant *args, const char *signature, GVariant **retval) +{ + gboolean ret; + char *body; + GError *error = NULL; + + body = soup_xmlrpc_build_request (method, args, &error); + g_assert_no_error (error); + if (!body) + return FALSE; + + ret = send_xmlrpc (body, signature, retval); + g_free (body); + + return ret; +} + +static void +test_sum (void) +{ + GVariantBuilder builder; + int i, val, sum, result; + GVariant *retval; + gboolean ok; + + SOUP_TEST_SKIP_IF_NO_XMLRPC_SERVER; + + debug_printf (2, "sum (array of int -> int): "); + + g_variant_builder_init (&builder, G_VARIANT_TYPE ("ai")); + for (i = sum = 0; i < 10; i++) { + val = g_random_int_range (0, 100); + debug_printf (2, "%s%d", i == 0 ? "[" : ", ", val); + g_variant_builder_add (&builder, "i", val); + sum += val; + } + debug_printf (2, "] -> "); + + ok = do_xmlrpc ("sum", + g_variant_new ("(@ai)", g_variant_builder_end (&builder)), + "i", &retval); + + if (!ok) + return; + + result = g_variant_get_int32 (retval); + debug_printf (2, "%d\n", result); + g_assert_cmpint (result, ==, sum); + + g_variant_unref (retval); +} + +static void +test_countBools (void) +{ + GVariantBuilder builder; + int i, trues, falses; + GVariant *retval; + int ret_trues, ret_falses; + gboolean val, ok; + + SOUP_TEST_SKIP_IF_NO_XMLRPC_SERVER; + + debug_printf (2, "countBools (array of boolean -> struct of ints): "); + + g_variant_builder_init (&builder, G_VARIANT_TYPE ("ab")); + for (i = trues = falses = 0; i < 10; i++) { + val = g_random_boolean (); + debug_printf (2, "%s%c", i == 0 ? "[" : ", ", val ? 'T' : 'F'); + g_variant_builder_add (&builder, "b", val); + if (val) + trues++; + else + falses++; + } + debug_printf (2, "] -> "); + + ok = do_xmlrpc ("countBools", + g_variant_new ("(@ab)", g_variant_builder_end (&builder)), + "a{si}", &retval); + if (!ok) + return; + + g_assert_true (g_variant_lookup (retval, "true", "i", &ret_trues)); + g_assert_true (g_variant_lookup (retval, "false", "i", &ret_falses)); + g_assert_cmpint (g_variant_n_children (retval), ==, 2); + g_variant_unref (retval); + + debug_printf (2, "{ true: %d, false: %d }\n", ret_trues, ret_falses); + g_assert_cmpint (trues, ==, ret_trues); + g_assert_cmpint (falses, ==, ret_falses); +} + +static void +test_md5sum (void) +{ + GByteArray *data; + int i; + GChecksum *checksum; + guchar digest[16]; + gsize digest_len = sizeof (digest); + GVariant *retval; + gboolean ok; + + SOUP_TEST_SKIP_IF_NO_XMLRPC_SERVER; + + debug_printf (2, "md5sum (base64 -> base64)\n"); + + data = g_byte_array_new (); + g_byte_array_set_size (data, 256); + for (i = 0; i < data->len; i++) + data->data[i] = (char)(g_random_int_range (0, 256)); + + checksum = g_checksum_new (G_CHECKSUM_MD5); + g_checksum_update (checksum, data->data, data->len); + g_checksum_get_digest (checksum, digest, &digest_len); + g_checksum_free (checksum); + + ok = do_xmlrpc ("md5sum", + g_variant_new ("(@ay)", + g_variant_new_from_data (G_VARIANT_TYPE_BYTESTRING, + data->data, data->len, + TRUE, NULL, NULL)), + "ay", &retval); + g_byte_array_free (data, TRUE); + if (!ok) + return; + + soup_assert_cmpmem (g_variant_get_data (retval), g_variant_get_size (retval), + digest, digest_len); + g_variant_unref (retval); +} + +static void +test_dateChange (void) +{ + GVariantDict structval; + SoupDate *date; + time_t timestamp; + GVariant *retval; + gboolean ok; + + SOUP_TEST_SKIP_IF_NO_XMLRPC_SERVER; + + debug_printf (2, "dateChange (date, struct of ints -> time)\n"); + + date = soup_date_new (1970 + (g_random_int_range (0, 50)), + 1 + g_random_int_range (0, 12), + 1 + g_random_int_range (0, 28), + g_random_int_range (0, 24), + g_random_int_range (0, 60), + g_random_int_range (0, 60)); + if (debug_level >= 2) { + char *tmp; + + tmp = soup_date_to_string (date, SOUP_DATE_ISO8601_XMLRPC); + debug_printf (2, "date: %s, {", tmp); + g_free (tmp); + } + + g_variant_dict_init (&structval, NULL); + +#define MAYBE (g_random_int_range (0, 3) != 0) + + if (MAYBE) { + date->year = 1970 + (g_random_int_range (0, 50)); + debug_printf (2, "tm_year: %d, ", date->year - 1900); + g_variant_dict_insert (&structval, "tm_year", + "i", date->year - 1900); + } + if (MAYBE) { + date->month = 1 + g_random_int_range (0, 12); + debug_printf (2, "tm_mon: %d, ", date->month - 1); + g_variant_dict_insert (&structval, "tm_mon", + "i", date->month - 1); + } + if (MAYBE) { + date->day = 1 + g_random_int_range (0, 28); + debug_printf (2, "tm_mday: %d, ", date->day); + g_variant_dict_insert (&structval, "tm_mday", + "i", date->day); + } + if (MAYBE) { + date->hour = g_random_int_range (0, 24); + debug_printf (2, "tm_hour: %d, ", date->hour); + g_variant_dict_insert (&structval, "tm_hour", + "i", date->hour); + } + if (MAYBE) { + date->minute = g_random_int_range (0, 60); + debug_printf (2, "tm_min: %d, ", date->minute); + g_variant_dict_insert (&structval, "tm_min", + "i", date->minute); + } + if (MAYBE) { + date->second = g_random_int_range (0, 60); + debug_printf (2, "tm_sec: %d, ", date->second); + g_variant_dict_insert (&structval, "tm_sec", + "i", date->second); + } + + timestamp = soup_date_to_time_t (date); + soup_date_free (date); + + debug_printf (2, "} -> "); + + ok = do_xmlrpc ("dateChange", + g_variant_new ("(vv)", + soup_xmlrpc_new_datetime (timestamp), + g_variant_dict_end (&structval)), + "x", &retval); + if (!ok) + return; + + debug_printf (2, "%"G_GINT64_FORMAT"\n", g_variant_get_int64 (retval)); + + g_assert_cmpint (timestamp, ==, g_variant_get_int64 (retval)); + g_variant_unref (retval); +} + +static const char *const echo_strings[] = { + "This is a test", + "& so is this", + "and so is <this>", + "& so is <this>", + NULL +}; + +static void +test_echo (void) +{ + GVariant *originals; + GVariant *retval; + char *str; + + SOUP_TEST_SKIP_IF_NO_XMLRPC_SERVER; + + debug_printf (2, "echo (array of string -> array of string):\n"); + + originals = g_variant_new ("^as", echo_strings); + g_variant_ref_sink (originals); + str = g_variant_print (originals, TRUE); + debug_printf (2, "%s -> ", str); + g_free (str); + + if (!do_xmlrpc ("echo", + g_variant_new ("(@as)", originals), + "as", &retval)) { + g_variant_unref (originals); + return; + } + + str = g_variant_print (retval, TRUE); + debug_printf (2, "%s\n", str); + g_free (str); + + g_assert_true (g_variant_equal (originals, retval)); + + g_variant_unref (originals); + g_variant_unref (retval); +} + +static void +test_ping (gconstpointer include_params) +{ + GVariant *retval; + char *request; + gboolean ret; + GError *error = NULL; + + g_test_bug ("671661"); + + SOUP_TEST_SKIP_IF_NO_XMLRPC_SERVER; + + debug_printf (2, "ping (void (%s) -> string)\n", + include_params ? "empty <params>" : "no <params>"); + + request = soup_xmlrpc_build_request ("ping", g_variant_new ("()"), &error); + g_assert_no_error (error); + if (!request) + return; + + if (!include_params) { + char *params, *end; + + params = strstr (request, "<params/>"); + if (!params) { + soup_test_assert (FALSE, "ERROR: XML did not contain <params/>!"); + return; + } + end = params + strlen ("<params/>"); + memmove (params, end, strlen (end) + 1); + } + + ret = send_xmlrpc (request, "s", &retval); + g_free (request); + + if (!ret) + return; + + g_assert_cmpstr (g_variant_get_string (retval, NULL), ==, "pong"); + g_variant_unref (retval); +} + +static void +do_bad_xmlrpc (const char *body) +{ + SoupMessage *msg; + GError *err = NULL; + + msg = soup_message_new ("POST", uri); + soup_message_set_request (msg, "text/xml", SOUP_MEMORY_COPY, + body, strlen (body)); + soup_session_send_message (session, msg); + + soup_test_assert_message_status (msg, SOUP_STATUS_OK); + + if (!soup_xmlrpc_parse_response (msg->response_body->data, + msg->response_body->length, + "()", &err)) { + if (err->domain == SOUP_XMLRPC_FAULT) { + debug_printf (1, "FAULT: %d %s (OK!)\n", + err->code, err->message); + g_error_free (err); + g_object_unref (msg); + return; + } else + soup_test_assert (FALSE, "ERROR: could not parse response: %s\n", err->message); + } else + soup_test_assert (FALSE, "Unexpectedly got successful response!\n"); + + g_object_unref (msg); +} + +static void +test_fault_malformed (void) +{ + SOUP_TEST_SKIP_IF_NO_XMLRPC_SERVER; + + do_bad_xmlrpc ("<methodCall/>"); +} + +static void +test_fault_method (void) +{ + SOUP_TEST_SKIP_IF_NO_XMLRPC_SERVER; + + do_bad_xmlrpc ("<methodCall><methodName>no_such_method</methodName><params><param><value><int>1</int></value></param></params></methodCall>"); +} + +static void +test_fault_args (void) +{ + SOUP_TEST_SKIP_IF_NO_XMLRPC_SERVER; + + do_bad_xmlrpc ("<methodCall><methodName>sum</methodName><params><param><value><int>1</int></value></param></params></methodCall>"); +} + +#define BODY_PREFIX \ + "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" \ + "<methodCall><methodName>MyMethod</methodName>" +#define BODY_SUFFIX \ + "</methodCall>\n" + +static void +verify_serialization (GVariant *value, + const char *expected_params) +{ + char *debug; + char *body; + char *params; + GError *error = NULL; + + debug = g_variant_print (value, TRUE); + + body = soup_xmlrpc_build_request ("MyMethod", value, &error); + g_assert_no_error (error); + g_assert (g_str_has_prefix (body, BODY_PREFIX)); + g_assert (g_str_has_suffix (body, BODY_SUFFIX)); + + params = g_strndup (body + strlen (BODY_PREFIX), + strlen (body) - strlen (BODY_PREFIX) + - strlen (BODY_SUFFIX)); + + if (!g_str_equal (params, expected_params)) + g_error ("Failed to serialize '%s':\n" + " expected: %s\n" + " got: %s\n", + debug, expected_params, params); + + g_free (params); + g_free (body); + g_free (debug); +} + +static void +verify_serialization_fail (GVariant *value) +{ + char *body; + GError *error = NULL; + + body = soup_xmlrpc_build_request ("MyMethod", value, &error); + g_assert (body == NULL); + g_assert (error != NULL); +} + +static void +test_serializer (void) +{ + verify_serialization (g_variant_new_parsed ("()"), + "<params/>"); + verify_serialization (g_variant_new_parsed ("(1, 2)"), + "<params>" + "<param><value><int>1</int></value></param>" + "<param><value><int>2</int></value></param>" + "</params>"); + verify_serialization (g_variant_new_parsed ("((1, 2),)"), + "<params><param><value><array><data>" + "<value><int>1</int></value>" + "<value><int>2</int></value>" + "</data></array></value></param></params>"); + verify_serialization (g_variant_new_parsed ("({'one', 1},)"), + "<params><param><value><struct>" + "<member><name>one</name><value><int>1</int></value></member>" + "</struct></value></param></params>"); + verify_serialization (g_variant_new_parsed ("([{'one', 1},{'two', 2}],)"), + "<params><param><value><struct>" + "<member><name>one</name><value><int>1</int></value></member>" + "<member><name>two</name><value><int>2</int></value></member>" + "</struct></value></param></params>"); + verify_serialization (g_variant_new ("(^ay)", "bytestring"), + "<params><param>" + "<value><base64>Ynl0ZXN0cmluZwA=</base64></value>" + "</param></params>"); + verify_serialization (g_variant_new ("(y)", 42), + "<params>" + "<param><value><int>42</int></value></param>" + "</params>"); + verify_serialization (g_variant_new ("(@*)", soup_xmlrpc_new_datetime (1434161309)), + "<params>" + "<param><value><dateTime.iso8601>20150613T02:08:29</dateTime.iso8601></value></param>" + "</params>"); + verify_serialization (g_variant_new ("(s)", "<>&"), + "<params>" + "<param><value><string><>&</string></value></param>" + "</params>"); + verify_serialization (g_variant_new ("(u)", 0), + "<params>" + "<param><value><i8>0</i8></value></param>" + "</params>"); + + verify_serialization_fail (g_variant_new_parsed ("1")); + verify_serialization_fail (g_variant_new_parsed ("({1, 2},)")); + verify_serialization_fail (g_variant_new ("(mi)", NULL)); + verify_serialization_fail (g_variant_new ("(t)", 0)); +} + +static void +verify_deserialization (GVariant *expected_variant, + const char *signature, + const char *params) +{ + char *body; + char *method_name; + GVariant *variant; + GError *error = NULL; + + body = g_strconcat (BODY_PREFIX, params, BODY_SUFFIX, NULL); + method_name = soup_xmlrpc_parse_request_full (body, strlen (body), + signature, + &variant, + &error); + g_assert_no_error (error); + g_assert_cmpstr (method_name, ==, "MyMethod"); + + if (!g_variant_equal (variant, expected_variant)) { + char *str1, *str2; + + str1 = g_variant_print (expected_variant, TRUE); + str2 = g_variant_print (variant, TRUE); + g_error ("Failed to deserialize '%s':\n" + " expected: %s\n" + " got: %s\n", + params, str1, str2); + g_free (str1); + g_free (str2); + } + + g_variant_unref (variant); + g_free (method_name); + g_free (body); +} + +static void +verify_deserialization_fail (const char *signature, + const char *params) +{ + char *body; + char *method_name; + GVariant *variant; + GError *error = NULL; + + body = g_strconcat (BODY_PREFIX, params, BODY_SUFFIX, NULL); + method_name = soup_xmlrpc_parse_request_full (body, strlen (body), + signature, + &variant, + &error); + g_assert_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS); + g_assert (method_name == NULL); + + g_free (body); +} + +static void +test_deserializer (void) +{ + char *tmp; + + verify_deserialization (g_variant_new_parsed ("@av []"), + NULL, + "<params/>"); + verify_deserialization (g_variant_new_parsed ("()"), + "()", + "<params/>"); + verify_deserialization (g_variant_new_parsed ("(@y 1,@n 2)"), + "(yn)", + "<params>" + "<param><value><int>1</int></value></param>" + "<param><value><int>2</int></value></param>" + "</params>"); + verify_deserialization (g_variant_new_parsed ("[<[{'one', <1>},{'two', <2>}]>]"), + NULL, + "<params><param><value><struct>" + "<member><name>one</name><value><int>1</int></value></member>" + "<member><name>two</name><value><int>2</int></value></member>" + "</struct></value></param></params>"); + verify_deserialization (g_variant_new_parsed ("([{'one', 1},{'two', 2}],)"), + "(a{si})", + "<params><param><value><struct>" + "<member><name>one</name><value><int>1</int></value></member>" + "<member><name>two</name><value><int>2</int></value></member>" + "</struct></value></param></params>"); + verify_deserialization (g_variant_new_parsed ("[<int64 1434146909>]"), + NULL, + "<params>" + "<param><value><dateTime.iso8601>20150612T22:08:29</dateTime.iso8601></value></param>" + "</params>"); + verify_deserialization (g_variant_new_parsed ("[<b'bytestring'>]"), + NULL, + "<params>" + "<param><value><base64>Ynl0ZXN0cmluZwA=</base64></value></param>" + "</params>"); + verify_deserialization (g_variant_new_parsed ("(@o '/path',)"), + "(o)", + "<params>" + "<param><value><string>/path</string></value></param>" + "</params>"); + verify_deserialization (g_variant_new_parsed ("[<1>]"), + "av", + "<params><param><value><int>1</int></value></param></params>"); + verify_deserialization (g_variant_new_parsed ("[<%s>]", "<>&"), + NULL, + "<params>" + "<param><value><string><>&</string></value></param>" + "</params>"); + verify_deserialization (g_variant_new_parsed ("(@y 255,)"), + "(y)", + "<params>" + "<param><value><int>255</int></value></param>" + "</params>"); + + tmp = g_strdup_printf ("<params>" + "<param><value><int>%"G_GUINT64_FORMAT"</int></value></param>" + "</params>", G_MAXUINT64); + verify_deserialization (g_variant_new ("(t)", G_MAXUINT64), + "(t)", tmp); + g_free (tmp); + + verify_deserialization_fail ("(o)", + "<params>" + "<param><value><string>not/a/path</string></value></param>" + "</params>"); + verify_deserialization_fail (NULL, + "<params>" + "<param><value><boolean>2</boolean></value></param>" + "</params>"); + verify_deserialization_fail ("(y)", + "<params>" + "<param><value><int>256</int></value></param>" + "</params>"); +} + +static void +test_fault (void) +{ + char *body; + GVariant *reply; + GError *error = NULL; + + body = soup_xmlrpc_build_fault (1, "error: %s", "failed"); + reply = soup_xmlrpc_parse_response (body, strlen (body), NULL, &error); + g_assert_error (error, SOUP_XMLRPC_FAULT, 1); + g_assert_cmpstr (error->message, ==, "error: failed"); + g_assert (reply == NULL); + + g_free (body); + g_clear_error (&error); +} + +static GOptionEntry xmlrpc_entries[] = { + { "uri", 'U', 0, G_OPTION_ARG_STRING, &uri, + "Alternate URI for server", NULL }, + { "server-test", 'S', 0, G_OPTION_ARG_NONE, &server_test, + "If this is being run from xmlrpc-server-test", NULL }, + { NULL } +}; + +int +main (int argc, char **argv) +{ + int ret; + + test_init (argc, argv, xmlrpc_entries); + + if (!uri && !server_test) { + apache_init (); + uri = default_uri; + } + + session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL); + + g_test_add_func ("/xmlrpc/variant/serializer", test_serializer); + g_test_add_func ("/xmlrpc/variant/deserializer", test_deserializer); + g_test_add_func ("/xmlrpc/variant/fault", test_fault); + g_test_add_func ("/xmlrpc/variant/sum", test_sum); + g_test_add_func ("/xmlrpc/variant/countBools", test_countBools); + g_test_add_func ("/xmlrpc/variant/md5sum", test_md5sum); + g_test_add_func ("/xmlrpc/variant/dateChange", test_dateChange); + g_test_add_func ("/xmlrpc/variant/echo", test_echo); + g_test_add_data_func ("/xmlrpc/variant/ping/empty-params", GINT_TO_POINTER (TRUE), test_ping); + g_test_add_data_func ("/xmlrpc/variant/ping/no-params", GINT_TO_POINTER (FALSE), test_ping); + g_test_add_func ("/xmlrpc/variant/fault/malformed", test_fault_malformed); + g_test_add_func ("/xmlrpc/variant/fault/method", test_fault_method); + g_test_add_func ("/xmlrpc/variant/fault/args", test_fault_args); + + ret = g_test_run (); + + soup_test_session_abort_unref (session); + + test_cleanup (); + return ret; +} |