[grilo] net: Support mocking of network answers
- From: Juan A. Suarez Romero <jasuarez src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [grilo] net: Support mocking of network answers
- Date: Mon, 22 Oct 2012 17:05:18 +0000 (UTC)
commit 7b168fcff60cdaf99f5967d0754777793c64bbcc
Author: Jens Georg <jensg openismus com>
Date: Thu Oct 11 15:09:09 2012 +0200
net: Support mocking of network answers
Mock network answers of webservices through predefined files. This is useful
for offline testing of plug-ins that provide sources from webservices. See the
header of libs/net/grl-net-mock.c for full documentation.
libs/net/Makefile.am | 3 +-
libs/net/grl-net-mock.c | 262 ++++++++++++++++++++++++++++++++++++++
libs/net/grl-net-mock.h | 49 +++++++
libs/net/grl-net-private.c | 43 ++++++
libs/net/grl-net-private.h | 4 +
libs/net/grl-net-soup-stable.c | 6 +-
libs/net/grl-net-soup-unstable.c | 5 +
libs/net/grl-net-wc.c | 24 +++-
8 files changed, 390 insertions(+), 6 deletions(-)
---
diff --git a/libs/net/Makefile.am b/libs/net/Makefile.am
index c6d8fb3..fd05d58 100644
--- a/libs/net/Makefile.am
+++ b/libs/net/Makefile.am
@@ -13,7 +13,8 @@ libgrlnet_ GRL_MAJORMINOR@_la_DEPENDENCIES = \
libgrlnet_ GRL_MAJORMINOR@_la_SOURCES = \
grl-net-private.c \
- grl-net-wc.c
+ grl-net-wc.c \
+ grl-net-mock.c
libgrlnet_ GRL_MAJORMINOR@_la_CFLAGS = \
-I $(top_srcdir)/src \
diff --git a/libs/net/grl-net-mock.c b/libs/net/grl-net-mock.c
new file mode 100644
index 0000000..2bc21ae
--- /dev/null
+++ b/libs/net/grl-net-mock.c
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2012 Openismus GmbH
+ *
+ * Authors: Jens Georg <jensg openismus com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+/*
+ * Mock network answers of webservices through predefined files. Useful for
+ * offline testing of plug-ins that provide sources from webservices.
+ *
+ * For configuring mock answers, a simple .ini file is used. The file is split
+ * into a "default" section and one section per URL.
+ * [default]
+ * version = 1
+ * ignore-parameters = [true,false]
+ *
+ * [http://www.example.com]
+ * data = content/of/response.txt
+ * timeout = 500
+ *
+ * Explanation of [default] parameters
+ * version needs to be "1"
+ * ignore-parameters can be used to map urls to sections without paying
+ * attention to query parameters, so that http://www.example.com?q=test+query
+ * will also match http://www.example.com . Default for this parameter is
+ * "false".
+ *
+ * Explanation of [url] sections
+ * The section title is used to map urls to response files.
+ * "data" is a path to a text file containing the raw response of the websserver.
+ * The path may be relative to the configuration file or an absolute path.
+ * "timeout" may be used to delay the response and in seconds. The default is
+ * don't delay at all.
+ * If you want to provoke a "not found" error, skip the "data" parameter.
+ *
+ * The name of the configuration file is either "grl-mock-data.ini" which is
+ * expected to be in the current directory or can be overridden by setting the
+ * environment variable GRL_REQUEST_MOCK_FILE.
+ *
+ * An easy way to capture the responses is to run your application with the
+ * environment variable GRL_WEB_CAPTURE_DIR. GrlNetWc will then write all
+ * each response into a file following the pattern "<url>-timestamp". If the
+ * directory does not exist yet it will be created.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <glib/gstdio.h>
+#include <gio/gio.h>
+#include <libsoup/soup.h>
+
+#define _GRILO_H_INSIDE_
+#include <grl-log.h>
+
+#include "grl-net-mock.h"
+
+#define GRL_MOCK_VERSION 1
+
+static GKeyFile *config;
+gboolean ignore_parameters;
+static char *base_path;
+
+void
+get_url_mocked (GrlNetWc *self,
+ const char *url,
+ GHashTable *headers,
+ GAsyncResult *result,
+ GCancellable *cancellable)
+{
+ char *data_file, *full_path;
+ GError *error = NULL;
+ GStatBuf stat_buf;
+ char *new_url;
+
+ if (ignore_parameters) {
+ SoupURI *uri = soup_uri_new (url);
+ soup_uri_set_query (uri, NULL);
+ new_url = soup_uri_to_string (uri, FALSE);
+ soup_uri_free (uri);
+ } else {
+ new_url = g_strdup (url);
+ }
+
+ if (!config) {
+ g_simple_async_result_set_error (G_SIMPLE_ASYNC_RESULT (result),
+ GRL_NET_WC_ERROR,
+ GRL_NET_WC_ERROR_NETWORK_ERROR,
+ "%s",
+ "No mock definition found");
+ g_simple_async_result_complete_in_idle (G_SIMPLE_ASYNC_RESULT (result));
+ return;
+ }
+
+ data_file = g_key_file_get_value (config, new_url, "data", &error);
+ if (error) {
+ g_simple_async_result_set_error (G_SIMPLE_ASYNC_RESULT (result),
+ GRL_NET_WC_ERROR,
+ GRL_NET_WC_ERROR_NOT_FOUND,
+ "Could not find mock content: %s",
+ error->message);
+ g_error_free (error);
+ g_simple_async_result_complete_in_idle (G_SIMPLE_ASYNC_RESULT (result));
+ return;
+ }
+ if (data_file[0] != '/') {
+ full_path = g_build_filename (base_path, data_file, NULL);
+ } else {
+ full_path = data_file;
+ data_file = NULL;
+ }
+
+ if (g_stat (full_path, &stat_buf) < 0) {
+ g_simple_async_result_set_error (G_SIMPLE_ASYNC_RESULT (result),
+ GRL_NET_WC_ERROR,
+ GRL_NET_WC_ERROR_NOT_FOUND,
+ "%s",
+ "Could not access mock content");
+ g_simple_async_result_complete_in_idle (G_SIMPLE_ASYNC_RESULT (result));
+ if (data_file)
+ g_free (data_file);
+ if (full_path)
+ g_free (full_path);
+ return;
+ }
+ if (data_file)
+ g_free (data_file);
+ if (full_path)
+ g_free (full_path);
+
+ g_simple_async_result_set_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result),
+ new_url,
+ NULL);
+ g_simple_async_result_complete_in_idle (G_SIMPLE_ASYNC_RESULT (result));
+}
+
+void
+get_content_mocked (GrlNetWc *self,
+ void *op,
+ gchar **content,
+ gsize *length)
+{
+ char *url = (char *) op;
+ char *data_file = NULL, *full_path = NULL;
+ GError *error = NULL;
+
+ data_file = g_key_file_get_value (config, url, "data", NULL);
+ if (data_file[0] != '/') {
+ full_path = g_build_filename (base_path, data_file, NULL);
+ } else {
+ full_path = data_file;
+ data_file = NULL;
+ }
+ g_file_get_contents (full_path, content, length, &error);
+
+ if (data_file)
+ g_free (data_file);
+
+ if (full_path)
+ g_free (full_path);
+}
+
+void init_mock_requester (GrlNetWc *self)
+{
+ const char *env;
+ GError *error = NULL;
+ int version;
+ GFile *file, *parent;
+
+ base_path = NULL;
+
+ config = g_key_file_new ();
+
+ env = g_getenv ("GRL_REQUEST_MOCK_FILE");
+ if (env) {
+ GRL_DEBUG ("Trying to load mock file %s", env);
+ g_key_file_load_from_file (config,
+ env,
+ G_KEY_FILE_NONE,
+ &error);
+ }
+ if (error) {
+ GRL_WARNING ("Failed to load mock file %s: %s", env, error->message);
+ g_error_free (error);
+ error = NULL;
+ }
+
+ /* Check if we managed to load a file */
+ version = g_key_file_get_integer (config, "default", "version", &error);
+ if (error || version < GRL_MOCK_VERSION) {
+ if (error) {
+ g_error_free (error);
+ error = NULL;
+ } else {
+ GRL_WARNING ("Unsupported mock version %d, trying default file.", version);
+ }
+
+ env = "grl-mock-data.ini";
+
+ g_key_file_load_from_file (config,
+ env,
+ G_KEY_FILE_NONE,
+ &error);
+ if (error) {
+ GRL_WARNING ("Failed to load default mock file: %s", error->message);
+ g_error_free (error);
+
+ g_key_file_unref (config);
+ config = NULL;
+ }
+ }
+
+ if (!config) {
+ return;
+ }
+
+ ignore_parameters = g_key_file_get_boolean (config, "default", "ignore-parameters", &error);
+ if (error) {
+ ignore_parameters = FALSE;
+ g_error_free (error);
+ }
+
+ file = g_file_new_for_commandline_arg (env);
+ parent = g_file_get_parent (file);
+ g_object_unref (file);
+
+ base_path = g_file_get_path (parent);
+ g_object_unref (parent);
+}
+
+void finalize_mock_requester (GrlNetWc *self)
+{
+ if (config) {
+ g_key_file_unref (config);
+ }
+
+ if (base_path) {
+ g_free (base_path);
+ }
+}
+
+void free_mock_op_res (void *op)
+{
+ g_free (op);
+}
diff --git a/libs/net/grl-net-mock.h b/libs/net/grl-net-mock.h
new file mode 100644
index 0000000..3b57fc6
--- /dev/null
+++ b/libs/net/grl-net-mock.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2012 Openismus GmbH
+ *
+ * Authors: Jens Georg <jensg openismus com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef _GRL_NET_MOCK_H_
+#define _GRL_NET_MOCK_H_
+
+#include "grl-net-wc.h"
+
+#define GRL_ENV_NET_MOCKED "GRL_NET_MOCKED"
+
+#define GRL_NET_IS_MOCKED (g_getenv (GRL_ENV_NET_MOCKED))
+
+void get_url_mocked (GrlNetWc *self,
+ const char *url,
+ GHashTable *headers,
+ GAsyncResult *result,
+ GCancellable *cancellable) G_GNUC_INTERNAL;
+
+void get_content_mocked (GrlNetWc *self,
+ void *op,
+ gchar **content,
+ gsize *length) G_GNUC_INTERNAL;
+
+void init_mock_requester (GrlNetWc *self) G_GNUC_INTERNAL;
+
+void finalize_mock_requester (GrlNetWc *self) G_GNUC_INTERNAL;
+
+void free_mock_op_res (void *op) G_GNUC_INTERNAL;
+
+#endif /* _GRL_NET_MOCK_H_ */
diff --git a/libs/net/grl-net-private.c b/libs/net/grl-net-private.c
index 7c1e557..401c913 100644
--- a/libs/net/grl-net-private.c
+++ b/libs/net/grl-net-private.c
@@ -89,3 +89,46 @@ parse_error (guint status,
g_message ("Unhandled status: %s", soup_status_get_phrase (status));
}
}
+
+void
+init_dump_directory ()
+{
+ const char *path;
+
+ path = g_getenv ("GRL_WEB_CAPTURE_DIR");
+ if (!path)
+ return;
+
+ g_mkdir_with_parents (path, 0700);
+}
+
+void
+dump_data (SoupURI *soup_uri,
+ const char *buffer,
+ const gsize length)
+{
+ const char *capture_dir;
+ char *uri, *escaped_uri, *file;
+ GError *error = NULL;
+
+ capture_dir = g_getenv ("GRL_WEB_CAPTURE_DIR");
+
+ if (!capture_dir)
+ return;
+
+ uri = soup_uri_to_string (soup_uri, FALSE);
+ escaped_uri = g_uri_escape_string (uri, NULL, FALSE);
+ g_free (uri);
+ file = g_strdup_printf ("%s"G_DIR_SEPARATOR_S"%s-%"G_GINT64_FORMAT,
+ capture_dir,
+ escaped_uri,
+ g_get_real_time ());
+ g_free (escaped_uri);
+
+ if (!g_file_set_contents (file, buffer, length, &error)) {
+ GRL_WARNING ("Could not write contents to disk: %s", error->message);
+ g_error_free (error);
+ }
+
+ g_free (file);
+}
diff --git a/libs/net/grl-net-private.h b/libs/net/grl-net-private.h
index 3d15a0c..f0ddc65 100644
--- a/libs/net/grl-net-private.h
+++ b/libs/net/grl-net-private.h
@@ -76,6 +76,10 @@ guint cache_get_size (GrlNetWc *self);
void free_op_res (void *op);
+void init_dump_directory (void) G_GNUC_INTERNAL;
+
+void dump_data (SoupURI *soup_uri, const gchar *data, gsize length) G_GNUC_INTERNAL;
+
G_END_DECLS
#endif /* _GRL_NET_PRIVATE_H_ */
diff --git a/libs/net/grl-net-soup-stable.c b/libs/net/grl-net-soup-stable.c
index 70ae7ec..cdd615d 100644
--- a/libs/net/grl-net-soup-stable.c
+++ b/libs/net/grl-net-soup-stable.c
@@ -147,12 +147,16 @@ get_content (GrlNetWc *self,
if (length)
*length = (gsize) msg->response_body->length;
+
+ dump_data (soup_message_get_uri (msg),
+ msg->response_body->data,
+ msg->response_body->length);
}
void
init_requester (GrlNetWc *self)
{
- /* noop */
+ init_dump_directory ();
}
void
diff --git a/libs/net/grl-net-soup-unstable.c b/libs/net/grl-net-soup-unstable.c
index cc81c38..2178f98 100644
--- a/libs/net/grl-net-soup-unstable.c
+++ b/libs/net/grl-net-soup-unstable.c
@@ -52,6 +52,7 @@ init_requester (GrlNetWc *self)
priv->requester = soup_requester_new ();
soup_session_add_feature (priv->session,
SOUP_SESSION_FEATURE (priv->requester));
+ init_dump_directory ();
}
void
@@ -287,6 +288,10 @@ get_content (GrlNetWc *self,
GrlNetWcPrivate *priv = self->priv;
struct request_res *rr = op;
+ dump_data (soup_request_get_uri (rr->request),
+ rr->buffer,
+ rr->offset);
+
if (priv->previous_data)
g_free (priv->previous_data);
diff --git a/libs/net/grl-net-wc.c b/libs/net/grl-net-wc.c
index 85efe6d..4683b76 100644
--- a/libs/net/grl-net-wc.c
+++ b/libs/net/grl-net-wc.c
@@ -44,6 +44,7 @@
#include <grilo.h>
#include "grl-net-wc.h"
#include "grl-net-private.h"
+#include "grl-net-mock.h"
#define GRL_LOG_DOMAIN_DEFAULT wc_log_domain
GRL_LOG_DOMAIN(wc_log_domain);
@@ -191,6 +192,7 @@ grl_net_wc_init (GrlNetWc *wc)
set_thread_context (wc);
init_requester (wc);
+ init_mock_requester (wc);
}
static void
@@ -203,6 +205,7 @@ grl_net_wc_finalize (GObject *object)
cache_down (wc);
finalize_requester (wc);
+ finalize_mock_requester (wc);
g_queue_free (wc->priv->pending);
g_object_unref (wc->priv->session);
@@ -295,7 +298,10 @@ get_url_delayed (gpointer user_data)
g_assert (c == d);
}
- get_url_now (c->self, c->url, c->headers, c->result, c->cancellable);
+ if (GRL_NET_IS_MOCKED)
+ get_url_mocked (c->self, c->url, c->headers, c->result, c->cancellable);
+ else
+ get_url_now (c->self, c->url, c->headers, c->result, c->cancellable);
g_free (c->url);
if (c->headers) {
@@ -321,7 +327,10 @@ get_url (GrlNetWc *self,
g_get_current_time (&now);
if ((now.tv_sec - priv->last_request.tv_sec) > priv->throttling) {
- get_url_now (self, url, headers, result, cancellable);
+ if (GRL_NET_IS_MOCKED)
+ get_url_mocked (self, url, headers, result, cancellable);
+ else
+ get_url_now (self, url, headers, result, cancellable);
g_get_current_time (&priv->last_request);
return;
@@ -477,6 +486,7 @@ grl_net_wc_request_with_headers_hash_async (GrlNetWc *self,
get_url (self, uri, headers, G_ASYNC_RESULT (result), cancellable);
}
+
/**
* grl_net_wc_request_finish:
* @self: a #GrlNetWc instance
@@ -515,10 +525,16 @@ grl_net_wc_request_finish (GrlNetWc *self,
goto end_func;
}
- get_content(self, op, content, length);
+ if (GRL_NET_IS_MOCKED)
+ get_content_mocked (self, op, content, length);
+ else
+ get_content(self, op, content, length);
end_func:
- free_op_res (op);
+ if (GRL_NET_IS_MOCKED)
+ free_mock_op_res (op);
+ else
+ free_op_res (op);
return ret;
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]