[libsoup/wip/xclaesse/xmlrpc: 1/2] xmlrpc: Add unit tests for GVariant API



commit 8f105b4ee162a30f28d23b919eddea46eba7ddec
Author: Xavier Claessens <xavier claessens collabora com>
Date:   Mon Jul 27 16:35:42 2015 -0400

    xmlrpc: Add unit tests for GVariant API
    
    https://bugzilla.gnome.org/show_bug.cgi?id=746495

 tests/Makefile.am          |    4 +-
 tests/xmlrpc-server-test.c |  363 ++++++++++++++++++++++
 tests/xmlrpc-test.c        |  719 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1085 insertions(+), 1 deletions(-)
---
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 148df86..62facb3 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -43,7 +43,9 @@ test_programs =                       \
        uri-parsing             \
        websocket-test          \
        xmlrpc-gvalue-server-test \
-       xmlrpc-gvalue-test
+       xmlrpc-gvalue-test      \
+       xmlrpc-server-test      \
+       xmlrpc-test
 
 test_extra_programs =          \
        ntlm-test-helper        \
diff --git a/tests/xmlrpc-server-test.c b/tests/xmlrpc-server-test.c
new file mode 100644
index 0000000..a0b2e65
--- /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})", &timestamp, &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,
+                                                &params, &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", soup_server_get_port (server));
+
+               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 0000000..b9bcb65
--- /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>",
+       "&amp; so is &lt;this&gt;",
+       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>&lt;&gt;&amp;</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>&lt;&gt;&amp;</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;
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]