[libgdata/offline-testing] tests: UNFINISHED additions to allow custom replies



commit 808ee079cf96df48ed43cc04464f6a70aca9bd03
Author: Philip Withnall <philip tecnocode co uk>
Date:   Sat Jul 6 18:17:19 2013 +0100

    tests: UNFINISHED additions to allow custom replies
    
    including proof-of-concept error unit tests for YouTube

 gdata/tests/mock-server.c |   59 ++++++++++++++++++++++++---
 gdata/tests/mock-server.h |    5 ++
 gdata/tests/youtube.c     |   97 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 155 insertions(+), 6 deletions(-)
---
diff --git a/gdata/tests/mock-server.c b/gdata/tests/mock-server.c
index 94e147c..e702f4b 100644
--- a/gdata/tests/mock-server.c
+++ b/gdata/tests/mock-server.c
@@ -33,6 +33,8 @@ static void gdata_mock_server_dispose (GObject *object);
 static void gdata_mock_server_get_property (GObject *object, guint property_id, GValue *value, GParamSpec 
*pspec);
 static void gdata_mock_server_set_property (GObject *object, guint property_id, const GValue *value, 
GParamSpec *pspec);
 
+static gboolean real_handle_message (GDataMockServer *self, SoupMessage *message, SoupClientContext *client);
+
 static void server_handler_cb (SoupServer *server, SoupMessage *message, const gchar *path, GHashTable 
*query, SoupClientContext *client, gpointer user_data);
 static void load_file_stream_thread_cb (GTask *task, gpointer source_object, gpointer task_data, 
GCancellable *cancellable);
 static void load_file_iteration_thread_cb (GTask *task, gpointer source_object, gpointer task_data, 
GCancellable *cancellable);
@@ -61,6 +63,13 @@ enum {
        PROP_ENABLE_LOGGING,
 };
 
+enum {
+       SIGNAL_HANDLE_MESSAGE = 1,
+       LAST_SIGNAL,
+};
+
+static guint signals[LAST_SIGNAL] = { 0, };
+
 G_DEFINE_TYPE (GDataMockServer, gdata_mock_server, G_TYPE_OBJECT)
 
 static void
@@ -74,23 +83,44 @@ gdata_mock_server_class_init (GDataMockServerClass *klass)
        gobject_class->set_property = gdata_mock_server_set_property;
        gobject_class->dispose = gdata_mock_server_dispose;
 
