summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Winship <danw@gnome.org>2012-12-25 18:17:05 -0500
committerDan Winship <danw@gnome.org>2012-12-26 15:21:15 -0500
commite25e9ee4704398f0f276f6f9e91d948b8924356e (patch)
tree73ba4f70fa3558f7a4cbe009f2aec5b68af727ed
parent24c61497852838b10854b79a8f332f819a72c2eb (diff)
downloadlibsoup-e25e9ee4704398f0f276f6f9e91d948b8924356e.tar.gz
libsoup-e25e9ee4704398f0f276f6f9e91d948b8924356e.tar.bz2
libsoup-e25e9ee4704398f0f276f6f9e91d948b8924356e.zip
tests: add cache-test
Add a test of basic cache functionality (finally!) Still needs more tests...
-rw-r--r--tests/Makefile.am3
-rw-r--r--tests/cache-test.c441
2 files changed, 444 insertions, 0 deletions
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 54ce7bdb..a9184055 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -12,6 +12,7 @@ LIBS = \
$(GLIB_LIBS)
noinst_PROGRAMS = \
+ cache-test \
chunk-test \
coding-test \
connection-test \
@@ -47,6 +48,7 @@ noinst_DATA = soup-tests.gresource
TEST_SRCS = test-utils.c test-utils.h
auth_test_SOURCES = auth-test.c $(TEST_SRCS)
+cache_test_SOURCES = cache-test.c $(TEST_SRCS)
chunk_test_SOURCES = chunk-test.c $(TEST_SRCS)
coding_test_SOURCES = coding-test.c $(TEST_SRCS)
connection_test_SOURCES = connection-test.c $(TEST_SRCS)
@@ -116,6 +118,7 @@ soup-tests.gresource: soup-tests.gresource.xml $(shell $(GLIB_COMPILE_RESOURCES)
$(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(srcdir) $<
TESTS = \
+ cache-test \
chunk-test \
coding-test \
connection-test \
diff --git a/tests/cache-test.c b/tests/cache-test.c
new file mode 100644
index 00000000..8f8d2b04
--- /dev/null
+++ b/tests/cache-test.c
@@ -0,0 +1,441 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright 2012 Red Hat, Inc.
+ */
+
+#include "test-utils.h"
+
+static void
+server_callback (SoupServer *server, SoupMessage *msg,
+ const char *path, GHashTable *query,
+ SoupClientContext *context, gpointer data)
+{
+ const char *last_modified, *etag;
+ const char *header;
+ guint status = SOUP_STATUS_OK;
+
+ if (msg->method != SOUP_METHOD_GET && msg->method != SOUP_METHOD_POST) {
+ soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
+ return;
+ }
+
+ header = soup_message_headers_get_one (msg->request_headers,
+ "Test-Set-Expires");
+ if (header) {
+ soup_message_headers_append (msg->response_headers,
+ "Expires",
+ header);
+ }
+
+ header = soup_message_headers_get_one (msg->request_headers,
+ "Test-Set-Cache-Control");
+ if (header) {
+ soup_message_headers_append (msg->response_headers,
+ "Cache-Control",
+ header);
+ }
+
+ last_modified = soup_message_headers_get_one (msg->request_headers,
+ "Test-Set-Last-Modified");
+ if (last_modified) {
+ soup_message_headers_append (msg->response_headers,
+ "Last-Modified",
+ last_modified);
+ }
+
+ etag = soup_message_headers_get_one (msg->request_headers,
+ "Test-Set-ETag");
+ if (etag) {
+ soup_message_headers_append (msg->response_headers,
+ "ETag",
+ etag);
+ }
+
+
+ header = soup_message_headers_get_one (msg->request_headers,
+ "If-Modified-Since");
+ if (header && last_modified) {
+ SoupDate *date;
+ time_t lastmod, check;
+
+ date = soup_date_new_from_string (last_modified);
+ lastmod = soup_date_to_time_t (date);
+ soup_date_free (date);
+
+ date = soup_date_new_from_string (header);
+ check = soup_date_to_time_t (date);
+ soup_date_free (date);
+
+ if (lastmod <= check)
+ status = SOUP_STATUS_NOT_MODIFIED;
+ }
+
+ header = soup_message_headers_get_one (msg->request_headers,
+ "If-None-Match");
+ if (header && etag) {
+ if (!strcmp (header, etag))
+ status = SOUP_STATUS_NOT_MODIFIED;
+ }
+
+ if (status == SOUP_STATUS_OK) {
+ GChecksum *sum;
+ const char *body;
+
+ sum = g_checksum_new (G_CHECKSUM_SHA256);
+ g_checksum_update (sum, (guchar *)path, strlen (path));
+ if (last_modified)
+ g_checksum_update (sum, (guchar *)last_modified, strlen (last_modified));
+ if (etag)
+ g_checksum_update (sum, (guchar *)etag, strlen (etag));
+ body = g_checksum_get_string (sum);
+ soup_message_set_response (msg, "text/plain",
+ SOUP_MEMORY_COPY,
+ body, strlen (body) + 1);
+ g_checksum_free (sum);
+ }
+ soup_message_set_status (msg, status);
+}
+
+static gboolean
+is_network_stream (GInputStream *stream)
+{
+ while (G_IS_FILTER_INPUT_STREAM (stream))
+ stream = G_FILTER_INPUT_STREAM (stream)->base_stream;
+
+ return !G_IS_FILE_INPUT_STREAM (stream);
+}
+
+static char *do_request (SoupSession *session,
+ SoupURI *base_uri,
+ const char *method,
+ const char *path,
+ ...) G_GNUC_NULL_TERMINATED;
+
+static gboolean last_request_hit_network;
+static gboolean last_request_validated;
+
+static char *
+do_request (SoupSession *session,
+ SoupURI *base_uri,
+ const char *method,
+ const char *path,
+ ...)
+{
+ SoupRequestHTTP *req;
+ GInputStream *stream;
+ SoupURI *uri;
+ va_list ap;
+ const char *header, *value;
+ char buf[256];
+ gsize nread;
+ GError *error = NULL;
+
+ last_request_validated = last_request_hit_network = FALSE;
+
+ uri = soup_uri_new_with_base (base_uri, path);
+ req = soup_session_request_http_uri (session, method, uri, NULL);
+ soup_uri_free (uri);
+
+ va_start (ap, path);
+ while ((header = va_arg (ap, const char *))) {
+ value = va_arg (ap, const char *);
+ soup_message_headers_append (req->request_headers,
+ header, value);
+ }
+
+ stream = soup_test_request_send (SOUP_REQUEST (req), NULL, &error);
+ if (!stream) {
+ debug_printf (1, " could not send request: %s\n",
+ error->message);
+ g_error_free (error);
+ g_object_unref (req);
+ return NULL;
+ }
+
+ last_request_hit_network = is_network_stream (stream);
+
+ g_input_stream_read_all (stream, buf, sizeof (buf), &nread,
+ NULL, &error);
+ if (error) {
+ debug_printf (1, " could not read response: %s\n",
+ error->message);
+ g_clear_error (&error);
+ }
+ soup_test_request_close_stream (SOUP_REQUEST (req), stream,
+ NULL, &error);
+ if (error) {
+ debug_printf (1, " could not close stream: %s\n",
+ error->message);
+ g_clear_error (&error);
+ }
+ g_object_unref (stream);
+ g_object_unref (req);
+
+ /* Cache writes are G_PRIORITY_LOW, so they won't have happened yet... */
+ soup_cache_flush ((SoupCache *)soup_session_get_feature (session, SOUP_TYPE_CACHE));
+
+ return nread ? g_memdup (buf, nread) : g_strdup ("");
+}
+
+static void
+request_started (SoupSession *session, SoupMessage *msg,
+ SoupSocket *socket)
+{
+ if (soup_message_headers_get_one (msg->request_headers,
+ "If-Modified-Since") ||
+ soup_message_headers_get_one (msg->request_headers,
+ "If-None-Match")) {
+ debug_printf (2, " Conditional request for %s\n",
+ soup_message_get_uri (msg)->path);
+ last_request_validated = TRUE;
+ }
+}
+
+static void
+do_basics_test (SoupURI *base_uri)
+{
+ SoupSession *session;
+ SoupCache *cache;
+ char *cache_dir;
+ char *body1, *body2, *body3, *body4, *body5, *cmp;
+
+ debug_printf (1, "Cache basics\n");
+
+ cache_dir = g_dir_make_tmp ("cache-test-XXXXXX", NULL);
+ debug_printf (2, " Caching to %s\n", cache_dir);
+ cache = soup_cache_new (cache_dir, SOUP_CACHE_SINGLE_USER);
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
+ SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
+ SOUP_SESSION_ADD_FEATURE, cache,
+ NULL);
+ g_signal_connect (session, "request-started",
+ G_CALLBACK (request_started), NULL);
+
+ debug_printf (2, " Initial requests\n");
+ body1 = do_request (session, base_uri, "GET", "/1",
+ "Test-Set-Expires", "Fri, 01 Jan 2100 00:00:00 GMT",
+ NULL);
+ body2 = do_request (session, base_uri, "GET", "/2",
+ "Test-Set-Last-Modified", "Fri, 01 Jan 2010 00:00:00 GMT",
+ NULL);
+ body3 = do_request (session, base_uri, "GET", "/3",
+ "Test-Set-Last-Modified", "Fri, 01 Jan 2010 00:00:00 GMT",
+ "Test-Set-Cache-Control", "must-revalidate",
+ NULL);
+ body4 = do_request (session, base_uri, "GET", "/4",
+ "Test-Set-ETag", "\"abcdefg\"",
+ "Test-Set-Cache-Control", "must-revalidate",
+ NULL);
+ body5 = do_request (session, base_uri, "GET", "/5",
+ "Test-Set-Cache-Control", "no-cache",
+ NULL);
+
+
+ /* Resource with future Expires should have been cached */
+ debug_printf (1, " Fresh cached resource\n");
+ cmp = do_request (session, base_uri, "GET", "/1",
+ NULL);
+ if (last_request_hit_network) {
+ debug_printf (1, " Request for /1 not filled from cache!\n");
+ errors++;
+ }
+ if (strcmp (body1, cmp) != 0) {
+ debug_printf (1, " Cached response (%s) did not match original (%s)\n",
+ cmp, body1);
+ errors++;
+ }
+ g_free (cmp);
+
+
+ /* Resource with long-ago Last-Modified should have been cached */
+ debug_printf (1, " Heuristically-fresh cached resource\n");
+ cmp = do_request (session, base_uri, "GET", "/2",
+ NULL);
+ if (last_request_hit_network) {
+ debug_printf (1, " Request for /2 not filled from cache!\n");
+ errors++;
+ }
+ if (strcmp (body2, cmp) != 0) {
+ debug_printf (1, " Cached response (%s) did not match original (%s)\n",
+ cmp, body2);
+ errors++;
+ }
+ g_free (cmp);
+
+
+ /* Adding a query string should bypass the cache but not invalidate it */
+ debug_printf (1, " Fresh cached resource with a query\n");
+ cmp = do_request (session, base_uri, "GET", "/1?attr=value",
+ NULL);
+ if (!last_request_hit_network) {
+ debug_printf (1, " Request for /1?attr=value filled from cache!\n");
+ errors++;
+ }
+ g_free (cmp);
+ debug_printf (2, " Second request\n");
+ cmp = do_request (session, base_uri, "GET", "/1",
+ NULL);
+ if (last_request_hit_network) {
+ debug_printf (1, " Second request for /1 not filled from cache!\n");
+ errors++;
+ }
+ if (strcmp (body1, cmp) != 0) {
+ debug_printf (1, " Cached response (%s) did not match original (%s)\n",
+ cmp, body1);
+ errors++;
+ }
+ g_free (cmp);
+
+
+ /* Last-Modified + must-revalidate causes a conditional request */
+ debug_printf (1, " Unchanged must-revalidate resource w/ Last-Modified\n");
+ cmp = do_request (session, base_uri, "GET", "/3",
+ "Test-Set-Last-Modified", "Fri, 01 Jan 2010 00:00:00 GMT",
+ "Test-Set-Cache-Control", "must-revalidate",
+ NULL);
+ if (!last_request_validated) {
+ debug_printf (1, " Request for /3 not validated!\n");
+ errors++;
+ }
+ if (last_request_hit_network) {
+ debug_printf (1, " Request for /3 not filled from cache!\n");
+ errors++;
+ }
+ if (strcmp (body3, cmp) != 0) {
+ debug_printf (1, " Cached response (%s) did not match original (%s)\n",
+ cmp, body3);
+ errors++;
+ }
+ g_free (cmp);
+
+
+ /* Validation failure should update cache */
+ debug_printf (1, " Changed must-revalidate resource w/ Last-Modified\n");
+ cmp = do_request (session, base_uri, "GET", "/3",
+ "Test-Set-Last-Modified", "Sat, 02 Jan 2010 00:00:00 GMT",
+ "Test-Set-Cache-Control", "must-revalidate",
+ NULL);
+ if (!last_request_validated) {
+ debug_printf (1, " Request for /3 not validated!\n");
+ errors++;
+ }
+ if (!last_request_hit_network) {
+ debug_printf (1, " Request for /3 filled from cache!\n");
+ errors++;
+ }
+ if (strcmp (body3, cmp) == 0) {
+ debug_printf (1, " Request for /3 returned cached response\n");
+ errors++;
+ }
+ g_free (cmp);
+
+#if 0 /* This doesn't work... is the test wrong or is SoupCache? */
+ debug_printf (2, " Second request\n");
+ cmp = do_request (session, base_uri, "GET", "/3",
+ "Test-Set-Last-Modified", "Sat, 02 Jan 2010 00:00:00 GMT",
+ "Test-Set-Cache-Control", "must-revalidate",
+ NULL);
+ if (!last_request_validated) {
+ debug_printf (1, " Second request for /3 not validated!\n");
+ errors++;
+ }
+ if (last_request_hit_network) {
+ debug_printf (1, " Second request for /3 not filled from cache!\n");
+ errors++;
+ }
+ if (strcmp (body3, cmp) == 0) {
+ debug_printf (1, " Replacement body for /3 not cached!\n");
+ errors++;
+ }
+ g_free (cmp);
+#endif
+
+
+ /* ETag + must-revalidate causes a conditional request */
+ debug_printf (1, " Unchanged must-revalidate resource w/ ETag\n");
+ cmp = do_request (session, base_uri, "GET", "/4",
+ "Test-Set-ETag", "\"abcdefg\"",
+ NULL);
+ if (!last_request_validated) {
+ debug_printf (1, " Request for /4 not validated!\n");
+ errors++;
+ }
+ if (last_request_hit_network) {
+ debug_printf (1, " Request for /4 not filled from cache!\n");
+ errors++;
+ }
+ if (strcmp (body4, cmp) != 0) {
+ debug_printf (1, " Cached response (%s) did not match original (%s)\n",
+ cmp, body4);
+ errors++;
+ }
+ g_free (cmp);
+
+
+ /* Cache-Control: no-cache prevents caching */
+ debug_printf (1, " Uncacheable resource\n");
+ cmp = do_request (session, base_uri, "GET", "/5",
+ "Test-Set-Cache-Control", "no-cache",
+ NULL);
+ if (!last_request_hit_network) {
+ debug_printf (1, " Request for /5 filled from cache!\n");
+ errors++;
+ }
+ if (strcmp (body5, cmp) != 0) {
+ debug_printf (1, " Re-retrieved response (%s) did not match original (%s)\n",
+ cmp, body5);
+ errors++;
+ }
+ g_free (cmp);
+
+
+ /* PUT to a URI invalidates the cache entry */
+ debug_printf (1, " Invalidating and re-requesting a cached resource\n");
+ cmp = do_request (session, base_uri, "PUT", "/1",
+ NULL);
+ if (!last_request_hit_network) {
+ debug_printf (1, " PUT filled from cache!\n");
+ errors++;
+ }
+ g_free (cmp);
+ cmp = do_request (session, base_uri, "GET", "/1",
+ NULL);
+ if (!last_request_hit_network) {
+ debug_printf (1, " PUT failed to invalidate cache entry!\n");
+ errors++;
+ }
+ g_free (cmp);
+
+
+ soup_test_session_abort_unref (session);
+ g_object_unref (cache);
+
+ g_free (cache_dir);
+ g_free (body1);
+ g_free (body2);
+ g_free (body3);
+ g_free (body4);
+ g_free (body5);
+}
+
+int
+main (int argc, char **argv)
+{
+ SoupServer *server;
+ SoupURI *base_uri;
+
+ test_init (argc, argv, NULL);
+
+ server = soup_test_server_new (TRUE);
+ soup_server_add_handler (server, NULL, server_callback, NULL, NULL);
+ base_uri = soup_uri_new ("http://127.0.0.1/");
+ soup_uri_set_port (base_uri, soup_server_get_port (server));
+
+ do_basics_test (base_uri);
+
+ soup_uri_free (base_uri);
+ soup_test_server_quit_unref (server);
+
+ test_cleanup ();
+ return errors != 0;
+}