diff options
Diffstat (limited to 'tests')
59 files changed, 8451 insertions, 5138 deletions
diff --git a/tests/Makefile.am b/tests/Makefile.am index 995196c3..a8b9d019 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,143 +1,136 @@ -INCLUDES = \ +include $(top_srcdir)/glib-tap.mk + +AM_CPPFLAGS = \ -I$(top_srcdir) \ - -DSRCDIR=\""$(abs_srcdir)"\" \ - -DLIBSOUP_DISABLE_DEPRECATED \ - $(SOUP_MAINTAINER_FLAGS) \ - $(XML_CFLAGS) \ $(GLIB_CFLAGS) LIBS = \ $(top_builddir)/libsoup/libsoup-2.4.la \ - $(LIBGNUTLS_LIBS) \ $(GLIB_LIBS) -noinst_PROGRAMS = \ - chunk-test \ - coding-test \ - connection-test \ - context-test \ - continue-test \ - cookies-test \ - date \ - dns \ - forms-test \ - get \ - header-parsing \ - misc-test \ - ntlm-test \ - redirect-test \ - requester-test \ - simple-httpd \ - simple-proxy \ - sniffing-test \ - ssl-test \ - streaming-test \ - timeout-test \ - tld-test \ - uri-parsing \ - $(CURL_TESTS) \ - $(APACHE_TESTS) \ - $(XMLRPC_TESTS) - -TEST_SRCS = test-utils.c test-utils.h - -auth_test_SOURCES = auth-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) -context_test_SOURCES = context-test.c $(TEST_SRCS) -continue_test_SOURCES = continue-test.c $(TEST_SRCS) -cookies_test_SOURCES = cookies-test.c $(TEST_SRCS) -date_SOURCES = date.c $(TEST_SRCS) -dns_SOURCES = dns.c -forms_test_SOURCES = forms-test.c $(TEST_SRCS) -get_SOURCES = get.c -if BUILD_LIBSOUP_GNOME -get_LDADD = $(top_builddir)/libsoup/libsoup-gnome-2.4.la -endif -header_parsing_SOURCES = header-parsing.c $(TEST_SRCS) -misc_test_SOURCES = misc-test.c $(TEST_SRCS) -ntlm_test_SOURCES = ntlm-test.c $(TEST_SRCS) -proxy_test_SOURCES = proxy-test.c $(TEST_SRCS) -pull_api_SOURCES = pull-api.c $(TEST_SRCS) -range_test_SOURCES = range-test.c $(TEST_SRCS) -redirect_test_SOURCES = redirect-test.c $(TEST_SRCS) -requester_test_SOURCES = requester-test.c $(TEST_SRCS) -server_auth_test_SOURCES = server-auth-test.c $(TEST_SRCS) -simple_httpd_SOURCES = simple-httpd.c -simple_proxy_SOURCES = simple-proxy.c -sniffing_test_SOURCES = sniffing-test.c $(TEST_SRCS) -ssl_test_SOURCES = ssl-test.c $(TEST_SRCS) -streaming_test_SOURCES = streaming-test.c $(TEST_SRCS) -timeout_test_SOURCES = timeout-test.c $(TEST_SRCS) -tld_test_SOURCES = tld-test.c $(TEST_SRCS) -uri_parsing_SOURCES = uri-parsing.c $(TEST_SRCS) -xmlrpc_test_SOURCES = xmlrpc-test.c $(TEST_SRCS) -xmlrpc_server_test_SOURCES = xmlrpc-server-test.c $(TEST_SRCS) +test_programs = \ + auth-test \ + cache-test \ + chunk-test \ + chunk-io-test \ + coding-test \ + connection-test \ + context-test \ + continue-test \ + cookies-test \ + date \ + forms-test \ + header-parsing \ + misc-test \ + multipart-test \ + no-ssl-test \ + ntlm-test \ + proxy-test \ + pull-api \ + range-test \ + redirect-test \ + requester-test \ + resource-test \ + session-test \ + server-auth-test \ + server-test \ + sniffing-test \ + socket-test \ + ssl-test \ + streaming-test \ + timeout-test \ + tld-test \ + uri-parsing \ + xmlrpc-server-test \ + xmlrpc-test + +test_extra_programs = \ + ntlm-test-helper \ + $(TESTS) + +test_data = \ + index.txt \ + soup-tests.gresource \ + test-cert.pem \ + test-key.pem \ + xmlrpc-server.php + +noinst_LTLIBRARIES += libtest.la + +libtest_la_SOURCES = \ + test-utils.c \ + test-utils.h + +LDADD = libtest.la if HAVE_APACHE -APACHE_TESTS = auth-test proxy-test pull-api range-test -endif -if HAVE_CURL -CURL_TESTS = forms-test server-auth-test -endif -if HAVE_XMLRPC_EPI_PHP -XMLRPC_TESTS = xmlrpc-test xmlrpc-server-test +if HAVE_APACHE_2_2 +httpd_conf_in = httpd.conf.22.in +else +httpd_conf_in = httpd.conf.24.in endif +httpd.conf: $(httpd_conf_in) + $(AM_V_GEN) sed -e 's,[@]srcdir@,$(srcdir),' \ + -e 's,[@]builddir@,$(builddir),' \ + -e 's,[@]APACHE_MODULE_DIR@,$(APACHE_MODULE_DIR),' \ + -e 's,[@]APACHE_PHP_MODULE_DIR@,$(APACHE_PHP_MODULE_DIR),' \ + -e 's,[@]APACHE_PHP_MODULE@,$(APACHE_PHP_MODULE),' \ + -e 's,[@]IF_HAVE_PHP@,$(IF_HAVE_PHP),' \ + -e 's,[@]APACHE_SSL_MODULE_DIR@,$(APACHE_SSL_MODULE_DIR),' \ + $< > $@ || rm -f $@ -TESTS = \ - chunk-test \ - coding-test \ - connection-test \ - context-test \ - continue-test \ - cookies-test \ - date \ - header-parsing \ - misc-test \ - ntlm-test \ - redirect-test \ - requester-test \ - sniffing-test \ - ssl-test \ - streaming-test \ - timeout-test \ - tld-test \ - uri-parsing \ - $(APACHE_TESTS) \ - $(CURL_TESTS) \ - $(XMLRPC_TESTS) - -RESOURCES = \ - resources/atom.xml \ - resources/home.gif \ - resources/html_binary.html \ - resources/mbox \ - resources/mbox.gz \ - resources/mbox.raw \ - resources/mbox.zlib \ - resources/ps_binary.ps \ - resources/rss20.xml \ - resources/test.html \ - resources/text_binary.txt - -EXTRA_DIST = \ +BUILT_SOURCES += httpd.conf +test_data += \ htdigest \ htpasswd \ - httpd.conf.in \ - index.txt \ - libsoup.supp \ - test-cert.pem \ - test-key.pem \ - xmlrpc-server.php \ + httpd.conf +endif + +RESOURCES = $(shell $(GLIB_COMPILE_RESOURCES) --sourcedir=$(srcdir) --generate-dependencies $(srcdir)/soup-tests.gresource.xml) + +soup-tests.gresource: soup-tests.gresource.xml $(RESOURCES) + $(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(srcdir) $< + +EXTRA_DIST += \ + htdigest \ + htpasswd \ + httpd.conf.22.in \ + httpd.conf.24.in \ + index.txt \ + libsoup.supp \ + soup-tests.gresource.xml \ + test-cert.pem \ + test-key.pem \ + xmlrpc-server.php \ $(RESOURCES) -if MISSING_REGRESSION_TEST_PACKAGES +DISTCLEANFILES += soup-tests.gresource httpd.conf + +TESTS_ENVIRONMENT += SOUP_TESTS_IN_MAKE_CHECK=1 + +check: start-httpd + check-local: check-TESTS - @echo "" - @echo "NOTE: some tests were not run due to missing packages:" $(MISSING_REGRESSION_TEST_PACKAGES) - @echo "" + @$(MAKE) kill-httpd + +.PHONY: start-httpd kill-httpd + +start-httpd: +if HAVE_APACHE_2_2 + @$(APACHE_HTTPD) -d $(abs_srcdir) -c "PidFile `pwd`/httpd.pid" -f `pwd`/httpd.conf -k start; +endif +if HAVE_APACHE_2_4 + @$(APACHE_HTTPD) -d $(abs_srcdir) -c "DefaultRuntimeDir `pwd`" -c "PidFile `pwd`/httpd.pid" -f `pwd`/httpd.conf -k start; endif kill-httpd: - $(APACHE_HTTPD) -d `pwd` -f httpd.conf -k stop +if HAVE_APACHE_2_2 + @if [ -f httpd.pid ]; then \ + $(APACHE_HTTPD) -d $(abs_srcdir) -c "PidFile `pwd`/httpd.pid" -f `pwd`/httpd.conf -k stop; \ + fi +endif +if HAVE_APACHE_2_4 + @if [ -f httpd.pid ]; then \ + $(APACHE_HTTPD) -d $(abs_srcdir) -c "DefaultRuntimeDir `pwd`" -c "PidFile `pwd`/httpd.pid" -f `pwd`/httpd.conf -k stop; \ + fi +endif diff --git a/tests/auth-test.c b/tests/auth-test.c index 85c4d414..8ed5cead 100644 --- a/tests/auth-test.c +++ b/tests/auth-test.c @@ -2,6 +2,7 @@ #include "test-utils.h" +static const char *base_uri; static GMainLoop *loop; typedef struct { @@ -35,10 +36,6 @@ typedef struct { guint final_status; } SoupAuthTest; -/* Will either point to main_tests or relogin_tests - */ -static SoupAuthTest *current_tests; - static SoupAuthTest main_tests[] = { { "No auth available, should fail", "Basic/realm1/", "", FALSE, "0", SOUP_STATUS_UNAUTHORIZED }, @@ -146,7 +143,12 @@ static SoupAuthTest main_tests[] = { "Digest/realm1/not/", "1", FALSE, /* should not be used */ "1", SOUP_STATUS_UNAUTHORIZED }, { "Make sure we've forgotten it", - "Digest/realm1/", "", FALSE, "0", SOUP_STATUS_UNAUTHORIZED } + "Digest/realm1/", "", FALSE, "0", SOUP_STATUS_UNAUTHORIZED }, + + { "Fail with URI-embedded password, then use right password in the authenticate signal", + "Basic/realm3/", "43", TRUE, "43", SOUP_STATUS_OK }, + + { NULL } }; static const char *auths[] = { @@ -202,14 +204,12 @@ handler (SoupMessage *msg, gpointer data) if (*expected) { exp = *expected - '0'; - if (auth != exp) { - debug_printf (1, " expected %s!\n", auths[exp]); - errors++; - } + soup_test_assert (auth == exp, + "expected %s", auths[exp]); memmove (expected, expected + 1, strlen (expected)); } else { - debug_printf (1, " expected to be finished\n"); - errors++; + soup_test_assert (*expected, + "expected to be finished"); } } @@ -217,18 +217,18 @@ static void authenticate (SoupSession *session, SoupMessage *msg, SoupAuth *auth, gboolean retrying, gpointer data) { - int *i = data; + SoupAuthTest *test = data; char *username, *password; char num; - if (!current_tests[*i].provided[0]) + if (!test->provided[0]) return; if (retrying) { - if (!current_tests[*i].provided[1]) + if (!test->provided[1]) return; - num = current_tests[*i].provided[1]; + num = test->provided[1]; } else - num = current_tests[*i].provided[0]; + num = test->provided[0]; username = g_strdup_printf ("user%c", num); password = g_strdup_printf ("realm%c", num); @@ -244,13 +244,10 @@ bug271540_sent (SoupMessage *msg, gpointer data) gboolean *authenticated = data; int auth = identify_auth (msg); - if (!*authenticated && auth) { - debug_printf (1, " using auth on message %d before authenticating!!??\n", n); - errors++; - } else if (*authenticated && !auth) { - debug_printf (1, " sent unauthenticated message %d after authenticating!\n", n); - errors++; - } + soup_test_assert (*authenticated || !auth, + "using auth on message %d before authenticating", n); + soup_test_assert (!*authenticated || auth, + "sent unauthenticated message %d after authenticating", n); } static void @@ -269,8 +266,8 @@ bug271540_authenticate (SoupSession *session, SoupMessage *msg, soup_auth_authenticate (auth, "user1", "realm1"); *authenticated = TRUE; } else { - debug_printf (1, " asked to authenticate message %d after authenticating!\n", n); - errors++; + soup_test_assert (!*authenticated, + "asked to authenticate message %d after authenticating", n); } } @@ -278,13 +275,8 @@ static void bug271540_finished (SoupSession *session, SoupMessage *msg, gpointer data) { int *left = data; - int n = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (msg), "#")); - if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) { - debug_printf (1, " got status '%d %s' on message %d!\n", - msg->status_code, msg->reason_phrase, n); - errors++; - } + soup_test_assert_message_status (msg, SOUP_STATUS_OK); (*left)--; if (!*left) @@ -292,7 +284,7 @@ bug271540_finished (SoupSession *session, SoupMessage *msg, gpointer data) } static void -do_pipelined_auth_test (const char *base_uri) +do_pipelined_auth_test (void) { SoupSession *session; SoupMessage *msg; @@ -300,7 +292,10 @@ do_pipelined_auth_test (const char *base_uri) char *uri; int i; - debug_printf (1, "Testing pipelined auth (bug 271540):\n"); + g_test_bug ("271540"); + + SOUP_TEST_SKIP_IF_NO_APACHE; + session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); authenticated = FALSE; @@ -437,28 +432,21 @@ do_digest_nonce_test (SoupSession *session, &got_401); got_401 = FALSE; soup_session_send_message (session, msg); - if (got_401 != expect_401) { - debug_printf (1, " %s request %s a 401 Unauthorized!\n", nth, - got_401 ? "got" : "did not get"); - errors++; - } - if (msg->status_code != SOUP_STATUS_OK) { - debug_printf (1, " %s request got status %d %s!\n", nth, - msg->status_code, msg->reason_phrase); - errors++; - } - if (errors == 0) - debug_printf (1, " %s request succeeded\n", nth); + soup_test_assert (got_401 == expect_401, + "%s request %s a 401 Unauthorized!\n", nth, + got_401 ? "got" : "did not get"); + soup_test_assert_message_status (msg, SOUP_STATUS_OK); + g_object_unref (msg); } static void -do_digest_expiration_test (const char *base_uri) +do_digest_expiration_test (void) { SoupSession *session; char *uri; - debug_printf (1, "\nTesting digest nonce expiration:\n"); + SOUP_TEST_SKIP_IF_NO_APACHE; session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); @@ -530,10 +518,8 @@ async_authenticate_assert_once (SoupSession *session, SoupMessage *msg, debug_printf (2, " async_authenticate_assert_once\n"); - if (*been_here) { - debug_printf (1, " ERROR: async_authenticate_assert_once called twice\n"); - errors++; - } + soup_test_assert (!*been_here, + "async_authenticate_assert_once called twice"); *been_here = TRUE; } @@ -545,10 +531,8 @@ async_authenticate_assert_once_and_stop (SoupSession *session, SoupMessage *msg, debug_printf (2, " async_authenticate_assert_once_and_stop\n"); - if (*been_here) { - debug_printf (1, " ERROR: async_authenticate_assert_once called twice\n"); - errors++; - } + soup_test_assert (!*been_here, + "async_authenticate_assert_once called twice"); *been_here = TRUE; soup_session_pause_message (session, msg); @@ -556,7 +540,7 @@ async_authenticate_assert_once_and_stop (SoupSession *session, SoupMessage *msg, } static void -do_async_auth_test (const char *base_uri) +do_async_auth_good_password_test (void) { SoupSession *session; SoupMessage *msg1, *msg2, *msg3, msg2_bak; @@ -564,15 +548,13 @@ do_async_auth_test (const char *base_uri) char *uri; SoupAuth *auth = NULL; int remaining; - gboolean been_there; - debug_printf (1, "\nTesting async auth:\n"); + SOUP_TEST_SKIP_IF_NO_APACHE; loop = g_main_loop_new (NULL, TRUE); session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); - remaining = 0; - uri = g_strconcat (base_uri, "Basic/realm1/", NULL); + remaining = 0; msg1 = soup_message_new ("GET", uri); g_object_set_data (G_OBJECT (msg1), "id", GINT_TO_POINTER (1)); @@ -590,13 +572,7 @@ do_async_auth_test (const char *base_uri) g_object_set_data (G_OBJECT (msg2), "id", GINT_TO_POINTER (2)); soup_session_send_message (session, msg2); - if (msg2->status_code == SOUP_STATUS_UNAUTHORIZED) - debug_printf (1, " msg2 failed as expected\n"); - else { - debug_printf (1, " msg2 got wrong status! (%u)\n", - msg2->status_code); - errors++; - } + soup_test_assert_message_status (msg2, SOUP_STATUS_UNAUTHORIZED); /* msg2 should be done at this point; assuming everything is * working correctly, the session won't look at it again; we @@ -627,25 +603,11 @@ do_async_auth_test (const char *base_uri) g_main_loop_run (loop); /* async_finished will quit the loop */ - } else { - debug_printf (1, " msg1 didn't get authenticate signal!\n"); - errors++; - } + } else + soup_test_assert (auth, "msg1 didn't get authenticate signal"); - if (msg1->status_code == SOUP_STATUS_OK) - debug_printf (1, " msg1 succeeded\n"); - else { - debug_printf (1, " msg1 FAILED! (%u %s)\n", - msg1->status_code, msg1->reason_phrase); - errors++; - } - if (msg3->status_code == SOUP_STATUS_OK) - debug_printf (1, " msg3 succeeded\n"); - else { - debug_printf (1, " msg3 FAILED! (%u %s)\n", - msg3->status_code, msg3->reason_phrase); - errors++; - } + soup_test_assert_message_status (msg1, SOUP_STATUS_OK); + soup_test_assert_message_status (msg3, SOUP_STATUS_OK); soup_test_session_abort_unref (session); @@ -654,27 +616,46 @@ do_async_auth_test (const char *base_uri) memcpy (msg2, &msg2_bak, sizeof (SoupMessage)); g_object_unref (msg2); + g_free (uri); + g_main_loop_unref (loop); +} + +static void +do_async_auth_bad_password_test (void) +{ + SoupSession *session; + SoupMessage *msg; + guint auth_id; + char *uri; + SoupAuth *auth = NULL; + int remaining; + gboolean been_there; + /* Test that giving the wrong password doesn't cause multiple * authenticate signals the second time. */ - debug_printf (1, "\nTesting async auth with wrong password (#522601):\n"); + g_test_bug ("522601"); + + SOUP_TEST_SKIP_IF_NO_APACHE; + loop = g_main_loop_new (NULL, TRUE); session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); + uri = g_strconcat (base_uri, "Basic/realm1/", NULL); remaining = 0; auth = NULL; - msg1 = soup_message_new ("GET", uri); - g_object_set_data (G_OBJECT (msg1), "id", GINT_TO_POINTER (1)); + msg = soup_message_new ("GET", uri); + g_object_set_data (G_OBJECT (msg), "id", GINT_TO_POINTER (1)); auth_id = g_signal_connect (session, "authenticate", G_CALLBACK (async_authenticate), &auth); - g_object_ref (msg1); + g_object_ref (msg); remaining++; - soup_session_queue_message (session, msg1, async_finished, &remaining); + soup_session_queue_message (session, msg, async_finished, &remaining); g_main_loop_run (loop); g_signal_handler_disconnect (session, auth_id); soup_auth_authenticate (auth, "user1", "wrong"); g_object_unref (auth); - soup_session_unpause_message (session, msg1); + soup_session_unpause_message (session, msg); been_there = FALSE; auth_id = g_signal_connect (session, "authenticate", @@ -683,55 +664,71 @@ do_async_auth_test (const char *base_uri) g_main_loop_run (loop); g_signal_handler_disconnect (session, auth_id); - if (!been_there) { - debug_printf (1, " authenticate not emitted?\n"); - errors++; - } + soup_test_assert (been_there, + "authenticate not emitted"); soup_test_session_abort_unref (session); - g_object_unref (msg1); + g_object_unref (msg); + + g_free (uri); + g_main_loop_unref (loop); +} + +static void +do_async_auth_no_password_test (void) +{ + SoupSession *session; + SoupMessage *msg; + guint auth_id; + char *uri; + int remaining; + gboolean been_there; /* Test that giving no password doesn't cause multiple * authenticate signals the second time. */ - debug_printf (1, "\nTesting async auth with no password (#583462):\n"); + g_test_bug ("583462"); + + SOUP_TEST_SKIP_IF_NO_APACHE; + loop = g_main_loop_new (NULL, TRUE); session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); + uri = g_strconcat (base_uri, "Basic/realm1/", NULL); remaining = 0; /* Send a message that doesn't actually authenticate */ - msg1 = soup_message_new ("GET", uri); - g_object_set_data (G_OBJECT (msg1), "id", GINT_TO_POINTER (1)); + msg = soup_message_new ("GET", uri); + g_object_set_data (G_OBJECT (msg), "id", GINT_TO_POINTER (1)); auth_id = g_signal_connect (session, "authenticate", G_CALLBACK (async_authenticate), NULL); - g_object_ref (msg1); + g_object_ref (msg); remaining++; - soup_session_queue_message (session, msg1, async_finished, &remaining); + soup_session_queue_message (session, msg, async_finished, &remaining); g_main_loop_run (loop); g_signal_handler_disconnect (session, auth_id); - soup_session_unpause_message (session, msg1); + soup_session_unpause_message (session, msg); g_main_loop_run (loop); - g_object_unref(msg1); + g_object_unref(msg); /* Now send a second message */ - msg1 = soup_message_new ("GET", uri); - g_object_set_data (G_OBJECT (msg1), "id", GINT_TO_POINTER (2)); - g_object_ref (msg1); + msg = soup_message_new ("GET", uri); + g_object_set_data (G_OBJECT (msg), "id", GINT_TO_POINTER (2)); + g_object_ref (msg); been_there = FALSE; auth_id = g_signal_connect (session, "authenticate", G_CALLBACK (async_authenticate_assert_once_and_stop), &been_there); remaining++; - soup_session_queue_message (session, msg1, async_finished, &remaining); + soup_session_queue_message (session, msg, async_finished, &remaining); g_main_loop_run (loop); - soup_session_unpause_message (session, msg1); + soup_session_unpause_message (session, msg); g_main_loop_run (loop); g_signal_handler_disconnect (session, auth_id); soup_test_session_abort_unref (session); - g_object_unref (msg1); + g_object_unref (msg); g_free (uri); g_main_loop_unref (loop); @@ -795,41 +792,27 @@ select_auth_test_one (SoupURI *uri, msg = soup_message_new_from_uri ("GET", uri); soup_session_send_message (session, msg); - if (strcmp (sad.round[0].headers, first_headers) != 0) { - debug_printf (1, " Header order wrong: expected %s, got %s\n", - first_headers, sad.round[0].headers); - errors++; - } - if (strcmp (sad.round[0].response, first_response) != 0) { - debug_printf (1, " Selected auth type wrong: expected %s, got %s\n", - first_response, sad.round[0].response); - errors++; - } - - if (second_headers && !sad.round[1].headers) { - debug_printf (1, " Expected a second round!\n"); - errors++; - } else if (!second_headers && sad.round[1].headers) { - debug_printf (1, " Didn't expect a second round!\n"); - errors++; - } else if (second_headers) { - if (strcmp (sad.round[1].headers, second_headers) != 0) { - debug_printf (1, " Second round header order wrong: expected %s, got %s\n", - second_headers, sad.round[1].headers); - errors++; - } - if (strcmp (sad.round[1].response, second_response) != 0) { - debug_printf (1, " Second round selected auth type wrong: expected %s, got %s\n", - second_response, sad.round[1].response); - errors++; - } + soup_test_assert (strcmp (sad.round[0].headers, first_headers) == 0, + "Header order wrong: expected %s, got %s", + first_headers, sad.round[0].headers); + soup_test_assert (strcmp (sad.round[0].response, first_response) == 0, + "Selected auth type wrong: expected %s, got %s", + first_response, sad.round[0].response); + + soup_test_assert (sad.round[1].headers || !second_headers, + "Expected a second round"); + soup_test_assert (!sad.round[1].headers || second_headers, + "Didn't expect a second round"); + if (second_headers && second_response) { + soup_test_assert (strcmp (sad.round[1].headers, second_headers) == 0, + "Second round header order wrong: expected %s, got %s\n", + second_headers, sad.round[1].headers); + soup_test_assert (strcmp (sad.round[1].response, second_response) == 0, + "Second round selected auth type wrong: expected %s, got %s\n", + second_response, sad.round[1].response); } - if (msg->status_code != final_status) { - debug_printf (1, " Final status wrong: expected %u, got %u\n", - final_status, msg->status_code); - errors++; - } + soup_test_assert_message_status (msg, final_status); g_object_unref (msg); soup_test_session_abort_unref (session); @@ -873,7 +856,7 @@ do_select_auth_test (void) SoupAuthDomain *basic_auth_domain, *digest_auth_domain; SoupURI *uri; - debug_printf (1, "\nTesting selection among multiple auths:\n"); + g_test_bug ("562339"); /* It doesn't seem to be possible to configure Apache to serve * multiple auth types for a single URL. So we have to use @@ -1029,8 +1012,6 @@ do_auth_close_test (void) SoupURI *uri; AuthCloseData acd; - debug_printf (1, "\nTesting auth when server times out connection:\n"); - server = soup_test_server_new (FALSE); soup_server_add_handler (server, NULL, server_callback, NULL, NULL); @@ -1057,18 +1038,130 @@ do_auth_close_test (void) soup_uri_free (uri); soup_session_send_message (acd.session, acd.msg); - if (acd.msg->status_code != SOUP_STATUS_OK) { - debug_printf (1, " Final status wrong: expected %u, got %u %s\n", - SOUP_STATUS_OK, acd.msg->status_code, - acd.msg->reason_phrase); - errors++; - } + soup_test_assert_message_status (acd.msg, SOUP_STATUS_OK); g_object_unref (acd.msg); soup_test_session_abort_unref (acd.session); soup_test_server_quit_unref (server); } +static gboolean +infinite_cancel (gpointer session) +{ + soup_session_abort (session); + return FALSE; +} + +static void +infinite_authenticate (SoupSession *session, SoupMessage *msg, + SoupAuth *auth, gboolean retrying, gpointer data) +{ + soup_auth_authenticate (auth, "user", "bad"); +} + +static void +do_infinite_auth_test (void) +{ + SoupSession *session; + SoupMessage *msg; + char *uri; + int timeout; + + SOUP_TEST_SKIP_IF_NO_APACHE; + + session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); + g_signal_connect (session, "authenticate", + G_CALLBACK (infinite_authenticate), NULL); + + uri = g_strconcat (base_uri, "Basic/realm1/", NULL); + msg = soup_message_new ("GET", uri); + g_free (uri); + + timeout = g_timeout_add (500, infinite_cancel, session); + g_test_expect_message ("libsoup", G_LOG_LEVEL_WARNING, + "*stuck in infinite loop*"); + soup_session_send_message (session, msg); + g_test_assert_expected_messages (); + + soup_test_assert (msg->status_code != SOUP_STATUS_CANCELLED, + "Got stuck in loop"); + soup_test_assert_message_status (msg, SOUP_STATUS_UNAUTHORIZED); + + g_source_remove (timeout); + soup_test_session_abort_unref (session); + g_object_unref (msg); +} + +static void +disappear_request_read (SoupServer *server, SoupMessage *msg, + SoupClientContext *context, gpointer user_data) +{ + /* Remove the WWW-Authenticate header if this was a failed attempt */ + if (soup_message_headers_get_one (msg->request_headers, "Authorization") && + msg->status_code == SOUP_STATUS_UNAUTHORIZED) + soup_message_headers_remove (msg->response_headers, "WWW-Authenticate"); +} + +static void +disappear_authenticate (SoupSession *session, SoupMessage *msg, + SoupAuth *auth, gboolean retrying, gpointer data) +{ + int *counter = data; + + (*counter)++; + if (!retrying) + soup_auth_authenticate (auth, "user", "bad"); +} + +static void +do_disappearing_auth_test (void) +{ + SoupServer *server; + SoupAuthDomain *auth_domain; + SoupURI *uri; + SoupMessage *msg; + SoupSession *session; + int counter; + + g_test_bug ("https://bugzilla.redhat.com/show_bug.cgi?id=916224"); + + server = soup_test_server_new (FALSE); + soup_server_add_handler (server, NULL, + server_callback, NULL, NULL); + + uri = soup_uri_new ("http://127.0.0.1/"); + soup_uri_set_port (uri, soup_server_get_port (server)); + + auth_domain = soup_auth_domain_basic_new ( + SOUP_AUTH_DOMAIN_REALM, "auth-test", + SOUP_AUTH_DOMAIN_ADD_PATH, "/", + SOUP_AUTH_DOMAIN_BASIC_AUTH_CALLBACK, server_basic_auth_callback, + NULL); + soup_server_add_auth_domain (server, auth_domain); + g_signal_connect (server, "request-read", + G_CALLBACK (disappear_request_read), NULL); + + session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); + + counter = 0; + g_signal_connect (session, "authenticate", + G_CALLBACK (disappear_authenticate), &counter); + + msg = soup_message_new_from_uri ("GET", uri); + soup_session_send_message (session, msg); + + soup_test_assert (counter <= 2, + "Got stuck in loop"); + soup_test_assert_message_status (msg, SOUP_STATUS_UNAUTHORIZED); + + g_object_unref (msg); + soup_test_session_abort_unref (session); + + g_object_unref (auth_domain); + soup_uri_free (uri); + soup_test_server_quit_unref (server); +} + static SoupAuthTest relogin_tests[] = { { "Auth provided via URL, should succeed", "Basic/realm12/", "1", TRUE, "01", SOUP_STATUS_OK }, @@ -1090,25 +1183,28 @@ static SoupAuthTest relogin_tests[] = { { "Should fail with no auth, fail again with bad password, and give up", "Basic/realm12/", "3", FALSE, "03", SOUP_STATUS_UNAUTHORIZED }, + + { NULL } }; static void -do_batch_tests (const gchar *base_uri_str, gint ntests) +do_batch_tests (gconstpointer data) { + const SoupAuthTest *current_tests = data; SoupSession *session; SoupMessage *msg; char *expected, *uristr; - SoupURI *base_uri; + SoupURI *base; + guint signal; int i; - session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); - g_signal_connect (session, "authenticate", - G_CALLBACK (authenticate), &i); + SOUP_TEST_SKIP_IF_NO_APACHE; - base_uri = soup_uri_new (base_uri_str); + session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); + base = soup_uri_new (base_uri); - for (i = 0; i < ntests; i++) { - SoupURI *soup_uri = soup_uri_new_with_base (base_uri, current_tests[i].url); + for (i = 0; current_tests[i].url; i++) { + SoupURI *soup_uri = soup_uri_new_with_base (base, current_tests[i].url); debug_printf (1, "Test %d: %s\n", i + 1, current_tests[i].explanation); @@ -1139,30 +1235,24 @@ do_batch_tests (const gchar *base_uri_str, gint ntests) soup_message_add_status_code_handler ( msg, "got_headers", SOUP_STATUS_OK, G_CALLBACK (handler), expected); + + signal = g_signal_connect (session, "authenticate", + G_CALLBACK (authenticate), + (gpointer)¤t_tests[i]); soup_session_send_message (session, msg); - if (msg->status_code != SOUP_STATUS_UNAUTHORIZED && - msg->status_code != SOUP_STATUS_OK) { - debug_printf (1, " %d %s !\n", msg->status_code, - msg->reason_phrase); - errors++; - } - if (*expected) { - debug_printf (1, " expected %d more round(s)\n", - (int)strlen (expected)); - errors++; - } - g_free (expected); + g_signal_handler_disconnect (session, signal); - if (msg->status_code != current_tests[i].final_status) { - debug_printf (1, " expected %d\n", - current_tests[i].final_status); - } + soup_test_assert_message_status (msg, current_tests[i].final_status); + soup_test_assert (!*expected, + "expected %d more round(s)\n", + (int)strlen (expected)); + g_free (expected); debug_printf (1, "\n"); g_object_unref (msg); } - soup_uri_free (base_uri); + soup_uri_free (base); soup_test_session_abort_unref (session); } @@ -1170,31 +1260,27 @@ do_batch_tests (const gchar *base_uri_str, gint ntests) int main (int argc, char **argv) { - const char *base_uri; - int ntests; + int ret; test_init (argc, argv, NULL); apache_init (); base_uri = "http://127.0.0.1:47524/"; - /* Main tests */ - current_tests = main_tests; - ntests = G_N_ELEMENTS (main_tests); - do_batch_tests (base_uri, ntests); - - /* Re-login tests */ - current_tests = relogin_tests; - ntests = G_N_ELEMENTS (relogin_tests); - do_batch_tests (base_uri, ntests); + g_test_add_data_func ("/auth/main-tests", main_tests, do_batch_tests); + g_test_add_data_func ("/auth/relogin-tests", relogin_tests, do_batch_tests); + g_test_add_func ("/auth/pipelined-auth", do_pipelined_auth_test); + g_test_add_func ("/auth/digest-expiration", do_digest_expiration_test); + g_test_add_func ("/auth/async-auth/good-password", do_async_auth_good_password_test); + g_test_add_func ("/auth/async-auth/bad-password", do_async_auth_bad_password_test); + g_test_add_func ("/auth/async-auth/no-password", do_async_auth_no_password_test); + g_test_add_func ("/auth/select-auth", do_select_auth_test); + g_test_add_func ("/auth/auth-close", do_auth_close_test); + g_test_add_func ("/auth/infinite-auth", do_infinite_auth_test); + g_test_add_func ("/auth/disappearing-auth", do_disappearing_auth_test); - /* Other regression tests */ - do_pipelined_auth_test (base_uri); - do_digest_expiration_test (base_uri); - do_async_auth_test (base_uri); - do_select_auth_test (); - do_auth_close_test (); + ret = g_test_run (); test_cleanup (); - return errors != 0; + return ret; } diff --git a/tests/cache-test.c b/tests/cache-test.c new file mode 100644 index 00000000..3478f377 --- /dev/null +++ b/tests/cache-test.c @@ -0,0 +1,659 @@ +/* -*- 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; + } + + header = soup_message_headers_get_one (msg->request_headers, + "Test-Set-My-Header"); + if (header) { + soup_message_headers_append (msg->response_headers, + "My-Header", + header); + } + + 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, + SoupMessageHeaders *response_headers, + ...) G_GNUC_NULL_TERMINATED; + +static gboolean last_request_hit_network; +static gboolean last_request_validated; +static guint cancelled_requests; + +static void +copy_headers (const char *name, + const char *value, + gpointer user_data) +{ + SoupMessageHeaders *headers = (SoupMessageHeaders *) user_data; + soup_message_headers_append (headers, name, value); +} + +static char * +do_request (SoupSession *session, + SoupURI *base_uri, + const char *method, + const char *path, + SoupMessageHeaders *response_headers, + ...) +{ + SoupRequestHTTP *req; + SoupMessage *msg; + 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); + msg = soup_request_http_get_message (req); + + va_start (ap, response_headers); + while ((header = va_arg (ap, const char *))) { + value = va_arg (ap, const char *); + soup_message_headers_append (msg->request_headers, + header, value); + } + va_end (ap); + + stream = soup_test_request_send (SOUP_REQUEST (req), NULL, 0, &error); + if (!stream) { + debug_printf (1, " could not send request: %s\n", + error->message); + g_error_free (error); + g_object_unref (req); + g_object_unref (msg); + return NULL; + } + + if (response_headers) + soup_message_headers_foreach (msg->response_headers, copy_headers, response_headers); + + g_object_unref (msg); + + 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 +do_request_with_cancel (SoupSession *session, + SoupURI *base_uri, + const char *method, + const char *path, + SoupTestRequestFlags flags) +{ + SoupRequestHTTP *req; + GInputStream *stream; + SoupURI *uri; + GError *error = NULL; + GCancellable *cancellable; + + last_request_validated = last_request_hit_network = FALSE; + cancelled_requests = 0; + + uri = soup_uri_new_with_base (base_uri, path); + req = soup_session_request_http_uri (session, method, uri, NULL); + soup_uri_free (uri); + cancellable = flags & SOUP_TEST_REQUEST_CANCEL_CANCELLABLE ? g_cancellable_new () : NULL; + stream = soup_test_request_send (SOUP_REQUEST (req), cancellable, flags, &error); + if (stream) { + debug_printf (1, " could not cancel the request\n"); + g_object_unref (stream); + g_object_unref (req); + return; + } + + g_clear_object (&cancellable); + g_clear_object (&stream); + g_clear_object (&req); + + soup_cache_flush ((SoupCache *)soup_session_get_feature (session, SOUP_TYPE_CACHE)); +} + +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 +request_unqueued (SoupSession *session, SoupMessage *msg, + gpointer data) +{ + if (msg->status_code == SOUP_STATUS_CANCELLED) + cancelled_requests++; +} + +static void +do_basics_test (gconstpointer data) +{ + SoupURI *base_uri = (SoupURI *)data; + SoupSession *session; + SoupCache *cache; + char *cache_dir; + char *body1, *body2, *body3, *body4, *body5, *cmp; + + 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", NULL, + "Test-Set-Expires", "Fri, 01 Jan 2100 00:00:00 GMT", + NULL); + body2 = do_request (session, base_uri, "GET", "/2", NULL, + "Test-Set-Last-Modified", "Fri, 01 Jan 2010 00:00:00 GMT", + NULL); + body3 = do_request (session, base_uri, "GET", "/3", NULL, + "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", NULL, + "Test-Set-ETag", "\"abcdefg\"", + "Test-Set-Cache-Control", "must-revalidate", + NULL); + body5 = do_request (session, base_uri, "GET", "/5", NULL, + "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, + NULL); + soup_test_assert (!last_request_hit_network, + "Request for /1 not filled from cache"); + g_assert_cmpstr (body1, ==, cmp); + 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, + NULL); + soup_test_assert (!last_request_hit_network, + "Request for /2 not filled from cache"); + g_assert_cmpstr (body2, ==, cmp); + 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, + NULL); + soup_test_assert (last_request_hit_network, + "Request for /1?attr=value filled from cache"); + g_free (cmp); + debug_printf (2, " Second request\n"); + cmp = do_request (session, base_uri, "GET", "/1", NULL, + NULL); + soup_test_assert (!last_request_hit_network, + "Second request for /1 not filled from cache"); + g_assert_cmpstr (body1, ==, cmp); + 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", NULL, + "Test-Set-Last-Modified", "Fri, 01 Jan 2010 00:00:00 GMT", + "Test-Set-Cache-Control", "must-revalidate", + NULL); + soup_test_assert (last_request_validated, + "Request for /3 not validated"); + soup_test_assert (!last_request_hit_network, + "Request for /3 not filled from cache"); + g_assert_cmpstr (body3, ==, cmp); + 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", NULL, + "Test-Set-Last-Modified", "Sat, 02 Jan 2010 00:00:00 GMT", + "Test-Set-Cache-Control", "must-revalidate", + NULL); + soup_test_assert (last_request_validated, + "Request for /3 not validated"); + soup_test_assert (last_request_hit_network, + "Request for /3 filled from cache"); + g_assert_cmpstr (body3, !=, cmp); + g_free (cmp); + + debug_printf (2, " Second request\n"); + cmp = do_request (session, base_uri, "GET", "/3", NULL, + "Test-Set-Last-Modified", "Sat, 02 Jan 2010 00:00:00 GMT", + "Test-Set-Cache-Control", "must-revalidate", + NULL); + soup_test_assert (last_request_validated, + "Second request for /3 not validated"); + soup_test_assert (!last_request_hit_network, + "Second request for /3 not filled from cache"); + g_assert_cmpstr (body3, !=, cmp); + g_free (cmp); + + /* 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", NULL, + "Test-Set-ETag", "\"abcdefg\"", + NULL); + soup_test_assert (last_request_validated, + "Request for /4 not validated"); + soup_test_assert (!last_request_hit_network, + "Request for /4 not filled from cache"); + g_assert_cmpstr (body4, ==, cmp); + g_free (cmp); + + + /* Cache-Control: no-cache prevents caching */ + debug_printf (1, " Uncacheable resource\n"); + cmp = do_request (session, base_uri, "GET", "/5", NULL, + "Test-Set-Cache-Control", "no-cache", + NULL); + soup_test_assert (last_request_hit_network, + "Request for /5 filled from cache"); + g_assert_cmpstr (body5, ==, cmp); + 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, + NULL); + soup_test_assert (last_request_hit_network, + "PUT filled from cache"); + g_free (cmp); + cmp = do_request (session, base_uri, "GET", "/1", NULL, + NULL); + soup_test_assert (last_request_hit_network, + "PUT failed to invalidate cache entry"); + g_assert_true (last_request_hit_network); + 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); +} + +static void +do_cancel_test (gconstpointer data) +{ + SoupURI *base_uri = (SoupURI *)data; + SoupSession *session; + SoupCache *cache; + char *cache_dir; + char *body1, *body2; + guint flags; + + g_test_bug ("692310"); + + 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-unqueued", + G_CALLBACK (request_unqueued), NULL); + + debug_printf (2, " Initial requests\n"); + body1 = do_request (session, base_uri, "GET", "/1", NULL, + "Test-Set-Expires", "Fri, 01 Jan 2100 00:00:00 GMT", + NULL); + body2 = do_request (session, base_uri, "GET", "/2", NULL, + "Test-Set-Last-Modified", "Fri, 01 Jan 2010 00:00:00 GMT", + "Test-Set-Cache-Control", "must-revalidate", + NULL); + + /* Check that messages are correctly processed on cancellations. */ + debug_printf (1, " Cancel fresh resource with soup_session_message_cancel()\n"); + flags = SOUP_TEST_REQUEST_CANCEL_MESSAGE | SOUP_TEST_REQUEST_CANCEL_IMMEDIATE; + do_request_with_cancel (session, base_uri, "GET", "/1", flags); + g_assert_cmpint (cancelled_requests, ==, 1); + + debug_printf (1, " Cancel fresh resource with g_cancellable_cancel()\n"); + flags = SOUP_TEST_REQUEST_CANCEL_CANCELLABLE | SOUP_TEST_REQUEST_CANCEL_IMMEDIATE; + do_request_with_cancel (session, base_uri, "GET", "/1", flags); + g_assert_cmpint (cancelled_requests, ==, 1); + + soup_test_session_abort_unref (session); + + 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-unqueued", + G_CALLBACK (request_unqueued), NULL); + + /* Check that messages are correctly processed on cancellations. */ + debug_printf (1, " Cancel a revalidating resource with soup_session_message_cancel()\n"); + flags = SOUP_TEST_REQUEST_CANCEL_MESSAGE | SOUP_TEST_REQUEST_CANCEL_IMMEDIATE; + do_request_with_cancel (session, base_uri, "GET", "/2", flags); + g_assert_cmpint (cancelled_requests, ==, 2); + + debug_printf (1, " Cancel a revalidating resource with g_cancellable_cancel()\n"); + flags = SOUP_TEST_REQUEST_CANCEL_CANCELLABLE | SOUP_TEST_REQUEST_CANCEL_IMMEDIATE; + do_request_with_cancel (session, base_uri, "GET", "/2", flags); + g_assert_cmpint (cancelled_requests, ==, 2); + + soup_test_session_abort_unref (session); + + g_object_unref (cache); + g_free (cache_dir); + g_free (body1); + g_free (body2); +} + +static gboolean +unref_stream (gpointer stream) +{ + g_object_unref (stream); + return FALSE; +} + +static void +base_stream_unreffed (gpointer loop, GObject *ex_base_stream) +{ + g_main_loop_quit (loop); +} + +static void +do_refcounting_test (gconstpointer data) +{ + SoupURI *base_uri = (SoupURI *)data; + SoupSession *session; + SoupCache *cache; + char *cache_dir; + SoupRequestHTTP *req; + GInputStream *stream, *base_stream; + SoupURI *uri; + GError *error = NULL; + guint flags; + GMainLoop *loop; + + g_test_bug ("682527"); + + 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); + + last_request_validated = last_request_hit_network = FALSE; + cancelled_requests = 0; + + uri = soup_uri_new_with_base (base_uri, "/1"); + req = soup_session_request_http_uri (session, "GET", uri, NULL); + soup_uri_free (uri); + + flags = SOUP_TEST_REQUEST_CANCEL_AFTER_SEND_FINISH | SOUP_TEST_REQUEST_CANCEL_MESSAGE; + stream = soup_test_request_send (SOUP_REQUEST (req), NULL, flags, &error); + if (!stream) { + debug_printf (1, " could not send request: %s\n", + error->message); + g_error_free (error); + g_object_unref (req); + return; + } + g_object_unref (req); + + base_stream = g_filter_input_stream_get_base_stream (G_FILTER_INPUT_STREAM (stream)); + + debug_printf (1, " Checking that the base stream is properly unref'ed\n"); + loop = g_main_loop_new (NULL, FALSE); + g_object_weak_ref (G_OBJECT (base_stream), base_stream_unreffed, loop); + g_idle_add (unref_stream, stream); + g_main_loop_run (loop); + g_main_loop_unref (loop); + + soup_cache_flush ((SoupCache *)soup_session_get_feature (session, SOUP_TYPE_CACHE)); + + soup_test_session_abort_unref (session); + + g_object_unref (cache); + g_free (cache_dir); +} + +static void +do_headers_test (gconstpointer data) +{ + SoupURI *base_uri = (SoupURI *)data; + SoupSession *session; + SoupMessageHeaders *headers; + SoupCache *cache; + char *cache_dir; + char *body1, *cmp; + const char *header_value; + + 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", NULL, + "Test-Set-Last-Modified", "Fri, 01 Jan 2100 00:00:00 GMT", + "Test-Set-My-Header", "My header value", + NULL); + + /* My-Header new value should be updated in cache */ + debug_printf (2, " Fresh cached resource which updates My-Header\n"); + cmp = do_request (session, base_uri, "GET", "/1", NULL, + "Test-Set-Last-Modified", "Fri, 01 Jan 2010 00:00:00 GMT", + "Test-Set-My-Header", "My header NEW value", + NULL); + soup_test_assert (last_request_validated, + "Request for /1 not validated"); + soup_test_assert (!last_request_hit_network, + "Request for /1 not filled from cache"); + g_free (cmp); + + /* Check that cache returns the updated header */ + debug_printf (2, " Fresh cached resource with new value for My-Header\n"); + headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE); + cmp = do_request (session, base_uri, "GET", "/1", headers, + "Test-Set-Last-Modified", "Fri, 01 Jan 2010 00:00:00 GMT", + NULL); + soup_test_assert (!last_request_hit_network, + "Request for /1 not filled from cache"); + g_free (cmp); + + header_value = soup_message_headers_get_list (headers, "My-Header"); + g_assert_cmpstr (header_value, ==, "My header NEW value"); + soup_message_headers_free (headers); + + soup_test_session_abort_unref (session); + g_object_unref (cache); + + g_free (cache_dir); + g_free (body1); +} + +int +main (int argc, char **argv) +{ + SoupServer *server; + SoupURI *base_uri; + int ret; + + 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)); + + g_test_add_data_func ("/cache/basics", base_uri, do_basics_test); + g_test_add_data_func ("/cache/cancellation", base_uri, do_cancel_test); + g_test_add_data_func ("/cache/refcounting", base_uri, do_refcounting_test); + g_test_add_data_func ("/cache/headers", base_uri, do_headers_test); + + ret = g_test_run (); + + soup_uri_free (base_uri); + soup_test_server_quit_unref (server); + + test_cleanup (); + return ret; +} diff --git a/tests/chunk-io-test.c b/tests/chunk-io-test.c new file mode 100644 index 00000000..1e53eef1 --- /dev/null +++ b/tests/chunk-io-test.c @@ -0,0 +1,610 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright 2013 Red Hat, Inc. + */ + +#include "test-utils.h" + +static void +force_io_streams_init (void) +{ + SoupServer *server; + SoupSession *session; + guint port; + SoupURI *base_uri; + SoupMessage *msg; + + /* Poke libsoup enough to cause SoupBodyInputStream and + * SoupBodyOutputStream to get defined, so we can find them + * via g_type_from_name() later. + */ + + server = soup_test_server_new (TRUE); + port = soup_server_get_port (server); + + base_uri = soup_uri_new ("http://127.0.0.1"); + soup_uri_set_port (base_uri, port); + + session = soup_test_session_new (SOUP_TYPE_SESSION, NULL); + msg = soup_message_new_from_uri ("POST", base_uri); + soup_session_send_message (session, msg); + g_object_unref (msg); + soup_test_session_abort_unref (session); + + soup_uri_free (base_uri); + soup_test_server_quit_unref (server); +} + +typedef struct { + GFilterInputStream grandparent; + + gpointer *soup_filter_input_stream_private; + + gboolean is_readable; +} SlowInputStream; + +typedef struct { + GFilterInputStreamClass grandparent; +} SlowInputStreamClass; + +GType slow_input_stream_get_type (void); +static void slow_pollable_input_stream_init (GPollableInputStreamInterface *pollable_interface, + gpointer interface_data); + +G_DEFINE_TYPE_WITH_CODE (SlowInputStream, slow_input_stream, + g_type_from_name ("SoupFilterInputStream"), + G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_INPUT_STREAM, slow_pollable_input_stream_init); + ) + +static void +slow_input_stream_init (SlowInputStream *sis) +{ +} + +static gssize +slow_input_stream_read (GInputStream *stream, + void *buffer, + gsize count, + GCancellable *cancellable, + GError **error) +{ + return g_input_stream_read (G_FILTER_INPUT_STREAM (stream)->base_stream, + buffer, 1, cancellable, error); +} + +static void +slow_input_stream_class_init (SlowInputStreamClass *sisclass) +{ + GInputStreamClass *input_stream_class = G_INPUT_STREAM_CLASS (sisclass); + + input_stream_class->read_fn = slow_input_stream_read; +} + +static gboolean +slow_input_stream_is_readable (GPollableInputStream *stream) +{ + return ((SlowInputStream *)stream)->is_readable; +} + +static gssize +slow_input_stream_read_nonblocking (GPollableInputStream *stream, + void *buffer, + gsize count, + GError **error) +{ + if (((SlowInputStream *)stream)->is_readable) { + ((SlowInputStream *)stream)->is_readable = FALSE; + return slow_input_stream_read (G_INPUT_STREAM (stream), buffer, count, + NULL, error); + } else { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK, + "would block"); + return -1; + } +} + +static GSource * +slow_input_stream_create_source (GPollableInputStream *stream, + GCancellable *cancellable) +{ + GSource *base_source, *pollable_source; + + ((SlowInputStream *)stream)->is_readable = TRUE; + base_source = g_timeout_source_new (0); + g_source_set_dummy_callback (base_source); + + pollable_source = g_pollable_source_new (G_OBJECT (stream)); + g_source_add_child_source (pollable_source, base_source); + g_source_unref (base_source); + + return pollable_source; +} + +static void +slow_pollable_input_stream_init (GPollableInputStreamInterface *pollable_interface, + gpointer interface_data) +{ + pollable_interface->is_readable = slow_input_stream_is_readable; + pollable_interface->read_nonblocking = slow_input_stream_read_nonblocking; + pollable_interface->create_source = slow_input_stream_create_source; +} + +typedef struct { + GFilterOutputStream parent; + + gboolean is_writable; +} SlowOutputStream; + +typedef struct { + GFilterOutputStreamClass parent; +} SlowOutputStreamClass; + +GType slow_output_stream_get_type (void); + +static void slow_pollable_output_stream_init (GPollableOutputStreamInterface *pollable_interface, + gpointer interface_data); + +G_DEFINE_TYPE_WITH_CODE (SlowOutputStream, slow_output_stream, + g_type_from_name ("GFilterOutputStream"), + G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_OUTPUT_STREAM, slow_pollable_output_stream_init); + ) + +static void +slow_output_stream_init (SlowOutputStream *sis) +{ +} + +static gssize +slow_output_stream_write (GOutputStream *stream, + const void *buffer, + gsize count, + GCancellable *cancellable, + GError **error) +{ + return g_output_stream_write (G_FILTER_OUTPUT_STREAM (stream)->base_stream, + buffer, 1, cancellable, error); +} + +static void +slow_output_stream_class_init (SlowOutputStreamClass *sisclass) +{ + GOutputStreamClass *output_stream_class = G_OUTPUT_STREAM_CLASS (sisclass); + + output_stream_class->write_fn = slow_output_stream_write; +} + +static gboolean +slow_output_stream_is_writable (GPollableOutputStream *stream) +{ + return ((SlowOutputStream *)stream)->is_writable; +} + +static gssize +slow_output_stream_write_nonblocking (GPollableOutputStream *stream, + const void *buffer, + gsize count, + GError **error) +{ + if (((SlowOutputStream *)stream)->is_writable) { + ((SlowOutputStream *)stream)->is_writable = FALSE; + return slow_output_stream_write (G_OUTPUT_STREAM (stream), buffer, count, + NULL, error); + } else { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK, + "would block"); + return -1; + } +} + +static GSource * +slow_output_stream_create_source (GPollableOutputStream *stream, + GCancellable *cancellable) +{ + GSource *base_source, *pollable_source; + + ((SlowOutputStream *)stream)->is_writable = TRUE; + base_source = g_timeout_source_new (0); + g_source_set_dummy_callback (base_source); + + pollable_source = g_pollable_source_new (G_OBJECT (stream)); + g_source_add_child_source (pollable_source, base_source); + g_source_unref (base_source); + + return pollable_source; +} + +static void +slow_pollable_output_stream_init (GPollableOutputStreamInterface *pollable_interface, + gpointer interface_data) +{ + pollable_interface->is_writable = slow_output_stream_is_writable; + pollable_interface->write_nonblocking = slow_output_stream_write_nonblocking; + pollable_interface->create_source = slow_output_stream_create_source; +} + +typedef struct { + GFilterOutputStream parent; + + gboolean is_broken; +} BreakingOutputStream; + +typedef struct { + GFilterOutputStreamClass parent; +} BreakingOutputStreamClass; + +GType breaking_output_stream_get_type (void); + +static void breaking_pollable_output_stream_init (GPollableOutputStreamInterface *pollable_interface, + gpointer interface_data); + +G_DEFINE_TYPE_WITH_CODE (BreakingOutputStream, breaking_output_stream, + g_type_from_name ("GFilterOutputStream"), + G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_OUTPUT_STREAM, breaking_pollable_output_stream_init); + ) + +static void +breaking_output_stream_init (BreakingOutputStream *sis) +{ +} + +static gssize +breaking_output_stream_write (GOutputStream *stream, + const void *buffer, + gsize count, + GCancellable *cancellable, + GError **error) +{ + if (((BreakingOutputStream *)stream)->is_broken) { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "failed"); + return -1; + } + + if (count > 128) { + ((BreakingOutputStream *)stream)->is_broken = TRUE; + count /= 2; + } + return g_output_stream_write (G_FILTER_OUTPUT_STREAM (stream)->base_stream, + buffer, count, cancellable, error); +} + +static void +breaking_output_stream_class_init (BreakingOutputStreamClass *sisclass) +{ + GOutputStreamClass *output_stream_class = G_OUTPUT_STREAM_CLASS (sisclass); + + output_stream_class->write_fn = breaking_output_stream_write; +} + +static gboolean +breaking_output_stream_is_writable (GPollableOutputStream *stream) +{ + return TRUE; +} + +static gssize +breaking_output_stream_write_nonblocking (GPollableOutputStream *stream, + const void *buffer, + gsize count, + GError **error) +{ + if (((BreakingOutputStream *)stream)->is_broken) { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "failed"); + return -1; + } + + if (count > 128) { + ((BreakingOutputStream *)stream)->is_broken = TRUE; + count /= 2; + } + return g_pollable_output_stream_write_nonblocking (G_POLLABLE_OUTPUT_STREAM (G_FILTER_OUTPUT_STREAM (stream)->base_stream), + buffer, count, NULL, error); +} + +static GSource * +breaking_output_stream_create_source (GPollableOutputStream *stream, + GCancellable *cancellable) +{ + GSource *base_source, *pollable_source; + + base_source = g_timeout_source_new (0); + g_source_set_dummy_callback (base_source); + + pollable_source = g_pollable_source_new (G_OBJECT (stream)); + g_source_add_child_source (pollable_source, base_source); + g_source_unref (base_source); + + return pollable_source; +} + +static void +breaking_pollable_output_stream_init (GPollableOutputStreamInterface *pollable_interface, + gpointer interface_data) +{ + pollable_interface->is_writable = breaking_output_stream_is_writable; + pollable_interface->write_nonblocking = breaking_output_stream_write_nonblocking; + pollable_interface->create_source = breaking_output_stream_create_source; +} + +#define CHUNK_SIZE 1024 + +static GString * +chunkify (const char *str, gsize length) +{ + GString *gstr; + int i, size; + + gstr = g_string_new (NULL); + for (i = 0; i < length; i += CHUNK_SIZE) { + size = MIN (CHUNK_SIZE, length - i); + g_string_append_printf (gstr, "%x\r\n", size); + g_string_append_len (gstr, str + i, size); + g_string_append (gstr, "\r\n"); + } + g_string_append (gstr, "0\r\n\r\n"); + + return gstr; +} + +static void +do_io_tests (void) +{ + GInputStream *imem, *islow, *in; + GOutputStream *omem, *oslow, *out; + GMemoryOutputStream *mem; + SoupBuffer *raw_contents; + char *buf; + GString *chunkified; + GError *error = NULL; + gssize nread, nwrote, total; + gssize chunk_length, chunk_total; + + raw_contents = soup_test_get_index (); + chunkified = chunkify (raw_contents->data, raw_contents->length); + + debug_printf (1, " sync read\n"); + + imem = g_memory_input_stream_new_from_data (chunkified->str, chunkified->len, NULL); + islow = g_object_new (slow_input_stream_get_type (), + "base-stream", imem, + "close-base-stream", TRUE, + NULL); + in = g_object_new (g_type_from_name ("SoupBodyInputStream"), + "base-stream", islow, + "close-base-stream", TRUE, + "encoding", SOUP_ENCODING_CHUNKED, + NULL); + g_object_unref (imem); + g_object_unref (islow); + + buf = g_malloc (raw_contents->length); + total = 0; + while (TRUE) { + nread = g_input_stream_read (in, buf + total, + raw_contents->length - total, + NULL, &error); + g_assert_no_error (error); + g_clear_error (&error); + if (nread > 0) + total += nread; + else + break; + } + + g_input_stream_close (in, NULL, &error); + g_assert_no_error (error); + g_clear_error (&error); + g_object_unref (in); + + soup_assert_cmpmem (buf, total, raw_contents->data, raw_contents->length); + g_free (buf); + + debug_printf (1, " async read\n"); + + imem = g_memory_input_stream_new_from_data (chunkified->str, chunkified->len, NULL); + islow = g_object_new (slow_input_stream_get_type (), + "base-stream", imem, + "close-base-stream", TRUE, + NULL); + in = g_object_new (g_type_from_name ("SoupBodyInputStream"), + "base-stream", islow, + "close-base-stream", TRUE, + "encoding", SOUP_ENCODING_CHUNKED, + NULL); + g_object_unref (imem); + g_object_unref (islow); + + buf = g_malloc (raw_contents->length); + total = 0; + while (TRUE) { + nread = g_pollable_input_stream_read_nonblocking (G_POLLABLE_INPUT_STREAM (in), + buf + total, + raw_contents->length - total, + NULL, &error); + if (nread == -1 && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) { + GSource *source; + + g_clear_error (&error); + source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (in), NULL); + g_source_set_dummy_callback (source); + g_source_attach (source, NULL); + while (!g_pollable_input_stream_is_readable (G_POLLABLE_INPUT_STREAM (in))) + g_main_context_iteration (NULL, TRUE); + g_source_destroy (source); + g_source_unref (source); + continue; + } else if (nread == -1) { + g_assert_no_error (error); + g_clear_error (&error); + break; + } else if (nread == 0) + break; + else + total += nread; + } + + g_input_stream_close (in, NULL, &error); + g_assert_no_error (error); + g_clear_error (&error); + g_object_unref (in); + + soup_assert_cmpmem (buf, total, raw_contents->data, raw_contents->length); + g_free (buf); + + debug_printf (1, " sync write\n"); + + buf = g_malloc (chunkified->len); + omem = g_memory_output_stream_new (buf, chunkified->len, NULL, NULL); + oslow = g_object_new (slow_output_stream_get_type (), + "base-stream", omem, + "close-base-stream", TRUE, + NULL); + out = g_object_new (g_type_from_name ("SoupBodyOutputStream"), + "base-stream", oslow, + "close-base-stream", TRUE, + "encoding", SOUP_ENCODING_CHUNKED, + NULL); + g_object_unref (omem); + g_object_unref (oslow); + + total = chunk_length = chunk_total = 0; + while (total < raw_contents->length) { + if (chunk_total == chunk_length) { + chunk_length = MIN (CHUNK_SIZE, raw_contents->length - total); + chunk_total = 0; + } + nwrote = g_output_stream_write (out, raw_contents->data + total, + chunk_length - chunk_total, NULL, &error); + g_assert_no_error (error); + g_clear_error (&error); + if (nwrote > 0) { + total += nwrote; + chunk_total += nwrote; + } else + break; + } + + g_output_stream_close (out, NULL, &error); + g_assert_no_error (error); + g_clear_error (&error); + + mem = G_MEMORY_OUTPUT_STREAM (omem); + soup_assert_cmpmem (g_memory_output_stream_get_data (mem), + g_memory_output_stream_get_data_size (mem), + chunkified->str, chunkified->len); + + g_object_unref (out); + g_free (buf); + + debug_printf (1, " async write\n"); + + buf = g_malloc (chunkified->len); + omem = g_memory_output_stream_new (buf, chunkified->len, NULL, NULL); + oslow = g_object_new (slow_output_stream_get_type (), + "base-stream", omem, + "close-base-stream", TRUE, + NULL); + out = g_object_new (g_type_from_name ("SoupBodyOutputStream"), + "base-stream", oslow, + "close-base-stream", TRUE, + "encoding", SOUP_ENCODING_CHUNKED, + NULL); + g_object_unref (omem); + g_object_unref (oslow); + + total = chunk_length = chunk_total = 0; + while (total < raw_contents->length) { + if (chunk_total == chunk_length) { + chunk_length = MIN (CHUNK_SIZE, raw_contents->length - total); + chunk_total = 0; + } + nwrote = g_pollable_output_stream_write_nonblocking (G_POLLABLE_OUTPUT_STREAM (out), + raw_contents->data + total, + chunk_length - chunk_total, + NULL, &error); + if (nwrote == -1 && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) { + GSource *source; + + g_clear_error (&error); + source = g_pollable_output_stream_create_source (G_POLLABLE_OUTPUT_STREAM (out), NULL); + g_source_set_dummy_callback (source); + g_source_attach (source, NULL); + while (!g_pollable_output_stream_is_writable (G_POLLABLE_OUTPUT_STREAM (out))) + g_main_context_iteration (NULL, TRUE); + g_source_destroy (source); + g_source_unref (source); + continue; + } else if (nwrote == -1) { + g_assert_no_error (error); + g_clear_error (&error); + break; + } else { + total += nwrote; + chunk_total += nwrote; + } + } + + g_output_stream_close (out, NULL, &error); + g_assert_no_error (error); + g_clear_error (&error); + + mem = G_MEMORY_OUTPUT_STREAM (omem); + soup_assert_cmpmem (g_memory_output_stream_get_data (mem), + g_memory_output_stream_get_data_size (mem), + chunkified->str, chunkified->len); + + g_object_unref (out); + g_free (buf); + + debug_printf (1, " failed write\n"); + /* this succeeds if it doesn't critical */ + + buf = g_malloc (chunkified->len); + omem = g_memory_output_stream_new (buf, chunkified->len, NULL, NULL); + oslow = g_object_new (breaking_output_stream_get_type (), + "base-stream", omem, + "close-base-stream", TRUE, + NULL); + out = g_object_new (g_type_from_name ("SoupBodyOutputStream"), + "base-stream", oslow, + "close-base-stream", TRUE, + "encoding", SOUP_ENCODING_CHUNKED, + NULL); + g_object_unref (omem); + g_object_unref (oslow); + + total = 0; + while (total < raw_contents->length) { + nwrote = g_output_stream_write (out, raw_contents->data + total, + raw_contents->length - total, NULL, NULL); + if (nwrote == -1) + break; + else + total += nwrote; + } + + g_assert_cmpint (total, !=, raw_contents->length); + + g_output_stream_close (out, NULL, NULL); + g_object_unref (out); + + g_free (buf); + + g_string_free (chunkified, TRUE); +} + +int +main (int argc, char **argv) +{ + int ret; + + test_init (argc, argv, NULL); + + force_io_streams_init (); + + g_test_add_func ("/chunk-io", do_io_tests); + + ret = g_test_run (); + + test_cleanup (); + return ret; +} diff --git a/tests/chunk-test.c b/tests/chunk-test.c index cce7127f..864f8620 100644 --- a/tests/chunk-test.c +++ b/tests/chunk-test.c @@ -5,6 +5,9 @@ #include "test-utils.h" +static SoupSession *session; +static SoupURI *base_uri; + typedef struct { SoupSession *session; SoupBuffer *chunks[3]; @@ -19,9 +22,9 @@ write_next_chunk (SoupMessage *msg, gpointer user_data) debug_printf (2, " writing chunk %d\n", ptd->next); - if (ptd->streaming && ptd->next > 0 && ptd->chunks[ptd->next - 1]) { - debug_printf (1, " error: next chunk requested before last one freed!\n"); - errors++; + if (ptd->streaming && ptd->next > 0) { + soup_test_assert (ptd->chunks[ptd->next - 1] == NULL, + "next chunk requested before last one freed"); } if (ptd->next < G_N_ELEMENTS (ptd->chunks)) { @@ -50,8 +53,8 @@ write_next_chunk_streaming_hack (SoupMessage *msg, gpointer user_data) soup_message_body_wrote_chunk (msg->request_body, chunk); soup_buffer_free (chunk); } else { - debug_printf (1, " error: written chunk does not exist!\n"); - errors++; + soup_test_assert (chunk, + "written chunk does not exist"); } write_next_chunk (msg, user_data); } @@ -77,8 +80,8 @@ clear_buffer_ptr (gpointer data) g_free ((char *)(*buffer_ptr)->data); *buffer_ptr = NULL; } else { - debug_printf (2, " chunk is already clear!\n"); - errors++; + soup_test_assert (*buffer_ptr, + "chunk is already clear"); } } @@ -136,26 +139,20 @@ typedef enum { } RequestTestFlags; static void -do_request_test (SoupSession *session, SoupURI *base_uri, RequestTestFlags flags) +do_request_test (gconstpointer data) { - SoupURI *uri = base_uri; + RequestTestFlags flags = GPOINTER_TO_UINT (data); + SoupURI *uri; PutTestData ptd; SoupMessage *msg; const char *client_md5, *server_md5; GChecksum *check; int i, length; - debug_printf (1, "PUT"); - if (flags & HACKY_STREAMING) - debug_printf (1, " w/ hacky streaming"); - else if (flags & PROPER_STREAMING) - debug_printf (1, " w/ proper streaming"); - if (flags & RESTART) { - debug_printf (1, " and restart"); + if (flags & RESTART) + uri = soup_uri_new_with_base (base_uri, "/redirect"); + else uri = soup_uri_copy (base_uri); - soup_uri_set_path (uri, "/redirect"); - } - debug_printf (1, "\n"); ptd.session = session; setup_request_body (&ptd); @@ -199,35 +196,19 @@ do_request_test (SoupSession *session, SoupURI *base_uri, RequestTestFlags flags G_CALLBACK (wrote_body_data), &ptd); soup_session_send_message (session, msg); - if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) { - debug_printf (1, " message failed: %d %s\n", - msg->status_code, msg->reason_phrase); - errors++; - } - - if (msg->request_body->data) { - debug_printf (1, " msg->request_body set!\n"); - errors++; - } - if (msg->request_body->length != length || length != ptd.nwrote) { - debug_printf (1, " sent length mismatch: %d vs %d vs %d\n", - (int)msg->request_body->length, length, ptd.nwrote); - errors++; - } + soup_test_assert_message_status (msg, SOUP_STATUS_CREATED); + g_assert_null (msg->request_body->data); + g_assert_cmpint (msg->request_body->length, ==, length); + g_assert_cmpint (length, ==, ptd.nwrote); server_md5 = soup_message_headers_get_one (msg->response_headers, "Content-MD5"); - if (!server_md5 || strcmp (client_md5, server_md5) != 0) { - debug_printf (1, " client/server data mismatch: %s vs %s\n", - client_md5, server_md5 ? server_md5 : "(null)"); - errors++; - } + g_assert_cmpstr (client_md5, ==, server_md5); g_object_unref (msg); g_checksum_free (check); - if (uri != base_uri) - soup_uri_free (uri); + soup_uri_free (uri); } typedef struct { @@ -243,10 +224,8 @@ chunk_allocator (SoupMessage *msg, gsize max_len, gpointer user_data) debug_printf (2, " allocating chunk\n"); - if (gtd->current_chunk) { - debug_printf (1, " error: next chunk allocated before last one freed!\n"); - errors++; - } + soup_test_assert (gtd->current_chunk == NULL, + "error: next chunk allocated before last one freed"); gtd->current_chunk = soup_buffer_new_with_owner (g_malloc (6), 6, >d->current_chunk, clear_buffer_ptr); @@ -270,56 +249,40 @@ got_chunk (SoupMessage *msg, SoupBuffer *chunk, gpointer user_data) } static void -do_response_test (SoupSession *session, SoupURI *base_uri) +do_response_test (void) { GetTestData gtd; SoupMessage *msg; const char *client_md5, *server_md5; - debug_printf (1, "GET\n"); - gtd.current_chunk = NULL; gtd.length = 0; gtd.check = g_checksum_new (G_CHECKSUM_MD5); msg = soup_message_new_from_uri ("GET", base_uri); soup_message_body_set_accumulate (msg->response_body, FALSE); + G_GNUC_BEGIN_IGNORE_DEPRECATIONS; soup_message_set_chunk_allocator (msg, chunk_allocator, >d, NULL); + G_GNUC_END_IGNORE_DEPRECATIONS; g_signal_connect (msg, "got_chunk", G_CALLBACK (got_chunk), >d); soup_session_send_message (session, msg); - if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) { - debug_printf (1, " message failed: %d %s\n", - msg->status_code, msg->reason_phrase); - errors++; - } - - if (msg->response_body->data) { - debug_printf (1, " msg->response_body set!\n"); - errors++; - } - if (soup_message_headers_get_content_length (msg->response_headers) != gtd.length) { - debug_printf (1, " received length mismatch: %d vs %d\n", - (int)soup_message_headers_get_content_length (msg->response_headers), gtd.length); - errors++; - } + soup_test_assert_message_status (msg, SOUP_STATUS_OK); + g_assert_null (msg->response_body->data); + g_assert_cmpint (soup_message_headers_get_content_length (msg->response_headers), ==, gtd.length); client_md5 = g_checksum_get_string (gtd.check); server_md5 = soup_message_headers_get_one (msg->response_headers, "Content-MD5"); - if (!server_md5 || strcmp (client_md5, server_md5) != 0) { - debug_printf (1, " client/server data mismatch: %s vs %s\n", - client_md5, server_md5 ? server_md5 : "(null)"); - errors++; - } + g_assert_cmpstr (client_md5, ==, server_md5); g_object_unref (msg); g_checksum_free (gtd.check); } /* Make sure TEMPORARY buffers are handled properly with non-accumulating - * message bodies. Part of https://bugs.webkit.org/show_bug.cgi?id=18343 + * message bodies. */ static void @@ -334,24 +297,24 @@ temp_test_wrote_chunk (SoupMessage *msg, gpointer session) * the I/O to stall since soup-message-io will think it's * done, but it hasn't written Content-Length bytes yet. */ - if (!chunk) { - debug_printf (1, " Lost second chunk!\n"); - errors++; - soup_session_abort (session); - } else + if (chunk) soup_buffer_free (chunk); + else { + soup_test_assert (chunk, "Lost second chunk"); + soup_session_abort (session); + } g_signal_handlers_disconnect_by_func (msg, temp_test_wrote_chunk, session); } static void -do_temporary_test (SoupSession *session, SoupURI *base_uri) +do_temporary_test (void) { SoupMessage *msg; char *client_md5; const char *server_md5; - debug_printf (1, "PUT w/ temporary buffers\n"); + g_test_bug ("https://bugs.webkit.org/show_bug.cgi?id=18343"); msg = soup_message_new_from_uri ("PUT", base_uri); soup_message_body_append (msg->request_body, SOUP_MEMORY_TEMPORARY, @@ -366,19 +329,11 @@ do_temporary_test (SoupSession *session, SoupURI *base_uri) G_CALLBACK (temp_test_wrote_chunk), session); soup_session_send_message (session, msg); - if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) { - debug_printf (1, " message failed: %d %s\n", - msg->status_code, msg->reason_phrase); - errors++; - } + soup_test_assert_message_status (msg, SOUP_STATUS_CREATED); server_md5 = soup_message_headers_get_one (msg->response_headers, "Content-MD5"); - if (!server_md5 || strcmp (client_md5, server_md5) != 0) { - debug_printf (1, " client/server data mismatch: %s vs %s\n", - client_md5, server_md5 ? server_md5 : "(null)"); - errors++; - } + g_assert_cmpstr (client_md5, ==, server_md5); g_free (client_md5); g_object_unref (msg); @@ -396,24 +351,20 @@ large_wrote_body_data (SoupMessage *msg, SoupBuffer *chunk, gpointer user_data) { LargeChunkData *lcd = user_data; - if (memcmp (chunk->data, lcd->buf->data + lcd->offset, chunk->length) != 0) { - debug_printf (1, " chunk data mismatch at %ld\n", (long)lcd->offset); - errors++; - } else - debug_printf (2, " chunk data match at %ld\n", (long)lcd->offset); + soup_assert_cmpmem (chunk->data, chunk->length, + lcd->buf->data + lcd->offset, + chunk->length); lcd->offset += chunk->length; } static void -do_large_chunk_test (SoupSession *session, SoupURI *base_uri) +do_large_chunk_test (void) { SoupMessage *msg; char *buf_data; int i; LargeChunkData lcd; - debug_printf (1, "PUT w/ large chunk\n"); - msg = soup_message_new_from_uri ("PUT", base_uri); buf_data = g_malloc0 (LARGE_CHUNK_SIZE); @@ -428,41 +379,13 @@ do_large_chunk_test (SoupSession *session, SoupURI *base_uri) G_CALLBACK (large_wrote_body_data), &lcd); soup_session_send_message (session, msg); - if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) { - debug_printf (1, " message failed: %d %s\n", - msg->status_code, msg->reason_phrase); - errors++; - } + soup_test_assert_message_status (msg, SOUP_STATUS_CREATED); soup_buffer_free (lcd.buf); g_object_unref (msg); } static void -do_chunk_tests (SoupURI *base_uri) -{ - SoupSession *session; - - session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); - do_request_test (session, base_uri, 0); - debug_printf (2, "\n\n"); - do_request_test (session, base_uri, PROPER_STREAMING); - debug_printf (2, "\n\n"); - do_request_test (session, base_uri, PROPER_STREAMING | RESTART); - debug_printf (2, "\n\n"); - do_request_test (session, base_uri, HACKY_STREAMING); - debug_printf (2, "\n\n"); - do_request_test (session, base_uri, HACKY_STREAMING | RESTART); - debug_printf (2, "\n\n"); - do_response_test (session, base_uri); - debug_printf (2, "\n\n"); - do_temporary_test (session, base_uri); - debug_printf (2, "\n\n"); - do_large_chunk_test (session, base_uri); - soup_test_session_abort_unref (session); -} - -static void server_callback (SoupServer *server, SoupMessage *msg, const char *path, GHashTable *query, SoupClientContext *context, gpointer data) @@ -505,7 +428,7 @@ main (int argc, char **argv) GMainLoop *loop; SoupServer *server; guint port; - SoupURI *base_uri; + int ret; test_init (argc, argv, NULL); @@ -518,12 +441,27 @@ main (int argc, char **argv) base_uri = soup_uri_new ("http://127.0.0.1"); soup_uri_set_port (base_uri, port); - do_chunk_tests (base_uri); + + session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); + + g_test_add_data_func ("/chunks/request/unstreamed", GINT_TO_POINTER (0), do_request_test); + g_test_add_data_func ("/chunks/request/proper-streaming", GINT_TO_POINTER (PROPER_STREAMING), do_request_test); + g_test_add_data_func ("/chunks/request/proper-streaming/restart", GINT_TO_POINTER (PROPER_STREAMING | RESTART), do_request_test); + g_test_add_data_func ("/chunks/request/hacky-streaming", GINT_TO_POINTER (HACKY_STREAMING), do_request_test); + g_test_add_data_func ("/chunks/request/hacky-streaming/restart", GINT_TO_POINTER (HACKY_STREAMING | RESTART), do_request_test); + g_test_add_func ("/chunks/response", do_response_test); + g_test_add_func ("/chunks/temporary", do_temporary_test); + g_test_add_func ("/chunks/large", do_large_chunk_test); + + ret = g_test_run (); + + soup_test_session_abort_unref (session); + soup_uri_free (base_uri); g_main_loop_unref (loop); soup_test_server_quit_unref (server); test_cleanup (); - return errors != 0; + return ret; } diff --git a/tests/coding-test.c b/tests/coding-test.c index cda4b89e..0445f632 100644 --- a/tests/coding-test.c +++ b/tests/coding-test.c @@ -16,8 +16,7 @@ server_callback (SoupServer *server, SoupMessage *msg, { const char *accept_encoding, *options; GSList *codings; - char *file = NULL, *contents; - gsize length; + SoupBuffer *response = NULL; options = soup_message_headers_get_one (msg->request_headers, "X-Test-Options"); @@ -33,7 +32,7 @@ server_callback (SoupServer *server, SoupMessage *msg, if (codings) { gboolean claim_deflate, claim_gzip; - const char *file_path = NULL, *encoding = NULL; + const char *extension = NULL, *encoding = NULL; claim_deflate = g_slist_find_custom (codings, "deflate", (GCompareFunc)g_ascii_strcasecmp) != NULL; claim_gzip = g_slist_find_custom (codings, "gzip", (GCompareFunc)g_ascii_strcasecmp) != NULL; @@ -41,44 +40,44 @@ server_callback (SoupServer *server, SoupMessage *msg, if (claim_gzip && (!claim_deflate || (!soup_header_contains (options, "prefer-deflate-zlib") && !soup_header_contains (options, "prefer-deflate-raw")))) { - file_path = SRCDIR "/resources%s.gz"; + extension = "gz"; encoding = "gzip"; } else if (claim_deflate) { if (soup_header_contains (options, "prefer-deflate-raw")) { - file_path = SRCDIR "/resources%s.raw"; + extension = "raw"; encoding = "deflate"; } else { - file_path = SRCDIR "/resources%s.zlib"; + extension = "zlib"; encoding = "deflate"; } } - if (file_path && encoding) { - file = g_strdup_printf (file_path, path); - if (g_file_test (file, G_FILE_TEST_EXISTS)) { + if (extension && encoding) { + char *resource; + + resource = g_strdup_printf ("%s.%s", path, extension); + response = soup_test_load_resource (resource, NULL); + + if (response) { soup_message_headers_append (msg->response_headers, "Content-Encoding", encoding); - } else { - g_free (file); - file = NULL; } + g_free (resource); } } soup_header_free_list (codings); - if (!file) - file = g_strdup_printf (SRCDIR "/resources%s", path); - if (!g_file_get_contents (file, &contents, &length, NULL)) { + if (!response) + response = soup_test_load_resource (path, NULL); + if (!response) { /* If path.gz exists but can't be read, we'll send back * the error with "Content-Encoding: gzip" but there's * no body, so, eh. */ - g_free (file); soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND); return; } - g_free (file); if (soup_header_contains (options, "force-encode")) { const gchar *encoding = "gzip"; @@ -106,10 +105,9 @@ server_callback (SoupServer *server, SoupMessage *msg, soup_message_set_status (msg, SOUP_STATUS_OK); soup_message_headers_set_encoding (msg->response_headers, SOUP_ENCODING_CHUNKED); - if (!soup_header_contains (options, "empty")) { - soup_message_body_append (msg->response_body, - SOUP_MEMORY_TAKE, contents, length); - } + if (!soup_header_contains (options, "empty")) + soup_message_body_append_buffer (msg->response_body, response); + soup_buffer_free (response); if (soup_header_contains (options, "trailing-junk")) { soup_message_body_append (msg->response_body, SOUP_MEMORY_COPY, @@ -118,6 +116,20 @@ server_callback (SoupServer *server, SoupMessage *msg, soup_message_body_complete (msg->response_body); } +typedef struct { + SoupSession *session; + SoupMessage *msg; + SoupRequest *req; + SoupBuffer *response; +} CodingTestData; + +typedef enum { + CODING_TEST_DEFAULT = 0, + CODING_TEST_NO_DECODER = (1 << 0), + CODING_TEST_REQUEST_API = (1 << 1), + CODING_TEST_EMPTY = (1 << 2) +} CodingTestType; + typedef enum { NO_CHECK, EXPECT_DECODED, @@ -125,184 +137,189 @@ typedef enum { } MessageContentStatus; static void -check_response (SoupMessage *msg, +check_response (CodingTestData *data, const char *expected_encoding, const char *expected_content_type, - MessageContentStatus status) + MessageContentStatus status, + GByteArray *body) { const char *coding, *type; - if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) { - debug_printf (1, " Unexpected status %d %s\n", - msg->status_code, msg->reason_phrase); - errors++; - } + soup_test_assert_message_status (data->msg, SOUP_STATUS_OK); - coding = soup_message_headers_get_one (msg->response_headers, "Content-Encoding"); - if (expected_encoding) { - if (!coding || g_ascii_strcasecmp (coding, expected_encoding) != 0) { - debug_printf (1, " Unexpected Content-Encoding: %s\n", - coding ? coding : "(none)"); - errors++; - } - } else { - if (coding) { - debug_printf (1, " Unexpected Content-Encoding: %s\n", - coding); - errors++; - } - } + coding = soup_message_headers_get_one (data->msg->response_headers, "Content-Encoding"); + g_assert_cmpstr (coding, ==, expected_encoding); if (status != NO_CHECK) { - if (status == EXPECT_DECODED) { - if (!(soup_message_get_flags (msg) & SOUP_MESSAGE_CONTENT_DECODED)) { - debug_printf (1, " SOUP_MESSAGE_CONTENT_DECODED not set!\n"); - errors++; - } - } else { - if (soup_message_get_flags (msg) & SOUP_MESSAGE_CONTENT_DECODED) { - debug_printf (1, " SOUP_MESSAGE_CONTENT_DECODED set!\n"); - errors++; - } - } + if (status == EXPECT_DECODED) + g_assert_true (soup_message_get_flags (data->msg) & SOUP_MESSAGE_CONTENT_DECODED); + else + g_assert_false (soup_message_get_flags (data->msg) & SOUP_MESSAGE_CONTENT_DECODED); } - type = soup_message_headers_get_one (msg->response_headers, "Content-Type"); - if (!type || g_ascii_strcasecmp (type, expected_content_type) != 0) { - debug_printf (1, " Unexpected Content-Type: %s\n", - type ? type : "(none)"); - errors++; + type = soup_message_headers_get_one (data->msg->response_headers, "Content-Type"); + g_assert_cmpstr (type, ==, expected_content_type); + + if (body) { + soup_assert_cmpmem (body->data, + body->len, + data->response->data, + data->response->length); + } else { + soup_assert_cmpmem (data->msg->response_body->data, + data->msg->response_body->length, + data->response->data, + data->response->length); } } static void -check_msg_bodies (SoupMessage *msg1, - SoupMessage *msg2, - const char *msg1_type, - const char *msg2_type) +setup_coding_test (CodingTestData *data, gconstpointer test_data) { - if (msg1->response_body->length != msg2->response_body->length) { - debug_printf (1, " Message length mismatch: %lu (%s) vs %lu (%s)\n", - (gulong)msg1->response_body->length, - msg1_type, - (gulong)msg2->response_body->length, - msg2_type); - errors++; - } else if (memcmp (msg1->response_body->data, - msg2->response_body->data, - msg1->response_body->length) != 0) { - debug_printf (1, " Message data mismatch (%s/%s)\n", - msg1_type, msg2_type); - errors++; + CodingTestType test_type = GPOINTER_TO_INT (test_data); + SoupMessage *msg; + SoupURI *uri; + + data->session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, + SOUP_SESSION_USE_THREAD_CONTEXT, TRUE, + NULL); + + uri = soup_uri_new_with_base (base_uri, "/mbox"); + + if (test_type & CODING_TEST_EMPTY) + data->response = soup_buffer_new (SOUP_MEMORY_STATIC, "", 0); + else { + msg = soup_message_new_from_uri ("GET", uri); + soup_session_send_message (data->session, msg); + + data->response = soup_message_body_flatten (msg->response_body); + g_object_unref (msg); } + + if (test_type & CODING_TEST_REQUEST_API) { + SoupRequestHTTP *reqh; + + reqh = soup_session_request_http_uri (data->session, "GET", uri, NULL); + data->req = SOUP_REQUEST (reqh); + data->msg = soup_request_http_get_message (reqh); + } else + data->msg = soup_message_new_from_uri ("GET", uri); + soup_uri_free (uri); + + if (! (test_type & CODING_TEST_NO_DECODER)) + soup_session_add_feature_by_type (data->session, SOUP_TYPE_CONTENT_DECODER); } static void -do_coding_test (void) +teardown_coding_test (CodingTestData *data, gconstpointer test_data) { - SoupSession *session; - SoupMessage *msg, *msgz, *msgj, *msge, *msgzl, *msgzlj, *msgzle, *msgzlr, *msgzlre; - SoupURI *uri; + soup_buffer_free (data->response); - debug_printf (1, "SoupMessage tests\n"); + g_clear_object (&data->req); + g_object_unref (data->msg); - session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); - uri = soup_uri_new_with_base (base_uri, "/mbox"); + soup_test_session_abort_unref (data->session); +} + +static void +do_coding_test_plain (CodingTestData *data, gconstpointer test_data) +{ + soup_session_send_message (data->session, data->msg); + check_response (data, NULL, "text/plain", EXPECT_NOT_DECODED, NULL); +} + +static void +do_coding_test_gzip (CodingTestData *data, gconstpointer test_data) +{ + soup_session_send_message (data->session, data->msg); + check_response (data, "gzip", "text/plain", EXPECT_DECODED, NULL); +} - /* Plain text data, no claim */ - debug_printf (1, " GET /mbox, plain\n"); - msg = soup_message_new_from_uri ("GET", uri); - soup_session_send_message (session, msg); - check_response (msg, NULL, "text/plain", EXPECT_NOT_DECODED); - - /* Plain text data, claim gzip */ - debug_printf (1, " GET /mbox, Accept-Encoding: gzip\n"); - soup_session_add_feature_by_type (session, SOUP_TYPE_CONTENT_DECODER); - msgz = soup_message_new_from_uri ("GET", uri); - soup_session_send_message (session, msgz); - check_response (msgz, "gzip", "text/plain", EXPECT_DECODED); - check_msg_bodies (msg, msgz, "plain", "compressed"); - - /* Plain text data, claim gzip w/ junk */ - debug_printf (1, " GET /mbox, Accept-Encoding: gzip, plus trailing junk\n"); - msgj = soup_message_new_from_uri ("GET", uri); - soup_message_headers_append (msgj->request_headers, +static void +do_coding_test_gzip_with_junk (CodingTestData *data, gconstpointer test_data) +{ + g_test_bug ("606352"); + g_test_bug ("676477"); + + soup_message_headers_append (data->msg->request_headers, "X-Test-Options", "trailing-junk"); - soup_session_send_message (session, msgj); - check_response (msgj, "gzip", "text/plain", EXPECT_DECODED); - check_msg_bodies (msg, msgj, "plain", "compressed w/ junk"); - - /* Plain text data, claim gzip with server error */ - debug_printf (1, " GET /mbox, Accept-Encoding: gzip, with server error\n"); - msge = soup_message_new_from_uri ("GET", uri); - soup_message_headers_append (msge->request_headers, + + soup_session_send_message (data->session, data->msg); + check_response (data, "gzip", "text/plain", EXPECT_DECODED, NULL); +} + +static void +do_coding_test_gzip_bad_server (CodingTestData *data, gconstpointer test_data) +{ + g_test_bug ("613361"); + + soup_message_headers_append (data->msg->request_headers, "X-Test-Options", "force-encode"); - soup_session_send_message (session, msge); - check_response (msge, "gzip", "text/plain", EXPECT_NOT_DECODED); + + soup_session_send_message (data->session, data->msg); /* Failed content-decoding should have left the body untouched * from what the server sent... which happens to be the * uncompressed data. */ - check_msg_bodies (msg, msge, "plain", "mis-encoded"); + check_response (data, "gzip", "text/plain", EXPECT_NOT_DECODED, NULL); +} - /* Plain text data, claim deflate */ - debug_printf (1, " GET /mbox, Accept-Encoding: deflate\n"); - msgzl = soup_message_new_from_uri ("GET", uri); - soup_message_headers_append (msgzl->request_headers, +static void +do_coding_test_deflate (CodingTestData *data, gconstpointer test_data) +{ + soup_message_headers_append (data->msg->request_headers, "X-Test-Options", "prefer-deflate-zlib"); - soup_session_send_message (session, msgzl); - check_response (msgzl, "deflate", "text/plain", EXPECT_DECODED); - check_msg_bodies (msg, msgzl, "plain", "compressed"); - - /* Plain text data, claim deflate w/ junk */ - debug_printf (1, " GET /mbox, Accept-Encoding: deflate, plus trailing junk\n"); - msgzlj = soup_message_new_from_uri ("GET", uri); - soup_message_headers_append (msgzlj->request_headers, + soup_session_send_message (data->session, data->msg); + + check_response (data, "deflate", "text/plain", EXPECT_DECODED, NULL); +} + +static void +do_coding_test_deflate_with_junk (CodingTestData *data, gconstpointer test_data) +{ + g_test_bug ("606352"); + g_test_bug ("676477"); + + soup_message_headers_append (data->msg->request_headers, "X-Test-Options", "prefer-deflate-zlib, trailing-junk"); - soup_session_send_message (session, msgzlj); - check_response (msgzlj, "deflate", "text/plain", EXPECT_DECODED); - check_msg_bodies (msg, msgzlj, "plain", "compressed w/ junk"); - - /* Plain text data, claim deflate with server error */ - debug_printf (1, " GET /mbox, Accept-Encoding: deflate, with server error\n"); - msgzle = soup_message_new_from_uri ("GET", uri); - soup_message_headers_append (msgzle->request_headers, + soup_session_send_message (data->session, data->msg); + + check_response (data, "deflate", "text/plain", EXPECT_DECODED, NULL); +} + +static void +do_coding_test_deflate_bad_server (CodingTestData *data, gconstpointer test_data) +{ + g_test_bug ("613361"); + + soup_message_headers_append (data->msg->request_headers, "X-Test-Options", "force-encode, prefer-deflate-zlib"); - soup_session_send_message (session, msgzle); - check_response (msgzle, "deflate", "text/plain", EXPECT_NOT_DECODED); - check_msg_bodies (msg, msgzle, "plain", "mis-encoded"); - - /* Plain text data, claim deflate (no zlib headers)*/ - debug_printf (1, " GET /mbox, Accept-Encoding: deflate (raw data)\n"); - msgzlr = soup_message_new_from_uri ("GET", uri); - soup_message_headers_append (msgzlr->request_headers, + soup_session_send_message (data->session, data->msg); + + check_response (data, "deflate", "text/plain", EXPECT_NOT_DECODED, NULL); +} + +static void +do_coding_test_deflate_raw (CodingTestData *data, gconstpointer test_data) +{ + soup_message_headers_append (data->msg->request_headers, "X-Test-Options", "prefer-deflate-raw"); - soup_session_send_message (session, msgzlr); - check_response (msgzlr, "deflate", "text/plain", EXPECT_DECODED); - check_msg_bodies (msg, msgzlr, "plain", "compressed"); - - /* Plain text data, claim deflate with server error */ - debug_printf (1, " GET /mbox, Accept-Encoding: deflate (raw data), with server error\n"); - msgzlre = soup_message_new_from_uri ("GET", uri); - soup_message_headers_append (msgzlre->request_headers, + soup_session_send_message (data->session, data->msg); + + check_response (data, "deflate", "text/plain", EXPECT_DECODED, NULL); +} + +static void +do_coding_test_deflate_raw_bad_server (CodingTestData *data, gconstpointer test_data) +{ + g_test_bug ("613361"); + + soup_message_headers_append (data->msg->request_headers, "X-Test-Options", "force-encode, prefer-deflate-raw"); - soup_session_send_message (session, msgzlre); - check_response (msgzlre, "deflate", "text/plain", EXPECT_NOT_DECODED); - check_msg_bodies (msg, msgzlre, "plain", "mis-encoded"); - - g_object_unref (msg); - g_object_unref (msgzlre); - g_object_unref (msgzlr); - g_object_unref (msgzlj); - g_object_unref (msgzle); - g_object_unref (msgzl); - g_object_unref (msgz); - g_object_unref (msgj); - g_object_unref (msge); - soup_uri_free (uri); + soup_session_send_message (data->session, data->msg); - soup_test_session_abort_unref (session); + check_response (data, "deflate", "text/plain", EXPECT_NOT_DECODED, NULL); } static void @@ -313,38 +330,29 @@ read_finished (GObject *stream, GAsyncResult *result, gpointer user_data) *nread = g_input_stream_read_finish (G_INPUT_STREAM (stream), result, &error); - if (error) { - debug_printf (1, " Error reading: %s\n", - error->message); - g_error_free (error); - errors++; - } + g_assert_no_error (error); + g_clear_error (&error); } -static GByteArray * -do_single_coding_req_test (SoupRequest *req, +static void +do_single_coding_req_test (CodingTestData *data, const char *expected_encoding, const char *expected_content_type, MessageContentStatus status) { GInputStream *stream; - SoupMessage *msg; - GByteArray *data; + GByteArray *body; guchar buf[1024]; gssize nread; GError *error = NULL; - data = g_byte_array_new (); - - msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (req)); + body = g_byte_array_new (); - stream = soup_test_request_send_async_as_sync (req, NULL, &error); - if (error) { - debug_printf (1, " Error sending request: %s\n", - error->message); + stream = soup_test_request_send (data->req, NULL, 0, &error); + if (!stream) { + g_assert_no_error (error); g_error_free (error); - errors++; - return data; + return; } do { @@ -356,208 +364,126 @@ do_single_coding_req_test (SoupRequest *req, g_main_context_iteration (NULL, TRUE); if (nread > 0) - g_byte_array_append (data, buf, nread); + g_byte_array_append (body, buf, nread); } while (nread > 0); - soup_test_stream_close_async_as_sync (stream, NULL, &error); - if (error) { - debug_printf (1, " error closing stream: %s\n", - error->message); - g_error_free (error); - errors++; - } + soup_test_request_close_stream (data->req, stream, NULL, &error); + g_assert_no_error (error); + g_clear_error (&error); + g_object_unref (stream); - check_response (msg, expected_encoding, expected_content_type, status); - g_object_unref (msg); + check_response (data, expected_encoding, expected_content_type, status, body); + g_byte_array_free (body, TRUE); +} - return data; +static void +do_coding_req_test_plain (CodingTestData *data, gconstpointer test_data) +{ + do_single_coding_req_test (data, NULL, "text/plain", EXPECT_NOT_DECODED); } static void -check_req_bodies (GByteArray *body1, - GByteArray *body2, - const char *msg1_type, - const char *msg2_type) +do_coding_req_test_gzip (CodingTestData *data, gconstpointer test_data) { - if (body1->len != body2->len) { - debug_printf (1, " Message length mismatch: %lu (%s) vs %lu (%s)\n", - (gulong)body1->len, msg1_type, - (gulong)body2->len, msg2_type); - errors++; - } else if (memcmp (body1->data, body2->data, body1->len) != 0) { - debug_printf (1, " Message data mismatch (%s/%s)\n", - msg1_type, msg2_type); - errors++; - } + do_single_coding_req_test (data, "gzip", "text/plain", EXPECT_DECODED); } static void -do_coding_req_test (void) +do_coding_req_test_gzip_with_junk (CodingTestData *data, gconstpointer test_data) { - SoupSession *session; - SoupRequester *requester; - SoupRequest *req; - SoupMessage *msg; - SoupURI *uri; - GByteArray *plain, *cmp; + g_test_bug ("606352"); + g_test_bug ("676477"); - debug_printf (1, "\nSoupRequest tests\n"); + soup_message_headers_append (data->msg->request_headers, + "X-Test-Options", "trailing-junk"); - session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, - SOUP_SESSION_USE_THREAD_CONTEXT, TRUE, - SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_REQUESTER, - NULL); - requester = (SoupRequester *)soup_session_get_feature (session, SOUP_TYPE_REQUESTER); - uri = soup_uri_new_with_base (base_uri, "/mbox"); + do_single_coding_req_test (data, "gzip", "text/plain", EXPECT_DECODED); +} - /* Plain text data, no claim */ - debug_printf (1, " GET /mbox, plain\n"); - req = soup_requester_request_uri (requester, uri, NULL); - plain = do_single_coding_req_test (req, NULL, "text/plain", EXPECT_NOT_DECODED); - g_object_unref (req); - - /* Plain text data, claim gzip */ - debug_printf (1, " GET /mbox, Accept-Encoding: gzip\n"); - soup_session_add_feature_by_type (session, SOUP_TYPE_CONTENT_DECODER); - req = soup_requester_request_uri (requester, uri, NULL); - cmp = do_single_coding_req_test (req, "gzip", "text/plain", EXPECT_DECODED); - check_req_bodies (plain, cmp, "plain", "compressed"); - g_byte_array_free (cmp, TRUE); - g_object_unref (req); - - /* Plain text data, claim gzip w/ junk */ - debug_printf (1, " GET /mbox, Accept-Encoding: gzip, plus trailing junk\n"); - req = soup_requester_request_uri (requester, uri, NULL); - msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (req)); - soup_message_headers_append (msg->request_headers, - "X-Test-Options", "trailing-junk"); - cmp = do_single_coding_req_test (req, "gzip", "text/plain", EXPECT_DECODED); - check_req_bodies (plain, cmp, "plain", "compressed w/ junk"); - g_byte_array_free (cmp, TRUE); - g_object_unref (req); - - /* Plain text data, claim gzip with server error */ - debug_printf (1, " GET /mbox, Accept-Encoding: gzip, with server error\n"); - req = soup_requester_request_uri (requester, uri, NULL); - msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (req)); - soup_message_headers_append (msg->request_headers, +static void +do_coding_req_test_gzip_bad_server (CodingTestData *data, gconstpointer test_data) +{ + g_test_bug ("613361"); + + soup_message_headers_append (data->msg->request_headers, "X-Test-Options", "force-encode"); - cmp = do_single_coding_req_test (req, "gzip", "text/plain", EXPECT_NOT_DECODED); + do_single_coding_req_test (data, "gzip", "text/plain", EXPECT_NOT_DECODED); +} - /* Failed content-decoding should have left the body untouched - * from what the server sent... which happens to be the - * uncompressed data. - */ - check_req_bodies (plain, cmp, "plain", "mis-encoded"); - g_byte_array_free (cmp, TRUE); - g_object_unref (req); - - /* Plain text data, claim deflate */ - debug_printf (1, " GET /mbox, Accept-Encoding: deflate\n"); - req = soup_requester_request_uri (requester, uri, NULL); - msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (req)); - soup_message_headers_append (msg->request_headers, +static void +do_coding_req_test_deflate (CodingTestData *data, gconstpointer test_data) +{ + soup_message_headers_append (data->msg->request_headers, "X-Test-Options", "prefer-deflate-zlib"); - cmp = do_single_coding_req_test (req, "deflate", "text/plain", EXPECT_DECODED); - check_req_bodies (plain, cmp, "plain", "compressed"); - g_byte_array_free (cmp, TRUE); - g_object_unref (req); - - /* Plain text data, claim deflate w/ junk */ - debug_printf (1, " GET /mbox, Accept-Encoding: deflate, plus trailing junk\n"); - req = soup_requester_request_uri (requester, uri, NULL); - msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (req)); - soup_message_headers_append (msg->request_headers, + do_single_coding_req_test (data, "deflate", "text/plain", EXPECT_DECODED); +} + +static void +do_coding_req_test_deflate_with_junk (CodingTestData *data, gconstpointer test_data) +{ + g_test_bug ("606352"); + g_test_bug ("676477"); + + soup_message_headers_append (data->msg->request_headers, "X-Test-Options", "prefer-deflate-zlib, trailing-junk"); - cmp = do_single_coding_req_test (req, "deflate", "text/plain", EXPECT_DECODED); - check_req_bodies (plain, cmp, "plain", "compressed w/ junk"); - g_byte_array_free (cmp, TRUE); - g_object_unref (req); - - /* Plain text data, claim deflate with server error */ - debug_printf (1, " GET /mbox, Accept-Encoding: deflate, with server error\n"); - req = soup_requester_request_uri (requester, uri, NULL); - msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (req)); - soup_message_headers_append (msg->request_headers, - "X-Test-Options", "force-encode, prefer-deflate-zlib"); - cmp = do_single_coding_req_test (req, "deflate", "text/plain", EXPECT_NOT_DECODED); - check_req_bodies (plain, cmp, "plain", "mis-encoded"); - g_byte_array_free (cmp, TRUE); - g_object_unref (req); - - /* Plain text data, claim deflate (no zlib headers)*/ - debug_printf (1, " GET /mbox, Accept-Encoding: deflate (raw data)\n"); - req = soup_requester_request_uri (requester, uri, NULL); - msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (req)); - soup_message_headers_append (msg->request_headers, - "X-Test-Options", "prefer-deflate-raw"); - cmp = do_single_coding_req_test (req, "deflate", "text/plain", EXPECT_DECODED); - check_req_bodies (plain, cmp, "plain", "compressed"); - g_byte_array_free (cmp, TRUE); - g_object_unref (req); - - /* Plain text data, claim deflate with server error */ - debug_printf (1, " GET /mbox, Accept-Encoding: deflate (raw data), with server error\n"); - req = soup_requester_request_uri (requester, uri, NULL); - msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (req)); - soup_message_headers_append (msg->request_headers, - "X-Test-Options", "force-encode, prefer-deflate-raw"); - cmp = do_single_coding_req_test (req, "deflate", "text/plain", EXPECT_NOT_DECODED); - check_req_bodies (plain, cmp, "plain", "mis-encoded"); - g_byte_array_free (cmp, TRUE); - g_object_unref (req); + do_single_coding_req_test (data, "deflate", "text/plain", EXPECT_DECODED); +} - g_byte_array_free (plain, TRUE); - soup_uri_free (uri); +static void +do_coding_req_test_deflate_bad_server (CodingTestData *data, gconstpointer test_data) +{ + g_test_bug ("613361"); - soup_test_session_abort_unref (session); + soup_message_headers_append (data->msg->request_headers, + "X-Test-Options", "force-encode, prefer-deflate-zlib"); + do_single_coding_req_test (data, "deflate", "text/plain", EXPECT_NOT_DECODED); } static void -do_coding_empty_test (void) +do_coding_req_test_deflate_raw (CodingTestData *data, gconstpointer test_data) { - SoupSession *session; - SoupMessage *msg; - SoupURI *uri; - SoupRequester *requester; - SoupRequest *req; - GByteArray *body; + soup_message_headers_append (data->msg->request_headers, + "X-Test-Options", "prefer-deflate-raw"); + do_single_coding_req_test (data, "deflate", "text/plain", EXPECT_DECODED); +} - debug_printf (1, "\nEmpty allegedly-encoded body test\n"); +static void +do_coding_req_test_deflate_raw_bad_server (CodingTestData *data, gconstpointer test_data) +{ + g_test_bug ("613361"); - session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, - SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_CONTENT_DECODER, - SOUP_SESSION_USE_THREAD_CONTEXT, TRUE, - SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_REQUESTER, - NULL); - requester = (SoupRequester *)soup_session_get_feature (session, SOUP_TYPE_REQUESTER); - uri = soup_uri_new_with_base (base_uri, "/mbox"); + soup_message_headers_append (data->msg->request_headers, + "X-Test-Options", "force-encode, prefer-deflate-raw"); + do_single_coding_req_test (data, "deflate", "text/plain", EXPECT_NOT_DECODED); +} - debug_printf (1, " SoupMessage\n"); - msg = soup_message_new_from_uri ("GET", uri); - soup_message_headers_append (msg->request_headers, - "X-Test-Options", "empty"); - soup_session_send_message (session, msg); - check_response (msg, "gzip", "text/plain", EXPECT_NOT_DECODED); - g_object_unref (msg); - - debug_printf (1, " SoupRequest\n"); - req = soup_requester_request_uri (requester, uri, NULL); - msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (req)); - soup_message_headers_append (msg->request_headers, +static void +do_coding_msg_empty_test (CodingTestData *data, gconstpointer test_data) +{ + g_test_bug ("697527"); + + soup_message_headers_append (data->msg->request_headers, "X-Test-Options", "empty"); - g_object_unref (msg); - body = do_single_coding_req_test (req, "gzip", "text/plain", EXPECT_NOT_DECODED); - g_byte_array_free (body, TRUE); - g_object_unref (req); + soup_session_send_message (data->session, data->msg); - soup_test_session_abort_unref (session); + check_response (data, "gzip", "text/plain", EXPECT_NOT_DECODED, NULL); } +static void +do_coding_req_empty_test (CodingTestData *data, gconstpointer test_data) +{ + g_test_bug ("697527"); + + soup_message_headers_append (data->msg->request_headers, + "X-Test-Options", "empty"); + do_single_coding_req_test (data, "gzip", "text/plain", EXPECT_NOT_DECODED); +} int main (int argc, char **argv) { + int ret; + test_init (argc, argv, NULL); server = soup_test_server_new (TRUE); @@ -565,13 +491,74 @@ main (int argc, char **argv) base_uri = soup_uri_new ("http://127.0.0.1/"); soup_uri_set_port (base_uri, soup_server_get_port (server)); - do_coding_test (); - do_coding_req_test (); - do_coding_empty_test (); + g_test_add ("/coding/message/plain", CodingTestData, + GINT_TO_POINTER (CODING_TEST_NO_DECODER), + setup_coding_test, do_coding_test_plain, teardown_coding_test); + g_test_add ("/coding/message/gzip", CodingTestData, + GINT_TO_POINTER (CODING_TEST_DEFAULT), + setup_coding_test, do_coding_test_gzip, teardown_coding_test); + g_test_add ("/coding/message/gzip/with-junk", CodingTestData, + GINT_TO_POINTER (CODING_TEST_DEFAULT), + setup_coding_test, do_coding_test_gzip_with_junk, teardown_coding_test); + g_test_add ("/coding/message/gzip/bad-server", CodingTestData, + GINT_TO_POINTER (CODING_TEST_DEFAULT), + setup_coding_test, do_coding_test_gzip_bad_server, teardown_coding_test); + g_test_add ("/coding/message/deflate", CodingTestData, + GINT_TO_POINTER (CODING_TEST_DEFAULT), + setup_coding_test, do_coding_test_deflate, teardown_coding_test); + g_test_add ("/coding/message/deflate/with-junk", CodingTestData, + GINT_TO_POINTER (CODING_TEST_DEFAULT), + setup_coding_test, do_coding_test_deflate_with_junk, teardown_coding_test); + g_test_add ("/coding/message/deflate/bad-server", CodingTestData, + GINT_TO_POINTER (CODING_TEST_DEFAULT), + setup_coding_test, do_coding_test_deflate_bad_server, teardown_coding_test); + g_test_add ("/coding/message/deflate-raw", CodingTestData, + GINT_TO_POINTER (CODING_TEST_DEFAULT), + setup_coding_test, do_coding_test_deflate_raw, teardown_coding_test); + g_test_add ("/coding/message/deflate-raw/bad-server", CodingTestData, + GINT_TO_POINTER (CODING_TEST_DEFAULT), + setup_coding_test, do_coding_test_deflate_raw_bad_server, teardown_coding_test); + + g_test_add ("/coding/request/plain", CodingTestData, + GINT_TO_POINTER (CODING_TEST_NO_DECODER | CODING_TEST_REQUEST_API), + setup_coding_test, do_coding_req_test_plain, teardown_coding_test); + g_test_add ("/coding/request/gzip", CodingTestData, + GINT_TO_POINTER (CODING_TEST_REQUEST_API), + setup_coding_test, do_coding_req_test_gzip, teardown_coding_test); + g_test_add ("/coding/request/gzip/with-junk", CodingTestData, + GINT_TO_POINTER (CODING_TEST_REQUEST_API), + setup_coding_test, do_coding_req_test_gzip_with_junk, teardown_coding_test); + g_test_add ("/coding/request/gzip/bad-server", CodingTestData, + GINT_TO_POINTER (CODING_TEST_REQUEST_API), + setup_coding_test, do_coding_req_test_gzip_bad_server, teardown_coding_test); + g_test_add ("/coding/request/deflate", CodingTestData, + GINT_TO_POINTER (CODING_TEST_REQUEST_API), + setup_coding_test, do_coding_req_test_deflate, teardown_coding_test); + g_test_add ("/coding/request/deflate/with-junk", CodingTestData, + GINT_TO_POINTER (CODING_TEST_REQUEST_API), + setup_coding_test, do_coding_req_test_deflate_with_junk, teardown_coding_test); + g_test_add ("/coding/request/deflate/bad-server", CodingTestData, + GINT_TO_POINTER (CODING_TEST_REQUEST_API), + setup_coding_test, do_coding_req_test_deflate_bad_server, teardown_coding_test); + g_test_add ("/coding/request/deflate-raw", CodingTestData, + GINT_TO_POINTER (CODING_TEST_REQUEST_API), + setup_coding_test, do_coding_req_test_deflate_raw, teardown_coding_test); + g_test_add ("/coding/request/deflate-raw/bad-server", CodingTestData, + GINT_TO_POINTER (CODING_TEST_REQUEST_API), + setup_coding_test, do_coding_req_test_deflate_raw_bad_server, teardown_coding_test); + + g_test_add ("/coding/message/empty", CodingTestData, + GINT_TO_POINTER (CODING_TEST_EMPTY), + setup_coding_test, do_coding_msg_empty_test, teardown_coding_test); + g_test_add ("/coding/request/empty", CodingTestData, + GINT_TO_POINTER (CODING_TEST_REQUEST_API | CODING_TEST_EMPTY), + setup_coding_test, do_coding_req_empty_test, teardown_coding_test); + + ret = g_test_run (); soup_uri_free (base_uri); soup_test_server_quit_unref (server); test_cleanup (); - return errors != 0; + return ret; } diff --git a/tests/connection-test.c b/tests/connection-test.c index ddf0c988..8f8c74be 100644 --- a/tests/connection-test.c +++ b/tests/connection-test.c @@ -5,6 +5,8 @@ #include "test-utils.h" +#include <gio/gnetworking.h> + SoupServer *server; SoupURI *base_uri; GMutex server_mutex; @@ -164,7 +166,7 @@ do_content_length_framing_test (void) SoupURI *request_uri; goffset declared_length; - debug_printf (1, "\nInvalid Content-Length framing tests\n"); + g_test_bug ("611481"); session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); @@ -172,21 +174,14 @@ do_content_length_framing_test (void) request_uri = soup_uri_new_with_base (base_uri, "/content-length/long"); msg = soup_message_new_from_uri ("GET", request_uri); soup_session_send_message (session, msg); - if (msg->status_code != SOUP_STATUS_OK) { - debug_printf (1, " Unexpected response: %d %s\n", - msg->status_code, msg->reason_phrase); - errors++; - } else { - declared_length = soup_message_headers_get_content_length (msg->response_headers); - debug_printf (2, " Content-Length: %lu, body: %s\n", - (gulong)declared_length, msg->response_body->data); - if (msg->response_body->length >= declared_length) { - debug_printf (1, " Body length %lu >= declared length %lu\n", - (gulong)msg->response_body->length, - (gulong)declared_length); - errors++; - } - } + + soup_test_assert_message_status (msg, SOUP_STATUS_OK); + + declared_length = soup_message_headers_get_content_length (msg->response_headers); + debug_printf (2, " Content-Length: %lu, body: %s\n", + (gulong)declared_length, msg->response_body->data); + g_assert_cmpint (msg->response_body->length, <, declared_length); + soup_uri_free (request_uri); g_object_unref (msg); @@ -194,21 +189,12 @@ do_content_length_framing_test (void) request_uri = soup_uri_new_with_base (base_uri, "/content-length/noclose"); msg = soup_message_new_from_uri ("GET", request_uri); soup_session_send_message (session, msg); - if (msg->status_code != SOUP_STATUS_OK) { - debug_printf (1, " Unexpected response: %d %s\n", - msg->status_code, msg->reason_phrase); - errors++; - } else { - declared_length = soup_message_headers_get_content_length (msg->response_headers); - debug_printf (2, " Content-Length: %lu, body: %s\n", - (gulong)declared_length, msg->response_body->data); - if (msg->response_body->length != declared_length) { - debug_printf (1, " Body length %lu != declared length %lu\n", - (gulong)msg->response_body->length, - (gulong)declared_length); - errors++; - } - } + + soup_test_assert_message_status (msg, SOUP_STATUS_OK); + + declared_length = soup_message_headers_get_content_length (msg->response_headers); + g_assert_cmpint (msg->response_body->length, ==, declared_length); + soup_uri_free (request_uri); g_object_unref (msg); @@ -232,12 +218,11 @@ request_started_socket_collector (SoupSession *session, SoupMessage *msg, * two consecutive sockets. */ sockets[i] = g_object_ref (socket); - return; + break; } } - debug_printf (1, " socket queue overflowed!\n"); - errors++; + soup_test_assert (i < 4, "socket queue overflowed"); } static void @@ -257,14 +242,10 @@ do_timeout_test_for_session (SoupSession *session) msg = soup_message_new_from_uri ("GET", timeout_uri); soup_uri_free (timeout_uri); soup_session_send_message (session, msg); - if (msg->status_code != SOUP_STATUS_OK) { - debug_printf (1, " Unexpected response: %d %s\n", - msg->status_code, msg->reason_phrase); - errors++; - } + soup_test_assert_message_status (msg, SOUP_STATUS_OK); + if (sockets[1]) { - debug_printf (1, " Message was retried??\n"); - errors++; + soup_test_assert (sockets[1] == NULL, "Message was retried"); sockets[1] = sockets[2] = sockets[3] = NULL; } g_object_unref (msg); @@ -272,24 +253,16 @@ do_timeout_test_for_session (SoupSession *session) debug_printf (1, " Second message\n"); msg = soup_message_new_from_uri ("GET", base_uri); soup_session_send_message (session, msg); - if (msg->status_code != SOUP_STATUS_OK) { - debug_printf (1, " Unexpected response: %d %s\n", - msg->status_code, msg->reason_phrase); - errors++; - } - if (sockets[1] != sockets[0]) { - debug_printf (1, " Message was not retried on existing connection\n"); - errors++; - } else if (!sockets[2]) { - debug_printf (1, " Message was not retried after disconnect\n"); - errors++; - } else if (sockets[2] == sockets[1]) { - debug_printf (1, " Message was retried on closed connection??\n"); - errors++; - } else if (sockets[3]) { - debug_printf (1, " Message was retried again??\n"); - errors++; - } + soup_test_assert_message_status (msg, SOUP_STATUS_OK); + + soup_test_assert (sockets[1] == sockets[0], + "Message was not retried on existing connection"); + soup_test_assert (sockets[2] != NULL, + "Message was not retried after disconnect"); + soup_test_assert (sockets[2] != sockets[1], + "Message was retried on closed connection"); + soup_test_assert (sockets[3] == NULL, + "Message was retried again"); g_object_unref (msg); for (i = 0; sockets[i]; i++) @@ -299,7 +272,6 @@ do_timeout_test_for_session (SoupSession *session) static void do_timeout_req_test_for_session (SoupSession *session) { - SoupRequester *requester; SoupRequest *req; SoupMessage *msg; GInputStream *stream; @@ -308,94 +280,67 @@ do_timeout_req_test_for_session (SoupSession *session) GError *error = NULL; int i; - requester = soup_requester_new (); - soup_session_add_feature (session, SOUP_SESSION_FEATURE (requester)); - g_object_unref (requester); - g_signal_connect (session, "request-started", G_CALLBACK (request_started_socket_collector), &sockets); debug_printf (1, " First request\n"); timeout_uri = soup_uri_new_with_base (base_uri, "/timeout-persistent"); - req = soup_requester_request_uri (requester, timeout_uri, NULL); + req = soup_session_request_uri (session, timeout_uri, NULL); soup_uri_free (timeout_uri); - if (SOUP_IS_SESSION_SYNC (session)) - stream = soup_request_send (req, NULL, &error); - else - stream = soup_test_request_send_async_as_sync (req, NULL, &error); - - if (!stream) { - debug_printf (1, " Unexpected error on send: %s\n", - error->message); - errors++; + stream = soup_test_request_send (req, NULL, 0, &error); + if (error) { + g_assert_no_error (error); g_clear_error (&error); } else { - if (SOUP_IS_SESSION_SYNC (session)) - g_input_stream_close (stream, NULL, &error); - else - soup_test_stream_close_async_as_sync (stream, NULL, &error); + soup_test_request_read_all (req, stream, NULL, &error); + if (error) { + g_assert_no_error (error); + g_clear_error (&error); + } + + soup_test_request_close_stream (req, stream, NULL, &error); if (error) { - debug_printf (1, " Unexpected error on close: %s\n", - error->message); - errors++; + g_assert_no_error (error); g_clear_error (&error); } + g_object_unref (stream); } if (sockets[1]) { - debug_printf (1, " Message was retried??\n"); - errors++; + soup_test_assert (sockets[1] == NULL, "Message was retried"); sockets[1] = sockets[2] = sockets[3] = NULL; } g_object_unref (req); debug_printf (1, " Second request\n"); - req = soup_requester_request_uri (requester, base_uri, NULL); + req = soup_session_request_uri (session, base_uri, NULL); - if (SOUP_IS_SESSION_SYNC (session)) - stream = soup_request_send (req, NULL, &error); - else - stream = soup_test_request_send_async_as_sync (req, NULL, &error); - - if (!stream) { - debug_printf (1, " Unexpected error on send: %s\n", - error->message); - errors++; + stream = soup_test_request_send (req, NULL, 0, &error); + if (error) { + g_assert_no_error (error); g_clear_error (&error); } else { - if (SOUP_IS_SESSION_SYNC (session)) - g_input_stream_close (stream, NULL, &error); - else - soup_test_stream_close_async_as_sync (stream, NULL, &error); + soup_test_request_close_stream (req, stream, NULL, &error); if (error) { - debug_printf (1, " Unexpected error on close: %s\n", - error->message); - errors++; + g_assert_no_error (error); g_clear_error (&error); } + g_object_unref (stream); } msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (req)); - if (msg->status_code != SOUP_STATUS_OK) { - debug_printf (1, " Unexpected response: %d %s\n", - msg->status_code, msg->reason_phrase); - errors++; - } - if (sockets[1] != sockets[0]) { - debug_printf (1, " Message was not retried on existing connection\n"); - errors++; - } else if (!sockets[2]) { - debug_printf (1, " Message was not retried after disconnect\n"); - errors++; - } else if (sockets[2] == sockets[1]) { - debug_printf (1, " Message was retried on closed connection??\n"); - errors++; - } else if (sockets[3]) { - debug_printf (1, " Message was retried again??\n"); - errors++; - } + soup_test_assert_message_status (msg, SOUP_STATUS_OK); + + soup_test_assert (sockets[1] == sockets[0], + "Message was not retried on existing connection"); + soup_test_assert (sockets[2] != NULL, + "Message was not retried after disconnect"); + soup_test_assert (sockets[2] != sockets[1], + "Message was retried on closed connection"); + soup_test_assert (sockets[3] == NULL, + "Message was retried again"); g_object_unref (msg); g_object_unref (req); @@ -408,13 +353,14 @@ do_persistent_connection_timeout_test (void) { SoupSession *session; - debug_printf (1, "\nUnexpected timing out of persistent connections\n"); + g_test_bug ("631525"); debug_printf (1, " Async session, message API\n"); session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); do_timeout_test_for_session (session); soup_test_session_abort_unref (session); + debug_printf (1, " Async session, request API\n"); session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, SOUP_SESSION_USE_THREAD_CONTEXT, TRUE, NULL); @@ -426,6 +372,7 @@ do_persistent_connection_timeout_test (void) do_timeout_test_for_session (session); soup_test_session_abort_unref (session); + debug_printf (1, " Sync session, request API\n"); session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL); do_timeout_req_test_for_session (session); soup_test_session_abort_unref (session); @@ -491,26 +438,18 @@ do_max_conns_test_for_session (SoupSession *session) } g_main_loop_run (max_conns_loop); - if (msgs_done != MAX_CONNS) { - debug_printf (1, " Queued %d connections out of max %d?", - msgs_done, MAX_CONNS); - errors++; - } + g_assert_cmpint (msgs_done, ==, MAX_CONNS); g_signal_handlers_disconnect_by_func (session, max_conns_request_started, NULL); msgs_done = 0; g_idle_add (idle_start_server, NULL); + if (quit_loop_timeout) + g_source_remove (quit_loop_timeout); quit_loop_timeout = g_timeout_add (1000, quit_loop, NULL); g_main_loop_run (max_conns_loop); - for (i = 0; i < TEST_CONNS; i++) { - if (!SOUP_STATUS_IS_SUCCESSFUL (msgs[i]->status_code)) { - debug_printf (1, " Message %d failed? %d %s\n", - i, msgs[i]->status_code, - msgs[i]->reason_phrase ? msgs[i]->reason_phrase : "-"); - errors++; - } - } + for (i = 0; i < TEST_CONNS; i++) + soup_test_assert_message_status (msgs[i], SOUP_STATUS_OK); if (msgs_done != TEST_CONNS) { /* Clean up so we don't get a spurious "Leaked @@ -522,8 +461,10 @@ do_max_conns_test_for_session (SoupSession *session) } g_main_loop_unref (max_conns_loop); - if (quit_loop_timeout) + if (quit_loop_timeout) { g_source_remove (quit_loop_timeout); + quit_loop_timeout = 0; + } for (i = 0; i < TEST_CONNS; i++) g_object_unref (msgs[i]); @@ -534,7 +475,7 @@ do_max_conns_test (void) { SoupSession *session; - debug_printf (1, "\nExceeding max-conns\n"); + g_test_bug ("634422"); debug_printf (1, " Async session\n"); session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, @@ -551,8 +492,6 @@ do_max_conns_test (void) soup_test_session_abort_unref (session); } -GMainLoop *loop; - static void np_request_started (SoupSession *session, SoupMessage *msg, SoupSocket *socket, gpointer user_data) @@ -568,10 +507,14 @@ np_request_unqueued (SoupSession *session, SoupMessage *msg, { SoupSocket *socket = *(SoupSocket **)user_data; - if (soup_socket_is_connected (socket)) { - debug_printf (1, " socket is still connected\n"); - errors++; - } + g_assert_false (soup_socket_is_connected (socket)); +} + +static void +np_request_finished (SoupSession *session, SoupMessage *msg, + gpointer user_data) +{ + GMainLoop *loop = user_data; g_main_loop_quit (loop); } @@ -581,6 +524,7 @@ do_non_persistent_test_for_session (SoupSession *session) { SoupMessage *msg; SoupSocket *socket = NULL; + GMainLoop *loop; loop = g_main_loop_new (NULL, FALSE); @@ -594,15 +538,15 @@ do_non_persistent_test_for_session (SoupSession *session) msg = soup_message_new_from_uri ("GET", base_uri); soup_message_headers_append (msg->request_headers, "Connection", "close"); g_object_ref (msg); - soup_session_queue_message (session, msg, NULL, NULL); + soup_session_queue_message (session, msg, + np_request_finished, loop); g_main_loop_run (loop); + g_main_loop_unref (loop); + + soup_test_assert_message_status (msg, SOUP_STATUS_OK); - if (msg->status_code != SOUP_STATUS_OK) { - debug_printf (1, " Unexpected response: %d %s\n", - msg->status_code, msg->reason_phrase); - errors++; - } g_object_unref (msg); + g_object_unref (socket); } static void @@ -610,7 +554,7 @@ do_non_persistent_connection_test (void) { SoupSession *session; - debug_printf (1, "\nNon-persistent connections are closed immediately\n"); + g_test_bug ("578990"); debug_printf (1, " Async session\n"); session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); @@ -637,14 +581,9 @@ do_non_idempotent_test_for_session (SoupSession *session) debug_printf (2, " GET\n"); msg = soup_message_new_from_uri ("GET", base_uri); soup_session_send_message (session, msg); - if (msg->status_code != SOUP_STATUS_OK) { - debug_printf (1, " Unexpected response: %d %s\n", - msg->status_code, msg->reason_phrase); - errors++; - } + soup_test_assert_message_status (msg, SOUP_STATUS_OK); if (sockets[1]) { - debug_printf (1, " Message was retried??\n"); - errors++; + soup_test_assert (sockets[1] == NULL, "Message was retried"); sockets[1] = sockets[2] = sockets[3] = NULL; } g_object_unref (msg); @@ -652,19 +591,12 @@ do_non_idempotent_test_for_session (SoupSession *session) debug_printf (2, " POST\n"); msg = soup_message_new_from_uri ("POST", base_uri); soup_session_send_message (session, msg); - if (msg->status_code != SOUP_STATUS_OK) { - debug_printf (1, " Unexpected response: %d %s\n", - msg->status_code, msg->reason_phrase); - errors++; - } - if (sockets[1] == sockets[0]) { - debug_printf (1, " Message was sent on existing connection!\n"); - errors++; - } - if (sockets[2]) { - debug_printf (1, " Too many connections used...\n"); - errors++; - } + soup_test_assert_message_status (msg, SOUP_STATUS_OK); + soup_test_assert (sockets[1] != sockets[0], + "Message was sent on existing connection"); + soup_test_assert (sockets[2] == NULL, + "Too many connections used"); + g_object_unref (msg); for (i = 0; sockets[i]; i++) @@ -676,8 +608,6 @@ do_non_idempotent_connection_test (void) { SoupSession *session; - debug_printf (1, "\nNon-idempotent methods are always sent on new connections\n"); - debug_printf (1, " Async session\n"); session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); do_non_idempotent_test_for_session (session); @@ -689,25 +619,254 @@ do_non_idempotent_connection_test (void) soup_test_session_abort_unref (session); } +#define HTTP_SERVER "http://127.0.0.1:47524" +#define HTTPS_SERVER "https://127.0.0.1:47525" +#define HTTP_PROXY "http://127.0.0.1:47526" + +static SoupConnectionState state_transitions[] = { + /* NEW -> */ SOUP_CONNECTION_CONNECTING, + /* CONNECTING -> */ SOUP_CONNECTION_IN_USE, + /* IDLE -> */ SOUP_CONNECTION_DISCONNECTED, + /* IN_USE -> */ SOUP_CONNECTION_IDLE, + + /* REMOTE_DISCONNECTED */ -1, + /* DISCONNECTED */ -1, +}; + +static const char *state_names[] = { + "NEW", "CONNECTING", "IDLE", "IN_USE", + "REMOTE_DISCONNECTED", "DISCONNECTED" +}; + +static void +connection_state_changed (GObject *object, GParamSpec *param, + gpointer user_data) +{ + SoupConnectionState *state = user_data; + SoupConnectionState new_state; + + g_object_get (object, "state", &new_state, NULL); + debug_printf (2, " %s -> %s\n", + state_names[*state], state_names[new_state]); + soup_test_assert (state_transitions[*state] == new_state, + "Unexpected transition: %s -> %s\n", + state_names[*state], state_names[new_state]); + *state = new_state; +} + +static void +connection_created (SoupSession *session, GObject *conn, + gpointer user_data) +{ + SoupConnectionState *state = user_data; + + g_object_get (conn, "state", state, NULL); + g_assert_cmpint (*state, ==, SOUP_CONNECTION_NEW); + + g_signal_connect (conn, "notify::state", + G_CALLBACK (connection_state_changed), + state); +} + +static void +do_one_connection_state_test (SoupSession *session, const char *uri) +{ + SoupMessage *msg; + + msg = soup_message_new ("GET", uri); + soup_session_send_message (session, msg); + soup_test_assert_message_status (msg, SOUP_STATUS_OK); + g_object_unref (msg); + soup_session_abort (session); +} + +static void +do_connection_state_test_for_session (SoupSession *session) +{ + SoupConnectionState state; + SoupURI *proxy_uri; + + g_signal_connect (session, "connection-created", + G_CALLBACK (connection_created), + &state); + + debug_printf (1, " http\n"); + do_one_connection_state_test (session, HTTP_SERVER); + + if (tls_available) { + debug_printf (1, " https\n"); + do_one_connection_state_test (session, HTTPS_SERVER); + } else + debug_printf (1, " https -- SKIPPING\n"); + + proxy_uri = soup_uri_new (HTTP_PROXY); + g_object_set (G_OBJECT (session), + SOUP_SESSION_PROXY_URI, proxy_uri, + NULL); + soup_uri_free (proxy_uri); + + debug_printf (1, " http with proxy\n"); + do_one_connection_state_test (session, HTTP_SERVER); + + if (tls_available) { + debug_printf (1, " https with proxy\n"); + do_one_connection_state_test (session, HTTPS_SERVER); + } else + debug_printf (1, " https with proxy -- SKIPPING\n"); +} + +static void +do_connection_state_test (void) +{ + SoupSession *session; + + SOUP_TEST_SKIP_IF_NO_APACHE; + + debug_printf (1, " Async session\n"); + session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); + do_connection_state_test_for_session (session); + soup_test_session_abort_unref (session); + + debug_printf (1, " Sync session\n"); + session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL); + do_connection_state_test_for_session (session); + soup_test_session_abort_unref (session); +} + + +static const char *event_names[] = { + "RESOLVING", "RESOLVED", "CONNECTING", "CONNECTED", + "PROXY_NEGOTIATING", "PROXY_NEGOTIATED", + "TLS_HANDSHAKING", "TLS_HANDSHAKED", "COMPLETE" +}; + +static const char event_abbrevs[] = { + 'r', 'R', 'c', 'C', 'p', 'P', 't', 'T', 'x', '\0' +}; + +static const char * +event_name_from_abbrev (char abbrev) +{ + int evt; + + for (evt = 0; event_abbrevs[evt]; evt++) { + if (event_abbrevs[evt] == abbrev) + return event_names[evt]; + } + return "???"; +} + +static void +network_event (SoupMessage *msg, GSocketClientEvent event, + GIOStream *connection, gpointer user_data) +{ + const char **events = user_data; + + debug_printf (2, " %s\n", event_name_from_abbrev (**events)); + soup_test_assert (**events == event_abbrevs[event], + "Unexpected event: %s (expected %s)\n", + event_names[event], + event_name_from_abbrev (**events)); + *events = *events + 1; +} + +static void +do_one_connection_event_test (SoupSession *session, const char *uri, + const char *events) +{ + SoupMessage *msg; + + msg = soup_message_new ("GET", uri); + g_signal_connect (msg, "network-event", + G_CALLBACK (network_event), + &events); + soup_session_send_message (session, msg); + soup_test_assert_message_status (msg, SOUP_STATUS_OK); + while (*events) { + soup_test_assert (!*events, + "Expected %s", + event_name_from_abbrev (*events)); + events++; + } + + g_object_unref (msg); + soup_session_abort (session); +} + +static void +do_connection_event_test_for_session (SoupSession *session) +{ + SoupURI *proxy_uri; + + debug_printf (1, " http\n"); + do_one_connection_event_test (session, HTTP_SERVER, "rRcCx"); + + if (tls_available) { + debug_printf (1, " https\n"); + do_one_connection_event_test (session, HTTPS_SERVER, "rRcCtTx"); + } else + debug_printf (1, " https -- SKIPPING\n"); + + proxy_uri = soup_uri_new (HTTP_PROXY); + g_object_set (G_OBJECT (session), + SOUP_SESSION_PROXY_URI, proxy_uri, + NULL); + soup_uri_free (proxy_uri); + + debug_printf (1, " http with proxy\n"); + do_one_connection_event_test (session, HTTP_SERVER, "rRcCx"); + + if (tls_available) { + debug_printf (1, " https with proxy\n"); + do_one_connection_event_test (session, HTTPS_SERVER, "rRcCpPtTx"); + } else + debug_printf (1, " https with proxy -- SKIPPING\n"); +} + +static void +do_connection_event_test (void) +{ + SoupSession *session; + + SOUP_TEST_SKIP_IF_NO_APACHE; + + debug_printf (1, " Async session\n"); + session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); + do_connection_event_test_for_session (session); + soup_test_session_abort_unref (session); + + debug_printf (1, " Sync session\n"); + session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL); + do_connection_event_test_for_session (session); + soup_test_session_abort_unref (session); +} + int main (int argc, char **argv) { + int ret; + test_init (argc, argv, NULL); + apache_init (); server = soup_test_server_new (TRUE); soup_server_add_handler (server, NULL, server_callback, "http", NULL); base_uri = soup_uri_new ("http://127.0.0.1/"); soup_uri_set_port (base_uri, soup_server_get_port (server)); - do_content_length_framing_test (); - do_persistent_connection_timeout_test (); - do_max_conns_test (); - do_non_persistent_connection_test (); - do_non_idempotent_connection_test (); + g_test_add_func ("/connection/content-length-framing", do_content_length_framing_test); + g_test_add_func ("/connection/persistent-connection-timeout", do_persistent_connection_timeout_test); + g_test_add_func ("/connection/max-conns", do_max_conns_test); + g_test_add_func ("/connection/non-persistent", do_non_persistent_connection_test); + g_test_add_func ("/connection/non-idempotent", do_non_idempotent_connection_test); + g_test_add_func ("/connection/state", do_connection_state_test); + g_test_add_func ("/connection/event", do_connection_event_test); + + ret = g_test_run (); soup_uri_free (base_uri); soup_test_server_quit_unref (server); test_cleanup (); - return errors != 0; + return ret; } diff --git a/tests/context-test.c b/tests/context-test.c index 97cd2c0c..727c63ba 100644 --- a/tests/context-test.c +++ b/tests/context-test.c @@ -84,13 +84,9 @@ static GMutex test1_mutex; static GMainLoop *test1_loop; static void -do_test1 (int n, gboolean use_thread_context) +do_test1 (gconstpointer data) { - debug_printf (1, "\nTest %d: blocking the main thread does not block other thread\n", n); - if (use_thread_context) - debug_printf (1, "(Using g_main_context_push_thread_default())\n"); - else - debug_printf (1, "(Using SOUP_SESSION_ASYNC_CONTEXT)\n"); + gboolean use_thread_context = GPOINTER_TO_INT (data); test1_loop = g_main_loop_new (NULL, FALSE); g_idle_add (idle_start_test1_thread, GINT_TO_POINTER (use_thread_context)); @@ -111,9 +107,8 @@ idle_start_test1_thread (gpointer use_thread_context) if (g_cond_wait_until (&test1_cond, &test1_mutex, time)) g_thread_join (thread); else { - debug_printf (1, " timeout!\n"); + soup_test_assert (FALSE, "timeout"); g_thread_unref (thread); - errors++; } g_mutex_unlock (&test1_mutex); @@ -158,11 +153,7 @@ test1_thread (gpointer use_thread_context) debug_printf (1, " send_message\n"); msg = soup_message_new ("GET", uri); soup_session_send_message (session, msg); - if (msg->status_code != SOUP_STATUS_OK) { - debug_printf (1, " unexpected status: %d %s\n", - msg->status_code, msg->reason_phrase); - errors++; - } + soup_test_assert_message_status (msg, SOUP_STATUS_OK); g_object_unref (msg); debug_printf (1, " queue_message\n"); @@ -172,11 +163,7 @@ test1_thread (gpointer use_thread_context) soup_session_queue_message (session, msg, test1_finished, loop); g_main_loop_run (loop); g_main_loop_unref (loop); - if (msg->status_code != SOUP_STATUS_OK) { - debug_printf (1, " unexpected status: %d %s\n", - msg->status_code, msg->reason_phrase); - errors++; - } + soup_test_assert_message_status (msg, SOUP_STATUS_OK); g_object_unref (msg); soup_test_session_abort_unref (session); @@ -196,20 +183,15 @@ test1_thread (gpointer use_thread_context) static gboolean idle_test2_fail (gpointer user_data); static void -do_test2 (int n, gboolean use_thread_context) +do_test2 (gconstpointer data) { + gboolean use_thread_context = GPOINTER_TO_INT (data); guint idle; GMainContext *async_context; SoupSession *session; char *uri; SoupMessage *msg; - debug_printf (1, "\nTest %d: a session with its own context is independent of the main loop.\n", n); - if (use_thread_context) - debug_printf (1, "(Using g_main_context_push_thread_default())\n"); - else - debug_printf (1, "(Using SOUP_SESSION_ASYNC_CONTEXT)\n"); - idle = g_idle_add_full (G_PRIORITY_HIGH, idle_test2_fail, NULL, NULL); async_context = g_main_context_new (); @@ -230,11 +212,7 @@ do_test2 (int n, gboolean use_thread_context) debug_printf (1, " send_message\n"); msg = soup_message_new ("GET", uri); soup_session_send_message (session, msg); - if (msg->status_code != SOUP_STATUS_OK) { - debug_printf (1, " unexpected status: %d %s\n", - msg->status_code, msg->reason_phrase); - errors++; - } + soup_test_assert_message_status (msg, SOUP_STATUS_OK); g_object_unref (msg); soup_test_session_abort_unref (session); @@ -249,8 +227,7 @@ do_test2 (int n, gboolean use_thread_context) static gboolean idle_test2_fail (gpointer user_data) { - debug_printf (1, " idle ran!\n"); - errors++; + soup_test_assert (FALSE, "idle ran"); return FALSE; } @@ -279,15 +256,13 @@ multi_msg_finished (SoupSession *session, SoupMessage *msg, gpointer user_data) } static void -do_multicontext_test (int n) +do_multicontext_test (void) { SoupSession *session; SoupMessage *msg1, *msg2; GMainContext *context1, *context2; GMainLoop *loop1, *loop2; - debug_printf (1, "\nTest %d: Using multiple async contexts\n", n); - session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, SOUP_SESSION_USE_THREAD_CONTEXT, TRUE, NULL); @@ -318,36 +293,26 @@ do_multicontext_test (int n) g_main_loop_run (loop1); g_main_context_pop_thread_default (context1); - if (!g_object_get_data (G_OBJECT (msg1), "started")) { - debug_printf (1, " msg1 not started??\n"); - errors++; - } - if (g_object_get_data (G_OBJECT (msg2), "started")) { - debug_printf (1, " msg2 started while loop1 was running!\n"); - errors++; - } + if (!g_object_get_data (G_OBJECT (msg1), "started")) + soup_test_assert (FALSE, "msg1 not started"); + if (g_object_get_data (G_OBJECT (msg2), "started")) + soup_test_assert (FALSE, "msg2 started while loop1 was running"); g_main_context_push_thread_default (context2); g_main_loop_run (loop2); g_main_context_pop_thread_default (context2); - if (g_object_get_data (G_OBJECT (msg1), "finished")) { - debug_printf (1, " msg1 finished while loop2 was running!\n"); - errors++; - } - if (!g_object_get_data (G_OBJECT (msg2), "finished")) { - debug_printf (1, " msg2 not finished??\n"); - errors++; - } + if (g_object_get_data (G_OBJECT (msg1), "finished")) + soup_test_assert (FALSE, "msg1 finished while loop2 was running"); + if (!g_object_get_data (G_OBJECT (msg2), "finished")) + soup_test_assert (FALSE, "msg2 not finished"); g_main_context_push_thread_default (context1); g_main_loop_run (loop1); g_main_context_pop_thread_default (context1); - if (!g_object_get_data (G_OBJECT (msg1), "finished")) { - debug_printf (1, " msg1 not finished??\n"); - errors++; - } + if (!g_object_get_data (G_OBJECT (msg1), "finished")) + soup_test_assert (FALSE, "msg1 not finished"); g_object_unref (msg1); g_object_unref (msg2); @@ -364,6 +329,7 @@ int main (int argc, char **argv) { SoupServer *server; + int ret; test_init (argc, argv, NULL); @@ -372,15 +338,17 @@ main (int argc, char **argv) base_uri = g_strdup_printf ("http://127.0.0.1:%u/", soup_server_get_port (server)); - do_test1 (1, FALSE); - do_test1 (2, TRUE); - do_test2 (3, FALSE); - do_test2 (4, TRUE); - do_multicontext_test (5); + g_test_add_data_func ("/context/blocking/explicit", GINT_TO_POINTER (FALSE), do_test1); + g_test_add_data_func ("/context/blocking/thread-default", GINT_TO_POINTER (TRUE), do_test1); + g_test_add_data_func ("/context/nested/explicit", GINT_TO_POINTER (FALSE), do_test2); + g_test_add_data_func ("/context/nested/thread-default", GINT_TO_POINTER (TRUE), do_test2); + g_test_add_func ("/context/multiple", do_multicontext_test); + + ret = g_test_run (); g_free (base_uri); soup_test_server_quit_unref (server); test_cleanup (); - return errors != 0; + return ret; } diff --git a/tests/continue-test.c b/tests/continue-test.c index fa9ef809..b6a5805f 100644 --- a/tests/continue-test.c +++ b/tests/continue-test.c @@ -59,13 +59,6 @@ do_message (const char *path, gboolean long_body, const char *expected_event; char *actual_event; int expected_status, actual_status; - static int count = 1; - - debug_printf (1, "%d. /%s, %s body, %sExpect, %s password\n", - count++, path, - long_body ? "long" : "short", - expect_continue ? "" : "no ", - auth ? "with" : "without"); uri = g_strdup_printf ("http://%s127.0.0.1:%d/%s", auth ? "user:pass@" : "", @@ -106,17 +99,13 @@ do_message (const char *path, gboolean long_body, while ((expected_event = va_arg (ap, const char *))) { if (!events) { - actual_event = g_strdup (""); - debug_printf (1, " Expected '%s', got end of list\n", - expected_event); - errors++; + soup_test_assert (events != NULL, + "Expected '%s', got end of list", + expected_event); + continue; } else { actual_event = events->data; - if (strcmp (expected_event, actual_event) != 0) { - debug_printf (1, " Expected '%s', got '%s'\n", - expected_event, actual_event); - errors++; - } + g_assert_cmpstr (expected_event, ==, actual_event); events = g_slist_delete_link (events, events); } @@ -134,10 +123,10 @@ do_message (const char *path, gboolean long_body, if (expected_status != -1 && actual_status != -1 && expected_status != actual_status) { - debug_printf (1, " Expected status '%s', got '%s'\n", - soup_status_get_phrase (expected_status), - soup_status_get_phrase (actual_status)); - errors++; + soup_test_assert (expected_status == actual_status, + "Expected status '%s', got '%s'", + soup_status_get_phrase (expected_status), + soup_status_get_phrase (actual_status)); } g_free (actual_event); @@ -145,8 +134,8 @@ do_message (const char *path, gboolean long_body, va_end (ap); while (events) { actual_event = events->data; - debug_printf (1, " Expected to be done, got '%s'\n", actual_event); - errors++; + soup_test_assert (events == NULL, + "Expected to be done, got '%s'", actual_event); events = g_slist_delete_link (events, events); if (!strcmp (actual_event, "server-wrote_headers") || @@ -157,7 +146,7 @@ do_message (const char *path, gboolean long_body, } static void -run_tests (void) +do_test_unauth_short_noexpect_nopass (void) { do_message ("unauth", FALSE, FALSE, FALSE, "client-wrote_headers", @@ -171,6 +160,11 @@ run_tests (void) "client-got_body", "client-finished", NULL); +} + +static void +do_test_unauth_long_noexpect_nopass (void) +{ do_message ("unauth", TRUE, FALSE, FALSE, "client-wrote_headers", "client-wrote_body", @@ -183,6 +177,11 @@ run_tests (void) "client-got_body", "client-finished", NULL); +} + +static void +do_test_unauth_short_expect_nopass (void) +{ do_message ("unauth", FALSE, TRUE, FALSE, "client-wrote_headers", "server-got_headers", @@ -197,6 +196,11 @@ run_tests (void) "client-got_body", "client-finished", NULL); +} + +static void +do_test_unauth_long_expect_nopass (void) +{ do_message ("unauth", TRUE, TRUE, FALSE, "client-wrote_headers", "server-got_headers", @@ -207,7 +211,11 @@ run_tests (void) "client-got_body", "client-finished", NULL); +} +static void +do_test_auth_short_noexpect_nopass (void) +{ do_message ("auth", FALSE, FALSE, FALSE, "client-wrote_headers", "client-wrote_body", @@ -220,6 +228,11 @@ run_tests (void) "client-got_body", "client-finished", NULL); +} + +static void +do_test_auth_long_noexpect_nopass (void) +{ do_message ("auth", TRUE, FALSE, FALSE, "client-wrote_headers", "client-wrote_body", @@ -232,6 +245,11 @@ run_tests (void) "client-got_body", "client-finished", NULL); +} + +static void +do_test_auth_short_expect_nopass (void) +{ do_message ("auth", FALSE, TRUE, FALSE, "client-wrote_headers", "server-got_headers", @@ -242,6 +260,11 @@ run_tests (void) "client-got_body", "client-finished", NULL); +} + +static void +do_test_auth_long_expect_nopass (void) +{ do_message ("auth", TRUE, TRUE, FALSE, "client-wrote_headers", "server-got_headers", @@ -252,7 +275,11 @@ run_tests (void) "client-got_body", "client-finished", NULL); +} +static void +do_test_auth_short_noexpect_pass (void) +{ do_message ("auth", FALSE, FALSE, TRUE, "client-wrote_headers", "client-wrote_body", @@ -274,6 +301,11 @@ run_tests (void) "client-got_body", "client-finished", NULL); +} + +static void +do_test_auth_long_noexpect_pass (void) +{ do_message ("auth", TRUE, FALSE, TRUE, "client-wrote_headers", "client-wrote_body", @@ -295,6 +327,11 @@ run_tests (void) "client-got_body", "client-finished", NULL); +} + +static void +do_test_auth_short_expect_pass (void) +{ do_message ("auth", FALSE, TRUE, TRUE, "client-wrote_headers", "server-got_headers", @@ -316,6 +353,11 @@ run_tests (void) "client-got_body", "client-finished", NULL); +} + +static void +do_test_auth_long_expect_pass (void) +{ do_message ("auth", TRUE, TRUE, TRUE, "client-wrote_headers", "server-got_headers", @@ -434,15 +476,30 @@ int main (int argc, char **argv) { SoupServer *server; + int ret; test_init (argc, argv, NULL); server = setup_server (); port = soup_server_get_port (server); - run_tests (); + g_test_add_func ("/continue/unauth_short_noexpect_nopass", do_test_unauth_short_noexpect_nopass); + g_test_add_func ("/continue/unauth_long_noexpect_nopass", do_test_unauth_long_noexpect_nopass); + g_test_add_func ("/continue/unauth_short_expect_nopass", do_test_unauth_short_expect_nopass); + g_test_add_func ("/continue/unauth_long_expect_nopass", do_test_unauth_long_expect_nopass); + g_test_add_func ("/continue/auth_short_noexpect_nopass", do_test_auth_short_noexpect_nopass); + g_test_add_func ("/continue/auth_long_noexpect_nopass", do_test_auth_long_noexpect_nopass); + g_test_add_func ("/continue/auth_short_expect_nopass", do_test_auth_short_expect_nopass); + g_test_add_func ("/continue/auth_long_expect_nopass", do_test_auth_long_expect_nopass); + g_test_add_func ("/continue/auth_short_noexpect_pass", do_test_auth_short_noexpect_pass); + g_test_add_func ("/continue/auth_long_noexpect_pass", do_test_auth_long_noexpect_pass); + g_test_add_func ("/continue/auth_short_expect_pass", do_test_auth_short_expect_pass); + g_test_add_func ("/continue/auth_long_expect_pass", do_test_auth_long_expect_pass); + + ret = g_test_run (); soup_test_server_quit_unref (server); test_cleanup (); - return errors != 0; + + return ret; } diff --git a/tests/cookies-test.c b/tests/cookies-test.c index 58f8052e..12529d81 100644 --- a/tests/cookies-test.c +++ b/tests/cookies-test.c @@ -55,8 +55,6 @@ do_cookies_accept_policy_test (void) GSList *l, *p; int i; - debug_printf (1, "SoupCookieJarAcceptPolicy test\n"); - session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); soup_session_add_feature_by_type (session, SOUP_TYPE_COOKIE_JAR); jar = SOUP_COOKIE_JAR (soup_session_get_feature (session, SOUP_TYPE_COOKIE_JAR)); @@ -83,13 +81,7 @@ do_cookies_accept_policy_test (void) g_object_unref (msg); l = soup_cookie_jar_all_cookies (jar); - if (g_slist_length (l) < validResults[i].n_cookies) { - debug_printf (1, " accepted less cookies than it should have\n"); - errors++; - } else if (g_slist_length (l) > validResults[i].n_cookies) { - debug_printf (1, " accepted more cookies than it should have\n"); - errors++; - } + g_assert_cmpint (g_slist_length (l), ==, validResults[i].n_cookies); for (p = l; p; p = p->next) { soup_cookie_jar_delete_cookie (jar, p->data); @@ -113,7 +105,7 @@ do_cookies_parsing_test (void) SoupCookie *cookie; gboolean got1, got2, got3; - debug_printf (1, "\nSoupCookie parsing test\n"); + g_test_bug ("678753"); session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); soup_session_add_feature_by_type (session, SOUP_TYPE_COOKIE_JAR); @@ -146,56 +138,28 @@ do_cookies_parsing_test (void) if (!strcmp (soup_cookie_get_name (cookie), "one")) { got1 = TRUE; - if (!soup_cookie_get_http_only (cookie)) { - debug_printf (1, " cookie 1 is not HttpOnly!\n"); - errors++; - } - if (!soup_cookie_get_expires (cookie)) { - debug_printf (1, " cookie 1 did not fully parse!\n"); - errors++; - } + g_assert_true (soup_cookie_get_http_only (cookie)); + g_assert_true (soup_cookie_get_expires (cookie) != NULL); } else if (!strcmp (soup_cookie_get_name (cookie), "two")) { got2 = TRUE; - if (!soup_cookie_get_http_only (cookie)) { - debug_printf (1, " cookie 2 is not HttpOnly!\n"); - errors++; - } - if (!soup_cookie_get_expires (cookie)) { - debug_printf (1, " cookie 3 did not fully parse!\n"); - errors++; - } + g_assert_true (soup_cookie_get_http_only (cookie)); + g_assert_true (soup_cookie_get_expires (cookie) != NULL); } else if (!strcmp (soup_cookie_get_name (cookie), "three")) { got3 = TRUE; - if (!soup_cookie_get_http_only (cookie)) { - debug_printf (1, " cookie 3 is not HttpOnly!\n"); - errors++; - } - if (!soup_cookie_get_expires (cookie)) { - debug_printf (1, " cookie 3 did not fully parse!\n"); - errors++; - } + g_assert_true (soup_cookie_get_http_only (cookie)); + g_assert_true (soup_cookie_get_expires (cookie) != NULL); } else { - debug_printf (1, " got unexpected cookie '%s'\n", - soup_cookie_get_name (cookie)); - errors++; + soup_test_assert (FALSE, "got unexpected cookie '%s'", + soup_cookie_get_name (cookie)); } soup_cookie_free (cookie); } g_slist_free (cookies); - if (!got1) { - debug_printf (1, " didn't get cookie 1\n"); - errors++; - } - if (!got2) { - debug_printf (1, " didn't get cookie 2\n"); - errors++; - } - if (!got3) { - debug_printf (1, " didn't get cookie 3\n"); - errors++; - } + g_assert_true (got1); + g_assert_true (got2); + g_assert_true (got3); soup_test_session_abort_unref (session); } @@ -203,6 +167,8 @@ do_cookies_parsing_test (void) int main (int argc, char **argv) { + int ret; + test_init (argc, argv, NULL); server = soup_test_server_new (TRUE); @@ -212,14 +178,15 @@ main (int argc, char **argv) soup_uri_set_port (first_party_uri, soup_server_get_port (server)); soup_uri_set_port (third_party_uri, soup_server_get_port (server)); - do_cookies_accept_policy_test (); - do_cookies_parsing_test (); + g_test_add_func ("/cookies/accept-policy", do_cookies_accept_policy_test); + g_test_add_func ("/cookies/parsing", do_cookies_parsing_test); + + ret = g_test_run (); soup_uri_free (first_party_uri); soup_uri_free (third_party_uri); soup_test_server_quit_unref (server); test_cleanup (); - - return errors != 0; + return ret; } diff --git a/tests/date.c b/tests/date.c index 54a59f2b..e51fa57f 100644 --- a/tests/date.c +++ b/tests/date.c @@ -5,7 +5,7 @@ #include "test-utils.h" -static gboolean check_ok (const char *strdate, SoupDate *date); +static void check_ok (gconstpointer data); static SoupDate * make_date (const char *strdate) @@ -24,178 +24,226 @@ make_date (const char *strdate) return date; } -static const struct { +static SoupDate * +check_correct_date (const char *strdate) +{ + SoupDate *date; + + date = make_date (strdate); + if (!date) { + g_assert_nonnull (date); + return NULL; + } + + g_assert_cmpint (date->year, ==, 2004); + g_assert_cmpint (date->month, ==, 11); + g_assert_cmpint (date->day, ==, 6); + g_assert_cmpint (date->hour, ==, 8); + g_assert_cmpint (date->minute, ==, 9); + g_assert_cmpint (date->second, ==, 7); + + return date; +} + +typedef struct { SoupDateFormat format; const char *date; -} good_dates[] = { - { SOUP_DATE_HTTP, "Sat, 06 Nov 2004 08:09:07 GMT" }, - { SOUP_DATE_COOKIE, "Sat, 06-Nov-2004 08:09:07 GMT" }, - { SOUP_DATE_RFC2822, "Sat, 6 Nov 2004 08:09:07 -0430" }, - { SOUP_DATE_ISO8601_COMPACT, "20041106T080907" }, - { SOUP_DATE_ISO8601_FULL, "2004-11-06T08:09:07" }, - { SOUP_DATE_ISO8601_XMLRPC, "20041106T08:09:07" } + const char *bugref; +} GoodDate; + +static const GoodDate good_dates[] = { + { SOUP_DATE_HTTP, "Sat, 06 Nov 2004 08:09:07 GMT", NULL }, + { SOUP_DATE_COOKIE, "Sat, 06-Nov-2004 08:09:07 GMT", NULL }, + { SOUP_DATE_RFC2822, "Sat, 6 Nov 2004 08:09:07 -0430", "579055" }, + { SOUP_DATE_ISO8601_COMPACT, "20041106T080907", NULL }, + { SOUP_DATE_ISO8601_FULL, "2004-11-06T08:09:07", NULL }, + { SOUP_DATE_ISO8601_XMLRPC, "20041106T08:09:07", NULL } }; static void -check_good (SoupDateFormat format, const char *strdate) +check_good (gconstpointer data) { + GoodDate *good = (GoodDate *)data; SoupDate *date; char *strdate2; - date = make_date (strdate); - g_assert (date); - strdate2 = soup_date_to_string (date, format); - if (!check_ok (strdate, date)) + if (good->bugref) + g_test_bug (good->bugref); + + date = check_correct_date (good->date); + if (!date) return; - if (strcmp (strdate, strdate2) != 0) { - debug_printf (1, " restringification failed: '%s' -> '%s'\n", - strdate, strdate2); - errors++; - } + strdate2 = soup_date_to_string (date, good->format); + soup_date_free (date); + + soup_test_assert (strcmp (good->date, strdate2) == 0, + "restringification failed: '%s' -> '%s'\n", + good->date, strdate2); g_free (strdate2); } -static const char *ok_dates[] = { +typedef struct { + const char *date; + const char *bugref; +} OkDate; + +static const OkDate ok_dates[] = { /* rfc1123-date, and broken variants */ - "Sat, 06 Nov 2004 08:09:07 GMT", - "Sat, 6 Nov 2004 08:09:07 GMT", - "Sat, 6 Nov 2004 08:09:07 GMT", - "Sat, 06 Nov 2004 08:09:07", - "06 Nov 2004 08:09:07 GMT", - "SAT, 06 NOV 2004 08:09:07 +1000", + { "Sat, 06 Nov 2004 08:09:07 GMT", NULL }, + { "Sat, 6 Nov 2004 08:09:07 GMT", NULL }, + { "Sat, 6 Nov 2004 08:09:07 GMT", NULL }, + { "Sat, 06 Nov 2004 08:09:07", NULL }, + { "06 Nov 2004 08:09:07 GMT", NULL }, + { "SAT, 06 NOV 2004 08:09:07 +1000", "644048" }, /* rfc850-date, and broken variants */ - "Saturday, 06-Nov-04 08:09:07 GMT", - "Saturday, 6-Nov-04 08:09:07 GMT", - "Saturday, 6-Nov-04 08:09:07 GMT", - "Saturday, 06-Nov-104 08:09:07 GMT", - "Saturday, 06-Nov-04 08:09:07", - "06-Nov-04 08:09:07 GMT", + { "Saturday, 06-Nov-04 08:09:07 GMT", NULL }, + { "Saturday, 6-Nov-04 08:09:07 GMT", NULL }, + { "Saturday, 6-Nov-04 08:09:07 GMT", NULL }, + { "Saturday, 06-Nov-104 08:09:07 GMT", NULL }, + { "Saturday, 06-Nov-04 08:09:07", NULL }, + { "06-Nov-04 08:09:07 GMT", NULL }, /* asctime-date, and broken variants */ - "Sat Nov 6 08:09:07 2004", - "Sat Nov 06 08:09:07 2004", - "Sat Nov 6 08:09:07 2004", - "Sat Nov 6 08:09:07 2004 GMT", + { "Sat Nov 6 08:09:07 2004", NULL }, + { "Sat Nov 06 08:09:07 2004", NULL }, + { "Sat Nov 6 08:09:07 2004", NULL }, + { "Sat Nov 6 08:09:07 2004 GMT", NULL }, /* ISO 8601 */ - "2004-11-06T08:09:07Z", - "20041106T08:09:07Z", - "20041106T08:09:07+00:00", - "20041106T080907+00:00", + { "2004-11-06T08:09:07Z", NULL }, + { "20041106T08:09:07Z", NULL }, + { "20041106T08:09:07+00:00", NULL }, + { "20041106T080907+00:00", NULL }, /* Netscape cookie spec date, and broken variants */ - "Sat, 06-Nov-2004 08:09:07 GMT", - "Sat, 6-Nov-2004 08:09:07 GMT", - "Sat, 6-Nov-2004 08:09:07 GMT", - "Sat, 06-Nov-2004 08:09:07", + { "Sat, 06-Nov-2004 08:09:07 GMT", NULL }, + { "Sat, 6-Nov-2004 08:09:07 GMT", NULL }, + { "Sat, 6-Nov-2004 08:09:07 GMT", NULL }, + { "Sat, 06-Nov-2004 08:09:07", NULL }, /* Original version of Netscape cookie spec, and broken variants */ - "Sat, 06-Nov-04 08:09:07 GMT", - "Sat, 6-Nov-04 08:09:07 GMT", - "Sat, 6-Nov-04 08:09:07 GMT", - "Sat, 06-Nov-104 08:09:07 GMT", - "Sat, 06-Nov-04 08:09:07", + { "Sat, 06-Nov-04 08:09:07 GMT", NULL }, + { "Sat, 6-Nov-04 08:09:07 GMT", NULL }, + { "Sat, 6-Nov-04 08:09:07 GMT", NULL }, + { "Sat, 06-Nov-104 08:09:07 GMT", NULL }, + { "Sat, 06-Nov-04 08:09:07", NULL }, /* Netscape cookie spec example syntax, and broken variants */ - "Saturday, 06-Nov-04 08:09:07 GMT", - "Saturday, 6-Nov-04 08:09:07 GMT", - "Saturday, 6-Nov-04 08:09:07 GMT", - "Saturday, 06-Nov-104 08:09:07 GMT", - "Saturday, 06-Nov-2004 08:09:07 GMT", - "Saturday, 6-Nov-2004 08:09:07 GMT", - "Saturday, 6-Nov-2004 08:09:07 GMT", - "Saturday, 06-Nov-04 08:09:07", + { "Saturday, 06-Nov-04 08:09:07 GMT", NULL }, + { "Saturday, 6-Nov-04 08:09:07 GMT", NULL }, + { "Saturday, 6-Nov-04 08:09:07 GMT", NULL }, + { "Saturday, 06-Nov-104 08:09:07 GMT", NULL }, + { "Saturday, 06-Nov-2004 08:09:07 GMT", NULL }, + { "Saturday, 6-Nov-2004 08:09:07 GMT", NULL }, + { "Saturday, 6-Nov-2004 08:09:07 GMT", NULL }, + { "Saturday, 06-Nov-04 08:09:07", NULL }, /* Miscellaneous broken formats seen on the web */ - "Sat 06-Nov-2004 08:9:07", - "Saturday, 06-Nov-04 8:9:07 GMT", - "Sat, 06 Nov 2004 08:09:7 GMT" + { "Sat 06-Nov-2004 08:9:07", NULL }, + { "Saturday, 06-Nov-04 8:9:07 GMT", NULL }, + { "Sat, 06 Nov 2004 08:09:7 GMT", NULL } }; +static void +check_ok (gconstpointer data) +{ + OkDate *ok = (OkDate *)data; + SoupDate *date; + + if (ok->bugref) + g_test_bug (ok->bugref); + + date = check_correct_date (ok->date); + if (!date) + return; + soup_date_free (date); +} + #define TIME_T 1099728547L #define TIME_T_STRING "1099728547" -static gboolean -check_ok (const char *strdate, SoupDate *date) +static void +check_ok_time_t (void) { - debug_printf (2, "%s\n", strdate); + SoupDate *date; - if (date && - date->year == 2004 && date->month == 11 && date->day == 6 && - date->hour == 8 && date->minute == 9 && date->second == 7) { - soup_date_free (date); - return TRUE; - } + date = soup_date_new_from_time_t (TIME_T); - debug_printf (1, " date parsing failed for '%s'.\n", strdate); - if (date) { - debug_printf (1, " got: %d %d %d - %d %d %d\n\n", - date->year, date->month, date->day, - date->hour, date->minute, date->second); - soup_date_free (date); - } - errors++; - return FALSE; + g_assert_cmpint (date->year, ==, 2004); + g_assert_cmpint (date->month, ==, 11); + g_assert_cmpint (date->day, ==, 6); + g_assert_cmpint (date->hour, ==, 8); + g_assert_cmpint (date->minute, ==, 9); + g_assert_cmpint (date->second, ==, 7); } -static const char *bad_dates[] = { +typedef struct { + const char *date; + const char *bugref; +} BadDate; + +static const BadDate bad_dates[] = { /* broken rfc1123-date */ - ", 06 Nov 2004 08:09:07 GMT", - "Sat, Nov 2004 08:09:07 GMT", - "Sat, 06 2004 08:09:07 GMT", - "Sat, 06 Nov 08:09:07 GMT", - "Sat, 06 Nov 2004 :09:07 GMT", - "Sat, 06 Nov 2004 09:07 GMT", - "Sat, 06 Nov 2004 08::07 GMT", - "Sat, 06 Nov 2004 08:09: GMT", + { ", 06 Nov 2004 08:09:07 GMT", NULL }, + { "Sat, Nov 2004 08:09:07 GMT", NULL }, + { "Sat, 06 2004 08:09:07 GMT", NULL }, + { "Sat, 06 Nov 08:09:07 GMT", NULL }, + { "Sat, 06 Nov 2004 :09:07 GMT", NULL }, + { "Sat, 06 Nov 2004 09:07 GMT", NULL }, + { "Sat, 06 Nov 2004 08::07 GMT", NULL }, + { "Sat, 06 Nov 2004 08:09: GMT", NULL }, /* broken rfc850-date */ - ", 06-Nov-04 08:09:07 GMT", - "Saturday, -Nov-04 08:09:07 GMT", - "Saturday, Nov-04 08:09:07 GMT", - "Saturday, 06-04 08:09:07 GMT", - "Saturday, 06--04 08:09:07 GMT", - "Saturday, 06-Nov- 08:09:07 GMT", - "Saturday, 06-Nov 08:09:07 GMT", - "Saturday, 06-Nov-04 :09:07 GMT", - "Saturday, 06-Nov-04 09:07 GMT", - "Saturday, 06-Nov-04 08::07 GMT", - "Saturday, 06-Nov-04 08:09: GMT", + { ", 06-Nov-04 08:09:07 GMT", NULL }, + { "Saturday, -Nov-04 08:09:07 GMT", NULL }, + { "Saturday, Nov-04 08:09:07 GMT", NULL }, + { "Saturday, 06-04 08:09:07 GMT", NULL }, + { "Saturday, 06--04 08:09:07 GMT", NULL }, + { "Saturday, 06-Nov- 08:09:07 GMT", NULL }, + { "Saturday, 06-Nov 08:09:07 GMT", NULL }, + { "Saturday, 06-Nov-04 :09:07 GMT", NULL }, + { "Saturday, 06-Nov-04 09:07 GMT", NULL }, + { "Saturday, 06-Nov-04 08::07 GMT", NULL }, + { "Saturday, 06-Nov-04 08:09: GMT", NULL }, /* broken asctime-date */ - "Nov 6 08:09:07 2004", - "Sat 6 08:09:07 2004", - "Sat Nov 08:09:07 2004", - "Sat Nov 6 :09:07 2004", - "Sat Nov 6 09:07 2004", - "Sat Nov 6 08::07 2004", - "Sat Nov 6 08:09: 2004", - "Sat Nov 6 08:09:07", - "Sat Nov 6 08:09:07 GMT 2004" + { "Nov 6 08:09:07 2004", NULL }, + { "Sat 6 08:09:07 2004", NULL }, + { "Sat Nov 08:09:07 2004", NULL }, + { "Sat Nov 6 :09:07 2004", NULL }, + { "Sat Nov 6 09:07 2004", NULL }, + { "Sat Nov 6 08::07 2004", NULL }, + { "Sat Nov 6 08:09: 2004", NULL }, + { "Sat Nov 6 08:09:07", NULL }, + { "Sat Nov 6 08:09:07 GMT 2004", NULL } }; static void -check_bad (const char *strdate, SoupDate *date) +check_bad (gconstpointer data) { - debug_printf (2, "%s\n", strdate); + BadDate *bad = (BadDate *)data; + SoupDate *date; - if (!date) - return; - errors++; + if (bad->bugref) + g_test_bug (bad->bugref); - debug_printf (1, " date parsing succeeded for '%s'!\n", strdate); - debug_printf (1, " got: %d %d %d - %d %d %d\n\n", - date->year, date->month, date->day, - date->hour, date->minute, date->second); - soup_date_free (date); + date = make_date (bad->date); + soup_test_assert (date == NULL, + "date parsing succeeded for '%s': %d %d %d - %d %d %d", + bad->date, + date->year, date->month, date->day, + date->hour, date->minute, date->second); + g_clear_pointer (&date, soup_date_free); } -static const struct conversion { +typedef struct { const char *source; const char *http, *cookie, *rfc2822, *compact, *full, *xmlrpc; -} conversions[] = { +} DateConversion; + +static const DateConversion conversions[] = { /* SOUP_DATE_HTTP */ { "Sat, 06 Nov 2004 08:09:07 GMT", @@ -288,71 +336,40 @@ static const struct conversion { }; static void -check_conversion (const struct conversion *conv) +check_conversion (gconstpointer data) { + const DateConversion *conv = data; SoupDate *date; char *str; - debug_printf (2, "%s\n", conv->source); date = make_date (conv->source); if (!date) { - debug_printf (1, " date parsing failed for '%s'.\n", conv->source); - errors++; + soup_test_assert (FALSE, "date parsing failed for '%s'.", conv->source); return; } str = soup_date_to_string (date, SOUP_DATE_HTTP); - if (!str || strcmp (str, conv->http) != 0) { - debug_printf (1, " conversion of '%s' to HTTP failed:\n" - " wanted: %s\n got: %s\n", - conv->source, conv->http, str ? str : "(null)"); - errors++; - } + g_assert_cmpstr (str, ==, conv->http); g_free (str); str = soup_date_to_string (date, SOUP_DATE_COOKIE); - if (!str || strcmp (str, conv->cookie) != 0) { - debug_printf (1, " conversion of '%s' to COOKIE failed:\n" - " wanted: %s\n got: %s\n", - conv->source, conv->cookie, str ? str : "(null)"); - errors++; - } + g_assert_cmpstr (str, ==, conv->cookie); g_free (str); str = soup_date_to_string (date, SOUP_DATE_RFC2822); - if (!str || strcmp (str, conv->rfc2822) != 0) { - debug_printf (1, " conversion of '%s' to RFC2822 failed:\n" - " wanted: %s\n got: %s\n", - conv->source, conv->rfc2822, str ? str : "(null)"); - errors++; - } + g_assert_cmpstr (str, ==, conv->rfc2822); g_free (str); str = soup_date_to_string (date, SOUP_DATE_ISO8601_COMPACT); - if (!str || strcmp (str, conv->compact) != 0) { - debug_printf (1, " conversion of '%s' to COMPACT failed:\n" - " wanted: %s\n got: %s\n", - conv->source, conv->compact, str ? str : "(null)"); - errors++; - } + g_assert_cmpstr (str, ==, conv->compact); g_free (str); str = soup_date_to_string (date, SOUP_DATE_ISO8601_FULL); - if (!str || strcmp (str, conv->full) != 0) { - debug_printf (1, " conversion of '%s' to FULL failed:\n" - " wanted: %s\n got: %s\n", - conv->source, conv->full, str ? str : "(null)"); - errors++; - } + g_assert_cmpstr (str, ==, conv->full); g_free (str); str = soup_date_to_string (date, SOUP_DATE_ISO8601_XMLRPC); - if (!str || strcmp (str, conv->xmlrpc) != 0) { - debug_printf (1, " conversion of '%s' to XMLRPC failed:\n" - " wanted: %s\n got: %s\n", - conv->source, conv->xmlrpc, str ? str : "(null)"); - errors++; - } + g_assert_cmpstr (str, ==, conv->xmlrpc); g_free (str); soup_date_free (date); @@ -361,27 +378,38 @@ check_conversion (const struct conversion *conv) int main (int argc, char **argv) { - int i; + int i, ret; + char *path; test_init (argc, argv, NULL); - debug_printf (1, "Good dates:\n"); - for (i = 0; i < G_N_ELEMENTS (good_dates); i++) - check_good (good_dates[i].format, good_dates[i].date); + for (i = 0; i < G_N_ELEMENTS (good_dates); i++) { + path = g_strdup_printf ("/date/good/%s", good_dates[i].date); + g_test_add_data_func (path, &good_dates[i], check_good); + g_free (path); + } + + for (i = 0; i < G_N_ELEMENTS (ok_dates); i++) { + path = g_strdup_printf ("/date/ok/%s", ok_dates[i].date); + g_test_add_data_func (path, &ok_dates[i], check_ok); + g_free (path); + } + g_test_add_func ("/date/ok/" TIME_T_STRING, check_ok_time_t); - debug_printf (1, "\nOK dates:\n"); - for (i = 0; i < G_N_ELEMENTS (ok_dates); i++) - check_ok (ok_dates[i], make_date (ok_dates[i])); - check_ok (TIME_T_STRING, soup_date_new_from_time_t (TIME_T)); + for (i = 0; i < G_N_ELEMENTS (bad_dates); i++) { + path = g_strdup_printf ("/date/bad/%s", bad_dates[i].date); + g_test_add_data_func (path, &bad_dates[i], check_bad); + g_free (path); + } - debug_printf (1, "\nBad dates:\n"); - for (i = 0; i < G_N_ELEMENTS (bad_dates); i++) - check_bad (bad_dates[i], make_date (bad_dates[i])); + for (i = 0; i < G_N_ELEMENTS (conversions); i++) { + path = g_strdup_printf ("/date/conversions/%s", conversions[i].source); + g_test_add_data_func (path, &conversions[i], check_conversion); + g_free (path); + } - debug_printf (1, "\nConversions:\n"); - for (i = 0; i < G_N_ELEMENTS (conversions); i++) - check_conversion (&conversions[i] ); + ret = g_test_run (); test_cleanup (); - return errors != 0; + return ret; } diff --git a/tests/dns.c b/tests/dns.c deleted file mode 100644 index f5940647..00000000 --- a/tests/dns.c +++ /dev/null @@ -1,62 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ - -#include "test-utils.h" - -static GMainLoop *loop; -static int nlookups = 0; - -static void -resolve_callback (SoupAddress *addr, guint status, gpointer data) -{ - if (status == SOUP_STATUS_OK) { - g_print ("Name: %s\n", soup_address_get_name (addr)); - g_print ("Address: %s\n", soup_address_get_physical (addr)); - } else { - g_print ("Name: %s\n", soup_address_get_name (addr)); - g_print ("Error: %s\n", soup_status_get_phrase (status)); - } - g_print ("\n"); - - g_object_unref (addr); - - nlookups--; - if (nlookups == 0) - g_main_loop_quit (loop); -} - -static void -usage (void) -{ - g_printerr ("Usage: dns hostname ...\n"); - exit (1); -} - -int -main (int argc, char **argv) -{ - SoupAddress *addr; - int i; - - if (argc < 2) - usage (); - - g_type_init (); - - for (i = 1; i < argc; i++) { - addr = soup_address_new (argv[i], 0); - if (!addr) { - g_printerr ("Could not parse address %s\n", argv[1]); - exit (1); - } - - soup_address_resolve_async (addr, NULL, NULL, - resolve_callback, NULL); - nlookups++; - } - - loop = g_main_loop_new (NULL, TRUE); - g_main_loop_run (loop); - g_main_loop_unref (loop); - - return 0; -} diff --git a/tests/forms-test.c b/tests/forms-test.c index 1d254b12..3915b019 100644 --- a/tests/forms-test.c +++ b/tests/forms-test.c @@ -41,6 +41,7 @@ do_hello_test (int n, gboolean extra, const char *uri) GPtrArray *args; char *title_arg = NULL, *name_arg = NULL; char *str_stdout = NULL; + GError *error = NULL; debug_printf (1, "%2d. '%s' '%s'%s: ", n * 2 + (extra ? 2 : 1), tests[n].title ? tests[n].title : "(null)", @@ -49,6 +50,8 @@ do_hello_test (int n, gboolean extra, const char *uri) args = g_ptr_array_new (); g_ptr_array_add (args, "curl"); + g_ptr_array_add (args, "--noproxy"); + g_ptr_array_add (args, "*"); g_ptr_array_add (args, "-G"); if (tests[n].title) { title_arg = soup_form_encode ("title", tests[n].title, NULL); @@ -70,20 +73,12 @@ do_hello_test (int n, gboolean extra, const char *uri) if (g_spawn_sync (NULL, (char **)args->pdata, NULL, G_SPAWN_SEARCH_PATH | G_SPAWN_STDERR_TO_DEV_NULL, NULL, NULL, - &str_stdout, NULL, NULL, NULL)) { - if (str_stdout && !strcmp (str_stdout, tests[n].result)) - debug_printf (1, "OK!\n"); - else { - debug_printf (1, "WRONG!\n"); - debug_printf (1, " expected '%s', got '%s'\n", - tests[n].result, - str_stdout ? str_stdout : "(error)"); - errors++; - } + &str_stdout, NULL, NULL, &error)) { + g_assert_cmpstr (str_stdout, ==, tests[n].result); g_free (str_stdout); } else { - debug_printf (1, "ERROR!\n"); - errors++; + g_assert_no_error (error); + g_error_free (error); } g_ptr_array_free (args, TRUE); g_free (title_arg); @@ -91,30 +86,75 @@ do_hello_test (int n, gboolean extra, const char *uri) } static void -do_hello_tests (const char *uri) +do_hello_tests (gconstpointer uri) { int n; - debug_printf (1, "Hello tests (GET, application/x-www-form-urlencoded)\n"); +#ifndef HAVE_CURL + g_test_skip ("/usr/bin/curl is not available"); + return; +#endif + for (n = 0; n < G_N_ELEMENTS (tests); n++) { do_hello_test (n, FALSE, uri); do_hello_test (n, TRUE, uri); } } +#define MD5_TEST_FILE (g_test_get_filename (G_TEST_DIST, "index.txt", NULL)) +#define MD5_TEST_FILE_BASENAME "index.txt" +#define MD5_TEST_FILE_MIME_TYPE "text/plain" + +static char * +get_md5_data (char **contents, gsize *length) +{ + char *my_contents, *md5; + gsize my_length; + GError *error = NULL; + + if (!g_file_get_contents (MD5_TEST_FILE, &my_contents, &my_length, &error)) { + g_assert_no_error (error); + g_error_free (error); + return NULL; + } + + md5 = g_compute_checksum_for_string (G_CHECKSUM_MD5, my_contents, my_length); + + if (contents) + *contents = my_contents; + else + g_free (my_contents); + if (length) + *length = my_length; + + return md5; +} + static void -do_md5_test_curl (const char *uri, const char *file, const char *md5) +do_md5_test_curl (gconstpointer data) { + const char *uri = data; + char *md5; GPtrArray *args; char *file_arg, *str_stdout; + GError *error = NULL; - debug_printf (1, " via curl: "); +#ifndef HAVE_CURL + g_test_skip ("/usr/bin/curl is not available"); + return; +#endif + + md5 = get_md5_data (NULL, NULL); + if (!md5) + return; args = g_ptr_array_new (); g_ptr_array_add (args, "curl"); + g_ptr_array_add (args, "--noproxy"); + g_ptr_array_add (args, "*"); g_ptr_array_add (args, "-L"); g_ptr_array_add (args, "-F"); - file_arg = g_strdup_printf ("file=@%s", file); + file_arg = g_strdup_printf ("file=@%s", MD5_TEST_FILE); g_ptr_array_add (args, file_arg); g_ptr_array_add (args, "-F"); g_ptr_array_add (args, "fmt=txt"); @@ -125,37 +165,34 @@ do_md5_test_curl (const char *uri, const char *file, const char *md5) G_SPAWN_SEARCH_PATH | G_SPAWN_STDERR_TO_DEV_NULL, NULL, NULL, &str_stdout, NULL, NULL, NULL)) { - if (str_stdout && !strcmp (str_stdout, md5)) - debug_printf (1, "OK!\n"); - else { - debug_printf (1, "WRONG!\n"); - debug_printf (1, " expected '%s', got '%s'\n", - md5, str_stdout ? str_stdout : "(error)"); - errors++; - } + g_assert_cmpstr (str_stdout, ==, md5); g_free (str_stdout); } else { - debug_printf (1, "ERROR!\n"); - errors++; + g_assert_no_error (error); + g_error_free (error); } g_ptr_array_free (args, TRUE); g_free (file_arg); -} -#define MD5_TEST_FILE SRCDIR "/resources/home.gif" -#define MD5_TEST_FILE_BASENAME "home.gif" -#define MD5_TEST_FILE_MIME_TYPE "image/gif" + g_free (md5); +} static void -do_md5_test_libsoup (const char *uri, const char *contents, - gsize length, const char *md5) +do_md5_test_libsoup (gconstpointer data) { + const char *uri = data; + char *contents, *md5; + gsize length; SoupMultipart *multipart; SoupBuffer *buffer; SoupMessage *msg; SoupSession *session; - debug_printf (1, " via libsoup: "); + g_test_bug ("601640"); + + md5 = get_md5_data (&contents, &length); + if (!md5) + return; multipart = soup_multipart_new (SOUP_FORM_MIME_TYPE_MULTIPART); buffer = soup_buffer_new (SOUP_MEMORY_COPY, contents, length); @@ -172,47 +209,16 @@ do_md5_test_libsoup (const char *uri, const char *contents, session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); soup_session_send_message (session, msg); - if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) { - debug_printf (1, "ERROR: Unexpected status %d %s\n", - msg->status_code, msg->reason_phrase); - errors++; - } else if (strcmp (msg->response_body->data, md5) != 0) { - debug_printf (1, "ERROR: Incorrect response: expected '%s' got '%s'\n", - md5, msg->response_body->data); - errors++; - } else - debug_printf (1, "OK!\n"); + soup_test_assert_message_status (msg, SOUP_STATUS_OK); + g_assert_cmpstr (msg->response_body->data, ==, md5); g_object_unref (msg); soup_test_session_abort_unref (session); -} - -static void -do_md5_tests (const char *uri) -{ - char *contents, *md5; - gsize length; - GError *error = NULL; - - debug_printf (1, "\nMD5 tests (POST, multipart/form-data)\n"); - - if (!g_file_get_contents (MD5_TEST_FILE, &contents, &length, &error)) { - debug_printf (1, " ERROR: Could not read " MD5_TEST_FILE ": %s\n", error->message); - g_error_free (error); - errors++; - return; - } - - md5 = g_compute_checksum_for_string (G_CHECKSUM_MD5, contents, length); - - do_md5_test_curl (uri, MD5_TEST_FILE, md5); - do_md5_test_libsoup (uri, contents, length, md5); g_free (contents); g_free (md5); } - static void do_form_decode_test (void) { @@ -220,7 +226,10 @@ do_form_decode_test (void) const gchar *value; gchar *tmp; - debug_printf (1, "\nDecode tests\n"); +#ifndef HAVE_CURL + g_test_skip ("/usr/bin/curl is not available"); + return; +#endif /* Test that the code handles multiple values with the same key. */ table = soup_form_decode ("foo=first&foo=second&foo=third"); @@ -232,11 +241,7 @@ do_form_decode_test (void) tmp = g_strdup ("other"); value = g_hash_table_lookup (table, "foo"); - if (g_strcmp0 (value, "third") != 0) { - debug_printf (1, " ERROR: expected '%s', got '%s'\n", - "third", value ? value : "(null)"); - errors++; - } + g_assert_cmpstr (value, ==, "third"); g_free (tmp); g_hash_table_destroy (table); @@ -417,6 +422,7 @@ main (int argc, char **argv) SoupServer *server; guint port; char *uri_str; + int ret = 0; test_init (argc, argv, no_test_entry); @@ -431,14 +437,16 @@ main (int argc, char **argv) if (run_tests) { uri_str = g_strdup_printf ("http://127.0.0.1:%u/hello", port); - do_hello_tests (uri_str); - g_free (uri_str); + g_test_add_data_func_full ("/forms/hello", uri_str, do_hello_tests, g_free); uri_str = g_strdup_printf ("http://127.0.0.1:%u/md5", port); - do_md5_tests (uri_str); + g_test_add_data_func_full ("/forms/md5/curl", g_strdup (uri_str), do_md5_test_curl, g_free); + g_test_add_data_func_full ("/forms/md5/libsoup", g_strdup (uri_str), do_md5_test_libsoup, g_free); g_free (uri_str); - do_form_decode_test (); + g_test_add_func ("/forms/decode", do_form_decode_test); + + ret = g_test_run (); } else { g_print ("Listening on port %d\n", port); g_main_loop_run (loop); @@ -449,5 +457,5 @@ main (int argc, char **argv) soup_test_server_quit_unref (server); if (run_tests) test_cleanup (); - return errors != 0; + return ret; } diff --git a/tests/get.c b/tests/get.c deleted file mode 100644 index 1d867155..00000000 --- a/tests/get.c +++ /dev/null @@ -1,189 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Copyright (C) 2001-2003, Ximian, Inc. - */ - -#include "test-utils.h" -#include <stdio.h> - -static SoupSession *session; -static GMainLoop *loop; -static gboolean debug = FALSE, quiet = FALSE; -static const char *method; - -static void -get_url (const char *url) -{ - const char *name; - SoupMessage *msg; - const char *header; - - msg = soup_message_new (method, url); - soup_message_set_flags (msg, SOUP_MESSAGE_NO_REDIRECT); - - soup_session_send_message (session, msg); - - name = soup_message_get_uri (msg)->path; - - if (debug) { - SoupMessageHeadersIter iter; - const char *hname, *value; - char *path = soup_uri_to_string (soup_message_get_uri (msg), TRUE); - - g_print ("%s %s HTTP/1.%d\n", method, path, - soup_message_get_http_version (msg)); - soup_message_headers_iter_init (&iter, msg->request_headers); - while (soup_message_headers_iter_next (&iter, &hname, &value)) - g_print ("%s: %s\r\n", hname, value); - g_print ("\n"); - - g_print ("HTTP/1.%d %d %s\n", - soup_message_get_http_version (msg), - msg->status_code, msg->reason_phrase); - - soup_message_headers_iter_init (&iter, msg->response_headers); - while (soup_message_headers_iter_next (&iter, &hname, &value)) - g_print ("%s: %s\r\n", hname, value); - g_print ("\n"); - } else if (msg->status_code == SOUP_STATUS_SSL_FAILED) { - GTlsCertificateFlags flags; - - if (soup_message_get_https_status (msg, NULL, &flags)) - g_print ("%s: %d %s (0x%x)\n", name, msg->status_code, msg->reason_phrase, flags); - else - g_print ("%s: %d %s (no handshake status)\n", name, msg->status_code, msg->reason_phrase); - } else if (!quiet || SOUP_STATUS_IS_TRANSPORT_ERROR (msg->status_code)) - g_print ("%s: %d %s\n", name, msg->status_code, msg->reason_phrase); - - if (SOUP_STATUS_IS_REDIRECTION (msg->status_code)) { - header = soup_message_headers_get_one (msg->response_headers, - "Location"); - if (header) { - SoupURI *uri; - char *uri_string; - - if (!debug && !quiet) - g_print (" -> %s\n", header); - - uri = soup_uri_new_with_base (soup_message_get_uri (msg), header); - uri_string = soup_uri_to_string (uri, FALSE); - get_url (uri_string); - g_free (uri_string); - soup_uri_free (uri); - } - } else if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) { - fwrite (msg->response_body->data, 1, - msg->response_body->length, stdout); - } -} - -static void -usage (void) -{ - g_printerr ("Usage: get [-c CAfile] [-p proxy URL] [-h] [-d] URL\n"); - exit (1); -} - -int -main (int argc, char **argv) -{ - const char *cafile = NULL, *url; - SoupURI *proxy = NULL, *parsed; - gboolean synchronous = FALSE, ntlm = FALSE; - int opt; - - g_type_init (); - - method = SOUP_METHOD_GET; - - while ((opt = getopt (argc, argv, "c:dhnp:qs")) != -1) { - switch (opt) { - case 'c': - cafile = optarg; - break; - - case 'd': - debug = TRUE; - break; - - case 'h': - method = SOUP_METHOD_HEAD; - debug = TRUE; - break; - - case 'n': - ntlm = TRUE; - break; - - case 'p': - proxy = soup_uri_new (optarg); - if (!proxy) { - g_printerr ("Could not parse %s as URI\n", - optarg); - exit (1); - } - break; - - case 'q': - quiet = TRUE; - break; - - case 's': - synchronous = TRUE; - break; - - case '?': - usage (); - break; - } - } - argc -= optind; - argv += optind; - - if (argc != 1) - usage (); - url = argv[0]; - parsed = soup_uri_new (url); - if (!parsed) { - g_printerr ("Could not parse '%s' as a URL\n", url); - exit (1); - } - soup_uri_free (parsed); - - if (synchronous) { - session = soup_session_sync_new_with_options ( - SOUP_SESSION_SSL_CA_FILE, cafile, - SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_CONTENT_DECODER, - SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_COOKIE_JAR, - SOUP_SESSION_USER_AGENT, "get ", - SOUP_SESSION_ACCEPT_LANGUAGE_AUTO, TRUE, - SOUP_SESSION_USE_NTLM, ntlm, - NULL); - } else { - session = soup_session_async_new_with_options ( - SOUP_SESSION_SSL_CA_FILE, cafile, - SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_CONTENT_DECODER, - SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_COOKIE_JAR, - SOUP_SESSION_USER_AGENT, "get ", - SOUP_SESSION_ACCEPT_LANGUAGE_AUTO, TRUE, - SOUP_SESSION_USE_NTLM, ntlm, - NULL); - } - - if (proxy) { - g_object_set (G_OBJECT (session), - SOUP_SESSION_PROXY_URI, proxy, - NULL); - } else - soup_session_add_feature_by_type (session, SOUP_TYPE_PROXY_RESOLVER_DEFAULT); - - if (!synchronous) - loop = g_main_loop_new (NULL, TRUE); - - get_url (url); - - if (!synchronous) - g_main_loop_unref (loop); - - return 0; -} diff --git a/tests/header-parsing.c b/tests/header-parsing.c index e57d7d83..fdc7885d 100644 --- a/tests/header-parsing.c +++ b/tests/header-parsing.c @@ -8,6 +8,7 @@ typedef struct { static struct RequestTest { const char *description; + const char *bugref; const char *request; int length; guint status; @@ -19,14 +20,14 @@ static struct RequestTest { /*** VALID REQUESTS ***/ /**********************/ - { "HTTP 1.0 request with no headers", + { "HTTP 1.0 request with no headers", NULL, "GET / HTTP/1.0\r\n", -1, SOUP_STATUS_OK, "GET", "/", SOUP_HTTP_1_0, { { NULL } } }, - { "Req w/ 1 header", + { "Req w/ 1 header", NULL, "GET / HTTP/1.1\r\nHost: example.com\r\n", -1, SOUP_STATUS_OK, "GET", "/", SOUP_HTTP_1_1, @@ -35,7 +36,7 @@ static struct RequestTest { } }, - { "Req w/ 1 header, no leading whitespace", + { "Req w/ 1 header, no leading whitespace", NULL, "GET / HTTP/1.1\r\nHost:example.com\r\n", -1, SOUP_STATUS_OK, "GET", "/", SOUP_HTTP_1_1, @@ -44,7 +45,7 @@ static struct RequestTest { } }, - { "Req w/ 1 header including trailing whitespace", + { "Req w/ 1 header including trailing whitespace", NULL, "GET / HTTP/1.1\r\nHost: example.com \r\n", -1, SOUP_STATUS_OK, "GET", "/", SOUP_HTTP_1_1, @@ -53,7 +54,7 @@ static struct RequestTest { } }, - { "Req w/ 1 header, wrapped", + { "Req w/ 1 header, wrapped", NULL, "GET / HTTP/1.1\r\nFoo: bar\r\n baz\r\n", -1, SOUP_STATUS_OK, "GET", "/", SOUP_HTTP_1_1, @@ -62,7 +63,7 @@ static struct RequestTest { } }, - { "Req w/ 1 header, wrapped with additional whitespace", + { "Req w/ 1 header, wrapped with additional whitespace", NULL, "GET / HTTP/1.1\r\nFoo: bar \r\n baz\r\n", -1, SOUP_STATUS_OK, "GET", "/", SOUP_HTTP_1_1, @@ -71,7 +72,7 @@ static struct RequestTest { } }, - { "Req w/ 1 header, wrapped with tab", + { "Req w/ 1 header, wrapped with tab", NULL, "GET / HTTP/1.1\r\nFoo: bar\r\n\tbaz\r\n", -1, SOUP_STATUS_OK, "GET", "/", SOUP_HTTP_1_1, @@ -80,7 +81,7 @@ static struct RequestTest { } }, - { "Req w/ 1 header, wrapped before value", + { "Req w/ 1 header, wrapped before value", NULL, "GET / HTTP/1.1\r\nFoo:\r\n bar baz\r\n", -1, SOUP_STATUS_OK, "GET", "/", SOUP_HTTP_1_1, @@ -89,7 +90,7 @@ static struct RequestTest { } }, - { "Req w/ 1 header with empty value", + { "Req w/ 1 header with empty value", NULL, "GET / HTTP/1.1\r\nHost:\r\n", -1, SOUP_STATUS_OK, "GET", "/", SOUP_HTTP_1_1, @@ -98,7 +99,7 @@ static struct RequestTest { } }, - { "Req w/ 2 headers", + { "Req w/ 2 headers", NULL, "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n", -1, SOUP_STATUS_OK, "GET", "/", SOUP_HTTP_1_1, @@ -108,7 +109,7 @@ static struct RequestTest { } }, - { "Req w/ 3 headers", + { "Req w/ 3 headers", NULL, "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\nBlah: blah\r\n", -1, SOUP_STATUS_OK, "GET", "/", SOUP_HTTP_1_1, @@ -119,7 +120,7 @@ static struct RequestTest { } }, - { "Req w/ 3 headers, 1st wrapped", + { "Req w/ 3 headers, 1st wrapped", NULL, "GET / HTTP/1.1\r\nFoo: bar\r\n baz\r\nConnection: close\r\nBlah: blah\r\n", -1, SOUP_STATUS_OK, "GET", "/", SOUP_HTTP_1_1, @@ -130,7 +131,7 @@ static struct RequestTest { } }, - { "Req w/ 3 headers, 2nd wrapped", + { "Req w/ 3 headers, 2nd wrapped", NULL, "GET / HTTP/1.1\r\nConnection: close\r\nBlah: blah\r\nFoo: bar\r\n baz\r\n", -1, SOUP_STATUS_OK, "GET", "/", SOUP_HTTP_1_1, @@ -141,7 +142,7 @@ static struct RequestTest { } }, - { "Req w/ 3 headers, 3rd wrapped", + { "Req w/ 3 headers, 3rd wrapped", NULL, "GET / HTTP/1.1\r\nConnection: close\r\nBlah: blah\r\nFoo: bar\r\n baz\r\n", -1, SOUP_STATUS_OK, "GET", "/", SOUP_HTTP_1_1, @@ -152,7 +153,7 @@ static struct RequestTest { } }, - { "Req w/ same header multiple times", + { "Req w/ same header multiple times", NULL, "GET / HTTP/1.1\r\nFoo: bar\r\nFoo: baz\r\nFoo: quux\r\n", -1, SOUP_STATUS_OK, "GET", "/", SOUP_HTTP_1_1, @@ -161,7 +162,7 @@ static struct RequestTest { } }, - { "Connection header on HTTP/1.0 message", + { "Connection header on HTTP/1.0 message", NULL, "GET / HTTP/1.0\r\nFoo: bar\r\nConnection: Bar, Quux\r\nBar: baz\r\nQuux: foo\r\n", -1, SOUP_STATUS_OK, "GET", "/", SOUP_HTTP_1_0, @@ -171,14 +172,14 @@ static struct RequestTest { } }, - { "GET with full URI", + { "GET with full URI", "667637", "GET http://example.com HTTP/1.1\r\n", -1, SOUP_STATUS_OK, "GET", "http://example.com", SOUP_HTTP_1_1, { { NULL } } }, - { "GET with full URI in upper-case", + { "GET with full URI in upper-case", "667637", "GET HTTP://example.com HTTP/1.1\r\n", -1, SOUP_STATUS_OK, "GET", "HTTP://example.com", SOUP_HTTP_1_1, @@ -188,7 +189,7 @@ static struct RequestTest { /* It's better for this to be passed through: this means a SoupServer * could implement ftp-over-http proxying, for instance */ - { "GET with full URI of unrecognised scheme", + { "GET with full URI of unrecognised scheme", "667637", "GET AbOuT: HTTP/1.1\r\n", -1, SOUP_STATUS_OK, "GET", "AbOuT:", SOUP_HTTP_1_1, @@ -201,7 +202,7 @@ static struct RequestTest { /* RFC 2616 section 4.1 says we SHOULD accept this */ - { "Spurious leading CRLF", + { "Spurious leading CRLF", NULL, "\r\nGET / HTTP/1.1\r\nHost: example.com\r\n", -1, SOUP_STATUS_OK, "GET", "/", SOUP_HTTP_1_1, @@ -212,7 +213,7 @@ static struct RequestTest { /* RFC 2616 section 3.1 says we MUST accept this */ - { "HTTP/01.01 request", + { "HTTP/01.01 request", NULL, "GET / HTTP/01.01\r\nHost: example.com\r\n", -1, SOUP_STATUS_OK, "GET", "/", SOUP_HTTP_1_1, @@ -223,7 +224,7 @@ static struct RequestTest { /* RFC 2616 section 19.3 says we SHOULD accept these */ - { "LF instead of CRLF after header", + { "LF instead of CRLF after header", NULL, "GET / HTTP/1.1\r\nHost: example.com\nConnection: close\n", -1, SOUP_STATUS_OK, "GET", "/", SOUP_HTTP_1_1, @@ -233,7 +234,7 @@ static struct RequestTest { } }, - { "LF instead of CRLF after Request-Line", + { "LF instead of CRLF after Request-Line", NULL, "GET / HTTP/1.1\nHost: example.com\r\n", -1, SOUP_STATUS_OK, "GET", "/", SOUP_HTTP_1_1, @@ -242,7 +243,7 @@ static struct RequestTest { } }, - { "Mixed CRLF/LF", + { "Mixed CRLF/LF", "666316", "GET / HTTP/1.1\r\na: b\r\nc: d\ne: f\r\ng: h\n", -1, SOUP_STATUS_OK, "GET", "/", SOUP_HTTP_1_1, @@ -254,7 +255,7 @@ static struct RequestTest { } }, - { "Req w/ incorrect whitespace in Request-Line", + { "Req w/ incorrect whitespace in Request-Line", NULL, "GET /\tHTTP/1.1\r\nHost: example.com\r\n", -1, SOUP_STATUS_OK, "GET", "/", SOUP_HTTP_1_1, @@ -263,7 +264,7 @@ static struct RequestTest { } }, - { "Req w/ incorrect whitespace after Request-Line", + { "Req w/ incorrect whitespace after Request-Line", "475169", "GET / HTTP/1.1 \r\nHost: example.com\r\n", -1, SOUP_STATUS_OK, "GET", "/", SOUP_HTTP_1_1, @@ -274,10 +275,9 @@ static struct RequestTest { /* If the request/status line is parseable, then we * just ignore any invalid-looking headers after that. - * (qv bug 579318). */ - { "Req w/ mangled header", + { "Req w/ mangled header", "579318", "GET / HTTP/1.1\r\nHost: example.com\r\nFoo one\r\nBar: two\r\n", -1, SOUP_STATUS_OK, "GET", "/", SOUP_HTTP_1_1, @@ -287,7 +287,7 @@ static struct RequestTest { } }, - { "First header line is continuation", + { "First header line is continuation", "666316", "GET / HTTP/1.1\r\n b\r\nHost: example.com\r\nc: d\r\n", -1, SOUP_STATUS_OK, "GET", "/", SOUP_HTTP_1_1, @@ -297,7 +297,7 @@ static struct RequestTest { } }, - { "Zero-length header name", + { "Zero-length header name", "666316", "GET / HTTP/1.1\r\na: b\r\n: example.com\r\nc: d\r\n", -1, SOUP_STATUS_OK, "GET", "/", SOUP_HTTP_1_1, @@ -307,7 +307,7 @@ static struct RequestTest { } }, - { "CR in header name", + { "CR in header name", "666316", "GET / HTTP/1.1\r\na: b\r\na\rb: cd\r\nx\r: y\r\n\rz: w\r\nc: d\r\n", -1, SOUP_STATUS_OK, "GET", "/", SOUP_HTTP_1_1, @@ -317,7 +317,7 @@ static struct RequestTest { } }, - { "CR in header value", + { "CR in header value", "666316", "GET / HTTP/1.1\r\na: b\r\nHost: example\rcom\r\np: \rq\r\ns: t\r\r\nc: d\r\n", -1, SOUP_STATUS_OK, "GET", "/", SOUP_HTTP_1_1, @@ -330,7 +330,7 @@ static struct RequestTest { } }, - { "Tab in header name", + { "Tab in header name", "666316", "GET / HTTP/1.1\r\na: b\r\na\tb: cd\r\nx\t: y\r\np: q\r\n\tz: w\r\nc: d\r\n", -1, SOUP_STATUS_OK, "GET", "/", SOUP_HTTP_1_1, @@ -345,7 +345,7 @@ static struct RequestTest { } }, - { "Tab in header value", + { "Tab in header value", "666316", "GET / HTTP/1.1\r\na: b\r\nab: c\td\r\nx: \ty\r\nz: w\t\r\nc: d\r\n", -1, SOUP_STATUS_OK, "GET", "/", SOUP_HTTP_1_1, @@ -362,84 +362,84 @@ static struct RequestTest { /*** INVALID REQUESTS ***/ /************************/ - { "HTTP 0.9 request; not supported", + { "HTTP 0.9 request; not supported", NULL, "GET /\r\n", -1, SOUP_STATUS_BAD_REQUEST, NULL, NULL, -1, { { NULL } } }, - { "HTTP 1.2 request (no such thing)", + { "HTTP 1.2 request (no such thing)", NULL, "GET / HTTP/1.2\r\n", -1, SOUP_STATUS_HTTP_VERSION_NOT_SUPPORTED, NULL, NULL, -1, { { NULL } } }, - { "HTTP 2000 request (no such thing)", + { "HTTP 2000 request (no such thing)", NULL, "GET / HTTP/2000.0\r\n", -1, SOUP_STATUS_HTTP_VERSION_NOT_SUPPORTED, NULL, NULL, -1, { { NULL } } }, - { "Non-HTTP request", + { "Non-HTTP request", NULL, "GET / SOUP/1.1\r\nHost: example.com\r\n", -1, SOUP_STATUS_BAD_REQUEST, NULL, NULL, -1, { { NULL } } }, - { "Junk after Request-Line", + { "Junk after Request-Line", NULL, "GET / HTTP/1.1 blah\r\nHost: example.com\r\n", -1, SOUP_STATUS_BAD_REQUEST, NULL, NULL, -1, { { NULL } } }, - { "NUL in Method", + { "NUL in Method", NULL, "G\x00T / HTTP/1.1\r\nHost: example.com\r\n", 37, SOUP_STATUS_BAD_REQUEST, NULL, NULL, -1, { { NULL } } }, - { "NUL at beginning of Method", + { "NUL at beginning of Method", "666316", "\x00 / HTTP/1.1\r\nHost: example.com\r\n", 35, SOUP_STATUS_BAD_REQUEST, NULL, NULL, -1, { { NULL } } }, - { "NUL in Path", + { "NUL in Path", NULL, "GET /\x00 HTTP/1.1\r\nHost: example.com\r\n", 38, SOUP_STATUS_BAD_REQUEST, NULL, NULL, -1, { { NULL } } }, - { "NUL in header name", + { "NUL in header name", "666316", "GET / HTTP/1.1\r\n\x00: silly\r\n", 37, SOUP_STATUS_BAD_REQUEST, NULL, NULL, -1, { { NULL } } }, - { "NUL in header value", + { "NUL in header value", NULL, "GET / HTTP/1.1\r\nHost: example\x00com\r\n", 37, SOUP_STATUS_BAD_REQUEST, NULL, NULL, -1, { { NULL } } }, - { "No terminating CRLF", + { "No terminating CRLF", NULL, "GET / HTTP/1.1\r\nHost: example.com", -1, SOUP_STATUS_BAD_REQUEST, NULL, NULL, -1, { { NULL } } }, - { "Unrecognized expectation", + { "Unrecognized expectation", NULL, "GET / HTTP/1.1\r\nHost: example.com\r\nExpect: the-impossible\r\n", -1, SOUP_STATUS_EXPECTATION_FAILED, NULL, NULL, -1, @@ -450,36 +450,37 @@ static const int num_reqtests = G_N_ELEMENTS (reqtests); static struct ResponseTest { const char *description; + const char *bugref; const char *response; int length; SoupHTTPVersion version; guint status_code; const char *reason_phrase; - Header headers[4]; + Header headers[10]; } resptests[] = { /***********************/ /*** VALID RESPONSES ***/ /***********************/ - { "HTTP 1.0 response w/ no headers", + { "HTTP 1.0 response w/ no headers", NULL, "HTTP/1.0 200 ok\r\n", -1, SOUP_HTTP_1_0, SOUP_STATUS_OK, "ok", { { NULL } } }, - { "HTTP 1.1 response w/ no headers", + { "HTTP 1.1 response w/ no headers", NULL, "HTTP/1.1 200 ok\r\n", -1, SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok", { { NULL } } }, - { "Response w/ multi-word Reason-Phrase", + { "Response w/ multi-word Reason-Phrase", NULL, "HTTP/1.1 400 bad request\r\n", -1, SOUP_HTTP_1_1, SOUP_STATUS_BAD_REQUEST, "bad request", { { NULL } } }, - { "Response w/ 1 header", + { "Response w/ 1 header", NULL, "HTTP/1.1 200 ok\r\nFoo: bar\r\n", -1, SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok", { { "Foo", "bar" }, @@ -487,7 +488,7 @@ static struct ResponseTest { } }, - { "Response w/ 2 headers", + { "Response w/ 2 headers", NULL, "HTTP/1.1 200 ok\r\nFoo: bar\r\nBaz: quux\r\n", -1, SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok", { { "Foo", "bar" }, @@ -496,7 +497,7 @@ static struct ResponseTest { } }, - { "Response w/ same header multiple times", + { "Response w/ same header multiple times", NULL, "HTTP/1.1 200 ok\r\nFoo: bar\r\nFoo: baz\r\nFoo: quux\r\n", -1, SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok", { { "Foo", "bar, baz, quux" }, @@ -504,7 +505,7 @@ static struct ResponseTest { } }, - { "Response w/ no reason phrase", + { "Response w/ no reason phrase", NULL, "HTTP/1.1 200 \r\nFoo: bar\r\n", -1, SOUP_HTTP_1_1, SOUP_STATUS_OK, "", { { "Foo", "bar" }, @@ -512,7 +513,7 @@ static struct ResponseTest { } }, - { "Connection header on HTTP/1.0 message", + { "Connection header on HTTP/1.0 message", NULL, "HTTP/1.0 200 ok\r\nFoo: bar\r\nConnection: Bar\r\nBar: quux\r\n", -1, SOUP_HTTP_1_0, SOUP_STATUS_OK, "ok", { { "Foo", "bar" }, @@ -521,13 +522,26 @@ static struct ResponseTest { } }, + /* Tests from Cockpit */ + + { "Response w/ 3 headers, check case-insensitivity", "722341", + "HTTP/1.0 200 ok\r\nHeader1: value3\r\nHeader2: field\r\nHead3: Another \r\n", -1, + SOUP_HTTP_1_0, SOUP_STATUS_OK, "ok", + { { "header1", "value3" }, + { "Header2", "field" }, + { "hEAD3", "Another" }, + { "Something else", NULL }, + { NULL } + } + }, + /*****************************/ /*** RECOVERABLE RESPONSES ***/ /*****************************/ /* RFC 2616 section 3.1 says we MUST accept this */ - { "HTTP/01.01 response", + { "HTTP/01.01 response", NULL, "HTTP/01.01 200 ok\r\nFoo: bar\r\n", -1, SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok", { { "Foo", "bar" }, @@ -537,7 +551,7 @@ static struct ResponseTest { /* RFC 2616 section 19.3 says we SHOULD accept these */ - { "Response w/ LF instead of CRLF after Status-Line", + { "Response w/ LF instead of CRLF after Status-Line", NULL, "HTTP/1.1 200 ok\nFoo: bar\r\n", -1, SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok", { { "Foo", "bar" }, @@ -545,7 +559,7 @@ static struct ResponseTest { } }, - { "Response w/ incorrect spacing in Status-Line", + { "Response w/ incorrect spacing in Status-Line", NULL, "HTTP/1.1 200\tok\r\nFoo: bar\r\n", -1, SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok", { { "Foo", "bar" }, @@ -553,7 +567,7 @@ static struct ResponseTest { } }, - { "Response w/ no reason phrase or preceding SP", + { "Response w/ no reason phrase or preceding SP", NULL, "HTTP/1.1 200\r\nFoo: bar\r\n", -1, SOUP_HTTP_1_1, SOUP_STATUS_OK, "", { { "Foo", "bar" }, @@ -561,7 +575,7 @@ static struct ResponseTest { } }, - { "Response w/ no whitespace after status code", + { "Response w/ no whitespace after status code", NULL, "HTTP/1.1 200ok\r\nFoo: bar\r\n", -1, SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok", { { "Foo", "bar" }, @@ -570,7 +584,7 @@ static struct ResponseTest { }, /* Shoutcast support */ - { "Shoutcast server not-quite-HTTP", + { "Shoutcast server not-quite-HTTP", "502325", "ICY 200 OK\r\nFoo: bar\r\n", -1, SOUP_HTTP_1_0, SOUP_STATUS_OK, "OK", { { "Foo", "bar" }, @@ -578,8 +592,7 @@ static struct ResponseTest { } }, - /* qv bug 579318, do_bad_header_tests() below */ - { "Response w/ mangled header", + { "Response w/ mangled header", "579318", "HTTP/1.1 200 ok\r\nFoo: one\r\nBar two:2\r\nBaz: three\r\n", -1, SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok", { { "Foo", "one" }, @@ -588,8 +601,7 @@ static struct ResponseTest { } }, - /* qv bug 602863 */ - { "HTTP 1.1 response with leading line break", + { "HTTP 1.1 response with leading line break", "602863", "\nHTTP/1.1 200 ok\r\nFoo: bar\r\n", -1, SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok", { { "Foo", "bar" }, @@ -600,79 +612,79 @@ static struct ResponseTest { /*** INVALID RESPONSES ***/ /*************************/ - { "Invalid HTTP version", + { "Invalid HTTP version", NULL, "HTTP/1.2 200 OK\r\nFoo: bar\r\n", -1, -1, 0, NULL, { { NULL } } }, - { "Non-HTTP response", + { "Non-HTTP response", NULL, "SOUP/1.1 200 OK\r\nFoo: bar\r\n", -1, -1, 0, NULL, { { NULL } } }, - { "Non-numeric status code", + { "Non-numeric status code", NULL, "HTTP/1.1 XXX OK\r\nFoo: bar\r\n", -1, -1, 0, NULL, { { NULL } } }, - { "No status code", + { "No status code", NULL, "HTTP/1.1 OK\r\nFoo: bar\r\n", -1, -1, 0, NULL, { { NULL } } }, - { "One-digit status code", + { "One-digit status code", NULL, "HTTP/1.1 2 OK\r\nFoo: bar\r\n", -1, -1, 0, NULL, { { NULL } } }, - { "Two-digit status code", + { "Two-digit status code", NULL, "HTTP/1.1 20 OK\r\nFoo: bar\r\n", -1, -1, 0, NULL, { { NULL } } }, - { "Four-digit status code", + { "Four-digit status code", NULL, "HTTP/1.1 2000 OK\r\nFoo: bar\r\n", -1, -1, 0, NULL, { { NULL } } }, - { "Status code < 100", + { "Status code < 100", NULL, "HTTP/1.1 001 OK\r\nFoo: bar\r\n", -1, -1, 0, NULL, { { NULL } } }, - { "Status code > 599", + { "Status code > 599", NULL, "HTTP/1.1 600 OK\r\nFoo: bar\r\n", -1, -1, 0, NULL, { { NULL } } }, - { "NUL at start", + { "NUL at start", "666316", "\x00HTTP/1.1 200 OK\r\nFoo: bar\r\n", 28, -1, 0, NULL, { { NULL } } }, - { "NUL in Reason Phrase", + { "NUL in Reason Phrase", NULL, "HTTP/1.1 200 O\x00K\r\nFoo: bar\r\n", 28, -1, 0, NULL, { { NULL } } }, - { "NUL in header name", + { "NUL in header name", NULL, "HTTP/1.1 200 OK\r\nF\x00oo: bar\r\n", 28, -1, 0, NULL, { { NULL } } }, - { "NUL in header value", + { "NUL in header value", NULL, "HTTP/1.1 200 OK\r\nFoo: b\x00ar\r\n", 28, -1, 0, NULL, { { NULL } } @@ -704,18 +716,11 @@ static struct QValueTest { static const int num_qvaluetests = G_N_ELEMENTS (qvaluetests); static void -print_header (const char *name, const char *value, gpointer data) -{ - debug_printf (1, " '%s': '%s'\n", name, value); -} - -static gboolean check_headers (Header *headers, SoupMessageHeaders *hdrs) { GSList *header_names, *h; SoupMessageHeadersIter iter; const char *name, *value; - gboolean ok = TRUE; int i; header_names = NULL; @@ -727,36 +732,33 @@ check_headers (Header *headers, SoupMessageHeaders *hdrs) } for (i = 0, h = header_names; headers[i].name && h; i++, h = h->next) { - if (strcmp (h->data, headers[i].name) != 0) { - ok = FALSE; - break; - } + g_assert (g_ascii_strcasecmp (h->data, headers[i].name) == 0); + value = soup_message_headers_get_list (hdrs, headers[i].name); - if (strcmp (value, headers[i].value) != 0) { - ok = FALSE; - break; - } + g_assert_cmpstr (value, ==, headers[i].value); + } + /* If we have remaining fields to check, they should return NULL */ + for (; headers[i].name; i++) { + value = soup_message_headers_get_list (hdrs, headers[i].name); + g_assert_null (value); } - if (headers[i].name || h) - ok = FALSE; + g_assert_null (headers[i].name); + g_assert_null (h); + g_slist_free (header_names); - return ok; } static void do_request_tests (void) { - int i, len, h; + int i, len; char *method, *path; SoupHTTPVersion version; SoupMessageHeaders *headers; guint status; - debug_printf (1, "Request tests\n"); for (i = 0; i < num_reqtests; i++) { - gboolean ok = TRUE; - - debug_printf (1, "%2d. %s (%s): ", i + 1, reqtests[i].description, + debug_printf (1, "%2d. %s (%s)\n", i + 1, reqtests[i].description, soup_status_get_phrase (reqtests[i].status)); headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_REQUEST); @@ -769,71 +771,32 @@ do_request_tests (void) status = soup_headers_parse_request (reqtests[i].request, len, headers, &method, &path, &version); + g_assert_cmpint (status, ==, reqtests[i].status); if (SOUP_STATUS_IS_SUCCESSFUL (status)) { - if ((reqtests[i].method && strcmp (reqtests[i].method, method) != 0) || !reqtests[i].method) - ok = FALSE; - if ((reqtests[i].path && strcmp (reqtests[i].path, path) != 0) || !reqtests[i].path) - ok = FALSE; - if (reqtests[i].version != version) - ok = FALSE; - - if (!check_headers (reqtests[i].headers, headers)) - ok = FALSE; - } else { - if (status != reqtests[i].status) - ok = FALSE; - } + g_assert_cmpstr (method, ==, reqtests[i].method); + g_assert_cmpstr (path, ==, reqtests[i].path); + g_assert_cmpint (version, ==, reqtests[i].version); - if (ok) - debug_printf (1, "OK!\n"); - else { - debug_printf (1, "BAD!\n"); - errors++; - if (reqtests[i].method) { - debug_printf (1, " expected: '%s' '%s' 'HTTP/1.%d'\n", - reqtests[i].method, - reqtests[i].path, - reqtests[i].version); - for (h = 0; reqtests[i].headers[h].name; h++) { - debug_printf (1, " '%s': '%s'\n", - reqtests[i].headers[h].name, - reqtests[i].headers[h].value); - } - } else { - debug_printf (1, " expected: %s\n", - soup_status_get_phrase (reqtests[i].status)); - } - if (method) { - debug_printf (1, " got: '%s' '%s' 'HTTP/1.%d'\n", - method, path, version); - soup_message_headers_foreach (headers, print_header, NULL); - } else { - debug_printf (1, " got: %s\n", - soup_status_get_phrase (status)); - } + check_headers (reqtests[i].headers, headers); } g_free (method); g_free (path); soup_message_headers_free (headers); } - debug_printf (1, "\n"); } static void do_response_tests (void) { - int i, len, h; + int i, len; guint status_code; char *reason_phrase; SoupHTTPVersion version; SoupMessageHeaders *headers; - debug_printf (1, "Response tests\n"); for (i = 0; i < num_resptests; i++) { - gboolean ok = TRUE; - - debug_printf (1, "%2d. %s (%s): ", i + 1, resptests[i].description, + debug_printf (1, "%2d. %s (%s)\n", i + 1, resptests[i].description, resptests[i].reason_phrase ? "should parse" : "should NOT parse"); headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE); @@ -846,49 +809,17 @@ do_response_tests (void) if (soup_headers_parse_response (resptests[i].response, len, headers, &version, &status_code, &reason_phrase)) { - if (resptests[i].version != version) - ok = FALSE; - if (resptests[i].status_code != status_code) - ok = FALSE; - if ((resptests[i].reason_phrase && strcmp (resptests[i].reason_phrase, reason_phrase) != 0) || !resptests[i].reason_phrase) - ok = FALSE; - - if (!check_headers (resptests[i].headers, headers)) - ok = FALSE; - } else { - if (resptests[i].reason_phrase) - ok = FALSE; - } + g_assert_cmpint (version, ==, resptests[i].version); + g_assert_cmpint (status_code, ==, resptests[i].status_code); + g_assert_cmpstr (reason_phrase, ==, resptests[i].reason_phrase); - if (ok) - debug_printf (1, "OK!\n"); - else { - debug_printf (1, "BAD!\n"); - errors++; - if (resptests[i].reason_phrase) { - debug_printf (1, " expected: 'HTTP/1.%d' '%03d' '%s'\n", - resptests[i].version, - resptests[i].status_code, - resptests[i].reason_phrase); - for (h = 0; resptests[i].headers[h].name; h++) { - debug_printf (1, " '%s': '%s'\n", - resptests[i].headers[h].name, - resptests[i].headers[h].value); - } - } else - debug_printf (1, " expected: parse error\n"); - if (reason_phrase) { - debug_printf (1, " got: 'HTTP/1.%d' '%03d' '%s'\n", - version, status_code, reason_phrase); - soup_message_headers_foreach (headers, print_header, NULL); - } else - debug_printf (1, " got: parse error\n"); - } + check_headers (resptests[i].headers, headers); + } else + g_assert_null (resptests[i].reason_phrase); g_free (reason_phrase); soup_message_headers_free (headers); } - debug_printf (1, "\n"); } static void @@ -896,9 +827,7 @@ do_qvalue_tests (void) { int i, j; GSList *acceptable, *unacceptable, *iter; - gboolean wrong; - debug_printf (1, "qvalue tests\n"); for (i = 0; i < num_qvaluetests; i++) { debug_printf (1, "%2d. %s:\n", i + 1, qvaluetests[i].header_value); @@ -907,48 +836,26 @@ do_qvalue_tests (void) &unacceptable); debug_printf (1, " acceptable: "); - wrong = FALSE; if (acceptable) { for (iter = acceptable, j = 0; iter; iter = iter->next, j++) { debug_printf (1, "%s ", (char *)iter->data); - if (!qvaluetests[i].acceptable[j] || - strcmp (iter->data, qvaluetests[i].acceptable[j]) != 0) - wrong = TRUE; + g_assert_cmpstr (iter->data, ==, qvaluetests[i].acceptable[j]); } debug_printf (1, "\n"); soup_header_free_list (acceptable); } else debug_printf (1, "(none)\n"); - if (wrong) { - debug_printf (1, " WRONG! expected: "); - for (j = 0; qvaluetests[i].acceptable[j]; j++) - debug_printf (1, "%s ", qvaluetests[i].acceptable[j]); - debug_printf (1, "\n"); - errors++; - } debug_printf (1, " unacceptable: "); - wrong = FALSE; if (unacceptable) { for (iter = unacceptable, j = 0; iter; iter = iter->next, j++) { debug_printf (1, "%s ", (char *)iter->data); - if (!qvaluetests[i].unacceptable[j] || - strcmp (iter->data, qvaluetests[i].unacceptable[j]) != 0) - wrong = TRUE; + g_assert_cmpstr (iter->data, ==, qvaluetests[i].unacceptable[j]); } debug_printf (1, "\n"); soup_header_free_list (unacceptable); } else debug_printf (1, "(none)\n"); - if (wrong) { - debug_printf (1, " WRONG! expected: "); - for (j = 0; qvaluetests[i].unacceptable[j]; j++) - debug_printf (1, "%s ", qvaluetests[i].unacceptable[j]); - debug_printf (1, "\n"); - errors++; - } - - debug_printf (1, "\n"); } } @@ -972,8 +879,6 @@ do_content_disposition_tests (void) SoupMultipart *multipart; SoupMessageBody *body; - debug_printf (1, "Content-Disposition tests\n"); - hdrs = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART); params = g_hash_table_new (g_str_hash, g_str_equal); g_hash_table_insert (params, "filename", RFC5987_TEST_FILENAME); @@ -981,13 +886,7 @@ do_content_disposition_tests (void) g_hash_table_destroy (params); header = soup_message_headers_get_one (hdrs, "Content-Disposition"); - if (!strcmp (header, RFC5987_TEST_HEADER_ENCODED)) - debug_printf (1, " encoded OK\n"); - else { - debug_printf (1, " encoding FAILED!\n expected: %s\n got: %s\n", - RFC5987_TEST_HEADER_ENCODED, header); - errors++; - } + g_assert_cmpstr (header, ==, RFC5987_TEST_HEADER_ENCODED); /* UTF-8 decoding */ soup_message_headers_clear (hdrs); @@ -996,22 +895,13 @@ do_content_disposition_tests (void) if (!soup_message_headers_get_content_disposition (hdrs, &disposition, ¶ms)) { - debug_printf (1, " UTF-8 decoding FAILED!\n could not parse\n"); - errors++; + soup_test_assert (FALSE, "UTF-8 decoding FAILED"); return; } g_free (disposition); filename = g_hash_table_lookup (params, "filename"); - if (!filename) { - debug_printf (1, " UTF-8 decoding FAILED!\n could not find filename\n"); - errors++; - } else if (strcmp (filename, RFC5987_TEST_FILENAME) != 0) { - debug_printf (1, " UTF-8 decoding FAILED!\n expected: %s\n got: %s\n", - RFC5987_TEST_FILENAME, filename); - errors++; - } else - debug_printf (1, " UTF-8 decoded OK\n"); + g_assert_cmpstr (filename, ==, RFC5987_TEST_FILENAME); g_hash_table_destroy (params); /* ISO-8859-1 decoding */ @@ -1021,22 +911,13 @@ do_content_disposition_tests (void) if (!soup_message_headers_get_content_disposition (hdrs, &disposition, ¶ms)) { - debug_printf (1, " iso-8859-1 decoding FAILED!\n could not parse\n"); - errors++; + soup_test_assert (FALSE, "iso-8859-1 decoding FAILED"); return; } g_free (disposition); filename = g_hash_table_lookup (params, "filename"); - if (!filename) { - debug_printf (1, " iso-8859-1 decoding FAILED!\n could not find filename\n"); - errors++; - } else if (strcmp (filename, RFC5987_TEST_FILENAME) != 0) { - debug_printf (1, " iso-8859-1 decoding FAILED!\n expected: %s\n got: %s\n", - RFC5987_TEST_FILENAME, filename); - errors++; - } else - debug_printf (1, " iso-8859-1 decoded OK\n"); + g_assert_cmpstr (filename, ==, RFC5987_TEST_FILENAME); g_hash_table_destroy (params); /* Fallback */ @@ -1046,27 +927,19 @@ do_content_disposition_tests (void) if (!soup_message_headers_get_content_disposition (hdrs, &disposition, ¶ms)) { - debug_printf (1, " fallback decoding FAILED!\n could not parse\n"); - errors++; + soup_test_assert (FALSE, "fallback decoding FAILED"); return; } g_free (disposition); filename = g_hash_table_lookup (params, "filename"); - if (!filename) { - debug_printf (1, " fallback decoding FAILED!\n could not find filename\n"); - errors++; - } else if (strcmp (filename, RFC5987_TEST_FALLBACK_FILENAME) != 0) { - debug_printf (1, " fallback decoding FAILED!\n expected: %s\n got: %s\n", - RFC5987_TEST_FALLBACK_FILENAME, filename); - errors++; - } else - debug_printf (1, " fallback decoded OK\n"); + g_assert_cmpstr (filename, ==, RFC5987_TEST_FALLBACK_FILENAME); g_hash_table_destroy (params); soup_message_headers_free (hdrs); - /* Ensure that soup-multipart always quotes filename (bug 641280) */ + /* Ensure that soup-multipart always quotes filename */ + g_test_bug ("641280"); multipart = soup_multipart_new (SOUP_FORM_MIME_TYPE_MULTIPART); buffer = soup_buffer_new (SOUP_MEMORY_STATIC, "foo", 3); soup_multipart_append_form_file (multipart, "test", "token", @@ -1082,15 +955,9 @@ do_content_disposition_tests (void) buffer = soup_message_body_flatten (body); soup_message_body_free (body); - if (strstr (buffer->data, "filename=\"token\"")) - debug_printf (1, " SoupMultipart encoded filename correctly\n"); - else { - debug_printf (1, " SoupMultipart encoded filename incorrectly!\n"); - errors++; - } - soup_buffer_free (buffer); + g_assert_true (strstr (buffer->data, "filename=\"token\"")); - debug_printf (1, "\n"); + soup_buffer_free (buffer); } #define CONTENT_TYPE_TEST_MIME_TYPE "text/plain" @@ -1107,7 +974,7 @@ do_content_type_tests (void) GHashTable *params; const char *header, *mime_type; - debug_printf (1, "Content-Type tests\n"); + g_test_bug ("576760"); hdrs = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART); params = g_hash_table_new (g_str_hash, g_str_equal); @@ -1117,13 +984,7 @@ do_content_type_tests (void) g_hash_table_destroy (params); header = soup_message_headers_get_one (hdrs, "Content-Type"); - if (!strcmp (header, CONTENT_TYPE_TEST_HEADER)) - debug_printf (1, " encoded OK\n"); - else { - debug_printf (1, " encoding FAILED!\n expected: %s\n got: %s\n", - CONTENT_TYPE_TEST_HEADER, header); - errors++; - } + g_assert_cmpstr (header, ==, CONTENT_TYPE_TEST_HEADER); soup_message_headers_clear (hdrs); soup_message_headers_append (hdrs, "Content-Type", @@ -1133,38 +994,20 @@ do_content_type_tests (void) CONTENT_TYPE_TEST_MIME_TYPE); mime_type = soup_message_headers_get_content_type (hdrs, ¶ms); - if (!mime_type) { - debug_printf (1, " decoding FAILED!\n could not parse\n"); - errors++; - } - - if (mime_type && strcmp (mime_type, CONTENT_TYPE_TEST_MIME_TYPE) != 0) { - debug_printf (1, " decoding FAILED!\n bad returned MIME type: %s\n", - mime_type); - errors++; - } else if (params && g_hash_table_size (params) != 0) { - debug_printf (1, " decoding FAILED!\n params contained %d params (should be 0)\n", - g_hash_table_size (params)); - errors++; - } else - debug_printf (1, " decoded OK\n"); - + g_assert_cmpstr (mime_type, ==, CONTENT_TYPE_TEST_MIME_TYPE); + g_assert_cmpint (g_hash_table_size (params), ==, 0); if (params) g_hash_table_destroy (params); + g_test_bug ("577630"); + soup_message_headers_clear (hdrs); soup_message_headers_append (hdrs, "Content-Type", CONTENT_TYPE_BAD_HEADER); mime_type = soup_message_headers_get_content_type (hdrs, ¶ms); - if (mime_type) { - debug_printf (1, " Bad content rejection FAILED!\n"); - errors++; - } else - debug_printf (1, " Bad content rejection OK\n"); + g_assert_null (mime_type); soup_message_headers_free (hdrs); - - debug_printf (1, "\n"); } struct { @@ -1185,7 +1028,7 @@ do_append_param_tests (void) GString *params; int i; - debug_printf (1, "soup_header_g_string_append_param() tests\n"); + g_test_bug ("577728"); params = g_string_new (NULL); for (i = 0; i < G_N_ELEMENTS (test_params); i++) { @@ -1195,15 +1038,8 @@ do_append_param_tests (void) test_params[i].name, test_params[i].value); } - if (strcmp (params->str, TEST_PARAMS_RESULT) != 0) { - debug_printf (1, " FAILED!\n expected: %s\n got: %s\n", - TEST_PARAMS_RESULT, params->str); - errors++; - } else - debug_printf (1, " OK\n"); + g_assert_cmpstr (params->str, ==, TEST_PARAMS_RESULT); g_string_free (params, TRUE); - - debug_printf (1, "\n"); } static const struct { @@ -1226,19 +1062,15 @@ do_bad_header_tests (void) SoupMessageHeaders *hdrs; int i; - debug_printf (1, "bad header rejection tests\n"); - hdrs = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART); for (i = 0; i < G_N_ELEMENTS (bad_headers); i++) { debug_printf (1, " %s\n", bad_headers[i].description); - expect_warning = TRUE; + + g_test_expect_message ("libsoup", G_LOG_LEVEL_CRITICAL, + "*soup_message_headers_append*assertion*failed*"); soup_message_headers_append (hdrs, bad_headers[i].name, bad_headers[i].value); - if (expect_warning) { - expect_warning = FALSE; - debug_printf (1, " FAILED: soup_message_headers_append() did not reject it\n"); - errors++; - } + g_test_assert_expected_messages (); } soup_message_headers_free (hdrs); } @@ -1246,16 +1078,20 @@ do_bad_header_tests (void) int main (int argc, char **argv) { + int ret; + test_init (argc, argv, NULL); - do_request_tests (); - do_response_tests (); - do_qvalue_tests (); - do_content_disposition_tests (); - do_content_type_tests (); - do_append_param_tests (); - do_bad_header_tests (); + g_test_add_func ("/header-parsing/request", do_request_tests); + g_test_add_func ("/header-parsing/response", do_response_tests); + g_test_add_func ("/header-parsing/qvalue", do_qvalue_tests); + g_test_add_func ("/header-parsing/content-disposition", do_content_disposition_tests); + g_test_add_func ("/header-parsing/content-type", do_content_type_tests); + g_test_add_func ("/header-parsing/append-param", do_append_param_tests); + g_test_add_func ("/header-parsing/bad", do_bad_header_tests); + + ret = g_test_run (); test_cleanup (); - return errors != 0; + return ret; } diff --git a/tests/httpd.conf.in b/tests/httpd.conf.22.in index de0b75fa..b912ca36 100644 --- a/tests/httpd.conf.in +++ b/tests/httpd.conf.22.in @@ -3,8 +3,7 @@ ServerName 127.0.0.1 Listen 127.0.0.1:47524 -PidFile @builddir@/httpd.pid -DocumentRoot @srcdir@ +DocumentRoot . # The tests shut down apache with "graceful-stop", because that makes # it close its listening socket right away. But it seems to sometimes @@ -83,7 +82,7 @@ Listen 127.0.0.1:47527 AuthType Basic AuthName realm1 - AuthUserFile @srcdir@/htpasswd + AuthUserFile ./htpasswd Require valid-user </Proxy> @@ -94,7 +93,7 @@ Listen 127.0.0.1:47527 AuthType Basic AuthName realm1 - AuthUserFile @srcdir@/htpasswd + AuthUserFile ./htpasswd Require valid-user </Proxy> @@ -124,7 +123,7 @@ Listen 127.0.0.1:47528 AuthType Basic AuthName realm1 - AuthUserFile @srcdir@/htpasswd + AuthUserFile ./htpasswd Require user no-such-user </Proxy> @@ -135,7 +134,7 @@ Listen 127.0.0.1:47528 AuthType Basic AuthName realm1 - AuthUserFile @srcdir@/htpasswd + AuthUserFile ./htpasswd Require user no-such-user </Proxy> @@ -154,89 +153,89 @@ Listen 127.0.0.1:47528 <VirtualHost 127.0.0.1:47525> SSLEngine on - SSLCertificateFile @srcdir@/test-cert.pem - SSLCertificateKeyFile @srcdir@/test-key.pem + SSLCertificateFile ./test-cert.pem + SSLCertificateKeyFile ./test-key.pem </VirtualHost> </IfModule> # Basic auth tests -Alias /Basic/realm1/realm2/realm1 @srcdir@ -Alias /Basic/realm1/realm2 @srcdir@ -Alias /Basic/realm1/subdir @srcdir@ -Alias /Basic/realm1/not @srcdir@ -Alias /Basic/realm1 @srcdir@ -Alias /Basic/realm12/subdir @srcdir@ -Alias /Basic/realm12 @srcdir@ -Alias /Basic/realm2 @srcdir@ -Alias /Basic/realm3 @srcdir@ -Alias /Basic @srcdir@ +Alias /Basic/realm1/realm2/realm1 . +Alias /Basic/realm1/realm2 . +Alias /Basic/realm1/subdir . +Alias /Basic/realm1/not . +Alias /Basic/realm1 . +Alias /Basic/realm12/subdir . +Alias /Basic/realm12 . +Alias /Basic/realm2 . +Alias /Basic/realm3 . +Alias /Basic . <Location /Basic/realm1> AuthType Basic AuthName realm1 - AuthUserFile @srcdir@/htpasswd + AuthUserFile ./htpasswd Require user user1 </Location> <Location /Basic/realm1/not> AuthType Basic AuthName realm1 - AuthUserFile @srcdir@/htpasswd + AuthUserFile ./htpasswd Require user user2 </Location> <Location /Basic/realm12> AuthType Basic AuthName realm12 - AuthUserFile @srcdir@/htpasswd + AuthUserFile ./htpasswd Require user user1 user2 </Location> <Location /Basic/realm1/realm2> AuthType Basic AuthName realm2 - AuthUserFile @srcdir@/htpasswd + AuthUserFile ./htpasswd Require user user2 </Location> <Location /Basic/realm1/realm2/realm1> AuthType Basic AuthName realm1 - AuthUserFile @srcdir@/htpasswd + AuthUserFile ./htpasswd Require user user1 </Location> <Location /Basic/realm2> AuthType Basic AuthName realm2 - AuthUserFile @srcdir@/htpasswd + AuthUserFile ./htpasswd Require user user2 </Location> <Location /Basic/realm3> AuthType Basic AuthName realm3 - AuthUserFile @srcdir@/htpasswd + AuthUserFile ./htpasswd Require user user3 </Location> # Digest auth tests -Alias /Digest/realm1/realm2/realm1 @srcdir@ -Alias /Digest/realm1/realm2 @srcdir@ -Alias /Digest/realm1/subdir @srcdir@ -Alias /Digest/realm1/expire @srcdir@ -Alias /Digest/realm1/not @srcdir@ -Alias /Digest/realm1 @srcdir@ -Alias /Digest/realm2 @srcdir@ -Alias /Digest/realm3 @srcdir@ -Alias /Digest @srcdir@ +Alias /Digest/realm1/realm2/realm1 . +Alias /Digest/realm1/realm2 . +Alias /Digest/realm1/subdir . +Alias /Digest/realm1/expire . +Alias /Digest/realm1/not . +Alias /Digest/realm1 . +Alias /Digest/realm2 . +Alias /Digest/realm3 . +Alias /Digest . <Location /Digest/realm1> AuthType Digest AuthName realm1 - AuthUserFile @srcdir@/htdigest + AuthUserFile ./htdigest AuthDigestDomain /Digest/realm1 /Digest/realm1/realm2/realm1 Require valid-user </Location> @@ -244,7 +243,7 @@ Alias /Digest @srcdir@ <Location /Digest/realm1/expire> AuthType Digest AuthName realm1 - AuthUserFile @srcdir@/htdigest + AuthUserFile ./htdigest AuthDigestDomain /Digest/realm1 /Digest/realm1/realm2/realm1 AuthDigestNonceLifetime 2 Require valid-user @@ -253,7 +252,7 @@ Alias /Digest @srcdir@ <Location /Digest/realm1/not> AuthType Digest AuthName realm1 - AuthUserFile @srcdir@/htdigest + AuthUserFile ./htdigest AuthDigestDomain /Digest/realm1 /Digest/realm1/realm2/realm1 Require user user2 </Location> @@ -261,7 +260,7 @@ Alias /Digest @srcdir@ <Location /Digest/realm1/realm2> AuthType Digest AuthName realm2 - AuthUserFile @srcdir@/htdigest + AuthUserFile ./htdigest AuthDigestDomain /Digest/realm2 /Digest/realm1/realm2 Require valid-user </Location> @@ -269,7 +268,7 @@ Alias /Digest @srcdir@ <Location /Digest/realm1/realm2/realm1> AuthType Digest AuthName realm1 - AuthUserFile @srcdir@/htdigest + AuthUserFile ./htdigest AuthDigestDomain /Digest/realm1 /Digest/realm1/realm2/realm1 Require valid-user </Location> @@ -277,7 +276,7 @@ Alias /Digest @srcdir@ <Location /Digest/realm2> AuthType Digest AuthName realm2 - AuthUserFile @srcdir@/htdigest + AuthUserFile ./htdigest AuthDigestDomain /Digest/realm2 /Digest/realm1/realm2 Require valid-user </Location> @@ -285,7 +284,7 @@ Alias /Digest @srcdir@ <Location /Digest/realm3> AuthType Digest AuthName realm3 - AuthUserFile @srcdir@/htdigest + AuthUserFile ./htdigest AuthDigestDomain /Digest/realm3 Require valid-user # test RFC2069-style Digest diff --git a/tests/httpd.conf.24.in b/tests/httpd.conf.24.in new file mode 100644 index 00000000..850b8393 --- /dev/null +++ b/tests/httpd.conf.24.in @@ -0,0 +1,276 @@ +# http.conf used for testing auth-test + +ServerName 127.0.0.1 +Listen 127.0.0.1:47524 + +DocumentRoot . + +# The tests shut down apache with "graceful-stop", because that makes +# it close its listening socket right away. But it seems to sometimes +# result in apache never fully exiting. This fixes that. +GracefulShutdownTimeout 1 + +# Change this to "./error.log" if it's failing and you don't know why +ErrorLog /dev/null + +LoadModule mpm_prefork_module @APACHE_MODULE_DIR@/mod_mpm_prefork.so +LoadModule alias_module @APACHE_MODULE_DIR@/mod_alias.so +LoadModule auth_basic_module @APACHE_MODULE_DIR@/mod_auth_basic.so +LoadModule auth_digest_module @APACHE_MODULE_DIR@/mod_auth_digest.so +LoadModule authn_core_module @APACHE_MODULE_DIR@/mod_authn_core.so +LoadModule authn_file_module @APACHE_MODULE_DIR@/mod_authn_file.so +LoadModule authz_core_module @APACHE_MODULE_DIR@/mod_authz_core.so +LoadModule authz_host_module @APACHE_MODULE_DIR@/mod_authz_host.so +LoadModule authz_user_module @APACHE_MODULE_DIR@/mod_authz_user.so +LoadModule dir_module @APACHE_MODULE_DIR@/mod_dir.so +LoadModule mime_module @APACHE_MODULE_DIR@/mod_mime.so +@IF_HAVE_PHP@LoadModule php5_module @APACHE_PHP_MODULE_DIR@/@APACHE_PHP_MODULE@ +LoadModule proxy_module @APACHE_MODULE_DIR@/mod_proxy.so +LoadModule proxy_http_module @APACHE_MODULE_DIR@/mod_proxy_http.so +LoadModule proxy_connect_module @APACHE_MODULE_DIR@/mod_proxy_connect.so +LoadModule ssl_module @APACHE_SSL_MODULE_DIR@/mod_ssl.so +LoadModule unixd_module @APACHE_SSL_MODULE_DIR@/mod_unixd.so + +DirectoryIndex index.txt +TypesConfig /dev/null +AddType application/x-httpd-php .php +Redirect permanent /redirected /index.txt + +# Proxy #1: unauthenticated +Listen 127.0.0.1:47526 +<VirtualHost 127.0.0.1:47526> + ProxyRequests On + AllowCONNECT 47525 + + # Deny proxying by default + <Proxy *> + Require all denied + </Proxy> + + # Allow local http connections + <Proxy http://127.0.0.1*> + Require all granted + </Proxy> + + # Allow CONNECT to local https port + <Proxy 127.0.0.1:47525> + Require all granted + </Proxy> + + # Deny non-proxy requests + <Directory /> + Require all denied + </Directory> +</VirtualHost> + +# Proxy #2: authenticated +Listen 127.0.0.1:47527 +<VirtualHost 127.0.0.1:47527> + ProxyRequests On + AllowCONNECT 47525 + + # Deny proxying by default + <Proxy *> + Require all denied + </Proxy> + + # Allow local http connections with authentication + <Proxy http://127.0.0.1:47524*> + AuthType Basic + AuthName realm1 + AuthUserFile ./htpasswd + Require valid-user + </Proxy> + + # Allow CONNECT to local https port with authentication + <Proxy 127.0.0.1:47525> + AuthType Basic + AuthName realm1 + AuthUserFile ./htpasswd + Require valid-user + </Proxy> + + # Fail non-proxy requests + <Directory /> + Require all denied + </Directory> +</VirtualHost> + +# Proxy #3: unauthenticatable-to +Listen 127.0.0.1:47528 +<VirtualHost 127.0.0.1:47528> + ProxyRequests On + AllowCONNECT 47525 + + # Deny proxying by default + <Proxy *> + Require all denied + </Proxy> + + # Allow local http connections with authentication + <Proxy http://127.0.0.1:47524*> + AuthType Basic + AuthName realm1 + AuthUserFile ./htpasswd + Require user no-such-user + </Proxy> + + # Allow CONNECT to local https port with authentication + <Proxy 127.0.0.1:47525> + AuthType Basic + AuthName realm1 + AuthUserFile ./htpasswd + Require user no-such-user + </Proxy> + + # Fail non-proxy requests + <Directory /> + Require all denied + </Directory> +</VirtualHost> + + +# SSL setup +<IfModule mod_ssl.c> + Listen 127.0.0.1:47525 + + <VirtualHost 127.0.0.1:47525> + SSLEngine on + + SSLCertificateFile ./test-cert.pem + SSLCertificateKeyFile ./test-key.pem + + </VirtualHost> +</IfModule> + + +# Basic auth tests +Alias /Basic/realm1/realm2/realm1 . +Alias /Basic/realm1/realm2 . +Alias /Basic/realm1/subdir . +Alias /Basic/realm1/not . +Alias /Basic/realm1 . +Alias /Basic/realm12/subdir . +Alias /Basic/realm12 . +Alias /Basic/realm2 . +Alias /Basic/realm3 . +Alias /Basic . + +<Location /Basic/realm1> + AuthType Basic + AuthName realm1 + AuthUserFile ./htpasswd + Require user user1 +</Location> + +<Location /Basic/realm1/not> + AuthType Basic + AuthName realm1 + AuthUserFile ./htpasswd + Require user user2 +</Location> + +<Location /Basic/realm12> + AuthType Basic + AuthName realm12 + AuthUserFile ./htpasswd + Require user user1 user2 +</Location> + +<Location /Basic/realm1/realm2> + AuthType Basic + AuthName realm2 + AuthUserFile ./htpasswd + Require user user2 +</Location> + +<Location /Basic/realm1/realm2/realm1> + AuthType Basic + AuthName realm1 + AuthUserFile ./htpasswd + Require user user1 +</Location> + +<Location /Basic/realm2> + AuthType Basic + AuthName realm2 + AuthUserFile ./htpasswd + Require user user2 +</Location> + +<Location /Basic/realm3> + AuthType Basic + AuthName realm3 + AuthUserFile ./htpasswd + Require user user3 +</Location> + +# Digest auth tests +Alias /Digest/realm1/realm2/realm1 . +Alias /Digest/realm1/realm2 . +Alias /Digest/realm1/subdir . +Alias /Digest/realm1/expire . +Alias /Digest/realm1/not . +Alias /Digest/realm1 . +Alias /Digest/realm2 . +Alias /Digest/realm3 . +Alias /Digest . + +<Location /Digest/realm1> + AuthType Digest + AuthName realm1 + AuthUserFile ./htdigest + AuthDigestDomain /Digest/realm1 /Digest/realm1/realm2/realm1 + Require valid-user +</Location> + +<Location /Digest/realm1/expire> + AuthType Digest + AuthName realm1 + AuthUserFile ./htdigest + AuthDigestDomain /Digest/realm1 /Digest/realm1/realm2/realm1 + AuthDigestNonceLifetime 2 + Require valid-user +</Location> + +<Location /Digest/realm1/not> + AuthType Digest + AuthName realm1 + AuthUserFile ./htdigest + AuthDigestDomain /Digest/realm1 /Digest/realm1/realm2/realm1 + Require user user2 +</Location> + +<Location /Digest/realm1/realm2> + AuthType Digest + AuthName realm2 + AuthUserFile ./htdigest + AuthDigestDomain /Digest/realm2 /Digest/realm1/realm2 + Require valid-user +</Location> + +<Location /Digest/realm1/realm2/realm1> + AuthType Digest + AuthName realm1 + AuthUserFile ./htdigest + AuthDigestDomain /Digest/realm1 /Digest/realm1/realm2/realm1 + Require valid-user +</Location> + +<Location /Digest/realm2> + AuthType Digest + AuthName realm2 + AuthUserFile ./htdigest + AuthDigestDomain /Digest/realm2 /Digest/realm1/realm2 + Require valid-user +</Location> + +<Location /Digest/realm3> + AuthType Digest + AuthName realm3 + AuthUserFile ./htdigest + AuthDigestDomain /Digest/realm3 + Require valid-user + # test RFC2069-style Digest + AuthDigestQop none +</Location> diff --git a/tests/libsoup.supp b/tests/libsoup.supp index 24c6516a..ae8bda38 100644 --- a/tests/libsoup.supp +++ b/tests/libsoup.supp @@ -321,7 +321,7 @@ ... fun:_dl_allocate_tls ... - fun:g_thread_create_posix_impl + fun:g_system_thread_new } { glib/filenamecharsets @@ -342,12 +342,78 @@ fun:g_tls_backend_gnutls_store_session } { + glib/gtlssessioncache_client + Memcheck:Leak + ... + fun:gnutls_session_get_data2 + fun:g_tls_client_connection_gnutls_finish_handshake +} +{ + glib/gtlssessioncache_client2 + Memcheck:Leak + ... + fun:g_bytes_new_with_free_func + fun:g_tls_client_connection_gnutls_finish_handshake +} +{ + glib/gtlssessioncache_client3 + Memcheck:Leak + ... + fun:g_bytes_new_take + fun:g_tls_client_connection_gnutls_constructed +} +{ + glib/gtlssessioncache_client4 + Memcheck:Leak + ... + fun:g_strdup_printf + fun:g_tls_client_connection_gnutls_constructed +} +{ + glib/gtlssessioncache_server + Memcheck:Leak + ... + fun:g_tls_server_connection_gnutls_db_store +} +{ glib/cached_poll_array Memcheck:Leak ... fun:g_malloc_n fun:g_main_context_iterate } +{ + glib/rand + Memcheck:Leak + ... + fun:g_rand_new + fun:g_random_int_range +} +{ + glib/g_cancellable_push_current + Memcheck:Leak + ... + fun:g_cancellable_push_current +} +{ + glib/slice_thread_local + Memcheck:Leak + ... + fun:thread_memory_from_self +} +{ + glib/gobjectinit + Memcheck:Leak + ... + fun:gobject_init_ctor +} +{ + glib/gtask threadpool + Memcheck:Leak + ... + fun:g_thread_pool_new + fun:g_task_thread_pool_init +} # probably inlines the aggressive memcpy/memcmp { @@ -421,6 +487,12 @@ ... fun:intern_header_name } +{ + libsoup/tlds + Memcheck:Leak + ... + fun:soup_tld_ensure_rules_hash_table +} # fixme? diff --git a/tests/misc-test.c b/tests/misc-test.c index 605fa216..00559a03 100644 --- a/tests/misc-test.c +++ b/tests/misc-test.c @@ -32,16 +32,6 @@ server_callback (SoupServer *server, SoupMessage *msg, SoupURI *uri = soup_message_get_uri (msg); const char *server_protocol = data; - soup_message_headers_append (msg->response_headers, - "X-Handled-By", "server_callback"); - - if (!strcmp (path, "*")) { - debug_printf (1, " default server_callback got request for '*'!\n"); - errors++; - soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR); - return; - } - if (msg->method != SOUP_METHOD_GET && msg->method != SOUP_METHOD_POST) { soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED); return; @@ -99,32 +89,8 @@ server_callback (SoupServer *server, SoupMessage *msg, } } -static void -server_star_callback (SoupServer *server, SoupMessage *msg, - const char *path, GHashTable *query, - SoupClientContext *context, gpointer data) -{ - soup_message_headers_append (msg->response_headers, - "X-Handled-By", "star_callback"); - - if (strcmp (path, "*") != 0) { - debug_printf (1, " server_star_callback got request for '%s'!\n", path); - errors++; - soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR); - return; - } - - if (msg->method != SOUP_METHOD_OPTIONS) { - soup_message_set_status (msg, SOUP_STATUS_METHOD_NOT_ALLOWED); - return; - } - - soup_message_set_status (msg, SOUP_STATUS_OK); -} - /* Host header handling: client must be able to override the default * value, server must be able to recognize different Host values. - * #539803. */ static void do_host_test (void) @@ -132,7 +98,7 @@ do_host_test (void) SoupSession *session; SoupMessage *one, *two; - debug_printf (1, "Host handling\n"); + g_test_bug ("539803"); session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL); @@ -145,42 +111,24 @@ do_host_test (void) soup_test_session_abort_unref (session); - if (!SOUP_STATUS_IS_SUCCESSFUL (one->status_code)) { - debug_printf (1, " Message 1 failed: %d %s\n", - one->status_code, one->reason_phrase); - errors++; - } else if (strcmp (one->response_body->data, "index") != 0) { - debug_printf (1, " Unexpected response to message 1: '%s'\n", - one->response_body->data); - errors++; - } + soup_test_assert_message_status (one, SOUP_STATUS_OK); + g_assert_cmpstr (one->response_body->data, ==, "index"); g_object_unref (one); - if (!SOUP_STATUS_IS_SUCCESSFUL (two->status_code)) { - debug_printf (1, " Message 2 failed: %d %s\n", - two->status_code, two->reason_phrase); - errors++; - } else if (strcmp (two->response_body->data, "foo-index") != 0) { - debug_printf (1, " Unexpected response to message 2: '%s'\n", - two->response_body->data); - errors++; - } + soup_test_assert_message_status (two, SOUP_STATUS_OK); + g_assert_cmpstr (two->response_body->data, ==, "foo-index"); g_object_unref (two); } /* Dropping the application's ref on the session from a callback * should not cause the session to be freed at an incorrect time. - * (This test will crash if it fails.) #533473 + * (This test will crash if it fails.) */ static void cu_one_completed (SoupSession *session, SoupMessage *msg, gpointer loop) { debug_printf (2, " Message 1 completed\n"); - if (msg->status_code != SOUP_STATUS_CANT_CONNECT) { - debug_printf (1, " Unexpected status on Message 1: %d %s\n", - msg->status_code, msg->reason_phrase); - errors++; - } + soup_test_assert_message_status (msg, SOUP_STATUS_CANT_CONNECT); g_object_unref (session); } @@ -195,11 +143,7 @@ static void cu_two_completed (SoupSession *session, SoupMessage *msg, gpointer loop) { debug_printf (2, " Message 2 completed\n"); - if (msg->status_code != SOUP_STATUS_CANT_CONNECT) { - debug_printf (1, " Unexpected status on Message 2: %d %s\n", - msg->status_code, msg->reason_phrase); - errors++; - } + soup_test_assert_message_status (msg, SOUP_STATUS_CANT_CONNECT); g_idle_add (cu_idle_quit, loop); } @@ -213,7 +157,7 @@ do_callback_unref_test (void) GMainLoop *loop; char *bad_uri; - debug_printf (1, "\nCallback unref handling (msg api)\n"); + g_test_bug ("533473"); /* Get a guaranteed-bad URI */ addr = soup_address_new ("127.0.0.1", SOUP_ADDRESS_ANY_PORT); @@ -243,22 +187,19 @@ do_callback_unref_test (void) g_main_loop_run (loop); g_main_loop_unref (loop); + g_assert_null (session); if (session) { g_object_remove_weak_pointer (G_OBJECT (session), (gpointer *)&session); - debug_printf (1, " Session not destroyed?\n"); - errors++; g_object_unref (session); } + g_assert_null (one); if (one) { g_object_remove_weak_pointer (G_OBJECT (one), (gpointer *)&one); - debug_printf (1, " Message 1 not destroyed?\n"); - errors++; g_object_unref (one); } + g_assert_null (two); if (two) { g_object_remove_weak_pointer (G_OBJECT (two), (gpointer *)&two); - debug_printf (1, " Message 2 not destroyed?\n"); - errors++; g_object_unref (two); } @@ -272,14 +213,8 @@ cur_one_completed (GObject *source, GAsyncResult *result, gpointer session) GError *error = NULL; debug_printf (2, " Request 1 completed\n"); - if (soup_request_send_finish (one, result, &error)) { - debug_printf (1, " Request 1 succeeded?\n"); - errors++; - } else if (!g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_CANT_CONNECT)) { - debug_printf (1, " Unexpected error on Request 1: %s\n", - error->message); - errors++; - } + soup_request_send_finish (one, result, &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CONNECTION_REFUSED); g_clear_error (&error); g_object_unref (session); @@ -299,14 +234,8 @@ cur_two_completed (GObject *source, GAsyncResult *result, gpointer loop) GError *error = NULL; debug_printf (2, " Request 2 completed\n"); - if (soup_request_send_finish (two, result, &error)) { - debug_printf (1, " Request 2 succeeded?\n"); - errors++; - } else if (!g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_CANT_CONNECT)) { - debug_printf (1, " Unexpected error on Request 2: %s\n", - error->message); - errors++; - } + soup_request_send_finish (two, result, &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CONNECTION_REFUSED); g_clear_error (&error); g_idle_add (cur_idle_quit, loop); @@ -318,13 +247,10 @@ do_callback_unref_req_test (void) SoupServer *bad_server; SoupAddress *addr; SoupSession *session; - SoupRequester *requester; SoupRequest *one, *two; GMainLoop *loop; char *bad_uri; - debug_printf (1, "\nCallback unref handling (request api)\n"); - /* Get a guaranteed-bad URI */ addr = soup_address_new ("127.0.0.1", SOUP_ADDRESS_ANY_PORT); soup_address_resolve_sync (addr, NULL); @@ -337,18 +263,15 @@ do_callback_unref_req_test (void) g_object_unref (bad_server); session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, - SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_REQUESTER, SOUP_SESSION_USE_THREAD_CONTEXT, TRUE, NULL); g_object_add_weak_pointer (G_OBJECT (session), (gpointer *)&session); - requester = (SoupRequester *)soup_session_get_feature (session, SOUP_TYPE_REQUESTER); - loop = g_main_loop_new (NULL, TRUE); - one = soup_requester_request (requester, bad_uri, NULL); + one = soup_session_request (session, bad_uri, NULL); g_object_add_weak_pointer (G_OBJECT (one), (gpointer *)&one); - two = soup_requester_request (requester, bad_uri, NULL); + two = soup_session_request (session, bad_uri, NULL); g_object_add_weak_pointer (G_OBJECT (two), (gpointer *)&two); g_free (bad_uri); @@ -360,22 +283,19 @@ do_callback_unref_req_test (void) g_main_loop_run (loop); g_main_loop_unref (loop); + g_assert_null (session); if (session) { g_object_remove_weak_pointer (G_OBJECT (session), (gpointer *)&session); - debug_printf (1, " Session not destroyed?\n"); - errors++; g_object_unref (session); } + g_assert_null (one); if (one) { g_object_remove_weak_pointer (G_OBJECT (one), (gpointer *)&one); - debug_printf (1, " Request 1 not destroyed?\n"); - errors++; g_object_unref (one); } + g_assert_null (two); if (two) { g_object_remove_weak_pointer (G_OBJECT (two), (gpointer *)&two); - debug_printf (1, " Request 2 not destroyed?\n"); - errors++; g_object_unref (two); } @@ -384,20 +304,19 @@ do_callback_unref_req_test (void) /* SoupSession should clean up all signal handlers on a message after * it is finished, allowing the message to be reused if desired. - * #559054 */ static void ensure_no_signal_handlers (SoupMessage *msg, guint *signal_ids, guint n_signal_ids) { int i; + guint id; for (i = 0; i < n_signal_ids; i++) { - if (g_signal_handler_find (msg, G_SIGNAL_MATCH_ID, signal_ids[i], - 0, NULL, NULL, NULL)) { - debug_printf (1, " Message has handler for '%s'\n", - g_signal_name (signal_ids[i])); - errors++; - } + id = g_signal_handler_find (msg, G_SIGNAL_MATCH_ID, signal_ids[i], + 0, NULL, NULL, NULL); + soup_test_assert (id == 0, + "message has handler for '%s'", + g_signal_name (signal_ids[i])); } } @@ -420,7 +339,7 @@ do_msg_reuse_test (void) SoupURI *uri; guint *signal_ids, n_signal_ids; - debug_printf (1, "\nSoupMessage reuse\n"); + g_test_bug ("559054"); signal_ids = g_signal_list_ids (SOUP_TYPE_MESSAGE, &n_signal_ids); @@ -438,10 +357,7 @@ do_msg_reuse_test (void) soup_message_set_uri (msg, uri); soup_uri_free (uri); soup_session_send_message (session, msg); - if (!soup_uri_equal (soup_message_get_uri (msg), base_uri)) { - debug_printf (1, " Message did not get redirected!\n"); - errors++; - } + g_assert_true (soup_uri_equal (soup_message_get_uri (msg), base_uri)); ensure_no_signal_handlers (msg, signal_ids, n_signal_ids); debug_printf (1, " Auth message\n"); @@ -449,10 +365,7 @@ do_msg_reuse_test (void) soup_message_set_uri (msg, uri); soup_uri_free (uri); soup_session_send_message (session, msg); - if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) { - debug_printf (1, " Message did not get authenticated!\n"); - errors++; - } + soup_test_assert_message_status (msg, SOUP_STATUS_OK); ensure_no_signal_handlers (msg, signal_ids, n_signal_ids); /* One last try to make sure the auth stuff got cleaned up */ @@ -466,79 +379,12 @@ do_msg_reuse_test (void) g_free (signal_ids); } -/* Server handlers for "*" work but are separate from handlers for - * all other URIs. #590751 - */ -static void -do_star_test (void) -{ - SoupSession *session; - SoupMessage *msg; - SoupURI *star_uri; - const char *handled_by; - - debug_printf (1, "\nOPTIONS *\n"); - - session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL); - star_uri = soup_uri_copy (base_uri); - soup_uri_set_path (star_uri, "*"); - - debug_printf (1, " Testing with no handler\n"); - msg = soup_message_new_from_uri ("OPTIONS", star_uri); - soup_session_send_message (session, msg); - - if (msg->status_code != SOUP_STATUS_NOT_FOUND) { - debug_printf (1, " Unexpected response: %d %s\n", - msg->status_code, msg->reason_phrase); - errors++; - } - handled_by = soup_message_headers_get_one (msg->response_headers, - "X-Handled-By"); - if (handled_by) { - /* Should have been rejected by SoupServer directly */ - debug_printf (1, " Message reached handler '%s'\n", - handled_by); - errors++; - } - g_object_unref (msg); - - soup_server_add_handler (server, "*", server_star_callback, NULL, NULL); - - debug_printf (1, " Testing with handler\n"); - msg = soup_message_new_from_uri ("OPTIONS", star_uri); - soup_session_send_message (session, msg); - - if (msg->status_code != SOUP_STATUS_OK) { - debug_printf (1, " Unexpected response: %d %s\n", - msg->status_code, msg->reason_phrase); - errors++; - } - handled_by = soup_message_headers_get_one (msg->response_headers, - "X-Handled-By"); - if (!handled_by) { - debug_printf (1, " Message did not reach handler!\n"); - errors++; - } else if (strcmp (handled_by, "star_callback") != 0) { - debug_printf (1, " Message reached incorrect handler '%s'\n", - handled_by); - errors++; - } - g_object_unref (msg); - - soup_test_session_abort_unref (session); - soup_uri_free (star_uri); -} - -/* Handle unexpectedly-early aborts. #596074, #618641 */ +/* Handle unexpectedly-early aborts. */ static void ea_msg_completed_one (SoupSession *session, SoupMessage *msg, gpointer loop) { debug_printf (2, " Message 1 completed\n"); - if (msg->status_code != SOUP_STATUS_CANCELLED) { - debug_printf (1, " Unexpected status on Message 1: %d %s\n", - msg->status_code, msg->reason_phrase); - errors++; - } + soup_test_assert_message_status (msg, SOUP_STATUS_CANCELLED); g_main_loop_quit (loop); } @@ -585,7 +431,8 @@ do_early_abort_test (void) GMainContext *context; GMainLoop *loop; - debug_printf (1, "\nAbort with pending connection (msg api)\n"); + g_test_bug ("596074"); + g_test_bug ("618641"); session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); msg = soup_message_new_from_uri ("GET", base_uri); @@ -609,11 +456,7 @@ do_early_abort_test (void) soup_session_send_message (session, msg); debug_printf (2, " Message 2 completed\n"); - if (msg->status_code != SOUP_STATUS_CANCELLED) { - debug_printf (1, " Unexpected response: %d %s\n", - msg->status_code, msg->reason_phrase); - errors++; - } + soup_test_assert_message_status (msg, SOUP_STATUS_CANCELLED); g_object_unref (msg); while (g_main_context_pending (context)) @@ -621,6 +464,8 @@ do_early_abort_test (void) soup_test_session_abort_unref (session); + g_test_bug ("668098"); + session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); msg = soup_message_new_from_uri ("GET", base_uri); @@ -629,11 +474,7 @@ do_early_abort_test (void) soup_session_send_message (session, msg); debug_printf (2, " Message 3 completed\n"); - if (msg->status_code != SOUP_STATUS_CANCELLED) { - debug_printf (1, " Unexpected response: %d %s\n", - msg->status_code, msg->reason_phrase); - errors++; - } + soup_test_assert_message_status (msg, SOUP_STATUS_CANCELLED); g_object_unref (msg); while (g_main_context_pending (context)) @@ -648,14 +489,8 @@ ear_one_completed (GObject *source, GAsyncResult *result, gpointer user_data) GError *error = NULL; debug_printf (2, " Request 1 completed\n"); - if (soup_request_send_finish (SOUP_REQUEST (source), result, &error)) { - debug_printf (1, " Request 1 succeeded?\n"); - errors++; - } else if (!g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_CANCELLED)) { - debug_printf (1, " Unexpected error on Request 1: %s\n", - error->message); - errors++; - } + soup_request_send_finish (SOUP_REQUEST (source), result, &error); + g_assert_error (error, SOUP_HTTP_ERROR, SOUP_STATUS_CANCELLED); g_clear_error (&error); } @@ -665,14 +500,8 @@ ear_two_completed (GObject *source, GAsyncResult *result, gpointer loop) GError *error = NULL; debug_printf (2, " Request 2 completed\n"); - if (soup_request_send_finish (SOUP_REQUEST (source), result, &error)) { - debug_printf (1, " Request 2 succeeded?\n"); - errors++; - } else if (!g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_CANCELLED)) { - debug_printf (1, " Unexpected error on Request 2: %s\n", - error->message); - errors++; - } + soup_request_send_finish (SOUP_REQUEST (source), result, &error); + g_assert_error (error, SOUP_HTTP_ERROR, SOUP_STATUS_CANCELLED); g_clear_error (&error); g_main_loop_quit (loop); @@ -684,14 +513,8 @@ ear_three_completed (GObject *source, GAsyncResult *result, gpointer loop) GError *error = NULL; debug_printf (2, " Request 3 completed\n"); - if (soup_request_send_finish (SOUP_REQUEST (source), result, &error)) { - debug_printf (1, " Request 3 succeeded?\n"); - errors++; - } else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { - debug_printf (1, " Unexpected error on Request 3: %s\n", - error->message); - errors++; - } + soup_request_send_finish (SOUP_REQUEST (source), result, &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); g_clear_error (&error); g_main_loop_quit (loop); @@ -708,20 +531,15 @@ static void do_early_abort_req_test (void) { SoupSession *session; - SoupRequester *requester; SoupRequest *req; GMainContext *context; GMainLoop *loop; GCancellable *cancellable; - debug_printf (1, "\nAbort with pending connection (request api)\n"); - session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, - SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_REQUESTER, SOUP_SESSION_USE_THREAD_CONTEXT, TRUE, NULL); - requester = (SoupRequester *)soup_session_get_feature (session, SOUP_TYPE_REQUESTER); - req = soup_requester_request_uri (requester, base_uri, NULL); + req = soup_session_request_uri (session, base_uri, NULL); context = g_main_context_default (); loop = g_main_loop_new (context, TRUE); @@ -735,11 +553,9 @@ do_early_abort_req_test (void) soup_test_session_abort_unref (session); session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, - SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_REQUESTER, SOUP_SESSION_USE_THREAD_CONTEXT, TRUE, NULL); - requester = (SoupRequester *)soup_session_get_feature (session, SOUP_TYPE_REQUESTER); - req = soup_requester_request_uri (requester, base_uri, NULL); + req = soup_session_request_uri (session, base_uri, NULL); g_signal_connect (session, "connection-created", G_CALLBACK (ea_connection_created), NULL); @@ -753,11 +569,9 @@ do_early_abort_req_test (void) soup_test_session_abort_unref (session); session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, - SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_REQUESTER, SOUP_SESSION_USE_THREAD_CONTEXT, TRUE, NULL); - requester = (SoupRequester *)soup_session_get_feature (session, SOUP_TYPE_REQUESTER); - req = soup_requester_request_uri (requester, base_uri, NULL); + req = soup_session_request_uri (session, base_uri, NULL); cancellable = g_cancellable_new (); g_signal_connect (session, "request-started", @@ -790,21 +604,10 @@ do_one_accept_language_test (const char *language, const char *expected_header) soup_session_send_message (session, msg); soup_test_session_abort_unref (session); - if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) { - debug_printf (1, " Message failed? %d %s\n", - msg->status_code, msg->reason_phrase); - errors++; - } + soup_test_assert_message_status (msg, SOUP_STATUS_OK); val = soup_message_headers_get_list (msg->request_headers, "Accept-Language"); - if (!val) { - debug_printf (1, " No Accept-Language set!\n"); - errors++; - } else if (strcmp (val, expected_header) != 0) { - debug_printf (1, " Wrong Accept-Language: expected '%s', got '%s'\n", - expected_header, val); - errors++; - } + g_assert_cmpstr (val, ==, expected_header); g_object_unref (msg); } @@ -814,7 +617,7 @@ do_accept_language_test (void) { const char *orig_language; - debug_printf (1, "\nAutomatic Accept-Language processing\n"); + g_test_bug ("602547"); orig_language = g_getenv ("LANGUAGE"); do_one_accept_language_test ("C", "en"); @@ -887,11 +690,7 @@ do_cancel_while_reading_test_for_session (SoupSession *session) while (!done) g_main_context_iteration (NULL, TRUE); - if (msg->status_code != SOUP_STATUS_CANCELLED) { - debug_printf (1, " FAILED: %d %s (expected Cancelled)\n", - msg->status_code, msg->reason_phrase); - errors++; - } + soup_test_assert_message_status (msg, SOUP_STATUS_CANCELLED); g_object_unref (msg); if (thread) @@ -903,7 +702,8 @@ do_cancel_while_reading_test (void) { SoupSession *session; - debug_printf (1, "\nCancelling message while reading response (msg api)\n"); + g_test_bug ("637741"); + g_test_bug ("676038"); debug_printf (1, " Async session\n"); session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); @@ -916,23 +716,9 @@ do_cancel_while_reading_test (void) soup_test_session_abort_unref (session); } -static gboolean -cancel_request_timeout (gpointer cancellable) -{ - g_cancellable_cancel (cancellable); - return FALSE; -} - -static gpointer -cancel_request_thread (gpointer cancellable) -{ - g_usleep (100000); /* .1s */ - g_cancellable_cancel (cancellable); - return NULL; -} - static void -do_cancel_while_reading_req_test_for_session (SoupRequester *requester) +do_cancel_while_reading_req_test_for_session (SoupSession *session, + guint flags) { SoupRequest *req; SoupURI *uri; @@ -940,30 +726,12 @@ do_cancel_while_reading_req_test_for_session (SoupRequester *requester) GError *error = NULL; uri = soup_uri_new_with_base (base_uri, "/slow"); - req = soup_requester_request_uri (requester, uri, NULL); + req = soup_session_request_uri (session, uri, NULL); soup_uri_free (uri); cancellable = g_cancellable_new (); - - if (SOUP_IS_SESSION_ASYNC (soup_request_get_session (req))) { - g_timeout_add (100, cancel_request_timeout, cancellable); - soup_test_request_send_async_as_sync (req, cancellable, &error); - } else { - GThread *thread; - - thread = g_thread_new ("cancel_request_thread", cancel_request_thread, cancellable); - soup_request_send (req, cancellable, &error); - g_thread_unref (thread); - } - - if (!error) { - debug_printf (1, " Request succeeded?\n"); - errors++; - } else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { - debug_printf (1, " Unexpected error: %s\n", - error->message); - errors++; - } + soup_test_request_send (req, cancellable, flags, &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); g_clear_error (&error); g_object_unref (req); @@ -971,169 +739,128 @@ do_cancel_while_reading_req_test_for_session (SoupRequester *requester) } static void -do_cancel_while_reading_req_test (void) +do_cancel_while_reading_immediate_req_test (void) { SoupSession *session; - SoupRequester *requester; + guint flags; - debug_printf (1, "\nCancelling message while reading response (request api)\n"); + g_test_bug ("692310"); + + flags = SOUP_TEST_REQUEST_CANCEL_CANCELLABLE | SOUP_TEST_REQUEST_CANCEL_IMMEDIATE; debug_printf (1, " Async session\n"); session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, - SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_REQUESTER, SOUP_SESSION_USE_THREAD_CONTEXT, TRUE, NULL); - requester = (SoupRequester *)soup_session_get_feature (session, SOUP_TYPE_REQUESTER); - do_cancel_while_reading_req_test_for_session (requester); + do_cancel_while_reading_req_test_for_session (session, flags); soup_test_session_abort_unref (session); debug_printf (1, " Sync session\n"); session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, - SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_REQUESTER, NULL); - requester = (SoupRequester *)soup_session_get_feature (session, SOUP_TYPE_REQUESTER); - do_cancel_while_reading_req_test_for_session (requester); + do_cancel_while_reading_req_test_for_session (session, flags); soup_test_session_abort_unref (session); } static void -do_aliases_test_for_session (SoupSession *session, - const char *redirect_protocol) +do_cancel_while_reading_delayed_req_test (void) { - SoupMessage *msg; - SoupURI *uri; - const char *redirected_protocol; - - uri = soup_uri_new_with_base (base_uri, "/alias-redirect"); - msg = soup_message_new_from_uri ("GET", uri); - if (redirect_protocol) - soup_message_headers_append (msg->request_headers, "X-Redirect-Protocol", redirect_protocol); - soup_uri_free (uri); - soup_session_send_message (session, msg); + SoupSession *session; + guint flags; - redirected_protocol = soup_message_headers_get_one (msg->response_headers, "X-Redirected-Protocol"); + flags = SOUP_TEST_REQUEST_CANCEL_CANCELLABLE | SOUP_TEST_REQUEST_CANCEL_SOON; - if (g_strcmp0 (redirect_protocol, redirected_protocol)) { - debug_printf (1, " redirect went to %s, should have gone to %s!\n", - redirected_protocol ? redirected_protocol : "(none)", - redirect_protocol ? redirect_protocol : "(none)"); - errors++; - } else if (redirect_protocol && !SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) { - debug_printf (1, " msg failed? (%d %s)\n", - msg->status_code, msg->reason_phrase); - errors++; - } else if (!redirect_protocol && SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) { - debug_printf (1, " msg succeeded? (%d %s)\n", - msg->status_code, msg->reason_phrase); - errors++; - } + debug_printf (1, " Async session\n"); + session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, + SOUP_SESSION_USE_THREAD_CONTEXT, TRUE, + NULL); + do_cancel_while_reading_req_test_for_session (session, flags); + soup_test_session_abort_unref (session); - g_object_unref (msg); + debug_printf (1, " Sync session\n"); + session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, + NULL); + do_cancel_while_reading_req_test_for_session (session, flags); + soup_test_session_abort_unref (session); } static void -do_aliases_test (void) +do_cancel_while_reading_preemptive_req_test (void) { SoupSession *session; - char *aliases[] = { "foo", NULL }; + guint flags; - debug_printf (1, "\nhttp-aliases / https-aliases\n"); + g_test_bug ("637039"); - debug_printf (1, " Default behavior\n"); - session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); - do_aliases_test_for_session (session, "http"); - soup_test_session_abort_unref (session); + flags = SOUP_TEST_REQUEST_CANCEL_CANCELLABLE | SOUP_TEST_REQUEST_CANCEL_PREEMPTIVE; - debug_printf (1, " foo-means-https\n"); + debug_printf (1, " Async session\n"); session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, - SOUP_SESSION_HTTPS_ALIASES, aliases, + SOUP_SESSION_USE_THREAD_CONTEXT, TRUE, NULL); - do_aliases_test_for_session (session, "https"); + do_cancel_while_reading_req_test_for_session (session, flags); soup_test_session_abort_unref (session); - debug_printf (1, " foo-means-nothing\n"); - session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, - SOUP_SESSION_HTTP_ALIASES, NULL, + debug_printf (1, " Sync session\n"); + session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL); - do_aliases_test_for_session (session, NULL); + do_cancel_while_reading_req_test_for_session (session, flags); soup_test_session_abort_unref (session); } static void -do_dot_dot_test (void) +do_aliases_test_for_session (SoupSession *session, + const char *redirect_protocol) { - SoupSession *session; SoupMessage *msg; SoupURI *uri; + const char *redirected_protocol; - debug_printf (1, "\n'..' smuggling test\n"); - - session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL); - - uri = soup_uri_new_with_base (base_uri, "/..%2ftest"); + uri = soup_uri_new_with_base (base_uri, "/alias-redirect"); msg = soup_message_new_from_uri ("GET", uri); + if (redirect_protocol) + soup_message_headers_append (msg->request_headers, "X-Redirect-Protocol", redirect_protocol); soup_uri_free (uri); - soup_session_send_message (session, msg); - if (msg->status_code != SOUP_STATUS_BAD_REQUEST) { - debug_printf (1, " FAILED: %d %s (expected Bad Request)\n", - msg->status_code, msg->reason_phrase); - errors++; - } - g_object_unref (msg); + redirected_protocol = soup_message_headers_get_one (msg->response_headers, "X-Redirected-Protocol"); - soup_test_session_abort_unref (session); + g_assert_cmpstr (redirect_protocol, ==, redirected_protocol); + if (redirect_protocol) + soup_test_assert_message_status (msg, SOUP_STATUS_OK); + else + soup_test_assert_message_status (msg, SOUP_STATUS_FOUND); + + g_object_unref (msg); } static void -do_ipv6_test (void) +do_aliases_test (void) { - SoupServer *ipv6_server; - SoupURI *ipv6_uri; - SoupAddress *ipv6_addr; SoupSession *session; - SoupMessage *msg; - - debug_printf (1, "\nIPv6 server test\n"); - - ipv6_addr = soup_address_new ("::1", SOUP_ADDRESS_ANY_PORT); - soup_address_resolve_sync (ipv6_addr, NULL); - ipv6_server = soup_server_new (SOUP_SERVER_INTERFACE, ipv6_addr, - NULL); - g_object_unref (ipv6_addr); - soup_server_add_handler (ipv6_server, NULL, server_callback, NULL, NULL); - soup_server_run_async (ipv6_server); - - ipv6_uri = soup_uri_new ("http://[::1]/"); - soup_uri_set_port (ipv6_uri, soup_server_get_port (ipv6_server)); + char *aliases[] = { "foo", NULL }; + debug_printf (1, " Default behavior\n"); session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); + do_aliases_test_for_session (session, "http"); + soup_test_session_abort_unref (session); - debug_printf (1, " HTTP/1.1\n"); - msg = soup_message_new_from_uri ("GET", ipv6_uri); - soup_session_send_message (session, msg); - if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) { - debug_printf (1, " request failed: %d %s\n", - msg->status_code, msg->reason_phrase); - errors++; - } - g_object_unref (msg); - - debug_printf (1, " HTTP/1.0\n"); - msg = soup_message_new_from_uri ("GET", ipv6_uri); - soup_message_set_http_version (msg, SOUP_HTTP_1_0); - soup_session_send_message (session, msg); - if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) { - debug_printf (1, " request failed: %d %s\n", - msg->status_code, msg->reason_phrase); - errors++; - } - g_object_unref (msg); + if (tls_available) { + debug_printf (1, " foo-means-https\n"); + session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, + SOUP_SESSION_HTTPS_ALIASES, aliases, + NULL); + do_aliases_test_for_session (session, "https"); + soup_test_session_abort_unref (session); + } else + debug_printf (1, " foo-means-https -- SKIPPING\n"); - soup_uri_free (ipv6_uri); + debug_printf (1, " foo-means-nothing\n"); + session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, + SOUP_SESSION_HTTP_ALIASES, NULL, + NULL); + do_aliases_test_for_session (session, NULL); soup_test_session_abort_unref (session); - soup_test_server_quit_unref (ipv6_server); } static void @@ -1143,7 +870,7 @@ do_idle_on_dispose_test (void) SoupMessage *msg; GMainContext *async_context; - debug_printf (1, "\nTesting SoupSessionAsync dispose behavior\n"); + g_test_bug ("667364"); async_context = g_main_context_new (); session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, @@ -1159,10 +886,8 @@ do_idle_on_dispose_test (void) g_object_run_dispose (G_OBJECT (session)); - if (g_main_context_iteration (async_context, FALSE)) { - debug_printf (1, " idle was queued!\n"); - errors++; - } + if (g_main_context_iteration (async_context, FALSE)) + soup_test_assert (FALSE, "idle was queued"); g_object_unref (session); g_main_context_unref (async_context); @@ -1175,7 +900,7 @@ do_pause_abort_test (void) SoupMessage *msg; gpointer ptr; - debug_printf (1, "\nTesting paused messages don't get leaked on abort\n"); + g_test_bug ("673905"); session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); @@ -1186,16 +911,14 @@ do_pause_abort_test (void) g_object_add_weak_pointer (G_OBJECT (msg), &ptr); soup_test_session_abort_unref (session); - if (ptr) { - debug_printf (1, " msg was leaked\n"); - errors++; - } + g_assert_null (ptr); } int main (int argc, char **argv) { SoupAuthDomain *auth_domain; + int ret; test_init (argc, argv, NULL); @@ -1212,32 +935,37 @@ main (int argc, char **argv) soup_server_add_auth_domain (server, auth_domain); g_object_unref (auth_domain); - ssl_server = soup_test_server_new_ssl (TRUE); - soup_server_add_handler (ssl_server, NULL, server_callback, "https", NULL); - ssl_base_uri = soup_uri_new ("https://127.0.0.1/"); - soup_uri_set_port (ssl_base_uri, soup_server_get_port (ssl_server)); - - do_host_test (); - do_callback_unref_test (); - do_callback_unref_req_test (); - do_msg_reuse_test (); - do_star_test (); - do_early_abort_test (); - do_early_abort_req_test (); - do_accept_language_test (); - do_cancel_while_reading_test (); - do_cancel_while_reading_req_test (); - do_aliases_test (); - do_dot_dot_test (); - do_ipv6_test (); - do_idle_on_dispose_test (); - do_pause_abort_test (); + if (tls_available) { + ssl_server = soup_test_server_new_ssl (TRUE); + soup_server_add_handler (ssl_server, NULL, server_callback, "https", NULL); + ssl_base_uri = soup_uri_new ("https://127.0.0.1/"); + soup_uri_set_port (ssl_base_uri, soup_server_get_port (ssl_server)); + } + + g_test_add_func ("/misc/host", do_host_test); + g_test_add_func ("/misc/callback-unref/msg", do_callback_unref_test); + g_test_add_func ("/misc/callback-unref/req", do_callback_unref_req_test); + g_test_add_func ("/misc/msg-reuse", do_msg_reuse_test); + g_test_add_func ("/misc/early-abort/msg", do_early_abort_test); + g_test_add_func ("/misc/early-abort/req", do_early_abort_req_test); + g_test_add_func ("/misc/accept-language", do_accept_language_test); + g_test_add_func ("/misc/cancel-while-reading/msg", do_cancel_while_reading_test); + g_test_add_func ("/misc/cancel-while-reading/req/immediate", do_cancel_while_reading_immediate_req_test); + g_test_add_func ("/misc/cancel-while-reading/req/delayed", do_cancel_while_reading_delayed_req_test); + g_test_add_func ("/misc/cancel-while-reading/req/preemptive", do_cancel_while_reading_preemptive_req_test); + g_test_add_func ("/misc/aliases", do_aliases_test); + g_test_add_func ("/misc/idle-on-dispose", do_idle_on_dispose_test); + g_test_add_func ("/misc/pause-abort", do_pause_abort_test); + + ret = g_test_run (); soup_uri_free (base_uri); - soup_uri_free (ssl_base_uri); soup_test_server_quit_unref (server); - soup_test_server_quit_unref (ssl_server); - test_cleanup (); - return errors != 0; + if (tls_available) { + soup_uri_free (ssl_base_uri); + soup_test_server_quit_unref (ssl_server); + } + + return ret; } diff --git a/tests/multipart-test.c b/tests/multipart-test.c new file mode 100644 index 00000000..e057412e --- /dev/null +++ b/tests/multipart-test.c @@ -0,0 +1,521 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2011 Collabora Ltd. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "test-utils.h" + +#define READ_BUFFER_SIZE 8192 + +typedef enum { + NO_MULTIPART, + SYNC_MULTIPART, + ASYNC_MULTIPART, + ASYNC_MULTIPART_SMALL_READS +} MultipartMode; + +char *buffer; +SoupSession *session; +char *base_uri_string; +SoupURI *base_uri; +SoupMultipartInputStream *multipart; +unsigned passes; + + +/* This payload contains 4 different responses. + * + * First, a text/html response with a Content-Length (31); + * Second, a response lacking Content-Type with Content-Length (11); + * Third, a text/css response with no Content-Length; + * Fourth, same as the third, but with different content; + */ +const char *payload = \ + "--cut-here\r\n" \ + "Content-Type: text/html\n" + "Content-Length: 30\r\n" \ + "\r\n" \ + "<html><body>Hey!</body></html>" \ + "\r\n--cut-here\r\n" \ + "Content-Length: 10\r\n" \ + "\r\n" \ + "soup rocks" \ + "\r\n--cut-here\r\n" \ + "Content-Type: text/css\r\n" \ + "\r\n" \ + ".soup { before: rocks; }" \ + "\r\n--cut-here\n" /* Tests boundary ending in a single \n. */ \ + "Content-Type: text/css\r\n" \ + "\r\n" \ + "#soup { background-color: black; }" \ + "\r\n--cut-here--"; + +static void +server_callback (SoupServer *server, SoupMessage *msg, + const char *path, GHashTable *query, + SoupClientContext *context, gpointer data) +{ + if (msg->method != SOUP_METHOD_GET) { + soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED); + return; + } + + soup_message_set_status (msg, SOUP_STATUS_OK); + + soup_message_headers_append (msg->response_headers, + "Content-Type", "multipart/x-mixed-replace; boundary=cut-here"); + + soup_message_body_append (msg->response_body, + SOUP_MEMORY_STATIC, + payload, + strlen (payload)); + + soup_message_body_complete (msg->response_body); +} + +static void +content_sniffed (SoupMessage *msg, char *content_type, GHashTable *params, int *sniffed_count) +{ + *sniffed_count = *sniffed_count + 1; + debug_printf (2, " content-sniffed -> %s\n", content_type); +} + +static void +check_is_next (gboolean is_next) +{ + soup_test_assert (is_next, + "expected a header, but there are no more headers"); +} + +static void +got_headers (SoupMessage *msg, int *headers_count) +{ + SoupMessageHeadersIter iter; + gboolean is_next; + const char* name, *value; + + *headers_count = *headers_count + 1; + + soup_message_headers_iter_init (&iter, msg->response_headers); + + is_next = soup_message_headers_iter_next (&iter, &name, &value); + check_is_next (is_next); + + if (g_str_equal (name, "Date")) { + is_next = soup_message_headers_iter_next (&iter, &name, &value); + check_is_next (is_next); + } + + g_assert_cmpstr (name, ==, "Content-Type"); + g_assert_cmpstr (value, ==, "multipart/x-mixed-replace; boundary=cut-here"); +} + +static void +read_cb (GObject *source, GAsyncResult *asyncResult, gpointer data) +{ + GMainLoop *loop = (GMainLoop*)data; + GInputStream *stream = G_INPUT_STREAM (source); + GError *error = NULL; + gssize bytes_read; + + bytes_read = g_input_stream_read_finish (stream, asyncResult, &error); + g_assert_no_error (error); + if (error) { + g_object_unref (stream); + g_main_loop_quit (loop); + return; + } + + if (!bytes_read) { + g_input_stream_close (stream, NULL, &error); + g_assert_no_error (error); + g_object_unref (stream); + g_main_loop_quit (loop); + return; + } + + g_input_stream_read_async (stream, buffer, READ_BUFFER_SIZE, + G_PRIORITY_DEFAULT, NULL, + read_cb, data); +} + +static void +no_multipart_handling_cb (GObject *source, GAsyncResult *res, gpointer data) +{ + GMainLoop *loop = (GMainLoop*)data; + SoupRequest *request = SOUP_REQUEST (source); + GError *error = NULL; + GInputStream* in; + + in = soup_request_send_finish (request, res, &error); + g_assert_no_error (error); + if (error) { + g_main_loop_quit (loop); + return; + } + + g_input_stream_read_async (in, buffer, READ_BUFFER_SIZE, + G_PRIORITY_DEFAULT, NULL, + read_cb, data); +} + +static void +multipart_close_part_cb (GObject *source, GAsyncResult *res, gpointer data) +{ + GInputStream *in = G_INPUT_STREAM (source); + GError *error = NULL; + + g_input_stream_close_finish (in, res, &error); + g_assert_no_error (error); +} + +static void multipart_next_part_cb (GObject *source, + GAsyncResult *res, + gpointer data); + +static void +check_read (gsize nread, unsigned passes) +{ + switch (passes) { + case 0: + g_assert_cmpint (nread, ==, 30); + break; + case 1: + g_assert_cmpint (nread, ==, 10); + break; + case 2: + g_assert_cmpint (nread, ==, 24); + break; + case 3: + g_assert_cmpint (nread, ==, 34); + break; + default: + soup_test_assert (FALSE, "unexpected read of size: %d", (int)nread); + break; + } +} + +static void +multipart_read_cb (GObject *source, GAsyncResult *asyncResult, gpointer data) +{ + GMainLoop *loop = (GMainLoop*)data; + GInputStream *in = G_INPUT_STREAM (source); + GError *error = NULL; + static gssize bytes_read_for_part = 0; + gssize bytes_read; + + bytes_read = g_input_stream_read_finish (in, asyncResult, &error); + g_assert_no_error (error); + if (error) { + g_input_stream_close_async (in, G_PRIORITY_DEFAULT, NULL, + multipart_close_part_cb, NULL); + g_object_unref (in); + + g_main_loop_quit (loop); + return; + } + + /* Read 0 bytes - try to start reading another part. */ + if (!bytes_read) { + check_read (bytes_read_for_part, passes); + bytes_read_for_part = 0; + passes++; + + g_input_stream_close_async (in, G_PRIORITY_DEFAULT, NULL, + multipart_close_part_cb, NULL); + g_object_unref (in); + + soup_multipart_input_stream_next_part_async (multipart, G_PRIORITY_DEFAULT, NULL, + multipart_next_part_cb, data); + return; + } + + bytes_read_for_part += bytes_read; + g_input_stream_read_async (in, buffer, READ_BUFFER_SIZE, + G_PRIORITY_DEFAULT, NULL, + multipart_read_cb, data); +} + +static void +check_headers (SoupMultipartInputStream* multipart, unsigned passes) +{ + SoupMessageHeaders *headers; + SoupMessageHeadersIter iter; + gboolean is_next; + const char *name, *value; + + headers = soup_multipart_input_stream_get_headers (multipart); + soup_message_headers_iter_init (&iter, headers); + + switch (passes) { + case 0: + is_next = soup_message_headers_iter_next (&iter, &name, &value); + check_is_next (is_next); + + g_assert_cmpstr (name, ==, "Content-Type"); + g_assert_cmpstr (value, ==, "text/html"); + + is_next = soup_message_headers_iter_next (&iter, &name, &value); + check_is_next (is_next); + + g_assert_cmpstr (name, ==, "Content-Length"); + g_assert_cmpstr (value, ==, "30"); + + break; + case 1: + is_next = soup_message_headers_iter_next (&iter, &name, &value); + check_is_next (is_next); + + g_assert_cmpstr (name, ==, "Content-Length"); + g_assert_cmpstr (value, ==, "10"); + + break; + case 2: + case 3: + is_next = soup_message_headers_iter_next (&iter, &name, &value); + check_is_next (is_next); + + g_assert_cmpstr (name, ==, "Content-Type"); + g_assert_cmpstr (value, ==, "text/css"); + + break; + default: + soup_test_assert (FALSE, "unexpected part received"); + break; + } +} + +static void +multipart_next_part_cb (GObject *source, GAsyncResult *res, gpointer data) +{ + GMainLoop *loop = (GMainLoop*)data; + GError *error = NULL; + GInputStream *in; + gsize read_size = READ_BUFFER_SIZE; + + g_assert (SOUP_MULTIPART_INPUT_STREAM (source) == multipart); + + in = soup_multipart_input_stream_next_part_finish (multipart, res, &error); + g_assert_no_error (error); + if (error) { + g_clear_error (&error); + g_object_unref (multipart); + g_main_loop_quit (loop); + return; + } + + if (!in) { + g_assert_cmpint (passes, ==, 4); + g_object_unref (multipart); + g_main_loop_quit (loop); + return; + } + + check_headers (multipart, passes); + + if (g_object_get_data (G_OBJECT (multipart), "multipart-small-reads")) + read_size = 4; + + g_input_stream_read_async (in, buffer, read_size, + G_PRIORITY_DEFAULT, NULL, + multipart_read_cb, data); +} + +static void +multipart_handling_cb (GObject *source, GAsyncResult *res, gpointer data) +{ + GMainLoop *loop = (GMainLoop*)data; + SoupRequest *request = SOUP_REQUEST (source); + GError *error = NULL; + GInputStream *in; + SoupMessage *message; + + in = soup_request_send_finish (request, res, &error); + g_assert_no_error (error); + if (error) { + g_main_loop_quit (loop); + return; + } + + message = soup_request_http_get_message (SOUP_REQUEST_HTTP (request)); + multipart = soup_multipart_input_stream_new (message, in); + g_object_unref (message); + g_object_unref (in); + + if (g_object_get_data (source, "multipart-small-reads")) + g_object_set_data (G_OBJECT (multipart), "multipart-small-reads", GINT_TO_POINTER(1)); + + soup_multipart_input_stream_next_part_async (multipart, G_PRIORITY_DEFAULT, NULL, + multipart_next_part_cb, data); +} + +static void +sync_multipart_handling_cb (GObject *source, GAsyncResult *res, gpointer data) +{ + GMainLoop *loop = (GMainLoop*)data; + SoupRequest *request = SOUP_REQUEST (source); + GError *error = NULL; + GInputStream *in; + SoupMessage *message; + char buffer[READ_BUFFER_SIZE]; + gsize bytes_read; + + in = soup_request_send_finish (request, res, &error); + g_assert_no_error (error); + if (error) { + g_main_loop_quit (loop); + return; + } + + message = soup_request_http_get_message (SOUP_REQUEST_HTTP (request)); + multipart = soup_multipart_input_stream_new (message, in); + g_object_unref (message); + g_object_unref (in); + + while (TRUE) { + in = soup_multipart_input_stream_next_part (multipart, NULL, &error); + g_assert_no_error (error); + if (error) { + g_clear_error (&error); + break; + } + + if (!in) + break; + + check_headers (multipart, passes); + + g_input_stream_read_all (in, (void*)buffer, sizeof (buffer), &bytes_read, NULL, &error); + g_assert_no_error (error); + if (error) { + g_clear_error (&error); + g_object_unref (in); + break; + } + + check_read (bytes_read, passes); + + passes++; + g_object_unref (in); + } + + g_assert_cmpint (passes, ==, 4); + + g_main_loop_quit (loop); + g_object_unref (multipart); +} + +static void +test_multipart (gconstpointer data) +{ + int headers_expected = 1, sniffed_expected = 1; + MultipartMode multipart_mode = GPOINTER_TO_INT (data); + SoupRequest* request; + SoupMessage *msg; + GMainLoop *loop; + int headers_count = 0; + int sniffed_count = 0; + GHashTable *params; + const char *content_type; + gboolean message_is_multipart = FALSE; + GError* error = NULL; + + request = soup_session_request (session, base_uri_string, &error); + g_assert_no_error (error); + if (error) + return; + + msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (request)); + + /* This is used to track the number of parts. */ + passes = 0; + + /* Force the server to close the connection. */ + soup_message_headers_append (msg->request_headers, + "Connection", "close"); + + g_signal_connect (msg, "got_headers", + G_CALLBACK (got_headers), &headers_count); + + g_signal_connect (msg, "content-sniffed", + G_CALLBACK (content_sniffed), &sniffed_count); + + loop = g_main_loop_new (NULL, TRUE); + + if (multipart_mode == ASYNC_MULTIPART) + soup_request_send_async (request, NULL, multipart_handling_cb, loop); + else if (multipart_mode == ASYNC_MULTIPART_SMALL_READS) { + g_object_set_data (G_OBJECT (request), "multipart-small-reads", GINT_TO_POINTER(1)); + soup_request_send_async (request, NULL, multipart_handling_cb, loop); + } else if (multipart_mode == SYNC_MULTIPART) + soup_request_send_async (request, NULL, sync_multipart_handling_cb, loop); + else + soup_request_send_async (request, NULL, no_multipart_handling_cb, loop); + + g_main_loop_run (loop); + + content_type = soup_message_headers_get_content_type (msg->response_headers, ¶ms); + + if (content_type && + g_str_has_prefix (content_type, "multipart/") && + g_hash_table_lookup (params, "boundary")) { + message_is_multipart = TRUE; + } + g_clear_pointer (¶ms, g_hash_table_unref); + + g_assert_true (message_is_multipart); + g_assert_cmpint (headers_count, ==, headers_expected); + g_assert_cmpint (sniffed_count, ==, sniffed_expected); + + g_object_unref (msg); + g_object_unref (request); + g_main_loop_unref (loop); +} + +int +main (int argc, char **argv) +{ + SoupServer *server; + int ret; + + test_init (argc, argv, NULL); + + buffer = g_malloc (READ_BUFFER_SIZE); + + server = soup_test_server_new (FALSE); + 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)); + base_uri_string = soup_uri_to_string (base_uri, FALSE); + + /* FIXME: I had to raise the number of connections allowed here, otherwise I + * was hitting the limit, which indicates some connections are not dying. + */ + session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, + "use-thread-context", TRUE, + "max-conns", 20, + "max-conns-per-host", 20, + NULL); + soup_session_add_feature_by_type (session, SOUP_TYPE_CONTENT_SNIFFER); + + g_test_add_data_func ("/multipart/no", GINT_TO_POINTER (NO_MULTIPART), test_multipart); + g_test_add_data_func ("/multipart/sync", GINT_TO_POINTER (SYNC_MULTIPART), test_multipart); + g_test_add_data_func ("/multipart/async", GINT_TO_POINTER (ASYNC_MULTIPART), test_multipart); + g_test_add_data_func ("/multipart/async-small-reads", GINT_TO_POINTER (ASYNC_MULTIPART_SMALL_READS), test_multipart); + + ret = g_test_run (); + + soup_uri_free (base_uri); + g_free (base_uri_string); + g_free (buffer); + + soup_test_session_abort_unref (session); + soup_test_server_quit_unref (server); + + test_cleanup (); + return ret; +} diff --git a/tests/no-ssl-test.c b/tests/no-ssl-test.c new file mode 100644 index 00000000..82532c74 --- /dev/null +++ b/tests/no-ssl-test.c @@ -0,0 +1,151 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +#include "test-utils.h" + +static void +do_ssl_test_for_session (SoupSession *session, const char *uri) +{ + SoupMessage *msg; + GTlsCertificate *cert = NULL; + GTlsCertificateFlags flags; + gboolean is_https; + + msg = soup_message_new ("GET", uri); + soup_session_send_message (session, msg); + soup_test_assert_message_status (msg, SOUP_STATUS_SSL_FAILED); + + is_https = soup_message_get_https_status (msg, &cert, &flags); + soup_test_assert (!is_https, "get_http_status() returned TRUE? (flags %x)", flags); + + g_assert_null (cert); + g_assert_false (soup_message_get_flags (msg) & SOUP_MESSAGE_CERTIFICATE_TRUSTED); + + g_object_unref (msg); +} + +static void +do_ssl_tests (gconstpointer uri) +{ + SoupSession *session; + + g_test_bug ("700518"); + + debug_printf (1, " plain\n"); + session = soup_test_session_new (SOUP_TYPE_SESSION, NULL); + do_ssl_test_for_session (session, uri); + soup_test_session_abort_unref (session); + + debug_printf (1, " async\n"); + session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); + do_ssl_test_for_session (session, uri); + soup_test_session_abort_unref (session); + + debug_printf (1, " sync\n"); + session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL); + do_ssl_test_for_session (session, uri); + soup_test_session_abort_unref (session); +} + +static void +do_session_property_tests (void) +{ + gboolean use_system; + GTlsDatabase *tlsdb; + char *ca_file; + SoupSession *session; + + g_test_bug ("700518"); + + session = soup_session_async_new (); + + g_object_get (G_OBJECT (session), + "ssl-use-system-ca-file", &use_system, + "tls-database", &tlsdb, + "ssl-ca-file", &ca_file, + NULL); + soup_test_assert (!use_system, "ssl-use-system-ca-file defaults to TRUE"); + soup_test_assert (tlsdb == NULL, "tls-database set by default"); + soup_test_assert (ca_file == NULL, "ca-file set by default"); + + g_object_set (G_OBJECT (session), + "ssl-use-system-ca-file", TRUE, + NULL); + g_object_get (G_OBJECT (session), + "ssl-ca-file", &ca_file, + NULL); + soup_test_assert (ca_file == NULL, "setting ssl-use-system-ca-file set ssl-ca-file"); + + g_object_set (G_OBJECT (session), + "ssl-ca-file", + g_test_get_filename (G_TEST_DIST, "test-cert.pem", NULL), + NULL); + g_object_get (G_OBJECT (session), + "ssl-use-system-ca-file", &use_system, + "tls-database", &tlsdb, + "ssl-ca-file", &ca_file, + NULL); + soup_test_assert (ca_file == NULL, "setting ssl-ca-file did not fail"); + soup_test_assert (!use_system, "setting ssl-ca-file set ssl-use-system-ca-file"); + soup_test_assert (tlsdb == NULL, "setting ssl-ca-file set tls-database"); + + g_object_set (G_OBJECT (session), + "tls-database", NULL, + NULL); + g_object_get (G_OBJECT (session), + "ssl-use-system-ca-file", &use_system, + "tls-database", &tlsdb, + "ssl-ca-file", &ca_file, + NULL); + soup_test_assert (tlsdb == NULL, "setting tls-database NULL failed"); + soup_test_assert (!use_system, "setting tls-database NULL set ssl-use-system-ca-file"); + soup_test_assert (ca_file == NULL, "setting tls-database NULL set ssl-ca-file"); + + soup_test_session_abort_unref (session); +} + +static void +server_handler (SoupServer *server, + SoupMessage *msg, + const char *path, + GHashTable *query, + SoupClientContext *client, + gpointer user_data) +{ + soup_message_set_status (msg, SOUP_STATUS_OK); + soup_message_set_response (msg, "text/plain", + SOUP_MEMORY_STATIC, + "ok\r\n", 4); +} + +int +main (int argc, char **argv) +{ + SoupServer *server; + char *uri; + int ret; + + /* Force this test to use the dummy TLS backend */ + g_setenv ("GIO_USE_TLS", "dummy", TRUE); + + test_init (argc, argv, NULL); + + /* Make a non-SSL server and pretend that it's ssl, which is fine + * since we won't ever actually talk to it anyway. We don't + * currently test that failing to construct an SSL server works. + */ + server = soup_test_server_new (TRUE); + soup_server_add_handler (server, NULL, server_handler, NULL, NULL); + uri = g_strdup_printf ("https://127.0.0.1:%u/", + soup_server_get_port (server)); + + g_test_add_func ("/no-ssl/session-properties", do_session_property_tests); + g_test_add_data_func ("/no-ssl/request-error", uri, do_ssl_tests); + + ret = g_test_run (); + + g_free (uri); + soup_test_server_quit_unref (server); + + test_cleanup (); + return ret; +} diff --git a/tests/ntlm-test-helper.c b/tests/ntlm-test-helper.c new file mode 100644 index 00000000..f2450cae --- /dev/null +++ b/tests/ntlm-test-helper.c @@ -0,0 +1,103 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright 2012 Red Hat, Inc. + */ + +#include <locale.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <glib.h> +#include "libsoup/soup.h" + +const char *helper_protocol, *username, *domain; +gboolean use_cached_creds; + +static GOptionEntry entries[] = { + { "helper-protocol", 0, 0, + G_OPTION_ARG_STRING, &helper_protocol, + NULL, NULL }, + { "use-cached-creds", 0, 0, + G_OPTION_ARG_NONE, &use_cached_creds, + NULL, NULL }, + { "username", 0, 0, + G_OPTION_ARG_STRING, &username, + NULL, NULL }, + { "domain", 0, 0, + G_OPTION_ARG_STRING, &domain, + NULL, NULL }, + { NULL } +}; + +int +main (int argc, char **argv) +{ + GOptionContext *opts; + char buf[256], *header; + SoupMessage *msg; + SoupAuth *auth; + + /* Don't recurse */ + g_setenv ("SOUP_NTLM_AUTH_DEBUG", "", TRUE); + + setlocale (LC_ALL, ""); + + opts = g_option_context_new (NULL); + g_option_context_add_main_entries (opts, entries, NULL); + if (!g_option_context_parse (opts, &argc, &argv, NULL)) { + g_printerr ("Bad arguments\n"); + exit (1); + } + g_option_context_free (opts); + + if (!username || !use_cached_creds || !helper_protocol || + !g_str_equal (helper_protocol, "ntlmssp-client-1")) { + g_printerr ("Wrong arguments; this program is only intended for use by ntlm-test\n"); + exit (1); + } + + msg = soup_message_new ("GET", "http://localhost/"); + auth = NULL; + + while (fgets (buf, sizeof (buf), stdin)) { + if (strchr (buf, '\n')) + *strchr (buf, '\n') = '\0'; + if (!strcmp (buf, "YR")) { + if (g_getenv ("SOUP_NTLM_AUTH_DEBUG_NOCREDS")) { + g_print ("PW\n"); + continue; + } + + g_clear_object (&auth); + auth = g_object_new (SOUP_TYPE_AUTH_NTLM, NULL); + header = soup_auth_get_authorization (auth, msg); + g_print ("YR %s\n", header + 5); + g_free (header); + } else if (g_str_has_prefix (buf, "TT ")) { + header = g_strdup_printf ("NTLM %s\n", buf + 3); + if (!soup_auth_update (auth, msg, header)) { + g_printerr ("Bad challenge\n"); + exit (1); + } + g_free (header); + + soup_auth_authenticate (auth, username, "password"); + header = soup_auth_get_authorization (auth, msg); + if (!header) { + g_printerr ("Internal authentication failure\n"); + exit (1); + } + g_print ("KK %s\n", header + 5); + g_free (header); + } else { + g_printerr ("Unexpected command\n"); + exit (1); + } + } + + g_object_unref (msg); + g_clear_object (&auth); + + return 0; +} diff --git a/tests/ntlm-test.c b/tests/ntlm-test.c index 00222e87..24a0f2e4 100644 --- a/tests/ntlm-test.c +++ b/tests/ntlm-test.c @@ -11,6 +11,8 @@ #include "test-utils.h" +static SoupURI *uri; + typedef enum { NTLM_UNAUTHENTICATED, NTLM_RECEIVED_REQUEST, @@ -19,12 +21,16 @@ typedef enum { NTLM_AUTHENTICATED_BOB } NTLMServerState; +static const char *state_name[] = { + "unauth", "recv", "sent", "alice", "bob" +}; + #define NTLM_REQUEST_START "TlRMTVNTUAABAAAA" #define NTLM_RESPONSE_START "TlRMTVNTUAADAAAA" #define NTLM_CHALLENGE "TlRMTVNTUAACAAAADAAMADAAAAABAoEAASNFZ4mrze8AAAAAAAAAAGIAYgA8AAAARABPAE0AQQBJAE4AAgAMAEQATwBNAEEASQBOAAEADABTAEUAUgBWAEUAUgAEABQAZABvAG0AYQBpAG4ALgBjAG8AbQADACIAcwBlAHIAdgBlAHIALgBkAG8AbQBhAGkAbgAuAGMAbwBtAAAAAAA=" -#define NTLM_RESPONSE_USER(response) ((response)[102] == 'E' ? NTLM_AUTHENTICATED_ALICE : NTLM_AUTHENTICATED_BOB) +#define NTLM_RESPONSE_USER(response) ((response)[86] == 'E' ? NTLM_AUTHENTICATED_ALICE : ((response)[86] == 'I' ? NTLM_AUTHENTICATED_BOB : NTLM_UNAUTHENTICATED)) static void clear_state (gpointer connections, GObject *ex_connection) @@ -134,16 +140,22 @@ server_callback (SoupServer *server, SoupMessage *msg, } } + debug_printf (2, " (S:%s)", state_name[state]); g_hash_table_insert (connections, socket, GINT_TO_POINTER (state)); g_object_weak_ref (G_OBJECT (socket), clear_state, connections); } +static gboolean authenticated_ntlm = FALSE; + static void authenticate (SoupSession *session, SoupMessage *msg, SoupAuth *auth, gboolean retrying, gpointer user) { - if (!retrying) + if (!retrying) { soup_auth_authenticate (auth, user, "password"); + if (g_str_equal (soup_auth_get_scheme_name (auth), "NTLM")) + authenticated_ntlm = TRUE; + } } typedef struct { @@ -238,82 +250,65 @@ do_message (SoupSession *session, SoupURI *base_uri, const char *path, if (state.got_ntlm_prompt) { debug_printf (1, " NTLM_PROMPT"); - if (!get_ntlm_prompt) { + if (!get_ntlm_prompt) debug_printf (1, "???"); - errors++; - } - } else if (get_ntlm_prompt) { + } else if (get_ntlm_prompt) debug_printf (1, " no-ntlm-prompt???"); - errors++; - } if (state.got_basic_prompt) { debug_printf (1, " BASIC_PROMPT"); - if (!get_basic_prompt) { + if (!get_basic_prompt) debug_printf (1, "???"); - errors++; - } - } else if (get_basic_prompt) { + } else if (get_basic_prompt) debug_printf (1, " no-basic-prompt???"); - errors++; - } if (state.sent_ntlm_request) { debug_printf (1, " REQUEST"); - if (!do_ntlm) { + if (!do_ntlm) debug_printf (1, "???"); - errors++; - } - } else if (do_ntlm) { + } else if (do_ntlm) debug_printf (1, " no-request???"); - errors++; - } if (state.got_ntlm_challenge) { debug_printf (1, " CHALLENGE"); - if (!do_ntlm) { + if (!do_ntlm) debug_printf (1, "???"); - errors++; - } - } else if (do_ntlm) { + } else if (do_ntlm) debug_printf (1, " no-challenge???"); - errors++; - } if (state.sent_ntlm_response) { debug_printf (1, " NTLM_RESPONSE"); - if (!do_ntlm) { + if (!do_ntlm) debug_printf (1, "???"); - errors++; - } - } else if (do_ntlm) { + } else if (do_ntlm) debug_printf (1, " no-ntlm-response???"); - errors++; - } if (state.sent_basic_response) { debug_printf (1, " BASIC_RESPONSE"); - if (!do_basic) { + if (!do_basic) debug_printf (1, "???"); - errors++; - } - } else if (do_basic) { + } else if (do_basic) debug_printf (1, " no-basic-response???"); - errors++; - } debug_printf (1, " -> %s", msg->reason_phrase); - if (msg->status_code != status_code) { + if (msg->status_code != status_code) debug_printf (1, "???"); - errors++; - } debug_printf (1, "\n"); + g_assert_true (state.got_ntlm_prompt == get_ntlm_prompt); + g_assert_true (state.got_basic_prompt == get_basic_prompt); + g_assert_true (state.sent_ntlm_request == do_ntlm); + g_assert_true (state.got_ntlm_challenge == do_ntlm); + g_assert_true (state.sent_ntlm_response == do_ntlm); + g_assert_true (state.sent_basic_response == do_basic); + soup_test_assert_message_status (msg, status_code); + g_object_unref (msg); } static void -do_ntlm_round (SoupURI *base_uri, gboolean use_ntlm, const char *user) +do_ntlm_round (SoupURI *base_uri, gboolean use_ntlm, + const char *user, gboolean use_builtin_ntlm) { SoupSession *session; gboolean alice = !g_strcmp0 (user, "alice"); @@ -322,13 +317,23 @@ do_ntlm_round (SoupURI *base_uri, gboolean use_ntlm, const char *user) gboolean bob_via_ntlm = use_ntlm && bob; gboolean alice_via_basic = !use_ntlm && alice; - session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); - if (use_ntlm) - soup_session_add_feature_by_type (session, SOUP_TYPE_AUTH_NTLM); + session = soup_test_session_new (SOUP_TYPE_SESSION, NULL); if (user) { g_signal_connect (session, "authenticate", G_CALLBACK (authenticate), (char *)user); + if (use_ntlm && !use_builtin_ntlm) + g_setenv ("NTLMUSER", user, TRUE); + } + if (use_ntlm) { + SoupAuthManager *auth_manager; + SoupAuth *ntlm; + + soup_session_add_feature_by_type (session, SOUP_TYPE_AUTH_NTLM); + auth_manager = SOUP_AUTH_MANAGER (soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER)); + ntlm = g_object_new (SOUP_TYPE_AUTH_NTLM, NULL); + soup_auth_manager_use_auth (auth_manager, base_uri, ntlm); + g_object_unref (ntlm); } /* 1. Server doesn't request auth, so both get_ntlm_prompt and @@ -336,11 +341,17 @@ do_ntlm_round (SoupURI *base_uri, gboolean use_ntlm, const char *user) * if we're using NTLM we'll try that even without the server * asking. */ + authenticated_ntlm = FALSE; do_message (session, base_uri, "/noauth", FALSE, use_ntlm, FALSE, FALSE, SOUP_STATUS_OK); + soup_test_assert (authenticated_ntlm == (use_ntlm && use_builtin_ntlm), + "%s built-in NTLM support, but authenticate signal %s emitted\n", + use_builtin_ntlm ? "Using" : "Not using", + authenticated_ntlm ? "was" : "wasn't"); + /* 2. Server requires auth as Alice, so it will request that * if we didn't already authenticate the connection to her in * the previous step. If we authenticated as Bob in the @@ -421,46 +432,190 @@ do_ntlm_round (SoupURI *base_uri, gboolean use_ntlm, const char *user) soup_test_session_abort_unref (session); } +typedef enum { + BUILTIN, + WINBIND, + FALLBACK +} NtlmType; + +typedef struct { + const char *name, *user; + gboolean conn_uses_ntlm; + NtlmType ntlm_type; +} NtlmTest; + +static const NtlmTest ntlm_tests[] = { + { "/ntlm/builtin/none", NULL, FALSE, BUILTIN }, + { "/ntlm/builtin/alice", "alice", TRUE, BUILTIN }, + { "/ntlm/builtin/bob", "bob", TRUE, BUILTIN }, + { "/ntlm/builtin/basic", "alice", FALSE, BUILTIN }, + + { "/ntlm/winbind/none", NULL, FALSE, WINBIND }, + { "/ntlm/winbind/alice", "alice", TRUE, WINBIND }, + { "/ntlm/winbind/bob", "bob", TRUE, WINBIND }, + { "/ntlm/winbind/basic", "alice", FALSE, WINBIND }, + + { "/ntlm/fallback/none", NULL, FALSE, FALLBACK }, + { "/ntlm/fallback/alice", "alice", TRUE, FALLBACK }, + { "/ntlm/fallback/bob", "bob", TRUE, FALLBACK }, + { "/ntlm/fallback/basic", "alice", FALSE, FALLBACK } +}; + static void -do_ntlm_tests (SoupURI *base_uri) +do_ntlm_test (gconstpointer data) { - debug_printf (1, "Round 1: Non-NTLM Connection, no auth\n"); - do_ntlm_round (base_uri, FALSE, NULL); - debug_printf (1, "Round 2: NTLM Connection, user=alice\n"); - do_ntlm_round (base_uri, TRUE, "alice"); - debug_printf (1, "Round 3: NTLM Connection, user=bob\n"); - do_ntlm_round (base_uri, TRUE, "bob"); - debug_printf (1, "Round 4: Non-NTLM Connection, user=alice\n"); - do_ntlm_round (base_uri, FALSE, "alice"); + const NtlmTest *test = data; + gboolean use_builtin_ntlm = TRUE; + + switch (test->ntlm_type) { + case BUILTIN: + /* Built-in NTLM auth support. (We set SOUP_NTLM_AUTH_DEBUG to + * an empty string to ensure that the built-in support is + * being used, even if /usr/bin/ntlm_auth is available.) + */ + g_setenv ("SOUP_NTLM_AUTH_DEBUG", "", TRUE); + break; + + case WINBIND: +#ifndef USE_NTLM_AUTH + g_test_skip ("/usr/bin/ntlm_auth is not available"); + return; +#endif + + /* Samba winbind /usr/bin/ntlm_auth helper support (via a + * helper program that emulates its interface). + */ + g_setenv ("SOUP_NTLM_AUTH_DEBUG", + g_test_get_filename (G_TEST_BUILT, "ntlm-test-helper", NULL), + TRUE); + g_unsetenv ("SOUP_NTLM_AUTH_DEBUG_NOCREDS"); + use_builtin_ntlm = FALSE; + break; + + case FALLBACK: +#ifndef USE_NTLM_AUTH + g_test_skip ("/usr/bin/ntlm_auth is not available"); + return; +#endif + + /* Support for when ntlm_auth is installed, but the user has + * no cached credentials (and thus we have to fall back to + * libsoup's built-in NTLM support). + */ + g_setenv ("SOUP_NTLM_AUTH_DEBUG", + g_test_get_filename (G_TEST_BUILT, "ntlm-test-helper", NULL), + TRUE); + g_setenv ("SOUP_NTLM_AUTH_DEBUG_NOCREDS", "1", TRUE); + break; + } + + do_ntlm_round (uri, test->conn_uses_ntlm, test->user, use_builtin_ntlm); +} + +static void +retry_test_authenticate (SoupSession *session, SoupMessage *msg, + SoupAuth *auth, gboolean retrying, + gpointer user_data) +{ + gboolean *retried = user_data; + + if (!retrying) { + /* server_callback doesn't actually verify the password, + * only the username. So we pass an incorrect username + * rather than an incorrect password. + */ + soup_auth_authenticate (auth, "wrong", "password"); + } else if (!*retried) { + soup_auth_authenticate (auth, "alice", "password"); + *retried = TRUE; + } +} + +static void +do_retrying_test (gconstpointer data) +{ + SoupURI *base_uri = (SoupURI *)data; + SoupSession *session; + SoupMessage *msg; + SoupURI *uri; + gboolean retried = FALSE; + + g_test_bug ("693222"); + + g_setenv ("SOUP_NTLM_AUTH_DEBUG", "", TRUE); + + debug_printf (1, " /alice\n"); + + session = soup_test_session_new (SOUP_TYPE_SESSION, + SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_AUTH_NTLM, + NULL); + g_signal_connect (session, "authenticate", + G_CALLBACK (retry_test_authenticate), &retried); + + uri = soup_uri_new_with_base (base_uri, "/alice"); + msg = soup_message_new_from_uri ("GET", uri); + soup_uri_free (uri); + + soup_session_send_message (session, msg); + + g_assert_true (retried); + soup_test_assert_message_status (msg, SOUP_STATUS_OK); + + g_object_unref (msg); + + soup_test_session_abort_unref (session); + + debug_printf (1, " /bob\n"); + + session = soup_test_session_new (SOUP_TYPE_SESSION, + SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_AUTH_NTLM, + NULL); + g_signal_connect (session, "authenticate", + G_CALLBACK (retry_test_authenticate), &retried); + retried = FALSE; + + uri = soup_uri_new_with_base (base_uri, "/bob"); + msg = soup_message_new_from_uri ("GET", uri); + soup_uri_free (uri); + + soup_session_send_message (session, msg); + + g_assert_true (retried); + soup_test_assert_message_status (msg, SOUP_STATUS_UNAUTHORIZED); + + g_object_unref (msg); + + soup_test_session_abort_unref (session); } int main (int argc, char **argv) { - GMainLoop *loop; SoupServer *server; GHashTable *connections; - SoupURI *uri; + int i, ret; test_init (argc, argv, NULL); - server = soup_test_server_new (FALSE); + server = soup_test_server_new (TRUE); connections = g_hash_table_new (NULL, NULL); soup_server_add_handler (server, NULL, server_callback, connections, NULL); - loop = g_main_loop_new (NULL, TRUE); - uri = soup_uri_new ("http://127.0.0.1/"); soup_uri_set_port (uri, soup_server_get_port (server)); - do_ntlm_tests (uri); - soup_uri_free (uri); - g_main_loop_unref (loop); + for (i = 0; i < G_N_ELEMENTS (ntlm_tests); i++) + g_test_add_data_func (ntlm_tests[i].name, &ntlm_tests[i], do_ntlm_test); + g_test_add_data_func ("/ntlm/retry", uri, do_retrying_test); + + ret = g_test_run (); + + soup_uri_free (uri); soup_test_server_quit_unref (server); test_cleanup (); g_hash_table_destroy (connections); - return errors != 0; + return ret; } diff --git a/tests/proxy-test.c b/tests/proxy-test.c index 85aac9f5..4b6679b3 100644 --- a/tests/proxy-test.c +++ b/tests/proxy-test.c @@ -6,16 +6,18 @@ typedef struct { const char *explanation; const char *url; const guint final_status; + const char *bugref; } SoupProxyTest; static SoupProxyTest tests[] = { - { "GET -> 200", "", SOUP_STATUS_OK }, - { "GET -> 404", "/not-found", SOUP_STATUS_NOT_FOUND }, - { "GET -> 401 -> 200", "/Basic/realm1/", SOUP_STATUS_OK }, - { "GET -> 401 -> 401", "/Basic/realm2/", SOUP_STATUS_UNAUTHORIZED }, - { "GET -> 403", "http://no-such-hostname.xx/", SOUP_STATUS_FORBIDDEN }, + { "GET -> 200", "", SOUP_STATUS_OK, NULL }, + { "GET -> 404", "/not-found", SOUP_STATUS_NOT_FOUND, NULL }, + { "GET -> 401 -> 200", "/Basic/realm1/", SOUP_STATUS_OK, NULL }, + { "GET -> 401 -> 401", "/Basic/realm2/", SOUP_STATUS_UNAUTHORIZED, NULL }, + { "GET -> 403", "http://no-such-hostname.xx/", SOUP_STATUS_FORBIDDEN, "577532" }, + { "GET -> 200 (unproxied)", "http://localhost:47524/", SOUP_STATUS_OK, "700472" }, }; -static int ntests = sizeof (tests) / sizeof (tests[0]); +static const int ntests = sizeof (tests) / sizeof (tests[0]); #define HTTP_SERVER "http://127.0.0.1:47524" #define HTTPS_SERVER "https://127.0.0.1:47525" @@ -35,25 +37,23 @@ static const char *proxy_names[] = { "authenticated proxy", "unauthenticatable-to proxy" }; +static GProxyResolver *proxy_resolvers[3]; +static const char *ignore_hosts[] = { "localhost", NULL }; static void authenticate (SoupSession *session, SoupMessage *msg, SoupAuth *auth, gboolean retrying, gpointer data) { if (msg->status_code == SOUP_STATUS_UNAUTHORIZED) { - if (soup_auth_is_for_proxy (auth)) { - debug_printf (1, " got proxy auth object for 401!\n"); - errors++; - } + soup_test_assert (!soup_auth_is_for_proxy (auth), + "got proxy auth object for 401"); } else if (msg->status_code == SOUP_STATUS_PROXY_UNAUTHORIZED) { - if (!soup_auth_is_for_proxy (auth)) { - debug_printf (1, " got regular auth object for 407!\n"); - errors++; - } + soup_test_assert (soup_auth_is_for_proxy (auth), + "got regular auth object for 407"); } else { - debug_printf (1, " got authenticate signal with status %d\n", - msg->status_code); - errors++; + soup_test_assert (FALSE, + "got authenticate signal with status %d\n", + msg->status_code); } if (!retrying) @@ -80,28 +80,29 @@ test_url (const char *url, int proxy, guint expected, gboolean sync, gboolean close) { SoupSession *session; - SoupURI *proxy_uri; SoupMessage *msg; + gboolean noproxy = !!strstr (url, "localhost"); if (!tls_available && g_str_has_prefix (url, "https:")) return; debug_printf (1, " GET %s via %s%s\n", url, proxy_names[proxy], close ? " (with Connection: close)" : ""); - if (proxy == UNAUTH_PROXY && expected != SOUP_STATUS_FORBIDDEN) + if (proxy == UNAUTH_PROXY && expected != SOUP_STATUS_FORBIDDEN && !noproxy) expected = SOUP_STATUS_PROXY_UNAUTHORIZED; /* We create a new session for each request to ensure that * connections/auth aren't cached between tests. */ - proxy_uri = soup_uri_new (proxies[proxy]); session = soup_test_session_new (sync ? SOUP_TYPE_SESSION_SYNC : SOUP_TYPE_SESSION_ASYNC, - SOUP_SESSION_PROXY_URI, proxy_uri, + SOUP_SESSION_PROXY_RESOLVER, proxy_resolvers[proxy], + SOUP_SESSION_USE_THREAD_CONTEXT, TRUE, + SOUP_SESSION_SSL_STRICT, FALSE, NULL); - soup_uri_free (proxy_uri); g_signal_connect (session, "authenticate", G_CALLBACK (authenticate), NULL); if (close) { + /* FIXME g_test_bug ("611663") */ g_signal_connect (session, "request-started", G_CALLBACK (set_close_on_connect), NULL); } @@ -115,10 +116,7 @@ test_url (const char *url, int proxy, guint expected, soup_session_send_message (session, msg); debug_printf (1, " %d %s\n", msg->status_code, msg->reason_phrase); - if (msg->status_code != expected) { - debug_printf (1, " EXPECTED %d!\n", expected); - errors++; - } + soup_test_assert_message_status (msg, expected); g_object_unref (msg); soup_test_session_abort_unref (session); @@ -129,73 +127,54 @@ test_url_new_api (const char *url, int proxy, guint expected, gboolean sync, gboolean close) { SoupSession *session; - SoupURI *proxy_uri; SoupMessage *msg; - SoupRequester *requester; SoupRequest *request; GInputStream *stream; GError *error = NULL; + gboolean noproxy = !!strstr (url, "localhost"); + /* FIXME g_test_skip() FIXME g_test_bug ("675865") */ if (!tls_available && g_str_has_prefix (url, "https:")) return; - debug_printf (1, " GET (requester API) %s via %s%s\n", url, proxy_names[proxy], + debug_printf (1, " GET (request API) %s via %s%s\n", url, proxy_names[proxy], close ? " (with Connection: close)" : ""); - if (proxy == UNAUTH_PROXY && expected != SOUP_STATUS_FORBIDDEN) + if (proxy == UNAUTH_PROXY && expected != SOUP_STATUS_FORBIDDEN && !noproxy) expected = SOUP_STATUS_PROXY_UNAUTHORIZED; /* We create a new session for each request to ensure that * connections/auth aren't cached between tests. */ - proxy_uri = soup_uri_new (proxies[proxy]); session = soup_test_session_new (sync ? SOUP_TYPE_SESSION_SYNC : SOUP_TYPE_SESSION_ASYNC, - SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_REQUESTER, SOUP_SESSION_USE_THREAD_CONTEXT, TRUE, - SOUP_SESSION_PROXY_URI, proxy_uri, + SOUP_SESSION_PROXY_RESOLVER, proxy_resolvers[proxy], + SOUP_SESSION_SSL_STRICT, FALSE, NULL); - soup_uri_free (proxy_uri); g_signal_connect (session, "authenticate", G_CALLBACK (authenticate), NULL); if (close) { + /* FIXME g_test_bug ("611663") */ g_signal_connect (session, "request-started", G_CALLBACK (set_close_on_connect), NULL); } - requester = (SoupRequester *)soup_session_get_feature (session, SOUP_TYPE_REQUESTER); - request = soup_requester_request (requester, url, NULL); + request = soup_session_request (session, url, NULL); msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (request)); - if (sync) - stream = soup_request_send (request, NULL, &error); - else - stream = soup_test_request_send_async_as_sync (request, NULL, &error); - - if (!stream) { - debug_printf (1, " Unexpected error on Request: %s\n", - error->message); - errors++; - g_clear_error (&error); - } + stream = soup_test_request_send (request, NULL, 0, &error); + g_assert_no_error (error); + g_clear_error (&error); if (stream) { - if (sync) - g_input_stream_close (stream, NULL, NULL); - else - soup_test_stream_close_async_as_sync (stream, NULL, NULL); - if (error) { - debug_printf (1, " Unexpected error on close: %s\n", - error->message); - errors++; - g_clear_error (&error); - } + soup_test_request_close_stream (request, stream, NULL, &error); + g_assert_no_error (error); + g_clear_error (&error); + g_object_unref (stream); } debug_printf (1, " %d %s\n", msg->status_code, msg->reason_phrase); - if (msg->status_code != expected) { - debug_printf (1, " EXPECTED %d!\n", expected); - errors++; - } + soup_test_assert_message_status (msg, expected); g_object_unref (msg); g_object_unref (request); @@ -204,42 +183,70 @@ test_url_new_api (const char *url, int proxy, guint expected, } static void -run_test (int i, gboolean sync) +do_proxy_test (SoupProxyTest *test, gboolean sync) { char *http_url, *https_url; - debug_printf (1, "Test %d: %s (%s)\n", i + 1, tests[i].explanation, - sync ? "sync" : "async"); + if (test->bugref) + g_test_bug (test->bugref); + + if (!strncmp (test->url, "http", 4)) { + SoupURI *uri; + guint port; - if (!strncmp (tests[i].url, "http", 4)) { - http_url = g_strdup (tests[i].url); - https_url = g_strdup_printf ("https%s", tests[i].url + 4); + http_url = g_strdup (test->url); + + uri = soup_uri_new (test->url); + port = uri->port; + soup_uri_set_scheme (uri, "https"); + if (port) + soup_uri_set_port (uri, port + 1); + https_url = soup_uri_to_string (uri, FALSE); + soup_uri_free (uri); } else { - http_url = g_strconcat (HTTP_SERVER, tests[i].url, NULL); - https_url = g_strconcat (HTTPS_SERVER, tests[i].url, NULL); + http_url = g_strconcat (HTTP_SERVER, test->url, NULL); + https_url = g_strconcat (HTTPS_SERVER, test->url, NULL); } - test_url (http_url, SIMPLE_PROXY, tests[i].final_status, sync, FALSE); - test_url_new_api (http_url, SIMPLE_PROXY, tests[i].final_status, sync, FALSE); - test_url (https_url, SIMPLE_PROXY, tests[i].final_status, sync, FALSE); - test_url_new_api (https_url, SIMPLE_PROXY, tests[i].final_status, sync, FALSE); + test_url (http_url, SIMPLE_PROXY, test->final_status, sync, FALSE); + test_url_new_api (http_url, SIMPLE_PROXY, test->final_status, sync, FALSE); + test_url (https_url, SIMPLE_PROXY, test->final_status, sync, FALSE); + test_url_new_api (https_url, SIMPLE_PROXY, test->final_status, sync, FALSE); - test_url (http_url, AUTH_PROXY, tests[i].final_status, sync, FALSE); - test_url_new_api (http_url, AUTH_PROXY, tests[i].final_status, sync, FALSE); - test_url (https_url, AUTH_PROXY, tests[i].final_status, sync, FALSE); - test_url_new_api (https_url, AUTH_PROXY, tests[i].final_status, sync, FALSE); - test_url (https_url, AUTH_PROXY, tests[i].final_status, sync, TRUE); - test_url_new_api (https_url, AUTH_PROXY, tests[i].final_status, sync, TRUE); + test_url (http_url, AUTH_PROXY, test->final_status, sync, FALSE); + test_url_new_api (http_url, AUTH_PROXY, test->final_status, sync, FALSE); + test_url (https_url, AUTH_PROXY, test->final_status, sync, FALSE); + test_url_new_api (https_url, AUTH_PROXY, test->final_status, sync, FALSE); + test_url (https_url, AUTH_PROXY, test->final_status, sync, TRUE); + test_url_new_api (https_url, AUTH_PROXY, test->final_status, sync, TRUE); - test_url (http_url, UNAUTH_PROXY, tests[i].final_status, sync, FALSE); - test_url_new_api (http_url, UNAUTH_PROXY, tests[i].final_status, sync, FALSE); - test_url (https_url, UNAUTH_PROXY, tests[i].final_status, sync, FALSE); - test_url_new_api (https_url, UNAUTH_PROXY, tests[i].final_status, sync, FALSE); + test_url (http_url, UNAUTH_PROXY, test->final_status, sync, FALSE); + test_url_new_api (http_url, UNAUTH_PROXY, test->final_status, sync, FALSE); + test_url (https_url, UNAUTH_PROXY, test->final_status, sync, FALSE); + test_url_new_api (https_url, UNAUTH_PROXY, test->final_status, sync, FALSE); g_free (http_url); g_free (https_url); +} + +static void +do_async_proxy_test (gconstpointer data) +{ + SoupProxyTest *test = (SoupProxyTest *)data; + + SOUP_TEST_SKIP_IF_NO_APACHE; - debug_printf (1, "\n"); + do_proxy_test (test, FALSE); +} + +static void +do_sync_proxy_test (gconstpointer data) +{ + SoupProxyTest *test = (SoupProxyTest *)data; + + SOUP_TEST_SKIP_IF_NO_APACHE; + + do_proxy_test (test, TRUE); } static void @@ -253,13 +260,14 @@ server_callback (SoupServer *server, SoupMessage *msg, } static void -do_proxy_fragment_test (SoupURI *base_uri) +do_proxy_fragment_test (gconstpointer data) { + SoupURI *base_uri = (SoupURI *)data; SoupSession *session; SoupURI *proxy_uri, *req_uri; SoupMessage *msg; - debug_printf (1, "\nTesting request with fragment via proxy\n"); + SOUP_TEST_SKIP_IF_NO_APACHE; proxy_uri = soup_uri_new (proxies[SIMPLE_PROXY]); session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, @@ -272,11 +280,7 @@ do_proxy_fragment_test (SoupURI *base_uri) soup_uri_free (req_uri); soup_session_send_message (session, msg); - if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) { - debug_printf (1, " unexpected status %d %s!\n", - msg->status_code, msg->reason_phrase); - errors++; - } + soup_test_assert_message_status (msg, SOUP_STATUS_OK); g_object_unref (msg); soup_test_session_abort_unref (session); @@ -289,7 +293,10 @@ do_proxy_redirect_test (void) SoupURI *proxy_uri, *req_uri, *new_uri; SoupMessage *msg; - debug_printf (1, "\nTesting redirection through proxy\n"); + g_test_bug ("631368"); + + SOUP_TEST_SKIP_IF_NO_APACHE; + SOUP_TEST_SKIP_IF_NO_TLS; proxy_uri = soup_uri_new (proxies[SIMPLE_PROXY]); session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, @@ -305,17 +312,11 @@ do_proxy_redirect_test (void) soup_session_send_message (session, msg); new_uri = soup_message_get_uri (msg); - if (!strcmp (req_uri->path, new_uri->path)) { - debug_printf (1, " message was not redirected!\n"); - errors++; - } + soup_test_assert (strcmp (req_uri->path, new_uri->path) != 0, + "message was not redirected"); soup_uri_free (req_uri); - if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) { - debug_printf (1, " unexpected status %d %s!\n", - msg->status_code, msg->reason_phrase); - errors++; - } + soup_test_assert_message_status (msg, SOUP_STATUS_OK); g_object_unref (msg); soup_test_session_abort_unref (session); @@ -326,14 +327,15 @@ main (int argc, char **argv) { SoupServer *server; SoupURI *base_uri; - int i; + char *path; + int i, ret; test_init (argc, argv, NULL); apache_init (); - for (i = 0; i < ntests; i++) { - run_test (i, FALSE); - run_test (i, TRUE); + for (i = 0; i < 3; i++) { + proxy_resolvers[i] = + g_simple_proxy_resolver_new (proxies[i], (char **) ignore_hosts); } server = soup_test_server_new (TRUE); @@ -341,12 +343,25 @@ main (int argc, char **argv) base_uri = soup_uri_new ("http://127.0.0.1/"); soup_uri_set_port (base_uri, soup_server_get_port (server)); - do_proxy_fragment_test (base_uri); - do_proxy_redirect_test (); + for (i = 0; i < ntests; i++) { + path = g_strdup_printf ("/proxy/async/%s", tests[i].explanation); + g_test_add_data_func (path, &tests[i], do_async_proxy_test); + g_free (path); + } + for (i = 0; i < ntests; i++) { + path = g_strdup_printf ("/proxy/sync/%s", tests[i].explanation); + g_test_add_data_func (path, &tests[i], do_sync_proxy_test); + g_free (path); + } + + g_test_add_data_func ("/proxy/fragment", base_uri, do_proxy_fragment_test); + g_test_add_func ("/proxy/redirect", do_proxy_redirect_test); + + ret = g_test_run (); soup_uri_free (base_uri); soup_test_server_quit_unref (server); test_cleanup (); - return errors != 0; + return ret; } diff --git a/tests/pull-api.c b/tests/pull-api.c index 512d1768..2915b9e4 100644 --- a/tests/pull-api.c +++ b/tests/pull-api.c @@ -12,6 +12,7 @@ authenticate (SoupSession *session, SoupMessage *msg, soup_auth_authenticate (auth, "user2", "realm2"); } +#if HAVE_APACHE static void get_correct_response (const char *uri) { @@ -32,6 +33,7 @@ get_correct_response (const char *uri) g_object_unref (msg); soup_test_session_abort_unref (session); } +#endif /* Pull API version 1: fully-async. More like a "poke" API. Rather * than having SoupMessage emit "got_chunk" signals whenever it wants, @@ -161,9 +163,7 @@ fully_async_got_headers (SoupMessage *msg, gpointer user_data) */ return; } else if (msg->status_code != SOUP_STATUS_OK) { - debug_printf (1, " unexpected status: %d %s\n", - msg->status_code, msg->reason_phrase); - errors++; + soup_test_assert_message_status (msg, SOUP_STATUS_OK); return; } @@ -192,18 +192,10 @@ fully_async_got_chunk (SoupMessage *msg, SoupBuffer *chunk, gpointer user_data) * test program, that means comparing it against * correct_response to make sure that we got the right data. */ - if (ad->read_so_far + chunk->length > correct_response->length) { - debug_printf (1, " read too far! (%lu > %lu)\n", - (unsigned long) (ad->read_so_far + chunk->length), - (unsigned long) correct_response->length); - errors++; - } else if (memcmp (chunk->data, - correct_response->data + ad->read_so_far, - chunk->length) != 0) { - debug_printf (1, " data mismatch in block starting at %lu\n", - (unsigned long) ad->read_so_far); - errors++; - } + g_assert_cmpint (ad->read_so_far + chunk->length, <=, correct_response->length); + soup_assert_cmpmem (chunk->data, chunk->length, + correct_response->data + ad->read_so_far, + chunk->length); ad->read_so_far += chunk->length; /* Now pause I/O, and prepare to read another chunk later. @@ -224,11 +216,7 @@ fully_async_finished (SoupSession *session, SoupMessage *msg, { FullyAsyncData *ad = user_data; - if (msg->status_code != ad->expected_status) { - debug_printf (1, " unexpected final status: %d %s !\n", - msg->status_code, msg->reason_phrase); - errors++; - } + soup_test_assert_message_status (msg, ad->expected_status); if (ad->timeout != 0) g_source_remove (ad->timeout); @@ -240,6 +228,45 @@ fully_async_finished (SoupSession *session, SoupMessage *msg, g_main_loop_quit (ad->loop); } +static void +do_fast_async_test (gconstpointer data) +{ + const char *base_uri = data; + SoupSession *session; + + SOUP_TEST_SKIP_IF_NO_APACHE; + + session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); + g_signal_connect (session, "authenticate", + G_CALLBACK (authenticate), NULL); + do_fully_async_test (session, base_uri, "/", + TRUE, SOUP_STATUS_OK); + do_fully_async_test (session, base_uri, "/Basic/realm1/", + TRUE, SOUP_STATUS_UNAUTHORIZED); + do_fully_async_test (session, base_uri, "/Basic/realm2/", + TRUE, SOUP_STATUS_OK); + soup_test_session_abort_unref (session); +} + +static void +do_slow_async_test (gconstpointer data) +{ + const char *base_uri = data; + SoupSession *session; + + SOUP_TEST_SKIP_IF_NO_APACHE; + + session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); + g_signal_connect (session, "authenticate", + G_CALLBACK (authenticate), NULL); + do_fully_async_test (session, base_uri, "/", + FALSE, SOUP_STATUS_OK); + do_fully_async_test (session, base_uri, "/Basic/realm1/", + FALSE, SOUP_STATUS_UNAUTHORIZED); + do_fully_async_test (session, base_uri, "/Basic/realm2/", + FALSE, SOUP_STATUS_OK); + soup_test_session_abort_unref (session); +} /* Pull API version 2: synchronous pull API via async I/O. */ @@ -284,14 +311,12 @@ do_synchronously_async_test (SoupSession *session, /* Send the message, get back headers */ sync_async_send (session, msg); - if (sync_async_is_finished (msg) && - expected_status == SOUP_STATUS_OK) { - debug_printf (1, " finished without reading response!\n"); - errors++; - } else if (!sync_async_is_finished (msg) && - expected_status != SOUP_STATUS_OK) { - debug_printf (1, " request failed to fail!\n"); - errors++; + if (expected_status == SOUP_STATUS_OK) { + soup_test_assert (!sync_async_is_finished (msg), + "finished without reading response"); + } else { + soup_test_assert (sync_async_is_finished (msg), + "request failed to fail"); } /* Now we're ready to read the response body (though we could @@ -303,32 +328,19 @@ do_synchronously_async_test (SoupSession *session, (unsigned long) read_so_far, (unsigned long) read_so_far + chunk->length); - if (read_so_far + chunk->length > correct_response->length) { - debug_printf (1, " read too far! (%lu > %lu)\n", - (unsigned long) read_so_far + chunk->length, - (unsigned long) correct_response->length); - errors++; - } else if (memcmp (chunk->data, - correct_response->data + read_so_far, - chunk->length) != 0) { - debug_printf (1, " data mismatch in block starting at %lu\n", - (unsigned long) read_so_far); - errors++; - } + g_assert_cmpint (read_so_far + chunk->length, <=, correct_response->length); + soup_assert_cmpmem (chunk->data, chunk->length, + correct_response->data + read_so_far, + chunk->length); + read_so_far += chunk->length; soup_buffer_free (chunk); } - if (!sync_async_is_finished (msg) || - (msg->status_code == SOUP_STATUS_OK && - read_so_far != correct_response->length)) { - debug_printf (1, " loop ended before message was fully read!\n"); - errors++; - } else if (msg->status_code != expected_status) { - debug_printf (1, " unexpected final status: %d %s !\n", - msg->status_code, msg->reason_phrase); - errors++; - } + g_assert_true (sync_async_is_finished (msg)); + soup_test_assert_message_status (msg, expected_status); + if (msg->status_code == SOUP_STATUS_OK) + g_assert_cmpint (read_so_far, ==, correct_response->length); sync_async_cleanup (msg); g_object_unref (msg); @@ -393,9 +405,7 @@ sync_async_got_headers (SoupMessage *msg, gpointer user_data) */ return; } else if (msg->status_code != SOUP_STATUS_OK) { - debug_printf (1, " unexpected status: %d %s\n", - msg->status_code, msg->reason_phrase); - errors++; + soup_test_assert_message_status (msg, SOUP_STATUS_OK); return; } @@ -473,44 +483,14 @@ sync_async_cleanup (SoupMessage *msg) g_free (ad); } - -int -main (int argc, char **argv) +static void +do_sync_async_test (gconstpointer data) { + const char *base_uri = data; SoupSession *session; - const char *base_uri; - test_init (argc, argv, NULL); - apache_init (); - - base_uri = "http://127.0.0.1:47524/"; - get_correct_response (base_uri); - - debug_printf (1, "\nFully async, fast requests\n"); - session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); - g_signal_connect (session, "authenticate", - G_CALLBACK (authenticate), NULL); - do_fully_async_test (session, base_uri, "/", - TRUE, SOUP_STATUS_OK); - do_fully_async_test (session, base_uri, "/Basic/realm1/", - TRUE, SOUP_STATUS_UNAUTHORIZED); - do_fully_async_test (session, base_uri, "/Basic/realm2/", - TRUE, SOUP_STATUS_OK); - soup_test_session_abort_unref (session); - - debug_printf (1, "\nFully async, slow requests\n"); - session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); - g_signal_connect (session, "authenticate", - G_CALLBACK (authenticate), NULL); - do_fully_async_test (session, base_uri, "/", - FALSE, SOUP_STATUS_OK); - do_fully_async_test (session, base_uri, "/Basic/realm1/", - FALSE, SOUP_STATUS_UNAUTHORIZED); - do_fully_async_test (session, base_uri, "/Basic/realm2/", - FALSE, SOUP_STATUS_OK); - soup_test_session_abort_unref (session); + SOUP_TEST_SKIP_IF_NO_APACHE; - debug_printf (1, "\nSynchronously async\n"); session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); g_signal_connect (session, "authenticate", G_CALLBACK (authenticate), NULL); @@ -521,9 +501,33 @@ main (int argc, char **argv) do_synchronously_async_test (session, base_uri, "/Basic/realm2/", SOUP_STATUS_OK); soup_test_session_abort_unref (session); +} + + +int +main (int argc, char **argv) +{ + const char *base_uri; + int ret; + + test_init (argc, argv, NULL); + apache_init (); + + base_uri = "http://127.0.0.1:47524/"; +#if HAVE_APACHE + get_correct_response (base_uri); +#endif + + g_test_add_data_func ("/pull-api/async/fast", base_uri, do_fast_async_test); + g_test_add_data_func ("/pull-api/async/slow", base_uri, do_slow_async_test); + g_test_add_data_func ("/pull-api/sync-async", base_uri, do_sync_async_test); + + ret = g_test_run (); +#if HAVE_APACHE soup_buffer_free (correct_response); +#endif test_cleanup (); - return errors != 0; + return ret; } diff --git a/tests/range-test.c b/tests/range-test.c index 98f56450..c23ba462 100644 --- a/tests/range-test.c +++ b/tests/range-test.c @@ -7,23 +7,6 @@ int total_length; char *test_response; static void -get_full_response (void) -{ - char *contents; - gsize length; - GError *error = NULL; - - if (!g_file_get_contents (SRCDIR "/index.txt", &contents, &length, &error)) { - g_printerr ("Could not read index.txt: %s\n", - error->message); - exit (1); - } - - full_response = soup_buffer_new (SOUP_MEMORY_TAKE, contents, length); - debug_printf (1, "Total response length is %d\n\n", (int)length); -} - -static void check_part (SoupMessageHeaders *headers, const char *body, gsize body_len, gboolean check_start_end, int expected_start, int expected_end) { @@ -33,41 +16,39 @@ check_part (SoupMessageHeaders *headers, const char *body, gsize body_len, soup_message_headers_get_one (headers, "Content-Range")); if (!soup_message_headers_get_content_range (headers, &start, &end, &total_length)) { - debug_printf (1, " Could not find/parse Content-Range\n"); - errors++; + soup_test_assert (FALSE, "Could not find/parse Content-Range"); return; } if (total_length != full_response->length && total_length != -1) { - debug_printf (1, " Unexpected total length %" G_GINT64_FORMAT " in response\n", - total_length); - errors++; + soup_test_assert (FALSE, + "Unexpected total length %" G_GINT64_FORMAT " in response\n", + total_length); return; } if (check_start_end) { if ((expected_start >= 0 && start != expected_start) || (expected_start < 0 && start != full_response->length + expected_start)) { - debug_printf (1, " Unexpected range start %" G_GINT64_FORMAT " in response\n", - start); - errors++; + soup_test_assert (FALSE, + "Unexpected range start %" G_GINT64_FORMAT " in response\n", + start); return; } if ((expected_end >= 0 && end != expected_end) || (expected_end < 0 && end != full_response->length - 1)) { - debug_printf (1, " Unexpected range end %" G_GINT64_FORMAT " in response\n", - end); - errors++; + soup_test_assert (FALSE, + "Unexpected range end %" G_GINT64_FORMAT " in response\n", + end); return; } } if (end - start + 1 != body_len) { - debug_printf (1, " Range length (%d) does not match body length (%d)\n", - (int)(end - start) + 1, - (int)body_len); - errors++; + soup_test_assert (FALSE, "Range length (%d) does not match body length (%d)\n", + (int)(end - start) + 1, + (int)body_len); return; } @@ -76,7 +57,7 @@ check_part (SoupMessageHeaders *headers, const char *body, gsize body_len, static void do_single_range (SoupSession *session, SoupMessage *msg, - int start, int end) + int start, int end, gboolean succeed) { const char *content_type; @@ -85,22 +66,26 @@ do_single_range (SoupSession *session, SoupMessage *msg, soup_session_send_message (session, msg); - if (msg->status_code != SOUP_STATUS_PARTIAL_CONTENT) { - debug_printf (1, " Unexpected status %d %s\n", - msg->status_code, msg->reason_phrase); + if (!succeed) { + soup_test_assert_message_status (msg, SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE); + if (msg->status_code != SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE) { + const char *content_range; + + content_range = soup_message_headers_get_one (msg->response_headers, + "Content-Range"); + if (content_range) + debug_printf (1, " Content-Range: %s\n", content_range); + } + g_object_unref (msg); - errors++; return; } + soup_test_assert_message_status (msg, SOUP_STATUS_PARTIAL_CONTENT); + content_type = soup_message_headers_get_content_type ( msg->response_headers, NULL); - if (content_type && !strcmp (content_type, "multipart/byteranges")) { - debug_printf (1, " Response body should not have been multipart/byteranges\n"); - g_object_unref (msg); - errors++; - return; - } + g_assert_cmpstr (content_type, !=, "multipart/byteranges"); check_part (msg->response_headers, msg->response_body->data, msg->response_body->length, TRUE, start, end); @@ -109,13 +94,13 @@ do_single_range (SoupSession *session, SoupMessage *msg, static void request_single_range (SoupSession *session, const char *uri, - int start, int end) + int start, int end, gboolean succeed) { SoupMessage *msg; msg = soup_message_new ("GET", uri); soup_message_headers_set_range (msg->request_headers, start, end); - do_single_range (session, msg, start, end); + do_single_range (session, msg, start, end, succeed); } static void @@ -131,38 +116,21 @@ do_multi_range (SoupSession *session, SoupMessage *msg, soup_session_send_message (session, msg); - if (msg->status_code != SOUP_STATUS_PARTIAL_CONTENT) { - debug_printf (1, " Unexpected status %d %s\n", - msg->status_code, msg->reason_phrase); - g_object_unref (msg); - errors++; - return; - } + soup_test_assert_message_status (msg, SOUP_STATUS_PARTIAL_CONTENT); content_type = soup_message_headers_get_content_type (msg->response_headers, NULL); - if (!content_type || strcmp (content_type, "multipart/byteranges") != 0) { - debug_printf (1, " Response Content-Type (%s) was not multipart/byteranges\n", - content_type); - g_object_unref (msg); - errors++; - return; - } + g_assert_cmpstr (content_type, ==, "multipart/byteranges"); multipart = soup_multipart_new_from_message (msg->response_headers, msg->response_body); if (!multipart) { - debug_printf (1, " Could not parse multipart\n"); + soup_test_assert (FALSE, "Could not parse multipart"); g_object_unref (msg); - errors++; return; } length = soup_multipart_get_length (multipart); - if (length != expected_return_ranges) { - debug_printf (1, " Expected %d ranges, got %d\n", - expected_return_ranges, length); - errors++; - } + g_assert_cmpint (length, ==, expected_return_ranges); for (i = 0; i < length; i++) { SoupMessageHeaders *headers; @@ -196,7 +164,8 @@ request_double_range (SoupSession *session, const char *uri, if (expected_return_ranges == 1) { do_single_range (session, msg, MIN (first_start, second_start), - MAX (first_end, second_end)); + MAX (first_end, second_end), + TRUE); } else do_multi_range (session, msg, expected_return_ranges); } @@ -223,13 +192,36 @@ request_triple_range (SoupSession *session, const char *uri, if (expected_return_ranges == 1) { do_single_range (session, msg, MIN (first_start, MIN (second_start, third_start)), - MAX (first_end, MAX (second_end, third_end))); + MAX (first_end, MAX (second_end, third_end)), + TRUE); } else do_multi_range (session, msg, expected_return_ranges); } static void -do_range_test (SoupSession *session, const char *uri, gboolean expect_coalesce) +request_semi_invalid_range (SoupSession *session, const char *uri, + int first_good_start, int first_good_end, + int bad_start, int bad_end, + int second_good_start, int second_good_end) +{ + SoupMessage *msg; + SoupRange ranges[3]; + + msg = soup_message_new ("GET", uri); + ranges[0].start = first_good_start; + ranges[0].end = first_good_end; + ranges[1].start = bad_start; + ranges[1].end = bad_end; + ranges[2].start = second_good_start; + ranges[2].end = second_good_end; + soup_message_headers_set_ranges (msg->request_headers, ranges, 3); + + do_multi_range (session, msg, 2); +} + +static void +do_range_test (SoupSession *session, const char *uri, + gboolean expect_coalesce, gboolean expect_partial_coalesce) { int twelfths = full_response->length / 12; @@ -255,7 +247,8 @@ do_range_test (SoupSession *session, const char *uri, gboolean expect_coalesce) /* A: 0, simple request */ debug_printf (1, "Requesting %d-%d\n", 0 * twelfths, 1 * twelfths); request_single_range (session, uri, - 0 * twelfths, 1 * twelfths); + 0 * twelfths, 1 * twelfths, + TRUE); /* B: 11, end-relative request. These two are mostly redundant * in terms of data coverage, but they may still catch @@ -263,10 +256,12 @@ do_range_test (SoupSession *session, const char *uri, gboolean expect_coalesce) */ debug_printf (1, "Requesting %d-\n", 11 * twelfths); request_single_range (session, uri, - 11 * twelfths, -1); + 11 * twelfths, -1, + TRUE); debug_printf (1, "Requesting -%d\n", 1 * twelfths); request_single_range (session, uri, - -1 * twelfths, -1); + -1 * twelfths, -1, + TRUE); /* C: 2 and 5 */ debug_printf (1, "Requesting %d-%d,%d-%d\n", @@ -309,12 +304,43 @@ do_range_test (SoupSession *session, const char *uri, gboolean expect_coalesce) 9 * twelfths, 10 * twelfths + 5, 4 * twelfths, 5 * twelfths, 10 * twelfths - 5, 11 * twelfths, - expect_coalesce ? 2 : 3); + expect_partial_coalesce ? 2 : 3); - if (memcmp (full_response->data, test_response, full_response->length) != 0) { - debug_printf (1, "\nfull_response and test_response don't match\n"); - errors++; - } + soup_assert_cmpmem (full_response->data, full_response->length, + test_response, full_response->length); + + debug_printf (1, "Requesting (invalid) %d-%d\n", + (int) full_response->length + 1, + (int) full_response->length + 100); + request_single_range (session, uri, + full_response->length + 1, full_response->length + 100, + FALSE); + + debug_printf (1, "Requesting (semi-invalid) 1-10,%d-%d,20-30\n", + (int) full_response->length + 1, + (int) full_response->length + 100); + request_semi_invalid_range (session, uri, + 1, 10, + full_response->length + 1, full_response->length + 100, + 20, 30); +} + +static void +do_apache_range_test (void) +{ + SoupSession *session; + + SOUP_TEST_SKIP_IF_NO_APACHE; + + session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); + +#if HAVE_APACHE_2_2 + do_range_test (session, "http://127.0.0.1:47524/", FALSE, FALSE); +#else + do_range_test (session, "http://127.0.0.1:47524/", TRUE, FALSE); +#endif + + soup_test_session_abort_unref (session); } static void @@ -330,38 +356,44 @@ server_handler (SoupServer *server, full_response); } -int -main (int argc, char **argv) +static void +do_libsoup_range_test (void) { SoupSession *session; SoupServer *server; char *base_uri; - test_init (argc, argv, NULL); - apache_init (); - - get_full_response (); - test_response = g_malloc0 (full_response->length); - session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); - debug_printf (1, "1. Testing against apache\n"); - do_range_test (session, "http://127.0.0.1:47524/", FALSE); - - debug_printf (1, "\n2. Testing against SoupServer\n"); server = soup_test_server_new (FALSE); soup_server_add_handler (server, NULL, server_handler, NULL, NULL); base_uri = g_strdup_printf ("http://127.0.0.1:%u/", soup_server_get_port (server)); - do_range_test (session, base_uri, TRUE); + do_range_test (session, base_uri, TRUE, TRUE); g_free (base_uri); soup_test_server_quit_unref (server); soup_test_session_abort_unref (session); +} + +int +main (int argc, char **argv) +{ + int ret; + + test_init (argc, argv, NULL); + apache_init (); + + full_response = soup_test_get_index (); + test_response = g_malloc0 (full_response->length); + + g_test_add_func ("/ranges/apache", do_apache_range_test); + g_test_add_func ("/ranges/libsoup", do_libsoup_range_test); + + ret = g_test_run (); - soup_buffer_free (full_response); g_free (test_response); test_cleanup (); - return errors != 0; + return ret; } diff --git a/tests/redirect-test.c b/tests/redirect-test.c index 59a2f077..ad8dabaa 100644 --- a/tests/redirect-test.c +++ b/tests/redirect-test.c @@ -5,7 +5,9 @@ #include "test-utils.h" +SoupURI *base_uri; char *server2_uri; +SoupSession *async_session, *sync_session; typedef struct { const char *method; @@ -14,104 +16,106 @@ typedef struct { gboolean repeat; } TestRequest; -static struct { +typedef struct { TestRequest requests[3]; guint final_status; - guint request_api_final_status; -} tests[] = { + const char *bugref; +} TestCase; + +static TestCase tests[] = { /* A redirecty response to a GET or HEAD should cause a redirect */ { { { "GET", "/301", 301 }, { "GET", "/", 200 }, - { NULL } }, 200 }, + { NULL } }, 200, NULL }, { { { "GET", "/302", 302 }, { "GET", "/", 200 }, - { NULL } }, 200 }, + { NULL } }, 200, NULL }, { { { "GET", "/303", 303 }, { "GET", "/", 200 }, - { NULL } }, 200 }, + { NULL } }, 200, NULL }, { { { "GET", "/307", 307 }, { "GET", "/", 200 }, - { NULL } }, 200 }, + { NULL } }, 200, NULL }, { { { "HEAD", "/301", 301 }, { "HEAD", "/", 200 }, - { NULL } }, 200 }, + { NULL } }, 200, "551190" }, { { { "HEAD", "/302", 302 }, { "HEAD", "/", 200 }, - { NULL } }, 200 }, + { NULL } }, 200, "551190" }, /* 303 is a nonsensical response to HEAD, but some sites do * it anyway. :-/ */ { { { "HEAD", "/303", 303 }, { "HEAD", "/", 200 }, - { NULL } }, 200 }, + { NULL } }, 200, "600830" }, { { { "HEAD", "/307", 307 }, { "HEAD", "/", 200 }, - { NULL } }, 200 }, + { NULL } }, 200, "551190" }, /* A non-redirecty response to a GET or HEAD should not */ { { { "GET", "/300", 300 }, - { NULL } }, 300 }, + { NULL } }, 300, NULL }, { { { "GET", "/304", 304 }, - { NULL } }, 304 }, + { NULL } }, 304, NULL }, { { { "GET", "/305", 305 }, - { NULL } }, 305 }, + { NULL } }, 305, NULL }, { { { "GET", "/306", 306 }, - { NULL } }, 306 }, + { NULL } }, 306, NULL }, { { { "GET", "/308", 308 }, - { NULL } }, 308 }, + { NULL } }, 308, NULL }, { { { "HEAD", "/300", 300 }, - { NULL } }, 300 }, + { NULL } }, 300, "551190" }, { { { "HEAD", "/304", 304 }, - { NULL } }, 304 }, + { NULL } }, 304, "551190" }, { { { "HEAD", "/305", 305 }, - { NULL } }, 305 }, + { NULL } }, 305, "551190" }, { { { "HEAD", "/306", 306 }, - { NULL } }, 306 }, + { NULL } }, 306, "551190" }, { { { "HEAD", "/308", 308 }, - { NULL } }, 308 }, + { NULL } }, 308, "551190" }, /* Test double-redirect */ { { { "GET", "/301/302", 301 }, { "GET", "/302", 302 }, - { "GET", "/", 200 } }, 200 }, + { "GET", "/", 200 } }, 200, NULL }, { { { "HEAD", "/301/302", 301 }, { "HEAD", "/302", 302 }, - { "HEAD", "/", 200 } }, 200 }, + { "HEAD", "/", 200 } }, 200, "551190" }, /* POST should only automatically redirect on 301, 302 and 303 */ { { { "POST", "/301", 301 }, { "GET", "/", 200 }, - { NULL } }, 200 }, + { NULL } }, 200, "586692" }, { { { "POST", "/302", 302 }, { "GET", "/", 200 }, - { NULL } }, 200 }, + { NULL } }, 200, NULL }, { { { "POST", "/303", 303 }, { "GET", "/", 200 }, - { NULL } }, 200 }, + { NULL } }, 200, NULL }, { { { "POST", "/307", 307 }, - { NULL } }, 307 }, + { NULL } }, 307, NULL }, /* Test behavior with recoverably-bad Location header */ { { { "GET", "/bad", 302 }, { "GET", "/bad%20with%20spaces", 200 }, - { NULL } }, 200 }, + { NULL } }, 200, "566530" }, /* Test behavior with irrecoverably-bad Location header */ { { { "GET", "/bad-no-host", 302 }, - { NULL } }, SOUP_STATUS_MALFORMED, 302 }, + { NULL } }, SOUP_STATUS_MALFORMED, "528882" }, /* Test infinite redirection */ { { { "GET", "/bad-recursive", 302, TRUE }, - { NULL } }, SOUP_STATUS_TOO_MANY_REDIRECTS }, + { NULL } }, SOUP_STATUS_TOO_MANY_REDIRECTS, "604383" }, /* Test redirection to a different server */ { { { "GET", "/server2", 302 }, { "GET", "/on-server2", 200 }, - { NULL } }, 200 }, + { NULL } }, 200, NULL }, }; static const int n_tests = G_N_ELEMENTS (tests); @@ -131,11 +135,7 @@ got_headers (SoupMessage *msg, gpointer user_data) if (!(*treq)->method) return; - if (msg->status_code != (*treq)->status_code) { - debug_printf (1, " - Expected %d !\n", - (*treq)->status_code); - errors++; - } + soup_test_assert_message_status (msg, (*treq)->status_code); } static void @@ -149,35 +149,25 @@ restarted (SoupMessage *msg, gpointer user_data) if ((*treq)->method && !(*treq)->repeat) (*treq)++; - if (!(*treq)->method) { - debug_printf (1, " - Expected to be done!\n"); - errors++; - return; - } + soup_test_assert ((*treq)->method, + "Expected to be done"); - if (strcmp (msg->method, (*treq)->method) != 0) { - debug_printf (1, " - Expected %s !\n", (*treq)->method); - errors++; - } - if (strcmp (uri->path, (*treq)->path) != 0) { - debug_printf (1, " - Expected %s !\n", (*treq)->path); - errors++; - } + g_assert_cmpstr (msg->method, ==, (*treq)->method); + g_assert_cmpstr (uri->path, ==, (*treq)->path); } static void -do_message_api_test (SoupSession *session, SoupURI *base_uri, int n) +do_message_api_test (SoupSession *session, TestCase *test) { SoupURI *uri; SoupMessage *msg; TestRequest *treq; - debug_printf (1, "%2d. %s %s\n", n + 1, - tests[n].requests[0].method, - tests[n].requests[0].path); + if (test->bugref) + g_test_bug (test->bugref); - uri = soup_uri_new_with_base (base_uri, tests[n].requests[0].path); - msg = soup_message_new_from_uri (tests[n].requests[0].method, uri); + uri = soup_uri_new_with_base (base_uri, test->requests[0].path); + msg = soup_message_new_from_uri (test->requests[0].method, uri); soup_uri_free (uri); if (msg->method == SOUP_METHOD_POST) { @@ -187,7 +177,7 @@ do_message_api_test (SoupSession *session, SoupURI *base_uri, int n) strlen ("post body")); } - treq = &tests[n].requests[0]; + treq = &test->requests[0]; g_signal_connect (msg, "got_headers", G_CALLBACK (got_headers), &treq); g_signal_connect (msg, "restarted", @@ -195,53 +185,36 @@ do_message_api_test (SoupSession *session, SoupURI *base_uri, int n) soup_session_send_message (session, msg); - if (msg->status_code != tests[n].final_status) { - debug_printf (1, " - Expected final status of %d, got %d !\n", - tests[n].final_status, msg->status_code); - errors++; - } + soup_test_assert_message_status (msg, test->final_status); g_object_unref (msg); - debug_printf (2, "\n"); } static void -do_request_api_test (SoupSession *session, SoupURI *base_uri, int n) +do_request_api_test (SoupSession *session, TestCase *test) { - SoupRequester *requester = (SoupRequester *)soup_session_get_feature (session, SOUP_TYPE_REQUESTER); SoupURI *uri; - SoupRequest *req; + SoupRequestHTTP *reqh; SoupMessage *msg; TestRequest *treq; GInputStream *stream; GError *error = NULL; - guint final_status; - - debug_printf (1, "%2d. %s %s\n", n + 1, - tests[n].requests[0].method, - tests[n].requests[0].path); - final_status = tests[n].request_api_final_status; - if (!final_status) - final_status = tests[n].final_status; + if (test->bugref) + g_test_bug (test->bugref); - uri = soup_uri_new_with_base (base_uri, tests[n].requests[0].path); - req = soup_requester_request_uri (requester, uri, &error); + uri = soup_uri_new_with_base (base_uri, test->requests[0].path); + reqh = soup_session_request_http_uri (session, + test->requests[0].method, + uri, &error); soup_uri_free (uri); - if (!req) { - debug_printf (1, " could not create request: %s\n", - error->message); + g_assert_no_error (error); + if (error) { g_error_free (error); - errors++; - debug_printf (2, "\n"); return; } - msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (req)); - g_object_set (G_OBJECT (msg), - SOUP_MESSAGE_METHOD, tests[n].requests[0].method, - NULL); - + msg = soup_request_http_get_message (reqh); if (msg->method == SOUP_METHOD_POST) { soup_message_set_request (msg, "text/plain", SOUP_MEMORY_STATIC, @@ -249,208 +222,75 @@ do_request_api_test (SoupSession *session, SoupURI *base_uri, int n) strlen ("post body")); } - treq = &tests[n].requests[0]; + treq = &test->requests[0]; g_signal_connect (msg, "got_headers", G_CALLBACK (got_headers), &treq); g_signal_connect (msg, "restarted", G_CALLBACK (restarted), &treq); - if (SOUP_IS_SESSION_SYNC (session)) - stream = soup_request_send (req, NULL, &error); - else - stream = soup_test_request_send_async_as_sync (req, NULL, &error); - - if (SOUP_STATUS_IS_TRANSPORT_ERROR (final_status)) { - if (stream) { - debug_printf (1, " expected failure (%s) but succeeded", - soup_status_get_phrase (final_status)); - errors++; - g_object_unref (stream); - } - if (error->domain != SOUP_HTTP_ERROR || - error->code != final_status) { - debug_printf (1, " expected '%s' but got '%s'", - soup_status_get_phrase (final_status), - error->message); - errors++; - } + stream = soup_test_request_send (SOUP_REQUEST (reqh), NULL, 0, &error); - g_error_free (error); - g_object_unref (req); - debug_printf (2, "\n"); - return; - } else if (!stream) { - debug_printf (1, " could not send request: %s\n", - error->message); - g_error_free (error); - g_object_unref (req); - errors++; - debug_printf (2, "\n"); + if (SOUP_STATUS_IS_TRANSPORT_ERROR (test->final_status) && + test->final_status != SOUP_STATUS_MALFORMED) { + g_assert_error (error, SOUP_HTTP_ERROR, test->final_status); + g_clear_error (&error); + + g_assert_null (stream); + g_clear_object (&stream); + + g_object_unref (msg); + g_object_unref (reqh); return; } - if (SOUP_IS_SESSION_SYNC (session)) - g_input_stream_close (stream, NULL, &error); - else - soup_test_stream_close_async_as_sync (stream, NULL, &error); + g_assert_no_error (error); if (error) { - debug_printf (1, " could not close stream: %s\n", - error->message); g_error_free (error); - errors++; + g_object_unref (msg); + g_object_unref (reqh); + return; } - if (msg->status_code != final_status) { - debug_printf (1, " - Expected final status of %d, got %d !\n", - final_status, msg->status_code); - errors++; - } + soup_test_request_read_all (SOUP_REQUEST (reqh), stream, NULL, &error); + g_assert_no_error (error); + g_clear_error (&error); - g_object_unref (req); - debug_printf (2, "\n"); -} + soup_test_request_close_stream (SOUP_REQUEST (reqh), stream, NULL, &error); + g_assert_no_error (error); + g_clear_error (&error); + g_object_unref (stream); -static void -do_redirect_tests (SoupURI *base_uri) -{ - SoupSession *session; - int n; - - session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, - SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_REQUESTER, - SOUP_SESSION_USE_THREAD_CONTEXT, TRUE, - NULL); - debug_printf (1, "Async session, SoupMessage\n"); - for (n = 0; n < n_tests; n++) - do_message_api_test (session, base_uri, n); - debug_printf (1, "\nAsync session, SoupRequest\n"); - for (n = 0; n < n_tests; n++) - do_request_api_test (session, base_uri, n); - soup_test_session_abort_unref (session); - - session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, - SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_REQUESTER, - NULL); - debug_printf (1, "\nSync session, SoupMessage\n"); - for (n = 0; n < n_tests; n++) - do_message_api_test (session, base_uri, n); - debug_printf (1, "\nSync session, SoupRequest\n"); - for (n = 0; n < n_tests; n++) - do_request_api_test (session, base_uri, n); - soup_test_session_abort_unref (session); -} - -typedef struct { - SoupSession *session; - SoupMessage *msg1, *msg2; - SoupURI *uri1, *uri2; - SoupSocket *sock1, *sock2; -} ConnectionTestData; + if (test->final_status == SOUP_STATUS_MALFORMED) + g_assert_cmpint (msg->status_code, ==, test->requests[0].status_code); + else + g_assert_cmpint (msg->status_code, ==, test->final_status); -static void -msg2_finished (SoupSession *session, SoupMessage *msg2, gpointer user_data) -{ - if (!SOUP_STATUS_IS_SUCCESSFUL (msg2->status_code)) { - debug_printf (1, " msg2 failed: %d %s\n", - msg2->status_code, msg2->reason_phrase); - errors++; - } + g_object_unref (msg); + g_object_unref (reqh); } static void -unpause_msg1 (SoupMessage *msg2, gpointer user_data) +do_async_msg_api_test (gconstpointer test) { - ConnectionTestData *data = user_data; - - if (!data->sock1) { - debug_printf (1, " msg1 has no connection?\n"); - errors++; - } else if (!data->sock2) { - debug_printf (1, " msg2 has no connection?\n"); - errors++; - } else if (data->sock1 == data->sock2) { - debug_printf (1, " Both messages sharing the same connection\n"); - errors++; - } - - soup_session_unpause_message (data->session, data->msg1); -} - -static gboolean -msg1_just_restarted (gpointer user_data) -{ - ConnectionTestData *data = user_data; - - soup_session_pause_message (data->session, data->msg1); - - data->msg2 = soup_message_new_from_uri ("GET", data->uri2); - - g_signal_connect (data->msg2, "got_body", - G_CALLBACK (unpause_msg1), data); - - soup_session_queue_message (data->session, data->msg2, msg2_finished, data); - return FALSE; + do_message_api_test (async_session, (TestCase *)test); } static void -msg1_about_to_restart (SoupMessage *msg1, gpointer user_data) +do_async_req_api_test (gconstpointer test) { - ConnectionTestData *data = user_data; - - /* Do nothing when loading the redirected-to resource */ - if (!SOUP_STATUS_IS_REDIRECTION (data->msg1->status_code)) - return; - - /* We have to pause msg1 after the I/O finishes, but before - * the queue runs again. - */ - g_idle_add_full (G_PRIORITY_HIGH, msg1_just_restarted, data, NULL); + do_request_api_test (async_session, (TestCase *)test); } static void -request_started (SoupSession *session, SoupMessage *msg, - SoupSocket *socket, gpointer user_data) +do_sync_msg_api_test (gconstpointer test) { - ConnectionTestData *data = user_data; - - if (msg == data->msg1) - data->sock1 = socket; - else if (msg == data->msg2) - data->sock2 = socket; - else - g_warn_if_reached (); + do_message_api_test (sync_session, (TestCase *)test); } static void -do_connection_test (SoupURI *base_uri) +do_sync_req_api_test (gconstpointer test) { - ConnectionTestData data; - - debug_printf (1, "\nConnection reuse\n"); - memset (&data, 0, sizeof (data)); - - data.session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); - g_signal_connect (data.session, "request-started", - G_CALLBACK (request_started), &data); - - data.uri1 = soup_uri_new_with_base (base_uri, "/301"); - data.uri2 = soup_uri_new_with_base (base_uri, "/"); - data.msg1 = soup_message_new_from_uri ("GET", data.uri1); - - g_signal_connect (data.msg1, "got-body", - G_CALLBACK (msg1_about_to_restart), &data); - soup_session_send_message (data.session, data.msg1); - - if (!SOUP_STATUS_IS_SUCCESSFUL (data.msg1->status_code)) { - debug_printf (1, " msg1 failed: %d %s\n", - data.msg1->status_code, data.msg1->reason_phrase); - errors++; - } - g_object_unref (data.msg1); - soup_uri_free (data.uri1); - soup_uri_free (data.uri2); - - soup_test_session_abort_unref (data.session); + do_request_api_test (sync_session, (TestCase *)test); } static void @@ -550,29 +390,23 @@ server2_callback (SoupServer *server, SoupMessage *msg, soup_message_set_status (msg, SOUP_STATUS_OK); } -static 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) { GMainLoop *loop; SoupServer *server, *server2; guint port; - SoupURI *base_uri; + char *path; + int n, ret; - test_init (argc, argv, no_test_entry); + test_init (argc, argv, NULL); server = soup_test_server_new (TRUE); soup_server_add_handler (server, NULL, server_callback, NULL, NULL); port = soup_server_get_port (server); + base_uri = soup_uri_new ("http://127.0.0.1"); + soup_uri_set_port (base_uri, port); server2 = soup_test_server_new (TRUE); soup_server_add_handler (server2, NULL, @@ -582,23 +416,47 @@ main (int argc, char **argv) loop = g_main_loop_new (NULL, TRUE); - if (run_tests) { - base_uri = soup_uri_new ("http://127.0.0.1"); - soup_uri_set_port (base_uri, port); - do_redirect_tests (base_uri); - do_connection_test (base_uri); - soup_uri_free (base_uri); - } else { - g_print ("Listening on port %d\n", port); - g_main_loop_run (loop); + async_session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, + SOUP_SESSION_USE_THREAD_CONTEXT, TRUE, + NULL); + sync_session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL); + + for (n = 0; n < n_tests; n++) { + path = g_strdup_printf ("/redirect/async/msg/%d-%s-%d", n + , tests[n].requests[0].method, + tests[n].requests[0].status_code); + g_test_add_data_func (path, &tests[n], do_async_msg_api_test); + g_free (path); + + path = g_strdup_printf ("/redirect/async/req/%d-%s-%d", n, + tests[n].requests[0].method, + tests[n].requests[0].status_code); + g_test_add_data_func (path, &tests[n], do_async_req_api_test); + g_free (path); + + path = g_strdup_printf ("/redirect/sync/msg/%d-%s-%d", n, + tests[n].requests[0].method, + tests[n].requests[0].status_code); + g_test_add_data_func (path, &tests[n], do_sync_msg_api_test); + g_free (path); + + path = g_strdup_printf ("/redirect/sync/req/%d-%s-%d", n, + tests[n].requests[0].method, + tests[n].requests[0].status_code); + g_test_add_data_func (path, &tests[n], do_sync_req_api_test); + g_free (path); } + ret = g_test_run (); + g_main_loop_unref (loop); - g_free (server2_uri); + soup_uri_free (base_uri); soup_test_server_quit_unref (server); + g_free (server2_uri); soup_test_server_quit_unref (server2); - if (run_tests) - test_cleanup (); - return errors != 0; + soup_test_session_abort_unref (async_session); + soup_test_session_abort_unref (sync_session); + + return ret; } diff --git a/tests/requester-test.c b/tests/requester-test.c index b4e86ea6..39b30bd5 100644 --- a/tests/requester-test.c +++ b/tests/requester-test.c @@ -3,6 +3,9 @@ * Copyright (C) 2011 Red Hat, Inc. */ +/* Kill SoupRequester-related deprecation warnings */ +#define SOUP_VERSION_MIN_REQUIRED SOUP_VERSION_2_40 + #include "test-utils.h" SoupServer *server; @@ -14,24 +17,21 @@ SoupBuffer *response, *auth_response; #define REDIRECT_HTML_BODY "<html><body>Try again</body></html>\r\n" #define AUTH_HTML_BODY "<html><body>Unauthorized</body></html>\r\n" -static void -get_index (void) +static gboolean +slow_finish_message (gpointer msg) { - char *contents; - gsize length; - GError *error = NULL; - - if (!g_file_get_contents (SRCDIR "/index.txt", &contents, &length, &error)) { - g_printerr ("Could not read index.txt: %s\n", - error->message); - exit (1); - } + SoupServer *server = g_object_get_data (G_OBJECT (msg), "server"); - response = soup_buffer_new (SOUP_MEMORY_TAKE, contents, length); + soup_server_unpause_message (server, msg); + return FALSE; +} - auth_response = soup_buffer_new (SOUP_MEMORY_STATIC, - AUTH_HTML_BODY, - strlen (AUTH_HTML_BODY)); +static void +slow_pause_message (SoupMessage *msg, gpointer server) +{ + soup_server_pause_message (server, msg); + soup_add_timeout (soup_server_get_async_context (server), + 1000, slow_finish_message, msg); } static void @@ -67,6 +67,10 @@ server_callback (SoupServer *server, SoupMessage *msg, } else if (strcmp (path, "/non-persistent") == 0) { soup_message_headers_append (msg->response_headers, "Connection", "close"); + } else if (!strcmp (path, "/slow")) { + g_object_set_data (G_OBJECT (msg), "server", server); + g_signal_connect (msg, "wrote-headers", + G_CALLBACK (slow_pause_message), server); } soup_message_set_status (msg, SOUP_STATUS_OK); @@ -99,11 +103,8 @@ stream_closed (GObject *source, GAsyncResult *res, gpointer user_data) GInputStream *stream = G_INPUT_STREAM (source); GError *error = NULL; - if (!g_input_stream_close_finish (stream, res, &error)) { - debug_printf (1, " close failed: %s\n", error->message); - g_error_free (error); - errors++; - } + g_input_stream_close_finish (stream, res, &error); + g_assert_no_error (error); g_main_loop_quit (loop); g_object_unref (stream); } @@ -119,9 +120,8 @@ test_read_ready (GObject *source, GAsyncResult *res, gpointer user_data) nread = g_input_stream_read_finish (stream, res, &error); if (nread == -1) { - debug_printf (1, " read_async failed: %s\n", error->message); + g_assert_no_error (error); g_error_free (error); - errors++; g_input_stream_close (stream, NULL, NULL); g_object_unref (stream); g_main_loop_quit (loop); @@ -150,28 +150,18 @@ auth_test_sent (GObject *source, GAsyncResult *res, gpointer user_data) stream = soup_request_send_finish (SOUP_REQUEST (source), res, &error); if (!stream) { - debug_printf (1, " send_async failed: %s\n", error->message); - errors++; + g_assert_no_error (error); + g_clear_error (&error); g_main_loop_quit (loop); return; } msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (source)); - if (msg->status_code != SOUP_STATUS_UNAUTHORIZED) { - debug_printf (1, " GET failed: %d %s\n", msg->status_code, - msg->reason_phrase); - errors++; - g_main_loop_quit (loop); - return; - } + soup_test_assert_message_status (msg, SOUP_STATUS_UNAUTHORIZED); g_object_unref (msg); content_type = soup_request_get_content_type (SOUP_REQUEST (source)); - if (g_strcmp0 (content_type, "text/html") != 0) { - debug_printf (1, " failed to sniff Content-Type: got %s\n", - content_type ? content_type : "(NULL)"); - errors++; - } + g_assert_cmpstr (content_type, ==, "text/html"); g_input_stream_read_async (stream, buf, sizeof (buf), G_PRIORITY_DEFAULT, NULL, @@ -188,22 +178,13 @@ test_sent (GObject *source, GAsyncResult *res, gpointer user_data) stream = soup_request_send_finish (SOUP_REQUEST (source), res, &error); if (data->cancel) { - if (stream) { - debug_printf (1, " send_async succeeded??\n"); - errors++; - g_input_stream_close (stream, NULL, NULL); - g_object_unref (stream); - } else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { - debug_printf (1, " send_async failed with wrong error: %s\n", error->message); - errors++; - g_clear_error (&error); - } + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); + g_clear_error (&error); g_main_loop_quit (loop); return; } else { + g_assert_no_error (error); if (!stream) { - debug_printf (1, " send_async failed: %s\n", error->message); - errors++; g_main_loop_quit (loop); g_clear_error (&error); return; @@ -211,11 +192,7 @@ test_sent (GObject *source, GAsyncResult *res, gpointer user_data) } content_type = soup_request_get_content_type (SOUP_REQUEST (source)); - if (g_strcmp0 (content_type, "text/plain") != 0) { - debug_printf (1, " failed to sniff Content-Type: got %s\n", - content_type ? content_type : "(NULL)"); - errors++; - } + g_assert_cmpstr (content_type, ==, "text/plain"); g_input_stream_read_async (stream, buf, sizeof (buf), G_PRIORITY_DEFAULT, NULL, @@ -234,6 +211,7 @@ request_started (SoupSession *session, SoupMessage *msg, { SoupSocket **save_socket = user_data; + g_clear_object (save_socket); *save_socket = g_object_ref (socket); } @@ -250,11 +228,17 @@ do_async_test (SoupSession *session, SoupURI *uri, SoupMessage *msg; RequestData data; - requester = SOUP_REQUESTER (soup_session_get_feature (session, SOUP_TYPE_REQUESTER)); + if (SOUP_IS_SESSION_ASYNC (session)) + requester = SOUP_REQUESTER (soup_session_get_feature (session, SOUP_TYPE_REQUESTER)); + else + requester = NULL; data.body = g_string_new (NULL); data.cancel = cancel; - request = soup_requester_request_uri (requester, uri, NULL); + if (requester) + request = soup_requester_request_uri (requester, uri, NULL); + else + request = soup_session_request_uri (session, uri, NULL); msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (request)); if (cancel) { @@ -275,43 +259,20 @@ do_async_test (SoupSession *session, SoupURI *uri, g_signal_handler_disconnect (session, started_id); - if (msg->status_code != expected_status) { - debug_printf (1, " GET failed: %d %s (expected %d)\n", - msg->status_code, msg->reason_phrase, - expected_status); - g_object_unref (msg); - errors++; - return; - } + soup_test_assert_message_status (msg, expected_status); g_object_unref (msg); - if (!expected_response) { - if (data.body->len) { - debug_printf (1, " body length mismatch: expected 0, got %d\n", - (int)data.body->len); - errors++; - } - } else if (data.body->len != expected_response->length) { - debug_printf (1, " body length mismatch: expected %d, got %d\n", - (int)expected_response->length, (int)data.body->len); - errors++; - } else if (memcmp (data.body->str, expected_response->data, - expected_response->length) != 0) { - debug_printf (1, " body data mismatch\n"); - errors++; - } + if (expected_response) { + soup_assert_cmpmem (data.body->str, data.body->len, + expected_response->data, expected_response->length); + } else + g_assert_cmpint (data.body->len, ==, 0); + + if (persistent) + g_assert_true (soup_socket_is_connected (socket)); + else + g_assert_false (soup_socket_is_connected (socket)); - if (persistent) { - if (!soup_socket_is_connected (socket)) { - debug_printf (1, " socket not still connected!\n"); - errors++; - } - } else { - if (soup_socket_is_connected (socket)) { - debug_printf (1, " socket still connected!\n"); - errors++; - } - } g_object_unref (socket); g_string_free (data.body, TRUE); @@ -323,19 +284,21 @@ do_test_for_thread_and_context (SoupSession *session, const char *base_uri) SoupRequester *requester; SoupURI *uri; - requester = soup_requester_new (); - soup_session_add_feature (session, SOUP_SESSION_FEATURE (requester)); - g_object_unref (requester); + if (SOUP_IS_SESSION_ASYNC (session)) { + requester = soup_requester_new (); + soup_session_add_feature (session, SOUP_SESSION_FEATURE (requester)); + g_object_unref (requester); + } soup_session_add_feature_by_type (session, SOUP_TYPE_CONTENT_SNIFFER); - debug_printf (1, " basic test\n"); + debug_printf (1, " basic test\n"); uri = soup_uri_new (base_uri); do_async_test (session, uri, test_sent, SOUP_STATUS_OK, response, TRUE, FALSE); soup_uri_free (uri); - debug_printf (1, " chunked test\n"); + debug_printf (1, " chunked test\n"); uri = soup_uri_new (base_uri); soup_uri_set_path (uri, "/chunked"); do_async_test (session, uri, test_sent, @@ -343,7 +306,7 @@ do_test_for_thread_and_context (SoupSession *session, const char *base_uri) TRUE, FALSE); soup_uri_free (uri); - debug_printf (1, " auth test\n"); + debug_printf (1, " auth test\n"); uri = soup_uri_new (base_uri); soup_uri_set_path (uri, "/auth"); do_async_test (session, uri, auth_test_sent, @@ -351,7 +314,7 @@ do_test_for_thread_and_context (SoupSession *session, const char *base_uri) TRUE, FALSE); soup_uri_free (uri); - debug_printf (1, " non-persistent test\n"); + debug_printf (1, " non-persistent test\n"); uri = soup_uri_new (base_uri); soup_uri_set_path (uri, "/non-persistent"); do_async_test (session, uri, test_sent, @@ -359,7 +322,7 @@ do_test_for_thread_and_context (SoupSession *session, const char *base_uri) FALSE, FALSE); soup_uri_free (uri); - debug_printf (1, " cancellation test\n"); + debug_printf (1, " cancellation test\n"); uri = soup_uri_new (base_uri); soup_uri_set_path (uri, "/"); do_async_test (session, uri, test_sent, @@ -369,11 +332,23 @@ do_test_for_thread_and_context (SoupSession *session, const char *base_uri) } static void -do_simple_test (const char *uri) +do_simple_plain_test (gconstpointer uri) +{ + SoupSession *session; + + g_test_bug ("653707"); + + session = soup_test_session_new (SOUP_TYPE_SESSION, NULL); + do_test_for_thread_and_context (session, uri); + soup_test_session_abort_unref (session); +} + +static void +do_simple_async_test (gconstpointer uri) { SoupSession *session; - debug_printf (1, "Simple streaming test\n"); + g_test_bug ("653707"); session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, SOUP_SESSION_USE_THREAD_CONTEXT, TRUE, @@ -382,16 +357,18 @@ do_simple_test (const char *uri) soup_test_session_abort_unref (session); } -static gpointer -do_test_with_context (const char *uri) +static void +do_test_with_context_and_type (const char *uri, gboolean plain_session) { GMainContext *async_context; SoupSession *session; + g_test_bug ("653707"); + async_context = g_main_context_new (); g_main_context_push_thread_default (async_context); - session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, + session = soup_test_session_new (plain_session ? SOUP_TYPE_SESSION : SOUP_TYPE_SESSION_ASYNC, SOUP_SESSION_ASYNC_CONTEXT, async_context, SOUP_SESSION_USE_THREAD_CONTEXT, TRUE, NULL); @@ -401,25 +378,52 @@ do_test_with_context (const char *uri) g_main_context_pop_thread_default (async_context); g_main_context_unref (async_context); - return NULL; } static void -do_context_test (const char *uri) +do_async_test_with_context (gconstpointer uri) +{ + do_test_with_context_and_type (uri, FALSE); +} + +static void +do_plain_test_with_context (gconstpointer uri) +{ + do_test_with_context_and_type (uri, TRUE); +} + +static gpointer +async_test_thread (gpointer uri) { - debug_printf (1, "Streaming with a non-default-context\n"); - do_test_with_context (uri); + do_test_with_context_and_type (uri, TRUE); + return NULL; +} + +static gpointer +plain_test_thread (gpointer uri) +{ + do_test_with_context_and_type (uri, FALSE); + return NULL; } static void -do_thread_test (const char *uri) +do_async_test_in_thread (gconstpointer uri) { GThread *thread; - debug_printf (1, "Streaming in another thread\n"); + thread = g_thread_new ("do_async_test_in_thread", + async_test_thread, + (gpointer)uri); + g_thread_join (thread); +} - thread = g_thread_new ("do_test_with_context", - (GThreadFunc)do_test_with_context, +static void +do_plain_test_in_thread (gconstpointer uri) +{ + GThread *thread; + + thread = g_thread_new ("do_plain_test_in_thread", + plain_test_thread, (gpointer)uri); g_thread_join (thread); } @@ -451,173 +455,381 @@ do_sync_request (SoupSession *session, SoupRequest *request, in = soup_request_send (request, NULL, &error); g_signal_handler_disconnect (session, started_id); if (cancel) { - if (in) { - debug_printf (1, " send succeeded??\n"); - errors++; - g_input_stream_close (in, NULL, NULL); - g_object_unref (in); - } else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { - debug_printf (1, " send failed with wrong error: %s\n", error->message); - errors++; - g_clear_error (&error); - } + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); + g_clear_error (&error); g_object_unref (msg); + g_object_unref (socket); return; } else if (!in) { - debug_printf (1, " soup_request_send failed: %s\n", - error->message); - g_object_unref (msg); + g_assert_no_error (error); g_clear_error (&error); - errors++; - return; - } - - if (msg->status_code != expected_status) { - debug_printf (1, " GET failed: %d %s\n", msg->status_code, - msg->reason_phrase); g_object_unref (msg); - g_object_unref (in); - errors++; + g_object_unref (socket); return; } + + soup_test_assert_message_status (msg, expected_status); g_object_unref (msg); body = g_string_new (NULL); do { nread = g_input_stream_read (in, buf, sizeof (buf), NULL, &error); + g_assert_no_error (error); if (nread == -1) { - debug_printf (1, " g_input_stream_read failed: %s\n", - error->message); g_clear_error (&error); - errors++; break; } g_string_append_len (body, buf, nread); } while (nread > 0); - if (!g_input_stream_close (in, NULL, &error)) { - debug_printf (1, " g_input_stream_close failed: %s\n", - error->message); - g_clear_error (&error); - errors++; - } + g_input_stream_close (in, NULL, &error); + g_assert_no_error (error); + g_clear_error (&error); g_object_unref (in); - if (!expected_response) { - if (body->len) { - debug_printf (1, " body length mismatch: expected 0, got %d\n", - (int)body->len); - errors++; - } - } else if (body->len != expected_response->length) { - debug_printf (1, " body length mismatch: expected %d, got %d\n", - (int)expected_response->length, (int)body->len); - errors++; - } else if (memcmp (body->str, expected_response->data, body->len) != 0) { - debug_printf (1, " body data mismatch\n"); - errors++; - } + if (expected_response) { + soup_assert_cmpmem (body->str, body->len, + expected_response->data, expected_response->length); + } else + g_assert_cmpint (body->len, ==, 0); - if (persistent) { - if (!soup_socket_is_connected (socket)) { - debug_printf (1, " socket not still connected!\n"); - errors++; - } - } else { - if (soup_socket_is_connected (socket)) { - debug_printf (1, " socket still connected!\n"); - errors++; - } - } + if (persistent) + g_assert_true (soup_socket_is_connected (socket)); + else + g_assert_false (soup_socket_is_connected (socket)); g_object_unref (socket); g_string_free (body, TRUE); } static void -do_sync_test (const char *uri_string) +do_sync_tests_for_session (SoupSession *session, const char *uri_string) { - SoupSession *session; SoupRequester *requester; SoupRequest *request; SoupURI *uri; - debug_printf (1, "Sync streaming\n"); - - session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL); - requester = soup_requester_new (); - soup_session_add_feature (session, SOUP_SESSION_FEATURE (requester)); - g_object_unref (requester); + requester = SOUP_REQUESTER (soup_session_get_feature (session, SOUP_TYPE_REQUESTER)); uri = soup_uri_new (uri_string); - debug_printf (1, " basic test\n"); - request = soup_requester_request_uri (requester, uri, NULL); + debug_printf (1, " basic test\n"); + if (requester) + request = soup_requester_request_uri (requester, uri, NULL); + else + request = soup_session_request_uri (session, uri, NULL); do_sync_request (session, request, SOUP_STATUS_OK, response, TRUE, FALSE); g_object_unref (request); - debug_printf (1, " chunked test\n"); + debug_printf (1, " chunked test\n"); soup_uri_set_path (uri, "/chunked"); - request = soup_requester_request_uri (requester, uri, NULL); + if (requester) + request = soup_requester_request_uri (requester, uri, NULL); + else + request = soup_session_request_uri (session, uri, NULL); do_sync_request (session, request, SOUP_STATUS_OK, response, TRUE, FALSE); g_object_unref (request); - debug_printf (1, " auth test\n"); + debug_printf (1, " auth test\n"); soup_uri_set_path (uri, "/auth"); - request = soup_requester_request_uri (requester, uri, NULL); + if (requester) + request = soup_requester_request_uri (requester, uri, NULL); + else + request = soup_session_request_uri (session, uri, NULL); do_sync_request (session, request, SOUP_STATUS_UNAUTHORIZED, auth_response, TRUE, FALSE); g_object_unref (request); - debug_printf (1, " non-persistent test\n"); + debug_printf (1, " non-persistent test\n"); soup_uri_set_path (uri, "/non-persistent"); - request = soup_requester_request_uri (requester, uri, NULL); + if (requester) + request = soup_requester_request_uri (requester, uri, NULL); + else + request = soup_session_request_uri (session, uri, NULL); do_sync_request (session, request, SOUP_STATUS_OK, response, FALSE, FALSE); g_object_unref (request); - debug_printf (1, " cancel test\n"); + debug_printf (1, " cancel test\n"); soup_uri_set_path (uri, "/"); - request = soup_requester_request_uri (requester, uri, NULL); + if (requester) + request = soup_requester_request_uri (requester, uri, NULL); + else + request = soup_session_request_uri (session, uri, NULL); do_sync_request (session, request, SOUP_STATUS_FORBIDDEN, NULL, TRUE, TRUE); g_object_unref (request); + soup_uri_free (uri); +} + +static void +do_plain_sync_test (gconstpointer uri) +{ + SoupSession *session; + + session = soup_test_session_new (SOUP_TYPE_SESSION, NULL); + do_sync_tests_for_session (session, uri); soup_test_session_abort_unref (session); +} + +static void +do_sync_sync_test (gconstpointer uri) +{ + SoupSession *session; + SoupRequester *requester; + + session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL); + requester = soup_requester_new (); + soup_session_add_feature (session, SOUP_SESSION_FEATURE (requester)); + g_object_unref (requester); + do_sync_tests_for_session (session, uri); + soup_test_session_abort_unref (session); +} + +static void +do_null_char_request (SoupSession *session, const char *encoded_data, + const char *expected_data, int expected_len) +{ + GError *error = NULL; + GInputStream *stream; + SoupRequest *request; + SoupURI *uri; + char *uri_string, buf[256]; + gsize nread; + + uri_string = g_strdup_printf ("data:text/html,%s", encoded_data); + uri = soup_uri_new (uri_string); + g_free (uri_string); + + request = soup_session_request_uri (session, uri, NULL); + stream = soup_test_request_send (request, NULL, 0, &error); + g_assert_no_error (error); + if (error) { + g_error_free (error); + g_object_unref (request); + soup_uri_free (uri); + return; + } + + g_input_stream_read_all (stream, buf, sizeof (buf), &nread, NULL, &error); + g_assert_no_error (error); + g_clear_error (&error); + + soup_test_request_close_stream (request, stream, NULL, &error); + g_assert_no_error (error); + g_clear_error (&error); + + soup_assert_cmpmem (buf, nread, expected_data, expected_len); + + g_object_unref (stream); + g_object_unref (request); soup_uri_free (uri); } +static void +do_null_char_test_for_session (SoupSession *session) +{ + static struct { + const char *encoded_data; + const char *expected_data; + int expected_len; + } test_cases[] = { + { "%3Cscript%3Ea%3D'%00'%3C%2Fscript%3E", "<script>a='\0'</script>", 22 }, + { "%00%3Cscript%3Ea%3D42%3C%2Fscript%3E", "\0<script>a=42</script>", 22 }, + { "%3Cscript%3E%00%3Cbr%2F%3E%3C%2Fscript%3E%00", "<script>\0<br/></script>\0", 24 }, + }; + static int num_test_cases = G_N_ELEMENTS(test_cases); + int i; + + for (i = 0; i < num_test_cases; i++) { + do_null_char_request (session, test_cases[i].encoded_data, + test_cases[i].expected_data, test_cases[i].expected_len); + } +} + +static void +do_plain_null_char_test (void) +{ + SoupSession *session; + + session = soup_test_session_new (SOUP_TYPE_SESSION, NULL); + do_null_char_test_for_session (session); + soup_test_session_abort_unref (session); +} + +static void +do_async_null_char_test (void) +{ + SoupSession *session; + + session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, + SOUP_SESSION_USE_THREAD_CONTEXT, TRUE, + NULL); + do_null_char_test_for_session (session); + soup_test_session_abort_unref (session); +} + +static void +close_test_msg_finished (SoupMessage *msg, + gpointer user_data) +{ + gboolean *finished = user_data; + + *finished = TRUE; +} + +static void +do_close_test_for_session (SoupSession *session, + SoupURI *uri) +{ + GError *error = NULL; + GInputStream *stream; + SoupRequest *request; + guint64 start, end; + GCancellable *cancellable; + SoupMessage *msg; + gboolean finished = FALSE; + + debug_printf (1, " normal close\n"); + + request = soup_session_request_uri (session, uri, NULL); + stream = soup_test_request_send (request, NULL, 0, &error); + g_assert_no_error (error); + if (error) { + g_error_free (error); + g_object_unref (request); + return; + } + + start = g_get_monotonic_time (); + soup_test_request_close_stream (request, stream, NULL, &error); + g_assert_no_error (error); + g_clear_error (&error); + end = g_get_monotonic_time (); + + g_assert_cmpint (end - start, <=, 500000); + + g_object_unref (stream); + g_object_unref (request); + + + debug_printf (1, " error close\n"); + + request = soup_session_request_uri (session, uri, NULL); + msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (request)); + g_signal_connect (msg, "finished", G_CALLBACK (close_test_msg_finished), &finished); + g_object_unref (msg); + + stream = soup_test_request_send (request, NULL, 0, &error); + g_assert_no_error (error); + if (error) { + g_error_free (error); + g_object_unref (request); + return; + } + + cancellable = g_cancellable_new (); + g_cancellable_cancel (cancellable); + soup_test_request_close_stream (request, stream, cancellable, &error); + if (error) + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); + g_clear_error (&error); + + g_assert_true (finished); + + g_object_unref (stream); + g_object_unref (request); +} + +static void +do_async_close_test (gconstpointer uri) +{ + SoupSession *session; + SoupURI *slow_uri; + + g_test_bug ("695652"); + g_test_bug ("711260"); + + slow_uri = soup_uri_new (uri); + soup_uri_set_path (slow_uri, "/slow"); + + session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, + SOUP_SESSION_USE_THREAD_CONTEXT, TRUE, + NULL); + do_close_test_for_session (session, slow_uri); + soup_test_session_abort_unref (session); + + soup_uri_free (slow_uri); +} + +static void +do_sync_close_test (gconstpointer uri) +{ + SoupSession *session; + SoupURI *slow_uri; + + g_test_bug ("695652"); + g_test_bug ("711260"); + + slow_uri = soup_uri_new (uri); + soup_uri_set_path (slow_uri, "/slow"); + + debug_printf (1, " SoupSessionSync\n"); + session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, + SOUP_SESSION_USE_THREAD_CONTEXT, TRUE, + NULL); + do_close_test_for_session (session, slow_uri); + soup_test_session_abort_unref (session); + + soup_uri_free (slow_uri); +} + int main (int argc, char **argv) { char *uri; + int ret; test_init (argc, argv, NULL); - get_index (); + + response = soup_test_get_index (); + auth_response = soup_buffer_new (SOUP_MEMORY_STATIC, + AUTH_HTML_BODY, + strlen (AUTH_HTML_BODY)); server = soup_test_server_new (TRUE); soup_server_add_handler (server, NULL, server_callback, NULL, NULL); uri = g_strdup_printf ("http://127.0.0.1:%u/foo", soup_server_get_port (server)); - do_simple_test (uri); - do_thread_test (uri); - do_context_test (uri); - do_sync_test (uri); + g_test_add_data_func ("/requester/simple/SoupSession", uri, do_simple_plain_test); + g_test_add_data_func ("/requester/simple/SoupSessionAsync", uri, do_simple_async_test); + g_test_add_data_func ("/requester/threaded/SoupSession", uri, do_plain_test_in_thread); + g_test_add_data_func ("/requester/threaded/SoupSessionAsync", uri, do_async_test_in_thread); + g_test_add_data_func ("/requester/context/SoupSession", uri, do_plain_test_with_context); + g_test_add_data_func ("/requester/context/SoupSessionAsync", uri, do_async_test_with_context); + g_test_add_data_func ("/requester/sync/SoupSession", uri, do_plain_sync_test); + g_test_add_data_func ("/requester/sync/SoupSessionSync", uri, do_sync_sync_test); + g_test_add_func ("/requester/null-char/SoupSession", do_plain_null_char_test); + g_test_add_func ("/requester/null-char/SoupSessionAsync", do_async_null_char_test); + g_test_add_data_func ("/requester/close/SoupSessionAsync", uri, do_async_close_test); + g_test_add_data_func ("/requester/close/SoupSessionSync", uri, do_sync_close_test); + + ret = g_test_run (); g_free (uri); - soup_buffer_free (response); soup_buffer_free (auth_response); soup_test_server_quit_unref (server); test_cleanup (); - return errors != 0; + return ret; } diff --git a/tests/resource-test.c b/tests/resource-test.c new file mode 100644 index 00000000..6fcb899e --- /dev/null +++ b/tests/resource-test.c @@ -0,0 +1,220 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2012 Igalia S.L. + */ + +#include "test-utils.h" + +SoupBuffer *index_buffer; + +typedef struct { + GString *body; + char buffer[1024]; + GMainLoop *loop; +} AsyncRequestData; + +static void +stream_closed (GObject *source, GAsyncResult *result, gpointer user_data) +{ + GInputStream *in = G_INPUT_STREAM (source); + AsyncRequestData *data = user_data; + GError *error = NULL; + + g_input_stream_close_finish (in, result, &error); + g_assert_no_error (error); + g_main_loop_quit (data->loop); + g_object_unref (in); +} + +static void +test_read_ready (GObject *source, GAsyncResult *result, gpointer user_data) +{ + GInputStream *in = G_INPUT_STREAM (source); + AsyncRequestData *data = user_data; + gssize nread; + GError *error = NULL; + + nread = g_input_stream_read_finish (in, result, &error); + if (nread == -1) { + g_assert_no_error (error); + g_clear_error (&error); + g_input_stream_close (in, NULL, NULL); + g_object_unref (in); + return; + } else if (nread == 0) { + g_input_stream_close_async (in, G_PRIORITY_DEFAULT, NULL, + stream_closed, data); + return; + } + + g_string_append_len (data->body, data->buffer, nread); + g_input_stream_read_async (in, data->buffer, sizeof (data->buffer), + G_PRIORITY_DEFAULT, NULL, + test_read_ready, data); +} + +static void +async_request_sent (GObject *source, GAsyncResult *result, gpointer user_data) +{ + GInputStream *in; + AsyncRequestData *data = user_data; + GError *error = NULL; + + in = soup_request_send_finish (SOUP_REQUEST (source), result, &error); + if (!in) { + g_assert_no_error (error); + g_clear_error (&error); + return; + } + + g_input_stream_read_async (in, data->buffer, sizeof (data->buffer), + G_PRIORITY_DEFAULT, NULL, + test_read_ready, data); +} + +static void +do_async_request (SoupRequest *request) +{ + AsyncRequestData data; + + data.body = g_string_new (NULL); + soup_request_send_async (request, NULL, async_request_sent, &data); + + data.loop = g_main_loop_new (soup_session_get_async_context (soup_request_get_session (request)), TRUE); + g_main_loop_run (data.loop); + g_main_loop_unref (data.loop); + + soup_assert_cmpmem (data.body->str, data.body->len, + index_buffer->data, index_buffer->length); + g_string_free (data.body, TRUE); +} + +static void +do_sync_request (SoupRequest *request) +{ + GInputStream *in; + GString *body; + char buffer[1024]; + gssize nread; + GError *error = NULL; + + in = soup_request_send (request, NULL, &error); + if (!in) { + g_assert_no_error (error); + g_clear_error (&error); + return; + } + + body = g_string_new (NULL); + do { + nread = g_input_stream_read (in, buffer, sizeof (buffer), + NULL, &error); + if (nread == -1) { + g_assert_no_error (error); + g_clear_error (&error); + break; + } + g_string_append_len (body, buffer, nread); + } while (nread > 0); + + g_input_stream_close (in, NULL, &error); + g_assert_no_error (error); + g_clear_error (&error); + g_object_unref (in); + + soup_assert_cmpmem (body->str, body->len, index_buffer->data, index_buffer->length); + g_string_free (body, TRUE); +} + +static void +do_request (const char *uri_string, gconstpointer type) +{ + SoupSession *session; + SoupRequest *request; + GError *error = NULL; + + session = soup_test_session_new (GPOINTER_TO_SIZE (type), + SOUP_SESSION_USE_THREAD_CONTEXT, TRUE, + NULL); + + request = soup_session_request (session, uri_string, &error); + g_assert_no_error (error); + + if (SOUP_IS_SESSION_ASYNC (session)) + do_async_request (request); + else + do_sync_request (request); + + g_object_unref (request); + soup_test_session_abort_unref (session); +} + +static void +do_request_file_test (gconstpointer type) +{ + GFile *index; + char *uri_string; + + index = g_file_new_for_path (g_test_get_filename (G_TEST_DIST, "index.txt", NULL)); + uri_string = g_file_get_uri (index); + g_object_unref (index); + + do_request (uri_string, type); + g_free (uri_string); +} + +static void +do_request_data_test (gconstpointer type) +{ + gchar *base64; + char *uri_string; + + base64 = g_base64_encode ((const guchar *)index_buffer->data, index_buffer->length); + uri_string = g_strdup_printf ("data:text/plain;charset=utf8;base64,%s", base64); + g_free (base64); + + do_request (uri_string, type); + g_free (uri_string); +} + +static void +do_request_gresource_test (gconstpointer type) +{ + do_request ("resource:///org/gnome/libsoup/tests/index.txt", type); +} + +int +main (int argc, char **argv) +{ + int ret; + + test_init (argc, argv, NULL); + + index_buffer = soup_test_get_index (); + soup_test_register_resources (); + + g_test_add_data_func ("/resource/sync/file", + GSIZE_TO_POINTER (SOUP_TYPE_SESSION_SYNC), + do_request_file_test); + g_test_add_data_func ("/resource/sync/data", + GSIZE_TO_POINTER (SOUP_TYPE_SESSION_SYNC), + do_request_data_test); + g_test_add_data_func ("/resource/sync/gresource", + GSIZE_TO_POINTER (SOUP_TYPE_SESSION_SYNC), + do_request_gresource_test); + + g_test_add_data_func ("/resource/async/file", + GSIZE_TO_POINTER (SOUP_TYPE_SESSION_ASYNC), + do_request_file_test); + g_test_add_data_func ("/resource/async/data", + GSIZE_TO_POINTER (SOUP_TYPE_SESSION_ASYNC), + do_request_data_test); + g_test_add_data_func ("/resource/async/gresource", + GSIZE_TO_POINTER (SOUP_TYPE_SESSION_ASYNC), + do_request_gresource_test); + + ret = g_test_run (); + + test_cleanup (); + return ret; +} diff --git a/tests/resources/feed.rdf b/tests/resources/feed.rdf new file mode 100644 index 00000000..f3d9e276 --- /dev/null +++ b/tests/resources/feed.rdf @@ -0,0 +1,32 @@ +<?xml version="1.0"?> + +<!-- RDF Site Summary (RSS) 1.0 + http://groups.yahoo.com/group/rss-dev/files/specification.html + Section 5.3 + --> + +<rdf:RDF + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns="http://purl.org/rss/1.0/"> + + <channel rdf:about="http://www.xml.com/xml/news.rss"> + <title>XML.com</title> + <link>http://xml.com/pub</link> + <description> + XML.com features a rich mix of information and services + for the XML community. + </description> + + <image rdf:resource="http://xml.com/universal/images/xml_tiny.gif" /> + + <items> + <rdf:Seq> + <rdf:li resource="http://xml.com/pub/2000/08/09/xslt/xslt.html" /> + <rdf:li resource="http://xml.com/pub/2000/08/09/rdfdb/index.html" /> + </rdf:Seq> + </items> + + <textinput rdf:resource="http://search.xml.com" /> + </channel> + +</rdf:RDF> diff --git a/tests/resources/home.jpg b/tests/resources/home.jpg Binary files differnew file mode 100644 index 00000000..ac1f3bbc --- /dev/null +++ b/tests/resources/home.jpg diff --git a/tests/resources/home.png b/tests/resources/home.png Binary files differnew file mode 100644 index 00000000..0bb82bac --- /dev/null +++ b/tests/resources/home.png diff --git a/tests/resources/html_binary.html b/tests/resources/html_binary.html index 9200dd42..d443048c 100644 --- a/tests/resources/html_binary.html +++ b/tests/resources/html_binary.html @@ -1 +1 @@ -<HTML +<HTML diff --git a/tests/resources/leading_space.html b/tests/resources/leading_space.html new file mode 100644 index 00000000..a640d653 --- /dev/null +++ b/tests/resources/leading_space.html @@ -0,0 +1,12 @@ + + +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> +<html> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> +<title></title> +</head> +<body> +<h1>GNOME!</h1> +</body> +</html> diff --git a/tests/resources/test.aiff b/tests/resources/test.aiff Binary files differnew file mode 100644 index 00000000..9a1ecbb2 --- /dev/null +++ b/tests/resources/test.aiff diff --git a/tests/resources/test.mp4 b/tests/resources/test.mp4 Binary files differnew file mode 100644 index 00000000..d278c8ad --- /dev/null +++ b/tests/resources/test.mp4 diff --git a/tests/resources/test.ogg b/tests/resources/test.ogg Binary files differnew file mode 100644 index 00000000..e8f49ac3 --- /dev/null +++ b/tests/resources/test.ogg diff --git a/tests/resources/test.wav b/tests/resources/test.wav Binary files differnew file mode 100644 index 00000000..11660b29 --- /dev/null +++ b/tests/resources/test.wav diff --git a/tests/resources/test.webm b/tests/resources/test.webm Binary files differnew file mode 100644 index 00000000..7e53d0b4 --- /dev/null +++ b/tests/resources/test.webm diff --git a/tests/resources/text.txt b/tests/resources/text.txt new file mode 100644 index 00000000..ff7066f6 --- /dev/null +++ b/tests/resources/text.txt @@ -0,0 +1 @@ +This is just text. diff --git a/tests/resources/tux.webp b/tests/resources/tux.webp Binary files differnew file mode 100644 index 00000000..8764f066 --- /dev/null +++ b/tests/resources/tux.webp diff --git a/tests/server-auth-test.c b/tests/server-auth-test.c index 757e065a..f386f526 100644 --- a/tests/server-auth-test.c +++ b/tests/server-auth-test.c @@ -5,6 +5,8 @@ #include "test-utils.h" +static SoupURI *base_uri; + static struct { gboolean client_sent_basic, client_sent_digest; gboolean server_requested_basic, server_requested_digest; @@ -21,7 +23,7 @@ curl_exited (GPid pid, int status, gpointer data) } static void -do_test (int n, SoupURI *base_uri, const char *path, +do_test (SoupURI *base_uri, const char *path, gboolean good_user, gboolean good_password, gboolean offer_basic, gboolean offer_digest, gboolean client_sends_basic, gboolean client_sends_digest, @@ -34,18 +36,14 @@ do_test (int n, SoupURI *base_uri, const char *path, GPid pid; gboolean done; - debug_printf (1, "%2d. %s, %soffer Basic, %soffer Digest, %s user, %s password\n", - n, path, offer_basic ? "" : "don't ", - offer_digest ? "" : "don't ", - good_user ? "good" : "bad", - good_password ? "good" : "bad"); - uri = soup_uri_new_with_base (base_uri, path); uri_str = soup_uri_to_string (uri, FALSE); soup_uri_free (uri); args = g_ptr_array_new (); g_ptr_array_add (args, "curl"); + g_ptr_array_add (args, "--noproxy"); + g_ptr_array_add (args, "*"); g_ptr_array_add (args, "-f"); g_ptr_array_add (args, "-s"); if (offer_basic || offer_digest) { @@ -86,135 +84,103 @@ do_test (int n, SoupURI *base_uri, const char *path, g_ptr_array_free (args, TRUE); g_free (uri_str); - if (server_requests_basic != test_data.server_requested_basic) { - errors++; - if (test_data.server_requested_basic) - debug_printf (1, " Server sent WWW-Authenticate: Basic, but shouldn't have!\n"); - else - debug_printf (1, " Server didn't send WWW-Authenticate: Basic, but should have!\n"); - } - if (server_requests_digest != test_data.server_requested_digest) { - errors++; - if (test_data.server_requested_digest) - debug_printf (1, " Server sent WWW-Authenticate: Digest, but shouldn't have!\n"); - else - debug_printf (1, " Server didn't send WWW-Authenticate: Digest, but should have!\n"); - } - if (client_sends_basic != test_data.client_sent_basic) { - errors++; - if (test_data.client_sent_basic) - debug_printf (1, " Client sent Authorization: Basic, but shouldn't have!\n"); - else - debug_printf (1, " Client didn't send Authorization: Basic, but should have!\n"); - } - if (client_sends_digest != test_data.client_sent_digest) { - errors++; - if (test_data.client_sent_digest) - debug_printf (1, " Client sent Authorization: Digest, but shouldn't have!\n"); - else - debug_printf (1, " Client didn't send Authorization: Digest, but should have!\n"); - } - if (success && !test_data.succeeded) { - errors++; - debug_printf (1, " Should have succeeded, but didn't!\n"); - } else if (!success && test_data.succeeded) { - errors++; - debug_printf (1, " Should not have succeeded, but did!\n"); - } + g_assert_cmpint (server_requests_basic, ==, test_data.server_requested_basic); + g_assert_cmpint (server_requests_digest, ==, test_data.server_requested_digest); + g_assert_cmpint (client_sends_basic, ==, test_data.client_sent_basic); + g_assert_cmpint (client_sends_digest, ==, test_data.client_sent_digest); + + g_assert_cmpint (success, ==, test_data.succeeded); } +#define TEST_USES_BASIC(t) (((t) & 1) == 1) +#define TEST_USES_DIGEST(t) (((t) & 2) == 2) +#define TEST_GOOD_USER(t) (((t) & 4) == 4) +#define TEST_GOOD_PASSWORD(t) (((t) & 8) == 8) + +#define TEST_GOOD_AUTH(t) (TEST_GOOD_USER (t) && TEST_GOOD_PASSWORD (t)) +#define TEST_PREEMPTIVE_BASIC(t) (TEST_USES_BASIC (t) && !TEST_USES_DIGEST (t)) + static void -do_auth_tests (SoupURI *base_uri) +do_server_auth_test (gconstpointer data) { - int i, n = 1; - gboolean use_basic, use_digest, good_user, good_password; - gboolean preemptive_basic, good_auth; - - for (i = 0; i < 16; i++) { - use_basic = (i & 1) == 1; - use_digest = (i & 2) == 2; - good_user = (i & 4) == 4; - good_password = (i & 8) == 8; - - good_auth = good_user && good_password; - - /* Curl will preemptively send Basic if it's told to - * use Basic but not Digest. - */ - preemptive_basic = use_basic && !use_digest; - - /* 1. No auth required. The server will ignore the - * Authorization headers completely, and the request - * will always succeed. - */ - do_test (n++, base_uri, "/foo", - good_user, good_password, - /* request */ - use_basic, use_digest, - /* expected from client */ - preemptive_basic, FALSE, - /* expected from server */ - FALSE, FALSE, - /* success? */ - TRUE); - - /* 2. Basic auth required. The server will send - * "WWW-Authenticate: Basic" if the client fails to - * send an Authorization: Basic on the first request, - * or if it sends a bad password. - */ - do_test (n++, base_uri, "/Basic/foo", - good_user, good_password, - /* request */ - use_basic, use_digest, - /* expected from client */ - use_basic, FALSE, - /* expected from server */ - !preemptive_basic || !good_auth, FALSE, - /* success? */ - use_basic && good_auth); - - /* 3. Digest auth required. Simpler than the basic - * case because the client can't send Digest auth - * premptively. - */ - do_test (n++, base_uri, "/Digest/foo", - good_user, good_password, - /* request */ - use_basic, use_digest, - /* expected from client */ - preemptive_basic, use_digest, - /* expected from server */ - FALSE, TRUE, - /* success? */ - use_digest && good_auth); - - /* 4. Any auth required. */ - do_test (n++, base_uri, "/Any/foo", - good_user, good_password, - /* request */ - use_basic, use_digest, - /* expected from client */ - preemptive_basic, use_digest, - /* expected from server */ - !preemptive_basic || !good_auth, !preemptive_basic || !good_auth, - /* success? */ - (use_basic || use_digest) && good_auth); - - /* 5. No auth required again. (Makes sure that - * SOUP_AUTH_DOMAIN_REMOVE_PATH works.) - */ - do_test (n++, base_uri, "/Any/Not/foo", - good_user, good_password, - /* request */ - use_basic, use_digest, - /* expected from client */ - preemptive_basic, FALSE, - /* expected from server */ - FALSE, FALSE, - /* success? */ - TRUE); - } + int i = GPOINTER_TO_INT (data); + +#ifndef HAVE_CURL + g_test_skip ("/usr/bin/curl is not available"); + return; +#endif + + /* 1. No auth required. The server will ignore the + * Authorization headers completely, and the request + * will always succeed. + */ + do_test (base_uri, "/foo", + TEST_GOOD_USER (i), TEST_GOOD_PASSWORD (i), + /* request */ + TEST_USES_BASIC (i), TEST_USES_DIGEST (i), + /* expected from client */ + TEST_PREEMPTIVE_BASIC (i), FALSE, + /* expected from server */ + FALSE, FALSE, + /* success? */ + TRUE); + + /* 2. Basic auth required. The server will send + * "WWW-Authenticate: Basic" if the client fails to + * send an Authorization: Basic on the first request, + * or if it sends a bad password. + */ + do_test (base_uri, "/Basic/foo", + TEST_GOOD_USER (i), TEST_GOOD_PASSWORD (i), + /* request */ + TEST_USES_BASIC (i), TEST_USES_DIGEST (i), + /* expected from client */ + TEST_USES_BASIC (i), FALSE, + /* expected from server */ + !TEST_PREEMPTIVE_BASIC (i) || !TEST_GOOD_AUTH (i), FALSE, + /* success? */ + TEST_USES_BASIC (i) && TEST_GOOD_AUTH (i)); + + /* 3. Digest auth required. Simpler than the basic + * case because the client can't send Digest auth + * premptively. + */ + do_test (base_uri, "/Digest/foo", + TEST_GOOD_USER (i), TEST_GOOD_PASSWORD (i), + /* request */ + TEST_USES_BASIC (i), TEST_USES_DIGEST (i), + /* expected from client */ + TEST_PREEMPTIVE_BASIC (i), TEST_USES_DIGEST (i), + /* expected from server */ + FALSE, TRUE, + /* success? */ + TEST_USES_DIGEST (i) && TEST_GOOD_AUTH (i)); + + /* 4. Any auth required. */ + do_test (base_uri, "/Any/foo", + TEST_GOOD_USER (i), TEST_GOOD_PASSWORD (i), + /* request */ + TEST_USES_BASIC (i), TEST_USES_DIGEST (i), + /* expected from client */ + TEST_PREEMPTIVE_BASIC (i), TEST_USES_DIGEST (i), + /* expected from server */ + !TEST_PREEMPTIVE_BASIC (i) || !TEST_GOOD_AUTH (i), !TEST_PREEMPTIVE_BASIC (i) || !TEST_GOOD_AUTH (i), + /* success? */ + (TEST_USES_BASIC (i) || TEST_USES_DIGEST (i)) && TEST_GOOD_AUTH (i)); + + /* 5. No auth required again. (Makes sure that + * SOUP_AUTH_DOMAIN_REMOVE_PATH works.) + */ + do_test (base_uri, "/Any/Not/foo", + TEST_GOOD_USER (i), TEST_GOOD_PASSWORD (i), + /* request */ + TEST_USES_BASIC (i), TEST_USES_DIGEST (i), + /* expected from client */ + TEST_PREEMPTIVE_BASIC (i), FALSE, + /* expected from server */ + FALSE, FALSE, + /* success? */ + TRUE); } static gboolean @@ -311,8 +277,8 @@ main (int argc, char **argv) { GMainLoop *loop; SoupServer *server; - SoupURI *uri; SoupAuthDomain *auth_domain; + int ret; test_init (argc, argv, no_test_entry); @@ -345,13 +311,45 @@ main (int argc, char **argv) loop = g_main_loop_new (NULL, TRUE); if (run_tests) { - uri = soup_uri_new ("http://127.0.0.1"); - soup_uri_set_port (uri, soup_server_get_port (server)); - do_auth_tests (uri); - soup_uri_free (uri); + int i; + + base_uri = soup_uri_new ("http://127.0.0.1"); + soup_uri_set_port (base_uri, soup_server_get_port (server)); + + for (i = 0; i < 16; i++) { + char *path; + const char *authtypes; + + if (!TEST_GOOD_USER (i) && !TEST_GOOD_PASSWORD (i)) + continue; + if (TEST_USES_BASIC (i)) { + if (TEST_USES_DIGEST (i)) + authtypes = "basic+digest"; + else + authtypes = "basic"; + } else { + if (TEST_USES_DIGEST (i)) + authtypes = "digest"; + else + authtypes = "none"; + } + + path = g_strdup_printf ("/server-auth/%s/%s-user%c%s-password", + authtypes, + TEST_GOOD_USER (i) ? "good" : "bad", + TEST_GOOD_USER (i) ? '/' : '\0', + TEST_GOOD_PASSWORD (i) ? "good" : "bad"); + g_test_add_data_func (path, GINT_TO_POINTER (i), do_server_auth_test); + g_free (path); + } + + ret = g_test_run (); + + soup_uri_free (base_uri); } else { g_print ("Listening on port %d\n", soup_server_get_port (server)); g_main_loop_run (loop); + ret = 0; } g_main_loop_unref (loop); @@ -359,5 +357,5 @@ main (int argc, char **argv) if (run_tests) test_cleanup (); - return errors != 0; + return ret; } diff --git a/tests/server-test.c b/tests/server-test.c new file mode 100644 index 00000000..0c980908 --- /dev/null +++ b/tests/server-test.c @@ -0,0 +1,329 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright 2007-2012 Red Hat, Inc. + */ + +#include "test-utils.h" + +SoupServer *server, *ssl_server; +SoupURI *base_uri, *ssl_base_uri; + +static void +server_callback (SoupServer *server, SoupMessage *msg, + const char *path, GHashTable *query, + SoupClientContext *context, gpointer data) +{ + soup_message_headers_append (msg->response_headers, + "X-Handled-By", "server_callback"); + + if (!strcmp (path, "*")) { + soup_test_assert (FALSE, "default server_callback got request for '*'"); + soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR); + return; + } + + if (msg->method != SOUP_METHOD_GET && msg->method != SOUP_METHOD_POST) { + soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED); + return; + } + + soup_message_set_status (msg, SOUP_STATUS_OK); + soup_message_set_response (msg, "text/plain", + SOUP_MEMORY_STATIC, "index", 5); +} + +static void +server_star_callback (SoupServer *server, SoupMessage *msg, + const char *path, GHashTable *query, + SoupClientContext *context, gpointer data) +{ + soup_message_headers_append (msg->response_headers, + "X-Handled-By", "star_callback"); + + if (strcmp (path, "*") != 0) { + soup_test_assert (FALSE, "server_star_callback got request for '%s'", path); + soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR); + return; + } + + if (msg->method != SOUP_METHOD_OPTIONS) { + soup_message_set_status (msg, SOUP_STATUS_METHOD_NOT_ALLOWED); + return; + } + + soup_message_set_status (msg, SOUP_STATUS_OK); +} + +/* Server handlers for "*" work but are separate from handlers for + * all other URIs. #590751 + */ +static void +do_star_test (void) +{ + SoupSession *session; + SoupMessage *msg; + SoupURI *star_uri; + const char *handled_by; + + g_test_bug ("590751"); + + session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL); + star_uri = soup_uri_copy (base_uri); + soup_uri_set_path (star_uri, "*"); + + debug_printf (1, " Testing with no handler\n"); + msg = soup_message_new_from_uri ("OPTIONS", star_uri); + soup_session_send_message (session, msg); + + soup_test_assert_message_status (msg, SOUP_STATUS_NOT_FOUND); + handled_by = soup_message_headers_get_one (msg->response_headers, + "X-Handled-By"); + g_assert_cmpstr (handled_by, ==, NULL); + g_object_unref (msg); + + soup_server_add_handler (server, "*", server_star_callback, NULL, NULL); + + debug_printf (1, " Testing with handler\n"); + msg = soup_message_new_from_uri ("OPTIONS", star_uri); + soup_session_send_message (session, msg); + + soup_test_assert_message_status (msg, SOUP_STATUS_OK); + handled_by = soup_message_headers_get_one (msg->response_headers, + "X-Handled-By"); + g_assert_cmpstr (handled_by, ==, "star_callback"); + g_object_unref (msg); + + soup_test_session_abort_unref (session); + soup_uri_free (star_uri); +} + +static void +do_one_server_aliases_test (SoupURI *uri, + const char *alias, + gboolean succeed) +{ + GSocketClient *client; + GSocketConnectable *addr; + GSocketConnection *conn; + GInputStream *in; + GOutputStream *out; + GError *error = NULL; + GString *req; + static char buf[1024]; + + debug_printf (1, " %s via %s\n", alias, uri->scheme); + + /* There's no way to make libsoup's client side send an absolute + * URI (to a non-proxy server), so we have to fake this. + */ + + client = g_socket_client_new (); + if (uri->scheme == SOUP_URI_SCHEME_HTTPS) { + g_socket_client_set_tls (client, TRUE); + g_socket_client_set_tls_validation_flags (client, 0); + } + addr = g_network_address_new (uri->host, uri->port); + + conn = g_socket_client_connect (client, addr, NULL, &error); + g_object_unref (addr); + g_object_unref (client); + if (!conn) { + g_assert_no_error (error); + g_error_free (error); + return; + } + + in = g_io_stream_get_input_stream (G_IO_STREAM (conn)); + out = g_io_stream_get_output_stream (G_IO_STREAM (conn)); + + req = g_string_new (NULL); + g_string_append_printf (req, "GET %s://%s:%d HTTP/1.1\r\n", + alias, uri->host, uri->port); + g_string_append_printf (req, "Host: %s:%d\r\n", + uri->host, uri->port); + g_string_append (req, "Connection: close\r\n\r\n"); + + if (!g_output_stream_write_all (out, req->str, req->len, NULL, NULL, &error)) { + g_assert_no_error (error); + g_error_free (error); + g_object_unref (conn); + g_string_free (req, TRUE); + return; + } + g_string_free (req, TRUE); + + if (!g_input_stream_read_all (in, buf, sizeof (buf), NULL, NULL, &error)) { + g_assert_no_error (error); + g_error_free (error); + g_object_unref (conn); + return; + } + + if (succeed) + g_assert_true (g_str_has_prefix (buf, "HTTP/1.1 200 ")); + else + g_assert_true (g_str_has_prefix (buf, "HTTP/1.1 400 ")); + + g_io_stream_close (G_IO_STREAM (conn), NULL, NULL); + g_object_unref (conn); +} + +static void +do_server_aliases_test (void) +{ + char *http_good[] = { "http", "dav", NULL }; + char *http_bad[] = { "https", "davs", "fred", NULL }; + char *https_good[] = { "https", "davs", NULL }; + char *https_bad[] = { "http", "dav", "fred", NULL }; + int i; + + g_test_bug ("703694"); + + for (i = 0; http_good[i]; i++) + do_one_server_aliases_test (base_uri, http_good[i], TRUE); + for (i = 0; http_bad[i]; i++) + do_one_server_aliases_test (base_uri, http_bad[i], FALSE); + + if (tls_available) { + for (i = 0; https_good[i]; i++) + do_one_server_aliases_test (ssl_base_uri, https_good[i], TRUE); + for (i = 0; https_bad[i]; i++) + do_one_server_aliases_test (ssl_base_uri, https_bad[i], FALSE); + } +} + +static void +do_dot_dot_test (void) +{ + SoupSession *session; + SoupMessage *msg; + SoupURI *uri; + + g_test_bug ("667635"); + + session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL); + + uri = soup_uri_new_with_base (base_uri, "/..%2ftest"); + msg = soup_message_new_from_uri ("GET", uri); + soup_uri_free (uri); + + soup_session_send_message (session, msg); + soup_test_assert_message_status (msg, SOUP_STATUS_BAD_REQUEST); + g_object_unref (msg); + + soup_test_session_abort_unref (session); +} + +static void +ipv6_server_callback (SoupServer *server, SoupMessage *msg, + const char *path, GHashTable *query, + SoupClientContext *context, gpointer data) +{ + const char *host; + char expected_host[128]; + + g_snprintf (expected_host, sizeof (expected_host), + "[::1]:%d", soup_server_get_port (server)); + + host = soup_message_headers_get_one (msg->request_headers, "Host"); + g_assert_cmpstr (host, ==, expected_host); + + if (g_test_failed ()) + soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST); + else + soup_message_set_status (msg, SOUP_STATUS_OK); +} + +static void +do_ipv6_test (void) +{ + SoupServer *ipv6_server; + SoupURI *ipv6_uri; + SoupAddress *ipv6_addr; + SoupSession *session; + SoupMessage *msg; + + g_test_bug ("666399"); + + ipv6_addr = soup_address_new ("::1", SOUP_ADDRESS_ANY_PORT); + soup_address_resolve_sync (ipv6_addr, NULL); + ipv6_server = soup_server_new (SOUP_SERVER_INTERFACE, ipv6_addr, + NULL); + g_object_unref (ipv6_addr); + if (!ipv6_server) { + debug_printf (1, " skipping due to lack of IPv6 support\n"); + return; + } + + soup_server_add_handler (ipv6_server, NULL, ipv6_server_callback, NULL, NULL); + soup_server_run_async (ipv6_server); + + ipv6_uri = soup_uri_new ("http://[::1]/"); + soup_uri_set_port (ipv6_uri, soup_server_get_port (ipv6_server)); + + session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); + + debug_printf (1, " HTTP/1.1\n"); + msg = soup_message_new_from_uri ("GET", ipv6_uri); + soup_session_send_message (session, msg); + soup_test_assert_message_status (msg, SOUP_STATUS_OK); + g_object_unref (msg); + + debug_printf (1, " HTTP/1.0\n"); + msg = soup_message_new_from_uri ("GET", ipv6_uri); + soup_message_set_http_version (msg, SOUP_HTTP_1_0); + soup_session_send_message (session, msg); + soup_test_assert_message_status (msg, SOUP_STATUS_OK); + g_object_unref (msg); + + soup_uri_free (ipv6_uri); + soup_test_session_abort_unref (session); + soup_test_server_quit_unref (ipv6_server); +} + +int +main (int argc, char **argv) +{ + char *http_aliases[] = { "dav", NULL }; + char *https_aliases[] = { "davs", NULL }; + int ret; + + 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)); + + g_object_set (G_OBJECT (server), + SOUP_SERVER_HTTP_ALIASES, http_aliases, + NULL); + + if (tls_available) { + ssl_server = soup_test_server_new_ssl (TRUE); + soup_server_add_handler (ssl_server, NULL, server_callback, NULL, NULL); + ssl_base_uri = soup_uri_new ("https://127.0.0.1/"); + soup_uri_set_port (ssl_base_uri, soup_server_get_port (ssl_server)); + g_object_set (G_OBJECT (ssl_server), + SOUP_SERVER_HTTPS_ALIASES, https_aliases, + NULL); + } + + g_test_add_func ("/server/OPTIONS *", do_star_test); + g_test_add_func ("/server/aliases", do_server_aliases_test); + g_test_add_func ("/server/..-in-path", do_dot_dot_test); + g_test_add_func ("/server/ipv6", do_ipv6_test); + + ret = g_test_run (); + + soup_uri_free (base_uri); + soup_test_server_quit_unref (server); + + if (tls_available) { + soup_uri_free (ssl_base_uri); + soup_test_server_quit_unref (ssl_server); + } + + test_cleanup (); + return ret; +} diff --git a/tests/session-test.c b/tests/session-test.c new file mode 100644 index 00000000..15072058 --- /dev/null +++ b/tests/session-test.c @@ -0,0 +1,396 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +#include "test-utils.h" + +static gboolean server_processed_message; +static gboolean timeout; +static GMainLoop *loop; +static SoupMessagePriority expected_priorities[3]; + +static gboolean +timeout_cb (gpointer user_data) +{ + gboolean *timeout = user_data; + + *timeout = TRUE; + return FALSE; +} + +static void +server_handler (SoupServer *server, + SoupMessage *msg, + const char *path, + GHashTable *query, + SoupClientContext *client, + gpointer user_data) +{ + if (!strcmp (path, "/request-timeout")) { + GMainContext *context = soup_server_get_async_context (server); + GSource *timer; + + timer = g_timeout_source_new (100); + g_source_set_callback (timer, timeout_cb, &timeout, NULL); + g_source_attach (timer, context); + g_source_unref (timer); + } else + server_processed_message = TRUE; + + soup_message_set_status (msg, SOUP_STATUS_OK); + soup_message_set_response (msg, "text/plain", + SOUP_MEMORY_STATIC, + "ok\r\n", 4); +} + +static void +finished_cb (SoupSession *session, SoupMessage *msg, gpointer user_data) +{ + gboolean *finished = user_data; + + *finished = TRUE; +} + +static void +cancel_message_cb (SoupMessage *msg, gpointer session) +{ + soup_session_cancel_message (session, msg, SOUP_STATUS_CANCELLED); + g_main_loop_quit (loop); +} + +static void +do_test_for_session (SoupSession *session, + const char *uri, + gboolean queue_is_async, + gboolean send_is_blocking, + gboolean cancel_is_immediate) +{ + SoupMessage *msg; + gboolean finished, local_timeout; + guint timeout_id; + char *timeout_uri; + + debug_printf (1, " queue_message\n"); + debug_printf (2, " requesting timeout\n"); + timeout_uri = g_strdup_printf ("%s/request-timeout", uri); + msg = soup_message_new ("GET", timeout_uri); + g_free (timeout_uri); + soup_session_send_message (session, msg); + g_object_unref (msg); + + msg = soup_message_new ("GET", uri); + server_processed_message = timeout = finished = FALSE; + soup_session_queue_message (session, msg, finished_cb, &finished); + while (!timeout) + g_usleep (100); + debug_printf (2, " got timeout\n"); + + if (queue_is_async) { + g_assert_false (server_processed_message); + debug_printf (2, " waiting for finished\n"); + while (!finished) + g_main_context_iteration (NULL, TRUE); + g_assert_true (server_processed_message); + } else { + g_assert_true (server_processed_message); + g_assert_false (finished); + debug_printf (2, " waiting for finished\n"); + while (!finished) + g_main_context_iteration (NULL, TRUE); + } + + debug_printf (1, " send_message\n"); + msg = soup_message_new ("GET", uri); + server_processed_message = local_timeout = FALSE; + timeout_id = g_idle_add_full (G_PRIORITY_HIGH, timeout_cb, &local_timeout, NULL); + soup_session_send_message (session, msg); + + g_assert_true (server_processed_message); + + if (send_is_blocking) { + soup_test_assert (!local_timeout, + "send_message ran main loop"); + } else { + soup_test_assert (local_timeout, + "send_message didn't run main loop"); + } + + if (!local_timeout) + g_source_remove (timeout_id); + + if (!queue_is_async) + return; + + debug_printf (1, " cancel_message\n"); + msg = soup_message_new ("GET", uri); + g_object_ref (msg); + finished = FALSE; + soup_session_queue_message (session, msg, finished_cb, &finished); + g_signal_connect (msg, "wrote-headers", + G_CALLBACK (cancel_message_cb), session); + + loop = g_main_loop_new (NULL, FALSE); + g_main_loop_run (loop); + + if (cancel_is_immediate) + g_assert_true (finished); + else + g_assert_false (finished); + + if (!finished) { + debug_printf (2, " waiting for finished\n"); + while (!finished) + g_main_context_iteration (NULL, TRUE); + } + + soup_test_assert_message_status (msg, SOUP_STATUS_CANCELLED); + g_object_unref (msg); +} + +static void +do_plain_tests (gconstpointer uri) +{ + SoupSession *session; + + session = soup_test_session_new (SOUP_TYPE_SESSION, NULL); + do_test_for_session (session, uri, TRUE, TRUE, FALSE); + soup_test_session_abort_unref (session); +} + +static void +do_async_tests (gconstpointer uri) +{ + SoupSession *session; + + session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); + do_test_for_session (session, uri, TRUE, FALSE, TRUE); + soup_test_session_abort_unref (session); +} + +static void +do_sync_tests (gconstpointer uri) +{ + SoupSession *session; + + session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL); + do_test_for_session (session, uri, FALSE, TRUE, FALSE); + soup_test_session_abort_unref (session); +} + +static void +priority_test_finished_cb (SoupSession *session, SoupMessage *msg, gpointer user_data) +{ + guint *finished_count = user_data; + SoupMessagePriority priority = soup_message_get_priority (msg); + + debug_printf (1, " received message %d with priority %d\n", + *finished_count, priority); + + soup_test_assert (priority == expected_priorities[*finished_count], + "message %d should have priority %d (%d found)", + *finished_count, expected_priorities[*finished_count], priority); + + (*finished_count)++; +} + +static void +do_priority_tests (gconstpointer data) +{ + const char *uri = data; + SoupSession *session; + int i, finished_count = 0; + SoupMessagePriority priorities[] = + { SOUP_MESSAGE_PRIORITY_LOW, + SOUP_MESSAGE_PRIORITY_HIGH, + SOUP_MESSAGE_PRIORITY_NORMAL }; + + g_test_bug ("696277"); + + session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); + g_object_set (session, "max-conns", 1, NULL); + + expected_priorities[0] = SOUP_MESSAGE_PRIORITY_HIGH; + expected_priorities[1] = SOUP_MESSAGE_PRIORITY_NORMAL; + expected_priorities[2] = SOUP_MESSAGE_PRIORITY_LOW; + + for (i = 0; i < 3; i++) { + char *msg_uri; + SoupMessage *msg; + + msg_uri = g_strdup_printf ("%s/%d", uri, i); + msg = soup_message_new ("GET", uri); + g_free (msg_uri); + + soup_message_set_priority (msg, priorities[i]); + soup_session_queue_message (session, msg, priority_test_finished_cb, &finished_count); + } + + debug_printf (2, " waiting for finished\n"); + while (finished_count != 3) + g_main_context_iteration (NULL, TRUE); + + soup_test_session_abort_unref (session); +} + +static void +test_session_properties (const char *name, + SoupSession *session, + GProxyResolver *expected_proxy_resolver, + GTlsDatabase *expected_tls_database) +{ + GProxyResolver *proxy_resolver = NULL; + GTlsDatabase *tlsdb = NULL; + + g_object_get (G_OBJECT (session), + SOUP_SESSION_PROXY_RESOLVER, &proxy_resolver, + SOUP_SESSION_TLS_DATABASE, &tlsdb, + NULL); + + soup_test_assert (proxy_resolver == expected_proxy_resolver, + "%s has %s proxy resolver", + name, proxy_resolver ? (expected_proxy_resolver ? "wrong" : "a") : "no"); + soup_test_assert (tlsdb == expected_tls_database, + "%s has %s TLS database", + name, tlsdb ? (expected_tls_database ? "wrong" : "a") : "no"); + + g_clear_object (&proxy_resolver); + g_clear_object (&tlsdb); +} + +static void +do_property_tests (void) +{ + SoupSession *session; + GProxyResolver *proxy_resolver, *default_proxy_resolver; + GTlsDatabase *tlsdb, *default_tlsdb; + SoupURI *uri; + + g_test_bug ("708696"); + + default_proxy_resolver = g_proxy_resolver_get_default (); + default_tlsdb = g_tls_backend_get_default_database (g_tls_backend_get_default ()); + + /* NOTE: We intentionally do not use soup_test_session_new() here */ + + session = g_object_new (SOUP_TYPE_SESSION, + NULL); + test_session_properties ("Base plain session", session, + default_proxy_resolver, default_tlsdb); + g_object_unref (session); + + session = g_object_new (SOUP_TYPE_SESSION, + SOUP_SESSION_PROXY_RESOLVER, NULL, + NULL); + test_session_properties ("Session with NULL :proxy-resolver", session, + NULL, default_tlsdb); + g_object_unref (session); + + proxy_resolver = g_simple_proxy_resolver_new (NULL, NULL); + session = g_object_new (SOUP_TYPE_SESSION, + SOUP_SESSION_PROXY_RESOLVER, proxy_resolver, + NULL); + test_session_properties ("Session with non-NULL :proxy-resolver", session, + proxy_resolver, default_tlsdb); + g_object_unref (proxy_resolver); + g_object_unref (session); + + session = g_object_new (SOUP_TYPE_SESSION, + SOUP_SESSION_PROXY_URI, NULL, + NULL); + test_session_properties ("Session with NULL :proxy-uri", session, + NULL, default_tlsdb); + g_object_unref (session); + + uri = soup_uri_new ("http://example.com/"); + session = g_object_new (SOUP_TYPE_SESSION, + SOUP_SESSION_PROXY_URI, uri, + NULL); + g_object_get (G_OBJECT (session), + SOUP_SESSION_PROXY_RESOLVER, &proxy_resolver, + NULL); + test_session_properties ("Session with non-NULL :proxy-uri", session, + proxy_resolver, default_tlsdb); + g_assert_cmpstr (G_OBJECT_TYPE_NAME (proxy_resolver), ==, "GSimpleProxyResolver"); + g_object_unref (proxy_resolver); + g_object_unref (session); + soup_uri_free (uri); + + G_GNUC_BEGIN_IGNORE_DEPRECATIONS; + session = g_object_new (SOUP_TYPE_SESSION, + SOUP_SESSION_REMOVE_FEATURE_BY_TYPE, SOUP_TYPE_PROXY_URI_RESOLVER, + NULL); + test_session_properties ("Session with removed proxy resolver feature", session, + NULL, default_tlsdb); + g_object_unref (session); + G_GNUC_END_IGNORE_DEPRECATIONS; + + session = g_object_new (SOUP_TYPE_SESSION, + SOUP_SESSION_TLS_DATABASE, NULL, + NULL); + test_session_properties ("Session with NULL :tls-database", session, + default_proxy_resolver, NULL); + g_object_unref (session); + + /* g_tls_file_database_new() will fail with the dummy backend, + * so we can only do this test if we have a real TLS backend. + */ + if (tls_available) { + GError *error = NULL; + + tlsdb = g_tls_file_database_new (g_test_get_filename (G_TEST_DIST, + "test-cert.pem", + NULL), &error); + g_assert_no_error (error); + + session = g_object_new (SOUP_TYPE_SESSION, + SOUP_SESSION_TLS_DATABASE, tlsdb, + NULL); + test_session_properties ("Session with non-NULL :tls-database", session, + default_proxy_resolver, tlsdb); + g_object_unref (tlsdb); + g_object_unref (session); + } + + session = g_object_new (SOUP_TYPE_SESSION, + SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE, FALSE, + NULL); + test_session_properties ("Session with :ssl-use-system-ca-file FALSE", session, + default_proxy_resolver, NULL); + g_object_unref (session); + + session = g_object_new (SOUP_TYPE_SESSION, + SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE, TRUE, + NULL); + test_session_properties ("Session with :ssl-use-system-ca-file TRUE", session, + default_proxy_resolver, default_tlsdb); + g_object_unref (session); +} + +int +main (int argc, char **argv) +{ + SoupServer *server; + char *uri, *timeout_uri; + int ret; + + test_init (argc, argv, NULL); + + server = soup_test_server_new (TRUE); + soup_server_add_handler (server, NULL, server_handler, NULL, NULL); + uri = g_strdup_printf ("http://127.0.0.1:%u", + soup_server_get_port (server)); + timeout_uri = g_strdup_printf ("%s/request-timeout", uri); + + g_test_add_data_func ("/session/SoupSession", uri, do_plain_tests); + g_test_add_data_func ("/session/SoupSessionAsync", uri, do_async_tests); + g_test_add_data_func ("/session/SoupSessionSync", uri, do_sync_tests); + g_test_add_data_func ("/session/priority", uri, do_priority_tests); + g_test_add_func ("/session/property", do_property_tests); + + ret = g_test_run (); + + g_free (uri); + g_free (timeout_uri); + soup_test_server_quit_unref (server); + + test_cleanup (); + return ret; +} diff --git a/tests/simple-httpd.c b/tests/simple-httpd.c deleted file mode 100644 index 46f0988d..00000000 --- a/tests/simple-httpd.c +++ /dev/null @@ -1,319 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Copyright (C) 2001-2003, Ximian, Inc. - */ - -#include "test-utils.h" - -#include <dirent.h> -#include <errno.h> -#include <fcntl.h> -#include <stdio.h> -#include <sys/stat.h> - -#ifdef HAVE_MMAP -#include <sys/mman.h> -#endif - -#ifdef HAVE_MMAP -struct mapping { - void *start; - size_t length; -}; - -static void -free_mapping (gpointer data) -{ - struct mapping *mapping = data; - munmap (mapping->start, mapping->length); - g_slice_free (struct mapping, mapping); -} -#endif - -static int -compare_strings (gconstpointer a, gconstpointer b) -{ - const char **sa = (const char **)a; - const char **sb = (const char **)b; - - return strcmp (*sa, *sb); -} - -static GString * -get_directory_listing (const char *path) -{ - GPtrArray *entries; - GString *listing; - char *escaped; - DIR *dir; - struct dirent *dent; - int i; - - entries = g_ptr_array_new (); - dir = opendir (path); - if (dir) { - while ((dent = readdir (dir))) { - if (!strcmp (dent->d_name, ".") || - (!strcmp (dent->d_name, "..") && - !strcmp (path, "./"))) - continue; - escaped = g_markup_escape_text (dent->d_name, -1); - g_ptr_array_add (entries, escaped); - } - closedir (dir); - } - - g_ptr_array_sort (entries, (GCompareFunc)compare_strings); - - listing = g_string_new ("<html>\r\n"); - escaped = g_markup_escape_text (strchr (path, '/'), -1); - g_string_append_printf (listing, "<head><title>Index of %s</title></head>\r\n", escaped); - g_string_append_printf (listing, "<body><h1>Index of %s</h1>\r\n<p>\r\n", escaped); - g_free (escaped); - for (i = 0; i < entries->len; i++) { - g_string_append_printf (listing, "<a href=\"%s\">%s</a><br>\r\n", - (char *)entries->pdata[i], - (char *)entries->pdata[i]); - g_free (entries->pdata[i]); - } - g_string_append (listing, "</body>\r\n</html>\r\n"); - - g_ptr_array_free (entries, TRUE); - return listing; -} - -static void -do_get (SoupServer *server, SoupMessage *msg, const char *path) -{ - char *slash; - struct stat st; - int fd; - - if (stat (path, &st) == -1) { - if (errno == EPERM) - soup_message_set_status (msg, SOUP_STATUS_FORBIDDEN); - else if (errno == ENOENT) - soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND); - else - soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR); - return; - } - - if (S_ISDIR (st.st_mode)) { - GString *listing; - char *index_path; - - slash = strrchr (path, '/'); - if (!slash || slash[1]) { - char *redir_uri; - - redir_uri = g_strdup_printf ("%s/", soup_message_get_uri (msg)->path); - soup_message_set_redirect (msg, SOUP_STATUS_MOVED_PERMANENTLY, - redir_uri); - g_free (redir_uri); - return; - } - - index_path = g_strdup_printf ("%s/index.html", path); - if (stat (index_path, &st) != -1) { - do_get (server, msg, index_path); - g_free (index_path); - return; - } - g_free (index_path); - - listing = get_directory_listing (path); - soup_message_set_response (msg, "text/html", - SOUP_MEMORY_TAKE, - listing->str, listing->len); - g_string_free (listing, FALSE); - return; - } - - fd = open (path, O_RDONLY); - if (fd == -1) { - soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR); - return; - } - - if (msg->method == SOUP_METHOD_GET) { -#ifdef HAVE_MMAP - struct mapping *mapping = g_slice_new (struct mapping); - SoupBuffer *buffer; - - mapping->start = mmap (NULL, st.st_size, PROT_READ, - MAP_PRIVATE, fd, 0); - mapping->length = st.st_size; - buffer = soup_buffer_new_with_owner (mapping->start, - mapping->length, - mapping, free_mapping); - soup_message_body_append_buffer (msg->response_body, buffer); - soup_buffer_free (buffer); -#else - char *buf; - - buf = g_malloc (st.st_size); - read (fd, buf, st.st_size); - close (fd); - soup_message_body_append (msg->response_body, SOUP_MEMORY_TAKE, - buf, st.st_size); -#endif - } else /* msg->method == SOUP_METHOD_HEAD */ { - char *length; - - /* We could just use the same code for both GET and - * HEAD. But we'll optimize and avoid the extra - * malloc. - */ - length = g_strdup_printf ("%lu", (gulong)st.st_size); - soup_message_headers_append (msg->response_headers, - "Content-Length", length); - g_free (length); - } - - soup_message_set_status (msg, SOUP_STATUS_OK); -} - -static void -do_put (SoupServer *server, SoupMessage *msg, const char *path) -{ - struct stat st; - FILE *f; - gboolean created = TRUE; - - if (stat (path, &st) != -1) { - const char *match = soup_message_headers_get_one (msg->request_headers, "If-None-Match"); - if (match && !strcmp (match, "*")) { - soup_message_set_status (msg, SOUP_STATUS_CONFLICT); - return; - } - - if (!S_ISREG (st.st_mode)) { - soup_message_set_status (msg, SOUP_STATUS_FORBIDDEN); - return; - } - - created = FALSE; - } - - f = fopen (path, "w"); - if (!f) { - soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR); - return; - } - - fwrite (msg->request_body->data, 1, msg->request_body->length, f); - fclose (f); - - soup_message_set_status (msg, created ? SOUP_STATUS_CREATED : SOUP_STATUS_OK); -} - -static void -server_callback (SoupServer *server, SoupMessage *msg, - const char *path, GHashTable *query, - SoupClientContext *context, gpointer data) -{ - char *file_path; - SoupMessageHeadersIter iter; - const char *name, *value; - - g_print ("%s %s HTTP/1.%d\n", msg->method, path, - soup_message_get_http_version (msg)); - soup_message_headers_iter_init (&iter, msg->request_headers); - while (soup_message_headers_iter_next (&iter, &name, &value)) - g_print ("%s: %s\n", name, value); - if (msg->request_body->length) - g_print ("%s\n", msg->request_body->data); - - file_path = g_strdup_printf (".%s", path); - - if (msg->method == SOUP_METHOD_GET || msg->method == SOUP_METHOD_HEAD) - do_get (server, msg, file_path); - else if (msg->method == SOUP_METHOD_PUT) - do_put (server, msg, file_path); - else - soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED); - - g_free (file_path); - g_print (" -> %d %s\n\n", msg->status_code, msg->reason_phrase); -} - -static void -quit (int sig) -{ - /* Exit cleanly on ^C in case we're valgrinding. */ - exit (0); -} - -int -main (int argc, char **argv) -{ - GMainLoop *loop; - SoupServer *server, *ssl_server; - int opt; - int port = SOUP_ADDRESS_ANY_PORT; - int ssl_port = SOUP_ADDRESS_ANY_PORT; - const char *ssl_cert_file = NULL, *ssl_key_file = NULL; - - g_type_init (); - signal (SIGINT, quit); - - while ((opt = getopt (argc, argv, "p:k:c:s:")) != -1) { - switch (opt) { - case 'p': - port = atoi (optarg); - break; - case 'k': - ssl_key_file = optarg; - break; - case 'c': - ssl_cert_file = optarg; - break; - case 's': - ssl_port = atoi (optarg); - break; - default: - g_printerr ("Usage: %s [-p port] [-c ssl-cert-file -k ssl-key-file [-s ssl-port]]\n", - argv[0]); - exit (1); - } - } - - server = soup_server_new (SOUP_SERVER_PORT, port, - SOUP_SERVER_SERVER_HEADER, "simple-httpd ", - NULL); - if (!server) { - g_printerr ("Unable to bind to server port %d\n", port); - exit (1); - } - soup_server_add_handler (server, NULL, - server_callback, NULL, NULL); - g_print ("\nStarting Server on port %d\n", - soup_server_get_port (server)); - soup_server_run_async (server); - - if (ssl_cert_file && ssl_key_file) { - ssl_server = soup_server_new ( - SOUP_SERVER_PORT, ssl_port, - SOUP_SERVER_SSL_CERT_FILE, ssl_cert_file, - SOUP_SERVER_SSL_KEY_FILE, ssl_key_file, - NULL); - - if (!ssl_server) { - g_printerr ("Unable to bind to SSL server port %d\n", ssl_port); - exit (1); - } - soup_server_add_handler (ssl_server, NULL, - server_callback, NULL, NULL); - g_print ("Starting SSL Server on port %d\n", - soup_server_get_port (ssl_server)); - soup_server_run_async (ssl_server); - } - - g_print ("\nWaiting for requests...\n"); - - loop = g_main_loop_new (NULL, TRUE); - g_main_loop_run (loop); - - return 0; -} diff --git a/tests/simple-proxy.c b/tests/simple-proxy.c deleted file mode 100644 index f25f9a04..00000000 --- a/tests/simple-proxy.c +++ /dev/null @@ -1,179 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Copyright (C) 2001-2003, Ximian, Inc. - */ - -#include "test-utils.h" - -/* WARNING: this is really really really not especially compliant with - * RFC 2616. But it does work for basic stuff. - */ - -static SoupSession *session; -static SoupServer *server; - -static void -copy_header (const char *name, const char *value, gpointer dest_headers) -{ - soup_message_headers_append (dest_headers, name, value); -} - -static void -send_headers (SoupMessage *from, SoupMessage *to) -{ - g_print ("[%p] HTTP/1.%d %d %s\n", to, - soup_message_get_http_version (from), - from->status_code, from->reason_phrase); - - soup_message_set_status_full (to, from->status_code, - from->reason_phrase); - soup_message_headers_foreach (from->response_headers, copy_header, - to->response_headers); - soup_message_headers_remove (to->response_headers, "Content-Length"); - soup_server_unpause_message (server, to); -} - -static void -send_chunk (SoupMessage *from, SoupBuffer *chunk, SoupMessage *to) -{ - g_print ("[%p] writing chunk of %lu bytes\n", to, - (unsigned long)chunk->length); - - soup_message_body_append_buffer (to->response_body, chunk); - soup_server_unpause_message (server, to); -} - -static void -client_msg_failed (SoupMessage *msg, gpointer msg2) -{ - soup_session_cancel_message (session, msg2, SOUP_STATUS_IO_ERROR); -} - -static void -finish_msg (SoupSession *session, SoupMessage *msg2, gpointer data) -{ - SoupMessage *msg = data; - - g_print ("[%p] done\n\n", msg); - g_signal_handlers_disconnect_by_func (msg, client_msg_failed, msg2); - - soup_message_body_complete (msg->response_body); - soup_server_unpause_message (server, msg); - g_object_unref (msg); -} - -static void -server_callback (SoupServer *server, SoupMessage *msg, - const char *path, GHashTable *query, - SoupClientContext *context, gpointer data) -{ - SoupMessage *msg2; - char *uristr; - - uristr = soup_uri_to_string (soup_message_get_uri (msg), FALSE); - g_print ("[%p] %s %s HTTP/1.%d\n", msg, msg->method, uristr, - soup_message_get_http_version (msg)); - - if (msg->method == SOUP_METHOD_CONNECT) { - soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED); - return; - } - - msg2 = soup_message_new (msg->method, uristr); - soup_message_headers_foreach (msg->request_headers, copy_header, - msg2->request_headers); - soup_message_headers_remove (msg2->request_headers, "Host"); - soup_message_headers_remove (msg2->request_headers, "Connection"); - - if (msg->request_body->length) { - SoupBuffer *request = soup_message_body_flatten (msg->request_body); - soup_message_body_append_buffer (msg2->request_body, request); - soup_buffer_free (request); - } - soup_message_headers_set_encoding (msg->response_headers, - SOUP_ENCODING_CHUNKED); - - g_signal_connect (msg2, "got_headers", - G_CALLBACK (send_headers), msg); - g_signal_connect (msg2, "got_chunk", - G_CALLBACK (send_chunk), msg); - - g_signal_connect (msg, "finished", G_CALLBACK (client_msg_failed), msg2); - - soup_session_queue_message (session, msg2, finish_msg, msg); - - g_object_ref (msg); - soup_server_pause_message (server, msg); -} - -static gboolean -auth_callback (SoupAuthDomain *auth_domain, SoupMessage *msg, - const char *username, const char *password, gpointer data) -{ - return !strcmp (username, "user") && !strcmp (password, "password"); -} - -static void -quit (int sig) -{ - /* Exit cleanly on ^C in case we're valgrinding. */ - exit (0); -} - -int -main (int argc, char **argv) -{ - GMainLoop *loop; - int opt; - int port = SOUP_ADDRESS_ANY_PORT; - SoupAuthDomain *auth_domain = NULL; - - g_type_init (); - signal (SIGINT, quit); - - while ((opt = getopt (argc, argv, "ap:")) != -1) { - switch (opt) { - case 'a': - auth_domain = soup_auth_domain_basic_new ( - SOUP_AUTH_DOMAIN_REALM, "simple-proxy", - SOUP_AUTH_DOMAIN_PROXY, TRUE, - SOUP_AUTH_DOMAIN_BASIC_AUTH_CALLBACK, auth_callback, - NULL); - break; - case 'p': - port = atoi (optarg); - break; - default: - g_printerr ("Usage: %s [-p port] [-n]\n", - argv[0]); - exit (1); - } - } - - server = soup_server_new (SOUP_SERVER_PORT, port, - NULL); - if (!server) { - g_printerr ("Unable to bind to server port %d\n", port); - exit (1); - } - soup_server_add_handler (server, NULL, - server_callback, NULL, NULL); - if (auth_domain) { - soup_server_add_auth_domain (server, auth_domain); - g_object_unref (auth_domain); - } - - g_print ("\nStarting proxy on port %d\n", - soup_server_get_port (server)); - soup_server_run_async (server); - - session = soup_session_async_new (); - - g_print ("\nWaiting for requests...\n"); - - loop = g_main_loop_new (NULL, TRUE); - g_main_loop_run (loop); - g_main_loop_unref (loop); - - return 0; -} diff --git a/tests/sniffing-test.c b/tests/sniffing-test.c index dd4eb4aa..32fad9fc 100644 --- a/tests/sniffing-test.c +++ b/tests/sniffing-test.c @@ -16,8 +16,8 @@ server_callback (SoupServer *server, SoupMessage *msg, { GError *error = NULL; char *query_key; - char *contents; - gsize length = 0, offset; + SoupBuffer *response = NULL; + gsize offset; gboolean empty_response = FALSE; if (msg->method != SOUP_METHOD_GET) { @@ -40,41 +40,35 @@ server_callback (SoupServer *server, SoupMessage *msg, } if (!strcmp (path, "/mbox")) { - if (empty_response) { - contents = g_strdup (""); - length = 0; - } else { - g_file_get_contents (SRCDIR "/resources/mbox", - &contents, &length, - &error); - } - - if (error) { - g_error ("%s", error->message); - g_error_free (error); - exit (1); + if (!empty_response) { + response = soup_test_load_resource ("mbox", &error); + g_assert_no_error (error); } soup_message_headers_append (msg->response_headers, "Content-Type", "text/plain"); } - if (g_str_has_prefix (path, "/text_or_binary/")) { + if (g_str_has_prefix (path, "/nosniff/")) { char *base_name = g_path_get_basename (path); - char *file_name = g_strdup_printf (SRCDIR "/resources/%s", base_name); - - g_file_get_contents (file_name, - &contents, &length, - &error); + response = soup_test_load_resource (base_name, &error); + g_assert_no_error (error); g_free (base_name); - g_free (file_name); - if (error) { - g_error ("%s", error->message); - g_error_free (error); - exit (1); - } + soup_message_headers_append (msg->response_headers, + "X-Content-Type-Options", "nosniff"); + + soup_message_headers_append (msg->response_headers, + "Content-Type", "no/sniffing-allowed"); + } + + if (g_str_has_prefix (path, "/text_or_binary/") || g_str_has_prefix (path, "/apache_bug/")) { + char *base_name = g_path_get_basename (path); + + response = soup_test_load_resource (base_name, &error); + g_assert_no_error (error); + g_free (base_name); soup_message_headers_append (msg->response_headers, "Content-Type", "text/plain"); @@ -82,20 +76,10 @@ server_callback (SoupServer *server, SoupMessage *msg, if (g_str_has_prefix (path, "/unknown/")) { char *base_name = g_path_get_basename (path); - char *file_name = g_strdup_printf (SRCDIR "/resources/%s", base_name); - - g_file_get_contents (file_name, - &contents, &length, - &error); + response = soup_test_load_resource (base_name, &error); + g_assert_no_error (error); g_free (base_name); - g_free (file_name); - - if (error) { - g_error ("%s", error->message); - g_error_free (error); - exit (1); - } soup_message_headers_append (msg->response_headers, "Content-Type", "UNKNOWN/unknown"); @@ -106,20 +90,10 @@ server_callback (SoupServer *server, SoupMessage *msg, char *ptr; char *base_name = g_path_get_basename (path); - char *file_name = g_strdup_printf (SRCDIR "/resources/%s", base_name); - - g_file_get_contents (file_name, - &contents, &length, - &error); + response = soup_test_load_resource (base_name, &error); + g_assert_no_error (error); g_free (base_name); - g_free (file_name); - - if (error) { - g_error ("%s", error->message); - g_error_free (error); - exit (1); - } /* Hack to allow passing type in the URI */ ptr = g_strrstr (components[2], "_"); @@ -132,20 +106,10 @@ server_callback (SoupServer *server, SoupMessage *msg, if (g_str_has_prefix (path, "/multiple_headers/")) { char *base_name = g_path_get_basename (path); - char *file_name = g_strdup_printf (SRCDIR "/resources/%s", base_name); - - g_file_get_contents (file_name, - &contents, &length, - &error); + response = soup_test_load_resource (base_name, &error); + g_assert_no_error (error); g_free (base_name); - g_free (file_name); - - if (error) { - g_error ("%s", error->message); - g_error_free (error); - exit (1); - } soup_message_headers_append (msg->response_headers, "Content-Type", "text/xml"); @@ -153,15 +117,18 @@ server_callback (SoupServer *server, SoupMessage *msg, "Content-Type", "text/plain"); } - for (offset = 0; offset < length; offset += 500) { - soup_message_body_append (msg->response_body, - SOUP_MEMORY_COPY, - contents + offset, - MIN(500, length - offset)); + if (response) { + for (offset = 0; offset < response->length; offset += 500) { + soup_message_body_append (msg->response_body, + SOUP_MEMORY_COPY, + response->data + offset, + MIN (500, response->length - offset)); + } + + soup_buffer_free (response); } - soup_message_body_complete (msg->response_body); - g_free (contents); + soup_message_body_complete (msg->response_body); } static gboolean @@ -181,10 +148,8 @@ content_sniffed (SoupMessage *msg, char *content_type, GHashTable *params, gpoin debug_printf (2, " content-sniffed -> %s\n", content_type); - if (g_object_get_data (G_OBJECT (msg), "got-chunk")) { - debug_printf (1, " got-chunk got emitted before content-sniffed\n"); - errors++; - } + soup_test_assert (g_object_get_data (G_OBJECT (msg), "got-chunk") == NULL, + "got-chunk got emitted before content-sniffed"); g_object_set_data (G_OBJECT (msg), "content-sniffed", GINT_TO_POINTER (TRUE)); @@ -202,10 +167,8 @@ got_headers (SoupMessage *msg, gpointer data) debug_printf (2, " got-headers\n"); - if (g_object_get_data (G_OBJECT (msg), "content-sniffed")) { - debug_printf (1, " content-sniffed got emitted before got-headers\n"); - errors++; - } + soup_test_assert (g_object_get_data (G_OBJECT (msg), "content-sniffed") == NULL, + "content-sniffed got emitted before got-headers"); g_object_set_data (G_OBJECT (msg), "got-headers", GINT_TO_POINTER (TRUE)); @@ -233,13 +196,6 @@ got_chunk (SoupMessage *msg, SoupBuffer *chunk, gpointer data) } static void -finished (SoupSession *session, SoupMessage *msg, gpointer data) -{ - GMainLoop *loop = (GMainLoop*)data; - g_main_loop_quit (loop); -} - -static void do_signals_test (gboolean should_content_sniff, gboolean should_pause, gboolean should_accumulate, @@ -248,9 +204,7 @@ do_signals_test (gboolean should_content_sniff, { SoupURI *uri = soup_uri_new_with_base (base_uri, "/mbox"); SoupMessage *msg = soup_message_new_from_uri ("GET", uri); - GMainLoop *loop = g_main_loop_new (NULL, TRUE); - char *contents; - gsize length; + SoupBuffer *expected; GError *error = NULL; SoupBuffer *body = NULL; @@ -283,34 +237,21 @@ do_signals_test (gboolean should_content_sniff, "signal::content_sniffed", content_sniffed, GINT_TO_POINTER (should_pause), NULL); - g_object_ref (msg); - soup_session_queue_message (session, msg, finished, loop); - - g_main_loop_run (loop); - - if (!should_content_sniff && - g_object_get_data (G_OBJECT (msg), "content-sniffed")) { - debug_printf (1, " content-sniffed got emitted without a sniffer\n"); - errors++; - } else if (should_content_sniff && - !g_object_get_data (G_OBJECT (msg), "content-sniffed")) { - debug_printf (1, " content-sniffed did not get emitted\n"); - errors++; - } + soup_session_send_message (session, msg); - if (empty_response) { - contents = g_strdup (""); - length = 0; + if (should_content_sniff) { + soup_test_assert (g_object_get_data (G_OBJECT (msg), "content-sniffed") != NULL, + "content-sniffed did not get emitted"); } else { - g_file_get_contents (SRCDIR "/resources/mbox", - &contents, &length, - &error); + soup_test_assert (g_object_get_data (G_OBJECT (msg), "content-sniffed") == NULL, + "content-sniffed got emitted without a sniffer"); } - if (error) { - g_error ("%s", error->message); - g_error_free (error); - exit (1); + if (empty_response) + expected = soup_buffer_new (SOUP_MEMORY_STATIC, "", 0); + else { + expected = soup_test_load_resource ("mbox", &error); + g_assert_no_error (error); } if (!should_accumulate && chunk_data) @@ -318,17 +259,12 @@ do_signals_test (gboolean should_content_sniff, else if (msg->response_body) body = soup_message_body_flatten (msg->response_body); - if (body && body->length != length) { - debug_printf (1, " lengths do not match\n"); - errors++; + if (body) { + soup_assert_cmpmem (body->data, body->length, + expected->data, expected->length); } - if (body && memcmp (body->data, contents, length)) { - debug_printf (1, " downloaded data does not match\n"); - errors++; - } - - g_free (contents); + soup_buffer_free (expected); if (body) soup_buffer_free (body); if (chunk_data) { @@ -338,7 +274,42 @@ do_signals_test (gboolean should_content_sniff, soup_uri_free (uri); g_object_unref (msg); - g_main_loop_unref (loop); +} + +static void +do_signals_tests (gconstpointer data) +{ + gboolean should_content_sniff = GPOINTER_TO_INT (data); + + if (!should_content_sniff) + soup_session_remove_feature_by_type (session, SOUP_TYPE_CONTENT_SNIFFER); + + do_signals_test (should_content_sniff, + FALSE, FALSE, FALSE, FALSE); + do_signals_test (should_content_sniff, + FALSE, FALSE, TRUE, FALSE); + do_signals_test (should_content_sniff, + FALSE, TRUE, FALSE, FALSE); + do_signals_test (should_content_sniff, + FALSE, TRUE, TRUE, FALSE); + + do_signals_test (should_content_sniff, + TRUE, TRUE, FALSE, FALSE); + do_signals_test (should_content_sniff, + TRUE, TRUE, TRUE, FALSE); + do_signals_test (should_content_sniff, + TRUE, FALSE, FALSE, FALSE); + do_signals_test (should_content_sniff, + TRUE, FALSE, TRUE, FALSE); + + /* FIXME g_test_bug ("587907") */ + do_signals_test (should_content_sniff, + TRUE, TRUE, FALSE, TRUE); + do_signals_test (should_content_sniff, + TRUE, TRUE, TRUE, TRUE); + + if (!should_content_sniff) + soup_session_add_feature_by_type (session, SOUP_TYPE_CONTENT_SNIFFER); } static void @@ -347,102 +318,125 @@ sniffing_content_sniffed (SoupMessage *msg, const char *content_type, { char **sniffed_type = (char **)data; GString *full_header; - GList *keys; - GList *iter; - - if (params == NULL) { - *sniffed_type = g_strdup (content_type); - return; - } + GHashTableIter iter; + gpointer key, value; full_header = g_string_new (content_type); - g_string_append (full_header, "; "); - - keys = g_hash_table_get_keys (params); - for (iter = keys; iter != NULL; iter = iter->next) { - const gchar *value = (const gchar*) g_hash_table_lookup (params, iter->data); + g_hash_table_iter_init (&iter, params); + while (g_hash_table_iter_next (&iter, &key, &value)) { + if (full_header->len) + g_string_append (full_header, "; "); soup_header_g_string_append_param (full_header, - (const gchar*) iter->data, - value); + (const char *) key, + (const char *) value); } - *sniffed_type = full_header->str; - - g_string_free (full_header, FALSE); - g_list_free (keys); + *sniffed_type = g_string_free (full_header, FALSE); } static void test_sniffing (const char *path, const char *expected_type) { - SoupURI *uri = soup_uri_new_with_base (base_uri, path); - SoupMessage *msg = soup_message_new_from_uri ("GET", uri); - GMainLoop *loop = g_main_loop_new (NULL, TRUE); + SoupURI *uri; + SoupMessage *msg; + SoupRequest *req; + GInputStream *stream; char *sniffed_type = NULL; + const char *req_sniffed_type; + GError *error = NULL; - debug_printf (1, "test_sniffing(\"%s\", \"%s\")\n", path, expected_type); + uri = soup_uri_new_with_base (base_uri, path); + msg = soup_message_new_from_uri ("GET", uri); g_signal_connect (msg, "content-sniffed", G_CALLBACK (sniffing_content_sniffed), &sniffed_type); - g_object_ref (msg); - - soup_session_queue_message (session, msg, finished, loop); - - g_main_loop_run (loop); + soup_session_send_message (session, msg); + g_assert_cmpstr (sniffed_type, ==, expected_type); + g_free (sniffed_type); + g_object_unref (msg); - if (!sniffed_type) { - debug_printf (1, " message was not sniffed!\n"); - errors++; - } else if (strcmp (sniffed_type, expected_type) != 0) { - debug_printf (1, " sniffing failed! expected %s, got %s\n", - expected_type, sniffed_type); - errors++; + req = soup_session_request_uri (session, uri, NULL); + stream = soup_test_request_send (req, NULL, 0, &error); + if (stream) { + soup_test_request_close_stream (req, stream, NULL, &error); + g_object_unref (stream); } - g_free (sniffed_type); + g_assert_no_error (error); + g_clear_error (&error); + + req_sniffed_type = soup_request_get_content_type (req); + g_assert_cmpstr (req_sniffed_type, ==, expected_type); + g_object_unref (req); soup_uri_free (uri); - g_object_unref (msg); - g_main_loop_unref (loop); } static void -test_disabled (const char *path) +do_sniffing_test (gconstpointer data) { - SoupURI *uri = soup_uri_new_with_base (base_uri, path); - SoupMessage *msg = soup_message_new_from_uri ("GET", uri); - GMainLoop *loop = g_main_loop_new (NULL, TRUE); + const char *path_and_result = data; + char **parts; + + parts = g_strsplit (path_and_result, " => ", -1); + g_assert (parts && parts[0] && parts[1] && !parts[2]); + + test_sniffing (parts[0], parts[1]); + g_strfreev (parts); +} + +static void +test_disabled (gconstpointer data) +{ + const char *path = data; + SoupURI *uri; + SoupMessage *msg; + SoupRequest *req; + GInputStream *stream; char *sniffed_type = NULL; + const char *sniffed_content_type; + GError *error = NULL; - soup_message_disable_feature (msg, SOUP_TYPE_CONTENT_SNIFFER); + g_test_bug ("574773"); - debug_printf (1, "test_disabled(\"%s\")\n", path); + uri = soup_uri_new_with_base (base_uri, path); + + msg = soup_message_new_from_uri ("GET", uri); + soup_message_disable_feature (msg, SOUP_TYPE_CONTENT_SNIFFER); g_signal_connect (msg, "content-sniffed", G_CALLBACK (sniffing_content_sniffed), &sniffed_type); - g_object_ref (msg); - - soup_session_queue_message (session, msg, finished, loop); + soup_session_send_message (session, msg); - g_main_loop_run (loop); + g_assert_null (sniffed_type); + g_object_unref (msg); - if (sniffed_type) { - debug_printf (1, " message was sniffed!\n"); - errors++; - g_free (sniffed_type); + req = soup_session_request_uri (session, uri, NULL); + msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (req)); + soup_message_disable_feature (msg, SOUP_TYPE_CONTENT_SNIFFER); + g_object_unref (msg); + stream = soup_test_request_send (req, NULL, 0, &error); + if (stream) { + soup_test_request_close_stream (req, stream, NULL, &error); + g_object_unref (stream); } + g_assert_no_error (error); + + sniffed_content_type = soup_request_get_content_type (req); + g_assert_cmpstr (sniffed_content_type, ==, NULL); + + g_object_unref (req); soup_uri_free (uri); - g_object_unref (msg); - g_main_loop_unref (loop); } int main (int argc, char **argv) { SoupServer *server; + int ret; test_init (argc, argv, NULL); @@ -451,112 +445,171 @@ main (int argc, char **argv) base_uri = soup_uri_new ("http://127.0.0.1/"); soup_uri_set_port (base_uri, soup_server_get_port (server)); - session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); - - /* No sniffer, no content_sniffed should be emitted */ - do_signals_test (FALSE, FALSE, FALSE, FALSE, FALSE); - do_signals_test (FALSE, FALSE, FALSE, TRUE, FALSE); - do_signals_test (FALSE, FALSE, TRUE, FALSE, FALSE); - do_signals_test (FALSE, FALSE, TRUE, TRUE, FALSE); - - do_signals_test (FALSE, TRUE, TRUE, FALSE, FALSE); - do_signals_test (FALSE, TRUE, TRUE, TRUE, FALSE); - do_signals_test (FALSE, TRUE, FALSE, FALSE, FALSE); - do_signals_test (FALSE, TRUE, FALSE, TRUE, FALSE); - - /* Tests that the signals are correctly emitted for empty - * responses; see - * http://bugzilla.gnome.org/show_bug.cgi?id=587907 */ - - do_signals_test (FALSE, TRUE, TRUE, FALSE, TRUE); - do_signals_test (FALSE, TRUE, TRUE, TRUE, TRUE); - + session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, + SOUP_SESSION_USE_THREAD_CONTEXT, TRUE, + NULL); soup_session_add_feature_by_type (session, SOUP_TYPE_CONTENT_SNIFFER); - /* Now, with a sniffer, content_sniffed must be emitted after - * got-headers, and before got-chunk. - */ - do_signals_test (TRUE, FALSE, FALSE, FALSE, FALSE); - do_signals_test (TRUE, FALSE, FALSE, TRUE, FALSE); - do_signals_test (TRUE, FALSE, TRUE, FALSE, FALSE); - do_signals_test (TRUE, FALSE, TRUE, TRUE, FALSE); - - do_signals_test (TRUE, TRUE, TRUE, FALSE, FALSE); - do_signals_test (TRUE, TRUE, TRUE, TRUE, FALSE); - do_signals_test (TRUE, TRUE, FALSE, FALSE, FALSE); - do_signals_test (TRUE, TRUE, FALSE, TRUE, FALSE); - - /* Empty response tests */ - do_signals_test (TRUE, TRUE, TRUE, FALSE, TRUE); - do_signals_test (TRUE, TRUE, TRUE, TRUE, TRUE); - - /* Test the text_or_binary sniffing path */ + g_test_add_data_func ("/sniffing/signals/no-sniffer", + GINT_TO_POINTER (FALSE), + do_signals_tests); + g_test_add_data_func ("/sniffing/signals/with-sniffer", + GINT_TO_POINTER (TRUE), + do_signals_tests); + + /* Test the apache bug sniffing path */ + g_test_add_data_func ("/sniffing/apache-bug/binary", + "/apache_bug/text_binary.txt => application/octet-stream", + do_sniffing_test); + g_test_add_data_func ("/sniffing/apache-bug/text", + "/apache_bug/text.txt => text/plain", + do_sniffing_test); + + /* X-Content-Type-Options: nosniff */ + g_test_add_data_func ("/sniffing/nosniff", + "nosniff/home.gif => no/sniffing-allowed", + do_sniffing_test); /* GIF is a 'safe' type */ - test_sniffing ("/text_or_binary/home.gif", "image/gif"); + g_test_add_data_func ("/sniffing/type/gif", + "text_or_binary/home.gif => image/gif", + do_sniffing_test); /* With our current code, no sniffing is done using GIO, so * the mbox will be identified as text/plain; should we change * this? */ - test_sniffing ("/text_or_binary/mbox", "text/plain"); + g_test_add_data_func ("/sniffing/type/mbox", + "text_or_binary/mbox => text/plain", + do_sniffing_test); /* HTML is considered unsafe for this algorithm, since it is * scriptable, so going from text/plain to text/html is * considered 'privilege escalation' */ - test_sniffing ("/text_or_binary/test.html", "text/plain"); + g_test_add_data_func ("/sniffing/type/html-in-text-context", + "text_or_binary/test.html => text/plain", + do_sniffing_test); /* text/plain with binary content and unknown pattern should be - * application/octet-stream */ - test_sniffing ("/text_or_binary/text_binary.txt", "application/octet-stream"); + * application/octet-stream + */ + g_test_add_data_func ("/sniffing/type/text-binary", + "text_or_binary/text_binary.txt => application/octet-stream", + do_sniffing_test); - /* text/plain with binary content and scriptable pattern should be - * application/octet-stream to avoid 'privilege escalation' */ - test_sniffing ("/text_or_binary/html_binary.html", "application/octet-stream"); + /* text/html with binary content and scriptable pattern should be + * application/octet-stream to avoid 'privilege escalation' + */ + g_test_add_data_func ("/sniffing/type/html-binary", + "text_or_binary/html_binary.html => application/octet-stream", + do_sniffing_test); /* text/plain with binary content and non scriptable known pattern should - * be the given type */ - test_sniffing ("/text_or_binary/ps_binary.ps", "application/postscript"); + * be the given type + */ + g_test_add_data_func ("/sniffing/type/ps", + "text_or_binary/ps_binary.ps => application/postscript", + do_sniffing_test); /* Test the unknown sniffing path */ - - test_sniffing ("/unknown/test.html", "text/html"); - test_sniffing ("/unknown/home.gif", "image/gif"); - test_sniffing ("/unknown/mbox", "text/plain"); - test_sniffing ("/unknown/text_binary.txt", "application/octet-stream"); + g_test_add_data_func ("/sniffing/type/unknown-html", + "unknown/test.html => text/html", + do_sniffing_test); + g_test_add_data_func ("/sniffing/type/unknown-gif", + "unknown/home.gif => image/gif", + do_sniffing_test); + g_test_add_data_func ("/sniffing/type/unknown-mbox", + "unknown/mbox => text/plain", + do_sniffing_test); + g_test_add_data_func ("/sniffing/type/unknown-binary", + "unknown/text_binary.txt => application/octet-stream", + do_sniffing_test); + /* FIXME g_test_bug ("715126") */ + g_test_add_data_func ("/sniffing/type/unknown-leading-space", + "unknown/leading_space.html => text/html", + do_sniffing_test); /* Test the XML sniffing path */ - - test_sniffing ("/type/text_xml/home.gif", "text/xml"); - test_sniffing ("/type/anice_type+xml/home.gif", "anice/type+xml"); - test_sniffing ("/type/application_xml/home.gif", "application/xml"); - - /* Test the image sniffing path */ - - test_sniffing ("/type/image_png/home.gif", "image/gif"); + g_test_add_data_func ("/sniffing/type/xml", + "type/text_xml/home.gif => text/xml", + do_sniffing_test); + g_test_add_data_func ("/sniffing/type/xml+xml", + "type/anice_type+xml/home.gif => anice/type+xml", + do_sniffing_test); + g_test_add_data_func ("/sniffing/type/application-xml", + "type/application_xml/home.gif => application/xml", + do_sniffing_test); /* Test the feed or html path */ + g_test_add_data_func ("/sniffing/type/html/html", + "type/text_html/test.html => text/html", + do_sniffing_test); + g_test_add_data_func ("/sniffing/type/html/rss", + "type/text_html/rss20.xml => application/rss+xml", + do_sniffing_test); + g_test_add_data_func ("/sniffing/type/html/atom", + "type/text_html/atom.xml => application/atom+xml", + do_sniffing_test); + g_test_add_data_func ("/sniffing/type/html/rdf", + "type/text_html/feed.rdf => application/rss+xml", + do_sniffing_test); - test_sniffing ("/type/text_html/test.html", "text/html"); - test_sniffing ("/type/text_html/rss20.xml", "application/rss+xml"); - test_sniffing ("/type/text_html/atom.xml", "application/atom+xml"); + /* Test the image sniffing path */ + g_test_add_data_func ("/sniffing/type/image/gif", + "type/image_png/home.gif => image/gif", + do_sniffing_test); + g_test_add_data_func ("/sniffing/type/image/png", + "type/image_gif/home.png => image/png", + do_sniffing_test); + g_test_add_data_func ("/sniffing/type/image/jpeg", + "type/image_png/home.jpg => image/jpeg", + do_sniffing_test); + g_test_add_data_func ("/sniffing/type/image/webp", + "type/image_png/tux.webp => image/webp", + do_sniffing_test); + + /* Test audio and video sniffing path */ + g_test_add_data_func ("/sniffing/type/audio/wav", + "type/audio_mpeg/test.wav => audio/wave", + do_sniffing_test); + g_test_add_data_func ("/sniffing/type/audio/aiff", + "type/audio_mpeg/test.aiff => audio/aiff", + do_sniffing_test); + g_test_add_data_func ("/sniffing/type/audio/ogg", + "type/audio_mpeg/test.ogg => application/ogg", + do_sniffing_test); + g_test_add_data_func ("/sniffing/type/video/webm", + "type/video_theora/test.webm => video/webm", + do_sniffing_test); + + /* Test the MP4 sniffing path */ + g_test_add_data_func ("/sniffing/type/video/mp4", + "unknown/test.mp4 => video/mp4", + do_sniffing_test); /* The spec tells us to only use the last Content-Type header */ - - test_sniffing ("/multiple_headers/home.gif", "image/gif"); + g_test_add_data_func ("/sniffing/multiple-headers", + "multiple_headers/home.gif => image/gif", + do_sniffing_test); /* Test that we keep the parameters when sniffing */ - test_sniffing ("/type/text_html; charset=UTF-8/test.html", "text/html; charset=UTF-8"); + g_test_add_data_func ("/sniffing/parameters", + "type/text_html; charset=UTF-8/test.html => text/html; charset=UTF-8", + do_sniffing_test); /* Test that disabling the sniffer works correctly */ + g_test_add_data_func ("/sniffing/disabled", + "/text_or_binary/home.gif", + test_disabled); - test_disabled ("/text_or_binary/home.gif"); + ret = g_test_run (); soup_uri_free (base_uri); soup_test_session_abort_unref (session); soup_test_server_quit_unref (server); + test_cleanup (); - return errors != 0; + return ret; } diff --git a/tests/socket-test.c b/tests/socket-test.c new file mode 100644 index 00000000..5bcc3b0c --- /dev/null +++ b/tests/socket-test.c @@ -0,0 +1,124 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright 2007-2012 Red Hat, Inc. + * Copyright 2012 Nokia Corporation + */ + +#include "test-utils.h" + +#include <gio/gnetworking.h> + +static void +do_unconnected_socket_test (void) +{ + SoupAddress *localhost; + SoupSocket *sock; + SoupSocket *client; + SoupAddress *addr; + guint res; + struct sockaddr_in in_localhost; + + g_test_bug ("673083"); + + in_localhost.sin_family = AF_INET; + in_localhost.sin_port = 0; + in_localhost.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + + localhost = soup_address_new_from_sockaddr ( + (struct sockaddr *) &in_localhost, sizeof (in_localhost)); + g_assert_true (localhost != NULL); + res = soup_address_resolve_sync (localhost, NULL); + g_assert_cmpuint (res, ==, SOUP_STATUS_OK); + + sock = soup_socket_new (SOUP_SOCKET_LOCAL_ADDRESS, localhost, + NULL); + g_assert_true (sock != NULL); + + addr = soup_socket_get_local_address (sock); + g_assert_true (addr != NULL); + g_assert_cmpstr (soup_address_get_physical (addr), ==, "127.0.0.1"); + g_assert_cmpuint (soup_address_get_port (addr), ==, 0); + + /* fails with ENOTCONN */ + g_test_expect_message ("libsoup", G_LOG_LEVEL_WARNING, + "*socket not connected*"); + addr = soup_socket_get_remote_address (sock); + g_test_assert_expected_messages (); + g_assert_null (addr); + + res = soup_socket_listen (sock); + g_assert_true (res); + + addr = soup_socket_get_local_address (sock); + g_assert_true (addr != NULL); + g_assert_cmpstr (soup_address_get_physical (addr), ==, "127.0.0.1"); + g_assert_cmpuint (soup_address_get_port (addr), >, 0); + + client = soup_socket_new (SOUP_SOCKET_REMOTE_ADDRESS, + soup_socket_get_local_address (sock), + NULL); + res = soup_socket_connect_sync (client, NULL); + g_assert_cmpuint (res, ==, SOUP_STATUS_OK); + addr = soup_socket_get_local_address (client); + g_assert_true (addr != NULL); + addr = soup_socket_get_remote_address (client); + g_assert_true (addr != NULL); + g_assert_cmpstr (soup_address_get_physical (addr), ==, "127.0.0.1"); + g_assert_cmpuint (soup_address_get_port (addr), >, 0); + g_object_unref (client); + + client = soup_socket_new (SOUP_SOCKET_REMOTE_ADDRESS, + soup_socket_get_local_address (sock), + NULL); + /* save it for later */ + + /* listening socket fails with ENOTCONN */ + g_test_expect_message ("libsoup", G_LOG_LEVEL_WARNING, + "*endpoint is not connected*"); + addr = soup_socket_get_remote_address (sock); + g_test_assert_expected_messages (); + g_assert_null (addr); + + soup_socket_disconnect (sock); + + g_test_expect_message ("libsoup", G_LOG_LEVEL_WARNING, + "*socket not connected*"); + addr = soup_socket_get_remote_address (sock); + g_test_assert_expected_messages (); + g_assert_null (addr); + + /* has never been connected */ + g_test_expect_message ("libsoup", G_LOG_LEVEL_WARNING, + "*socket not connected*"); + addr = soup_socket_get_local_address (client); + g_test_assert_expected_messages (); + g_assert_null (addr); + + res = soup_socket_connect_sync (client, NULL); + g_assert_cmpuint (res, ==, SOUP_STATUS_CANT_CONNECT); + + g_test_expect_message ("libsoup", G_LOG_LEVEL_WARNING, + "*socket not connected*"); + addr = soup_socket_get_local_address (client); + g_test_assert_expected_messages (); + g_assert_null (addr); + + g_object_unref (localhost); + g_object_unref (client); + g_object_unref (sock); +} + +int +main (int argc, char **argv) +{ + int ret; + + test_init (argc, argv, NULL); + + g_test_add_func ("/sockets/unconnected", do_unconnected_socket_test); + + ret = g_test_run (); + + test_cleanup (); + return ret; +} diff --git a/tests/soup-tests.gresource.xml b/tests/soup-tests.gresource.xml new file mode 100644 index 00000000..b24a7297 --- /dev/null +++ b/tests/soup-tests.gresource.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<gresources> + <gresource prefix="/org/gnome/libsoup/tests"> + <file>index.txt</file> + <file>resources/atom.xml</file> + <file>resources/feed.rdf</file> + <file>resources/home.gif</file> + <file>resources/home.jpg</file> + <file>resources/home.png</file> + <file>resources/html_binary.html</file> + <file>resources/leading_space.html</file> + <file>resources/mbox</file> + <file>resources/mbox.gz</file> + <file>resources/mbox.raw</file> + <file>resources/mbox.zlib</file> + <file>resources/ps_binary.ps</file> + <file>resources/rss20.xml</file> + <file>resources/test.aiff</file> + <file>resources/test.html</file> + <file>resources/test.mp4</file> + <file>resources/test.ogg</file> + <file>resources/test.wav</file> + <file>resources/test.webm</file> + <file>resources/text.txt</file> + <file>resources/text_binary.txt</file> + <file>resources/tux.webp</file> + </gresource> +</gresources> diff --git a/tests/ssl-test.c b/tests/ssl-test.c index 6d96bc79..e6bbb615 100644 --- a/tests/ssl-test.c +++ b/tests/ssl-test.c @@ -2,8 +2,10 @@ #include "test-utils.h" +static char *uri; + static void -do_properties_test_for_session (SoupSession *session, char *uri) +do_properties_test_for_session (SoupSession *session, const char *uri) { SoupMessage *msg; GTlsCertificate *cert; @@ -11,42 +13,27 @@ do_properties_test_for_session (SoupSession *session, char *uri) msg = soup_message_new ("GET", uri); soup_session_send_message (session, msg); - if (msg->status_code != SOUP_STATUS_OK) { - debug_printf (1, " FAILED: %d %s\n", - msg->status_code, msg->reason_phrase); - errors++; - } + soup_test_assert_message_status (msg, SOUP_STATUS_OK); if (soup_message_get_https_status (msg, &cert, &flags)) { - if (!G_IS_TLS_CERTIFICATE (cert)) { - debug_printf (1, " No certificate?\n"); - errors++; - } - if (flags != G_TLS_CERTIFICATE_UNKNOWN_CA) { - debug_printf (1, " Wrong cert flags (got %x, wanted %x)\n", - flags, G_TLS_CERTIFICATE_UNKNOWN_CA); - errors++; - } - } else { - debug_printf (1, " Response not https\n"); - errors++; - } - if (soup_message_get_flags (msg) & SOUP_MESSAGE_CERTIFICATE_TRUSTED) { - debug_printf (1, " CERTIFICATE_TRUSTED set?\n"); - errors++; - } + g_assert_true (G_IS_TLS_CERTIFICATE (cert)); + g_assert_cmpuint (flags, ==, G_TLS_CERTIFICATE_UNKNOWN_CA); + } else + soup_test_assert (FALSE, "Response not https"); + + g_test_bug ("665182"); + g_assert_false (soup_message_get_flags (msg) & SOUP_MESSAGE_CERTIFICATE_TRUSTED); g_object_unref (msg); } static void -do_properties_tests (char *uri) +do_async_properties_tests (void) { SoupSession *session; - debug_printf (1, "\nSoupMessage properties\n"); + SOUP_TEST_SKIP_IF_NO_TLS; - debug_printf (1, " async\n"); session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); g_object_set (G_OBJECT (session), SOUP_SESSION_SSL_CA_FILE, "/dev/null", @@ -54,8 +41,15 @@ do_properties_tests (char *uri) NULL); do_properties_test_for_session (session, uri); soup_test_session_abort_unref (session); +} + +static void +do_sync_properties_tests (void) +{ + SoupSession *session; + + SOUP_TEST_SKIP_IF_NO_TLS; - debug_printf (1, " sync\n"); session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL); g_object_set (G_OBJECT (session), SOUP_SESSION_SSL_CA_FILE, "/dev/null", @@ -65,81 +59,74 @@ do_properties_tests (char *uri) soup_test_session_abort_unref (session); } +typedef struct { + const char *name; + gboolean sync; + gboolean strict; + gboolean with_ca_list; + guint expected_status; +} StrictnessTest; + +static const StrictnessTest strictness_tests[] = { + { "/ssl/strictness/async/strict/with-ca", + FALSE, TRUE, TRUE, SOUP_STATUS_OK }, + { "/ssl/strictness/async/strict/without-ca", + FALSE, TRUE, FALSE, SOUP_STATUS_SSL_FAILED }, + { "/ssl/strictness/async/non-strict/with-ca", + FALSE, FALSE, TRUE, SOUP_STATUS_OK }, + { "/ssl/strictness/async/non-strict/without-ca", + FALSE, FALSE, FALSE, SOUP_STATUS_OK }, + { "/ssl/strictness/sync/strict/with-ca", + TRUE, TRUE, TRUE, SOUP_STATUS_OK }, + { "/ssl/strictness/sync/strict/without-ca", + TRUE, TRUE, FALSE, SOUP_STATUS_SSL_FAILED }, + { "/ssl/strictness/sync/non-strict/with-ca", + TRUE, FALSE, TRUE, SOUP_STATUS_OK }, + { "/ssl/strictness/sync/non-strict/without-ca", + TRUE, FALSE, FALSE, SOUP_STATUS_OK }, +}; + static void -do_one_strict_test (SoupSession *session, char *uri, - gboolean strict, gboolean with_ca_list, - guint expected_status) +do_strictness_test (gconstpointer data) { + const StrictnessTest *test = data; + SoupSession *session; SoupMessage *msg; + GTlsCertificateFlags flags = 0; - /* Note that soup_test_session_new() sets - * SOUP_SESSION_SSL_CA_FILE by default, and turns off - * SOUP_SESSION_SSL_STRICT. - */ + SOUP_TEST_SKIP_IF_NO_TLS; - g_object_set (G_OBJECT (session), - SOUP_SESSION_SSL_STRICT, strict, - SOUP_SESSION_SSL_CA_FILE, with_ca_list ? SRCDIR "/test-cert.pem" : "/dev/null", - NULL); - /* Close existing connections with old params */ - soup_session_abort (session); + session = soup_test_session_new (test->sync ? SOUP_TYPE_SESSION_SYNC : SOUP_TYPE_SESSION_ASYNC, + NULL); + if (!test->strict) { + g_object_set (G_OBJECT (session), + SOUP_SESSION_SSL_STRICT, FALSE, + NULL); + } + if (!test->with_ca_list) { + g_object_set (G_OBJECT (session), + SOUP_SESSION_SSL_CA_FILE, "/dev/null", + NULL); + } msg = soup_message_new ("GET", uri); soup_session_send_message (session, msg); - if (msg->status_code != expected_status) { - debug_printf (1, " FAILED: %d %s (expected %d %s)\n", - msg->status_code, msg->reason_phrase, - expected_status, - soup_status_get_phrase (expected_status)); - if (msg->status_code == SOUP_STATUS_SSL_FAILED) { - GTlsCertificateFlags flags = 0; - - soup_message_get_https_status (msg, NULL, &flags); - debug_printf (1, " tls error flags: 0x%x\n", flags); - } - errors++; - } else if (with_ca_list && SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) { - if (!(soup_message_get_flags (msg) & SOUP_MESSAGE_CERTIFICATE_TRUSTED)) { - debug_printf (1, " CERTIFICATE_TRUSTED not set?\n"); - errors++; - } - } else { - if (with_ca_list && soup_message_get_flags (msg) & SOUP_MESSAGE_CERTIFICATE_TRUSTED) { - debug_printf (1, " CERTIFICATE_TRUSTED set?\n"); - errors++; - } - } - - g_object_unref (msg); -} + soup_test_assert_message_status (msg, test->expected_status); -static void -do_strict_tests (char *uri) -{ - SoupSession *session; + g_test_bug ("690176"); + g_assert_true (soup_message_get_https_status (msg, NULL, &flags)); - debug_printf (1, "\nstrict/nonstrict\n"); + g_test_bug ("665182"); + if (test->with_ca_list && SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) + g_assert_true (soup_message_get_flags (msg) & SOUP_MESSAGE_CERTIFICATE_TRUSTED); + else + g_assert_false (soup_message_get_flags (msg) & SOUP_MESSAGE_CERTIFICATE_TRUSTED); - session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); - debug_printf (1, " async with CA list\n"); - do_one_strict_test (session, uri, TRUE, TRUE, SOUP_STATUS_OK); - debug_printf (1, " async without CA list\n"); - do_one_strict_test (session, uri, TRUE, FALSE, SOUP_STATUS_SSL_FAILED); - debug_printf (1, " async non-strict with CA list\n"); - do_one_strict_test (session, uri, FALSE, TRUE, SOUP_STATUS_OK); - debug_printf (1, " async non-strict without CA list\n"); - do_one_strict_test (session, uri, FALSE, FALSE, SOUP_STATUS_OK); - soup_test_session_abort_unref (session); + if (msg->status_code == SOUP_STATUS_SSL_FAILED && + test->expected_status != SOUP_STATUS_SSL_FAILED) + debug_printf (1, " tls error flags: 0x%x\n", flags); - session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL); - debug_printf (1, " sync with CA list\n"); - do_one_strict_test (session, uri, TRUE, TRUE, SOUP_STATUS_OK); - debug_printf (1, " sync without CA list\n"); - do_one_strict_test (session, uri, TRUE, FALSE, SOUP_STATUS_SSL_FAILED); - debug_printf (1, " sync non-strict with CA list\n"); - do_one_strict_test (session, uri, FALSE, TRUE, SOUP_STATUS_OK); - debug_printf (1, " sync non-strict without CA list\n"); - do_one_strict_test (session, uri, FALSE, FALSE, SOUP_STATUS_OK); + g_object_unref (msg); soup_test_session_abort_unref (session); } @@ -160,7 +147,9 @@ do_session_property_tests (void) char *ca_file; SoupSession *session; - debug_printf (1, "session properties\n"); + g_test_bug ("673678"); + + SOUP_TEST_SKIP_IF_NO_TLS; session = soup_session_async_new (); g_signal_connect (session, "notify::ssl-use-system-ca-file", @@ -175,20 +164,12 @@ do_session_property_tests (void) "tls-database", &tlsdb, "ssl-ca-file", &ca_file, NULL); - if (use_system) { - debug_printf (1, " ssl-use-system-ca-file defaults to TRUE?\n"); - errors++; - } - if (tlsdb) { - debug_printf (1, " tls-database set by default?\n"); - errors++; - g_object_unref (tlsdb); - } - if (ca_file) { - debug_printf (1, " ca-file set by default?\n"); - errors++; - g_free (ca_file); - } + soup_test_assert (!use_system, + "ssl-use-system-ca-file defaults to TRUE"); + soup_test_assert (tlsdb == NULL, + "tls-database set by default"); + soup_test_assert (ca_file == NULL, + "ca-file set by default"); use_system_changed = tlsdb_changed = ca_file_changed = FALSE; g_object_set (G_OBJECT (session), @@ -199,68 +180,37 @@ do_session_property_tests (void) "tls-database", &tlsdb, "ssl-ca-file", &ca_file, NULL); - if (!use_system) { - debug_printf (1, " setting ssl-use-system-ca-file failed\n"); - errors++; - } - if (!tlsdb) { - debug_printf (1, " setting ssl-use-system-ca-file didn't set tls-database\n"); - errors++; - } else - g_object_unref (tlsdb); - if (ca_file) { - debug_printf (1, " setting ssl-use-system-ca-file set ssl-ca-file\n"); - errors++; - g_free (ca_file); - } - if (!use_system_changed) { - debug_printf (1, " setting ssl-use-system-ca-file didn't emit notify::ssl-use-system-ca-file\n"); - errors++; - } - if (!tlsdb_changed) { - debug_printf (1, " setting ssl-use-system-ca-file didn't emit notify::tls-database\n"); - errors++; - } - if (ca_file_changed) { - debug_printf (1, " setting ssl-use-system-ca-file emitted notify::ssl-ca-file\n"); - errors++; - } + soup_test_assert (use_system, + "setting ssl-use-system-ca-file failed"); + g_assert_true (use_system_changed); + soup_test_assert (tlsdb != NULL, + "setting ssl-use-system-ca-file didn't set tls-database"); + g_assert_true (tlsdb_changed); + g_clear_object (&tlsdb); + soup_test_assert (ca_file == NULL, + "setting ssl-use-system-ca-file set ssl-ca-file"); + g_assert_false (ca_file_changed); use_system_changed = tlsdb_changed = ca_file_changed = FALSE; g_object_set (G_OBJECT (session), - "ssl-ca-file", SRCDIR "/test-cert.pem", + "ssl-ca-file", g_test_get_filename (G_TEST_DIST, "/test-cert.pem", NULL), NULL); g_object_get (G_OBJECT (session), "ssl-use-system-ca-file", &use_system, "tls-database", &tlsdb, "ssl-ca-file", &ca_file, NULL); - if (use_system) { - debug_printf (1, " setting ssl-ca-file left ssl-use-system-ca-file set\n"); - errors++; - } - if (!tlsdb) { - debug_printf (1, " setting ssl-ca-file didn't set tls-database\n"); - errors++; - } else - g_object_unref (tlsdb); - if (!ca_file) { - debug_printf (1, " setting ssl-ca-file failed\n"); - errors++; - } else - g_free (ca_file); - if (!use_system_changed) { - debug_printf (1, " setting ssl-ca-file didn't emit notify::ssl-use-system-ca-file\n"); - errors++; - } - if (!tlsdb_changed) { - debug_printf (1, " setting ssl-ca-file didn't emit notify::tls-database\n"); - errors++; - } - if (!ca_file_changed) { - debug_printf (1, " setting ssl-ca-file didn't emit notify::ssl-ca-file\n"); - errors++; - } + soup_test_assert (!use_system, + "setting ssl-ca-file left ssl-use-system-ca-file set"); + g_assert_true (use_system_changed); + soup_test_assert (tlsdb != NULL, + "setting ssl-ca-file didn't set tls-database"); + g_assert_true (tlsdb_changed); + g_clear_object (&tlsdb); + soup_test_assert (ca_file != NULL, + "setting ssl-ca-file failed"); + g_assert_true (ca_file_changed); + g_free (ca_file); use_system_changed = tlsdb_changed = ca_file_changed = FALSE; g_object_set (G_OBJECT (session), @@ -271,32 +221,15 @@ do_session_property_tests (void) "tls-database", &tlsdb, "ssl-ca-file", &ca_file, NULL); - if (use_system) { - debug_printf (1, " setting tls-database NULL left ssl-use-system-ca-file set\n"); - errors++; - } - if (tlsdb) { - debug_printf (1, " setting tls-database NULL failed\n"); - errors++; - g_object_unref (tlsdb); - } - if (ca_file) { - debug_printf (1, " setting tls-database didn't clear ssl-ca-file\n"); - errors++; - g_free (ca_file); - } - if (use_system_changed) { - debug_printf (1, " setting tls-database emitted notify::ssl-use-system-ca-file\n"); - errors++; - } - if (!tlsdb_changed) { - debug_printf (1, " setting tls-database didn't emit notify::tls-database\n"); - errors++; - } - if (!ca_file_changed) { - debug_printf (1, " setting tls-database didn't emit notify::ssl-ca-file\n"); - errors++; - } + soup_test_assert (!use_system, + "setting tls-database NULL left ssl-use-system-ca-file set"); + g_assert_false (use_system_changed); + soup_test_assert (tlsdb == NULL, + "setting tls-database NULL failed"); + g_assert_true (tlsdb_changed); + soup_test_assert (ca_file == NULL, + "setting tls-database didn't clear ssl-ca-file"); + g_assert_true (ca_file_changed); soup_test_session_abort_unref (session); } @@ -319,7 +252,7 @@ int main (int argc, char **argv) { SoupServer *server; - char *uri; + int i, ret; test_init (argc, argv, NULL); @@ -328,15 +261,25 @@ main (int argc, char **argv) soup_server_add_handler (server, NULL, server_handler, NULL, NULL); uri = g_strdup_printf ("https://127.0.0.1:%u/", soup_server_get_port (server)); + } - do_session_property_tests (); - do_strict_tests (uri); - do_properties_tests (uri); + g_test_add_func ("/ssl/session-properties", do_session_property_tests); + g_test_add_func ("/ssl/message-properties/async", do_async_properties_tests); + g_test_add_func ("/ssl/message-properties/sync", do_sync_properties_tests); + for (i = 0; i < G_N_ELEMENTS (strictness_tests); i++) { + g_test_add_data_func (strictness_tests[i].name, + &strictness_tests[i], + do_strictness_test); + } + + ret = g_test_run (); + + if (tls_available) { g_free (uri); soup_test_server_quit_unref (server); } test_cleanup (); - return errors != 0; + return ret; } diff --git a/tests/streaming-test.c b/tests/streaming-test.c index 239e0ce8..8d8c02ef 100644 --- a/tests/streaming-test.c +++ b/tests/streaming-test.c @@ -7,28 +7,8 @@ #define RESPONSE_CHUNK_SIZE 1024 -char *full_response, *full_response_md5; -gsize full_response_length; - -static void -get_full_response (void) -{ - GError *error = NULL; - - if (!g_file_get_contents (SRCDIR "/index.txt", - &full_response, - &full_response_length, - &error)) { - g_printerr ("Could not read index file %s: %s\n", - SRCDIR "/index.txt", error->message); - g_error_free (error); - exit (1); - } - - full_response_md5 = g_compute_checksum_for_data (G_CHECKSUM_MD5, - (guchar *)full_response, - full_response_length); -} +SoupBuffer *full_response; +char *full_response_md5; static void write_next_chunk (SoupMessage *msg, gpointer user_data) @@ -36,12 +16,12 @@ write_next_chunk (SoupMessage *msg, gpointer user_data) gsize *offset = user_data; gsize chunk_length; - chunk_length = MIN (RESPONSE_CHUNK_SIZE, full_response_length - *offset); + chunk_length = MIN (RESPONSE_CHUNK_SIZE, full_response->length - *offset); if (chunk_length > 0) { debug_printf (2, " writing chunk\n"); soup_message_body_append (msg->response_body, SOUP_MEMORY_STATIC, - full_response + *offset, + full_response->data + *offset, chunk_length); *offset += chunk_length; } else { @@ -74,7 +54,7 @@ server_callback (SoupServer *server, SoupMessage *msg, soup_message_headers_set_encoding (msg->response_headers, SOUP_ENCODING_CONTENT_LENGTH); soup_message_headers_set_content_length (msg->response_headers, - full_response_length); + full_response->length); } else if (!strcmp (path, "/eof")) { soup_message_headers_set_encoding (msg->response_headers, SOUP_ENCODING_EOF); @@ -106,44 +86,49 @@ do_request (SoupSession *session, SoupURI *base_uri, char *path) soup_session_send_message (session, msg); - if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) { - debug_printf (1, " message failed: %d %s\n", - msg->status_code, msg->reason_phrase); - errors++; - } - - if (msg->response_body->length != full_response_length) { - debug_printf (1, " received length mismatch: expected %d, got %d\n", - (int)full_response_length, (int)msg->request_body->length); - errors++; - } + soup_test_assert_message_status (msg, SOUP_STATUS_OK); + g_assert_cmpint (msg->response_body->length, ==, full_response->length); md5 = g_compute_checksum_for_data (G_CHECKSUM_MD5, (guchar *)msg->response_body->data, msg->response_body->length); - if (strcmp (md5, full_response_md5) != 0) { - debug_printf (1, " data mismatch: expected %s, got %s\n", - full_response_md5, md5); - errors++; - } + g_assert_cmpstr (md5, ==, full_response_md5); g_free (md5); g_object_unref (msg); } static void -do_tests (SoupURI *base_uri) +do_chunked_test (gconstpointer data) { + SoupURI *base_uri = (SoupURI *)data; SoupSession *session; session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); - debug_printf (1, "Chunked encoding\n"); do_request (session, base_uri, "chunked"); - debug_printf (1, "\n"); - debug_printf (1, "Content-Length encoding\n"); + soup_test_session_abort_unref (session); +} + +static void +do_content_length_test (gconstpointer data) +{ + SoupURI *base_uri = (SoupURI *)data; + SoupSession *session; + + session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); do_request (session, base_uri, "content-length"); - debug_printf (1, "\n"); - debug_printf (1, "EOF encoding\n"); + soup_test_session_abort_unref (session); +} + +static void +do_eof_test (gconstpointer data) +{ + SoupURI *base_uri = (SoupURI *)data; + SoupSession *session; + + g_test_bug ("572153"); + + session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); do_request (session, base_uri, "eof"); soup_test_session_abort_unref (session); } @@ -155,9 +140,14 @@ main (int argc, char **argv) SoupServer *server; guint port; SoupURI *base_uri; + int ret; test_init (argc, argv, NULL); - get_full_response (); + + full_response = soup_test_get_index (); + full_response_md5 = g_compute_checksum_for_data (G_CHECKSUM_MD5, + (guchar *)full_response->data, + full_response->length); server = soup_test_server_new (FALSE); soup_server_add_handler (server, NULL, @@ -168,14 +158,19 @@ main (int argc, char **argv) base_uri = soup_uri_new ("http://127.0.0.1"); soup_uri_set_port (base_uri, port); - do_tests (base_uri); - soup_uri_free (base_uri); + g_test_add_data_func ("/streaming/chunked", base_uri, do_chunked_test); + g_test_add_data_func ("/streaming/content-length", base_uri, do_content_length_test); + g_test_add_data_func ("/streaming/eof", base_uri, do_eof_test); + + ret = g_test_run (); + + soup_uri_free (base_uri); g_main_loop_unref (loop); - g_free (full_response); g_free (full_response_md5); soup_test_server_quit_unref (server); test_cleanup (); - return errors != 0; + + return ret; } diff --git a/tests/test-utils.c b/tests/test-utils.c index f040b738..bc160aea 100644 --- a/tests/test-utils.c +++ b/tests/test-utils.c @@ -12,8 +12,9 @@ static gboolean apache_running; #endif static SoupLogger *logger; +static SoupBuffer *index_buffer; -int debug_level, errors; +int debug_level; gboolean expect_warning, tls_available; static int http_debug_level; @@ -37,7 +38,7 @@ static GOptionEntry debug_entry[] = { { "debug", 'd', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, increment_debug_level, "Enable (or increase) test-specific debugging", NULL }, - { "http-debug", 'h', G_OPTION_FLAG_NO_ARG, + { "http-debug", 'H', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, increment_http_debug_level, "Enable (or increase) HTTP-level debugging", NULL }, { NULL } @@ -54,21 +55,6 @@ quit (int sig) exit (1); } -static void -test_log_handler (const char *log_domain, GLogLevelFlags log_level, - const char *message, gpointer user_data) -{ - if (log_level & (G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL)) { - if (expect_warning) { - expect_warning = FALSE; - debug_printf (2, "Got expected warning: %s\n", message); - return; - } else - errors++; - } - g_log_default_handler (log_domain, log_level, message, user_data); -} - void test_init (int argc, char **argv, GOptionEntry *entries) { @@ -78,7 +64,8 @@ test_init (int argc, char **argv, GOptionEntry *entries) GTlsBackend *tls_backend; setlocale (LC_ALL, ""); - g_type_init (); + g_setenv ("GSETTINGS_BACKEND", "memory", TRUE); + g_setenv ("GIO_USE_PROXY_RESOLVER", "dummy", TRUE); name = strrchr (argv[0], '/'); if (!name++) @@ -87,6 +74,10 @@ test_init (int argc, char **argv, GOptionEntry *entries) name += 3; g_set_prgname (name); + g_test_init (&argc, &argv, NULL); + g_test_set_nonfatal_assertions (); + g_test_bug_base ("https://bugzilla.gnome.org/"); + opts = g_option_context_new (NULL); g_option_context_add_main_entries (opts, debug_entry, NULL); if (entries) @@ -104,8 +95,6 @@ test_init (int argc, char **argv, GOptionEntry *entries) /* Exit cleanly on ^C in case we're valgrinding. */ signal (SIGINT, quit); - g_log_set_default_handler (test_log_handler, NULL); - tls_backend = g_tls_backend_get_default (); tls_available = g_tls_backend_supports_tls (tls_backend); } @@ -120,16 +109,12 @@ test_cleanup (void) if (logger) g_object_unref (logger); + if (index_buffer) + soup_buffer_free (index_buffer); g_main_context_unref (g_main_context_default ()); debug_printf (1, "\n"); - if (errors) { - g_print ("%s: %d error(s).%s\n", - g_get_prgname (), errors, - debug_level == 0 ? " Run with '-d' for details" : ""); - } else - g_print ("%s: OK\n", g_get_prgname ()); } void @@ -150,30 +135,52 @@ debug_printf (int level, const char *format, ...) static gboolean apache_cmd (const char *cmd) { - const char *argv[8]; - char *cwd, *conf; + GPtrArray *argv; + char *server_root, *cwd, *pid_file; +#ifdef HAVE_APACHE_2_4 + char *default_runtime_dir; +#endif int status; gboolean ok; + server_root = g_test_build_filename (G_TEST_BUILT, "", NULL); + cwd = g_get_current_dir (); - conf = g_build_filename (cwd, "httpd.conf", NULL); - - argv[0] = APACHE_HTTPD; - argv[1] = "-d"; - argv[2] = cwd; - argv[3] = "-f"; - argv[4] = conf; - argv[5] = "-k"; - argv[6] = cmd; - argv[7] = NULL; - - ok = g_spawn_sync (cwd, (char **)argv, NULL, 0, NULL, NULL, +#ifdef HAVE_APACHE_2_4 + default_runtime_dir = g_strdup_printf ("DefaultRuntimeDir %s", cwd); +#endif + pid_file = g_strdup_printf ("PidFile %s/httpd.pid", cwd); + + argv = g_ptr_array_new (); + g_ptr_array_add (argv, APACHE_HTTPD); + g_ptr_array_add (argv, "-d"); + g_ptr_array_add (argv, server_root); + g_ptr_array_add (argv, "-f"); + g_ptr_array_add (argv, "httpd.conf"); + +#ifdef HAVE_APACHE_2_4 + g_ptr_array_add (argv, "-c"); + g_ptr_array_add (argv, default_runtime_dir); +#endif + g_ptr_array_add (argv, "-c"); + g_ptr_array_add (argv, pid_file); + + g_ptr_array_add (argv, "-k"); + g_ptr_array_add (argv, (char *)cmd); + g_ptr_array_add (argv, NULL); + + ok = g_spawn_sync (cwd, (char **)argv->pdata, NULL, 0, NULL, NULL, NULL, NULL, &status, NULL); if (ok) ok = (status == 0); + g_free (server_root); g_free (cwd); - g_free (conf); + g_free (pid_file); +#ifdef HAVE_APACHE_2_4 + g_free (default_runtime_dir); +#endif + g_ptr_array_free (argv, TRUE); return ok; } @@ -181,6 +188,9 @@ apache_cmd (const char *cmd) void apache_init (void) { + if (g_getenv ("SOUP_TESTS_IN_MAKE_CHECK")) + return; + if (!apache_cmd ("start")) { g_printerr ("Could not start apache\n"); exit (1); @@ -218,15 +228,18 @@ soup_test_session_new (GType type, ...) va_list args; const char *propname; SoupSession *session; + char *cafile; va_start (args, type); propname = va_arg (args, const char *); session = (SoupSession *)g_object_new_valist (type, propname, args); va_end (args); + cafile = g_test_build_filename (G_TEST_DIST, "test-cert.pem", NULL); g_object_set (G_OBJECT (session), - SOUP_SESSION_SSL_CA_FILE, SRCDIR "/test-cert.pem", + SOUP_SESSION_SSL_CA_FILE, cafile, NULL); + g_free (cafile); if (http_debug_level && !logger) { SoupLoggerLogLevel level = MIN ((SoupLoggerLogLevel)http_debug_level, SOUP_LOGGER_LOG_BODY); @@ -243,16 +256,10 @@ soup_test_session_new (GType type, ...) void soup_test_session_abort_unref (SoupSession *session) { - g_object_add_weak_pointer (G_OBJECT (session), (gpointer *)&session); - soup_session_abort (session); - g_object_unref (session); - if (session) { - errors++; - debug_printf (1, "leaked SoupSession!\n"); - g_object_remove_weak_pointer (G_OBJECT (session), (gpointer *)&session); - } + g_assert_cmpint (G_OBJECT (session)->ref_count, ==, 1); + g_object_unref (session); } static gpointer run_server_thread (gpointer user_data); @@ -262,14 +269,14 @@ test_server_new (gboolean in_own_thread, gboolean ssl) { SoupServer *server; GMainContext *async_context; - const char *ssl_cert_file, *ssl_key_file; + char *ssl_cert_file, *ssl_key_file; SoupAddress *addr; async_context = in_own_thread ? g_main_context_new () : NULL; if (ssl) { - ssl_cert_file = SRCDIR "/test-cert.pem"; - ssl_key_file = SRCDIR "/test-key.pem"; + ssl_cert_file = g_test_build_filename (G_TEST_DIST, "test-cert.pem", NULL); + ssl_key_file = g_test_build_filename (G_TEST_DIST, "test-key.pem", NULL); } else ssl_cert_file = ssl_key_file = NULL; @@ -284,6 +291,8 @@ test_server_new (gboolean in_own_thread, gboolean ssl) g_object_unref (addr); if (async_context) g_main_context_unref (async_context); + g_free (ssl_cert_file); + g_free (ssl_key_file); if (!server) { g_printerr ("Unable to create server\n"); @@ -334,9 +343,6 @@ soup_test_server_quit_unref (SoupServer *server) { GThread *thread; - g_object_add_weak_pointer (G_OBJECT (server), - (gpointer *)&server); - thread = g_object_get_data (G_OBJECT (server), "thread"); if (thread) { soup_add_completion (soup_server_get_async_context (server), @@ -344,19 +350,14 @@ soup_test_server_quit_unref (SoupServer *server) g_thread_join (thread); } else soup_server_quit (server); - g_object_unref (server); - if (server) { - errors++; - debug_printf (1, "leaked SoupServer!\n"); - g_object_remove_weak_pointer (G_OBJECT (server), - (gpointer *)&server); - } + g_assert_cmpint (G_OBJECT (server)->ref_count, ==, 1); + g_object_unref (server); } typedef struct { - GMainLoop *loop; - GAsyncResult *result; + GMainLoop *loop; + GAsyncResult *result; } AsyncAsSyncData; static void @@ -364,51 +365,269 @@ async_as_sync_callback (GObject *object, GAsyncResult *result, gpointer user_data) { - AsyncAsSyncData *data = user_data; + AsyncAsSyncData *data = user_data; + GMainContext *context; + + data->result = g_object_ref (result); + context = g_main_loop_get_context (data->loop); + while (g_main_context_pending (context)) + g_main_context_iteration (context, FALSE); + g_main_loop_quit (data->loop); +} + +typedef struct { + SoupRequest *req; + GCancellable *cancellable; + SoupTestRequestFlags flags; +} CancelData; + +static CancelData * +create_cancel_data (SoupRequest *req, + GCancellable *cancellable, + SoupTestRequestFlags flags) +{ + CancelData *cancel_data; + + if (!flags) + return NULL; + + cancel_data = g_slice_new0 (CancelData); + cancel_data->flags = flags; + if (flags & SOUP_TEST_REQUEST_CANCEL_MESSAGE && SOUP_IS_REQUEST_HTTP (req)) + cancel_data->req = g_object_ref (req); + else if (flags & SOUP_TEST_REQUEST_CANCEL_CANCELLABLE) + cancel_data->cancellable = g_object_ref (cancellable); + return cancel_data; +} - data->result = g_object_ref (result); - g_main_loop_quit (data->loop); +static void inline +cancel_message_or_cancellable (CancelData *cancel_data) +{ + if (cancel_data->flags & SOUP_TEST_REQUEST_CANCEL_MESSAGE) { + SoupRequest *req = cancel_data->req; + SoupMessage *msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (req)); + soup_session_cancel_message (soup_request_get_session (req), msg, + SOUP_STATUS_CANCELLED); + g_object_unref (msg); + g_object_unref (req); + } else if (cancel_data->flags & SOUP_TEST_REQUEST_CANCEL_CANCELLABLE) { + g_cancellable_cancel (cancel_data->cancellable); + g_object_unref (cancel_data->cancellable); + } + g_slice_free (CancelData, cancel_data); +} + +static gboolean +cancel_request_timeout (gpointer data) +{ + cancel_message_or_cancellable ((CancelData *) data); + return FALSE; +} + +static gpointer +cancel_request_thread (gpointer data) +{ + g_usleep (100000); /* .1s */ + cancel_message_or_cancellable ((CancelData *) data); + return NULL; } GInputStream * -soup_test_request_send_async_as_sync (SoupRequest *req, - GCancellable *cancellable, - GError **error) +soup_test_request_send (SoupRequest *req, + GCancellable *cancellable, + guint flags, + GError **error) { - AsyncAsSyncData data; - GInputStream *stream; + AsyncAsSyncData data; + GInputStream *stream; + CancelData *cancel_data = create_cancel_data (req, cancellable, flags); - data.loop = g_main_loop_new (g_main_context_get_thread_default (), FALSE); + if (SOUP_IS_SESSION_SYNC (soup_request_get_session (req))) { + GThread *thread; - soup_request_send_async (req, cancellable, async_as_sync_callback, &data); - g_main_loop_run (data.loop); + if (cancel_data) + thread = g_thread_new ("cancel_request_thread", cancel_request_thread, + cancel_data); + stream = soup_request_send (req, cancellable, error); + if (cancel_data) + g_thread_unref (thread); + return stream; + } + + data.loop = g_main_loop_new (g_main_context_get_thread_default (), FALSE); + if (cancel_data && + (flags & SOUP_TEST_REQUEST_CANCEL_SOON || flags & SOUP_TEST_REQUEST_CANCEL_IMMEDIATE)) { + guint interval = flags & SOUP_TEST_REQUEST_CANCEL_SOON ? 100 : 0; + g_timeout_add_full (G_PRIORITY_HIGH, interval, cancel_request_timeout, cancel_data, NULL); + } + if (cancel_data && (flags & SOUP_TEST_REQUEST_CANCEL_PREEMPTIVE)) + g_cancellable_cancel (cancellable); + soup_request_send_async (req, cancellable, async_as_sync_callback, &data); + g_main_loop_run (data.loop); - stream = soup_request_send_finish (req, data.result, error); + stream = soup_request_send_finish (req, data.result, error); - g_main_loop_unref (data.loop); - g_object_unref (data.result); + if (cancel_data && (flags & SOUP_TEST_REQUEST_CANCEL_AFTER_SEND_FINISH)) { + GMainContext *context; - return stream; + cancel_message_or_cancellable (cancel_data); + + context = g_main_loop_get_context (data.loop); + while (g_main_context_pending (context)) + g_main_context_iteration (context, FALSE); + } + + g_main_loop_unref (data.loop); + g_object_unref (data.result); + + return stream; +} + +gboolean +soup_test_request_read_all (SoupRequest *req, + GInputStream *stream, + GCancellable *cancellable, + GError **error) +{ + char buf[8192]; + AsyncAsSyncData data; + gsize nread; + + if (!SOUP_IS_SESSION_SYNC (soup_request_get_session (req))) + data.loop = g_main_loop_new (g_main_context_get_thread_default (), FALSE); + + do { + if (SOUP_IS_SESSION_SYNC (soup_request_get_session (req))) { + nread = g_input_stream_read (stream, buf, sizeof (buf), + cancellable, error); + } else { + g_input_stream_read_async (stream, buf, sizeof (buf), + G_PRIORITY_DEFAULT, cancellable, + async_as_sync_callback, &data); + g_main_loop_run (data.loop); + nread = g_input_stream_read_finish (stream, data.result, error); + g_object_unref (data.result); + } + } while (nread > 0); + + if (!SOUP_IS_SESSION_SYNC (soup_request_get_session (req))) + g_main_loop_unref (data.loop); + + return nread == 0; } gboolean -soup_test_stream_close_async_as_sync (GInputStream *stream, - GCancellable *cancellable, - GError **error) +soup_test_request_close_stream (SoupRequest *req, + GInputStream *stream, + GCancellable *cancellable, + GError **error) { - AsyncAsSyncData data; - gboolean ok; + AsyncAsSyncData data; + gboolean ok; - data.loop = g_main_loop_new (g_main_context_get_thread_default (), FALSE); + if (SOUP_IS_SESSION_SYNC (soup_request_get_session (req))) + return g_input_stream_close (stream, cancellable, error); - g_input_stream_close_async (stream, G_PRIORITY_DEFAULT, cancellable, - async_as_sync_callback, &data); - g_main_loop_run (data.loop); + data.loop = g_main_loop_new (g_main_context_get_thread_default (), FALSE); - ok = g_input_stream_close_finish (stream, data.result, error); + g_input_stream_close_async (stream, G_PRIORITY_DEFAULT, cancellable, + async_as_sync_callback, &data); + g_main_loop_run (data.loop); - g_main_loop_unref (data.loop); - g_object_unref (data.result); + ok = g_input_stream_close_finish (stream, data.result, error); - return ok; + g_main_loop_unref (data.loop); + g_object_unref (data.result); + + return ok; } + +void +soup_test_register_resources (void) +{ + static gboolean registered = FALSE; + GResource *resource; + char *path; + GError *error = NULL; + + if (registered) + return; + + path = g_test_build_filename (G_TEST_BUILT, "soup-tests.gresource", NULL); + resource = g_resource_load (path, &error); + if (!resource) { + g_printerr ("Could not load resource soup-tests.gresource: %s\n", + error->message); + exit (1); + } + g_free (path); + + g_resources_register (resource); + g_resource_unref (resource); + + registered = TRUE; +} + +SoupBuffer * +soup_test_load_resource (const char *name, + GError **error) +{ + GBytes *bytes; + char *path; + + soup_test_register_resources (); + + path = g_build_path ("/", "/org/gnome/libsoup/tests/resources", name, NULL); + bytes = g_resources_lookup_data (path, G_RESOURCE_LOOKUP_FLAGS_NONE, error); + g_free (path); + + if (!bytes) + return NULL; + + return soup_buffer_new_with_owner (g_bytes_get_data (bytes, NULL), + g_bytes_get_size (bytes), + bytes, + (GDestroyNotify) g_bytes_unref); +} + +SoupBuffer * +soup_test_get_index (void) +{ + if (!index_buffer) { + char *path, *contents; + gsize length; + GError *error = NULL; + + path = g_test_build_filename (G_TEST_DIST, "index.txt", NULL); + if (!g_file_get_contents (path, &contents, &length, &error)) { + g_printerr ("Could not read index.txt: %s\n", + error->message); + exit (1); + } + g_free (path); + + index_buffer = soup_buffer_new (SOUP_MEMORY_TAKE, contents, length); + } + + return index_buffer; +} + +#ifndef G_HAVE_ISO_VARARGS +void +soup_test_assert (gboolean expr, const char *fmt, ...) +{ + char *message; + va_list args; + + if (G_UNLIKELY (!expr)) { + va_start (args, fmt); + message = g_strdup_vprintf (fmt, args); + va_end (args); + + g_assertion_message (G_LOG_DOMAIN, + "???", 0, "???" + message); + g_free (message); + } +} +#endif diff --git a/tests/test-utils.h b/tests/test-utils.h index 9e969038..03637dcf 100644 --- a/tests/test-utils.h +++ b/tests/test-utils.h @@ -1,3 +1,5 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -9,22 +11,47 @@ #include "libsoup/soup.h" #include "libsoup/soup-requester.h" -#include "libsoup/soup-request-data.h" -#include "libsoup/soup-request-file.h" -#include "libsoup/soup-request-http.h" void test_init (int argc, char **argv, GOptionEntry *entries); void test_cleanup (void); -extern int debug_level, errors; -extern gboolean expect_warning, tls_available; +extern int debug_level; +extern gboolean tls_available; +extern gboolean apache_available; void debug_printf (int level, const char *format, ...) G_GNUC_PRINTF (2, 3); +#define SOUP_TEST_SKIP_IF_NO_TLS \ + G_STMT_START { \ + if (!tls_available) { \ + g_test_skip ("TLS is not available"); \ + return; \ + } \ + } G_STMT_END + #ifdef HAVE_APACHE void apache_init (void); void apache_cleanup (void); +#define SOUP_TEST_SKIP_IF_NO_APACHE +#else +#define apache_init() +#define apache_cleanup() +#define SOUP_TEST_SKIP_IF_NO_APACHE \ + G_STMT_START { \ + g_test_skip ("apache is not available"); \ + return; \ + } G_STMT_END #endif +typedef enum { + SOUP_TEST_REQUEST_NONE = 0, + SOUP_TEST_REQUEST_CANCEL_MESSAGE = (1 << 0), + SOUP_TEST_REQUEST_CANCEL_CANCELLABLE = (1 << 1), + SOUP_TEST_REQUEST_CANCEL_SOON = (1 << 2), + SOUP_TEST_REQUEST_CANCEL_IMMEDIATE = (1 << 3), + SOUP_TEST_REQUEST_CANCEL_PREEMPTIVE = (1 << 4), + SOUP_TEST_REQUEST_CANCEL_AFTER_SEND_FINISH = (1 << 5), +} SoupTestRequestFlags; + SoupSession *soup_test_session_new (GType type, ...); void soup_test_session_abort_unref (SoupSession *session); @@ -32,9 +59,68 @@ SoupServer *soup_test_server_new (gboolean in_own_thread); SoupServer *soup_test_server_new_ssl (gboolean in_own_thread); void soup_test_server_quit_unref (SoupServer *server); -GInputStream *soup_test_request_send_async_as_sync (SoupRequest *req, - GCancellable *cancellable, - GError **error); -gboolean soup_test_stream_close_async_as_sync (GInputStream *stream, - GCancellable *cancellable, - GError **error); +GInputStream *soup_test_request_send (SoupRequest *req, + GCancellable *cancellable, + guint flags, + GError **error); +gboolean soup_test_request_read_all (SoupRequest *req, + GInputStream *stream, + GCancellable *cancellable, + GError **error); +gboolean soup_test_request_close_stream (SoupRequest *req, + GInputStream *stream, + GCancellable *cancellable, + GError **error); + +void soup_test_register_resources (void); +SoupBuffer *soup_test_load_resource (const char *name, + GError **error); + +SoupBuffer *soup_test_get_index (void); + +#ifdef G_HAVE_ISO_VARARGS +#define soup_test_assert(expr, ...) \ +G_STMT_START { \ + char *_message; \ + if (G_UNLIKELY (!(expr))) { \ + _message = g_strdup_printf (__VA_ARGS__); \ + g_assertion_message (G_LOG_DOMAIN, \ + __FILE__, __LINE__, G_STRFUNC, \ + _message); \ + g_free (_message); \ + } \ +} G_STMT_END +#else +void soup_test_assert (gboolean expr, const char *fmt, ...); +#endif + +#define soup_test_assert_message_status(msg, status) \ +G_STMT_START { \ + SoupMessage *_msg = (msg); \ + guint _status = (status); \ + char *_message; \ + \ + if (G_UNLIKELY (_msg->status_code != _status)) { \ + _message = g_strdup_printf ("Unexpected status %d %s (expected %d %s)", \ + _msg->status_code, _msg->reason_phrase, \ + _status, soup_status_get_phrase (_status)); \ + g_assertion_message (G_LOG_DOMAIN, \ + __FILE__, __LINE__, G_STRFUNC, \ + _message); \ + g_free (_message); \ + } \ +} G_STMT_END + +#define soup_assert_cmpmem(s1, l1, s2, l2) \ +G_STMT_START { \ + int __l1 = l1, __l2 = l2; \ + gconstpointer __s1 = s1, __s2 = s2; \ + if (G_UNLIKELY ((__l1) != (__l2))) { \ + g_assertion_message_cmpnum (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ + "len(" #s1 ") == len(" #s2 ")", __l1, "==", __l2, \ + 'i'); \ + } else if (G_UNLIKELY (memcmp (__s1, __s2, __l1) != 0)) { \ + g_assertion_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ + "assertion failed (" #s1 " == " #s2 ")"); \ + } \ +} G_STMT_END diff --git a/tests/timeout-test.c b/tests/timeout-test.c index 2bb91d8a..81fb4331 100644 --- a/tests/timeout-test.c +++ b/tests/timeout-test.c @@ -2,31 +2,14 @@ #include "test-utils.h" +static gboolean slow_https; + static void -do_message_to_session (SoupSession *session, const char *uri, - const char *comment, guint expected_status) +message_finished (SoupMessage *msg, gpointer user_data) { - SoupMessage *msg; - - debug_printf (1, " %s\n", comment); - msg = soup_message_new ("GET", uri); - soup_session_send_message (session, msg); - - if (msg->status_code != expected_status) { - debug_printf (1, " FAILED: %d %s (expected %d %s)\n", - msg->status_code, msg->reason_phrase, - expected_status, - soup_status_get_phrase (expected_status)); - errors++; - } + gboolean *finished = user_data; - if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code) && - !soup_message_is_keepalive (msg)) { - debug_printf (1, " ERROR: message is not keepalive!"); - errors++; - } - - g_object_unref (msg); + *finished = TRUE; } static void @@ -39,10 +22,37 @@ request_started_cb (SoupSession *session, SoupMessage *msg, } static void -do_tests_for_session (SoupSession *timeout_session, - SoupSession *idle_session, - SoupSession *plain_session, - char *fast_uri, char *slow_uri) +do_message_to_session (SoupSession *session, const char *uri, + const char *comment, guint expected_status) +{ + SoupMessage *msg; + gboolean finished = FALSE; + + if (comment) + debug_printf (1, " msg %s\n", comment); + msg = soup_message_new ("GET", uri); + + g_signal_connect (msg, "finished", + G_CALLBACK (message_finished), &finished); + soup_session_send_message (session, msg); + + soup_test_assert_message_status (msg, expected_status); + if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) + g_assert_true (soup_message_is_keepalive (msg)); + g_assert_true (finished); + + g_signal_handlers_disconnect_by_func (msg, + G_CALLBACK (message_finished), + &finished); + g_object_unref (msg); +} + +static void +do_msg_tests_for_session (SoupSession *timeout_session, + SoupSession *idle_session, + SoupSession *plain_session, + const char *fast_uri, + const char *slow_uri) { SoupSocket *ret, *idle_first, *idle_second; SoupSocket *plain_first, *plain_second; @@ -51,14 +61,14 @@ do_tests_for_session (SoupSession *timeout_session, g_signal_connect (idle_session, "request-started", G_CALLBACK (request_started_cb), &ret); do_message_to_session (idle_session, fast_uri, "fast to idle", SOUP_STATUS_OK); - idle_first = ret; + idle_first = g_object_ref (ret); } if (plain_session) { g_signal_connect (plain_session, "request-started", G_CALLBACK (request_started_cb), &ret); do_message_to_session (plain_session, fast_uri, "fast to plain", SOUP_STATUS_OK); - plain_first = ret; + plain_first = g_object_ref (ret); } do_message_to_session (timeout_session, fast_uri, "fast to timeout", SOUP_STATUS_OK); @@ -71,10 +81,9 @@ do_tests_for_session (SoupSession *timeout_session, (gpointer)request_started_cb, &ret); - if (idle_first == idle_second) { - debug_printf (1, " ERROR: idle_session did not close first connection\n"); - errors++; - } + soup_test_assert (idle_first != idle_second, + "idle_session did not close first connection"); + g_object_unref (idle_first); } if (plain_session) { @@ -84,46 +93,174 @@ do_tests_for_session (SoupSession *timeout_session, (gpointer)request_started_cb, &ret); - if (plain_first != plain_second) { - debug_printf (1, " ERROR: plain_session closed connection\n"); - errors++; - } + soup_test_assert (plain_first == plain_second, + "plain_session closed connection"); + g_object_unref (plain_first); + } +} + +static void +do_request_to_session (SoupSession *session, const char *uri, + const char *comment, gboolean expect_timeout) +{ + SoupRequest *req; + SoupMessage *msg; + GInputStream *stream; + GError *error = NULL; + gboolean finished = FALSE; + + debug_printf (1, " req %s\n", comment); + req = soup_session_request (session, uri, NULL); + msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (req)); + + g_signal_connect (msg, "finished", + G_CALLBACK (message_finished), &finished); + stream = soup_test_request_send (req, NULL, 0, &error); + + if (expect_timeout) + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT); + else + g_assert_no_error (error); + g_clear_error (&error); + + if (stream) { + soup_test_request_read_all (req, stream, NULL, &error); + g_assert_no_error (error); + } + + if (stream) { + soup_test_request_close_stream (req, stream, NULL, &error); + g_assert_no_error (error); + g_object_unref (stream); } + + if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) + g_assert_true (soup_message_is_keepalive (msg)); + g_assert_true (finished); + + g_signal_handlers_disconnect_by_func (msg, + G_CALLBACK (message_finished), + &finished); + g_object_unref (msg); + g_object_unref (req); } static void -do_timeout_tests (char *fast_uri, char *slow_uri) +do_req_tests_for_session (SoupSession *timeout_session, + SoupSession *idle_session, + SoupSession *plain_session, + const char *fast_uri, + const char *slow_uri) +{ + SoupSocket *ret, *idle_first, *idle_second; + SoupSocket *plain_first, *plain_second; + + if (idle_session) { + g_signal_connect (idle_session, "request-started", + G_CALLBACK (request_started_cb), &ret); + do_request_to_session (idle_session, fast_uri, "fast to idle", FALSE); + idle_first = g_object_ref (ret); + } + + if (plain_session) { + g_signal_connect (plain_session, "request-started", + G_CALLBACK (request_started_cb), &ret); + do_request_to_session (plain_session, fast_uri, "fast to plain", FALSE); + plain_first = g_object_ref (ret); + } + + do_request_to_session (timeout_session, fast_uri, "fast to timeout", FALSE); + do_request_to_session (timeout_session, slow_uri, "slow to timeout", TRUE); + + if (idle_session) { + do_request_to_session (idle_session, fast_uri, "fast to idle", FALSE); + idle_second = ret; + g_signal_handlers_disconnect_by_func (idle_session, + (gpointer)request_started_cb, + &ret); + + soup_test_assert (idle_first != idle_second, + "idle_session did not close first connection"); + g_object_unref (idle_first); + } + + if (plain_session) { + do_request_to_session (plain_session, fast_uri, "fast to plain", FALSE); + plain_second = ret; + g_signal_handlers_disconnect_by_func (plain_session, + (gpointer)request_started_cb, + &ret); + + soup_test_assert (plain_first == plain_second, + "plain_session closed connection"); + g_object_unref (plain_first); + } +} + +static void +do_async_timeout_tests (gconstpointer data) { SoupSession *timeout_session, *idle_session, *plain_session; + const char *fast_uri = data; + const char *slow_uri = g_build_path ("/", fast_uri, "slow", NULL); + gboolean extra_slow; + + if (g_str_has_prefix (fast_uri, "https")) { + SOUP_TEST_SKIP_IF_NO_TLS; + + extra_slow = slow_https; + } else + extra_slow = FALSE; - debug_printf (1, " async\n"); timeout_session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, - SOUP_SESSION_TIMEOUT, 1, - NULL); + SOUP_SESSION_TIMEOUT, extra_slow ? 3 : 1, + SOUP_SESSION_USE_THREAD_CONTEXT, TRUE, + NULL); idle_session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, - SOUP_SESSION_IDLE_TIMEOUT, 1, + SOUP_SESSION_IDLE_TIMEOUT, extra_slow ? 2 : 1, + SOUP_SESSION_USE_THREAD_CONTEXT, TRUE, NULL); /* The "plain" session also has an idle timeout, but it's longer * than the test takes, so for our purposes it should behave like * it has no timeout. */ plain_session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, - SOUP_SESSION_IDLE_TIMEOUT, 2, + SOUP_SESSION_IDLE_TIMEOUT, 20, + SOUP_SESSION_USE_THREAD_CONTEXT, TRUE, NULL); - do_tests_for_session (timeout_session, idle_session, plain_session, - fast_uri, slow_uri); + + do_msg_tests_for_session (timeout_session, idle_session, plain_session, + fast_uri, slow_uri); + do_req_tests_for_session (timeout_session, idle_session, plain_session, + fast_uri, slow_uri); soup_test_session_abort_unref (timeout_session); soup_test_session_abort_unref (idle_session); soup_test_session_abort_unref (plain_session); +} + +static void +do_sync_timeout_tests (gconstpointer data) +{ + SoupSession *timeout_session, *plain_session; + const char *fast_uri = data; + const char *slow_uri = g_build_path ("/", fast_uri, "slow", NULL); + gboolean extra_slow; + + if (g_str_has_prefix (fast_uri, "https")) { + SOUP_TEST_SKIP_IF_NO_TLS; + + extra_slow = slow_https; + } else + extra_slow = FALSE; - debug_printf (1, " sync\n"); timeout_session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, - SOUP_SESSION_TIMEOUT, 1, + SOUP_SESSION_TIMEOUT, extra_slow ? 3 : 1, NULL); /* SOUP_SESSION_TIMEOUT doesn't work with sync sessions */ plain_session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL); - do_tests_for_session (timeout_session, NULL, plain_session, fast_uri, slow_uri); + do_msg_tests_for_session (timeout_session, NULL, plain_session, fast_uri, slow_uri); + do_req_tests_for_session (timeout_session, NULL, plain_session, fast_uri, slow_uri); soup_test_session_abort_unref (timeout_session); soup_test_session_abort_unref (plain_session); } @@ -154,44 +291,63 @@ server_handler (SoupServer *server, soup_server_pause_message (server, msg); g_object_set_data (G_OBJECT (msg), "server", server); soup_add_timeout (soup_server_get_async_context (server), - 1100, timeout_finish_message, msg); + 4000, timeout_finish_message, msg); } } int main (int argc, char **argv) { - SoupServer *server; - char *fast_uri, *slow_uri; + SoupServer *server, *https_server = NULL; + char *uri, *https_uri = NULL; + int ret; test_init (argc, argv, NULL); - debug_printf (1, "http\n"); server = soup_test_server_new (TRUE); soup_server_add_handler (server, NULL, server_handler, NULL, NULL); - fast_uri = g_strdup_printf ("http://127.0.0.1:%u/", - soup_server_get_port (server)); - slow_uri = g_strdup_printf ("http://127.0.0.1:%u/slow", - soup_server_get_port (server)); - do_timeout_tests (fast_uri, slow_uri); - g_free (fast_uri); - g_free (slow_uri); - soup_test_server_quit_unref (server); + uri = g_strdup_printf ("http://127.0.0.1:%u/", + soup_server_get_port (server)); if (tls_available) { - debug_printf (1, "\nhttps\n"); - server = soup_test_server_new_ssl (TRUE); - soup_server_add_handler (server, NULL, server_handler, NULL, NULL); - fast_uri = g_strdup_printf ("https://127.0.0.1:%u/", - soup_server_get_port (server)); - slow_uri = g_strdup_printf ("https://127.0.0.1:%u/slow", - soup_server_get_port (server)); - do_timeout_tests (fast_uri, slow_uri); - g_free (fast_uri); - g_free (slow_uri); - soup_test_server_quit_unref (server); - } + SoupSession *test_session; + gint64 start, end; + + https_server = soup_test_server_new_ssl (TRUE); + soup_server_add_handler (https_server, NULL, server_handler, NULL, NULL); + https_uri = g_strdup_printf ("https://127.0.0.1:%u/", + soup_server_get_port (https_server)); + + /* The 1-second timeouts are too fast for some machines... */ + test_session = soup_test_session_new (SOUP_TYPE_SESSION, NULL); + start = g_get_monotonic_time (); + do_message_to_session (test_session, uri, NULL, SOUP_STATUS_OK); + end = g_get_monotonic_time (); + soup_test_session_abort_unref (test_session); + debug_printf (2, " (https request took %0.3fs)\n", (end - start) / 1000000.0); + if (end - start > 750000) { + debug_printf (1, " (using extra-slow mode)\n\n"); + slow_https = TRUE; + } else { + debug_printf (2, "\n"); + slow_https = FALSE; + } + } else + https_uri = g_strdup ("https://fail."); + + g_test_add_data_func ("/timeout/http/async", uri, do_async_timeout_tests); + g_test_add_data_func ("/timeout/http/sync", uri, do_sync_timeout_tests); + g_test_add_data_func ("/timeout/https/async", https_uri, do_async_timeout_tests); + g_test_add_data_func ("/timeout/https/sync", https_uri, do_sync_timeout_tests); + + ret = g_test_run (); + + g_free (uri); + g_free (https_uri); + soup_test_server_quit_unref (server); + if (https_server) + soup_test_server_quit_unref (https_server); test_cleanup (); - return errors != 0; + return ret; } diff --git a/tests/tld-test.c b/tests/tld-test.c index 5f66c686..4fad8625 100644 --- a/tests/tld-test.c +++ b/tests/tld-test.c @@ -7,124 +7,186 @@ /* From http://publicsuffix.org/list/test.txt */ static struct { - const char *hostname; - const char *result; + const char *hostname; + const char *result; + SoupTLDError error; } tld_tests[] = { - /* NULL input. Not checked here because the API requires a valid hostname. */ - /* { NULL, NULL }, */ - /* Mixed case. Not checked because the API requires a valid hostname. */ - /* { "COM", NULL }, */ - /* { "example.COM", "example.com" }, */ - /* { "WwW.example.COM", "example.com" }, */ - /* Leading dot. */ - { ".com", NULL }, - { ".example", NULL }, - { ".example.com", NULL }, - { ".example.example", NULL }, - /* Unlisted TLD.*/ - { "example", NULL }, - { "example.example", NULL }, - { "b.example.example", NULL }, - { "a.b.example.example", NULL }, - /* Listed, but non-Internet, TLD. */ - { "local", NULL }, - { "example.local", NULL }, - { "b.example.local", NULL }, - { "a.b.example.local", NULL }, - /* TLD with only 1 rule. */ - { "biz", NULL }, - { "domain.biz", "domain.biz" }, - { "b.domain.biz", "domain.biz" }, - { "a.b.domain.biz", "domain.biz" }, - /* TLD with some 2-level rules. */ - { "com", NULL }, - { "example.com", "example.com" }, - { "b.example.com", "example.com" }, - { "a.b.example.com", "example.com" }, - { "uk.com", NULL }, - { "example.uk.com", "example.uk.com" }, - { "b.example.uk.com", "example.uk.com" }, - { "a.b.example.uk.com", "example.uk.com" }, - { "test.ac", "test.ac" }, - /* TLD with only 1 (wildcard) rule. */ - { "cy", NULL }, - { "c.cy", NULL }, - { "b.c.cy", "b.c.cy" }, - { "a.b.c.cy", "b.c.cy" }, - /* More complex TLD. */ - { "jp", NULL }, - { "test.jp", "test.jp" }, - { "www.test.jp", "test.jp" }, - { "ac.jp", NULL }, - { "test.ac.jp", "test.ac.jp" }, - { "www.test.ac.jp", "test.ac.jp" }, - { "kyoto.jp", NULL }, - { "c.kyoto.jp", NULL }, - { "b.c.kyoto.jp", "b.c.kyoto.jp" }, - { "a.b.c.kyoto.jp", "b.c.kyoto.jp" }, - { "pref.kyoto.jp", "pref.kyoto.jp" }, /* Exception rule. */ - { "www.pref.kyoto.jp", "pref.kyoto.jp" }, /* Exception rule. */ - { "city.kyoto.jp", "city.kyoto.jp" }, /* Exception rule. */ - { "www.city.kyoto.jp", "city.kyoto.jp" }, /* Exception rule. */ - /* TLD with a wildcard rule and exceptions. */ - { "om", NULL }, - { "test.om", NULL }, - { "b.test.om", "b.test.om" }, - { "a.b.test.om", "b.test.om" }, - { "songfest.om", "songfest.om" }, - { "www.songfest.om", "songfest.om" }, - /* US K12. */ - { "us", NULL }, - { "test.us", "test.us" }, - { "www.test.us", "test.us" }, - { "ak.us", NULL }, - { "test.ak.us", "test.ak.us" }, - { "www.test.ak.us", "test.ak.us" }, - { "k12.ak.us", NULL }, - { "test.k12.ak.us", "test.k12.ak.us" }, - { "www.test.k12.ak.us", "test.k12.ak.us" }, - /* This is not in http://publicsuffix.org/list/test.txt but we want to check it anyway. */ - { "co.uk", NULL }, - /* The original list does not include non-ASCII tests. Let's add a couple. */ - { "公司.cn", NULL }, - { "a.b.åfjord.no", "b.åfjord.no" } + /* NULL input. Not checked here because the API requires a valid hostname. */ + /* { NULL, NULL, -1 }, */ + /* Mixed case. Not checked because the API requires a valid hostname. */ + /* { "COM", NULL, -1 }, */ + /* { "example.COM", "example.com", -1 }, */ + /* { "WwW.example.COM", "example.com", -1 }, */ + /* Leading dot. */ + { ".com", NULL, SOUP_TLD_ERROR_INVALID_HOSTNAME }, + { ".example", NULL, SOUP_TLD_ERROR_INVALID_HOSTNAME }, + { ".example.com", NULL, SOUP_TLD_ERROR_INVALID_HOSTNAME }, + { ".example.example", NULL, SOUP_TLD_ERROR_INVALID_HOSTNAME }, + /* TLD with only 1 rule. */ + { "biz", NULL, SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS }, + { "domain.biz", "domain.biz", -1 }, + { "b.domain.biz", "domain.biz", -1 }, + { "a.b.domain.biz", "domain.biz", -1 }, + /* TLD with some 2-level rules. */ + { "com", NULL, SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS }, + { "example.com", "example.com", -1 }, + { "b.example.com", "example.com", -1 }, + { "a.b.example.com", "example.com", -1 }, + { "uk.com", NULL, SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS }, + { "example.uk.com", "example.uk.com", -1 }, + { "b.example.uk.com", "example.uk.com", -1 }, + { "a.b.example.uk.com", "example.uk.com", -1 }, + { "test.ac", "test.ac", -1 }, + /* TLD with only 1 (wildcard) rule. */ + { "cy", NULL, SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS }, + { "c.cy", NULL, SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS }, + { "b.c.cy", "b.c.cy", -1 }, + { "a.b.c.cy", "b.c.cy", -1 }, + /* More complex TLD. */ + { "jp", NULL, SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS }, + { "test.jp", "test.jp", -1 }, + { "www.test.jp", "test.jp", -1 }, + { "ac.jp", NULL, SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS }, + { "test.ac.jp", "test.ac.jp", -1 }, + { "www.test.ac.jp", "test.ac.jp", -1 }, + { "kyoto.jp", NULL, SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS }, + { "minami.kyoto.jp", NULL, SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS }, + { "b.minami.kyoto.jp", "b.minami.kyoto.jp", -1 }, + { "a.b.minami.kyoto.jp", "b.minami.kyoto.jp", -1 }, + { "pref.kyoto.jp", "pref.kyoto.jp", -1 }, + { "www.pref.kyoto.jp", "pref.kyoto.jp", -1 }, + { "city.kyoto.jp", "city.kyoto.jp", -1 }, + { "www.city.kyoto.jp", "city.kyoto.jp", -1 }, + /* TLD with a wildcard rule and exceptions. */ + { "ck", NULL, SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS }, + { "test.ck", NULL, SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS }, + { "b.test.ck", "b.test.ck", -1 }, + { "a.b.test.ck", "b.test.ck", -1 }, + { "www.ck", "www.ck", -1 }, + { "www.www.ck", "www.ck", -1 }, + /* US K12. */ + { "us", NULL, SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS }, + { "test.us", "test.us", -1 }, + { "www.test.us", "test.us", -1 }, + { "ak.us", NULL, SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS }, + { "test.ak.us", "test.ak.us", -1 }, + { "www.test.ak.us", "test.ak.us", -1 }, + { "k12.ak.us", NULL, SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS }, + { "test.k12.ak.us", "test.k12.ak.us", -1 }, + { "www.test.k12.ak.us", "test.k12.ak.us", -1 }, + /* IDN labels. */ + { "食狮.com.cn", "食狮.com.cn", -1 }, + { "食狮.公司.cn", "食狮.公司.cn", -1 }, + { "www.食狮.公司.cn", "食狮.公司.cn", -1 }, + { "shishi.公司.cn", "shishi.公司.cn", -1 }, + { "公司.cn", NULL, SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS }, + { "食狮.中国", "食狮.中国", -1 }, + { "www.食狮.中国", "食狮.中国", -1 }, + { "shishi.中国", "shishi.中国", -1 }, + { "中国", NULL, SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS }, + /* Same as above, but punycoded. */ + { "xn--85x722f.com.cn", "xn--85x722f.com.cn", -1 }, + { "xn--85x722f.xn--55qx5d.cn", "xn--85x722f.xn--55qx5d.cn", -1 }, + { "www.xn--85x722f.xn--55qx5d.cn", "xn--85x722f.xn--55qx5d.cn", -1 }, + { "shishi.xn--55qx5d.cn", "shishi.xn--55qx5d.cn", -1 }, + { "xn--55qx5d.cn", NULL, SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS }, + { "xn--85x722f.xn--fiqs8s", "xn--85x722f.xn--fiqs8s", -1 }, + { "www.xn--85x722f.xn--fiqs8s", "xn--85x722f.xn--fiqs8s", -1 }, + { "shishi.xn--fiqs8s", "shishi.xn--fiqs8s", -1 }, + { "xn--fiqs8s", NULL, SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS }, + /* End of publicsuffix.org tests */ + + /* Let's just double-check this one... */ + { "co.uk", NULL, SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS }, + { "test.co.uk", "test.co.uk", -1 }, + { "www.test.co.uk", "test.co.uk", -1 }, + + /* Two levels of non-ASCII */ + { "våler.østfold.no", NULL, SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS }, + { "test.våler.østfold.no", "test.våler.østfold.no", -1 }, + { "www.test.våler.østfold.no", "test.våler.østfold.no", -1 }, + { "xn--vler-qoa.xn--stfold-9xa.no", NULL, SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS }, + { "test.xn--vler-qoa.xn--stfold-9xa.no", "test.xn--vler-qoa.xn--stfold-9xa.no", -1 }, + { "www.test.xn--vler-qoa.xn--stfold-9xa.no", "test.xn--vler-qoa.xn--stfold-9xa.no", -1 }, +}, + +/* Non Internet TLDs have NULL as expected result + */ +non_inet_tld_tests[] = { + /* Unlisted TLD.*/ + { "example", NULL }, + { "example.example", NULL }, + { "b.example.example", NULL }, + { "a.b.example.example", NULL }, + /* Listed, but non-Internet, TLD. */ + { "local", NULL }, + { "example.local", NULL }, + { "b.example.local", NULL }, + { "a.b.example.local", NULL } }; +static void +do_inet_tests (void) +{ + int i; + + for (i = 0; i < G_N_ELEMENTS (tld_tests); i++) { + GError *error = NULL; + gboolean is_public; + const char *base_domain; + + debug_printf (1, "Testing %s\n", tld_tests[i].hostname); + + is_public = soup_tld_domain_is_public_suffix (tld_tests[i].hostname); + base_domain = soup_tld_get_base_domain (tld_tests[i].hostname, &error); + + if (base_domain) { + g_assert_no_error (error); + g_assert_false (is_public); + g_assert_cmpstr (base_domain, ==, tld_tests[i].result); + } else { + g_assert_null (tld_tests[i].result); + g_assert_error (error, SOUP_TLD_ERROR, tld_tests[i].error); + g_clear_error (&error); + } + } +} + +static void +do_non_inet_tests (void) +{ + int i; + + g_test_bug ("679230"); + g_test_bug ("681085"); + + for (i = 0; i < G_N_ELEMENTS (non_inet_tld_tests); i++) { + gboolean is_public; + const char *base_domain; + + debug_printf (1, "Testing %s\n", non_inet_tld_tests[i].hostname); + + is_public = soup_tld_domain_is_public_suffix (non_inet_tld_tests[i].hostname); + base_domain = soup_tld_get_base_domain (non_inet_tld_tests[i].hostname, NULL); + + g_assert_false (is_public); + g_assert_null (base_domain); + } +} + int main (int argc, char **argv) { - int i; + int ret; test_init (argc, argv, NULL); - errors = 0; - for (i = 0; i < G_N_ELEMENTS (tld_tests); ++i) { - gboolean is_public = soup_tld_domain_is_public_suffix (tld_tests[i].hostname); - const char *base_domain = soup_tld_get_base_domain (tld_tests[i].hostname, NULL); - - debug_printf (1, "Testing %s: ", tld_tests[i].hostname); - if (tld_tests[i].result) { - /* Public domains have NULL expected results. */ - if (is_public || g_strcmp0 (tld_tests[i].result, base_domain)) { - debug_printf (1, "ERROR: %s got %s (%s expected)\n", - tld_tests[i].hostname, base_domain, tld_tests[i].result); - ++errors; - } else - debug_printf (1, "OK\n"); - } else { - /* If there is no expected result then either the domain is public or - * the hostname invalid (for example starts with a leading dot). - */ - if (!is_public && base_domain) { - debug_printf (1, "ERROR: public domain %s got %s (none expected)\n", - tld_tests[i].hostname, base_domain); - ++errors; - } else - debug_printf (1, "OK\n"); - } - } + g_test_add_func ("/tld/inet", do_inet_tests); + g_test_add_func ("/tld/non-inet", do_non_inet_tests); + + ret = g_test_run (); test_cleanup (); - return errors != 0; + return ret; } diff --git a/tests/uri-parsing.c b/tests/uri-parsing.c index 08274a28..d56b655f 100644 --- a/tests/uri-parsing.c +++ b/tests/uri-parsing.c @@ -3,123 +3,155 @@ #include "test-utils.h" static struct { - const char *uri_string, *result; + const char *uri_string, *result, *bugref; const SoupURI bits; } abs_tests[] = { - { "foo:", "foo:", + { "foo:", "foo:", NULL, { "foo", NULL, NULL, NULL, 0, "", NULL, NULL } }, - { "file:/dev/null", "file:/dev/null", + { "file:/dev/null", "file:/dev/null", NULL, { "file", NULL, NULL, NULL, 0, "/dev/null", NULL, NULL } }, - { "file:///dev/null", "file:///dev/null", + { "file:///dev/null", "file:///dev/null", NULL, { "file", NULL, NULL, "", 0, "/dev/null", NULL, NULL } }, - { "ftp://user@host/path", "ftp://user@host/path", + { "ftp://user@host/path", "ftp://user@host/path", NULL, { "ftp", "user", NULL, "host", 21, "/path", NULL, NULL } }, - { "ftp://user@host:9999/path", "ftp://user@host:9999/path", + { "ftp://user@host:9999/path", "ftp://user@host:9999/path", NULL, { "ftp", "user", NULL, "host", 9999, "/path", NULL, NULL } }, - { "ftp://user:password@host/path", "ftp://user@host/path", + { "ftp://user:password@host/path", "ftp://user@host/path", NULL, { "ftp", "user", "password", "host", 21, "/path", NULL, NULL } }, - { "ftp://user:password@host:9999/path", "ftp://user@host:9999/path", + { "ftp://user:password@host:9999/path", "ftp://user@host:9999/path", NULL, { "ftp", "user", "password", "host", 9999, "/path", NULL, NULL } }, - { "ftp://user:password@host", "ftp://user@host", + { "ftp://user:password@host", "ftp://user@host", NULL, { "ftp", "user", "password", "host", 21, "", NULL, NULL } }, - { "http://us%65r@host", "http://user@host/", + { "http://us%65r@host", "http://user@host/", NULL, { "http", "user", NULL, "host", 80, "/", NULL, NULL } }, - { "http://us%40r@host", "http://us%40r@host/", + { "http://us%40r@host", "http://us%40r@host/", NULL, { "http", "us\x40r", NULL, "host", 80, "/", NULL, NULL } }, - { "http://us%3ar@host", "http://us%3Ar@host/", + { "http://us%3ar@host", "http://us%3Ar@host/", NULL, { "http", "us\x3ar", NULL, "host", 80, "/", NULL, NULL } }, - { "http://us%2fr@host", "http://us%2Fr@host/", + { "http://us%2fr@host", "http://us%2Fr@host/", NULL, { "http", "us\x2fr", NULL, "host", 80, "/", NULL, NULL } }, - { "http://us%3fr@host", "http://us%3Fr@host/", + { "http://us%3fr@host", "http://us%3Fr@host/", NULL, { "http", "us\x3fr", NULL, "host", 80, "/", NULL, NULL } }, - { "http://host?query", "http://host/?query", + { "http://host?query", "http://host/?query", NULL, { "http", NULL, NULL, "host", 80, "/", "query", NULL } }, { "http://host/path?query=http%3A%2F%2Fhost%2Fpath%3Fchildparam%3Dchildvalue¶m=value", - "http://host/path?query=http%3A%2F%2Fhost%2Fpath%3Fchildparam%3Dchildvalue¶m=value", + "http://host/path?query=http%3A%2F%2Fhost%2Fpath%3Fchildparam%3Dchildvalue¶m=value", NULL, { "http", NULL, NULL, "host", 80, "/path", "query=http%3A%2F%2Fhost%2Fpath%3Fchildparam%3Dchildvalue¶m=value", NULL } }, { "http://control-chars/%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F%7F", - "http://control-chars/%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F%7F", + "http://control-chars/%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F%7F", NULL, { "http", NULL, NULL, "control-chars", 80, "/%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F%7F", NULL, NULL } }, { "http://space/%20", - "http://space/%20", + "http://space/%20", NULL, { "http", NULL, NULL, "space", 80, "/%20", NULL, NULL } }, { "http://delims/%3C%3E%23%25%22", - "http://delims/%3C%3E%23%25%22", + "http://delims/%3C%3E%23%25%22", NULL, { "http", NULL, NULL, "delims", 80, "/%3C%3E%23%25%22", NULL, NULL } }, { "http://unwise-chars/%7B%7D%7C%5C%5E%5B%5D%60", - "http://unwise-chars/%7B%7D%7C%5C%5E%5B%5D%60", + "http://unwise-chars/%7B%7D%7C%5C%5E%5B%5D%60", NULL, { "http", NULL, NULL, "unwise-chars", 80, "/%7B%7D%7C%5C%5E%5B%5D%60", NULL, NULL } }, /* From RFC 2732 */ { "http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:80/index.html", - "http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]/index.html", + "http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]/index.html", NULL, { "http", NULL, NULL, "FEDC:BA98:7654:3210:FEDC:BA98:7654:3210", 80, "/index.html", NULL, NULL } }, { "http://[1080:0:0:0:8:800:200C:417A]/index.html", - "http://[1080:0:0:0:8:800:200C:417A]/index.html", + "http://[1080:0:0:0:8:800:200C:417A]/index.html", NULL, { "http", NULL, NULL, "1080:0:0:0:8:800:200C:417A", 80, "/index.html", NULL, NULL } }, { "http://[3ffe:2a00:100:7031::1]", - "http://[3ffe:2a00:100:7031::1]/", + "http://[3ffe:2a00:100:7031::1]/", NULL, { "http", NULL, NULL, "3ffe:2a00:100:7031::1", 80, "/", NULL, NULL } }, { "http://[1080::8:800:200C:417A]/foo", - "http://[1080::8:800:200C:417A]/foo", + "http://[1080::8:800:200C:417A]/foo", NULL, { "http", NULL, NULL, "1080::8:800:200C:417A", 80, "/foo", NULL, NULL } }, { "http://[::192.9.5.5]/ipng", - "http://[::192.9.5.5]/ipng", + "http://[::192.9.5.5]/ipng", NULL, { "http", NULL, NULL, "::192.9.5.5", 80, "/ipng", NULL, NULL } }, { "http://[::FFFF:129.144.52.38]:80/index.html", - "http://[::FFFF:129.144.52.38]/index.html", + "http://[::FFFF:129.144.52.38]/index.html", NULL, { "http", NULL, NULL, "::FFFF:129.144.52.38", 80, "/index.html", NULL, NULL } }, { "http://[2010:836B:4179::836B:4179]", - "http://[2010:836B:4179::836B:4179]/", + "http://[2010:836B:4179::836B:4179]/", NULL, { "http", NULL, NULL, "2010:836B:4179::836B:4179", 80, "/", NULL, NULL } }, /* Try to recover certain kinds of invalid URIs */ { "http://host/path with spaces", - "http://host/path%20with%20spaces", + "http://host/path%20with%20spaces", "566530", { "http", NULL, NULL, "host", 80, "/path%20with%20spaces", NULL, NULL } }, - { " http://host/path", "http://host/path", + { " http://host/path", "http://host/path", "594405", { "http", NULL, NULL, "host", 80, "/path", NULL, NULL } }, - { "http://host/path ", "http://host/path", + { "http://host/path ", "http://host/path", "594405", { "http", NULL, NULL, "host", 80, "/path", NULL, NULL } }, - { "http://host ", "http://host/", + { "http://host ", "http://host/", "594405", { "http", NULL, NULL, "host", 80, "/", NULL, NULL } }, - { "http://host:999 ", "http://host:999/", + { "http://host:999 ", "http://host:999/", "594405", { "http", NULL, NULL, "host", 999, "/", NULL, NULL } }, - { "http://host/pa\nth", "http://host/path", + { "http://host/pa\nth", "http://host/path", "594405", { "http", NULL, NULL, "host", 80, "/path", NULL, NULL } }, - { "http:\r\n//host/path", "http://host/path", + { "http:\r\n//host/path", "http://host/path", "594405", { "http", NULL, NULL, "host", 80, "/path", NULL, NULL } }, - { "http://\thost/path", "http://host/path", + { "http://\thost/path", "http://host/path", "594405", { "http", NULL, NULL, "host", 80, "/path", NULL, NULL } }, - /* Bug 594405; 0-length is different from not-present */ - { "http://host/path?", "http://host/path?", + /* 0-length is different from not-present */ + { "http://host/path?", "http://host/path?", "594405", { "http", NULL, NULL, "host", 80, "/path", "", NULL } }, - { "http://host/path#", "http://host/path#", + { "http://host/path#", "http://host/path#", "594405", { "http", NULL, NULL, "host", 80, "/path", NULL, "" } }, - /* Bug 590524; ignore badly-%-encoding */ - { "http://host/path%", "http://host/path%", + /* ignore bad %-encoding */ + { "http://host/path%", "http://host/path%", "590524", { "http", NULL, NULL, "host", 80, "/path%", NULL, NULL } }, - { "http://h%ost/path", "http://h%25ost/path", + { "http://h%ost/path", "http://h%25ost/path", "590524", { "http", NULL, NULL, "h%ost", 80, "/path", NULL, NULL } }, - { "http://host/path%%", "http://host/path%%", + { "http://host/path%%", "http://host/path%%", "590524", { "http", NULL, NULL, "host", 80, "/path%%", NULL, NULL } }, - { "http://host/path%%%", "http://host/path%%%", + { "http://host/path%%%", "http://host/path%%%", "590524", { "http", NULL, NULL, "host", 80, "/path%%%", NULL, NULL } }, - { "http://host/path%/x/", "http://host/path%/x/", + { "http://host/path%/x/", "http://host/path%/x/", "590524", { "http", NULL, NULL, "host", 80, "/path%/x/", NULL, NULL } }, - { "http://host/path%0x/", "http://host/path%0x/", + { "http://host/path%0x/", "http://host/path%0x/", "590524", { "http", NULL, NULL, "host", 80, "/path%0x/", NULL, NULL } }, - { "http://host/path%ax", "http://host/path%ax", + { "http://host/path%ax", "http://host/path%ax", "590524", { "http", NULL, NULL, "host", 80, "/path%ax", NULL, NULL } }, - /* Bug 662806; %-encode non-ASCII characters */ - { "http://host/p\xc3\xa4th/", "http://host/p%C3%A4th/", + /* %-encode non-ASCII characters */ + { "http://host/p\xc3\xa4th/", "http://host/p%C3%A4th/", "662806", { "http", NULL, NULL, "host", 80, "/p%C3%A4th/", NULL, NULL } }, - { "HTTP:////////////////", "http:////////////////", + { "HTTP:////////////////", "http:////////////////", "667637", { "http", NULL, NULL, "", 80, "//////////////", NULL, NULL } }, + + { "http://@host", "http://@host/", NULL, + { "http", "", NULL, "host", 80, "/", NULL, NULL } }, + { "http://:@host", "http://@host/", NULL, + { "http", "", "", "host", 80, "/", NULL, NULL } }, + + { "http://host/keep%00nuls", "http://host/keep%00nuls", NULL, + { "http", NULL, NULL, "host", 80, "/keep%00nuls", NULL, NULL } }, + + /* scheme parsing */ + { "foo0://host/path", "foo0://host/path", "703776", + { "foo0", NULL, NULL, "host", 0, "/path", NULL, NULL } }, + { "f0.o://host/path", "f0.o://host/path", "703776", + { "f0.o", NULL, NULL, "host", 0, "/path", NULL, NULL } }, + { "http++://host/path", "http++://host/path", "703776", + { "http++", NULL, NULL, "host", 0, "/path", NULL, NULL } }, + { "http-ish://host/path", "http-ish://host/path", "703776", + { "http-ish", NULL, NULL, "host", 0, "/path", NULL, NULL } }, + { "99http://host/path", NULL, "703776", + { NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL } }, + { ".http://host/path", NULL, "703776", + { NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL } }, + { "+http://host/path", NULL, "703776", + { NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL } }, + + /* IPv6 scope ID parsing (both correct and incorrect) */ + { "http://[fe80::dead:beef%em1]/", "http://[fe80::dead:beef%25em1]/", NULL, + { "http", NULL, NULL, "fe80::dead:beef%em1", 80, "/", NULL, NULL } }, + { "http://[fe80::dead:beef%25em1]/", "http://[fe80::dead:beef%25em1]/", NULL, + { "http", NULL, NULL, "fe80::dead:beef%em1", 80, "/", NULL, NULL } }, + { "http://[fe80::dead:beef%10]/", "http://[fe80::dead:beef%2510]/", NULL, + { "http", NULL, NULL, "fe80::dead:beef%10", 80, "/", NULL, NULL } } }; static int num_abs_tests = G_N_ELEMENTS(abs_tests); @@ -223,50 +255,18 @@ static struct { static int num_rel_tests = G_N_ELEMENTS(rel_tests); static struct { - const char *one, *two; + const char *one, *two, *bugref; } eq_tests[] = { - { "example://a/b/c/%7Bfoo%7D", "eXAMPLE://a/./b/../b/%63/%7Bfoo%7D" }, - { "http://example.com", "http://example.com/" }, + { "example://a/b/c/%7Bfoo%7D", "eXAMPLE://a/./b/../b/%63/%7Bfoo%7D", "628728" }, + { "http://example.com", "http://example.com/", NULL }, /* From RFC 2616 */ - { "http://abc.com:80/~smith/home.html", "http://abc.com:80/~smith/home.html" }, - { "http://abc.com:80/~smith/home.html", "http://ABC.com/%7Esmith/home.html" }, - { "http://abc.com:80/~smith/home.html", "http://ABC.com:/%7esmith/home.html" }, + { "http://abc.com:80/~smith/home.html", "http://abc.com:80/~smith/home.html", NULL }, + { "http://abc.com:80/~smith/home.html", "http://ABC.com/%7Esmith/home.html", NULL }, + { "http://abc.com:80/~smith/home.html", "http://ABC.com:/%7esmith/home.html", NULL }, }; static int num_eq_tests = G_N_ELEMENTS(eq_tests); -#define test_cmpstr(a, b) _test_cmpstr (#a, #b, a, b) - -static gboolean -_test_cmpstr (const char *got_desc, - const char *exp_desc, - const char *got, - const char *expected) -{ - if (got == expected) - return TRUE; - - if (got == NULL) { - debug_printf (1, "ERR\n %s = NULL, expected %s = \"%s\"\n", - got_desc, exp_desc, expected); - return FALSE; - } - - if (expected == NULL) { - debug_printf (1, "ERR\n %s = \"%s\", expected %s = NULL\n", - got_desc, got, exp_desc); - return FALSE; - } - - if (strcmp (got, expected) != 0) { - debug_printf (1, "ERR\n %s = \"%s\", expected %s = \"%s\"\n", - got_desc, got, exp_desc, expected); - return FALSE; - } - - return TRUE; -} - -static gboolean +static void do_uri (SoupURI *base_uri, const char *base_str, const char *in_uri, const char *out_uri, const SoupURI *bits) @@ -275,76 +275,94 @@ do_uri (SoupURI *base_uri, const char *base_str, char *uri_string; if (base_uri) { - debug_printf (1, "<%s> + <%s> = <%s>? ", base_str, in_uri, + debug_printf (1, "<%s> + <%s> = <%s>\n", base_str, in_uri, out_uri ? out_uri : "ERR"); uri = soup_uri_new_with_base (base_uri, in_uri); } else { - debug_printf (1, "<%s> => <%s>? ", in_uri, + debug_printf (1, "<%s> => <%s>\n", in_uri, out_uri ? out_uri : "ERR"); uri = soup_uri_new (in_uri); } if (!uri) { - if (out_uri) { - debug_printf (1, "ERR\n Could not parse %s\n", in_uri); - return FALSE; - } else { - debug_printf (1, "OK\n"); - return TRUE; - } + g_assert_null (out_uri); + return; } if (bits != NULL) { - gboolean failed = FALSE; - - if (!test_cmpstr (uri->scheme, bits->scheme)) - failed = TRUE; + g_assert_cmpstr (uri->scheme, ==, bits->scheme); + g_assert_cmpstr (uri->user, ==, bits->user); + g_assert_cmpstr (uri->password, ==, bits->password); + g_assert_cmpstr (uri->host, ==, bits->host); + g_assert_cmpuint (uri->port, ==, bits->port); + g_assert_cmpstr (uri->path, ==, bits->path); + g_assert_cmpstr (uri->query, ==, bits->query); + g_assert_cmpstr (uri->fragment, ==, bits->fragment); + } - if (!test_cmpstr (uri->user, bits->user)) - failed = TRUE; + uri_string = soup_uri_to_string (uri, FALSE); + soup_uri_free (uri); - if (!test_cmpstr (uri->password, bits->password)) - failed = TRUE; + g_assert_cmpstr (uri_string, ==, out_uri); + g_free (uri_string); +} - if (!test_cmpstr (uri->host, bits->host)) - failed = TRUE; +static void +do_absolute_uri_tests (void) +{ + int i; - if (uri->port != bits->port) { - debug_printf (1, "ERR\n port was %u, expected %u\n", - uri->port, bits->port); - failed = TRUE; - } + for (i = 0; i < num_abs_tests; i++) { + if (abs_tests[i].bugref) + g_test_bug (abs_tests[i].bugref); + do_uri (NULL, NULL, abs_tests[i].uri_string, + abs_tests[i].result, &abs_tests[i].bits); + } +} - if (!test_cmpstr (uri->path, bits->path)) - failed = TRUE; +static void +do_relative_uri_tests (void) +{ + SoupURI *base_uri; + char *uri_string; + int i; - if (!test_cmpstr (uri->query, bits->query)) - failed = TRUE; + base_uri = soup_uri_new (base); + if (!base_uri) { + g_printerr ("Could not parse %s!\n", base); + exit (1); + } - if (!test_cmpstr (uri->fragment, bits->fragment)) - failed = TRUE; + uri_string = soup_uri_to_string (base_uri, FALSE); + g_assert_cmpstr (uri_string, ==, base); + g_free (uri_string); - if (failed) - return FALSE; + for (i = 0; i < num_rel_tests; i++) { + do_uri (base_uri, base, rel_tests[i].uri_string, + rel_tests[i].result, &rel_tests[i].bits); } + soup_uri_free (base_uri); +} - uri_string = soup_uri_to_string (uri, FALSE); - soup_uri_free (uri); +static void +do_equality_tests (void) +{ + SoupURI *uri1, *uri2; + int i; - if (!out_uri) { - debug_printf (1, "ERR\n Got %s\n", uri_string); - return FALSE; - } + for (i = 0; i < num_eq_tests; i++) { + if (eq_tests[i].bugref) + g_test_bug (eq_tests[i].bugref); - if (strcmp (uri_string, out_uri) != 0) { - debug_printf (1, "NO\n Unparses to <%s>\n", uri_string); - g_free (uri_string); - return FALSE; - } - g_free (uri_string); + uri1 = soup_uri_new (eq_tests[i].one); + uri2 = soup_uri_new (eq_tests[i].two); - debug_printf (1, "OK\n"); - return TRUE; + debug_printf (1, "<%s> == <%s>\n", eq_tests[i].one, eq_tests[i].two); + g_assert_true (soup_uri_equal (uri1, uri2)); + + soup_uri_free (uri1); + soup_uri_free (uri2); + } } static void @@ -353,146 +371,83 @@ do_soup_uri_null_tests (void) SoupURI *uri, *uri2; char *uri_string; - debug_printf (1, "\nsoup_uri_new (NULL)\n"); + g_test_bug ("667637"); + g_test_bug ("670431"); + uri = soup_uri_new (NULL); - if (SOUP_URI_IS_VALID (uri) || SOUP_URI_VALID_FOR_HTTP (uri)) { - debug_printf (1, " ERROR: soup_uri_new(NULL) returns valid URI?\n"); - errors++; - } + g_assert_false (SOUP_URI_IS_VALID (uri)); + g_assert_false (SOUP_URI_VALID_FOR_HTTP (uri)); /* This implicitly also verifies that none of these methods g_warn */ - if (soup_uri_get_scheme (uri) || - soup_uri_get_user (uri) || - soup_uri_get_password (uri) || - soup_uri_get_host (uri) || - soup_uri_get_port (uri) || - soup_uri_get_path (uri) || - soup_uri_get_query (uri) || - soup_uri_get_fragment (uri)) { - debug_printf (1, " ERROR: soup_uri_new(NULL) returns non-empty URI?\n"); - errors++; - } - - expect_warning = TRUE; + g_assert_null (soup_uri_get_scheme (uri)); + g_assert_null (soup_uri_get_user (uri)); + g_assert_null (soup_uri_get_password (uri)); + g_assert_null (soup_uri_get_host (uri)); + g_assert_cmpint (soup_uri_get_port (uri), ==, 0); + g_assert_null (soup_uri_get_path (uri)); + g_assert_null (soup_uri_get_query (uri)); + g_assert_null (soup_uri_get_fragment (uri)); + + g_test_expect_message ("libsoup", G_LOG_LEVEL_CRITICAL, + "*base == NULL*"); uri2 = soup_uri_new_with_base (uri, "/path"); - if (uri2 || expect_warning) { - debug_printf (1, " ERROR: soup_uri_new_with_base didn't fail on NULL URI?\n"); - errors++; - expect_warning = FALSE; - } + g_test_assert_expected_messages (); + g_assert_null (uri2); - expect_warning = TRUE; + g_test_expect_message ("libsoup", G_LOG_LEVEL_WARNING, + "*SOUP_URI_IS_VALID*"); uri_string = soup_uri_to_string (uri, FALSE); - if (expect_warning) { - debug_printf (1, " ERROR: soup_uri_to_string didn't fail on NULL URI?\n"); - errors++; - expect_warning = FALSE; - } else if (*uri_string) { - debug_printf (1, " ERROR: soup_uri_to_string on NULL URI returned '%s'\n", - uri_string); - errors++; - } + g_test_assert_expected_messages (); + g_assert_cmpstr (uri_string, ==, ""); g_free (uri_string); soup_uri_set_scheme (uri, SOUP_URI_SCHEME_HTTP); - if (SOUP_URI_IS_VALID (uri) || SOUP_URI_VALID_FOR_HTTP (uri)) { - debug_printf (1, " ERROR: setting scheme on NULL URI makes it valid?\n"); - errors++; - } + g_assert_false (SOUP_URI_IS_VALID (uri)); + g_assert_false (SOUP_URI_VALID_FOR_HTTP (uri)); - expect_warning = TRUE; + g_test_expect_message ("libsoup", G_LOG_LEVEL_WARNING, + "*SOUP_URI_IS_VALID*"); uri_string = soup_uri_to_string (uri, FALSE); - if (expect_warning) { - debug_printf (1, " ERROR: soup_uri_to_string didn't fail on scheme-only URI?\n"); - errors++; - expect_warning = FALSE; - } else if (strcmp (uri_string, "http:") != 0) { - debug_printf (1, " ERROR: soup_uri_to_string returned '%s' instead of 'http:'\n", - uri_string); - errors++; - } + g_test_assert_expected_messages (); + g_assert_cmpstr (uri_string, ==, "http:"); g_free (uri_string); soup_uri_set_host (uri, "localhost"); - if (SOUP_URI_IS_VALID (uri)) { - debug_printf (1, " ERROR: setting scheme+host on NULL URI makes it valid?\n"); - errors++; - } - if (SOUP_URI_VALID_FOR_HTTP (uri)) { - debug_printf (1, " ERROR: setting scheme+host on NULL URI makes it valid for http?\n"); - errors++; - } + g_assert_false (SOUP_URI_IS_VALID (uri)); + g_assert_false (SOUP_URI_VALID_FOR_HTTP (uri)); - expect_warning = TRUE; + g_test_expect_message ("libsoup", G_LOG_LEVEL_WARNING, + "*SOUP_URI_IS_VALID*"); uri_string = soup_uri_to_string (uri, FALSE); - if (expect_warning) { - debug_printf (1, " ERROR: soup_uri_to_string didn't fail on scheme+host URI?\n"); - errors++; - expect_warning = FALSE; - } else if (strcmp (uri_string, "http://localhost/") != 0) { - debug_printf (1, " ERROR: soup_uri_to_string with NULL path returned '%s' instead of 'http://localhost/'\n", - uri_string); - errors++; - } + g_test_assert_expected_messages (); + g_assert_cmpstr (uri_string, ==, "http://localhost/"); g_free (uri_string); - expect_warning = TRUE; + g_test_expect_message ("libsoup", G_LOG_LEVEL_WARNING, + "*SOUP_URI_IS_VALID*"); uri2 = soup_uri_new_with_base (uri, "/path"); - if (expect_warning) { - debug_printf (1, " ERROR: soup_uri_new_with_base didn't warn on NULL+scheme URI?\n"); - errors++; - expect_warning = FALSE; - } else if (!uri2) { - debug_printf (1, " ERROR: soup_uri_new_with_base didn't fix path on NULL+scheme URI\n"); - errors++; - } + g_test_assert_expected_messages (); + g_assert_true (uri2 != NULL); if (uri2) { uri_string = soup_uri_to_string (uri2, FALSE); - if (!uri_string) { - debug_printf (1, " ERROR: soup_uri_to_string failed on uri2?\n"); - errors++; - } else if (strcmp (uri_string, "http://localhost/path") != 0) { - debug_printf (1, " ERROR: soup_uri_to_string returned '%s' instead of 'http://localhost/path'\n", - uri_string); - errors++; - } + g_assert_cmpstr (uri_string, ==, "http://localhost/path"); g_free (uri_string); soup_uri_free (uri2); } - expect_warning = TRUE; + g_test_expect_message ("libsoup", G_LOG_LEVEL_WARNING, + "*path != NULL*"); soup_uri_set_path (uri, NULL); - if (expect_warning) { - debug_printf (1, " ERROR: setting path to NULL doesn't warn\n"); - errors++; - expect_warning = FALSE; - } - if (!uri->path || *uri->path) { - debug_printf (1, " ERROR: setting path to NULL != \"\"\n"); - errors++; - soup_uri_set_path (uri, ""); - } + g_test_assert_expected_messages (); + g_assert_cmpstr (uri->path, ==, ""); uri_string = soup_uri_to_string (uri, FALSE); - if (!uri_string) { - debug_printf (1, " ERROR: soup_uri_to_string failed on complete URI?\n"); - errors++; - } else if (strcmp (uri_string, "http://localhost/") != 0) { - debug_printf (1, " ERROR: soup_uri_to_string with empty path returned '%s' instead of 'http://localhost/'\n", - uri_string); - errors++; - } + g_assert_cmpstr (uri_string, ==, "http://localhost/"); g_free (uri_string); - if (!SOUP_URI_IS_VALID (uri)) { - debug_printf (1, " ERROR: setting scheme+path on NULL URI doesn't make it valid?\n"); - errors++; - } - if (!SOUP_URI_VALID_FOR_HTTP (uri)) { - debug_printf (1, " ERROR: setting scheme+host+path on NULL URI doesn't make it valid for http?\n"); - errors++; - } + g_assert_true (SOUP_URI_IS_VALID (uri)); + g_assert_true (SOUP_URI_VALID_FOR_HTTP (uri)); soup_uri_free (uri); } @@ -517,29 +472,23 @@ do_normalization_tests (void) char *normalized; int i; - debug_printf (1, "\nsoup_uri_normalize\n"); + g_test_bug ("680018"); for (i = 0; i < num_normalization_tests; i++) { if (normalization_tests[i].unescape_extra) { - debug_printf (1, "<%s> unescaping <%s> => <%s>: ", + debug_printf (1, "<%s> unescaping <%s> => <%s>\n", normalization_tests[i].uri_string, normalization_tests[i].unescape_extra, normalization_tests[i].result); } else { - debug_printf (1, "<%s> => <%s>: ", + debug_printf (1, "<%s> => <%s>\n", normalization_tests[i].uri_string, normalization_tests[i].result); } normalized = soup_uri_normalize (normalization_tests[i].uri_string, normalization_tests[i].unescape_extra); - - if (!strcmp (normalized, normalization_tests[i].result)) - debug_printf (1, "OK\n"); - else { - debug_printf (1, "NO, got <%s>\n", normalized); - errors++; - } + g_assert_cmpstr (normalized, ==, normalization_tests[i].result); g_free (normalized); } } @@ -547,62 +496,18 @@ do_normalization_tests (void) int main (int argc, char **argv) { - SoupURI *base_uri, *uri1, *uri2; - char *uri_string; - int i; + int ret; test_init (argc, argv, NULL); - debug_printf (1, "Absolute URI parsing\n"); - for (i = 0; i < num_abs_tests; i++) { - if (!do_uri (NULL, NULL, abs_tests[i].uri_string, - abs_tests[i].result, &abs_tests[i].bits)) - errors++; - } - - debug_printf (1, "\nRelative URI parsing\n"); - base_uri = soup_uri_new (base); - if (!base_uri) { - g_printerr ("Could not parse %s!\n", base); - exit (1); - } - - uri_string = soup_uri_to_string (base_uri, FALSE); - if (strcmp (uri_string, base) != 0) { - g_printerr ("URI <%s> unparses to <%s>\n", - base, uri_string); - errors++; - } - g_free (uri_string); - - for (i = 0; i < num_rel_tests; i++) { - if (!do_uri (base_uri, base, rel_tests[i].uri_string, - rel_tests[i].result, &rel_tests[i].bits)) - errors++; - } - soup_uri_free (base_uri); - - debug_printf (1, "\nURI equality testing\n"); - for (i = 0; i < num_eq_tests; i++) { - uri1 = soup_uri_new (eq_tests[i].one); - uri2 = soup_uri_new (eq_tests[i].two); - debug_printf (1, "<%s> == <%s>? ", eq_tests[i].one, eq_tests[i].two); - if (soup_uri_equal (uri1, uri2)) - debug_printf (1, "OK\n"); - else { - debug_printf (1, "NO\n"); - debug_printf (1, "%s : %s : %s\n%s : %s : %s\n", - uri1->scheme, uri1->host, uri1->path, - uri2->scheme, uri2->host, uri2->path); - errors++; - } - soup_uri_free (uri1); - soup_uri_free (uri2); - } + g_test_add_func ("/uri/absolute", do_absolute_uri_tests); + g_test_add_func ("/uri/relative", do_relative_uri_tests); + g_test_add_func ("/uri/equality", do_equality_tests); + g_test_add_func ("/uri/null", do_soup_uri_null_tests); + g_test_add_func ("/uri/normalization", do_normalization_tests); - do_soup_uri_null_tests (); - do_normalization_tests (); + ret = g_test_run (); test_cleanup (); - return errors != 0; + return ret; } diff --git a/tests/xmlrpc-server-test.c b/tests/xmlrpc-server-test.c index 8ad2187b..bfeb200a 100644 --- a/tests/xmlrpc-server-test.c +++ b/tests/xmlrpc-server-test.c @@ -5,12 +5,12 @@ #include "test-utils.h" +static char *uri; + #ifdef G_GNUC_BEGIN_IGNORE_DEPRECATIONS G_GNUC_BEGIN_IGNORE_DEPRECATIONS #endif -GMainLoop *loop; - static void type_error (SoupMessage *msg, GType expected, GValueArray *params, int bad_value) { @@ -180,7 +180,17 @@ do_echo (SoupMessage *msg, GValueArray *params) soup_xmlrpc_set_response (msg, G_TYPE_VALUE_ARRAY, out); g_value_array_free (out); +} + +static void +do_ping (SoupMessage *msg, GValueArray *params) +{ + if (params->n_values) { + args_error (msg, params, 0); + return; + } + soup_xmlrpc_set_response (msg, G_TYPE_STRING, "pong"); } static void @@ -216,6 +226,8 @@ server_callback (SoupServer *server, SoupMessage *msg, 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_set_fault (msg, SOUP_XMLRPC_FAULT_SERVER_ERROR_REQUESTED_METHOD_NOT_FOUND, "Unknown method %s", method_name); @@ -225,73 +237,64 @@ server_callback (SoupServer *server, SoupMessage *msg, g_value_array_free (params); } -static void -xmlrpc_test_exited (GPid pid, int status, gpointer data) -{ - errors = WIFEXITED (status) ? WEXITSTATUS (status) : 1; - g_main_loop_quit (loop); -} - static gboolean -xmlrpc_test_print (GIOChannel *io, GIOCondition cond, gpointer data) +run_xmlrpc_test (char **argv, + char **stdout_out, + char **stderr_out, + GError **error) { - char *line; - gsize len; - GIOStatus status; + gboolean ok; + int status; - if (!(cond & G_IO_IN)) - return FALSE; + 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]); - status = g_io_channel_read_line (io, &line, &len, NULL, NULL); - if (status == G_IO_STATUS_NORMAL) { - /* Don't print the exit status, just the debug stuff */ - if (strncmp (line, "xmlrpc-test:", strlen ("xmlrpc-test:")) != 0) - g_print ("%s", line); - g_free (line); - return TRUE; - } else if (status == G_IO_STATUS_AGAIN) - return TRUE; - else + if (!ok) return FALSE; + + return g_spawn_check_exit_status (status, error); } static void -do_xmlrpc_tests (SoupURI *uri) +do_one_xmlrpc_test (gconstpointer data) { - char *argv[8]; - int arg, out; - gboolean ok; - GPid pid; + const char *path = data; + char *argv[12]; + char *stdout_out, *stderr_out; GError *error = NULL; - GIOChannel *child_out; + int arg; - argv[0] = "./xmlrpc-test"; - argv[1] = "-s"; - argv[2] = "-u"; - argv[3] = soup_uri_to_string (uri, FALSE); + 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 + 4] = "-d"; - argv[arg + 4] = NULL; - - ok = g_spawn_async_with_pipes (NULL, argv, NULL, - G_SPAWN_DO_NOT_REAP_CHILD, - NULL, NULL, &pid, - NULL, &out, NULL, - &error); - g_free (argv[3]); - - if (!ok) { - g_print ("Could not run xmlrpc-test: %s\n", error->message); - errors++; - return; + 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); } - g_child_watch_add (pid, xmlrpc_test_exited, NULL); - child_out = g_io_channel_unix_new (out); - g_io_add_watch (child_out, G_IO_IN | G_IO_ERR | G_IO_HUP, - xmlrpc_test_print, NULL); - g_io_channel_unref (child_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; @@ -307,29 +310,61 @@ int main (int argc, char **argv) { SoupServer *server; - SoupURI *uri; + int ret; test_init (argc, argv, no_test_entry); - server = soup_test_server_new (FALSE); + server = soup_test_server_new (run_tests); soup_server_add_handler (server, "/xmlrpc-server.php", server_callback, NULL, NULL); - - loop = g_main_loop_new (NULL, TRUE); + uri = g_strdup_printf ("http://127.0.0.1:%u/xmlrpc-server.php", + soup_server_get_port (server)); if (run_tests) { - uri = soup_uri_new ("http://127.0.0.1/xmlrpc-server.php"); - soup_uri_set_port (uri, soup_server_get_port (server)); - do_xmlrpc_tests (uri); - soup_uri_free (uri); - } else + 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)); - g_main_loop_run (loop); - g_main_loop_unref (loop); + 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); + g_free (uri); if (run_tests) test_cleanup (); - return errors != 0; + return ret; } diff --git a/tests/xmlrpc-server.php b/tests/xmlrpc-server.php index 2e3dd381..66cb2be7 100644 --- a/tests/xmlrpc-server.php +++ b/tests/xmlrpc-server.php @@ -2,13 +2,13 @@ function paramfault () { - # xmlrpc-epi-php translates this into a real <fault> + # xmlrpc-php translates this into a real <fault> $fault["faultCode"] = -32602; $fault["faultString"] = "bad parameter"; return $fault; } -# We only check the params in sum(), because that's the one that +# We only check the params in sum(), because that is the one that # xmlrpc-test tests will fail if given bad args function sum ($method_name, $params, $app_data) @@ -69,8 +69,16 @@ function echo_ ($method_name, $params, $app_data) return $params[0]; } -# Work around xmlrpc-epi-php lossage; otherwise the datetime values -# we return will sometimes get a DST adjustment we don't want. +function ping ($method_name, $params, $app_data) +{ + if (count ($params) == 0) + return "pong"; + else + return paramfault (); +} + +# Work around xmlrpc-php lossage; otherwise the datetime values +# we return will sometimes get a DST adjustment we do not want. putenv ("TZ="); $xmlrpc_server = xmlrpc_server_create (); @@ -79,6 +87,7 @@ xmlrpc_server_register_method($xmlrpc_server, "countBools", "countBools"); xmlrpc_server_register_method($xmlrpc_server, "md5sum", "md5sum"); xmlrpc_server_register_method($xmlrpc_server, "dateChange", "dateChange"); xmlrpc_server_register_method($xmlrpc_server, "echo", "echo_"); +xmlrpc_server_register_method($xmlrpc_server, "ping", "ping"); $response = xmlrpc_server_call_method ($xmlrpc_server, implode("\r\n", file('php://input')), diff --git a/tests/xmlrpc-test.c b/tests/xmlrpc-test.c index ec67e4d1..f6b20b8f 100644 --- a/tests/xmlrpc-test.c +++ b/tests/xmlrpc-test.c @@ -14,6 +14,18 @@ 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", @@ -27,44 +39,26 @@ static const char *const value_type[] = { }; static gboolean -do_xmlrpc (const char *method, GValue *retval, ...) +send_xmlrpc (const char *body, GValue *retval) { SoupMessage *msg; - va_list args; - GValueArray *params; GError *err = NULL; - char *body; - - va_start (args, retval); - params = soup_value_array_from_args (args); - va_end (args); - - body = soup_xmlrpc_build_method_call (method, params->values, - params->n_values); - g_value_array_free (params); - if (!body) - return FALSE; msg = soup_message_new ("POST", uri); - soup_message_set_request (msg, "text/xml", SOUP_MEMORY_TAKE, + soup_message_set_request (msg, "text/xml", SOUP_MEMORY_COPY, body, strlen (body)); soup_session_send_message (session, msg); - if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) { - debug_printf (1, "ERROR: %d %s\n", msg->status_code, - msg->reason_phrase); - g_object_unref (msg); - return FALSE; - } + soup_test_assert_message_status (msg, SOUP_STATUS_OK); if (!soup_xmlrpc_parse_method_response (msg->response_body->data, msg->response_body->length, retval, &err)) { if (err) { - debug_printf (1, "FAULT: %d %s\n", err->code, err->message); + soup_test_assert (FALSE, "FAULT: %d %s\n", err->code, err->message); g_error_free (err); } else - debug_printf (1, "ERROR: could not parse response\n"); + soup_test_assert (FALSE, "ERROR: could not parse response\n"); g_object_unref (msg); return FALSE; } @@ -74,13 +68,36 @@ do_xmlrpc (const char *method, GValue *retval, ...) } static gboolean +do_xmlrpc (const char *method, GValue *retval, ...) +{ + va_list args; + GValueArray *params; + char *body; + gboolean ret; + + va_start (args, retval); + params = soup_value_array_from_args (args); + va_end (args); + + body = soup_xmlrpc_build_method_call (method, params->values, + params->n_values); + g_value_array_free (params); + if (!body) + return FALSE; + + ret = send_xmlrpc (body, retval); + g_free (body); + + return ret; +} + +static gboolean check_xmlrpc (GValue *value, GType type, ...) { va_list args; if (!G_VALUE_HOLDS (value, type)) { - debug_printf (1, "ERROR: could not parse response\n"); - g_value_unset (value); + g_assert_true (G_VALUE_HOLDS (value, type)); return FALSE; } @@ -90,7 +107,7 @@ check_xmlrpc (GValue *value, GType type, ...) return TRUE; } -static gboolean +static void test_sum (void) { GValueArray *ints; @@ -98,7 +115,9 @@ test_sum (void) GValue retval; gboolean ok; - debug_printf (1, "sum (array of int -> int): "); + SOUP_TEST_SKIP_IF_NO_XMLRPC_SERVER; + + debug_printf (2, "sum (array of int -> int): "); ints = g_value_array_new (10); for (i = sum = 0; i < 10; i++) { @@ -116,14 +135,13 @@ test_sum (void) g_value_array_free (ints); if (!ok) - return FALSE; + return; - debug_printf (2, "%d: ", result); - debug_printf (1, "%s\n", result == sum ? "OK!" : "WRONG!"); - return result == sum; + debug_printf (2, "%d\n", result); + g_assert_cmpint (result, ==, sum); } -static gboolean +static void test_countBools (void) { GValueArray *bools; @@ -133,7 +151,9 @@ test_countBools (void) gboolean val, ok; GHashTable *result; - debug_printf (1, "countBools (array of boolean -> struct of ints): "); + SOUP_TEST_SKIP_IF_NO_XMLRPC_SERVER; + + debug_printf (2, "countBools (array of boolean -> struct of ints): "); bools = g_value_array_new (10); for (i = trues = falses = 0; i < 10; i++) { @@ -153,25 +173,19 @@ test_countBools (void) check_xmlrpc (&retval, G_TYPE_HASH_TABLE, &result)); g_value_array_free (bools); if (!ok) - return FALSE; + return; + + g_assert_true (soup_value_hash_lookup (result, "true", G_TYPE_INT, &ret_trues)); + g_assert_true (soup_value_hash_lookup (result, "false", G_TYPE_INT, &ret_falses)); - if (!soup_value_hash_lookup (result, "true", G_TYPE_INT, &ret_trues)) { - debug_printf (1, "NO 'true' value in response\n"); - return FALSE; - } - if (!soup_value_hash_lookup (result, "false", G_TYPE_INT, &ret_falses)) { - debug_printf (1, "NO 'false' value in response\n"); - return FALSE; - } g_hash_table_destroy (result); - debug_printf (2, "{ true: %d, false: %d } ", ret_trues, ret_falses); - ok = (trues == ret_trues) && (falses == ret_falses); - debug_printf (1, "%s\n", ok ? "OK!" : "WRONG!"); - return ok; + 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 gboolean +static void test_md5sum (void) { GByteArray *data, *result; @@ -182,7 +196,9 @@ test_md5sum (void) GValue retval; gboolean ok; - debug_printf (1, "md5sum (base64 -> base64): "); + 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); @@ -200,21 +216,14 @@ test_md5sum (void) check_xmlrpc (&retval, SOUP_TYPE_BYTE_ARRAY, &result)); g_byte_array_free (data, TRUE); if (!ok) - return FALSE; + return; - if (result->len != digest_len) { - debug_printf (1, "result has WRONG length (%d)\n", result->len); - g_byte_array_free (result, TRUE); - return FALSE; - } - - ok = (memcmp (digest, result->data, digest_len) == 0); - debug_printf (1, "%s\n", ok ? "OK!" : "WRONG!"); + soup_assert_cmpmem (result->data, result->len, + digest, digest_len); g_byte_array_free (result, TRUE); - return ok; } -static gboolean +static void test_dateChange (void) { GHashTable *structval; @@ -223,7 +232,9 @@ test_dateChange (void) GValue retval; gboolean ok; - debug_printf (1, "dateChange (date, struct of ints -> time): "); + 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), @@ -288,26 +299,24 @@ test_dateChange (void) g_hash_table_destroy (structval); if (!ok) { soup_date_free (date); - return FALSE; + return; } if (debug_level >= 2) { timestamp = soup_date_to_string (result, SOUP_DATE_ISO8601_XMLRPC); - debug_printf (2, "%s: ", timestamp); + debug_printf (2, "%s\n", timestamp); g_free (timestamp); } - ok = ((date->year == result->year) && - (date->month == result->month) && - (date->day == result->day) && - (date->hour == result->hour) && - (date->minute == result->minute) && - (date->second == result->second)); + g_assert_cmpint (date->year, ==, result->year); + g_assert_cmpint (date->month, ==, result->month); + g_assert_cmpint (date->day, ==, result->day); + g_assert_cmpint (date->hour, ==, result->hour); + g_assert_cmpint (date->minute, ==, result->minute); + g_assert_cmpint (date->second, ==, result->second); + soup_date_free (date); soup_date_free (result); - - debug_printf (1, "%s\n", ok ? "OK!" : "WRONG!"); - return ok; } static const char *const echo_strings[] = { @@ -325,15 +334,16 @@ static const char *const echo_strings_broken[] = { "amp; so is lt;thisgt;" }; -static gboolean +static void test_echo (void) { GValueArray *originals, *echoes; GValue retval; int i; - gboolean php_bug = FALSE; - debug_printf (1, "echo (array of string -> array of string): "); + SOUP_TEST_SKIP_IF_NO_XMLRPC_SERVER; + + debug_printf (2, "echo (array of string -> array of string):\n"); originals = g_value_array_new (N_ECHO_STRINGS); for (i = 0; i < N_ECHO_STRINGS; i++) { @@ -347,7 +357,7 @@ test_echo (void) G_TYPE_INVALID) && check_xmlrpc (&retval, G_TYPE_VALUE_ARRAY, &echoes))) { g_value_array_free (originals); - return FALSE; + return; } g_value_array_free (originals); @@ -356,36 +366,71 @@ test_echo (void) debug_printf (2, "%s\"%s\"", i == 0 ? "[" : ", ", g_value_get_string (&echoes->values[i])); } - debug_printf (2, "] -> "); + debug_printf (2, "]\n"); } - if (echoes->n_values != N_ECHO_STRINGS) { - debug_printf (1, " WRONG! Wrong number of return strings"); - g_value_array_free (echoes); - return FALSE; - } + g_assert_cmpint (echoes->n_values, ==, N_ECHO_STRINGS); for (i = 0; i < echoes->n_values; i++) { - if (strcmp (echo_strings[i], g_value_get_string (&echoes->values[i])) != 0) { - if (!server_test && strcmp (echo_strings_broken[i], g_value_get_string (&echoes->values[i])) == 0) - php_bug = TRUE; - else { - debug_printf (1, " WRONG! Mismatch at %d\n", i + 1); - g_value_array_free (echoes); - return FALSE; - } + if (!server_test && strcmp (echo_strings_broken[i], g_value_get_string (&echoes->values[i])) == 0) { + g_test_skip ("PHP bug"); + g_value_array_free (echoes); + return; } + + g_assert_cmpstr (echo_strings[i], ==, g_value_get_string (&echoes->values[i])); } - if (php_bug) - debug_printf (1, "WRONG, but it's php's fault\n"); - else - debug_printf (1, "OK!\n"); g_value_array_free (echoes); - return TRUE; } -static gboolean +static void +test_ping (gconstpointer include_params) +{ + GValueArray *params; + GValue retval; + char *request; + char *out; + gboolean ret; + + 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>"); + + params = soup_value_array_new (); + request = soup_xmlrpc_build_method_call ("ping", params->values, + params->n_values); + g_value_array_free (params); + 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, &retval); + g_free (request); + + if (!ret || !check_xmlrpc (&retval, G_TYPE_STRING, &out)) + return; + + g_assert_cmpstr (out, ==, "pong"); + + g_free (out); +} + +static void do_bad_xmlrpc (const char *body) { SoupMessage *msg; @@ -397,12 +442,7 @@ do_bad_xmlrpc (const char *body) body, strlen (body)); soup_session_send_message (session, msg); - if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) { - debug_printf (1, "ERROR: %d %s\n", msg->status_code, - msg->reason_phrase); - g_object_unref (msg); - return FALSE; - } + soup_test_assert_message_status (msg, SOUP_STATUS_OK); if (!soup_xmlrpc_parse_method_response (msg->response_body->data, msg->response_body->length, @@ -412,44 +452,43 @@ do_bad_xmlrpc (const char *body) err->code, err->message); g_error_free (err); g_object_unref (msg); - return TRUE; + return; } else - debug_printf (1, "ERROR: could not parse response\n"); + soup_test_assert (FALSE, "ERROR: could not parse response\n"); } else - debug_printf (1, "Unexpectedly got successful response!\n"); + soup_test_assert (FALSE, "Unexpectedly got successful response!\n"); g_object_unref (msg); - return FALSE; } -static gboolean +static void test_fault_malformed (void) { - debug_printf (1, "malformed request: "); + SOUP_TEST_SKIP_IF_NO_XMLRPC_SERVER; - return do_bad_xmlrpc ("<methodCall/>"); + do_bad_xmlrpc ("<methodCall/>"); } -static gboolean +static void test_fault_method (void) { - debug_printf (1, "request to non-existent method: "); + SOUP_TEST_SKIP_IF_NO_XMLRPC_SERVER; - return do_bad_xmlrpc ("<methodCall><methodName>no_such_method</methodName><params><param><value><int>1</int></value></param></params></methodCall>"); + do_bad_xmlrpc ("<methodCall><methodName>no_such_method</methodName><params><param><value><int>1</int></value></param></params></methodCall>"); } -static gboolean +static void test_fault_args (void) { - debug_printf (1, "request with invalid args: "); + SOUP_TEST_SKIP_IF_NO_XMLRPC_SERVER; - return do_bad_xmlrpc ("<methodCall><methodName>sum</methodName><params><param><value><int>1</int></value></param></params></methodCall>"); + do_bad_xmlrpc ("<methodCall><methodName>sum</methodName><params><param><value><int>1</int></value></param></params></methodCall>"); } static GOptionEntry xmlrpc_entries[] = { - { "uri", 'u', 0, G_OPTION_ARG_STRING, &uri, + { "uri", 'U', 0, G_OPTION_ARG_STRING, &uri, "Alternate URI for server", NULL }, - { "server-test", 's', 0, G_OPTION_ARG_NONE, &server_test, + { "server-test", 'S', 0, G_OPTION_ARG_NONE, &server_test, "If this is being run from xmlrpc-server-test", NULL }, { NULL } }; @@ -457,34 +496,32 @@ static GOptionEntry xmlrpc_entries[] = { int main (int argc, char **argv) { + int ret; + test_init (argc, argv, xmlrpc_entries); - if (!uri) { + if (!uri && !server_test) { apache_init (); uri = default_uri; } session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL); - if (!test_sum ()) - errors++; - if (!test_countBools ()) - errors++; - if (!test_md5sum ()) - errors++; - if (!test_dateChange ()) - errors++; - if (!test_echo ()) - errors++; - if (!test_fault_malformed ()) - errors++; - if (!test_fault_method ()) - errors++; - if (!test_fault_args ()) - errors++; + g_test_add_func ("/xmlrpc/sum", test_sum); + g_test_add_func ("/xmlrpc/countBools", test_countBools); + g_test_add_func ("/xmlrpc/md5sum", test_md5sum); + g_test_add_func ("/xmlrpc/dateChange", test_dateChange); + g_test_add_func ("/xmlrpc/echo", test_echo); + g_test_add_data_func ("/xmlrpc/ping/empty-params", GINT_TO_POINTER (TRUE), test_ping); + g_test_add_data_func ("/xmlrpc/ping/no-params", GINT_TO_POINTER (FALSE), test_ping); + g_test_add_func ("/xmlrpc/fault/malformed", test_fault_malformed); + g_test_add_func ("/xmlrpc/fault/method", test_fault_method); + g_test_add_func ("/xmlrpc/fault/args", test_fault_args); + + ret = g_test_run (); soup_test_session_abort_unref (session); test_cleanup (); - return errors != 0; + return ret; } |