[libgdata/offline-testing] tests: UNFINISHED additions to the mock server for comparison mode



commit ecbefe699174d936be0b35a50b9ed87556434ed9
Author: Philip Withnall <philip tecnocode co uk>
Date:   Sun Jul 28 13:13:16 2013 +0200

    tests: UNFINISHED additions to the mock server for comparison mode

 gdata/tests/common.c      |   21 ++++++-
 gdata/tests/mock-server.c |  135 +++++++++++++++++++++++++++++++++++++++-----
 gdata/tests/mock-server.h |    3 +-
 3 files changed, 138 insertions(+), 21 deletions(-)
---
diff --git a/gdata/tests/common.c b/gdata/tests/common.c
index 27d96be..d6f51dd 100644
--- a/gdata/tests/common.c
+++ b/gdata/tests/common.c
@@ -48,6 +48,9 @@ static GFile *trace_dir = NULL;
 /* TRUE if tests should be run online and a trace file written for each; FALSE if tests should run offline 
against existing trace files. */
 static gboolean write_traces = FALSE;
 
+/* TRUE if tests should be run online and the server's responses compared to the existing trace file for 
each; FALSE if tests should run offline without comparison. */
+static gboolean compare_traces = FALSE;
+
 /* Global mock server instance used by all tests. */
 static GDataMockServer *mock_server = NULL;
 
@@ -82,6 +85,9 @@ gdata_test_init (int argc, char **argv)
                } else if (strcmp ("--write-traces", argv[i]) == 0 || strcmp ("-w", argv[i]) == 0) {
                        write_traces = TRUE;
                        argv[i] = (char*) "";
+               } else if (strcmp ("--compare-traces", argv[i]) == 0 || strcmp ("-c", argv[i]) == 0) {
+                       compare_traces = TRUE;
+                       argv[i] = (char*) "";
                } else if (strcmp ("-?", argv[i]) == 0 || strcmp ("--help", argv[i]) == 0 || strcmp ("-h" , 
argv[i]) == 0) {
                        /* We have to override --help in order to document --no-internet and --no-interactive 
*/
                        printf ("Usage:\n"
@@ -100,12 +106,19 @@ gdata_test_init (int argc, char **argv)
                                  "  -n, --no-internet              Only execute tests which don't require 
Internet connectivity\n"
                                  "  -i, --no-interactive           Only execute tests which don't require 
user interaction\n"
                                  "  -t, --trace-dir [directory]    Read/Write trace files in the specified 
directory\n"
-                                 "  -w, --write-traces             Work online and write trace files to 
--trace-dir\n",
+                                 "  -w, --write-traces             Work online and write trace files to 
--trace-dir\n"
+                                 "  -c, --compare-traces           Work online and compare with existing 
trace files in --trace-dir\n",
                                  argv[0]);
                        exit (0);
                }
        }
 
+       /* --[write|compare]-traces are mutually exclusive. */
+       if (write_traces == TRUE && compare_traces == TRUE) {
+               fprintf (stderr, "Error: --write-traces and --compare-traces are mutually exclusive.\n");
+               exit (1);
+       }
+
        g_test_init (&argc, &argv, NULL);
        g_test_bug_base ("http://bugzilla.gnome.org/show_bug.cgi?id=";);
 
@@ -121,7 +134,7 @@ gdata_test_init (int argc, char **argv)
 
        mock_server = gdata_mock_server_new ();
        gdata_mock_server_set_enable_logging (mock_server, write_traces);
-       gdata_mock_server_set_enable_online (mock_server, write_traces);
+       gdata_mock_server_set_enable_online (mock_server, write_traces || compare_traces);
 }
 
 /*
@@ -757,8 +770,8 @@ gdata_test_debug_handler (const gchar *log_domain, GLogLevelFlags log_level, con
        message_list = g_slist_append (message_list, g_strdup (message));
 
        /* Log to the trace file. */