+       klass->handle_message = real_handle_message;
+
+       /**
+        * TODO: Document me.
+        */
        g_object_class_install_property (gobject_class, PROP_TRACE_DIRECTORY,
                                         g_param_spec_object ("trace-directory",
                                                              "Trace Directory", "TODO",
                                                              G_TYPE_FILE,
                                                              G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
+       /**
+        * TODO: Document me.
+        */
        g_object_class_install_property (gobject_class, PROP_ENABLE_ONLINE,
                                         g_param_spec_boolean ("enable-online",
                                                               "Enable Online", "TODO",
                                                               FALSE,
                                                               G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
+       /**
+        * TODO: Document me.
+        */
        g_object_class_install_property (gobject_class, PROP_ENABLE_LOGGING,
                                         g_param_spec_boolean ("enable-logging",
                                                               "Enable Logging", "TODO",
                                                               FALSE,
                                                               G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+       /**
+        * TODO: Document me.
+        */
+       signals[SIGNAL_HANDLE_MESSAGE] = g_signal_new ("handle-message", G_OBJECT_CLASS_TYPE (klass), 
G_SIGNAL_RUN_LAST,
+                                                      G_STRUCT_OFFSET (GDataMockServerClass, handle_message),
+                                                      g_signal_accumulator_true_handled, NULL,
+                                                      g_cclosure_marshal_generic,
+                                                      G_TYPE_BOOLEAN, 2,
+                                                      SOUP_TYPE_MESSAGE, SOUP_TYPE_CLIENT_CONTEXT);
 }
 
 static void
@@ -251,15 +281,27 @@ static void
 server_handler_cb (SoupServer *server, SoupMessage *message, const gchar *path, GHashTable *query, 
SoupClientContext *client, gpointer user_data)
 {
        GDataMockServer *self = user_data;
+       gboolean message_handled = FALSE;
+
+       soup_server_pause_message (server, message);
+       g_signal_emit (self, signals[SIGNAL_HANDLE_MESSAGE], 0, message, client, &message_handled);
+       soup_server_unpause_message (server, message);
+
+       /* The message should always be handled by real_handle_message() at least. */
+       g_assert (message_handled == TRUE);
+}
+
+static gboolean
+real_handle_message (GDataMockServer *self, SoupMessage *message, SoupClientContext *client)
+{
        GDataMockServerPrivate *priv = self->priv;
-g_message ("%s: %s", __func__, path);
+       gboolean handled = FALSE;
+
        /* Asynchronously load the next expected message from the trace file. */
        if (priv->next_message == NULL) {
                GTask *task;
                GError *child_error = NULL;
 
-               soup_server_pause_message (server, message);
-
                task = g_task_new (self, NULL, NULL, NULL);
                g_task_set_task_data (task, g_object_ref (priv->input_stream), g_object_unref);
                g_task_run_in_thread_sync (task, load_file_iteration_thread_cb);
@@ -276,6 +318,7 @@ g_message ("%s: %s", __func__, path);
 
                        body = g_strdup_printf ("Error: %s", child_error->message);
                        soup_message_body_append_take (message->response_body, (guchar *) body, strlen 
(body));
+                       handled = TRUE;
 
                        g_error_free (child_error);
                } else if (priv->next_message == NULL) {
@@ -288,15 +331,19 @@ g_message ("%s: %s", __func__, path);
                        body = g_strdup_printf ("Expected no request, but got ‘%s’.", actual_uri);
                        g_free (actual_uri);
                        soup_message_body_append_take (message->response_body, (guchar *) body, strlen 
(body));
+                       handled = TRUE;
                }
-
-               soup_server_unpause_message (server, message);
        }
 
        /* Process the actual message if we already know the expected message. */
-       if (priv->next_message != NULL) {
+       g_assert (priv->next_message != NULL || handled == TRUE);
+       if (handled == FALSE) {
                server_process_message (self, message, client);
+               handled = TRUE;
        }
+
+       g_assert (handled == TRUE);
+       return handled;
 }
 
 /**
diff --git a/gdata/tests/mock-server.h b/gdata/tests/mock-server.h
index d0bbd21..710eaff 100644
--- a/gdata/tests/mock-server.h
+++ b/gdata/tests/mock-server.h
@@ -42,6 +42,11 @@ typedef struct {
 
 typedef struct {
        GObjectClass parent;
+
+       /**
+        * TODO: Document me.
+        */
+       gboolean (*handle_message) (GDataMockServer *self, SoupMessage *message, SoupClientContext *client);
 } GDataMockServerClass;
 
 GType gdata_mock_server_get_type (void) G_GNUC_CONST;
diff --git a/gdata/tests/youtube.c b/gdata/tests/youtube.c
index 781556a..02f1e27 100644
--- a/gdata/tests/youtube.c
+++ b/gdata/tests/youtube.c
@@ -18,6 +18,7 @@
  */
 
 #include <glib.h>
+#include <string.h>
 #include <unistd.h>
 
 #include "gdata.h"
@@ -59,6 +60,101 @@ test_authentication (void)
        gdata_mock_server_end_trace (mock_server);
 }
 
+/* TODO: Document me. */
+typedef struct {
+       guint status_code;
+       const gchar *reason_phrase;
+       const gchar *message_body;
+       gboolean client_login_authorizer_error; /* TRUE if error domain is 
GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR; FALSE if it's GDATA_SERVICE_ERROR */
+       gint error_code;
+} RequestErrorData;
+
+static const RequestErrorData authentication_errors[] = {
+       { SOUP_STATUS_BAD_REQUEST, "Bad Request", "Invalid parameter ‘foobar’.",
+         FALSE, GDATA_SERVICE_ERROR_PROTOCOL_ERROR },
+       { SOUP_STATUS_FORBIDDEN, "Access Forbidden", "Error=BadAuthentication\n",
+         TRUE, GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_BAD_AUTHENTICATION },
+       { SOUP_STATUS_FORBIDDEN, "Access Forbidden", "Error=BadAuthentication\nInfo=InvalidSecondFactor\n",
+         TRUE, GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_INVALID_SECOND_FACTOR },
+       { SOUP_STATUS_FORBIDDEN, "Access Forbidden", "Error=NotVerified\nUrl=http://example.com/\n";,
+         TRUE, GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_NOT_VERIFIED },
+       { SOUP_STATUS_FORBIDDEN, "Access Forbidden", "Error=TermsNotAgreed\nUrl=http://example.com/\n";,
+         TRUE, GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_TERMS_NOT_AGREED },
+       { SOUP_STATUS_FORBIDDEN, "Access Forbidden", "Error=Unknown\nUrl=http://example.com/\n";,
+         FALSE, GDATA_SERVICE_ERROR_AUTHENTICATION_REQUIRED },
+       { SOUP_STATUS_FORBIDDEN, "Access Forbidden", "Error=AccountDeleted\nUrl=http://example.com/\n";,
+         TRUE, GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_ACCOUNT_DELETED },
+       { SOUP_STATUS_FORBIDDEN, "Access Forbidden", "Error=AccountDisabled\nUrl=http://example.com/\n";,
+         TRUE, GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_ACCOUNT_DISABLED },
+       { SOUP_STATUS_FORBIDDEN, "Access Forbidden", "Error=AccountMigrated\nUrl=http://example.com/\n";,
+         TRUE, GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_ACCOUNT_MIGRATED },
+       { SOUP_STATUS_FORBIDDEN, "Access Forbidden", "Error=ServiceDisabled\nUrl=http://example.com/\n";,
+         TRUE, GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR_SERVICE_DISABLED },
+       { SOUP_STATUS_FORBIDDEN, "Access Forbidden", "Error=ServiceUnavailable\nUrl=http://example.com/\n";,
+         FALSE, GDATA_SERVICE_ERROR_UNAVAILABLE },
+};
+
+static gboolean
+authentication_error_cb (GDataMockServer *self, SoupMessage *message, SoupClientContext *client, gpointer 
user_data)
+{
+       const RequestErrorData *data = user_data;
+
+       soup_message_set_status_full (message, data->status_code, data->reason_phrase);
+       soup_message_body_append (message->response_body, SOUP_MEMORY_STATIC, data->message_body, strlen 
(data->message_body));
+
+       return TRUE;
+}
+
+static void
+test_authentication_error (void)
+{
+       gboolean retval;
+       GDataClientLoginAuthorizer *authorizer;
+       GError *error = NULL;
+       gulong handler_id;
+       guint i;
+
+       if (gdata_mock_server_get_enable_logging (mock_server) == TRUE) {
+               g_test_message ("Ignoring test due to logging being enabled.");
+               return;
+       } else if (gdata_mock_server_get_enable_online (mock_server) == TRUE) {
+               g_test_message ("Ignoring test due to running online and test not being reproducible.");
+               return;
+       }
+
+       for (i = 0; i < G_N_ELEMENTS (authentication_errors); i++) {
+               const RequestErrorData *data = &authentication_errors[i];
+               GQuark error_domain;
+
+               handler_id = g_signal_connect (mock_server, "handle-message", (GCallback) 
authentication_error_cb, (gpointer) data);
+               gdata_mock_server_run (mock_server);
+
+               /* Create an authorizer */
+               authorizer = gdata_client_login_authorizer_new (CLIENT_ID, GDATA_TYPE_YOUTUBE_SERVICE);
+
+               g_assert_cmpstr (gdata_client_login_authorizer_get_client_id (authorizer), ==, CLIENT_ID);
+
+               /* Log in */
+               retval = gdata_client_login_authorizer_authenticate (authorizer, USERNAME, PASSWORD, NULL, 
&error);
+               error_domain = (data->client_login_authorizer_error == TRUE) ? 
GDATA_CLIENT_LOGIN_AUTHORIZER_ERROR : GDATA_SERVICE_ERROR;
+               g_assert_error (error, error_domain, data->error_code);
+               g_assert (retval == FALSE);
+               g_clear_error (&error);
+
+               /* Check nothing's changed in the authoriser. */
+               g_assert_cmpstr (gdata_client_login_authorizer_get_username (authorizer), ==, NULL);
+               g_assert_cmpstr (gdata_client_login_authorizer_get_password (authorizer), ==, NULL);
+
+               g_assert (gdata_authorizer_is_authorized_for_domain (GDATA_AUTHORIZER (authorizer),
+                                                                    
gdata_youtube_service_get_primary_authorization_domain ()) == FALSE);
+
+               g_object_unref (authorizer);
+
+               gdata_mock_server_stop (mock_server);
+               g_signal_handler_disconnect (mock_server, handler_id);
+       }
+}
+
 GDATA_ASYNC_TEST_FUNCTIONS (authentication, void,
 G_STMT_START {
        GDataClientLoginAuthorizer *authorizer;
@@ -2015,6 +2111,7 @@ main (int argc, char *argv[])
        service = GDATA_SERVICE (gdata_youtube_service_new (DEVELOPER_KEY, authorizer));
 
        g_test_add_func ("/youtube/authentication", test_authentication);
+       g_test_add_func ("/youtube/authentication/error", test_authentication_error);
        g_test_add ("/youtube/authentication/async", GDataAsyncTestData, NULL, gdata_set_up_async_test_data, 
test_authentication_async,
                    gdata_tear_down_async_test_data);
        g_test_add ("/youtube/authentication/async/cancellation", GDataAsyncTestData, NULL, 
gdata_set_up_async_test_data,


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