Blame gdata/tests/streams.c

Packit 4b6dd7
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
Packit 4b6dd7
/*
Packit 4b6dd7
 * GData Client
Packit 4b6dd7
 * Copyright (C) Philip Withnall 2010, 2015 <philip@tecnocode.co.uk>
Packit 4b6dd7
 *
Packit 4b6dd7
 * GData Client is free software; you can redistribute it and/or
Packit 4b6dd7
 * modify it under the terms of the GNU Lesser General Public
Packit 4b6dd7
 * License as published by the Free Software Foundation; either
Packit 4b6dd7
 * version 2.1 of the License, or (at your option) any later version.
Packit 4b6dd7
 *
Packit 4b6dd7
 * GData Client is distributed in the hope that it will be useful,
Packit 4b6dd7
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 4b6dd7
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 4b6dd7
 * Lesser General Public License for more details.
Packit 4b6dd7
 *
Packit 4b6dd7
 * You should have received a copy of the GNU Lesser General Public
Packit 4b6dd7
 * License along with GData Client.  If not, see <http://www.gnu.org/licenses/>.
Packit 4b6dd7
 */
Packit 4b6dd7
Packit 4b6dd7
#include "config.h"
Packit 4b6dd7
Packit 4b6dd7
#include <glib.h>
Packit 4b6dd7
#include <locale.h>
Packit 4b6dd7
#include <string.h>
Packit 4b6dd7
#include <arpa/inet.h>
Packit 4b6dd7
#include <sys/types.h>
Packit 4b6dd7
#include <netinet/in.h>
Packit 4b6dd7
#include <sys/socket.h>
Packit 4b6dd7
Packit 4b6dd7
#include "gdata.h"
Packit 4b6dd7
#include "common.h"
Packit 4b6dd7
Packit 4b6dd7
#ifdef HAVE_LIBSOUP_2_55_90
Packit 4b6dd7
static gpointer
Packit 4b6dd7
run_server_thread (GMainLoop *loop)
Packit 4b6dd7
{
Packit 4b6dd7
	g_main_context_push_thread_default (g_main_loop_get_context (loop));
Packit 4b6dd7
	g_main_loop_run (loop);
Packit 4b6dd7
	g_main_context_pop_thread_default (g_main_loop_get_context (loop));
Packit 4b6dd7
Packit 4b6dd7
	return NULL;
Packit 4b6dd7
}
Packit 4b6dd7
#else /* if !HAVE_LIBSOUP_2_55_90 */
Packit 4b6dd7
static gpointer
Packit 4b6dd7
run_server_thread (SoupServer *server)
Packit 4b6dd7
{
Packit 4b6dd7
	soup_server_run (server);
Packit 4b6dd7
Packit 4b6dd7
	return NULL;
Packit 4b6dd7
}
Packit 4b6dd7
#endif /* !HAVE_LIBSOUP_2_55_90 */
Packit 4b6dd7
Packit 4b6dd7
static GThread *
Packit 4b6dd7
run_server (SoupServer *server, GMainLoop *loop)
Packit 4b6dd7
{
Packit 4b6dd7
	GThread *thread;
Packit 4b6dd7
	gchar *port_string;
Packit 4b6dd7
	GError *error = NULL;
Packit 4b6dd7
	guint16 port;
Packit 4b6dd7
Packit 4b6dd7
#ifdef HAVE_LIBSOUP_2_55_90
Packit 4b6dd7
	thread = g_thread_try_new ("server-thread", (GThreadFunc) run_server_thread, loop, &error);
Packit 4b6dd7
#else /* if !HAVE_LIBSOUP_2_55_90 */
Packit 4b6dd7
	thread = g_thread_try_new ("server-thread", (GThreadFunc) run_server_thread, server, &error);
Packit 4b6dd7
#endif /* !HAVE_LIBSOUP_2_55_90 */
Packit 4b6dd7
	g_assert_no_error (error);
Packit 4b6dd7
	g_assert (thread != NULL);
Packit 4b6dd7
Packit 4b6dd7
	/* Set the port so that libgdata doesn't override it. */
Packit 4b6dd7
#ifdef HAVE_LIBSOUP_2_55_90
Packit 4b6dd7
{
Packit 4b6dd7
	GSList *uris;  /* owned */
Packit 4b6dd7
Packit 4b6dd7
	uris = soup_server_get_uris (server);
Packit 4b6dd7
	g_assert (uris != NULL);
Packit 4b6dd7
	port = soup_uri_get_port (uris->data);
Packit 4b6dd7
Packit 4b6dd7
	g_slist_free_full (uris, (GDestroyNotify) soup_uri_free);
Packit 4b6dd7
}
Packit 4b6dd7
#else /* if !HAVE_LIBSOUP_2_55_90 */
Packit 4b6dd7
	port = soup_server_get_port (server);
Packit 4b6dd7
#endif /* !HAVE_LIBSOUP_2_55_90 */
Packit 4b6dd7
Packit 4b6dd7
	port_string = g_strdup_printf ("%u", port);
Packit 4b6dd7
	g_setenv ("LIBGDATA_HTTPS_PORT", port_string, TRUE);
Packit 4b6dd7
	g_free (port_string);
Packit 4b6dd7
Packit 4b6dd7
	return thread;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
#ifdef HAVE_LIBSOUP_2_55_90
Packit 4b6dd7
static gboolean
Packit 4b6dd7
quit_server_cb (GMainLoop *loop)
Packit 4b6dd7
{
Packit 4b6dd7
	g_main_loop_quit (loop);
Packit 4b6dd7
Packit 4b6dd7
	return FALSE;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
stop_server (SoupServer *server, GMainLoop *loop)
Packit 4b6dd7
{
Packit 4b6dd7
	soup_add_completion (g_main_loop_get_context (loop),
Packit 4b6dd7
	                     (GSourceFunc) quit_server_cb, loop);
Packit 4b6dd7
}
Packit 4b6dd7
#else /* if !HAVE_LIBSOUP_2_55_90 */
Packit 4b6dd7
static gboolean
Packit 4b6dd7
quit_server_cb (SoupServer *server)
Packit 4b6dd7
{
Packit 4b6dd7
	soup_server_quit (server);
Packit 4b6dd7
Packit 4b6dd7
	return FALSE;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
stop_server (SoupServer *server, GMainLoop *loop)
Packit 4b6dd7
{
Packit 4b6dd7
	soup_add_completion (g_main_loop_get_context (loop),
Packit 4b6dd7
	                     (GSourceFunc) quit_server_cb, server);
Packit 4b6dd7
}
Packit 4b6dd7
#endif /* !HAVE_LIBSOUP_2_55_90 */
Packit 4b6dd7
Packit 4b6dd7
static gchar *
Packit 4b6dd7
get_test_string (guint start_num, guint end_num)
Packit 4b6dd7
{
Packit 4b6dd7
	GString *test_string;
Packit 4b6dd7
	guint i;
Packit 4b6dd7
Packit 4b6dd7
	g_return_val_if_fail (end_num < G_MAXUINT, NULL);
Packit 4b6dd7
Packit 4b6dd7
	test_string = g_string_new (NULL);
Packit 4b6dd7
Packit 4b6dd7
	for (i = start_num; i < end_num + 1; i++)
Packit 4b6dd7
		g_string_append_printf (test_string, "%u\n", i);
Packit 4b6dd7
Packit 4b6dd7
	return g_string_free (test_string, FALSE);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
test_download_stream_download_server_content_length_handler_cb (SoupServer *server, SoupMessage *message, const char *path, GHashTable *query,
Packit 4b6dd7
                                                                SoupClientContext *client, gpointer user_data)
Packit 4b6dd7
{
Packit 4b6dd7
	gchar *test_string;
Packit 4b6dd7
	goffset test_string_length;
Packit 4b6dd7
Packit 4b6dd7
	test_string = get_test_string (1, 1000);
Packit 4b6dd7
	test_string_length = strlen (test_string) + 1;
Packit 4b6dd7
Packit 4b6dd7
	/* Add some response headers */
Packit 4b6dd7
	soup_message_set_status (message, SOUP_STATUS_OK);
Packit 4b6dd7
	soup_message_headers_set_content_type (message->response_headers, "text/plain", NULL);
Packit 4b6dd7
	soup_message_headers_set_content_length (message->response_headers, test_string_length);
Packit 4b6dd7
Packit 4b6dd7
	/* Set the response body */
Packit 4b6dd7
	soup_message_body_append (message->response_body, SOUP_MEMORY_TAKE, test_string, test_string_length);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static SoupServer *
Packit 4b6dd7
create_server (SoupServerCallback callback, gpointer user_data, GMainLoop **main_loop)
Packit 4b6dd7
{
Packit 4b6dd7
	GMainContext *context;
Packit 4b6dd7
	SoupServer *server;
Packit 4b6dd7
#ifdef HAVE_LIBSOUP_2_55_90
Packit 4b6dd7
	gchar *cert_path = NULL, *key_path = NULL;
Packit 4b6dd7
	GError *error = NULL;
Packit 4b6dd7
#else /* if !HAVE_LIBSOUP_2_55_90 */
Packit 4b6dd7
	union {
Packit 4b6dd7
		struct sockaddr_in in;
Packit 4b6dd7
		struct sockaddr norm;
Packit 4b6dd7
	} sock;
Packit 4b6dd7
	SoupAddress *addr;
Packit 4b6dd7
#endif /* HAVE_LIBSOUP_2_55_90 */
Packit 4b6dd7
Packit 4b6dd7
	/* Create the server */
Packit 4b6dd7
	g_assert (main_loop != NULL);
Packit 4b6dd7
	context = g_main_context_new ();
Packit 4b6dd7
	*main_loop = g_main_loop_new (context, FALSE);
Packit 4b6dd7
Packit 4b6dd7
#ifdef HAVE_LIBSOUP_2_55_90
Packit 4b6dd7
	server = soup_server_new (NULL, NULL);
Packit 4b6dd7
Packit 4b6dd7
	cert_path = g_test_build_filename (G_TEST_DIST, "cert.pem", NULL);
Packit 4b6dd7
	key_path = g_test_build_filename (G_TEST_DIST, "key.pem", NULL);
Packit 4b6dd7
Packit 4b6dd7
	soup_server_set_ssl_cert_file (server, cert_path, key_path, &error);
Packit 4b6dd7
	g_assert_no_error (error);
Packit 4b6dd7
Packit 4b6dd7
	g_free (key_path);
Packit 4b6dd7
	g_free (cert_path);
Packit 4b6dd7
Packit 4b6dd7
	soup_server_add_handler (server, NULL, callback, user_data, NULL);
Packit 4b6dd7
Packit 4b6dd7
	g_main_context_push_thread_default (context);
Packit 4b6dd7
Packit 4b6dd7
	soup_server_listen_local (server, 0  /* random port */,
Packit 4b6dd7
	                          SOUP_SERVER_LISTEN_HTTPS, &error);
Packit 4b6dd7
	g_assert_no_error (error);
Packit 4b6dd7
Packit 4b6dd7
	g_main_context_pop_thread_default (context);
Packit 4b6dd7
#else /* if !HAVE_LIBSOUP_2_55_90 */
Packit 4b6dd7
	memset (&sock, 0, sizeof (sock));
Packit 4b6dd7
	sock.in.sin_family = AF_INET;
Packit 4b6dd7
	sock.in.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
Packit 4b6dd7
	sock.in.sin_port = htons (0); /* random port */
Packit 4b6dd7
Packit 4b6dd7
	addr = soup_address_new_from_sockaddr (&sock.norm, sizeof (sock.norm));
Packit 4b6dd7
	g_assert (addr != NULL);
Packit 4b6dd7
Packit 4b6dd7
	server = soup_server_new (SOUP_SERVER_INTERFACE, addr,
Packit 4b6dd7
	                          SOUP_SERVER_ASYNC_CONTEXT, context,
Packit 4b6dd7
	                          NULL);
Packit 4b6dd7
Packit 4b6dd7
	soup_server_add_handler (server, NULL, callback, user_data, NULL);
Packit 4b6dd7
Packit 4b6dd7
	g_object_unref (addr);
Packit 4b6dd7
#endif /* !HAVE_LIBSOUP_2_55_90 */
Packit 4b6dd7
Packit 4b6dd7
	g_assert (server != NULL);
Packit 4b6dd7
	g_main_context_unref (context);
Packit 4b6dd7
Packit 4b6dd7
	return server;
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static gchar *
Packit 4b6dd7
build_server_uri (SoupServer *server)
Packit 4b6dd7
{
Packit 4b6dd7
#ifdef HAVE_LIBSOUP_2_55_90
Packit 4b6dd7
	GSList *uris;  /* owned */
Packit 4b6dd7
	GSList *l;  /* unowned */
Packit 4b6dd7
	gchar *retval = NULL;  /* owned */
Packit 4b6dd7
Packit 4b6dd7
	uris = soup_server_get_uris (server);
Packit 4b6dd7
Packit 4b6dd7
	for (l = uris; l != NULL && retval == NULL; l = l->next) {
Packit 4b6dd7
		if (soup_uri_get_scheme (l->data) == SOUP_URI_SCHEME_HTTPS) {
Packit 4b6dd7
			retval = soup_uri_to_string (l->data, FALSE);
Packit 4b6dd7
		}
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	g_slist_free_full (uris, (GDestroyNotify) soup_uri_free);
Packit 4b6dd7
Packit 4b6dd7
	g_assert (retval != NULL);
Packit 4b6dd7
Packit 4b6dd7
	return retval;
Packit 4b6dd7
#else /* if !HAVE_LIBSOUP_2_55_90 */
Packit 4b6dd7
	return g_strdup_printf ("https://%s:%u/",
Packit 4b6dd7
	                        soup_address_get_physical (soup_socket_get_local_address (soup_server_get_listener (server))),
Packit 4b6dd7
	                        soup_server_get_port (server));
Packit 4b6dd7
#endif /* !HAVE_LIBSOUP_2_55_90 */
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
test_download_stream_download_content_length (void)
Packit 4b6dd7
{
Packit 4b6dd7
	SoupServer *server;
Packit 4b6dd7
	GMainLoop *main_loop;
Packit 4b6dd7
	GThread *thread;
Packit 4b6dd7
	gchar *download_uri, *test_string;
Packit 4b6dd7
	GDataService *service;
Packit 4b6dd7
	GInputStream *download_stream;
Packit 4b6dd7
	gssize length_read;
Packit 4b6dd7
	GString *contents;
Packit 4b6dd7
	guint8 buffer[20];
Packit 4b6dd7
	gboolean success;
Packit 4b6dd7
	GError *error = NULL;
Packit 4b6dd7
Packit 4b6dd7
	/* Create and run the server */
Packit 4b6dd7
	server = create_server ((SoupServerCallback) test_download_stream_download_server_content_length_handler_cb, NULL, &main_loop);
Packit 4b6dd7
	thread = run_server (server, main_loop);
Packit 4b6dd7
Packit 4b6dd7
	/* Create a new download stream connected to the server */
Packit 4b6dd7
	download_uri = build_server_uri (server);
Packit 4b6dd7
	service = GDATA_SERVICE (gdata_youtube_service_new ("developer-key", NULL));
Packit 4b6dd7
	download_stream = gdata_download_stream_new (service, NULL, download_uri, NULL);
Packit 4b6dd7
	g_object_unref (service);
Packit 4b6dd7
	g_free (download_uri);
Packit 4b6dd7
Packit 4b6dd7
	/* Read the entire stream into a string which we can later compare with what we expect. */
Packit 4b6dd7
	contents = g_string_new (NULL);
Packit 4b6dd7
Packit 4b6dd7
	while ((length_read = g_input_stream_read (download_stream, buffer, sizeof (buffer), NULL, &error)) > 0) {
Packit 4b6dd7
		g_assert_cmpint (length_read, <=, sizeof (buffer));
Packit 4b6dd7
Packit 4b6dd7
		g_string_append_len (contents, (const gchar*) buffer, length_read);
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	/* Check we've reached EOF successfully */
Packit 4b6dd7
	g_assert_no_error (error);
Packit 4b6dd7
	g_assert_cmpint (length_read, ==, 0);
Packit 4b6dd7
Packit 4b6dd7
	/* Close the stream */
Packit 4b6dd7
	success = g_input_stream_close (download_stream, NULL, &error);
Packit 4b6dd7
	g_assert_no_error (error);
Packit 4b6dd7
	g_assert (success == TRUE);
Packit 4b6dd7
Packit 4b6dd7
	/* Compare the downloaded string to the original */
Packit 4b6dd7
	test_string = get_test_string (1, 1000);
Packit 4b6dd7
Packit 4b6dd7
	g_assert_cmpint (contents->len, ==, strlen (test_string) + 1);
Packit 4b6dd7
	g_assert_cmpstr (contents->str, ==, test_string);
Packit 4b6dd7
Packit 4b6dd7
	g_free (test_string);
Packit 4b6dd7
	g_string_free (contents, TRUE);
Packit 4b6dd7
Packit 4b6dd7
	/* Kill the server and wait for it to die */
Packit 4b6dd7
	stop_server (server, main_loop);
Packit 4b6dd7
	g_thread_join (thread);
Packit 4b6dd7
Packit 4b6dd7
	g_object_unref (download_stream);
Packit 4b6dd7
	g_object_unref (server);
Packit 4b6dd7
	g_main_loop_unref (main_loop);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
test_download_stream_download_server_seek_handler_cb (SoupServer *server, SoupMessage *message, const char *path, GHashTable *query,
Packit 4b6dd7
                                                      SoupClientContext *client, gpointer user_data)
Packit 4b6dd7
{
Packit 4b6dd7
	gchar *test_string;
Packit 4b6dd7
	goffset test_string_length;
Packit 4b6dd7
Packit 4b6dd7
	test_string = get_test_string (1, 1000);
Packit 4b6dd7
	test_string_length = strlen (test_string) + 1;
Packit 4b6dd7
Packit 4b6dd7
	/* Add some response headers */
Packit 4b6dd7
	soup_message_set_status (message, SOUP_STATUS_OK);
Packit 4b6dd7
	soup_message_body_append (message->response_body, SOUP_MEMORY_TAKE, test_string, test_string_length);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/* Test seeking before the first read */
Packit 4b6dd7
static void
Packit 4b6dd7
test_download_stream_download_seek_before_start (void)
Packit 4b6dd7
{
Packit 4b6dd7
	SoupServer *server;
Packit 4b6dd7
	GMainLoop *main_loop;
Packit 4b6dd7
	GThread *thread;
Packit 4b6dd7
	gchar *download_uri, *test_string;
Packit 4b6dd7
	goffset test_string_offset = 0;
Packit 4b6dd7
	guint test_string_length;
Packit 4b6dd7
	GDataService *service;
Packit 4b6dd7
	GInputStream *download_stream;
Packit 4b6dd7
	gssize length_read;
Packit 4b6dd7
	guint8 buffer[20];
Packit 4b6dd7
	gboolean success;
Packit 4b6dd7
	GError *error = NULL;
Packit 4b6dd7
Packit 4b6dd7
	/* Create and run the server */
Packit 4b6dd7
	server = create_server ((SoupServerCallback) test_download_stream_download_server_seek_handler_cb, NULL, &main_loop);
Packit 4b6dd7
	thread = run_server (server, main_loop);
Packit 4b6dd7
Packit 4b6dd7
	/* Create a new download stream connected to the server */
Packit 4b6dd7
	download_uri = build_server_uri (server);
Packit 4b6dd7
	service = GDATA_SERVICE (gdata_youtube_service_new ("developer-key", NULL));
Packit 4b6dd7
	download_stream = gdata_download_stream_new (service, NULL, download_uri, NULL);
Packit 4b6dd7
	g_object_unref (service);
Packit 4b6dd7
	g_free (download_uri);
Packit 4b6dd7
Packit 4b6dd7
	/* Read alternating blocks into a string and compare with what we expect as we go. i.e. Skip 20 bytes, then read 20 bytes, etc. */
Packit 4b6dd7
	test_string = get_test_string (1, 1000);
Packit 4b6dd7
	test_string_length = strlen (test_string) + 1;
Packit 4b6dd7
Packit 4b6dd7
	while (TRUE) {
Packit 4b6dd7
		/* Check the seek offset */
Packit 4b6dd7
		g_assert_cmpint (g_seekable_tell (G_SEEKABLE (download_stream)), ==, test_string_offset);
Packit 4b6dd7
Packit 4b6dd7
		/* Seek forward a buffer length */
Packit 4b6dd7
		if (g_seekable_seek (G_SEEKABLE (download_stream), sizeof (buffer), G_SEEK_CUR, NULL, &error) == FALSE) {
Packit 4b6dd7
			/* Tried to seek past the end of the stream */
Packit 4b6dd7
			g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
Packit 4b6dd7
			g_clear_error (&error);
Packit 4b6dd7
			break;
Packit 4b6dd7
		}
Packit 4b6dd7
Packit 4b6dd7
		test_string_offset += sizeof (buffer);
Packit 4b6dd7
		g_assert_no_error (error);
Packit 4b6dd7
Packit 4b6dd7
		/* Check the seek offset again */
Packit 4b6dd7
		g_assert_cmpint (g_seekable_tell (G_SEEKABLE (download_stream)), ==, test_string_offset);
Packit 4b6dd7
Packit 4b6dd7
		/* Read a buffer-load */
Packit 4b6dd7
		length_read = g_input_stream_read (download_stream, buffer, sizeof (buffer), NULL, &error);
Packit 4b6dd7
Packit 4b6dd7
		g_assert_no_error (error);
Packit 4b6dd7
		g_assert_cmpint (length_read, <=, sizeof (buffer));
Packit 4b6dd7
Packit 4b6dd7
		/* Check the buffer-load against the test string */
Packit 4b6dd7
		g_assert (memcmp (buffer, test_string + test_string_offset, length_read) == 0);
Packit 4b6dd7
		test_string_offset += length_read;
Packit 4b6dd7
Packit 4b6dd7
		/* Check the seek offset again */
Packit 4b6dd7
		g_assert_cmpint (g_seekable_tell (G_SEEKABLE (download_stream)), ==, test_string_offset);
Packit 4b6dd7
Packit 4b6dd7
		if (length_read < (gssize) sizeof (buffer)) {
Packit 4b6dd7
			/* Done */
Packit 4b6dd7
			break;
Packit 4b6dd7
		}
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	g_free (test_string);
Packit 4b6dd7
Packit 4b6dd7
	/* Check the seek offset is within one buffer-load of the end */
Packit 4b6dd7
	g_assert_cmpint (g_seekable_tell (G_SEEKABLE (download_stream)), >, test_string_length - sizeof (buffer));
Packit 4b6dd7
	g_assert_cmpint (g_seekable_tell (G_SEEKABLE (download_stream)), <=, test_string_length);
Packit 4b6dd7
Packit 4b6dd7
	/* Close the stream */
Packit 4b6dd7
	success = g_input_stream_close (download_stream, NULL, &error);
Packit 4b6dd7
	g_assert_no_error (error);
Packit 4b6dd7
	g_assert (success == TRUE);
Packit 4b6dd7
Packit 4b6dd7
	/* Kill the server and wait for it to die */
Packit 4b6dd7
	stop_server (server, main_loop);
Packit 4b6dd7
	g_thread_join (thread);
Packit 4b6dd7
Packit 4b6dd7
	g_object_unref (download_stream);
Packit 4b6dd7
	g_object_unref (server);
Packit 4b6dd7
	g_main_loop_unref (main_loop);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/* Test seeking forwards after the first read */
Packit 4b6dd7
static void
Packit 4b6dd7
test_download_stream_download_seek_after_start_forwards (void)
Packit 4b6dd7
{
Packit 4b6dd7
	SoupServer *server;
Packit 4b6dd7
	GMainLoop *main_loop;
Packit 4b6dd7
	GThread *thread;
Packit 4b6dd7
	gchar *download_uri, *test_string;
Packit 4b6dd7
	goffset test_string_offset = 0;
Packit 4b6dd7
	guint test_string_length;
Packit 4b6dd7
	GDataService *service;
Packit 4b6dd7
	GInputStream *download_stream;
Packit 4b6dd7
	gssize length_read;
Packit 4b6dd7
	guint8 buffer[20];
Packit 4b6dd7
	gboolean success;
Packit 4b6dd7
	GError *error = NULL;
Packit 4b6dd7
Packit 4b6dd7
	/* Create and run the server */
Packit 4b6dd7
	server = create_server ((SoupServerCallback) test_download_stream_download_server_seek_handler_cb, NULL, &main_loop);
Packit 4b6dd7
	thread = run_server (server, main_loop);
Packit 4b6dd7
Packit 4b6dd7
	/* Create a new download stream connected to the server */
Packit 4b6dd7
	download_uri = build_server_uri (server);
Packit 4b6dd7
	service = GDATA_SERVICE (gdata_youtube_service_new ("developer-key", NULL));
Packit 4b6dd7
	download_stream = gdata_download_stream_new (service, NULL, download_uri, NULL);
Packit 4b6dd7
	g_object_unref (service);
Packit 4b6dd7
	g_free (download_uri);
Packit 4b6dd7
Packit 4b6dd7
	/* Read alternating blocks into a string and compare with what we expect as we go. i.e. Read 20 bytes, then skip 20 bytes, etc. */
Packit 4b6dd7
	test_string = get_test_string (1, 1000);
Packit 4b6dd7
	test_string_length = strlen (test_string) + 1;
Packit 4b6dd7
Packit 4b6dd7
	while (TRUE) {
Packit 4b6dd7
		/* Check the seek offset */
Packit 4b6dd7
		g_assert_cmpint (g_seekable_tell (G_SEEKABLE (download_stream)), ==, test_string_offset);
Packit 4b6dd7
Packit 4b6dd7
		/* Read a buffer-load */
Packit 4b6dd7
		length_read = g_input_stream_read (download_stream, buffer, sizeof (buffer), NULL, &error);
Packit 4b6dd7
Packit 4b6dd7
		g_assert_no_error (error);
Packit 4b6dd7
		g_assert_cmpint (length_read, <=, sizeof (buffer));
Packit 4b6dd7
Packit 4b6dd7
		/* Check the buffer-load against the test string */
Packit 4b6dd7
		g_assert (memcmp (buffer, test_string + test_string_offset, length_read) == 0);
Packit 4b6dd7
		test_string_offset += length_read;
Packit 4b6dd7
Packit 4b6dd7
		/* Check the seek offset again */
Packit 4b6dd7
		g_assert_cmpint (g_seekable_tell (G_SEEKABLE (download_stream)), ==, test_string_offset);
Packit 4b6dd7
Packit 4b6dd7
		if (length_read < (gssize) sizeof (buffer)) {
Packit 4b6dd7
			/* Done */
Packit 4b6dd7
			break;
Packit 4b6dd7
		}
Packit 4b6dd7
Packit 4b6dd7
		/* Seek forward a buffer length */
Packit 4b6dd7
		if (g_seekable_seek (G_SEEKABLE (download_stream), sizeof (buffer), G_SEEK_CUR, NULL, &error) == FALSE) {
Packit 4b6dd7
			/* Tried to seek past the end of the stream */
Packit 4b6dd7
			g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
Packit 4b6dd7
			g_clear_error (&error);
Packit 4b6dd7
			break;
Packit 4b6dd7
		}
Packit 4b6dd7
Packit 4b6dd7
		test_string_offset += sizeof (buffer);
Packit 4b6dd7
		g_assert_no_error (error);
Packit 4b6dd7
Packit 4b6dd7
		/* Check the seek offset again */
Packit 4b6dd7
		g_assert_cmpint (g_seekable_tell (G_SEEKABLE (download_stream)), ==, test_string_offset);
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	g_free (test_string);
Packit 4b6dd7
Packit 4b6dd7
	/* Check the seek offset is within one buffer-load of the end */
Packit 4b6dd7
	g_assert_cmpint (g_seekable_tell (G_SEEKABLE (download_stream)), >, test_string_length - sizeof (buffer));
Packit 4b6dd7
	g_assert_cmpint (g_seekable_tell (G_SEEKABLE (download_stream)), <=, test_string_length);
Packit 4b6dd7
Packit 4b6dd7
	/* Close the stream */
Packit 4b6dd7
	success = g_input_stream_close (download_stream, NULL, &error);
Packit 4b6dd7
	g_assert_no_error (error);
Packit 4b6dd7
	g_assert (success == TRUE);
Packit 4b6dd7
Packit 4b6dd7
	/* Kill the server and wait for it to die */
Packit 4b6dd7
	stop_server (server, main_loop);
Packit 4b6dd7
	g_thread_join (thread);
Packit 4b6dd7
Packit 4b6dd7
	g_object_unref (download_stream);
Packit 4b6dd7
	g_object_unref (server);
Packit 4b6dd7
	g_main_loop_unref (main_loop);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/* Test seeking backwards after the first read */
Packit 4b6dd7
static void
Packit 4b6dd7
test_download_stream_download_seek_after_start_backwards (void)
Packit 4b6dd7
{
Packit 4b6dd7
	SoupServer *server;
Packit 4b6dd7
	GMainLoop *main_loop;
Packit 4b6dd7
	GThread *thread;
Packit 4b6dd7
	gchar *download_uri, *test_string;
Packit 4b6dd7
	goffset test_string_offset = 0;
Packit 4b6dd7
	guint repeat_count;
Packit 4b6dd7
	GDataService *service;
Packit 4b6dd7
	GInputStream *download_stream;
Packit 4b6dd7
	gssize length_read;
Packit 4b6dd7
	guint8 buffer[20];
Packit 4b6dd7
	gboolean success;
Packit 4b6dd7
	GError *error = NULL;
Packit 4b6dd7
Packit 4b6dd7
	/* Create and run the server */
Packit 4b6dd7
	server = create_server ((SoupServerCallback) test_download_stream_download_server_seek_handler_cb, NULL, &main_loop);
Packit 4b6dd7
	thread = run_server (server, main_loop);
Packit 4b6dd7
Packit 4b6dd7
	/* Create a new download stream connected to the server */
Packit 4b6dd7
	download_uri = build_server_uri (server);
Packit 4b6dd7
	service = GDATA_SERVICE (gdata_youtube_service_new ("developer-key", NULL));
Packit 4b6dd7
	download_stream = gdata_download_stream_new (service, NULL, download_uri, NULL);
Packit 4b6dd7
	g_object_unref (service);
Packit 4b6dd7
	g_free (download_uri);
Packit 4b6dd7
Packit 4b6dd7
	/* Read a block in, then skip back over the block again. i.e. Read the first block, read the second block, skip back over the second block,
Packit 4b6dd7
	 * read the second block again, skip back over it, etc. Close the stream after doing this several times. */
Packit 4b6dd7
	test_string = get_test_string (1, 1000);
Packit 4b6dd7
Packit 4b6dd7
	/* Read a buffer-load to begin with */
Packit 4b6dd7
	length_read = g_input_stream_read (download_stream, buffer, sizeof (buffer), NULL, &error);
Packit 4b6dd7
Packit 4b6dd7
	g_assert_no_error (error);
Packit 4b6dd7
	test_string_offset += length_read;
Packit 4b6dd7
Packit 4b6dd7
	for (repeat_count = 6; repeat_count > 0; repeat_count--) {
Packit 4b6dd7
		/* Check the seek offset */
Packit 4b6dd7
		g_assert_cmpint (g_seekable_tell (G_SEEKABLE (download_stream)), ==, test_string_offset);
Packit 4b6dd7
Packit 4b6dd7
		/* Read a buffer-load */
Packit 4b6dd7
		length_read = g_input_stream_read (download_stream, buffer, sizeof (buffer), NULL, &error);
Packit 4b6dd7
Packit 4b6dd7
		g_assert_no_error (error);
Packit 4b6dd7
		g_assert_cmpint (length_read, <=, sizeof (buffer));
Packit 4b6dd7
Packit 4b6dd7
		/* Check the buffer-load against the test string */
Packit 4b6dd7
		g_assert (memcmp (buffer, test_string + test_string_offset, length_read) == 0);
Packit 4b6dd7
		test_string_offset += length_read;
Packit 4b6dd7
Packit 4b6dd7
		/* Check the seek offset again */
Packit 4b6dd7
		g_assert_cmpint (g_seekable_tell (G_SEEKABLE (download_stream)), ==, test_string_offset);
Packit 4b6dd7
Packit 4b6dd7
		/* Seek backwards a buffer length */
Packit 4b6dd7
		success = g_seekable_seek (G_SEEKABLE (download_stream), -length_read, G_SEEK_CUR, NULL, &error);
Packit 4b6dd7
		g_assert_no_error (error);
Packit 4b6dd7
		g_assert (success == TRUE);
Packit 4b6dd7
		test_string_offset -= length_read;
Packit 4b6dd7
Packit 4b6dd7
		/* Check the seek offset again */
Packit 4b6dd7
		g_assert_cmpint (g_seekable_tell (G_SEEKABLE (download_stream)), ==, test_string_offset);
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	g_free (test_string);
Packit 4b6dd7
Packit 4b6dd7
	/* Check the seek offset is at the end of the first buffer-load */
Packit 4b6dd7
	g_assert_cmpint (g_seekable_tell (G_SEEKABLE (download_stream)), ==, sizeof (buffer));
Packit 4b6dd7
Packit 4b6dd7
	/* Close the stream */
Packit 4b6dd7
	success = g_input_stream_close (download_stream, NULL, &error);
Packit 4b6dd7
	g_assert_no_error (error);
Packit 4b6dd7
	g_assert (success == TRUE);
Packit 4b6dd7
Packit 4b6dd7
	/* Kill the server and wait for it to die */
Packit 4b6dd7
	stop_server (server, main_loop);
Packit 4b6dd7
	g_thread_join (thread);
Packit 4b6dd7
Packit 4b6dd7
	g_object_unref (download_stream);
Packit 4b6dd7
	g_object_unref (server);
Packit 4b6dd7
	g_main_loop_unref (main_loop);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
test_upload_stream_upload_no_entry_content_length_server_handler_cb (SoupServer *server, SoupMessage *message, const char *path, GHashTable *query,
Packit 4b6dd7
                                                                     SoupClientContext *client, gpointer user_data)
Packit 4b6dd7
{
Packit 4b6dd7
	gchar *test_string;
Packit 4b6dd7
	goffset test_string_length;
Packit 4b6dd7
Packit 4b6dd7
	/* Check the Slug and Content-Type headers have been correctly set by the client */
Packit 4b6dd7
	g_assert_cmpstr (soup_message_headers_get_content_type (message->request_headers, NULL), ==, "text/plain");
Packit 4b6dd7
	g_assert_cmpstr (soup_message_headers_get_one (message->request_headers, "Slug"), ==, "slug");
Packit 4b6dd7
Packit 4b6dd7
	/* Check the client sent the right data */
Packit 4b6dd7
	test_string = get_test_string (1, 1000);
Packit 4b6dd7
	test_string_length = strlen (test_string) + 1;
Packit 4b6dd7
Packit 4b6dd7
	g_assert_cmpint (message->request_body->length, ==, test_string_length);
Packit 4b6dd7
	g_assert_cmpstr (message->request_body->data, ==, test_string);
Packit 4b6dd7
Packit 4b6dd7
	g_free (test_string);
Packit 4b6dd7
Packit 4b6dd7
	/* Add some response headers */
Packit 4b6dd7
	soup_message_set_status (message, SOUP_STATUS_OK);
Packit 4b6dd7
	soup_message_headers_set_content_type (message->response_headers, "text/plain", NULL);
Packit 4b6dd7
Packit 4b6dd7
	/* Set the response body */
Packit 4b6dd7
	soup_message_body_append (message->response_body, SOUP_MEMORY_STATIC, "Test passed!", 13);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
test_upload_stream_upload_no_entry_content_length (void)
Packit 4b6dd7
{
Packit 4b6dd7
	SoupServer *server;
Packit 4b6dd7
	GMainLoop *main_loop;
Packit 4b6dd7
	GThread *thread;
Packit 4b6dd7
	gchar *upload_uri, *test_string;
Packit 4b6dd7
	GDataService *service;
Packit 4b6dd7
	GOutputStream *upload_stream;
Packit 4b6dd7
	gssize length_written;
Packit 4b6dd7
	gsize total_length_written = 0;
Packit 4b6dd7
	goffset test_string_length;
Packit 4b6dd7
	gboolean success;
Packit 4b6dd7
	GError *error = NULL;
Packit 4b6dd7
Packit 4b6dd7
	/* Create and run the server */
Packit 4b6dd7
	server = create_server ((SoupServerCallback) test_upload_stream_upload_no_entry_content_length_server_handler_cb, NULL, &main_loop);
Packit 4b6dd7
	thread = run_server (server, main_loop);
Packit 4b6dd7
Packit 4b6dd7
	/* Create a new upload stream uploading to the server */
Packit 4b6dd7
	upload_uri = build_server_uri (server);
Packit 4b6dd7
	service = GDATA_SERVICE (gdata_youtube_service_new ("developer-key", NULL));
Packit 4b6dd7
	upload_stream = gdata_upload_stream_new (service, NULL, SOUP_METHOD_POST, upload_uri, NULL, "slug", "text/plain", NULL);
Packit 4b6dd7
	g_object_unref (service);
Packit 4b6dd7
	g_free (upload_uri);
Packit 4b6dd7
Packit 4b6dd7
	/* Write the entire test string to the stream */
Packit 4b6dd7
	test_string = get_test_string (1, 1000);
Packit 4b6dd7
	test_string_length = strlen (test_string) + 1;
Packit 4b6dd7
Packit 4b6dd7
	while ((length_written = g_output_stream_write (upload_stream, test_string + total_length_written,
Packit 4b6dd7
	                                                test_string_length - total_length_written, NULL, &error)) > 0) {
Packit 4b6dd7
		g_assert_cmpint (length_written, <=, test_string_length - total_length_written);
Packit 4b6dd7
Packit 4b6dd7
		total_length_written += length_written;
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	g_free (test_string);
Packit 4b6dd7
Packit 4b6dd7
	/* Check we've had a successful return value */
Packit 4b6dd7
	g_assert_no_error (error);
Packit 4b6dd7
	g_assert_cmpint (length_written, ==, 0);
Packit 4b6dd7
	g_assert_cmpint (total_length_written, ==, test_string_length);
Packit 4b6dd7
Packit 4b6dd7
	/* Close the stream */
Packit 4b6dd7
	success = g_output_stream_close (upload_stream, NULL, &error);
Packit 4b6dd7
	g_assert_no_error (error);
Packit 4b6dd7
	g_assert (success == TRUE);
Packit 4b6dd7
Packit 4b6dd7
	/* Kill the server and wait for it to die */
Packit 4b6dd7
	stop_server (server, main_loop);
Packit 4b6dd7
	g_thread_join (thread);
Packit 4b6dd7
Packit 4b6dd7
	g_object_unref (upload_stream);
Packit 4b6dd7
	g_object_unref (server);
Packit 4b6dd7
	g_main_loop_unref (main_loop);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
/* Test parameters for a run of test_upload_stream_resumable(). */
Packit 4b6dd7
typedef struct {
Packit 4b6dd7
	enum {
Packit 4b6dd7
		CONTENT_ONLY = 0,
Packit 4b6dd7
		CONTENT_AND_METADATA = 1,
Packit 4b6dd7
		METADATA_ONLY = 2,
Packit 4b6dd7
	} content_type;
Packit 4b6dd7
#define UPLOAD_STREAM_RESUMABLE_MAX_CONTENT_TYPE 2
Packit 4b6dd7
	gsize file_size;
Packit 4b6dd7
	enum {
Packit 4b6dd7
		ERROR_ON_INITIAL_REQUEST = 0,
Packit 4b6dd7
		ERROR_ON_SUBSEQUENT_REQUEST = 1,
Packit 4b6dd7
		ERROR_ON_FINAL_REQUEST = 2,
Packit 4b6dd7
		NO_ERROR = 3,
Packit 4b6dd7
	} error_type;
Packit 4b6dd7
#define UPLOAD_STREAM_RESUMABLE_MAX_ERROR_TYPE 3
Packit 4b6dd7
} UploadStreamResumableTestParams;
Packit 4b6dd7
Packit 4b6dd7
static const gchar *upload_stream_resumable_content_type_names[] = {
Packit 4b6dd7
	"content-only",
Packit 4b6dd7
	"content-and-metadata",
Packit 4b6dd7
	"metadata-only",
Packit 4b6dd7
};
Packit 4b6dd7
Packit 4b6dd7
static const gchar *upload_stream_resumable_error_type_names[] = {
Packit 4b6dd7
	"initial-error",
Packit 4b6dd7
	"subsequent-error",
Packit 4b6dd7
	"final-error",
Packit 4b6dd7
	"success",
Packit 4b6dd7
};
Packit 4b6dd7
Packit 4b6dd7
typedef struct {
Packit 4b6dd7
	UploadStreamResumableTestParams *test_params;
Packit 4b6dd7
	gsize next_range_start;
Packit 4b6dd7
	gsize next_range_end;
Packit 4b6dd7
	guint next_path_index;
Packit 4b6dd7
	const gchar *test_string;
Packit 4b6dd7
} UploadStreamResumableServerData;
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
test_upload_stream_resumable_server_handler_cb (SoupServer *server, SoupMessage *message, const char *path, GHashTable *query,
Packit 4b6dd7
                                                SoupClientContext *client, UploadStreamResumableServerData *server_data)
Packit 4b6dd7
{
Packit 4b6dd7
	UploadStreamResumableTestParams *test_params;
Packit 4b6dd7
Packit 4b6dd7
	test_params = server_data->test_params;
Packit 4b6dd7
Packit 4b6dd7
	/* Are we handling the initial request, or a subsequent one? */
Packit 4b6dd7
	if (strcmp (path, "/") == 0) {
Packit 4b6dd7
		/* Initial request. */
Packit 4b6dd7
		gchar *file_size_str;
Packit 4b6dd7
Packit 4b6dd7
		/* Check the Slug and X-Upload-* headers. */
Packit 4b6dd7
		g_assert_cmpstr (soup_message_headers_get_one (message->request_headers, "Slug"), ==, "slug");
Packit 4b6dd7
Packit 4b6dd7
		file_size_str = g_strdup_printf ("%" G_GSIZE_FORMAT, test_params->file_size);
Packit 4b6dd7
		g_assert_cmpstr (soup_message_headers_get_one (message->request_headers, "X-Upload-Content-Type"), ==, "text/plain");
Packit 4b6dd7
		g_assert_cmpstr (soup_message_headers_get_one (message->request_headers, "X-Upload-Content-Length"), ==, file_size_str);
Packit 4b6dd7
		g_free (file_size_str);
Packit 4b6dd7
Packit 4b6dd7
		/* Check the Content-Type and content. */
Packit 4b6dd7
		switch (test_params->content_type) {
Packit 4b6dd7
			case CONTENT_ONLY:
Packit 4b6dd7
				/* Check nothing was sent. */
Packit 4b6dd7
				g_assert_cmpstr (soup_message_headers_get_content_type (message->request_headers, NULL), ==, NULL);
Packit 4b6dd7
				g_assert_cmpint (message->request_body->length, ==, 0);
Packit 4b6dd7
Packit 4b6dd7
				break;
Packit 4b6dd7
			case CONTENT_AND_METADATA:
Packit 4b6dd7
			case METADATA_ONLY:
Packit 4b6dd7
				/* Check the JSON sent by the client. */
Packit 4b6dd7
				g_assert_cmpstr (soup_message_headers_get_content_type (message->request_headers, NULL), ==, "application/json");
Packit 4b6dd7
Packit 4b6dd7
				g_assert (message->request_body->data[message->request_body->length] == '\0');
Packit 4b6dd7
				g_assert (gdata_test_compare_json_strings (message->request_body->data,
Packit 4b6dd7
					"{"
Packit 4b6dd7
						"'title': 'Test title!',"
Packit 4b6dd7
						"'kind': 'youtube#video',"
Packit 4b6dd7
						"'snippet': {"
Packit 4b6dd7
							"'title': 'Test title!'"
Packit 4b6dd7
						"},"
Packit 4b6dd7
						"'status': {"
Packit 4b6dd7
							"'privacyStatus': 'public'"
Packit 4b6dd7
						"},"
Packit 4b6dd7
						"'recordingDetails': {}"
Packit 4b6dd7
					"}", TRUE) == TRUE);
Packit 4b6dd7
Packit 4b6dd7
				break;
Packit 4b6dd7
			default:
Packit 4b6dd7
				g_assert_not_reached ();
Packit 4b6dd7
		}
Packit 4b6dd7
Packit 4b6dd7
		/* Send a response. */
Packit 4b6dd7
		switch (test_params->error_type) {
Packit 4b6dd7
			case ERROR_ON_INITIAL_REQUEST:
Packit 4b6dd7
				/* Error. */
Packit 4b6dd7
				goto error;
Packit 4b6dd7
			case ERROR_ON_SUBSEQUENT_REQUEST:
Packit 4b6dd7
			case ERROR_ON_FINAL_REQUEST:
Packit 4b6dd7
			case NO_ERROR:
Packit 4b6dd7
				/* Success. */
Packit 4b6dd7
				if (test_params->file_size == 0) {
Packit 4b6dd7
					goto completion;
Packit 4b6dd7
				} else {
Packit 4b6dd7
					goto continuation;
Packit 4b6dd7
				}
Packit 4b6dd7
Packit 4b6dd7
				break;
Packit 4b6dd7
			default:
Packit 4b6dd7
				g_assert_not_reached ();
Packit 4b6dd7
		}
Packit 4b6dd7
	} else if (*path == '/' && g_ascii_strtoull (path + 1, NULL, 10) == server_data->next_path_index) {
Packit 4b6dd7
		/* Subsequent request. */
Packit 4b6dd7
Packit 4b6dd7
		/* Check the Slug and X-Upload-* headers. */
Packit 4b6dd7
		g_assert_cmpstr (soup_message_headers_get_one (message->request_headers, "Slug"), ==, NULL);
Packit 4b6dd7
		g_assert_cmpstr (soup_message_headers_get_one (message->request_headers, "X-Upload-Content-Type"), ==, NULL);
Packit 4b6dd7
		g_assert_cmpstr (soup_message_headers_get_one (message->request_headers, "X-Upload-Content-Length"), ==, NULL);
Packit 4b6dd7
Packit 4b6dd7
		/* Check the Content-Type and content. */
Packit 4b6dd7
		switch (test_params->content_type) {
Packit 4b6dd7
			case CONTENT_ONLY:
Packit 4b6dd7
			case CONTENT_AND_METADATA: {
Packit 4b6dd7
				goffset range_start, range_end, range_length;
Packit 4b6dd7
Packit 4b6dd7
				/* Check the headers. */
Packit 4b6dd7
				g_assert_cmpstr (soup_message_headers_get_content_type (message->request_headers, NULL), ==, "text/plain");
Packit 4b6dd7
				g_assert_cmpint (soup_message_headers_get_content_length (message->request_headers), ==, message->request_body->length);
Packit 4b6dd7
				g_assert_cmpint (message->request_body->length, >, 0);
Packit 4b6dd7
				g_assert_cmpint (message->request_body->length, <=, 512 * 1024 /* 512 KiB */);
Packit 4b6dd7
				g_assert (soup_message_headers_get_content_range (message->request_headers, &range_start, &range_end,
Packit 4b6dd7
				                                                  &range_length) == TRUE);
Packit 4b6dd7
				g_assert_cmpint (range_start, ==, server_data->next_range_start);
Packit 4b6dd7
				g_assert_cmpint (range_end, ==, server_data->next_range_end);
Packit 4b6dd7
				g_assert_cmpint (range_length, ==, test_params->file_size);
Packit 4b6dd7
Packit 4b6dd7
				/* Check the content. */
Packit 4b6dd7
				g_assert (memcmp (server_data->test_string + range_start, message->request_body->data,
Packit 4b6dd7
				                  message->request_body->length) == 0);
Packit 4b6dd7
Packit 4b6dd7
				/* Update the expected values. */
Packit 4b6dd7
				server_data->next_range_start = range_end + 1;
Packit 4b6dd7
				server_data->next_range_end = MIN (server_data->next_range_start + 512 * 1024, test_params->file_size) - 1;
Packit 4b6dd7
				server_data->next_path_index++;
Packit 4b6dd7
Packit 4b6dd7
				break;
Packit 4b6dd7
			}
Packit 4b6dd7
			case METADATA_ONLY:
Packit 4b6dd7
			default:
Packit 4b6dd7
				g_assert_not_reached ();
Packit 4b6dd7
		}
Packit 4b6dd7
Packit 4b6dd7
		/* Send a response. */
Packit 4b6dd7
		switch (test_params->error_type) {
Packit 4b6dd7
			case ERROR_ON_SUBSEQUENT_REQUEST:
Packit 4b6dd7
			case ERROR_ON_FINAL_REQUEST:
Packit 4b6dd7
				/* Skip the error if this isn't the final request. */
Packit 4b6dd7
				if (test_params->error_type == ERROR_ON_SUBSEQUENT_REQUEST ||
Packit 4b6dd7
				    (test_params->error_type == ERROR_ON_FINAL_REQUEST && server_data->next_range_start == test_params->file_size)) {
Packit 4b6dd7
					goto error;
Packit 4b6dd7
				}
Packit 4b6dd7
Packit 4b6dd7
				/* Fall through */
Packit 4b6dd7
			case NO_ERROR:
Packit 4b6dd7
				/* Success. */
Packit 4b6dd7
				if (server_data->next_range_start == test_params->file_size) {
Packit 4b6dd7
					goto completion;
Packit 4b6dd7
				} else {
Packit 4b6dd7
					goto continuation;
Packit 4b6dd7
				}
Packit 4b6dd7
Packit 4b6dd7
				break;
Packit 4b6dd7
			case ERROR_ON_INITIAL_REQUEST:
Packit 4b6dd7
			default:
Packit 4b6dd7
				g_assert_not_reached ();
Packit 4b6dd7
		}
Packit 4b6dd7
	} else {
Packit 4b6dd7
		g_assert_not_reached ();
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	return;
Packit 4b6dd7
Packit 4b6dd7
error: {
Packit 4b6dd7
		const gchar *error_response =
Packit 4b6dd7
			"{"
Packit 4b6dd7
				"'error': {"
Packit 4b6dd7
					"'errors': ["
Packit 4b6dd7
						"{"
Packit 4b6dd7
							"'domain': 'global',"
Packit 4b6dd7
							"'reason': 'authError',"
Packit 4b6dd7
							"'message': 'Invalid token.',"
Packit 4b6dd7
							"'location': 'Authorization: GoogleLogin'"
Packit 4b6dd7
						"}"
Packit 4b6dd7
					"],"
Packit 4b6dd7
					"'code': 400,"
Packit 4b6dd7
					"'message': 'Invalid token.'"
Packit 4b6dd7
				"}"
Packit 4b6dd7
			"}";
Packit 4b6dd7
Packit 4b6dd7
		/* Error. */
Packit 4b6dd7
		soup_message_set_status (message, SOUP_STATUS_UNAUTHORIZED); /* arbitrary error status code */
Packit 4b6dd7
		soup_message_body_append (message->response_body, SOUP_MEMORY_STATIC, error_response, strlen (error_response));
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	return;
Packit 4b6dd7
Packit 4b6dd7
continuation: {
Packit 4b6dd7
		gchar *upload_uri, *server_uri;
Packit 4b6dd7
Packit 4b6dd7
		/* Continuation. */
Packit 4b6dd7
		if (server_data->next_path_index == 0) {
Packit 4b6dd7
			soup_message_set_status (message, SOUP_STATUS_OK);
Packit 4b6dd7
		} else {
Packit 4b6dd7
			soup_message_set_status (message, 308);
Packit 4b6dd7
		}
Packit 4b6dd7
Packit 4b6dd7
		server_uri = build_server_uri (server);
Packit 4b6dd7
		g_assert_cmpstr (server_uri + strlen (server_uri) - 1, ==, "/");
Packit 4b6dd7
		upload_uri = g_strdup_printf ("%s%u", server_uri,
Packit 4b6dd7
		                              ++server_data->next_path_index);
Packit 4b6dd7
		soup_message_headers_replace (message->response_headers, "Location", upload_uri);
Packit 4b6dd7
		g_free (upload_uri);
Packit 4b6dd7
		g_free (server_uri);
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	return;
Packit 4b6dd7
Packit 4b6dd7
completion: {
Packit 4b6dd7
		const gchar *completion_response =
Packit 4b6dd7
			"{"
Packit 4b6dd7
				"'kind': 'youtube#video',"
Packit 4b6dd7
				"'snippet': {"
Packit 4b6dd7
					"'title': 'Test title!',"
Packit 4b6dd7
					"'categoryId': '10'"  /* Music */
Packit 4b6dd7
				"},"
Packit 4b6dd7
				"'status': {"
Packit 4b6dd7
					"'privacyStatus': 'public'"
Packit 4b6dd7
				"},"
Packit 4b6dd7
				"'recordingDetails': {"
Packit 4b6dd7
					"'recordingDate': '2005-10-02'"
Packit 4b6dd7
				"}"
Packit 4b6dd7
			"}";
Packit 4b6dd7
Packit 4b6dd7
		/* Completion. */
Packit 4b6dd7
		soup_message_set_status (message, SOUP_STATUS_CREATED);
Packit 4b6dd7
		soup_message_headers_set_content_type (message->response_headers, "application/json", NULL);
Packit 4b6dd7
		soup_message_body_append (message->response_body, SOUP_MEMORY_STATIC, completion_response, strlen (completion_response));
Packit 4b6dd7
	}
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
static void
Packit 4b6dd7
test_upload_stream_resumable (gconstpointer user_data)
Packit 4b6dd7
{
Packit 4b6dd7
	UploadStreamResumableTestParams *test_params;
Packit 4b6dd7
	UploadStreamResumableServerData server_data;
Packit 4b6dd7
	SoupServer *server;
Packit 4b6dd7
	GMainLoop *main_loop;
Packit 4b6dd7
	GThread *thread;
Packit 4b6dd7
	gchar *upload_uri;
Packit 4b6dd7
	GDataService *service;
Packit 4b6dd7
	GDataEntry *entry = NULL;
Packit 4b6dd7
	GOutputStream *upload_stream;
Packit 4b6dd7
	gssize length_written;
Packit 4b6dd7
	gsize total_length_written = 0;
Packit 4b6dd7
	gchar *test_string;
Packit 4b6dd7
	goffset test_string_length;
Packit 4b6dd7
	gboolean success;
Packit 4b6dd7
	GError *error = NULL;
Packit 4b6dd7
Packit 4b6dd7
	test_params = (UploadStreamResumableTestParams*) user_data;
Packit 4b6dd7
Packit 4b6dd7
	/* Build the test string. */
Packit 4b6dd7
	if (test_params->file_size > 0) {
Packit 4b6dd7
		test_string = get_test_string (1, test_params->file_size / 4 /* arbitrary number which should generate enough data */);
Packit 4b6dd7
		g_assert (strlen (test_string) + 1 >= test_params->file_size);
Packit 4b6dd7
		test_string[test_params->file_size - 1] = '\0'; /* trim the string to the right length */
Packit 4b6dd7
	} else {
Packit 4b6dd7
		test_string = NULL;
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	test_string_length = test_params->file_size;
Packit 4b6dd7
Packit 4b6dd7
	/* Create and run the server */
Packit 4b6dd7
	server_data.test_params = test_params;
Packit 4b6dd7
	server_data.next_range_start = 0;
Packit 4b6dd7
	server_data.next_range_end = MIN (test_params->file_size, 512 * 1024 /* 512 KiB */) - 1;
Packit 4b6dd7
	server_data.next_path_index = 0;
Packit 4b6dd7
	server_data.test_string = test_string;
Packit 4b6dd7
Packit 4b6dd7
	server = create_server ((SoupServerCallback) test_upload_stream_resumable_server_handler_cb, &server_data, &main_loop);
Packit 4b6dd7
	thread = run_server (server, main_loop);
Packit 4b6dd7
Packit 4b6dd7
	/* Create a new upload stream uploading to the server */
Packit 4b6dd7
	if (test_params->content_type == CONTENT_AND_METADATA || test_params->content_type == METADATA_ONLY) {
Packit 4b6dd7
		/* Build a test entry. */
Packit 4b6dd7
		entry = GDATA_ENTRY (gdata_youtube_video_new (NULL));
Packit 4b6dd7
		gdata_entry_set_title (entry, "Test title!");
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	upload_uri = build_server_uri (server);
Packit 4b6dd7
	service = GDATA_SERVICE (gdata_youtube_service_new ("developer-key", NULL));
Packit 4b6dd7
	upload_stream = gdata_upload_stream_new_resumable (service, NULL, SOUP_METHOD_POST, upload_uri, entry, "slug", "text/plain",
Packit 4b6dd7
	                                                   test_params->file_size, NULL);
Packit 4b6dd7
	g_object_unref (service);
Packit 4b6dd7
	g_free (upload_uri);
Packit 4b6dd7
	g_clear_object (&entry);
Packit 4b6dd7
Packit 4b6dd7
	if (test_params->file_size > 0) {
Packit 4b6dd7
		while ((length_written = g_output_stream_write (upload_stream, test_string + total_length_written,
Packit 4b6dd7
		                                                test_string_length - total_length_written, NULL, &error)) > 0) {
Packit 4b6dd7
			g_assert_cmpint (length_written, <=, test_string_length - total_length_written);
Packit 4b6dd7
Packit 4b6dd7
			total_length_written += length_written;
Packit 4b6dd7
		}
Packit 4b6dd7
	} else {
Packit 4b6dd7
		/* Do an empty write to poke things into action. */
Packit 4b6dd7
		if ((length_written = g_output_stream_write (upload_stream, "", 0, NULL, &error)) > 0) {
Packit 4b6dd7
			total_length_written += length_written;
Packit 4b6dd7
		}
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	/* Check the return value. */
Packit 4b6dd7
	switch (test_params->error_type) {
Packit 4b6dd7
		case ERROR_ON_INITIAL_REQUEST:
Packit 4b6dd7
		case ERROR_ON_SUBSEQUENT_REQUEST:
Packit 4b6dd7
		case ERROR_ON_FINAL_REQUEST:
Packit 4b6dd7
			/* We can't check the write() call for errors, since whether it throws an error depends on whether the range it's writing
Packit 4b6dd7
			 * overlaps a resumable upload chunk, which is entirely arbitrary and unpredictable. */
Packit 4b6dd7
			g_assert_cmpint (length_written, <=, 0);
Packit 4b6dd7
			g_assert_cmpint (total_length_written, <=, test_string_length);
Packit 4b6dd7
			g_clear_error (&error);
Packit 4b6dd7
Packit 4b6dd7
			/* Close the stream */
Packit 4b6dd7
			success = g_output_stream_close (upload_stream, NULL, &error);
Packit 4b6dd7
			g_assert_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_AUTHENTICATION_REQUIRED);
Packit 4b6dd7
			g_assert (success == FALSE);
Packit 4b6dd7
			g_clear_error (&error);
Packit 4b6dd7
Packit 4b6dd7
			break;
Packit 4b6dd7
		case NO_ERROR:
Packit 4b6dd7
			/* Check we've had a successful return value */
Packit 4b6dd7
			g_assert_no_error (error);
Packit 4b6dd7
			g_assert_cmpint (length_written, ==, 0);
Packit 4b6dd7
			g_assert_cmpint (total_length_written, ==, test_string_length);
Packit 4b6dd7
Packit 4b6dd7
			/* Close the stream */
Packit 4b6dd7
			success = g_output_stream_close (upload_stream, NULL, &error);
Packit 4b6dd7
			g_assert_no_error (error);
Packit 4b6dd7
			g_assert (success == TRUE);
Packit 4b6dd7
Packit 4b6dd7
			break;
Packit 4b6dd7
		default:
Packit 4b6dd7
			g_assert_not_reached ();
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	/* Kill the server and wait for it to die */
Packit 4b6dd7
	stop_server (server, main_loop);
Packit 4b6dd7
	g_thread_join (thread);
Packit 4b6dd7
Packit 4b6dd7
	g_free (test_string);
Packit 4b6dd7
	g_object_unref (upload_stream);
Packit 4b6dd7
	g_object_unref (server);
Packit 4b6dd7
	g_main_loop_unref (main_loop);
Packit 4b6dd7
}
Packit 4b6dd7
Packit 4b6dd7
int
Packit 4b6dd7
main (int argc, char *argv[])
Packit 4b6dd7
{
Packit 4b6dd7
	gdata_test_init (argc, argv);
Packit 4b6dd7
Packit 4b6dd7
	/* Only print out headers, since we're sending a lot of data. */
Packit 4b6dd7
	g_setenv ("LIBGDATA_DEBUG", "2" /* GDATA_LOG_HEADERS */, TRUE);
Packit 4b6dd7
Packit 4b6dd7
	g_test_add_func ("/download-stream/download_content_length", test_download_stream_download_content_length);
Packit 4b6dd7
	g_test_add_func ("/download-stream/download_seek/before_start", test_download_stream_download_seek_before_start);
Packit 4b6dd7
	g_test_add_func ("/download-stream/download_seek/after_start_forwards", test_download_stream_download_seek_after_start_forwards);
Packit 4b6dd7
	g_test_add_func ("/download-stream/download_seek/after_start_backwards", test_download_stream_download_seek_after_start_backwards);
Packit 4b6dd7
Packit 4b6dd7
	g_test_add_func ("/upload-stream/upload_no_entry_content_length", test_upload_stream_upload_no_entry_content_length);
Packit 4b6dd7
Packit 4b6dd7
	/* Test all possible combinations of conditions for resumable uploads. */
Packit 4b6dd7
	{
Packit 4b6dd7
		guint i, j, k;
Packit 4b6dd7
Packit 4b6dd7
		const gsize file_sizes[] = { /* all in bytes */
Packit 4b6dd7
			407 * 1024, /* < 512 KiB */
Packit 4b6dd7
			512 * 1024, /* 512 KiB */
Packit 4b6dd7
			666 * 1024, /* > 512 KiB, < 1024 KiB */
Packit 4b6dd7
			1024 * 1024, /* 1024 KiB */
Packit 4b6dd7
			1025 * 1024, /* > 1024 KiB */
Packit 4b6dd7
		};
Packit 4b6dd7
Packit 4b6dd7
		for (i = 0; i < UPLOAD_STREAM_RESUMABLE_MAX_CONTENT_TYPE + 1; i++) {
Packit 4b6dd7
			for (j = 0; j < G_N_ELEMENTS (file_sizes); j++) {
Packit 4b6dd7
				for (k = 0; k < UPLOAD_STREAM_RESUMABLE_MAX_ERROR_TYPE + 1; k++) {
Packit 4b6dd7
					UploadStreamResumableTestParams *test_params;
Packit 4b6dd7
					gchar *test_name;
Packit 4b6dd7
					gsize file_size;
Packit 4b6dd7
Packit 4b6dd7
					/* Ignore combinations of METADATA_ONLY with file_sizes or non-initial errors. */
Packit 4b6dd7
					if (i == METADATA_ONLY && (j != 0 || k != 0)) {
Packit 4b6dd7
						continue;
Packit 4b6dd7
					} else if (i == METADATA_ONLY) {
Packit 4b6dd7
						file_size = 0 /* bytes */;
Packit 4b6dd7
					} else {
Packit 4b6dd7
						file_size = file_sizes[j];
Packit 4b6dd7
					}
Packit 4b6dd7
Packit 4b6dd7
					test_name = g_strdup_printf ("/upload-stream/resumable/%s/%s/%" G_GSIZE_FORMAT,
Packit 4b6dd7
					                             upload_stream_resumable_content_type_names[i],
Packit 4b6dd7
					                             upload_stream_resumable_error_type_names[k],
Packit 4b6dd7
					                             file_size);
Packit 4b6dd7
Packit 4b6dd7
					/* Allocate a new struct. We leak this. */
Packit 4b6dd7
					test_params = g_slice_new (UploadStreamResumableTestParams);
Packit 4b6dd7
					test_params->content_type = i;
Packit 4b6dd7
					test_params->file_size = file_size;
Packit 4b6dd7
					test_params->error_type = k;
Packit 4b6dd7
Packit 4b6dd7
					g_test_add_data_func (test_name, test_params, test_upload_stream_resumable);
Packit 4b6dd7
Packit 4b6dd7
					g_free (test_name);
Packit 4b6dd7
				}
Packit 4b6dd7
			}
Packit 4b6dd7
		}
Packit 4b6dd7
	}
Packit 4b6dd7
Packit 4b6dd7
	return g_test_run ();
Packit 4b6dd7
}