-       if (write_traces == TRUE && message != NULL && (*message == '<' || *message == '>' || *message == ' 
') && *(message + 1) == ' ') {
-               gdata_mock_server_log_message_chunk (mock_server, message);
+       if (message != NULL && (*message == '<' || *message == '>' || *message == ' ') && *(message + 1) == ' 
') {
+               gdata_mock_server_received_message_chunk (mock_server, message);
        }
 }
 
diff --git a/gdata/tests/mock-server.c b/gdata/tests/mock-server.c
index ae1cc39..240bcc6 100644
--- a/gdata/tests/mock-server.c
+++ b/gdata/tests/mock-server.c
@@ -30,6 +30,14 @@
  * The mock server currently only operates on a single network interface, on HTTPS only. This may change in 
future. A dummy TLS certificate is used
  * to authenticate the server. This certificate is not signed by a CA, so the SoupSession:ssl-strict 
property must be set to %FALSE in client code
  * during (and only during!) testing.
+ *
+ * The server can operate in three modes: logging, testing, and comparing. These are set by 
#GDataMockServer:enable-logging and #GDataMockServer:enable-online.
+ *  • Logging mode (#GDataMockServer:enable-logging: %TRUE, #GDataMockServer:enable-online: %TRUE): Requests 
are sent to the real server online, and the
+ *    request–response pairs recorded to a log file.
+ *  • Testing mode (#GDataMockServer:enable-logging: %FALSE, #GDataMockServer:enable-online: %FALSE): 
Requests are sent to the mock server, which responds
+ *    from the trace file.
+ *  • Comparing mode (#GDataMockServer:enable-logging: %FALSE, #GDataMockServer:enable-online: %TRUE): 
Requests are sent to the real server online, and
+ *    the request–response pairs are compared against those in an existing log file to see if the log file 
is up-to-date.
  */
 
 #include <glib.h>
@@ -74,6 +82,9 @@ struct _GDataMockServerPrivate {
        GFile *trace_directory;
        gboolean enable_online;
        gboolean enable_logging;
+
+       GString *comparison_message;
+       gboolean comparison_message_seen_request;
 };
 
 enum {
@@ -211,6 +222,10 @@ gdata_mock_server_dispose (GObject *object)
        g_clear_object (&priv->trace_directory);
        g_clear_pointer (&priv->server_thread, g_thread_unref);
 
+       if (priv->comparison_message != NULL) {
+               g_string_free (priv->comparison_message, TRUE);
+       }
+
        /* Chain up to the parent class */
        G_OBJECT_CLASS (gdata_mock_server_parent_class)->dispose (object);
 }
@@ -284,10 +299,16 @@ load_file_iteration_data_free (LoadFileIterationData *data)
 static SoupURI * /* transfer full */
 build_base_uri (GDataMockServer *self)
 {
+       GDataMockServerPrivate *priv = self->priv;
        gchar *base_uri_string;
        SoupURI *base_uri;
 
-       base_uri_string = g_strdup_printf ("https://%s:%u";, soup_address_get_physical (self->priv->address), 
self->priv->port);
+       if (priv->enable_online == FALSE) {
+               base_uri_string = g_strdup_printf ("https://%s:%u";, soup_address_get_physical 
(self->priv->address), self->priv->port);
+       } else {
+               base_uri_string = g_strdup ("https://localhost";); /* FIXME */
+       }
+
        base_uri = soup_uri_new (base_uri_string);
        g_free (base_uri_string);
 
@@ -842,6 +863,28 @@ load_file_iteration_thread_cb (GTask *task, gpointer source_object, gpointer tas
 }
 
 /**
+ * gdata_mock_server_unload_trace:
+ * @self: a #GDataMockServer
+ *
+ * Unloads the current trace file of network messages, as loaded by gdata_mock_server_load_trace() or 
gdata_mock_server_load_trace_async().
+ */
+void
+gdata_mock_server_unload_trace (GDataMockServer *self)
+{
+       GDataMockServerPrivate *priv = self->priv;
+
+       g_return_if_fail (GDATA_IS_MOCK_SERVER (self));
+
+       g_clear_object (&priv->next_message);
+       g_clear_object (&priv->input_stream);
+       g_clear_object (&priv->trace_file);
+
+       g_string_free (priv->comparison_message, TRUE);
+       priv->comparison_message = NULL;
+       priv->comparison_message_seen_request = FALSE;
+}
+
+/**
  * gdata_mock_server_load_trace:
  * @self: a #GDataMockServer
  * @trace_file: trace file to load
@@ -877,6 +920,8 @@ gdata_mock_server_load_trace (GDataMockServer *self, GFile *trace_file, GCancell
                GError *child_error = NULL;
 
                priv->next_message = load_file_iteration (priv->input_stream, base_uri, cancellable, 
&child_error);
+               priv->comparison_message = g_string_new (NULL);
+               priv->comparison_message_seen_request = FALSE;
 
                if (child_error != NULL) {
                        g_clear_object (&priv->trace_file);
@@ -983,6 +1028,8 @@ gdata_mock_server_load_trace_finish (GDataMockServer *self, GAsyncResult *result
        g_return_if_fail (g_task_is_valid (result, self));
 
        self->priv->next_message = g_task_propagate_pointer (G_TASK (result), error);
+       self->priv->comparison_message = g_string_new (NULL);
+       self->priv->comparison_message_seen_request = FALSE;
 }
 
 static gpointer
@@ -1106,9 +1153,7 @@ gdata_mock_server_stop (GDataMockServer *self)
        g_object_thaw_notify (G_OBJECT (self));
 
        /* Reset the trace file. */
-       g_clear_object (&priv->next_message);
-       g_clear_object (&priv->input_stream);
-       g_clear_object (&priv->trace_file);
+       gdata_mock_server_unload_trace (self);
 }
 
 /**
@@ -1218,7 +1263,7 @@ gdata_mock_server_start_trace_full (GDataMockServer *self, GFile *trace_file)
                }
        }
 
-       /* Start reading from a trace file if online testing is disabled. */
+       /* Start reading from a trace file if online testing is disabled or if we need to compare server 
responses to the trace file. */
        if (priv->enable_online == FALSE) {
                gdata_mock_server_run (self);
                gdata_mock_server_load_trace (self, trace_file, NULL, &child_error);
@@ -1234,6 +1279,18 @@ gdata_mock_server_start_trace_full (GDataMockServer *self, GFile *trace_file)
 
                        return;
                }
+       } else if (priv->enable_online == TRUE && priv->enable_logging == FALSE) {
+               gdata_mock_server_load_trace (self, trace_file, NULL, &child_error);
+
+               if (child_error != NULL) {
+                       gchar *trace_file_path = g_file_get_path (trace_file);
+                       g_error ("Error loading trace file ‘%s’: %s", trace_file_path, child_error->message);
+                       g_free (trace_file_path);
+
+                       g_error_free (child_error);
+
+                       return;
+               }
        }
 }
 
@@ -1255,6 +1312,8 @@ gdata_mock_server_end_trace (GDataMockServer *self)
 
        if (priv->enable_online == FALSE) {
                gdata_mock_server_stop (self);
+       } else if (priv->enable_online == TRUE && priv->enable_logging == FALSE) {
+               gdata_mock_server_unload_trace (self);
        }
 
        if (priv->enable_logging == TRUE) {
@@ -1327,19 +1386,19 @@ gdata_mock_server_set_enable_logging (GDataMockServer *self, gboolean enable_log
 }
 
 /**
- * gdata_mock_server_log_message_chunk:
+ * gdata_mock_server_received_message_chunk:
  * @self: a #GDataMockServer
- * @message_chunk: single line of a message to log
- *
- * Appends a single new line of a message to the current trace file, adding a newline character at the end.
+ * @message_chunk: single line of a message which was received
  *
- * This function is a no-op if the mock server is not in logging mode (i.e. if 
#GDataMockServer:enable-logging is %FALSE),
- * or if a trace file has not been specified using gdata_mock_server_start_trace().
+ * Indicates to the mock server that a single new line of a message was received from the real server. The 
message line may be
+ * appended to the current trace file if logging is enabled (#GDataMockServer:enable-logging is %TRUE), 
adding a newline character
+ * at the end. If logging is disabled but online mode is enabled (#GDataMockServer:enable-online is %TRUE), 
the message line will
+ * be compared to the next expected line in the existing trace file. Otherwise, this function is a no-op.
  *
  * On error, a warning will be printed. FIXME: That's icky.
  */
 void
-gdata_mock_server_log_message_chunk (GDataMockServer *self, const gchar *message_chunk)
+gdata_mock_server_received_message_chunk (GDataMockServer *self, const gchar *message_chunk)
 {
        GDataMockServerPrivate *priv = self->priv;
        GError *child_error = NULL;
@@ -1347,14 +1406,15 @@ gdata_mock_server_log_message_chunk (GDataMockServer *self, const gchar *message
        g_return_if_fail (GDATA_IS_MOCK_SERVER (self));
        g_return_if_fail (message_chunk != NULL);
 
-       /* Silently ignore the call if logging is disabled or if a trace file hasn't been specified. */
-       if (priv->enable_logging == FALSE || priv->output_stream == NULL) {
+       /* Silently ignore the call if logging is disabled and we're offline, or if a trace file hasn't been 
specified. */
+       if ((priv->enable_logging == FALSE && priv->enable_online == FALSE) || (priv->enable_logging == TRUE 
&& priv->output_stream == NULL)) {
                return;
        }
 
        /* Append to the trace file. */
-       if (g_output_stream_write_all (G_OUTPUT_STREAM (priv->output_stream), message_chunk, strlen 
(message_chunk), NULL, NULL, &child_error) == FALSE ||
-           g_output_stream_write_all (G_OUTPUT_STREAM (priv->output_stream), "\n", 1, NULL, NULL, 
&child_error) == FALSE) {
+       if (priv->enable_logging == TRUE &&
+           (g_output_stream_write_all (G_OUTPUT_STREAM (priv->output_stream), message_chunk, strlen 
(message_chunk), NULL, NULL, &child_error) == FALSE ||
+            g_output_stream_write_all (G_OUTPUT_STREAM (priv->output_stream), "\n", 1, NULL, NULL, 
&child_error) == FALSE)) {
                gchar *trace_file_path = g_file_get_path (priv->trace_file);
                g_warning ("Error appending to log file ‘%s’: %s", trace_file_path, child_error->message);
                g_free (trace_file_path);
@@ -1363,6 +1423,49 @@ gdata_mock_server_log_message_chunk (GDataMockServer *self, const gchar *message
 
                return;
        }
+
+       /* Or compare to the existing trace file. */
+       if (priv->enable_logging == FALSE && priv->enable_online == TRUE) {
+               /* Build up the message to compare. */
+               g_string_append (priv->comparison_message, message_chunk);
+               g_string_append_c (priv->comparison_message, '\n');
+
+               if (strcmp (message_chunk, "  ") == 0 && priv->comparison_message_seen_request == FALSE) {
+                       /* Need to capture both the request and the response before processing; both are 
terminated by a single “  ” chunk. */
+                       priv->comparison_message_seen_request = TRUE;
+               } else if (strcmp (message_chunk, "  ") == 0) {
+                       /* Received the last chunk of the response, so compare the message from the trace 
file and that from online. */
+                       SoupMessage *online_message;
+                       SoupURI *base_uri;
+
+                       /* End of a message. */
+                       base_uri = soup_uri_new ("https://localhost/";); /* FIXME */
+                       online_message = trace_to_soup_message (priv->comparison_message->str, base_uri);
+                       soup_uri_free (base_uri);
+
+                       g_string_truncate (priv->comparison_message, 0);
+                       priv->comparison_message_seen_request = FALSE;
+
+                       g_assert (priv->next_message != NULL);
+
+                       /* Compare the message from the server with the message in the log file. */
+                       if (compare_incoming_message (online_message, priv->next_message, NULL) != 0) {
+                               gchar *next_uri, *actual_uri;
+
+                               next_uri = soup_uri_to_string (soup_message_get_uri (priv->next_message), 
TRUE);
+                               actual_uri = soup_uri_to_string (soup_message_get_uri (online_message), TRUE);
+                               g_warning ("Expected URI ‘%s’, but got ‘%s’.", next_uri, actual_uri);
+                               g_free (actual_uri);
+                               g_free (next_uri);
+
+                               g_object_unref (online_message);
+
+                               return;
+                       }
+
+                       g_object_unref (online_message);
+               }
+       }
 }
 
 /**
diff --git a/gdata/tests/mock-server.h b/gdata/tests/mock-server.h
index 1819494..bc175a0 100644
--- a/gdata/tests/mock-server.h
+++ b/gdata/tests/mock-server.h
@@ -60,6 +60,7 @@ GDataMockServer *gdata_mock_server_new (void) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_R
 void gdata_mock_server_load_trace (GDataMockServer *self, GFile *trace_file, GCancellable *cancellable, 
GError **error);
 void gdata_mock_server_load_trace_async (GDataMockServer *self, GFile *trace_file, GCancellable 
*cancellable, GAsyncReadyCallback callback, gpointer user_data);
 void gdata_mock_server_load_trace_finish (GDataMockServer *self, GAsyncResult *result, GError **error);
+void gdata_mock_server_unload_trace (GDataMockServer *self);
 
 void gdata_mock_server_run (GDataMockServer *self);
 void gdata_mock_server_stop (GDataMockServer *self);
@@ -77,7 +78,7 @@ void gdata_mock_server_set_enable_online (GDataMockServer *self, gboolean enable
 gboolean gdata_mock_server_get_enable_logging (GDataMockServer *self) G_GNUC_WARN_UNUSED_RESULT;
 void gdata_mock_server_set_enable_logging (GDataMockServer *self, gboolean enable_logging);
 
-void gdata_mock_server_log_message_chunk (GDataMockServer *self, const gchar *message_chunk);
+void gdata_mock_server_received_message_chunk (GDataMockServer *self, const gchar *message_chunk);
 
 SoupAddress *gdata_mock_server_get_address (GDataMockServer *self);
 guint gdata_mock_server_get_port (GDataMockServer *self);


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