[libgdata] Bug 633364 — Add cancellation tests



commit a62cc92cab588ed89aeb771929f7a227e8518d74
Author: Philip Withnall <philip tecnocode co uk>
Date:   Fri Aug 19 00:16:38 2011 +0100

    Bug 633364 â Add cancellation tests
    
    Add a set of generic macros for writing async operation tests. The macros
    have inbuilt support for generating cancellation tests too, testing
    cancellation before the operation has started and at several time offsets
    afterwards.
    
    This ports the Calendar, Contacts, Documents, PicasaWeb and YouTube test
    suites to use the new macros, and thus gain lots of tests for cancellation.
    
    Closes: bgo#633364

 gdata/tests/calendar.c  |  310 +++++++----------
 gdata/tests/common.c    |   28 ++
 gdata/tests/common.h    |  204 +++++++++++
 gdata/tests/contacts.c  |  449 ++++++++++--------------
 gdata/tests/documents.c |  337 ++++++------------
 gdata/tests/picasaweb.c |  897 ++++++++++++++--------------------------------
 gdata/tests/youtube.c   |  607 +++++++++++++--------------------
 7 files changed, 1147 insertions(+), 1685 deletions(-)
---
diff --git a/gdata/tests/calendar.c b/gdata/tests/calendar.c
index 7deb12a..b9e7a8e 100644
--- a/gdata/tests/calendar.c
+++ b/gdata/tests/calendar.c
@@ -85,47 +85,45 @@ test_authentication (void)
 	g_object_unref (authorizer);
 }
 
-static void
-test_authentication_async_cb (GDataClientLoginAuthorizer *authorizer, GAsyncResult *async_result, GMainLoop *main_loop)
-{
-	gboolean retval;
-	GError *error = NULL;
+GDATA_ASYNC_TEST_FUNCTIONS (authentication, void,
+G_STMT_START {
+	GDataClientLoginAuthorizer *authorizer;
 
-	retval = gdata_client_login_authorizer_authenticate_finish (authorizer, async_result, &error);
-	g_assert_no_error (error);
-	g_assert (retval == TRUE);
-	g_clear_error (&error);
+	/* Create an authorizer */
+	authorizer = gdata_client_login_authorizer_new (CLIENT_ID, GDATA_TYPE_CALENDAR_SERVICE);
 
-	g_main_loop_quit (main_loop);
+	g_assert_cmpstr (gdata_client_login_authorizer_get_client_id (authorizer), ==, CLIENT_ID);
 
-	/* Check all is as it should be */
-	g_assert_cmpstr (gdata_client_login_authorizer_get_username (authorizer), ==, USERNAME);
-	g_assert_cmpstr (gdata_client_login_authorizer_get_password (authorizer), ==, PASSWORD);
+	gdata_client_login_authorizer_authenticate_async (authorizer, USERNAME, PASSWORD, cancellable, async_ready_callback, async_data);
 
-	g_assert (gdata_authorizer_is_authorized_for_domain (GDATA_AUTHORIZER (authorizer),
-	                                                     gdata_calendar_service_get_primary_authorization_domain ()) == TRUE);
-}
+	g_object_unref (authorizer);
+} G_STMT_END,
+G_STMT_START {
+	gboolean retval;
+	GDataClientLoginAuthorizer *authorizer = GDATA_CLIENT_LOGIN_AUTHORIZER (obj);
 
-static void
-test_authentication_async (void)
-{
-	GMainLoop *main_loop;
-	GDataClientLoginAuthorizer *authorizer;
+	retval = gdata_client_login_authorizer_authenticate_finish (authorizer, async_result, &error);
 
-	/* Create an authorizer */
-	authorizer = gdata_client_login_authorizer_new (CLIENT_ID, GDATA_TYPE_CALENDAR_SERVICE);
+	if (error == NULL) {
+		g_assert (retval == TRUE);
 
-	g_assert_cmpstr (gdata_client_login_authorizer_get_client_id (authorizer), ==, CLIENT_ID);
+		/* Check all is as it should be */
+		g_assert_cmpstr (gdata_client_login_authorizer_get_username (authorizer), ==, USERNAME);
+		g_assert_cmpstr (gdata_client_login_authorizer_get_password (authorizer), ==, PASSWORD);
 
-	main_loop = g_main_loop_new (NULL, TRUE);
-	gdata_client_login_authorizer_authenticate_async (authorizer, USERNAME, PASSWORD, NULL,
-	                                                  (GAsyncReadyCallback) test_authentication_async_cb, main_loop);
+		g_assert (gdata_authorizer_is_authorized_for_domain (GDATA_AUTHORIZER (authorizer),
+		                                                     gdata_calendar_service_get_primary_authorization_domain ()) == TRUE);
+	} else {
+		g_assert (retval == FALSE);
 
-	g_main_loop_run (main_loop);
+		/* Check nothing's changed */
+		g_assert_cmpstr (gdata_client_login_authorizer_get_username (authorizer), ==, NULL);
+		g_assert_cmpstr (gdata_client_login_authorizer_get_password (authorizer), ==, NULL);
 
-	g_main_loop_unref (main_loop);
-	g_object_unref (authorizer);
-}
+		g_assert (gdata_authorizer_is_authorized_for_domain (GDATA_AUTHORIZER (authorizer),
+		                                                     gdata_calendar_service_get_primary_authorization_domain ()) == FALSE);
+	}
+} G_STMT_END);
 
 typedef struct {
 	GDataCalendarCalendar *calendar1;
@@ -190,25 +188,6 @@ tear_down_query_calendars (QueryCalendarsData *data, gconstpointer service)
 	g_object_unref (data->calendar3);
 }
 
-typedef struct {
-	QueryCalendarsData parent;
-	GMainLoop *main_loop;
-} QueryCalendarsAsyncData;
-
-static void
-set_up_query_calendars_async (QueryCalendarsAsyncData *data, gconstpointer service)
-{
-	set_up_query_calendars ((QueryCalendarsData*) data, service);
-	data->main_loop = g_main_loop_new (NULL, FALSE);
-}
-
-static void
-tear_down_query_calendars_async (QueryCalendarsAsyncData *data, gconstpointer service)
-{
-	g_main_loop_unref (data->main_loop);
-	tear_down_query_calendars ((QueryCalendarsData*) data, service);
-}
-
 static void
 test_query_all_calendars (QueryCalendarsData *data, gconstpointer service)
 {
@@ -225,34 +204,30 @@ test_query_all_calendars (QueryCalendarsData *data, gconstpointer service)
 	g_object_unref (feed);
 }
 
-static void
-test_query_all_calendars_async_cb (GDataService *service, GAsyncResult *async_result, QueryCalendarsAsyncData *data)
-{
-	GDataFeed *feed;
-	GError *error = NULL;
+GDATA_ASYNC_CLOSURE_FUNCTIONS (query_calendars, QueryCalendarsData);
 
-	feed = gdata_service_query_finish (service, async_result, &error);
-	g_assert_no_error (error);
-	g_assert (GDATA_IS_CALENDAR_FEED (feed));
-	g_clear_error (&error);
-
-	/* TODO: Tests? */
-	g_main_loop_quit (data->main_loop);
+GDATA_ASYNC_TEST_FUNCTIONS (query_all_calendars, QueryCalendarsData,
+G_STMT_START {
+	gdata_calendar_service_query_all_calendars_async (GDATA_CALENDAR_SERVICE (service), NULL, cancellable, NULL,
+	                                                  NULL, NULL, async_ready_callback, async_data);
+} G_STMT_END,
+G_STMT_START {
+	GDataFeed *feed;
 
-	g_object_unref (feed);
-}
+	feed = gdata_service_query_finish (GDATA_SERVICE (obj), async_result, &error);
 
-static void
-test_query_all_calendars_async (QueryCalendarsAsyncData *data, gconstpointer service)
-{
-	gdata_calendar_service_query_all_calendars_async (GDATA_CALENDAR_SERVICE (service), NULL, NULL, NULL,
-	                                                  NULL, NULL, (GAsyncReadyCallback) test_query_all_calendars_async_cb, data);
+	if (error == NULL) {
+		/* TODO: Tests? */
+		g_assert (GDATA_IS_CALENDAR_FEED (feed));
 
-	g_main_loop_run (data->main_loop);
-}
+		g_object_unref (feed);
+	} else {
+		g_assert (feed == NULL);
+	}
+} G_STMT_END);
 
 static void
-test_query_all_calendars_async_progress_closure (QueryCalendarsAsyncData *query_data, gconstpointer service)
+test_query_all_calendars_async_progress_closure (QueryCalendarsData *query_data, gconstpointer service)
 {
 	GDataAsyncProgressClosure *data = g_slice_new0 (GDataAsyncProgressClosure);
 
@@ -289,34 +264,28 @@ test_query_own_calendars (QueryCalendarsData *data, gconstpointer service)
 	g_object_unref (feed);
 }
 
-static void
-test_query_own_calendars_async_cb (GDataService *service, GAsyncResult *async_result, QueryCalendarsAsyncData *data)
-{
+GDATA_ASYNC_TEST_FUNCTIONS (query_own_calendars, QueryCalendarsData,
+G_STMT_START {
+	gdata_calendar_service_query_own_calendars_async (GDATA_CALENDAR_SERVICE (service), NULL, cancellable, NULL,
+	                                                  NULL, NULL, async_ready_callback, async_data);
+} G_STMT_END,
+G_STMT_START {
 	GDataFeed *feed;
-	GError *error = NULL;
 
-	feed = gdata_service_query_finish (service, async_result, &error);
-	g_assert_no_error (error);
-	g_assert (GDATA_IS_CALENDAR_FEED (feed));
-	g_clear_error (&error);
-
-	/* TODO: Tests? */
-	g_main_loop_quit (data->main_loop);
+	feed = gdata_service_query_finish (GDATA_SERVICE (obj), async_result, &error);
 
-	g_object_unref (feed);
-}
+	if (error == NULL) {
+		g_assert (GDATA_IS_CALENDAR_FEED (feed));
+		/* TODO: Tests? */
 
-static void
-test_query_own_calendars_async (QueryCalendarsAsyncData *data, gconstpointer service)
-{
-	gdata_calendar_service_query_own_calendars_async (GDATA_CALENDAR_SERVICE (service), NULL, NULL, NULL,
-	                                                  NULL, NULL, (GAsyncReadyCallback) test_query_own_calendars_async_cb, data);
-
-	g_main_loop_run (data->main_loop);
-}
+		g_object_unref (feed);
+	} else {
+		g_assert (feed == NULL);
+	}
+} G_STMT_END);
 
 static void
-test_query_own_calendars_async_progress_closure (QueryCalendarsAsyncData *query_data, gconstpointer service)
+test_query_own_calendars_async_progress_closure (QueryCalendarsData *query_data, gconstpointer service)
 {
 	GDataAsyncProgressClosure *data = g_slice_new0 (GDataAsyncProgressClosure);
 
@@ -409,58 +378,35 @@ test_query_events (QueryEventsData *data, gconstpointer service)
 	g_object_unref (feed);
 }
 
-typedef struct {
-	QueryEventsData parent;
-	GMainLoop *main_loop;
-} QueryEventsAsyncData;
-
-static void
-set_up_query_events_async (QueryEventsAsyncData *data, gconstpointer service)
-{
-	set_up_query_events ((QueryEventsData*) data, service);
-	data->main_loop = g_main_loop_new (NULL, FALSE);
-}
+GDATA_ASYNC_CLOSURE_FUNCTIONS (query_events, QueryEventsData);
 
-static void
-tear_down_query_events_async (QueryEventsAsyncData *data, gconstpointer service)
-{
-	g_main_loop_unref (data->main_loop);
-	tear_down_query_events ((QueryEventsData*) data, service);
-}
-
-static void
-test_query_events_async_cb (GDataService *service, GAsyncResult *async_result, QueryEventsAsyncData *data)
-{
+GDATA_ASYNC_TEST_FUNCTIONS (query_events, QueryEventsData,
+G_STMT_START {
+	gdata_calendar_service_query_events_async (GDATA_CALENDAR_SERVICE (service), data->parent.calendar, NULL, cancellable, NULL, NULL, NULL,
+	                                           async_ready_callback, async_data);
+} G_STMT_END,
+G_STMT_START {
 	GDataFeed *feed;
-	GError *error = NULL;
 
-	feed = gdata_service_query_finish (service, async_result, &error);
-	g_assert_no_error (error);
-	g_assert (GDATA_IS_CALENDAR_FEED (feed));
-
-	/* TODO: Tests? */
-	g_main_loop_quit (data->main_loop);
-
-	g_object_unref (feed);
-}
+	feed = gdata_service_query_finish (GDATA_SERVICE (obj), async_result, &error);
 
-static void
-test_query_events_async (QueryEventsAsyncData *data, gconstpointer service)
-{
-	gdata_calendar_service_query_events_async (GDATA_CALENDAR_SERVICE (service), data->parent.parent.calendar, NULL, NULL, NULL, NULL, NULL,
-	                                           (GAsyncReadyCallback) test_query_events_async_cb, data);
+	if (error == NULL) {
+		g_assert (GDATA_IS_CALENDAR_FEED (feed));
 
-	g_main_loop_run (data->main_loop);
-}
+		g_object_unref (feed);
+	} else {
+		g_assert (feed == NULL);
+	}
+} G_STMT_END);
 
 static void
-test_query_events_async_progress_closure (QueryEventsAsyncData *query_data, gconstpointer service)
+test_query_events_async_progress_closure (QueryEventsData *query_data, gconstpointer service)
 {
 	GDataAsyncProgressClosure *data = g_slice_new0 (GDataAsyncProgressClosure);
 
 	data->main_loop = g_main_loop_new (NULL, TRUE);
 
-	gdata_calendar_service_query_events_async (GDATA_CALENDAR_SERVICE (service), query_data->parent.parent.calendar, NULL, NULL,
+	gdata_calendar_service_query_events_async (GDATA_CALENDAR_SERVICE (service), query_data->parent.calendar, NULL, NULL,
 	                                           (GDataQueryProgressCallback) gdata_test_async_progress_callback,
 	                                           data, (GDestroyNotify) gdata_test_async_progress_closure_free,
 	                                           (GAsyncReadyCallback) gdata_test_async_progress_finish_callback, data);
@@ -538,51 +484,16 @@ test_event_insert (InsertEventData *data, gconstpointer service)
 	g_object_unref (event);
 }
 
-typedef struct {
-	InsertEventData parent;
-	GMainLoop *main_loop;
-} InsertEventAsyncData;
-
-static void
-set_up_insert_event_async (InsertEventAsyncData *data, gconstpointer service)
-{
-	set_up_insert_event ((InsertEventData*) data, service);
-	data->main_loop = g_main_loop_new (NULL, FALSE);
-}
+GDATA_ASYNC_CLOSURE_FUNCTIONS (insert_event, InsertEventData);
 
-static void
-tear_down_insert_event_async (InsertEventAsyncData *data, gconstpointer service)
-{
-	g_main_loop_unref (data->main_loop);
-	tear_down_insert_event ((InsertEventData*) data, service);
-}
-
-static void
-test_event_insert_async_cb (GDataService *service, GAsyncResult *result, InsertEventAsyncData *data)
-{
-	GDataEntry *event;
-	GError *error = NULL;
-
-	event = gdata_service_insert_entry_finish (service, result, &error);
-	g_assert_no_error (error);
-	g_assert (GDATA_IS_CALENDAR_EVENT (event));
-
-	data->parent.new_event = GDATA_CALENDAR_EVENT (event);
-
-	/* Check the event is correct */
-	g_assert_cmpstr (gdata_entry_get_title (event), ==, "Tennis with Beth");
-
-	g_main_loop_quit (data->main_loop);
-}
-
-static void
-test_event_insert_async (InsertEventAsyncData *data, gconstpointer service)
-{
+GDATA_ASYNC_TEST_FUNCTIONS (event_insert, InsertEventData,
+G_STMT_START {
 	GDataCalendarEvent *event;
 	GDataGDWhere *where;
 	GDataGDWho *who;
 	GDataGDWhen *when;
-	GTimeVal start_time, end_time;
+	GTimeVal start_time;
+	GTimeVal end_time;
 
 	event = gdata_calendar_event_new (NULL);
 
@@ -603,13 +514,21 @@ test_event_insert_async (InsertEventAsyncData *data, gconstpointer service)
 	g_object_unref (when);
 
 	/* Insert the event */
-	gdata_calendar_service_insert_event_async (GDATA_CALENDAR_SERVICE (service), event, NULL, (GAsyncReadyCallback) test_event_insert_async_cb,
-	                                           data);
-
-	g_main_loop_run (data->main_loop);
+	gdata_calendar_service_insert_event_async (GDATA_CALENDAR_SERVICE (service), event, cancellable, async_ready_callback, async_data);
 
 	g_object_unref (event);
-}
+} G_STMT_END,
+G_STMT_START {
+	GDataEntry *event;
+	event = gdata_service_insert_entry_finish (GDATA_SERVICE (obj), async_result, &error);
+	if (error == NULL) {
+		g_assert (GDATA_IS_CALENDAR_EVENT (event));
+		data->new_event = GDATA_CALENDAR_EVENT (event);
+		g_assert_cmpstr (gdata_entry_get_title (event), ==, "Tennis with Beth");
+	} else {
+		g_assert (event == NULL);
+	}
+} G_STMT_END);
 
 static void
 test_event_xml (void)
@@ -1432,31 +1351,42 @@ main (int argc, char *argv[])
 		service = GDATA_SERVICE (gdata_calendar_service_new (authorizer));
 
 		g_test_add_func ("/calendar/authentication", test_authentication);
-		g_test_add_func ("/calendar/authentication/async", test_authentication_async);
+		g_test_add ("/calendar/authentication/async", GDataAsyncTestData, NULL, gdata_set_up_async_test_data, test_authentication_async,
+		            gdata_tear_down_async_test_data);
+		g_test_add ("/calendar/authentication/async/cancellation", GDataAsyncTestData, NULL, gdata_set_up_async_test_data,
+		            test_authentication_async_cancellation, gdata_tear_down_async_test_data);
 
 		g_test_add ("/calendar/query/all_calendars", QueryCalendarsData, service, set_up_query_calendars, test_query_all_calendars,
 		            tear_down_query_calendars);
-		g_test_add ("/calendar/query/all_calendars/async", QueryCalendarsAsyncData, service, set_up_query_calendars_async,
+		g_test_add ("/calendar/query/all_calendars/async", GDataAsyncTestData, service, set_up_query_calendars_async,
 		            test_query_all_calendars_async, tear_down_query_calendars_async);
-		g_test_add ("/calendar/query/all_calendars/async/progress_closure", QueryCalendarsAsyncData, service, set_up_query_calendars_async,
-		            test_query_all_calendars_async_progress_closure, tear_down_query_calendars_async);
+		g_test_add ("/calendar/query/all_calendars/async/progress_closure", QueryCalendarsData, service, set_up_query_calendars,
+		            test_query_all_calendars_async_progress_closure, tear_down_query_calendars);
+		g_test_add ("/calendar/query/all_calendars/async/cancellation", GDataAsyncTestData, service, set_up_query_calendars_async,
+		            test_query_all_calendars_async_cancellation, tear_down_query_calendars_async);
 
 		g_test_add ("/calendar/query/own_calendars", QueryCalendarsData, service, set_up_query_calendars, test_query_own_calendars,
 		            tear_down_query_calendars);
-		g_test_add ("/calendar/query/own_calendars/async", QueryCalendarsAsyncData, service, set_up_query_calendars_async,
+		g_test_add ("/calendar/query/own_calendars/async", GDataAsyncTestData, service, set_up_query_calendars_async,
 		            test_query_own_calendars_async, tear_down_query_calendars_async);
-		g_test_add ("/calendar/query/own_calendars/async/progress_closure", QueryCalendarsAsyncData, service, set_up_query_calendars_async,
-		            test_query_own_calendars_async_progress_closure, tear_down_query_calendars_async);
+		g_test_add ("/calendar/query/own_calendars/async/progress_closure", QueryCalendarsData, service, set_up_query_calendars,
+		            test_query_own_calendars_async_progress_closure, tear_down_query_calendars);
+		g_test_add ("/calendar/query/own_calendars/async/cancellation", GDataAsyncTestData, service, set_up_query_calendars_async,
+		            test_query_own_calendars_async_cancellation, tear_down_query_calendars_async);
 
 		g_test_add ("/calendar/query/events", QueryEventsData, service, set_up_query_events, test_query_events, tear_down_query_events);
-		g_test_add ("/calendar/query/events/async", QueryEventsAsyncData, service, set_up_query_events_async, test_query_events_async,
+		g_test_add ("/calendar/query/events/async", GDataAsyncTestData, service, set_up_query_events_async, test_query_events_async,
 		            tear_down_query_events_async);
-		g_test_add ("/calendar/query/events/async/progress_closure", QueryEventsAsyncData, service, set_up_query_events_async,
-		            test_query_events_async_progress_closure, tear_down_query_events_async);
+		g_test_add ("/calendar/query/events/async/progress_closure", QueryEventsData, service, set_up_query_events,
+		            test_query_events_async_progress_closure, tear_down_query_events);
+		g_test_add ("/calendar/query/events/async/cancellation", GDataAsyncTestData, service, set_up_query_events_async,
+		            test_query_events_async_cancellation, tear_down_query_events_async);
 
 		g_test_add ("/calendar/event/insert", InsertEventData, service, set_up_insert_event, test_event_insert, tear_down_insert_event);
-		g_test_add ("/calendar/event/insert/async", InsertEventAsyncData, service, set_up_insert_event_async, test_event_insert_async,
+		g_test_add ("/calendar/event/insert/async", GDataAsyncTestData, service, set_up_insert_event_async, test_event_insert_async,
 		            tear_down_insert_event_async);
+		g_test_add ("/calendar/event/insert/async/cancellation", GDataAsyncTestData, service, set_up_insert_event_async,
+		            test_event_insert_async_cancellation, tear_down_insert_event_async);
 
 		g_test_add ("/calendar/access-rule/get", TempCalendarAclsData, service, set_up_temp_calendar_acls, test_access_rule_get,
 		            tear_down_temp_calendar_acls);
diff --git a/gdata/tests/common.c b/gdata/tests/common.c
index 6aedaec..a4b0573 100644
--- a/gdata/tests/common.c
+++ b/gdata/tests/common.c
@@ -592,3 +592,31 @@ gdata_test_async_progress_finish_callback (GObject *service, GAsyncResult *res,
 
     g_main_loop_quit (data->main_loop);
 }
+
+gboolean
+gdata_async_test_cancellation_cb (GDataAsyncTestData *async_data)
+{
+	g_cancellable_cancel (async_data->cancellable);
+	async_data->cancellation_timeout_id = 0;
+	return FALSE;
+}
+
+void
+gdata_set_up_async_test_data (GDataAsyncTestData *async_data, gconstpointer test_data)
+{
+	async_data->main_loop = g_main_loop_new (NULL, FALSE);
+	async_data->cancellable = g_cancellable_new ();
+	async_data->cancellation_timeout = 0;
+	async_data->cancellation_successful = FALSE;
+
+	async_data->test_data = test_data;
+}
+
+void
+gdata_tear_down_async_test_data (GDataAsyncTestData *async_data, gconstpointer test_data)
+{
+	g_assert (async_data->test_data == test_data); /* sanity check */
+
+	g_object_unref (async_data->cancellable);
+	g_main_loop_unref (async_data->main_loop);
+}
diff --git a/gdata/tests/common.h b/gdata/tests/common.h
index 1efe06f..da3406f 100644
--- a/gdata/tests/common.h
+++ b/gdata/tests/common.h
@@ -72,6 +72,210 @@ void gdata_test_async_progress_callback (GDataEntry *entry, guint entry_key, gui
 void gdata_test_async_progress_closure_free (GDataAsyncProgressClosure *data);
 void gdata_test_async_progress_finish_callback (GObject *service, GAsyncResult *res, GDataAsyncProgressClosure *data);
 
+typedef struct {
+	/*< private >*/
+	GMainLoop *main_loop;
+	GCancellable *cancellable;
+	guint cancellation_timeout; /* timeout period in ms */
+	guint cancellation_timeout_id; /* ID of the callback source */
+	gboolean cancellation_successful;
+
+	gconstpointer test_data;
+} GDataAsyncTestData;
+
+/**
+ * GDATA_ASYNC_CLOSURE_FUNCTIONS:
+ * @CLOSURE_NAME: the name of the closure
+ * @TestStructType: the type of the synchronous closure structure
+ *
+ * Defines set up and tear down functions for a version of @TestStructType which is wrapped by #GDataAsyncTestData (i.e. allocated and pointed to by
+ * the #GDataAsyncTestData.test_data pointer). These functions will be named <function>set_up_<replaceable>CLOSURE_NAME</replaceable>_async</function>
+ * and <function>tear_down_<replaceable>CLOSURE_NAME</replaceable>_async</function>.
+ *
+ * Since: 0.9.2
+ */
+#define GDATA_ASYNC_CLOSURE_FUNCTIONS(CLOSURE_NAME, TestStructType) \
+static void \
+set_up_##CLOSURE_NAME##_async (GDataAsyncTestData *async_data, gconstpointer service) \
+{ \
+	TestStructType *test_data = g_slice_new (TestStructType); \
+	set_up_##CLOSURE_NAME (test_data, service); \
+	gdata_set_up_async_test_data (async_data, test_data); \
+} \
+ \
+static void \
+tear_down_##CLOSURE_NAME##_async (GDataAsyncTestData *async_data, gconstpointer service) \
+{ \
+	tear_down_##CLOSURE_NAME ((TestStructType*) async_data->test_data, service); \
+	g_slice_free (TestStructType, (TestStructType*) async_data->test_data); \
+	gdata_tear_down_async_test_data (async_data, async_data->test_data); \
+}
+
+/**
+ * GDATA_ASYNC_STARTING_TIMEOUT:
+ *
+ * The initial timeout for cancellation tests, which will be the first timeout used after testing cancelling the operation before it's started.
+ * The value is in milliseconds.
+ *
+ * Since: 0.9.2
+ */
+#define GDATA_ASYNC_STARTING_TIMEOUT 20 /* ms */
+
+/**
+ * GDATA_ASYNC_TIMEOUT_MULTIPLIER:
+ *
+ * The factor by which the asynchronous cancellation timeout will be multiplied between iterations of the cancellation test.
+ *
+ * Since: 0.9.2
+ */
+#define GDATA_ASYNC_TIMEOUT_MULTIPLIER 3
+
+/**
+ * GDATA_ASYNC_MAXIMUM_TIMEOUT:
+ *
+ * The maximum timeout value for cancellation tests before they fail. i.e. If an operation takes longer than this period of time, the asynchronous
+ * operation test will fail.
+ * The value is in milliseconds.
+ *
+ * Since: 0.9.2
+ */
+#define GDATA_ASYNC_MAXIMUM_TIMEOUT 43740 /* ms */
+
+/**
+ * GDATA_ASYNC_TEST_FUNCTIONS:
+ * @TEST_NAME: the name of the test, excluding the âtest_â prefix and the â_asyncâ suffix
+ * @TestStructType: type of the closure structure to use, or <type>void</type>
+ * @TEST_BEGIN_CODE: code to execute to begin the test and start the asynchronous call
+ * @TEST_END_CODE: code to execute once the asynchronous call has completed, which will check the return values and any changed state
+ *
+ * Defines test and callback functions to test normal asynchronous operation and the cancellation behaviour of the given asynchronous function call.
+ *
+ * The asynchronous function call should be started in @TEST_BEGIN_CODE, using <varname>cancellable</varname> as its #GCancellable parameter,
+ * <varname>async_ready_callback</varname> as its #GAsyncReadyCallback parameter and <varname>async_data</varname> as its <varname>user_data</varname>
+ * parameter. There is no need for the code to create its own main loop: that's taken care of by the wrapper code.
+ *
+ * The code in @TEST_END_CODE will be inserted into the callback function for both the normal asynchronous test and the cancellation test, so should
+ * finish the asynchronous function call, using <varname>obj</varname> as the object on which the asynchronous function call was made,
+ * <varname>async_result</varname> as its #GAsyncResult parameter and <varname>error</varname> as its #GError parameter. The code should then check
+ * <varname>error</code>: if it's %NULL, the code should assert success conditions; if it's non-%NULL, the code should assert failure conditions.
+ * The wrapper code will ensure that the error is a %G_IO_ERROR_CANCELLED at the appropriate times.
+ *
+ * The following functions will be defined, and should be added to the test suite using the #GAsyncTestData closure structure:
+ * <function>test_<replaceable>TEST_NAME</replaceable>_async</function> and
+ * <function>test_<replaceable>TEST_NAME</replaceable>_async_cancellation</function>.
+ *
+ * Since: 0.9.2
+ */
+#define GDATA_ASYNC_TEST_FUNCTIONS(TEST_NAME, TestStructType, TEST_BEGIN_CODE, TEST_END_CODE) \
+static void \
+test_##TEST_NAME##_async_cb (GObject *obj, GAsyncResult *async_result, GDataAsyncTestData *async_data) \
+{ \
+	TestStructType *data = (TestStructType*) async_data->test_data; \
+	GError *error = NULL; \
+ \
+	(void) data; /* hide potential unused variable warning */ \
+ \
+	{ \
+		TEST_END_CODE; \
+ \
+		if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) == TRUE) { \
+			g_assert (g_cancellable_is_cancelled (async_data->cancellable) == TRUE); \
+			async_data->cancellation_successful = TRUE; \
+		} else if (error == NULL) { \
+			g_assert (g_cancellable_is_cancelled (async_data->cancellable) == FALSE || async_data->cancellation_timeout > 0); \
+			async_data->cancellation_successful = FALSE; \
+		} else { \
+			/* Unexpected error: explode. */ \
+			g_assert_no_error (error); \
+			async_data->cancellation_successful = FALSE; \
+		} \
+	} \
+ \
+	g_clear_error (&error); \
+ \
+	g_main_loop_quit (async_data->main_loop); \
+} \
+\
+static void \
+test_##TEST_NAME##_async (GDataAsyncTestData *async_data, gconstpointer service) \
+{ \
+	GAsyncReadyCallback async_ready_callback = (GAsyncReadyCallback) test_##TEST_NAME##_async_cb; \
+	TestStructType *data = (TestStructType*) async_data->test_data; \
+	GCancellable *cancellable = NULL; /* don't expose the cancellable, so the test proceeds as normal */ \
+ \
+	(void) data; /* hide potential unused variable warning */ \
+ \
+	/* Just run the test without doing any cancellation, and assert that it succeeds. */ \
+	async_data->cancellation_timeout = 0; \
+ \
+	g_test_message ("Running normal operation testâ"); \
+ \
+	{ \
+		TEST_BEGIN_CODE; \
+	} \
+ \
+	g_main_loop_run (async_data->main_loop); \
+} \
+ \
+static void \
+test_##TEST_NAME##_async_cancellation (GDataAsyncTestData *async_data, gconstpointer service) \
+{ \
+	async_data->cancellation_timeout = 0; \
+ \
+	/* Starting with a short timeout, repeatedly run the async. operation, cancelling it after the timeout and increasing the timeout until
+	 * the operation succeeds for the first time. We then finish the test. This guarantees that if, for example, the test creates an entry on
+	 * the server, it only ever creates one; because the test only ever succeeds once. (Of course, this assumes that the server doesn't change
+	 * state if we cancel the operation, which is a fairly optimistic assumption. Sigh.) */ \
+	do { \
+		GCancellable *cancellable = async_data->cancellable; \
+		GAsyncReadyCallback async_ready_callback = (GAsyncReadyCallback) test_##TEST_NAME##_async_cb; \
+		TestStructType *data = (TestStructType*) async_data->test_data; \
+ \
+		(void) data; /* hide potential unused variable warning */ \
+ \
+		/* Ensure the timeout remains sane. */ \
+		g_assert_cmpuint (async_data->cancellation_timeout, <=, GDATA_ASYNC_MAXIMUM_TIMEOUT); \
+ \
+		/* Schedule the cancellation after the timeout. */ \
+		if (async_data->cancellation_timeout == 0) { \
+			/* For the first test, cancel the cancellable before the test code is run */ \
+			gdata_async_test_cancellation_cb (async_data); \
+		} else { \
+			async_data->cancellation_timeout_id = g_timeout_add (async_data->cancellation_timeout, \
+			                                                     (GSourceFunc) gdata_async_test_cancellation_cb, async_data); \
+		} \
+ \
+		/* Mark the cancellation as unsuccessful and hope we get proven wrong. */ \
+		async_data->cancellation_successful = FALSE; \
+ \
+		g_test_message ("Running cancellation test with timeout of %u msâ", async_data->cancellation_timeout); \
+ \
+		{ \
+			TEST_BEGIN_CODE; \
+		} \
+ \
+		g_main_loop_run (async_data->main_loop); \
+ \
+		/* Reset the cancellable for the next iteration and increase the timeout geometrically. */ \
+		g_cancellable_reset (cancellable); \
+ \
+		if (async_data->cancellation_timeout == 0) { \
+			async_data->cancellation_timeout = GDATA_ASYNC_STARTING_TIMEOUT; /* ms */ \
+		} else { \
+			async_data->cancellation_timeout *= GDATA_ASYNC_TIMEOUT_MULTIPLIER; \
+		} \
+	} while (async_data->cancellation_successful == TRUE); \
+ \
+	/* Clean up the last timeout callback */ \
+	if (async_data->cancellation_timeout_id != 0) { \
+		g_source_remove (async_data->cancellation_timeout_id); \
+	} \
+}
+
+gboolean gdata_async_test_cancellation_cb (GDataAsyncTestData *async_data);
+void gdata_set_up_async_test_data (GDataAsyncTestData *async_data, gconstpointer test_data);
+void gdata_tear_down_async_test_data (GDataAsyncTestData *async_data, gconstpointer test_data);
+
 G_END_DECLS
 
 #endif /* !GDATA_TEST_COMMON_H */
diff --git a/gdata/tests/contacts.c b/gdata/tests/contacts.c
index d3e449d..d693f97 100644
--- a/gdata/tests/contacts.c
+++ b/gdata/tests/contacts.c
@@ -83,24 +83,7 @@ tear_down_temp_contact (TempContactData *data, gconstpointer service)
 	g_object_unref (updated_contact);
 }
 
-typedef struct {
-	TempContactData parent;
-	GMainLoop *main_loop;
-} TempContactAsyncData;
-
-static void
-set_up_temp_contact_async (TempContactAsyncData *data, gconstpointer service)
-{
-	set_up_temp_contact ((TempContactData*) data, service);
-	data->main_loop = g_main_loop_new (NULL, FALSE);
-}
-
-static void
-tear_down_temp_contact_async (TempContactAsyncData *data, gconstpointer service)
-{
-	g_main_loop_unref (data->main_loop);
-	tear_down_temp_contact ((TempContactData*) data, service);
-}
+GDATA_ASYNC_CLOSURE_FUNCTIONS (temp_contact, TempContactData);
 
 static void
 test_authentication (void)
@@ -130,47 +113,45 @@ test_authentication (void)
 	g_object_unref (authorizer);
 }
 
-static void
-test_authentication_async_cb (GDataClientLoginAuthorizer *authorizer, GAsyncResult *async_result, GMainLoop *main_loop)
-{
-	gboolean retval;
-	GError *error = NULL;
+GDATA_ASYNC_TEST_FUNCTIONS (authentication, void,
+G_STMT_START {
+	GDataClientLoginAuthorizer *authorizer;
 
-	retval = gdata_client_login_authorizer_authenticate_finish (authorizer, async_result, &error);
-	g_assert_no_error (error);
-	g_assert (retval == TRUE);
-	g_clear_error (&error);
+	/* Create an authorizer */
+	authorizer = gdata_client_login_authorizer_new (CLIENT_ID, GDATA_TYPE_CONTACTS_SERVICE);
 
-	g_main_loop_quit (main_loop);
+	g_assert_cmpstr (gdata_client_login_authorizer_get_client_id (authorizer), ==, CLIENT_ID);
 
-	/* Check all is as it should be */
-	g_assert_cmpstr (gdata_client_login_authorizer_get_username (authorizer), ==, USERNAME);
-	g_assert_cmpstr (gdata_client_login_authorizer_get_password (authorizer), ==, PASSWORD);
+	gdata_client_login_authorizer_authenticate_async (authorizer, USERNAME, PASSWORD, cancellable, async_ready_callback, async_data);
 
-	g_assert (gdata_authorizer_is_authorized_for_domain (GDATA_AUTHORIZER (authorizer),
-	                                                     gdata_contacts_service_get_primary_authorization_domain ()) == TRUE);
-}
+	g_object_unref (authorizer);
+} G_STMT_END,
+G_STMT_START {
+	gboolean retval;
+	GDataClientLoginAuthorizer *authorizer = GDATA_CLIENT_LOGIN_AUTHORIZER (obj);
 
-static void
-test_authentication_async (void)
-{
-	GMainLoop *main_loop;
-	GDataClientLoginAuthorizer *authorizer;
+	retval = gdata_client_login_authorizer_authenticate_finish (authorizer, async_result, &error);
 
-	/* Create an authorizer */
-	authorizer = gdata_client_login_authorizer_new (CLIENT_ID, GDATA_TYPE_CONTACTS_SERVICE);
+	if (error == NULL) {
+		g_assert (retval == TRUE);
 
-	g_assert_cmpstr (gdata_client_login_authorizer_get_client_id (authorizer), ==, CLIENT_ID);
+		/* Check all is as it should be */
+		g_assert_cmpstr (gdata_client_login_authorizer_get_username (authorizer), ==, USERNAME);
+		g_assert_cmpstr (gdata_client_login_authorizer_get_password (authorizer), ==, PASSWORD);
 
-	main_loop = g_main_loop_new (NULL, TRUE);
-	gdata_client_login_authorizer_authenticate_async (authorizer, USERNAME, PASSWORD, NULL,
-	                                                  (GAsyncReadyCallback) test_authentication_async_cb, main_loop);
+		g_assert (gdata_authorizer_is_authorized_for_domain (GDATA_AUTHORIZER (authorizer),
+		                                                     gdata_contacts_service_get_primary_authorization_domain ()) == TRUE);
+	} else {
+		g_assert (retval == FALSE);
 
-	g_main_loop_run (main_loop);
+		/* Check nothing's changed */
+		g_assert_cmpstr (gdata_client_login_authorizer_get_username (authorizer), ==, NULL);
+		g_assert_cmpstr (gdata_client_login_authorizer_get_password (authorizer), ==, NULL);
 
-	g_main_loop_unref (main_loop);
-	g_object_unref (authorizer);
-}
+		g_assert (gdata_authorizer_is_authorized_for_domain (GDATA_AUTHORIZER (authorizer),
+		                                                     gdata_contacts_service_get_primary_authorization_domain ()) == FALSE);
+	}
+} G_STMT_END);
 
 typedef struct {
 	GDataContactsContact *contact1;
@@ -233,53 +214,30 @@ test_query_all_contacts (QueryAllContactsData *data, gconstpointer service)
 	g_object_unref (feed);
 }
 
-typedef struct {
-	QueryAllContactsData parent;
-	GMainLoop *main_loop;
-} QueryAllContactsAsyncData;
+GDATA_ASYNC_CLOSURE_FUNCTIONS (query_all_contacts, QueryAllContactsData);
 
-static void
-set_up_query_all_contacts_async (QueryAllContactsAsyncData *data, gconstpointer service)
-{
-	set_up_query_all_contacts ((QueryAllContactsData*) data, service);
-	data->main_loop = g_main_loop_new (NULL, FALSE);
-}
-
-static void
-tear_down_query_all_contacts_async (QueryAllContactsAsyncData *data, gconstpointer service)
-{
-	g_main_loop_unref (data->main_loop);
-	tear_down_query_all_contacts ((QueryAllContactsData*) data, service);
-}
-
-static void
-test_query_all_contacts_async_cb (GDataService *service, GAsyncResult *async_result, QueryAllContactsAsyncData *data)
-{
+GDATA_ASYNC_TEST_FUNCTIONS (query_all_contacts, QueryAllContactsData,
+G_STMT_START {
+	gdata_contacts_service_query_contacts_async (GDATA_CONTACTS_SERVICE (service), NULL, cancellable, NULL,
+	                                             NULL, NULL, async_ready_callback, async_data);
+} G_STMT_END,
+G_STMT_START {
 	GDataFeed *feed;
-	GError *error = NULL;
-
-	feed = gdata_service_query_finish (service, async_result, &error);
-	g_assert_no_error (error);
-	g_assert (GDATA_IS_FEED (feed));
-	g_clear_error (&error);
 
-	/* TODO: Tests? */
-	g_main_loop_quit (data->main_loop);
+	feed = gdata_service_query_finish (GDATA_SERVICE (obj), async_result, &error);
 
-	g_object_unref (feed);
-}
+	if (error == NULL) {
+		g_assert (GDATA_IS_FEED (feed));
+		/* TODO: Tests? */
 
-static void
-test_query_all_contacts_async (QueryAllContactsAsyncData *data, gconstpointer service)
-{
-	gdata_contacts_service_query_contacts_async (GDATA_CONTACTS_SERVICE (service), NULL, NULL, NULL,
-	                                             NULL, NULL, (GAsyncReadyCallback) test_query_all_contacts_async_cb, data);
-
-	g_main_loop_run (data->main_loop);
-}
+		g_object_unref (feed);
+	} else {
+		g_assert (feed == NULL);
+	}
+} G_STMT_END);
 
 static void
-test_query_all_contacts_async_progress_closure (QueryAllContactsAsyncData *query_data, gconstpointer service)
+test_query_all_contacts_async_progress_closure (QueryAllContactsData *query_data, gconstpointer service)
 {
 	GDataAsyncProgressClosure *data = g_slice_new0 (GDataAsyncProgressClosure);
 
@@ -674,53 +632,30 @@ test_query_all_groups (QueryAllGroupsData *data, gconstpointer service)
 	g_object_unref (feed);
 }
 
-typedef struct {
-	QueryAllGroupsData parent;
-	GMainLoop *main_loop;
-} QueryAllGroupsAsyncData;
+GDATA_ASYNC_CLOSURE_FUNCTIONS (query_all_groups, QueryAllGroupsData);
 
-static void
-set_up_query_all_groups_async (QueryAllGroupsAsyncData *data, gconstpointer service)
-{
-	set_up_query_all_groups ((QueryAllGroupsData*) data, service);
-	data->main_loop = g_main_loop_new (NULL, FALSE);
-}
-
-static void
-tear_down_query_all_groups_async (QueryAllGroupsAsyncData *data, gconstpointer service)
-{
-	g_main_loop_unref (data->main_loop);
-	tear_down_query_all_groups ((QueryAllGroupsData*) data, service);
-}
-
-static void
-test_query_all_groups_async_cb (GDataService *service, GAsyncResult *async_result, QueryAllGroupsAsyncData *data)
-{
+GDATA_ASYNC_TEST_FUNCTIONS (query_all_groups, QueryAllGroupsData,
+G_STMT_START {
+	gdata_contacts_service_query_groups_async (GDATA_CONTACTS_SERVICE (service), NULL, cancellable, NULL, NULL, NULL,
+	                                           async_ready_callback, async_data);
+} G_STMT_END,
+G_STMT_START {
 	GDataFeed *feed;
-	GError *error = NULL;
 
-	feed = gdata_service_query_finish (service, async_result, &error);
-	g_assert_no_error (error);
-	g_assert (GDATA_IS_FEED (feed));
-	g_clear_error (&error);
+	feed = gdata_service_query_finish (GDATA_SERVICE (obj), async_result, &error);
 
-	/* TODO: Tests? */
-	g_main_loop_quit (data->main_loop);
+	if (error == NULL) {
+		g_assert (GDATA_IS_FEED (feed));
+		/* TODO: Tests? */
 
-	g_object_unref (feed);
-}
-
-static void
-test_query_all_groups_async (QueryAllGroupsAsyncData *data, gconstpointer service)
-{
-	gdata_contacts_service_query_groups_async (GDATA_CONTACTS_SERVICE (service), NULL, NULL, NULL, NULL, NULL,
-	                                           (GAsyncReadyCallback) test_query_all_groups_async_cb, data);
-
-	g_main_loop_run (data->main_loop);
-}
+		g_object_unref (feed);
+	} else {
+		g_assert (feed == NULL);
+	}
+} G_STMT_END);
 
 static void
-test_query_all_groups_async_progress_closure (QueryAllGroupsAsyncData *query_data, gconstpointer service)
+test_query_all_groups_async_progress_closure (QueryAllGroupsData *query_data, gconstpointer service)
 {
 	GDataAsyncProgressClosure *data = g_slice_new0 (GDataAsyncProgressClosure);
 
@@ -800,46 +735,10 @@ test_group_insert (InsertGroupData *data, gconstpointer service)
 	g_object_unref (group);
 }
 
-typedef struct {
-	InsertGroupData parent;
-	GMainLoop *main_loop;
-} InsertGroupAsyncData;
-
-static void
-set_up_insert_group_async (InsertGroupAsyncData *data, gconstpointer service)
-{
-	set_up_insert_group ((InsertGroupData*) data, service);
-	data->main_loop = g_main_loop_new (NULL, FALSE);
-}
-
-static void
-tear_down_insert_group_async (InsertGroupAsyncData *data, gconstpointer service)
-{
-	g_main_loop_unref (data->main_loop);
-	tear_down_insert_group ((InsertGroupData*) data, service);
-}
-
-static void
-test_group_insert_async_cb (GDataService *service, GAsyncResult *async_result, InsertGroupAsyncData *data)
-{
-	GDataEntry *entry;
-	GError *error = NULL;
-
-	entry = gdata_service_insert_entry_finish (service, async_result, &error);
-	g_assert_no_error (error);
-	g_assert (GDATA_IS_CONTACTS_GROUP (entry));
-	g_clear_error (&error);
-
-	data->parent.new_group = GDATA_CONTACTS_GROUP (entry);
-
-	/* TODO: Tests? */
-
-	g_main_loop_quit (data->main_loop);
-}
+GDATA_ASYNC_CLOSURE_FUNCTIONS (insert_group, InsertGroupData);
 
-static void
-test_group_insert_async (InsertGroupAsyncData *data, gconstpointer service)
-{
+GDATA_ASYNC_TEST_FUNCTIONS (group_insert, InsertGroupData,
+G_STMT_START {
 	GDataContactsGroup *group;
 
 	group = gdata_contacts_group_new (NULL);
@@ -852,13 +751,24 @@ test_group_insert_async (InsertGroupAsyncData *data, gconstpointer service)
 	gdata_entry_set_title (GDATA_ENTRY (group), "New Group!");
 	g_assert (gdata_contacts_group_set_extended_property (group, "foobar", "barfoo") == TRUE);
 
-	gdata_contacts_service_insert_group_async (GDATA_CONTACTS_SERVICE (service), group, NULL, (GAsyncReadyCallback) test_group_insert_async_cb,
-	                                           data);
-
-	g_main_loop_run (data->main_loop);
+	gdata_contacts_service_insert_group_async (GDATA_CONTACTS_SERVICE (service), group, cancellable, async_ready_callback, async_data);
 
 	g_object_unref (group);
-}
+} G_STMT_END,
+G_STMT_START {
+	GDataEntry *entry;
+
+	entry = gdata_service_insert_entry_finish (GDATA_SERVICE (obj), async_result, &error);
+
+	if (error == NULL) {
+		g_assert (GDATA_IS_CONTACTS_GROUP (entry));
+		/* TODO: Tests? */
+
+		data->new_group = GDATA_CONTACTS_GROUP (entry);
+	} else {
+		g_assert (entry == NULL);
+	}
+} G_STMT_END);
 
 static void
 test_contact_properties (void)
@@ -2008,24 +1918,8 @@ test_photo_add (TempContactData *data, gconstpointer service)
 	g_free (photo_data);
 }
 
-static void
-test_photo_add_async_cb (GDataContactsContact *contact, GAsyncResult *result, TempContactAsyncData *data)
-{
-	gboolean success;
-	GError *error = NULL;
-
-	success = gdata_contacts_contact_set_photo_finish (contact, result, &error);
-	g_assert_no_error (error);
-	g_assert (success == TRUE);
-
-	g_assert (gdata_contacts_contact_get_photo_etag (contact) != NULL);
-
-	g_main_loop_quit (data->main_loop);
-}
-
-static void
-test_photo_add_async (TempContactAsyncData *data, gconstpointer service)
-{
+GDATA_ASYNC_TEST_FUNCTIONS (photo_add, TempContactData,
+G_STMT_START {
 	guint8 *photo_data;
 	gsize length;
 
@@ -2033,13 +1927,25 @@ test_photo_add_async (TempContactAsyncData *data, gconstpointer service)
 	g_assert (g_file_get_contents (TEST_FILE_DIR "photo.jpg", (gchar**) &photo_data, &length, NULL) == TRUE);
 
 	/* Add it to the contact asynchronously */
-	gdata_contacts_contact_set_photo_async (data->parent.contact, GDATA_CONTACTS_SERVICE (service), photo_data, length, "image/jpeg", NULL,
-	                                        (GAsyncReadyCallback) test_photo_add_async_cb, data);
-
-	g_main_loop_run (data->main_loop);
+	gdata_contacts_contact_set_photo_async (data->contact, GDATA_CONTACTS_SERVICE (service), photo_data, length, "image/jpeg", cancellable,
+	                                        async_ready_callback, async_data);
 
 	g_free (photo_data);
-}
+} G_STMT_END,
+G_STMT_START {
+	GDataContactsContact *contact = GDATA_CONTACTS_CONTACT (obj);
+	gboolean success;
+
+	success = gdata_contacts_contact_set_photo_finish (contact, async_result, &error);
+
+	if (error == NULL) {
+		g_assert (success == TRUE);
+		g_assert (gdata_contacts_contact_get_photo_etag (contact) != NULL);
+	} else {
+		g_assert (success == FALSE);
+		g_assert (gdata_contacts_contact_get_photo_etag (contact) == NULL);
+	}
+} G_STMT_END);
 
 static void
 add_photo_to_contact (GDataContactsService *service, GDataContactsContact *contact)
@@ -2054,14 +1960,24 @@ add_photo_to_contact (GDataContactsService *service, GDataContactsContact *conta
 	g_free (photo_data);
 }
 
+typedef TempContactData TempContactWithPhotoData;
+
 static void
-set_up_temp_contact_with_photo (TempContactData *data, gconstpointer service)
+set_up_temp_contact_with_photo (TempContactWithPhotoData *data, gconstpointer service)
 {
-	set_up_temp_contact (data, service);
+	set_up_temp_contact ((TempContactData*) data, service);
 	add_photo_to_contact (GDATA_CONTACTS_SERVICE (service), data->contact);
 }
 
 static void
+tear_down_temp_contact_with_photo (TempContactWithPhotoData *data, gconstpointer service)
+{
+	tear_down_temp_contact ((TempContactData*) data, service);
+}
+
+GDATA_ASYNC_CLOSURE_FUNCTIONS (temp_contact_with_photo, TempContactWithPhotoData);
+
+static void
 test_photo_get (TempContactData *data, gconstpointer service)
 {
 	guint8 *photo_data;
@@ -2085,47 +2001,37 @@ test_photo_get (TempContactData *data, gconstpointer service)
 	g_clear_error (&error);
 }
 
-static void
-set_up_temp_contact_async_with_photo (TempContactAsyncData *data, gconstpointer service)
-{
-	set_up_temp_contact_async (data, service);
-	add_photo_to_contact (GDATA_CONTACTS_SERVICE (service), data->parent.contact);
-}
+GDATA_ASYNC_TEST_FUNCTIONS (photo_get, TempContactData,
+G_STMT_START {
+	g_assert (gdata_contacts_contact_get_photo_etag (data->contact) != NULL);
 
-static void
-test_photo_get_async_cb (GDataContactsContact *contact, GAsyncResult *result, TempContactAsyncData *data)
-{
+	/* Get the photo from the network asynchronously */
+	gdata_contacts_contact_get_photo_async (data->contact, GDATA_CONTACTS_SERVICE (service), cancellable, async_ready_callback, async_data);
+} G_STMT_END,
+G_STMT_START {
+	GDataContactsContact *contact = GDATA_CONTACTS_CONTACT (obj);
 	guint8 *photo_data;
 	gsize length;
 	gchar *content_type;
-	GError *error = NULL;
 
 	/* Finish getting the photo */
-	photo_data = gdata_contacts_contact_get_photo_finish (contact, result, &length, &content_type, &error);
-	g_assert_no_error (error);
-	g_assert (photo_data != NULL);
-	g_assert (length != 0);
-	g_assert_cmpstr (content_type, ==, "image/jpeg");
-
-	g_assert (gdata_contacts_contact_get_photo_etag (contact) != NULL);
-
-	g_main_loop_quit (data->main_loop);
+	photo_data = gdata_contacts_contact_get_photo_finish (contact, async_result, &length, &content_type, &error);
+
+	if (error == NULL) {
+		g_assert (photo_data != NULL);
+		g_assert (length != 0);
+		g_assert_cmpstr (content_type, ==, "image/jpeg");
+
+		g_assert (gdata_contacts_contact_get_photo_etag (contact) != NULL);
+	} else {
+		g_assert (photo_data == NULL);
+		g_assert (length == 0);
+		g_assert (content_type == NULL);
+	}
 
 	g_free (content_type);
 	g_free (photo_data);
-}
-
-static void
-test_photo_get_async (TempContactAsyncData *data, gconstpointer service)
-{
-	g_assert (gdata_contacts_contact_get_photo_etag (data->parent.contact) != NULL);
-
-	/* Get the photo from the network asynchronously */
-	gdata_contacts_contact_get_photo_async (data->parent.contact, GDATA_CONTACTS_SERVICE (service), NULL,
-	                                        (GAsyncReadyCallback) test_photo_get_async_cb, data);
-
-	g_main_loop_run (data->main_loop);
-}
+} G_STMT_END);
 
 static void
 test_photo_delete (TempContactData *data, gconstpointer service)
@@ -2143,32 +2049,28 @@ test_photo_delete (TempContactData *data, gconstpointer service)
 	g_clear_error (&error);
 }
 
-static void
-test_photo_delete_async_cb (GDataContactsContact *contact, GAsyncResult *result, TempContactAsyncData *data)
-{
-	gboolean success;
-	GError *error = NULL;
-
-	success = gdata_contacts_contact_set_photo_finish (contact, result, &error);
-	g_assert_no_error (error);
-	g_assert (success == TRUE);
-
-	g_assert (gdata_contacts_contact_get_photo_etag (contact) == NULL);
-
-	g_main_loop_quit (data->main_loop);
-}
-
-static void
-test_photo_delete_async (TempContactAsyncData *data, gconstpointer service)
-{
-	g_assert (gdata_contacts_contact_get_photo_etag (data->parent.contact) != NULL);
+GDATA_ASYNC_TEST_FUNCTIONS (photo_delete, TempContactData,
+G_STMT_START {
+	g_assert (gdata_contacts_contact_get_photo_etag (data->contact) != NULL);
 
 	/* Delete it from the contact asynchronously */
-	gdata_contacts_contact_set_photo_async (data->parent.contact, GDATA_CONTACTS_SERVICE (service), NULL, 0, NULL, NULL,
-	                                        (GAsyncReadyCallback) test_photo_delete_async_cb, data);
+	gdata_contacts_contact_set_photo_async (data->contact, GDATA_CONTACTS_SERVICE (service), NULL, 0, NULL, cancellable,
+	                                        async_ready_callback, async_data);
+} G_STMT_END,
+G_STMT_START {
+	GDataContactsContact *contact = GDATA_CONTACTS_CONTACT (obj);
+	gboolean success;
 
-	g_main_loop_run (data->main_loop);
-}
+	success = gdata_contacts_contact_set_photo_finish (contact, async_result, &error);
+
+	if (error == NULL) {
+		g_assert (success == TRUE);
+		g_assert (gdata_contacts_contact_get_photo_etag (contact) == NULL);
+	} else {
+		g_assert (success == FALSE);
+		g_assert (gdata_contacts_contact_get_photo_etag (contact) != NULL);
+	}
+} G_STMT_END);
 
 static void
 test_batch (gconstpointer service)
@@ -2490,29 +2392,40 @@ main (int argc, char *argv[])
 		service = GDATA_SERVICE (gdata_contacts_service_new (authorizer));
 
 		g_test_add_func ("/contacts/authentication", test_authentication);
-		g_test_add_func ("/contacts/authentication/async", test_authentication_async);
+		g_test_add ("/contacts/authentication/async", GDataAsyncTestData, NULL, gdata_set_up_async_test_data, test_authentication_async,
+		            gdata_tear_down_async_test_data);
+		g_test_add ("/contacts/authentication/async/cancellation", GDataAsyncTestData, NULL, gdata_set_up_async_test_data,
+		            test_authentication_async_cancellation, gdata_tear_down_async_test_data);
 
 		g_test_add ("/contacts/contact/insert", InsertData, service, set_up_insert, test_contact_insert, tear_down_insert);
 		g_test_add ("/contacts/contact/update", TempContactData, service, set_up_temp_contact, test_contact_update, tear_down_temp_contact);
 
 		g_test_add ("/contacts/query/all_contacts", QueryAllContactsData, service, set_up_query_all_contacts, test_query_all_contacts,
 		            tear_down_query_all_contacts);
-		g_test_add ("/contacts/query/all_contacts/async", QueryAllContactsAsyncData, service, set_up_query_all_contacts_async,
+		g_test_add ("/contacts/query/all_contacts/async", GDataAsyncTestData, service, set_up_query_all_contacts_async,
 		            test_query_all_contacts_async, tear_down_query_all_contacts_async);
-		g_test_add ("/contacts/query/all_contacts/async/progress_closure", QueryAllContactsAsyncData, service,
-		            set_up_query_all_contacts_async, test_query_all_contacts_async_progress_closure, tear_down_query_all_contacts_async);
+		g_test_add ("/contacts/query/all_contacts/async/progress_closure", QueryAllContactsData, service,
+		            set_up_query_all_contacts, test_query_all_contacts_async_progress_closure, tear_down_query_all_contacts);
+		g_test_add ("/contacts/query/all_contacts/cancellation", GDataAsyncTestData, service, set_up_query_all_contacts_async,
+		            test_query_all_contacts_async_cancellation, tear_down_query_all_contacts_async);
 
 		g_test_add_data_func ("/contacts/photo/has_photo", service, test_photo_has_photo);
 		g_test_add ("/contacts/photo/add", TempContactData, service, set_up_temp_contact, test_photo_add, tear_down_temp_contact);
-		g_test_add ("/contacts/photo/add/async", TempContactAsyncData, service, set_up_temp_contact_async, test_photo_add_async,
+		g_test_add ("/contacts/photo/add/async", GDataAsyncTestData, service, set_up_temp_contact_async, test_photo_add_async,
 		            tear_down_temp_contact_async);
+		g_test_add ("/contacts/photo/add/async/cancellation", GDataAsyncTestData, service, set_up_temp_contact_async,
+		            test_photo_add_async_cancellation, tear_down_temp_contact_async);
 		g_test_add ("/contacts/photo/get", TempContactData, service, set_up_temp_contact_with_photo, test_photo_get, tear_down_temp_contact);
-		g_test_add ("/contacts/photo/get/async", TempContactAsyncData, service, set_up_temp_contact_async_with_photo, test_photo_get_async,
-		            tear_down_temp_contact_async);
+		g_test_add ("/contacts/photo/get/async", GDataAsyncTestData, service, set_up_temp_contact_with_photo_async, test_photo_get_async,
+		            tear_down_temp_contact_with_photo_async);
+		g_test_add ("/contacts/photo/get/async/cancellation", GDataAsyncTestData, service, set_up_temp_contact_with_photo_async,
+		            test_photo_get_async_cancellation, tear_down_temp_contact_with_photo_async);
 		g_test_add ("/contacts/photo/delete", TempContactData, service, set_up_temp_contact_with_photo, test_photo_delete,
 		            tear_down_temp_contact);
-		g_test_add ("/contacts/photo/delete/async", TempContactAsyncData, service, set_up_temp_contact_async_with_photo,
-		            test_photo_delete_async, tear_down_temp_contact_async);
+		g_test_add ("/contacts/photo/delete/async", GDataAsyncTestData, service, set_up_temp_contact_with_photo_async,
+		            test_photo_delete_async, tear_down_temp_contact_with_photo_async);
+		g_test_add ("/contacts/photo/delete/async/cancellation", GDataAsyncTestData, service, set_up_temp_contact_with_photo_async,
+		            test_photo_delete_async_cancellation, tear_down_temp_contact_with_photo_async);
 
 		g_test_add_data_func ("/contacts/batch", service, test_batch);
 		g_test_add ("/contacts/batch/async", BatchAsyncData, service, setup_batch_async, test_batch_async, teardown_batch_async);
@@ -2521,14 +2434,18 @@ main (int argc, char *argv[])
 
 		g_test_add ("/contacts/group/query", QueryAllGroupsData, service, set_up_query_all_groups, test_query_all_groups,
 		            tear_down_query_all_groups);
-		g_test_add ("/contacts/group/query/async", QueryAllGroupsAsyncData, service, set_up_query_all_groups_async,
+		g_test_add ("/contacts/group/query/async", GDataAsyncTestData, service, set_up_query_all_groups_async,
 		            test_query_all_groups_async, tear_down_query_all_groups_async);
-		g_test_add ("/contacts/group/query/async/progress_closure", QueryAllGroupsAsyncData, service, set_up_query_all_groups_async,
-		            test_query_all_groups_async_progress_closure, tear_down_query_all_groups_async);
+		g_test_add ("/contacts/group/query/async/progress_closure", QueryAllGroupsData, service, set_up_query_all_groups,
+		            test_query_all_groups_async_progress_closure, tear_down_query_all_groups);
+		g_test_add ("/contacts/group/query/async/cancellation", GDataAsyncTestData, service, set_up_query_all_groups_async,
+		            test_query_all_groups_async_cancellation, tear_down_query_all_groups_async);
 
 		g_test_add ("/contacts/group/insert", InsertGroupData, service, set_up_insert_group, test_group_insert, tear_down_insert_group);
-		g_test_add ("/contacts/group/insert/async", InsertGroupAsyncData, service, set_up_insert_group_async, test_group_insert_async,
+		g_test_add ("/contacts/group/insert/async", GDataAsyncTestData, service, set_up_insert_group_async, test_group_insert_async,
 		            tear_down_insert_group_async);
+		g_test_add ("/contacts/group/insert/async/cancellation", GDataAsyncTestData, service, set_up_insert_group_async,
+		            test_group_insert_async_cancellation, tear_down_insert_group_async);
 	}
 
 	g_test_add_func ("/contacts/contact/properties", test_contact_properties);
diff --git a/gdata/tests/documents.c b/gdata/tests/documents.c
index 898b21b..db9ee1c 100644
--- a/gdata/tests/documents.c
+++ b/gdata/tests/documents.c
@@ -55,8 +55,8 @@ delete_entry (GDataDocumentsEntry *entry, GDataService *service)
 	                                              gdata_entry_get_id (GDATA_ENTRY (entry)), NULL, G_OBJECT_TYPE (entry), NULL, NULL);
 	g_assert (GDATA_IS_DOCUMENTS_ENTRY (new_entry));
 
-	/* Delete the entry */
-	g_assert (gdata_service_delete_entry (service, gdata_documents_service_get_primary_authorization_domain (), new_entry, NULL, NULL) == TRUE);
+	/* Delete the entry. Don't bother asserting that it succeeds, because it will often fail because Google keep giving us the wrong ETag above. */
+	gdata_service_delete_entry (service, gdata_documents_service_get_primary_authorization_domain (), new_entry, NULL, NULL);
 	g_object_unref (new_entry);
 }
 
@@ -90,49 +90,49 @@ test_authentication (void)
 	g_object_unref (authorizer);
 }
 
-static void
-test_authentication_async_cb (GDataClientLoginAuthorizer *authorizer, GAsyncResult *async_result, GMainLoop *main_loop)
-{
-	gboolean retval;
-	GError *error = NULL;
+GDATA_ASYNC_TEST_FUNCTIONS (authentication, void,
+G_STMT_START {
+	GDataClientLoginAuthorizer *authorizer;
 
-	retval = gdata_client_login_authorizer_authenticate_finish (authorizer, async_result, &error);
-	g_assert_no_error (error);
-	g_assert (retval == TRUE);
-	g_clear_error (&error);
+	/* Create an authorizer */
+	authorizer = gdata_client_login_authorizer_new (CLIENT_ID, GDATA_TYPE_DOCUMENTS_SERVICE);
 
-	g_main_loop_quit (main_loop);
+	g_assert_cmpstr (gdata_client_login_authorizer_get_client_id (authorizer), ==, CLIENT_ID);
 
-	/* Check all is as it should be */
-	g_assert_cmpstr (gdata_client_login_authorizer_get_username (authorizer), ==, USERNAME);
-	g_assert_cmpstr (gdata_client_login_authorizer_get_password (authorizer), ==, PASSWORD);
+	gdata_client_login_authorizer_authenticate_async (authorizer, USERNAME, PASSWORD, cancellable, async_ready_callback, async_data);
 
-	g_assert (gdata_authorizer_is_authorized_for_domain (GDATA_AUTHORIZER (authorizer),
-	                                                     gdata_documents_service_get_primary_authorization_domain ()) == TRUE);
-	g_assert (gdata_authorizer_is_authorized_for_domain (GDATA_AUTHORIZER (authorizer),
-	                                                     gdata_documents_service_get_spreadsheet_authorization_domain ()) == TRUE);
-}
+	g_object_unref (authorizer);
+} G_STMT_END,
+G_STMT_START {
+	gboolean retval;
+	GDataClientLoginAuthorizer *authorizer = GDATA_CLIENT_LOGIN_AUTHORIZER (obj);
 
-static void
-test_authentication_async (void)
-{
-	GMainLoop *main_loop;
-	GDataClientLoginAuthorizer *authorizer;
+	retval = gdata_client_login_authorizer_authenticate_finish (authorizer, async_result, &error);
 
-	/* Create an authorizer */
-	authorizer = gdata_client_login_authorizer_new (CLIENT_ID, GDATA_TYPE_DOCUMENTS_SERVICE);
+	if (error == NULL) {
+		g_assert (retval == TRUE);
 
-	g_assert_cmpstr (gdata_client_login_authorizer_get_client_id (authorizer), ==, CLIENT_ID);
+		/* Check all is as it should be */
+		g_assert_cmpstr (gdata_client_login_authorizer_get_username (authorizer), ==, USERNAME);
+		g_assert_cmpstr (gdata_client_login_authorizer_get_password (authorizer), ==, PASSWORD);
 
-	main_loop = g_main_loop_new (NULL, TRUE);
-	gdata_client_login_authorizer_authenticate_async (authorizer, USERNAME, PASSWORD, NULL,
-	                                                  (GAsyncReadyCallback) test_authentication_async_cb, main_loop);
+		g_assert (gdata_authorizer_is_authorized_for_domain (GDATA_AUTHORIZER (authorizer),
+		                                                     gdata_documents_service_get_primary_authorization_domain ()) == TRUE);
+		g_assert (gdata_authorizer_is_authorized_for_domain (GDATA_AUTHORIZER (authorizer),
+		                                                     gdata_documents_service_get_spreadsheet_authorization_domain ()) == TRUE);
+	} else {
+		g_assert (retval == FALSE);
 
-	g_main_loop_run (main_loop);
+		/* Check nothing's changed */
+		g_assert_cmpstr (gdata_client_login_authorizer_get_username (authorizer), ==, NULL);
+		g_assert_cmpstr (gdata_client_login_authorizer_get_password (authorizer), ==, NULL);
 
-	g_main_loop_unref (main_loop);
-	g_object_unref (authorizer);
-}
+		g_assert (gdata_authorizer_is_authorized_for_domain (GDATA_AUTHORIZER (authorizer),
+		                                                     gdata_documents_service_get_primary_authorization_domain ()) == FALSE);
+		g_assert (gdata_authorizer_is_authorized_for_domain (GDATA_AUTHORIZER (authorizer),
+		                                                     gdata_documents_service_get_spreadsheet_authorization_domain ()) == FALSE);
+	}
+} G_STMT_END);
 
 typedef struct {
 	GDataDocumentsFolder *folder;
@@ -413,53 +413,30 @@ test_query_all_documents (TempDocumentsData *data, gconstpointer service)
 	g_object_unref (feed);
 }
 
-typedef struct {
-	TempDocumentsData parent;
-	GMainLoop *main_loop;
-} TempDocumentsAsyncData;
+GDATA_ASYNC_CLOSURE_FUNCTIONS (temp_documents, TempDocumentsData);
 
-static void
-set_up_temp_documents_async (TempDocumentsAsyncData *data, gconstpointer service)
-{
-	set_up_temp_documents ((TempDocumentsData*) data, service);
-	data->main_loop = g_main_loop_new (NULL, FALSE);
-}
-
-static void
-tear_down_temp_documents_async (TempDocumentsAsyncData *data, gconstpointer service)
-{
-	g_main_loop_unref (data->main_loop);
-	tear_down_temp_documents ((TempDocumentsData*) data, service);
-}
-
-static void
-test_query_all_documents_async_cb (GDataService *service, GAsyncResult *async_result, TempDocumentsAsyncData *data)
-{
+GDATA_ASYNC_TEST_FUNCTIONS (query_all_documents, TempDocumentsData,
+G_STMT_START {
+	gdata_documents_service_query_documents_async (GDATA_DOCUMENTS_SERVICE (service), NULL, cancellable, NULL, NULL,
+	                                               NULL, async_ready_callback, async_data);
+} G_STMT_END,
+G_STMT_START {
 	GDataDocumentsFeed *feed;
-	GError *error = NULL;
-
-	feed = GDATA_DOCUMENTS_FEED (gdata_service_query_finish (GDATA_SERVICE (service), async_result, &error));
-	g_assert_no_error (error);
-	g_assert (GDATA_IS_FEED (feed));
-	g_clear_error (&error);
 
-	/* TODO: Tests? */
+	feed = GDATA_DOCUMENTS_FEED (gdata_service_query_finish (GDATA_SERVICE (obj), async_result, &error));
 
-	g_main_loop_quit (data->main_loop);
-	g_object_unref (feed);
-}
+	if (error == NULL) {
+		g_assert (GDATA_IS_FEED (feed));
+		/* TODO: Tests? */
 
-static void
-test_query_all_documents_async (TempDocumentsAsyncData *data, gconstpointer service)
-{
-	gdata_documents_service_query_documents_async (GDATA_DOCUMENTS_SERVICE (service), NULL, NULL, NULL, NULL,
-	                                               NULL, (GAsyncReadyCallback) test_query_all_documents_async_cb, data);
-
-	g_main_loop_run (data->main_loop);
-}
+		g_object_unref (feed);
+	} else {
+		g_assert (feed == NULL);
+	}
+} G_STMT_END);
 
 static void
-test_query_all_documents_async_progress_closure (TempDocumentsAsyncData *documents_data, gconstpointer service)
+test_query_all_documents_async_progress_closure (TempDocumentsData *documents_data, gconstpointer service)
 {
 	GDataAsyncProgressClosure *data = g_slice_new0 (GDataAsyncProgressClosure);
 
@@ -782,89 +759,31 @@ test_folders_add_to_folder (FoldersData *data, gconstpointer service)
 	g_object_unref (new_document);
 }
 
-typedef struct {
-	FoldersData data;
-	GMainLoop *main_loop;
-} FoldersAsyncData;
-
-static void
-set_up_folders_add_to_folder_async (FoldersAsyncData *data, gconstpointer service)
-{
-	set_up_folders_add_to_folder ((FoldersData*) data, service);
-	data->main_loop = g_main_loop_new (NULL, TRUE);
-}
-
-static void
-tear_down_folders_add_to_folder_async (FoldersAsyncData *data, gconstpointer service)
-{
-	g_main_loop_unref (data->main_loop);
-	tear_down_folders_add_to_folder ((FoldersData*) data, service);
-}
-
-static void
-test_folders_add_to_folder_async_cb (GDataDocumentsService *service, GAsyncResult *async_result, FoldersAsyncData *data)
-{
-	GDataDocumentsEntry *entry;
-	GError *error = NULL;
-
-	entry = gdata_documents_service_add_entry_to_folder_finish (service, async_result, &error);
-	g_assert_no_error (error);
-	g_assert (GDATA_IS_DOCUMENTS_ENTRY (entry));
-	g_clear_error (&error);
-
-	/* Check it's still the same document */
-	g_assert_cmpstr (gdata_entry_get_title (GDATA_ENTRY (entry)), ==, gdata_entry_get_title (GDATA_ENTRY (data->data.document)));
-	g_assert (check_document_is_in_folder (GDATA_DOCUMENTS_DOCUMENT (entry), data->data.folder) == TRUE);
-
-	g_object_unref (entry);
-
-	g_main_loop_quit (data->main_loop);
-}
+GDATA_ASYNC_CLOSURE_FUNCTIONS (folders_add_to_folder, FoldersData);
 
-static void
-test_folders_add_to_folder_async (FoldersAsyncData *data, gconstpointer service)
-{
+GDATA_ASYNC_TEST_FUNCTIONS (folders_add_to_folder, FoldersData,
+G_STMT_START {
 	/* Add the document to the folder asynchronously */
-	gdata_documents_service_add_entry_to_folder_async (GDATA_DOCUMENTS_SERVICE (service), GDATA_DOCUMENTS_ENTRY (data->data.document),
-	                                                   data->data.folder, NULL, (GAsyncReadyCallback) test_folders_add_to_folder_async_cb, data);
-	g_main_loop_run (data->main_loop);
-}
-
-static void
-test_folders_add_to_folder_cancellation_cb (GDataDocumentsService *service, GAsyncResult *async_result, FoldersAsyncData *data)
-{
+	gdata_documents_service_add_entry_to_folder_async (GDATA_DOCUMENTS_SERVICE (service), GDATA_DOCUMENTS_ENTRY (data->document),
+	                                                   data->folder, cancellable, async_ready_callback, async_data);
+} G_STMT_END,
+G_STMT_START {
 	GDataDocumentsEntry *entry;
-	GError *error = NULL;
-
-	entry = gdata_documents_service_add_entry_to_folder_finish (service, async_result, &error);
-	g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
-	g_assert (entry == NULL);
-	g_clear_error (&error);
 
-	g_main_loop_quit (data->main_loop);
-}
-
-static gboolean
-test_folders_add_to_folder_cancellation_cancel_cb (GCancellable *cancellable)
-{
-	g_cancellable_cancel (cancellable);
-	return FALSE;
-}
+	entry = gdata_documents_service_add_entry_to_folder_finish (GDATA_DOCUMENTS_SERVICE (obj), async_result, &error);
 
-static void
-test_folders_add_to_folder_cancellation (FoldersAsyncData *data, gconstpointer service)
-{
-	GCancellable *cancellable = g_cancellable_new ();
-	g_timeout_add (1, (GSourceFunc) test_folders_add_to_folder_cancellation_cancel_cb, cancellable);
+	if (error == NULL) {
+		g_assert (GDATA_IS_DOCUMENTS_ENTRY (entry));
 
-	/* Add the document to the folder asynchronously and cancel the operation after a few milliseconds */
-	gdata_documents_service_add_entry_to_folder_async (GDATA_DOCUMENTS_SERVICE (service), GDATA_DOCUMENTS_ENTRY (data->data.document),
-	                                                   data->data.folder, cancellable,
-	                                                   (GAsyncReadyCallback) test_folders_add_to_folder_cancellation_cb, data);
-	g_main_loop_run (data->main_loop);
+		/* Check it's still the same document */
+		g_assert_cmpstr (gdata_entry_get_title (GDATA_ENTRY (entry)), ==, gdata_entry_get_title (GDATA_ENTRY (data->document)));
+		g_assert (check_document_is_in_folder (GDATA_DOCUMENTS_DOCUMENT (entry), data->folder) == TRUE);
 
-	g_object_unref (cancellable);
-}
+		g_object_unref (entry);
+	} else {
+		g_assert (entry == NULL);
+	}
+} G_STMT_END);
 
 static void
 set_up_folders_remove_from_folder (FoldersData *data, gconstpointer service)
@@ -898,85 +817,31 @@ test_folders_remove_from_folder (FoldersData *data, gconstpointer service)
 	g_object_unref (new_document);
 }
 
-static void
-set_up_folders_remove_from_folder_async (FoldersAsyncData *data, gconstpointer service)
-{
-	set_up_folders_remove_from_folder ((FoldersData*) data, service);
-	data->main_loop = g_main_loop_new (NULL, TRUE);
-}
+GDATA_ASYNC_CLOSURE_FUNCTIONS (folders_remove_from_folder, FoldersData);
 
-static void
-tear_down_folders_remove_from_folder_async (FoldersAsyncData *data, gconstpointer service)
-{
-	g_main_loop_unref (data->main_loop);
-	tear_down_folders_remove_from_folder ((FoldersData*) data, service);
-}
-
-static void
-test_folders_remove_from_folder_async_cb (GDataDocumentsService *service, GAsyncResult *async_result, FoldersAsyncData *data)
-{
-	GDataDocumentsEntry *entry;
-	GError *error = NULL;
-
-	entry = gdata_documents_service_remove_entry_from_folder_finish (service, async_result, &error);
-	g_assert_no_error (error);
-	g_assert (GDATA_IS_DOCUMENTS_ENTRY (entry));
-	g_clear_error (&error);
-
-	/* Check it's still the same document */
-	g_assert_cmpstr (gdata_entry_get_title (GDATA_ENTRY (entry)), ==, gdata_entry_get_title (GDATA_ENTRY (data->data.document)));
-	g_assert (check_document_is_in_folder (GDATA_DOCUMENTS_DOCUMENT (entry), data->data.folder) == FALSE);
-
-	g_object_unref (entry);
-
-	g_main_loop_quit (data->main_loop);
-}
-
-static void
-test_folders_remove_from_folder_async (FoldersAsyncData *data, gconstpointer service)
-{
+GDATA_ASYNC_TEST_FUNCTIONS (folders_remove_from_folder, FoldersData,
+G_STMT_START {
 	/* Remove the document from the folder asynchronously */
-	gdata_documents_service_remove_entry_from_folder_async (GDATA_DOCUMENTS_SERVICE (service), GDATA_DOCUMENTS_ENTRY (data->data.document),
-	                                                        data->data.folder, NULL,
-	                                                        (GAsyncReadyCallback) test_folders_remove_from_folder_async_cb, data);
-	g_main_loop_run (data->main_loop);
-}
-
-static void
-test_folders_remove_from_folder_cancellation_cb (GDataDocumentsService *service, GAsyncResult *async_result, FoldersAsyncData *data)
-{
+	gdata_documents_service_remove_entry_from_folder_async (GDATA_DOCUMENTS_SERVICE (service), GDATA_DOCUMENTS_ENTRY (data->document),
+	                                                        data->folder, cancellable, async_ready_callback, async_data);
+} G_STMT_END,
+G_STMT_START {
 	GDataDocumentsEntry *entry;
-	GError *error = NULL;
 
-	entry = gdata_documents_service_remove_entry_from_folder_finish (service, async_result, &error);
-	g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
-	g_assert (entry == NULL);
-	g_clear_error (&error);
+	entry = gdata_documents_service_remove_entry_from_folder_finish (GDATA_DOCUMENTS_SERVICE (obj), async_result, &error);
 
-	g_main_loop_quit (data->main_loop);
-}
+	if (error == NULL) {
+		g_assert (GDATA_IS_DOCUMENTS_ENTRY (entry));
 
-static gboolean
-test_folders_remove_from_folder_cancellation_cancel_cb (GCancellable *cancellable)
-{
-	g_cancellable_cancel (cancellable);
-	return FALSE;
-}
+		/* Check it's still the same document */
+		g_assert_cmpstr (gdata_entry_get_title (GDATA_ENTRY (entry)), ==, gdata_entry_get_title (GDATA_ENTRY (data->document)));
+		g_assert (check_document_is_in_folder (GDATA_DOCUMENTS_DOCUMENT (entry), data->folder) == FALSE);
 
-static void
-test_folders_remove_from_folder_cancellation (FoldersAsyncData *data, gconstpointer service)
-{
-	GCancellable *cancellable = g_cancellable_new ();
-	g_timeout_add (1, (GSourceFunc) test_folders_remove_from_folder_cancellation_cancel_cb, cancellable);
-
-	/* Remove the document from the folder asynchronously and cancel the operation after a few milliseconds */
-	gdata_documents_service_remove_entry_from_folder_async (GDATA_DOCUMENTS_SERVICE (service), GDATA_DOCUMENTS_ENTRY (data->data.document),
-	                                                        data->data.folder, cancellable,
-	                                                        (GAsyncReadyCallback) test_folders_remove_from_folder_cancellation_cb, data);
-	g_main_loop_run (data->main_loop);
-
-	g_object_unref (cancellable);
-}
+		g_object_unref (entry);
+	} else {
+		g_assert (entry == NULL);
+	}
+} G_STMT_END);
 
 static void
 test_upload_file_metadata_in_new_folder (UploadDocumentData *data, gconstpointer service)
@@ -1584,7 +1449,10 @@ main (int argc, char *argv[])
 		service = GDATA_SERVICE (gdata_documents_service_new (authorizer));
 
 		g_test_add_func ("/documents/authentication", test_authentication);
-		g_test_add_func ("/documents/authentication/async", test_authentication_async);
+		g_test_add ("/documents/authentication/async", GDataAsyncTestData, NULL, gdata_set_up_async_test_data, test_authentication_async,
+		            gdata_tear_down_async_test_data);
+		g_test_add ("/documents/authentication/async/cancellation", GDataAsyncTestData, NULL, gdata_set_up_async_test_data,
+		            test_authentication_async_cancellation, gdata_tear_down_async_test_data);
 
 		g_test_add ("/documents/delete/document", TempDocumentData, service, set_up_temp_document_spreadsheet, test_delete_document,
 		            tear_down_temp_document);
@@ -1616,24 +1484,27 @@ main (int argc, char *argv[])
 		            tear_down_temp_documents);
 		g_test_add ("/documents/query/all_documents/with_folder", TempDocumentsData, service, set_up_temp_documents,
 		            test_query_all_documents_with_folder, tear_down_temp_documents);
-		g_test_add ("/documents/query/all_documents/async", TempDocumentsAsyncData, service, set_up_temp_documents_async,
+		g_test_add ("/documents/query/all_documents/async", GDataAsyncTestData, service, set_up_temp_documents_async,
 		            test_query_all_documents_async, tear_down_temp_documents_async);
-		g_test_add ("/documents/query/all_documents/async/progress_closure", TempDocumentsAsyncData, service, set_up_temp_documents_async,
-		            test_query_all_documents_async_progress_closure, tear_down_temp_documents_async);
+		g_test_add ("/documents/query/all_documents/async/progress_closure", TempDocumentsData, service, set_up_temp_documents,
+		            test_query_all_documents_async_progress_closure, tear_down_temp_documents);
+		g_test_add ("/documents/query/all_documents/async/cancellation", GDataAsyncTestData, service, set_up_temp_documents_async,
+		            test_query_all_documents_async_cancellation, tear_down_temp_documents_async);
 
 		g_test_add ("/documents/folders/add_to_folder", FoldersData, service, set_up_folders_add_to_folder,
 		            test_folders_add_to_folder, tear_down_folders_add_to_folder);
-		g_test_add ("/documents/folders/add_to_folder/async", FoldersAsyncData, service, set_up_folders_add_to_folder_async,
+		g_test_add ("/documents/folders/add_to_folder/async", GDataAsyncTestData, service, set_up_folders_add_to_folder_async,
 		            test_folders_add_to_folder_async, tear_down_folders_add_to_folder_async);
-		g_test_add ("/documents/folders/add_to_folder/cancellation", FoldersAsyncData, service, set_up_folders_add_to_folder_async,
-		            test_folders_add_to_folder_cancellation, tear_down_folders_add_to_folder_async);
+		g_test_add ("/documents/folders/add_to_folder/async/cancellation", GDataAsyncTestData, service, set_up_folders_add_to_folder_async,
+		            test_folders_add_to_folder_async_cancellation, tear_down_folders_add_to_folder_async);
 
 		g_test_add ("/documents/folders/remove_from_folder", FoldersData, service, set_up_folders_remove_from_folder,
 		            test_folders_remove_from_folder, tear_down_folders_remove_from_folder);
-		g_test_add ("/documents/folders/remove_from_folder/async", FoldersAsyncData, service, set_up_folders_remove_from_folder_async,
+		g_test_add ("/documents/folders/remove_from_folder/async", GDataAsyncTestData, service, set_up_folders_remove_from_folder_async,
 		            test_folders_remove_from_folder_async, tear_down_folders_remove_from_folder_async);
-		g_test_add ("/documents/folders/remove_from_folder/cancellation", FoldersAsyncData, service, set_up_folders_remove_from_folder_async,
-		            test_folders_remove_from_folder_cancellation, tear_down_folders_remove_from_folder_async);
+		g_test_add ("/documents/folders/remove_from_folder/async/cancellation", GDataAsyncTestData, service,
+		            set_up_folders_remove_from_folder_async, test_folders_remove_from_folder_async_cancellation,
+		            tear_down_folders_remove_from_folder_async);
 
 		g_test_add_data_func ("/documents/batch", service, test_batch);
 		g_test_add ("/documents/batch/async", BatchAsyncData, service, set_up_batch_async, test_batch_async, tear_down_batch_async);
diff --git a/gdata/tests/picasaweb.c b/gdata/tests/picasaweb.c
index bed1a8f..f1ef39b 100644
--- a/gdata/tests/picasaweb.c
+++ b/gdata/tests/picasaweb.c
@@ -360,47 +360,45 @@ test_authentication (void)
 	g_object_unref (authorizer);
 }
 
-static void
-test_authentication_async_cb (GDataClientLoginAuthorizer *authorizer, GAsyncResult *async_result, GMainLoop *main_loop)
-{
-	gboolean retval;
-	GError *error = NULL;
+GDATA_ASYNC_TEST_FUNCTIONS (authentication, void,
+G_STMT_START {
+	GDataClientLoginAuthorizer *authorizer;
 
-	retval = gdata_client_login_authorizer_authenticate_finish (authorizer, async_result, &error);
-	g_assert_no_error (error);
-	g_assert (retval == TRUE);
-	g_clear_error (&error);
+	/* Create an authorizer */
+	authorizer = gdata_client_login_authorizer_new (CLIENT_ID, GDATA_TYPE_PICASAWEB_SERVICE);
 
-	g_main_loop_quit (main_loop);
+	g_assert_cmpstr (gdata_client_login_authorizer_get_client_id (authorizer), ==, CLIENT_ID);
 
-	/* Check all is as it should be */
-	g_assert_cmpstr (gdata_client_login_authorizer_get_username (authorizer), ==, PW_USERNAME);
-	g_assert_cmpstr (gdata_client_login_authorizer_get_password (authorizer), ==, PASSWORD);
+	gdata_client_login_authorizer_authenticate_async (authorizer, PW_USERNAME, PASSWORD, cancellable, async_ready_callback, async_data);
 
-	g_assert (gdata_authorizer_is_authorized_for_domain (GDATA_AUTHORIZER (authorizer),
-	                                                     gdata_picasaweb_service_get_primary_authorization_domain ()) == TRUE);
-}
+	g_object_unref (authorizer);
+} G_STMT_END,
+G_STMT_START {
+	gboolean retval;
+	GDataClientLoginAuthorizer *authorizer = GDATA_CLIENT_LOGIN_AUTHORIZER (obj);
 
-static void
-test_authentication_async (void)
-{
-	GMainLoop *main_loop;
-	GDataClientLoginAuthorizer *authorizer;
+	retval = gdata_client_login_authorizer_authenticate_finish (authorizer, async_result, &error);
 
-	/* Create an authorizer */
-	authorizer = gdata_client_login_authorizer_new (CLIENT_ID, GDATA_TYPE_PICASAWEB_SERVICE);
+	if (error == NULL) {
+		g_assert (retval == TRUE);
 
-	g_assert_cmpstr (gdata_client_login_authorizer_get_client_id (authorizer), ==, CLIENT_ID);
+		/* Check all is as it should be */
+		g_assert_cmpstr (gdata_client_login_authorizer_get_username (authorizer), ==, PW_USERNAME);
+		g_assert_cmpstr (gdata_client_login_authorizer_get_password (authorizer), ==, PASSWORD);
 
-	main_loop = g_main_loop_new (NULL, TRUE);
-	gdata_client_login_authorizer_authenticate_async (authorizer, PW_USERNAME, PASSWORD, NULL,
-	                                                  (GAsyncReadyCallback) test_authentication_async_cb, main_loop);
+		g_assert (gdata_authorizer_is_authorized_for_domain (GDATA_AUTHORIZER (authorizer),
+		                                                     gdata_picasaweb_service_get_primary_authorization_domain ()) == TRUE);
+	} else {
+		g_assert (retval == FALSE);
 
-	g_main_loop_run (main_loop);
+		/* Check nothing's changed */
+		g_assert_cmpstr (gdata_client_login_authorizer_get_username (authorizer), ==, NULL);
+		g_assert_cmpstr (gdata_client_login_authorizer_get_password (authorizer), ==, NULL);
 
-	g_main_loop_unref (main_loop);
-	g_object_unref (authorizer);
-}
+		g_assert (gdata_authorizer_is_authorized_for_domain (GDATA_AUTHORIZER (authorizer),
+		                                                     gdata_picasaweb_service_get_primary_authorization_domain ()) == FALSE);
+	}
+} G_STMT_END);
 
 typedef struct {
 	GDataPicasaWebAlbum *album;
@@ -554,65 +552,39 @@ test_query_files (QueryFilesData *data, gconstpointer service)
 	g_object_unref (photo_feed);
 }
 
-typedef struct {
-	QueryFilesData data;
-	GMainLoop *main_loop;
-} QueryFilesAsyncData;
-
-static void
-set_up_query_files_async (QueryFilesAsyncData *data, gconstpointer service)
-{
-	set_up_query_files ((QueryFilesData*) data, service);
-	data->main_loop = g_main_loop_new (NULL, TRUE);
-}
-
-static void
-tear_down_query_files_async (QueryFilesAsyncData *data, gconstpointer service)
-{
-	g_main_loop_unref (data->main_loop);
-	tear_down_query_files ((QueryFilesData*) data, service);
-}
+GDATA_ASYNC_CLOSURE_FUNCTIONS (query_files, QueryFilesData);
 
-static void
-test_query_files_async_cb (GDataService *service, GAsyncResult *async_result, QueryFilesAsyncData *data)
-{
+/* Test that asynchronously querying for all photos in an album lists them correctly. */
+GDATA_ASYNC_TEST_FUNCTIONS (query_files, QueryFilesData,
+G_STMT_START {
+	gdata_picasaweb_service_query_files_async (GDATA_PICASAWEB_SERVICE (service), data->album, NULL, cancellable, NULL, NULL, NULL,
+	                                           async_ready_callback, async_data);
+} G_STMT_END,
+G_STMT_START {
 	GDataFeed *photo_feed;
-	GError *error = NULL;
 
 	/* Get the photo feed */
-	photo_feed = gdata_service_query_finish (service, async_result, &error);
-	g_assert_no_error (error);
-	g_clear_error (&error);
+	photo_feed = gdata_service_query_finish (GDATA_SERVICE (obj), async_result, &error);
 
-	_test_query_files (photo_feed, (QueryFilesData*) data);
-
-	g_object_unref (photo_feed);
-
-	g_main_loop_quit (data->main_loop);
-}
-
-
-/* Test that asynchronously querying for all photos in an album lists them correctly. */
-static void
-test_query_files_async (QueryFilesAsyncData *data, gconstpointer service)
-{
-	gdata_picasaweb_service_query_files_async (GDATA_PICASAWEB_SERVICE (service), data->data.album, NULL, NULL, NULL, NULL, NULL,
-	                                           (GAsyncReadyCallback) test_query_files_async_cb, data);
-
-	g_main_loop_run (data->main_loop);
-}
+	if (error == NULL) {
+		_test_query_files (photo_feed, data);
+		g_object_unref (photo_feed);
+	} else {
+		g_assert (photo_feed == NULL);
+	}
+} G_STMT_END);
 
 /* Test that the progress callbacks from gdata_picasaweb_service_query_files_async() are called correctly.
- * We take a QueryFilesAsyncData so that we can guarantee the album and at least one file exists (since it's created in the setup function for
- * QueryFilesAsyncData), but we don't use it much as we don't actually care about the specific files. */
+ * We take a QueryFilesData so that we can guarantee the album and at least one file exists (since it's created in the setup function for
+ * QueryFilesData), but we don't use it much as we don't actually care about the specific files. */
 static void
-test_query_files_async_progress_closure (QueryFilesAsyncData *query_data, gconstpointer service)
+test_query_files_async_progress_closure (QueryFilesData *query_data, gconstpointer service)
 {
 	GDataAsyncProgressClosure *data = g_slice_new0 (GDataAsyncProgressClosure);
 
 	data->main_loop = g_main_loop_new (NULL, TRUE);
 
-	gdata_picasaweb_service_query_files_async (GDATA_PICASAWEB_SERVICE (service), query_data->data.album, NULL, NULL,
+	gdata_picasaweb_service_query_files_async (GDATA_PICASAWEB_SERVICE (service), query_data->album, NULL, NULL,
 	                                           (GDataQueryProgressCallback) gdata_test_async_progress_callback,
 	                                           data, (GDestroyNotify) gdata_test_async_progress_closure_free,
 	                                           (GAsyncReadyCallback) gdata_test_async_progress_finish_callback, data);
@@ -830,54 +802,29 @@ test_insert_album (InsertAlbumData *data, gconstpointer service)
 	g_object_unref (inserted_album);
 }
 
-typedef struct {
-	InsertAlbumData data;
-	GMainLoop *main_loop;
-} InsertAlbumAsyncData;
-
-static void
-set_up_insert_album_async (InsertAlbumAsyncData *data, gconstpointer service)
-{
-	set_up_insert_album ((InsertAlbumData*) data, service);
-	data->main_loop = g_main_loop_new (NULL, TRUE);
-}
-
-static void
-tear_down_insert_album_async (InsertAlbumAsyncData *data, gconstpointer service)
-{
-	g_main_loop_unref (data->main_loop);
-	tear_down_insert_album ((InsertAlbumData*) data, service);
-}
+GDATA_ASYNC_CLOSURE_FUNCTIONS (insert_album, InsertAlbumData);
 
-static void
-test_insert_album_async_cb (GDataService *service, GAsyncResult *async_result, InsertAlbumAsyncData *data)
-{
+GDATA_ASYNC_TEST_FUNCTIONS (insert_album, InsertAlbumData,
+G_STMT_START {
+	gdata_picasaweb_service_insert_album_async (GDATA_PICASAWEB_SERVICE (service), data->album, cancellable,
+	                                            async_ready_callback, async_data);
+} G_STMT_END,
+G_STMT_START {
 	GDataEntry *entry;
-	GError *error = NULL;
-
-	entry = gdata_service_insert_entry_finish (service, async_result, &error);
-	g_assert_no_error (error);
-	g_assert (GDATA_IS_PICASAWEB_ALBUM (entry));
-	g_clear_error (&error);
-
-	data->data.inserted_album = g_object_ref (entry);
-
-	/* Test the album was uploaded correctly */
-	assert_albums_equal (GDATA_PICASAWEB_ALBUM (entry), data->data.album, FALSE);
 
-	g_object_unref (entry);
+	entry = gdata_service_insert_entry_finish (GDATA_SERVICE (obj), async_result, &error);
 
-	g_main_loop_quit (data->main_loop);
-}
+	if (error == NULL) {
+		g_assert (GDATA_IS_PICASAWEB_ALBUM (entry));
 
-static void
-test_insert_album_async (InsertAlbumAsyncData *data, gconstpointer service)
-{
-	gdata_picasaweb_service_insert_album_async (GDATA_PICASAWEB_SERVICE (service), data->data.album, NULL,
-	                                            (GAsyncReadyCallback) test_insert_album_async_cb, data);
+		/* Test the album was uploaded correctly */
+		assert_albums_equal (GDATA_PICASAWEB_ALBUM (entry), data->album, FALSE);
 
-	g_main_loop_run (data->main_loop);
-}
+		data->inserted_album = GDATA_PICASAWEB_ALBUM (entry);
+	} else {
+		g_assert (entry == NULL);
+	}
+} G_STMT_END);
 
 typedef struct {
 	GDataPicasaWebAlbum *album1;
@@ -1080,58 +1027,34 @@ test_query_all_albums_with_limits (QueryAllAlbumsData *data, gconstpointer servi
 	g_object_unref (album_feed_1);
 }
 
-typedef struct {
-	QueryAllAlbumsData data;
-	GMainLoop *main_loop;
-} QueryAllAlbumsAsyncData;
-
-static void
-set_up_query_all_albums_async (QueryAllAlbumsAsyncData *data, gconstpointer service)
-{
-	set_up_query_all_albums ((QueryAllAlbumsData*) data, service);
-	data->main_loop = g_main_loop_new (NULL, TRUE);
-}
+GDATA_ASYNC_CLOSURE_FUNCTIONS (query_all_albums, QueryAllAlbumsData);
 
-static void
-tear_down_query_all_albums_async (QueryAllAlbumsAsyncData *data, gconstpointer service)
-{
-	g_main_loop_unref (data->main_loop);
-	tear_down_query_all_albums ((QueryAllAlbumsData*) data, service);
-}
-
-static void
-test_query_all_albums_async_cb (GDataService *service, GAsyncResult *async_result, QueryAllAlbumsAsyncData *data)
-{
+/* Test that asynchronously querying for all albums lists them correctly. */
+GDATA_ASYNC_TEST_FUNCTIONS (query_all_albums, QueryAllAlbumsData,
+G_STMT_START {
+	gdata_picasaweb_service_query_all_albums_async (GDATA_PICASAWEB_SERVICE (service), NULL, NULL, cancellable, NULL,
+	                                                NULL, NULL, async_ready_callback, async_data);
+} G_STMT_END,
+G_STMT_START {
 	GDataFeed *album_feed;
-	GError *error = NULL;
 
 	/* Get the album feed */
-	album_feed = gdata_service_query_finish (service, async_result, &error);
-	g_assert_no_error (error);
-	g_clear_error (&error);
-
-	_test_query_all_albums (album_feed, (QueryAllAlbumsData*) data);
-
-	g_object_unref (album_feed);
-
-	g_main_loop_quit (data->main_loop);
-}
+	album_feed = gdata_service_query_finish (GDATA_SERVICE (obj), async_result, &error);
 
-/* Test that asynchronously querying for all albums lists them correctly. */
-static void
-test_query_all_albums_async (QueryAllAlbumsAsyncData *data, gconstpointer service)
-{
-	gdata_picasaweb_service_query_all_albums_async (GDATA_PICASAWEB_SERVICE (service), NULL, NULL, NULL, NULL,
-	                                                NULL, NULL, (GAsyncReadyCallback) test_query_all_albums_async_cb, data);
+	if (error == NULL) {
+		_test_query_all_albums (album_feed, (QueryAllAlbumsData*) data);
 
-	g_main_loop_run (data->main_loop);
-}
+		g_object_unref (album_feed);
+	} else {
+		g_assert (album_feed == NULL);
+	}
+} G_STMT_END);
 
 /* Test that the progress callbacks from gdata_picasaweb_service_query_all_albums_async() are called correctly.
- * We take a QueryAllAlbumsAsyncData so that we can guarantee at least one album exists (since it's created in the setup function for
- * QueryAllAlbumsAsyncData), but we don't use it as we don't actually care about the specific album. */
+ * We take a QueryAllAlbumsData so that we can guarantee at least one album exists (since it's created in the setup function for
+ * QueryAllAlbumsData), but we don't use it as we don't actually care about the specific album. */
 static void
-test_query_all_albums_async_progress_closure (QueryAllAlbumsAsyncData *unused_data, gconstpointer service)
+test_query_all_albums_async_progress_closure (QueryAllAlbumsData *unused_data, gconstpointer service)
 {
 	GDataAsyncProgressClosure *data = g_slice_new0 (GDataAsyncProgressClosure);
 
@@ -1211,20 +1134,20 @@ tear_down_query_comments (QueryCommentsData *data, gconstpointer service)
 {
 	/* Delete the test comments. */
 	if (data->comment1 != NULL) {
-		g_assert (gdata_commentable_delete_comment (GDATA_COMMENTABLE (data->parent.file1), GDATA_SERVICE (service),
-		                                            GDATA_COMMENT (data->comment1), NULL, NULL) == TRUE);
+		gdata_commentable_delete_comment (GDATA_COMMENTABLE (data->parent.file1), GDATA_SERVICE (service),
+		                                  GDATA_COMMENT (data->comment1), NULL, NULL);
 		g_object_unref (data->comment1);
 	}
 
 	if (data->comment2 != NULL) {
-		g_assert (gdata_commentable_delete_comment (GDATA_COMMENTABLE (data->parent.file1), GDATA_SERVICE (service),
-		                                            GDATA_COMMENT (data->comment2), NULL, NULL) == TRUE);
+		gdata_commentable_delete_comment (GDATA_COMMENTABLE (data->parent.file1), GDATA_SERVICE (service),
+		                                  GDATA_COMMENT (data->comment2), NULL, NULL);
 		g_object_unref (data->comment2);
 	}
 
 	if (data->comment3 != NULL) {
-		g_assert (gdata_commentable_delete_comment (GDATA_COMMENTABLE (data->parent.file1), GDATA_SERVICE (service),
-		                                            GDATA_COMMENT (data->comment3), NULL, NULL) == TRUE);
+		gdata_commentable_delete_comment (GDATA_COMMENTABLE (data->parent.file1), GDATA_SERVICE (service),
+		                                  GDATA_COMMENT (data->comment3), NULL, NULL);
 		g_object_unref (data->comment3);
 	}
 
@@ -1295,92 +1218,40 @@ test_comment_query (QueryCommentsData *data, gconstpointer service)
 	g_object_unref (comments_feed);
 }
 
-typedef struct {
-	QueryCommentsData parent;
-	GMainLoop *main_loop;
-} QueryCommentsAsyncData;
-
-static void
-set_up_query_comments_async (QueryCommentsAsyncData *data, gconstpointer service)
-{
-	set_up_query_comments ((QueryCommentsData*) data, service);
-	data->main_loop = g_main_loop_new (NULL, FALSE);
-}
-
-static void
-tear_down_query_comments_async (QueryCommentsAsyncData *data, gconstpointer service)
-{
-	g_main_loop_unref (data->main_loop);
-	tear_down_query_comments ((QueryCommentsData*) data, service);
-}
-
-static void
-test_comment_query_async_cb (GDataCommentable *commentable, GAsyncResult *result, QueryCommentsAsyncData *data)
-{
-	GDataFeed *comments_feed;
-	GError *error = NULL;
-
-	comments_feed = gdata_commentable_query_comments_finish (commentable, result, &error);
-	g_assert_no_error (error);
-	g_clear_error (&error);
-
-	assert_comments_feed ((QueryCommentsData*) data, comments_feed);
-
-	g_object_unref (comments_feed);
-
-	g_main_loop_quit (data->main_loop);
-}
-
-static void
-test_comment_query_async (QueryCommentsAsyncData *data, gconstpointer service)
-{
-	gdata_commentable_query_comments_async (GDATA_COMMENTABLE (data->parent.parent.file1), GDATA_SERVICE (service), NULL, NULL, NULL, NULL, NULL,
-	                                        (GAsyncReadyCallback) test_comment_query_async_cb, data);
-
-	g_main_loop_run (data->main_loop);
-}
+GDATA_ASYNC_CLOSURE_FUNCTIONS (query_comments, QueryCommentsData);
 
-static void
-test_comment_query_async_cancellation_cb (GDataCommentable *commentable, GAsyncResult *result, QueryCommentsAsyncData *data)
-{
+/* Test that asynchronously querying for all albums lists them correctly. */
+GDATA_ASYNC_TEST_FUNCTIONS (comment_query, QueryCommentsData,
+G_STMT_START {
+	gdata_commentable_query_comments_async (GDATA_COMMENTABLE (data->parent.file1), GDATA_SERVICE (service), NULL, cancellable, NULL, NULL, NULL,
+	                                        async_ready_callback, async_data);
+} G_STMT_END,
+G_STMT_START {
 	GDataFeed *comments_feed;
-	GError *error = NULL;
-
-	comments_feed = gdata_commentable_query_comments_finish (commentable, result, &error);
-	g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
-	g_assert (comments_feed == NULL);
-	g_clear_error (&error);
-
-	g_main_loop_quit (data->main_loop);
-}
-
-static void
-test_comment_query_async_cancellation (QueryCommentsAsyncData *data, gconstpointer service)
-{
-	GCancellable *cancellable;
-
-	cancellable = g_cancellable_new ();
-	g_cancellable_cancel (cancellable);
 
-	gdata_commentable_query_comments_async (GDATA_COMMENTABLE (data->parent.parent.file1), GDATA_SERVICE (service), NULL, cancellable, NULL, NULL,
-	                                        NULL, (GAsyncReadyCallback) test_comment_query_async_cancellation_cb, data);
+	/* Get the comments feed */
+	comments_feed = gdata_commentable_query_comments_finish (GDATA_COMMENTABLE (obj), async_result, &error);
 
-	g_main_loop_run (data->main_loop);
+	if (error == NULL) {
+		assert_comments_feed (data, comments_feed);
 
-	g_object_unref (cancellable);
-}
+		g_object_unref (comments_feed);
+	} else {
+		g_assert (comments_feed == NULL);
+	}
+} G_STMT_END);
 
 /* Test that the progress callbacks from gdata_commentable_query_comments_async() are called correctly.
- * We take a QueryCommentsAsyncData so that we can guarantee the file exists, but we don't use it much as we don't actually care about the specific
+ * We take a QueryCommentsData so that we can guarantee the file exists, but we don't use it much as we don't actually care about the specific
  * file. */
 static void
-test_comment_query_async_progress_closure (QueryCommentsAsyncData *query_data, gconstpointer service)
+test_comment_query_async_progress_closure (QueryCommentsData *query_data, gconstpointer service)
 {
 	GDataAsyncProgressClosure *data = g_slice_new0 (GDataAsyncProgressClosure);
 
 	data->main_loop = g_main_loop_new (NULL, TRUE);
 
-	gdata_commentable_query_comments_async (GDATA_COMMENTABLE (query_data->parent.parent.file1), GDATA_SERVICE (service), NULL, NULL,
+	gdata_commentable_query_comments_async (GDATA_COMMENTABLE (query_data->parent.file1), GDATA_SERVICE (service), NULL, NULL,
 	                                        (GDataQueryProgressCallback) gdata_test_async_progress_callback,
 	                                        data, (GDestroyNotify) gdata_test_async_progress_closure_free,
 	                                        (GAsyncReadyCallback) gdata_test_async_progress_finish_callback, data);
@@ -1470,81 +1341,26 @@ test_comment_insert (InsertCommentData *data, gconstpointer service)
 	data->new_comment = GDATA_PICASAWEB_COMMENT (new_comment);
 }
 
-typedef struct {
-	InsertCommentData parent;
-	GMainLoop *main_loop;
-} InsertCommentAsyncData;
-
-static void
-set_up_insert_comment_async (InsertCommentAsyncData *data, gconstpointer service)
-{
-	set_up_insert_comment ((InsertCommentData*) data, service);
-	data->main_loop = g_main_loop_new (NULL, FALSE);
-}
-
-static void
-tear_down_insert_comment_async (InsertCommentAsyncData *data, gconstpointer service)
-{
-	g_main_loop_unref (data->main_loop);
-	tear_down_insert_comment ((InsertCommentData*) data, service);
-}
-
-static void
-test_comment_insert_async_cb (GDataCommentable *commentable, GAsyncResult *result, InsertCommentAsyncData *data)
-{
-	GDataComment *new_comment;
-	GError *error = NULL;
-
-	new_comment = gdata_commentable_insert_comment_finish (commentable, result, &error);
-	g_assert_no_error (error);
-	g_clear_error (&error);
+GDATA_ASYNC_CLOSURE_FUNCTIONS (insert_comment, InsertCommentData);
 
-	assert_comments_equal (new_comment, data->parent.comment);
-
-	data->parent.new_comment = GDATA_PICASAWEB_COMMENT (new_comment);
-
-	g_main_loop_quit (data->main_loop);
-}
-
-static void
-test_comment_insert_async (InsertCommentAsyncData *data, gconstpointer service)
-{
-	gdata_commentable_insert_comment_async (GDATA_COMMENTABLE (data->parent.parent.file1), GDATA_SERVICE (service),
-	                                        GDATA_COMMENT (data->parent.comment), NULL, (GAsyncReadyCallback) test_comment_insert_async_cb, data);
-
-	g_main_loop_run (data->main_loop);
-}
-
-static void
-test_comment_insert_async_cancellation_cb (GDataCommentable *commentable, GAsyncResult *result, InsertCommentAsyncData *data)
-{
+GDATA_ASYNC_TEST_FUNCTIONS (comment_insert, InsertCommentData,
+G_STMT_START {
+	gdata_commentable_insert_comment_async (GDATA_COMMENTABLE (data->parent.file1), GDATA_SERVICE (service),
+	                                        GDATA_COMMENT (data->comment), cancellable, async_ready_callback, async_data);
+} G_STMT_END,
+G_STMT_START {
 	GDataComment *new_comment;
-	GError *error = NULL;
-
-	new_comment = gdata_commentable_insert_comment_finish (commentable, result, &error);
-	g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
-	g_assert (new_comment == NULL);
-	g_clear_error (&error);
 
-	g_main_loop_quit (data->main_loop);
-}
-
-static void
-test_comment_insert_async_cancellation (InsertCommentAsyncData *data, gconstpointer service)
-{
-	GCancellable *cancellable;
+	new_comment = gdata_commentable_insert_comment_finish (GDATA_COMMENTABLE (obj), async_result, &error);
 
-	cancellable = g_cancellable_new ();
-	g_cancellable_cancel (cancellable);
+	if (error == NULL) {
+		assert_comments_equal (new_comment, data->comment);
 
-	gdata_commentable_insert_comment_async (GDATA_COMMENTABLE (data->parent.parent.file1), GDATA_SERVICE (service),
-	                                        GDATA_COMMENT (data->parent.comment), cancellable,
-	                                        (GAsyncReadyCallback) test_comment_insert_async_cancellation_cb, data);
-
-	g_main_loop_run (data->main_loop);
-
-	g_object_unref (cancellable);
-}
+		data->new_comment = GDATA_PICASAWEB_COMMENT (new_comment);
+	} else {
+		g_assert (new_comment == NULL);
+	}
+} G_STMT_END);
 
 static void
 test_comment_delete (QueryCommentsData *data, gconstpointer service)
@@ -1562,61 +1378,34 @@ test_comment_delete (QueryCommentsData *data, gconstpointer service)
 	data->comment1 = NULL;
 }
 
-static void
-test_comment_delete_async_cb (GDataCommentable *commentable, GAsyncResult *result, QueryCommentsAsyncData *data)
-{
+GDATA_ASYNC_TEST_FUNCTIONS (comment_delete, QueryCommentsData,
+G_STMT_START {
+	gdata_commentable_delete_comment_async (GDATA_COMMENTABLE (data->parent.file1), GDATA_SERVICE (service),
+	                                        GDATA_COMMENT (data->comment1), cancellable, async_ready_callback, async_data);
+} G_STMT_END,
+G_STMT_START {
 	gboolean success;
-	GError *error = NULL;
-
-	success = gdata_commentable_delete_comment_finish (commentable, result, &error);
-	g_assert_no_error (error);
-	g_assert (success == TRUE);
-	g_clear_error (&error);
 
-	g_object_unref (data->parent.comment1);
-	data->parent.comment1 = NULL;
+	success = gdata_commentable_delete_comment_finish (GDATA_COMMENTABLE (obj), async_result, &error);
 
-	g_main_loop_quit (data->main_loop);
-}
-
-static void
-test_comment_delete_async (QueryCommentsAsyncData *data, gconstpointer service)
-{
-	gdata_commentable_delete_comment_async (GDATA_COMMENTABLE (data->parent.parent.file1), GDATA_SERVICE (service),
-	                                        GDATA_COMMENT (data->parent.comment1), NULL, (GAsyncReadyCallback) test_comment_delete_async_cb, data);
+	if (error == NULL) {
+		g_assert (success == TRUE);
 
-	g_main_loop_run (data->main_loop);
-}
-
-static void
-test_comment_delete_async_cancellation_cb (GDataCommentable *commentable, GAsyncResult *result, QueryCommentsAsyncData *data)
-{
-	gboolean success;
-	GError *error = NULL;
-
-	success = gdata_commentable_delete_comment_finish (commentable, result, &error);
-	g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
-	g_assert (success == FALSE);
-	g_clear_error (&error);
-
-	g_main_loop_quit (data->main_loop);
-}
-
-static void
-test_comment_delete_async_cancellation (QueryCommentsAsyncData *data, gconstpointer service)
-{
-	GCancellable *cancellable;
-
-	cancellable = g_cancellable_new ();
-	g_cancellable_cancel (cancellable);
-	gdata_commentable_delete_comment_async (GDATA_COMMENTABLE (data->parent.parent.file1), GDATA_SERVICE (service),
-	                                        GDATA_COMMENT (data->parent.comment1), cancellable,
-	                                        (GAsyncReadyCallback) test_comment_delete_async_cancellation_cb, data);
-
-	g_main_loop_run (data->main_loop);
-
-	g_object_unref (cancellable);
-}
+		/* Prevent the closure tear down function from trying to delete the comment again */
+		g_object_unref (data->comment1);
+		data->comment1 = NULL;
+	} else {
+		g_assert (success == FALSE);
+
+		/* The server's naughty and often deletes comments even if the connection's closed prematurely (when we cancel the operation). In
+		 * this case, it returns an error 400, which we sneakily hide. */
+		if (g_error_matches (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_PROTOCOL_ERROR) == TRUE &&
+		    async_data->cancellation_timeout > 0) {
+			async_data->cancellation_successful = FALSE;
+			g_clear_error (&error);
+		}
+	}
+} G_STMT_END);
 
 static void
 test_query_user (gconstpointer service)
@@ -1633,113 +1422,53 @@ test_query_user (gconstpointer service)
 	g_object_unref (user);
 }
 
-typedef struct {
-	GMainLoop *main_loop;
-} QueryUserAsyncData;
-
-static void
-set_up_query_user_async (QueryUserAsyncData *data, gconstpointer service)
-{
-	data->main_loop = g_main_loop_new (NULL, FALSE);
-}
-
-static void
-tear_down_query_user_async (QueryUserAsyncData *data, gconstpointer service)
-{
-	g_main_loop_unref (data->main_loop);
-}
-
-static void
-test_query_user_async_cb (GDataPicasaWebService *service, GAsyncResult *result, QueryUserAsyncData *data)
-{
-	GDataPicasaWebUser *user;
-	GError *error = NULL;
-
-	user = gdata_picasaweb_service_get_user_finish (service, result, &error);
-	g_assert_no_error (error);
-	g_clear_error (&error);
-
-	check_authenticated_user_details (user);
-
-	g_object_unref (user);
-
-	g_main_loop_quit (data->main_loop);
-}
-
 /* Check that asynchronously querying for the currently authenticated user's details works and returns the correct details. */
-static void
-test_query_user_async (QueryUserAsyncData *data, gconstpointer service)
-{
-	gdata_picasaweb_service_get_user_async (GDATA_PICASAWEB_SERVICE (service), NULL, NULL, (GAsyncReadyCallback) test_query_user_async_cb, data);
-
-	g_main_loop_run (data->main_loop);
-}
-
-static void
-test_query_user_async_cancellable_cb (GDataPicasaWebService *service, GAsyncResult *result, QueryUserAsyncData *data)
-{
+GDATA_ASYNC_TEST_FUNCTIONS (query_user, void,
+G_STMT_START {
+	gdata_picasaweb_service_get_user_async (GDATA_PICASAWEB_SERVICE (service), NULL, cancellable, async_ready_callback, async_data);
+} G_STMT_END,
+G_STMT_START {
 	GDataPicasaWebUser *user;
-	GError *error = NULL;
-
-	user = gdata_picasaweb_service_get_user_finish (service, result, &error);
-	g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
-	g_assert (user == NULL);
-	g_clear_error (&error);
-
-	g_main_loop_quit (data->main_loop);
-}
-
-/* Check that cancelling an asynchronous query for the currently authenticated user's details works. */
-static void
-test_query_user_async_cancellation (QueryUserAsyncData *data, gconstpointer service)
-{
-	GCancellable *cancellable;
 
-	cancellable = g_cancellable_new ();
-	g_cancellable_cancel (cancellable);
-	gdata_picasaweb_service_get_user_async (GDATA_PICASAWEB_SERVICE (service), NULL, cancellable,
-	                                        (GAsyncReadyCallback) test_query_user_async_cancellable_cb, data);
+	user = gdata_picasaweb_service_get_user_finish (GDATA_PICASAWEB_SERVICE (obj), async_result, &error);
 
-	g_main_loop_run (data->main_loop);
+	if (error == NULL) {
+		check_authenticated_user_details (user);
 
-	g_object_unref (cancellable);
-}
+		g_object_unref (user);
+	} else {
+		g_assert (user == NULL);
+	}
+} G_STMT_END);
 
-static void
-test_query_user_by_username_async_cb (GDataPicasaWebService *service, GAsyncResult *result, QueryUserAsyncData *data)
-{
+/* Check that querying for a user other than the currently authenticated user, asynchronously, gives us an appropriate result. This result should,
+ * for example, not contain any private information about the queried user. (That's a server-side consideration, but libgdata has to handle the
+ * lack of information correctly.) */
+GDATA_ASYNC_TEST_FUNCTIONS (query_user_by_username, void,
+G_STMT_START {
+	gdata_picasaweb_service_get_user_async (GDATA_PICASAWEB_SERVICE (service), "philip.withnall", cancellable, async_ready_callback, async_data);
+} G_STMT_END,
+G_STMT_START {
 	GDataPicasaWebUser *user;
-	GError *error = NULL;
 
-	user = gdata_picasaweb_service_get_user_finish (service, result, &error);
-	g_assert_no_error (error);
-	g_assert (GDATA_IS_PICASAWEB_USER (user));
-	g_clear_error (&error);
-
-	g_assert_cmpstr (gdata_picasaweb_user_get_user (user), ==, "philip.withnall");
-	g_assert_cmpstr (gdata_picasaweb_user_get_nickname (user), ==, "Philip");
-	g_assert_cmpint (gdata_picasaweb_user_get_quota_limit (user), ==, -1); /* not the logged in user */
-	g_assert_cmpint (gdata_picasaweb_user_get_quota_current (user), ==, -1); /* not the logged in user */
-	g_assert_cmpint (gdata_picasaweb_user_get_max_photos_per_album (user), ==, -1); /* not the logged in user */
-	/* tested weakly to avoid having to update it regularly */
-	g_assert_cmpstr (gdata_picasaweb_user_get_thumbnail_uri (user), !=, NULL);
-
-	g_object_unref (user);
+	user = gdata_picasaweb_service_get_user_finish (GDATA_PICASAWEB_SERVICE (obj), async_result, &error);
 
-	g_main_loop_quit (data->main_loop);
-}
+	if (error == NULL) {
+		g_assert (GDATA_IS_PICASAWEB_USER (user));
 
-/* Check that querying for a user other than the currently authenticated user, asynchronously, gives us an appropriate result. This result should,
- * for example, not contain any private information about the queried user. (That's a server-side consideration, but libgdata has to handle the
- * lack of information correctly.) */
-static void
-test_query_user_by_username_async (QueryUserAsyncData *data, gconstpointer service)
-{
-	gdata_picasaweb_service_get_user_async (GDATA_PICASAWEB_SERVICE (service), "philip.withnall", NULL,
-	                                        (GAsyncReadyCallback) test_query_user_by_username_async_cb, data);
+		g_assert_cmpstr (gdata_picasaweb_user_get_user (user), ==, "104200312198892774147");
+		g_assert_cmpstr (gdata_picasaweb_user_get_nickname (user), ==, "Philip Withnall");
+		g_assert_cmpint (gdata_picasaweb_user_get_quota_limit (user), ==, -1); /* not the logged in user */
+		g_assert_cmpint (gdata_picasaweb_user_get_quota_current (user), ==, -1); /* not the logged in user */
+		g_assert_cmpint (gdata_picasaweb_user_get_max_photos_per_album (user), ==, -1); /* not the logged in user */
+		/* tested weakly to avoid having to update it regularly */
+		g_assert_cmpstr (gdata_picasaweb_user_get_thumbnail_uri (user), !=, NULL);
 
-	g_main_loop_run (data->main_loop);
-}
+		g_object_unref (user);
+	} else {
+		g_assert (user == NULL);
+	}
+} G_STMT_END);
 
 typedef struct {
 	GDataPicasaWebService *service;
@@ -1752,7 +1481,7 @@ typedef struct {
 } UploadData;
 
 static void
-setup_upload (UploadData *data, gconstpointer service)
+set_up_upload (UploadData *data, gconstpointer service)
 {
 	GFileInfo *file_info;
 	const gchar * const tags[] = { "foo", "bar", ",,baz,baz", NULL };
@@ -1788,7 +1517,7 @@ setup_upload (UploadData *data, gconstpointer service)
 }
 
 static void
-teardown_upload (UploadData *data, gconstpointer service)
+tear_down_upload (UploadData *data, gconstpointer service)
 {
 	/* Delete the uploaded photo (don't worry if this fails) */
 	if (data->updated_photo != NULL) {
@@ -1844,158 +1573,69 @@ test_upload_default_album (UploadData *data, gconstpointer service)
 	g_assert_cmpstr (tags2[2], ==, tags[2]);
 }
 
-typedef struct {
-	UploadData data;
-	GMainLoop *main_loop;
-} UploadAsyncData;
-
-static void
-setup_upload_async (UploadAsyncData *data, gconstpointer service)
-{
-	setup_upload ((UploadData*) data, service);
-	data->main_loop = g_main_loop_new (NULL, TRUE);
-}
-
-static void
-teardown_upload_async (UploadAsyncData *data, gconstpointer service)
-{
-	g_main_loop_unref (data->main_loop);
-	teardown_upload ((UploadData*) data, service);
-}
-
-static void
-test_upload_default_album_async_cb (GOutputStream *stream, GAsyncResult *result, UploadAsyncData *data)
-{
-	const gchar * const *tags, * const *tags2;
-	gssize transfer_size;
-	GError *error = NULL;
-
-	/* Finish off the transfer */
-	transfer_size = g_output_stream_splice_finish (stream, result, &error);
-	g_assert_no_error (error);
-	g_assert_cmpint (transfer_size, >, 0);
-
-	/* Finish off the upload */
-	data->data.updated_photo = gdata_picasaweb_service_finish_file_upload (data->data.service, GDATA_UPLOAD_STREAM (stream), &error);
-	g_assert_no_error (error);
-	g_assert (GDATA_IS_PICASAWEB_FILE (data->data.updated_photo));
-
-	/* Check the photo's properties */
-	g_assert (gdata_entry_is_inserted (GDATA_ENTRY (data->data.updated_photo)));
-	g_assert_cmpstr (gdata_entry_get_title (GDATA_ENTRY (data->data.updated_photo)), ==, gdata_entry_get_title (GDATA_ENTRY (data->data.photo)));
-	g_assert_cmpstr (gdata_picasaweb_file_get_caption (data->data.updated_photo), ==, gdata_picasaweb_file_get_caption (data->data.photo));
-
-	tags = gdata_picasaweb_file_get_tags (data->data.photo);
-	tags2 = gdata_picasaweb_file_get_tags (data->data.updated_photo);
-	g_assert_cmpuint (g_strv_length ((gchar**) tags2), ==, g_strv_length ((gchar**) tags));
-	g_assert_cmpstr (tags2[0], ==, tags[0]);
-	g_assert_cmpstr (tags2[1], ==, tags[1]);
-	g_assert_cmpstr (tags2[2], ==, tags[2]);
-
-	g_main_loop_quit (data->main_loop);
-}
+GDATA_ASYNC_CLOSURE_FUNCTIONS (upload, UploadData);
 
-static void
-test_upload_default_album_async (UploadAsyncData *data, gconstpointer service)
-{
+GDATA_ASYNC_TEST_FUNCTIONS (upload_default_album, UploadData,
+G_STMT_START {
 	GDataUploadStream *upload_stream;
 	GError *error = NULL;
 
 	/* Prepare the upload stream */
-	upload_stream = gdata_picasaweb_service_upload_file (GDATA_PICASAWEB_SERVICE (service), NULL, data->data.photo, data->data.slug,
-	                                                     data->data.content_type, NULL, &error);
+	upload_stream = gdata_picasaweb_service_upload_file (GDATA_PICASAWEB_SERVICE (service), NULL, data->photo, data->slug,
+	                                                     data->content_type, cancellable, &error);
 	g_assert_no_error (error);
 	g_assert (GDATA_IS_UPLOAD_STREAM (upload_stream));
 
 	/* Upload the photo asynchronously */
-	g_output_stream_splice_async (G_OUTPUT_STREAM (upload_stream), G_INPUT_STREAM (data->data.file_stream),
-	                              G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET, G_PRIORITY_DEFAULT, NULL,
-	                              (GAsyncReadyCallback) test_upload_default_album_async_cb, data);
-	g_main_loop_run (data->main_loop);
+	g_output_stream_splice_async (G_OUTPUT_STREAM (upload_stream), G_INPUT_STREAM (data->file_stream),
+	                              G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET, G_PRIORITY_DEFAULT, NULL,
+	                              async_ready_callback, async_data);
 
 	g_object_unref (upload_stream);
-}
 
-static void
-test_upload_default_album_cancellation_cb (GOutputStream *stream, GAsyncResult *result, UploadAsyncData *data)
-{
+	/* Reset the input stream to the beginning. */
+	g_assert (g_seekable_seek (G_SEEKABLE (data->file_stream), 0, G_SEEK_SET, NULL, NULL) == TRUE);
+} G_STMT_END,
+G_STMT_START {
+	GOutputStream *stream = G_OUTPUT_STREAM (obj);
+	const gchar * const *tags;
+	const gchar * const *tags2;
 	gssize transfer_size;
-	GError *error = NULL;
+	GError *upload_error = NULL;
 
 	/* Finish off the transfer */
-	transfer_size = g_output_stream_splice_finish (stream, result, &error);
-	g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
-	g_assert_cmpint (transfer_size, ==, -1);
-	g_clear_error (&error);
-
-	/* Finish off the upload */
-	data->data.updated_photo = gdata_picasaweb_service_finish_file_upload (data->data.service, GDATA_UPLOAD_STREAM (stream), &error);
-	g_assert_no_error (error);
-	g_assert (data->data.updated_photo == NULL);
-
-	g_main_loop_quit (data->main_loop);
-}
-
-static gboolean
-test_upload_default_album_cancellation_cancel_cb (GCancellable *cancellable)
-{
-	g_cancellable_cancel (cancellable);
-	return FALSE;
-}
-
-static void
-test_upload_default_album_cancellation (UploadAsyncData *data, gconstpointer service)
-{
-	GDataUploadStream *upload_stream;
-	GCancellable *cancellable;
-	GError *error = NULL;
-
-	/* Create an idle function which will cancel the upload */
-	cancellable = g_cancellable_new ();
-	g_idle_add ((GSourceFunc) test_upload_default_album_cancellation_cancel_cb, cancellable);
-
-	/* Prepare the upload stream */
-	upload_stream = gdata_picasaweb_service_upload_file (GDATA_PICASAWEB_SERVICE (service), NULL, data->data.photo, data->data.slug,
-	                                                     data->data.content_type, cancellable, &error);
-	g_assert_no_error (error);
-	g_assert (GDATA_IS_UPLOAD_STREAM (upload_stream));
-
-	/* Upload the photo asynchronously */
-	g_output_stream_splice_async (G_OUTPUT_STREAM (upload_stream), G_INPUT_STREAM (data->data.file_stream),
-	                              G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET, G_PRIORITY_DEFAULT, NULL,
-	                              (GAsyncReadyCallback) test_upload_default_album_cancellation_cb, data);
-	g_main_loop_run (data->main_loop);
-
-	g_object_unref (upload_stream);
-	g_object_unref (cancellable);
-}
-
-static void
-test_upload_default_album_cancellation2 (UploadAsyncData *data, gconstpointer service)
-{
-	GDataUploadStream *upload_stream;
-	GCancellable *cancellable;
-	GError *error = NULL;
-
-	/* Create a timeout function which will cancel the upload after 1ms */
-	cancellable = g_cancellable_new ();
-	g_timeout_add (1, (GSourceFunc) test_upload_default_album_cancellation_cancel_cb, cancellable);
-
-	/* Prepare the upload stream */
-	upload_stream = gdata_picasaweb_service_upload_file (GDATA_PICASAWEB_SERVICE (service), NULL, data->data.photo, data->data.slug,
-	                                                     data->data.content_type, cancellable, &error);
-	g_assert_no_error (error);
-	g_assert (GDATA_IS_UPLOAD_STREAM (upload_stream));
-
-	/* Upload the photo asynchronously */
-	g_output_stream_splice_async (G_OUTPUT_STREAM (upload_stream), G_INPUT_STREAM (data->data.file_stream),
-	                              G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET, G_PRIORITY_DEFAULT, NULL,
-	                              (GAsyncReadyCallback) test_upload_default_album_cancellation_cb, data);
-	g_main_loop_run (data->main_loop);
+	transfer_size = g_output_stream_splice_finish (stream, async_result, &error);
+
+	if (error == NULL) {
+		g_assert_cmpint (transfer_size, >, 0);
+
+		/* Finish off the upload */
+		data->updated_photo = gdata_picasaweb_service_finish_file_upload (data->service, GDATA_UPLOAD_STREAM (stream), &upload_error);
+		g_assert_no_error (upload_error);
+		g_assert (GDATA_IS_PICASAWEB_FILE (data->updated_photo));
+
+		/* Check the photo's properties */
+		g_assert (gdata_entry_is_inserted (GDATA_ENTRY (data->updated_photo)));
+		g_assert_cmpstr (gdata_entry_get_title (GDATA_ENTRY (data->updated_photo)), ==, gdata_entry_get_title (GDATA_ENTRY (data->photo)));
+		g_assert_cmpstr (gdata_picasaweb_file_get_caption (data->updated_photo), ==, gdata_picasaweb_file_get_caption (data->photo));
+
+		tags = gdata_picasaweb_file_get_tags (data->photo);
+		tags2 = gdata_picasaweb_file_get_tags (data->updated_photo);
+		g_assert_cmpuint (g_strv_length ((gchar**) tags2), ==, g_strv_length ((gchar**) tags));
+		g_assert_cmpstr (tags2[0], ==, tags[0]);
+		g_assert_cmpstr (tags2[1], ==, tags[1]);
+		g_assert_cmpstr (tags2[2], ==, tags[2]);
+	} else {
+		g_assert_cmpint (transfer_size, ==, -1);
+
+		/* Finish off the upload */
+		data->updated_photo = gdata_picasaweb_service_finish_file_upload (data->service, GDATA_UPLOAD_STREAM (stream), &upload_error);
+		g_assert_no_error (upload_error);
+		g_assert (data->updated_photo == NULL);
+	}
 
-	g_object_unref (upload_stream);
-	g_object_unref (cancellable);
-}
+	g_clear_error (&upload_error);
+} G_STMT_END);
 
 static void
 test_album_new (void)
@@ -2312,69 +1952,78 @@ main (int argc, char *argv[])
 		service = GDATA_SERVICE (gdata_picasaweb_service_new (authorizer));
 
 		g_test_add_func ("/picasaweb/authentication", test_authentication);
-		g_test_add_func ("/picasaweb/authentication_async", test_authentication_async);
+		g_test_add ("/picasaweb/authentication/async", GDataAsyncTestData, NULL, gdata_set_up_async_test_data, test_authentication_async,
+		            gdata_tear_down_async_test_data);
+		g_test_add ("/picasaweb/authentication/async/cancellation", GDataAsyncTestData, NULL, gdata_set_up_async_test_data,
+		            test_authentication_async_cancellation, gdata_tear_down_async_test_data);
 
 		g_test_add ("/picasaweb/query/all_albums", QueryAllAlbumsData, service, set_up_query_all_albums, test_query_all_albums,
 		            tear_down_query_all_albums);
 		g_test_add ("/picasaweb/query/all_albums/with_limits", QueryAllAlbumsData, service, set_up_query_all_albums,
 		            test_query_all_albums_with_limits, tear_down_query_all_albums);
-		g_test_add ("/picasaweb/query/all_albums/async", QueryAllAlbumsAsyncData, service, set_up_query_all_albums_async,
+		g_test_add ("/picasaweb/query/all_albums/async", GDataAsyncTestData, service, set_up_query_all_albums_async,
 		            test_query_all_albums_async, tear_down_query_all_albums_async);
-		g_test_add ("/picasaweb/query/all_albums/async/progress_closure", QueryAllAlbumsAsyncData, service, set_up_query_all_albums_async,
-		            test_query_all_albums_async_progress_closure, tear_down_query_all_albums_async);
+		g_test_add ("/picasaweb/query/all_albums/async/progress_closure", QueryAllAlbumsData, service, set_up_query_all_albums,
+		            test_query_all_albums_async_progress_closure, tear_down_query_all_albums);
+		g_test_add ("/picasaweb/query/all_albums/async/cancellation", GDataAsyncTestData, service, set_up_query_all_albums_async,
+		            test_query_all_albums_async_cancellation, tear_down_query_all_albums_async);
 		g_test_add_data_func ("/picasaweb/query/all_albums/bad_query", service, test_query_all_albums_bad_query);
 		g_test_add_data_func ("/picasaweb/query/all_albums/bad_query/with_limits", service, test_query_all_albums_bad_query_with_limits);
 
 		g_test_add_data_func ("/picasaweb/query/user", service, test_query_user);
-		g_test_add ("/picasaweb/query/user/async", QueryUserAsyncData, service, set_up_query_user_async, test_query_user_async,
-		            tear_down_query_user_async);
-		g_test_add ("/picasaweb/query/user/async/cancellation", QueryUserAsyncData, service, set_up_query_user_async,
-		            test_query_user_async_cancellation, tear_down_query_user_async);
-		g_test_add ("/picasaweb/query/user/by-username/async", QueryUserAsyncData, service, set_up_query_user_async,
-		            test_query_user_by_username_async, tear_down_query_user_async);
+		g_test_add ("/picasaweb/query/user/async", GDataAsyncTestData, service, gdata_set_up_async_test_data, test_query_user_async,
+		            gdata_tear_down_async_test_data);
+		g_test_add ("/picasaweb/query/user/async/cancellation", GDataAsyncTestData, service, gdata_set_up_async_test_data,
+		            test_query_user_async_cancellation, gdata_tear_down_async_test_data);
+		g_test_add ("/picasaweb/query/user/by-username/async", GDataAsyncTestData, service, gdata_set_up_async_test_data,
+		            test_query_user_by_username_async, gdata_tear_down_async_test_data);
+		g_test_add ("/picasaweb/query/user/by-username/async/cancellation", GDataAsyncTestData, service, gdata_set_up_async_test_data,
+		            test_query_user_by_username_async_cancellation, gdata_tear_down_async_test_data);
 
 		g_test_add ("/picasaweb/insert/album", InsertAlbumData, service, set_up_insert_album, test_insert_album, tear_down_insert_album);
-		g_test_add ("/picasaweb/insert/album/async", InsertAlbumAsyncData, service, set_up_insert_album_async, test_insert_album_async,
+		g_test_add ("/picasaweb/insert/album/async", GDataAsyncTestData, service, set_up_insert_album_async, test_insert_album_async,
 		            tear_down_insert_album_async);
+		g_test_add ("/picasaweb/insert/album/async/cancellation", GDataAsyncTestData, service, set_up_insert_album_async,
+		            test_insert_album_async_cancellation, tear_down_insert_album_async);
 
 		g_test_add ("/picasaweb/query/files", QueryFilesData, service, set_up_query_files, test_query_files, tear_down_query_files);
-		g_test_add ("/picasaweb/query/files/async", QueryFilesAsyncData, service, set_up_query_files_async, test_query_files_async,
+		g_test_add ("/picasaweb/query/files/async", GDataAsyncTestData, service, set_up_query_files_async, test_query_files_async,
 		            tear_down_query_files_async);
-		g_test_add ("/picasaweb/query/files/async/progress_closure", QueryFilesAsyncData, service, set_up_query_files_async,
-		            test_query_files_async_progress_closure, tear_down_query_files_async);
+		g_test_add ("/picasaweb/query/files/async/progress_closure", QueryFilesData, service, set_up_query_files,
+		            test_query_files_async_progress_closure, tear_down_query_files);
+		g_test_add ("/picasaweb/query/files/async/cancellation", GDataAsyncTestData, service, set_up_query_files_async,
+		            test_query_files_async_cancellation, tear_down_query_files_async);
 		g_test_add ("/picasaweb/query/files/single", QueryFilesData, service, set_up_query_files, test_query_files_single,
 		            tear_down_query_files);
 
 		g_test_add ("/picasaweb/comment/query", QueryCommentsData, service, set_up_query_comments, test_comment_query,
 		            tear_down_query_comments);
-		g_test_add ("/picasaweb/comment/query/async", QueryCommentsAsyncData, service, set_up_query_comments_async, test_comment_query_async,
+		g_test_add ("/picasaweb/comment/query/async", GDataAsyncTestData, service, set_up_query_comments_async, test_comment_query_async,
 		            tear_down_query_comments_async);
-		g_test_add ("/picasaweb/comment/query/async/cancellation", QueryCommentsAsyncData, service, set_up_query_comments_async,
+		g_test_add ("/picasaweb/comment/query/async/cancellation", GDataAsyncTestData, service, set_up_query_comments_async,
 		            test_comment_query_async_cancellation, tear_down_query_comments_async);
-		g_test_add ("/picasaweb/comment/query/progress_closure", QueryCommentsAsyncData, service, set_up_query_comments_async,
-		            test_comment_query_async_progress_closure, tear_down_query_comments_async);
+		g_test_add ("/picasaweb/comment/query/progress_closure", QueryCommentsData, service, set_up_query_comments,
+		            test_comment_query_async_progress_closure, tear_down_query_comments);
 
 		g_test_add ("/picasaweb/comment/insert", InsertCommentData, service, set_up_insert_comment, test_comment_insert,
 		            tear_down_insert_comment);
-		g_test_add ("/picasaweb/comment/insert/async", InsertCommentAsyncData, service, set_up_insert_comment_async, test_comment_insert_async,
+		g_test_add ("/picasaweb/comment/insert/async", GDataAsyncTestData, service, set_up_insert_comment_async, test_comment_insert_async,
 		            tear_down_insert_comment_async);
-		g_test_add ("/picasaweb/comment/insert/async/cancellation", InsertCommentAsyncData, service, set_up_insert_comment_async,
+		g_test_add ("/picasaweb/comment/insert/async/cancellation", GDataAsyncTestData, service, set_up_insert_comment_async,
 		            test_comment_insert_async_cancellation, tear_down_insert_comment_async);
 
 		g_test_add ("/picasaweb/comment/delete", QueryCommentsData, service, set_up_query_comments, test_comment_delete,
 		            tear_down_query_comments);
-		g_test_add ("/picasaweb/comment/delete/async", QueryCommentsAsyncData, service, set_up_query_comments_async, test_comment_delete_async,
+		g_test_add ("/picasaweb/comment/delete/async", GDataAsyncTestData, service, set_up_query_comments_async, test_comment_delete_async,
 		            tear_down_query_comments_async);
-		g_test_add ("/picasaweb/comment/delete/async/cancellation", QueryCommentsAsyncData, service, set_up_query_comments_async,
+		g_test_add ("/picasaweb/comment/delete/async/cancellation", GDataAsyncTestData, service, set_up_query_comments_async,
 		            test_comment_delete_async_cancellation, tear_down_query_comments_async);
 
-		g_test_add ("/picasaweb/upload/default_album", UploadData, service, setup_upload, test_upload_default_album, teardown_upload);
-		g_test_add ("/picasaweb/upload/default_album/async", UploadAsyncData, service, setup_upload_async, test_upload_default_album_async,
-		            teardown_upload_async);
-		g_test_add ("/picasaweb/upload/default_album/cancellation", UploadAsyncData, service, setup_upload_async,
-		            test_upload_default_album_cancellation, teardown_upload_async);
-		g_test_add ("/picasaweb/upload/default_album/cancellation2", UploadAsyncData, service, setup_upload_async,
-		            test_upload_default_album_cancellation2, teardown_upload_async);
+		g_test_add ("/picasaweb/upload/default_album", UploadData, service, set_up_upload, test_upload_default_album, tear_down_upload);
+		g_test_add ("/picasaweb/upload/default_album/async", GDataAsyncTestData, service, set_up_upload_async, test_upload_default_album_async,
+		            tear_down_upload_async);
+		g_test_add ("/picasaweb/upload/default_album/async/cancellation", GDataAsyncTestData, service, set_up_upload_async,
+		            test_upload_default_album_async_cancellation, tear_down_upload_async);
 
 		g_test_add ("/picasaweb/download/photo", QueryFilesData, service, set_up_query_files, test_download_photo, tear_down_query_files);
 		g_test_add ("/picasaweb/download/thumbnails", QueryFilesData, service, set_up_query_files, test_download_thumbnails,
diff --git a/gdata/tests/youtube.c b/gdata/tests/youtube.c
index 708e7db..8ec9710 100644
--- a/gdata/tests/youtube.c
+++ b/gdata/tests/youtube.c
@@ -53,47 +53,45 @@ test_authentication (void)
 	g_object_unref (authorizer);
 }
 
-static void
-test_authentication_async_cb (GDataClientLoginAuthorizer *authorizer, GAsyncResult *async_result, GMainLoop *main_loop)
-{
-	gboolean retval;
-	GError *error = NULL;
+GDATA_ASYNC_TEST_FUNCTIONS (authentication, void,
+G_STMT_START {
+	GDataClientLoginAuthorizer *authorizer;
 
-	retval = gdata_client_login_authorizer_authenticate_finish (authorizer, async_result, &error);
-	g_assert_no_error (error);
-	g_assert (retval == TRUE);
-	g_clear_error (&error);
+	/* Create an authorizer */
+	authorizer = gdata_client_login_authorizer_new (CLIENT_ID, GDATA_TYPE_YOUTUBE_SERVICE);
 
-	g_main_loop_quit (main_loop);
+	g_assert_cmpstr (gdata_client_login_authorizer_get_client_id (authorizer), ==, CLIENT_ID);
 
-	/* Check all is as it should be */
-	g_assert_cmpstr (gdata_client_login_authorizer_get_username (authorizer), ==, USERNAME);
-	g_assert_cmpstr (gdata_client_login_authorizer_get_password (authorizer), ==, PASSWORD);
+	gdata_client_login_authorizer_authenticate_async (authorizer, USERNAME, PASSWORD, cancellable, async_ready_callback, async_data);
 
-	g_assert (gdata_authorizer_is_authorized_for_domain (GDATA_AUTHORIZER (authorizer),
-	                                                     gdata_youtube_service_get_primary_authorization_domain ()) == TRUE);
-}
+	g_object_unref (authorizer);
+} G_STMT_END,
+G_STMT_START {
+	gboolean retval;
+	GDataClientLoginAuthorizer *authorizer = GDATA_CLIENT_LOGIN_AUTHORIZER (obj);
 
-static void
-test_authentication_async (void)
-{
-	GMainLoop *main_loop;
-	GDataClientLoginAuthorizer *authorizer;
+	retval = gdata_client_login_authorizer_authenticate_finish (authorizer, async_result, &error);
 
-	/* Create an authorizer */
-	authorizer = gdata_client_login_authorizer_new (CLIENT_ID, GDATA_TYPE_YOUTUBE_SERVICE);
+	if (error == NULL) {
+		g_assert (retval == TRUE);
 
-	g_assert_cmpstr (gdata_client_login_authorizer_get_client_id (authorizer), ==, CLIENT_ID);
+		/* Check all is as it should be */
+		g_assert_cmpstr (gdata_client_login_authorizer_get_username (authorizer), ==, USERNAME);
+		g_assert_cmpstr (gdata_client_login_authorizer_get_password (authorizer), ==, PASSWORD);
 
-	main_loop = g_main_loop_new (NULL, TRUE);
-	gdata_client_login_authorizer_authenticate_async (authorizer, USERNAME, PASSWORD, NULL,
-	                                                  (GAsyncReadyCallback) test_authentication_async_cb, main_loop);
+		g_assert (gdata_authorizer_is_authorized_for_domain (GDATA_AUTHORIZER (authorizer),
+		                                                     gdata_youtube_service_get_primary_authorization_domain ()) == TRUE);
+	} else {
+		g_assert (retval == FALSE);
 
-	g_main_loop_run (main_loop);
+		/* Check nothing's changed */
+		g_assert_cmpstr (gdata_client_login_authorizer_get_username (authorizer), ==, NULL);
+		g_assert_cmpstr (gdata_client_login_authorizer_get_password (authorizer), ==, NULL);
 
-	g_main_loop_unref (main_loop);
-	g_object_unref (authorizer);
-}
+		g_assert (gdata_authorizer_is_authorized_for_domain (GDATA_AUTHORIZER (authorizer),
+		                                                     gdata_youtube_service_get_primary_authorization_domain ()) == FALSE);
+	}
+} G_STMT_END);
 
 static void
 test_service_properties (void)
@@ -126,34 +124,25 @@ test_query_standard_feed (gconstpointer service)
 	g_object_unref (feed);
 }
 
-static void
-test_query_standard_feed_async_cb (GDataService *service, GAsyncResult *async_result, GMainLoop *main_loop)
-{
+GDATA_ASYNC_TEST_FUNCTIONS (query_standard_feed, void,
+G_STMT_START {
+	gdata_youtube_service_query_standard_feed_async (GDATA_YOUTUBE_SERVICE (service), GDATA_YOUTUBE_TOP_RATED_FEED, NULL, cancellable,
+	                                                 NULL, NULL, NULL, async_ready_callback, async_data);
+} G_STMT_END,
+G_STMT_START {
 	GDataFeed *feed;
-	GError *error = NULL;
-
-	feed = gdata_service_query_finish (service, async_result, &error);
-	g_assert_no_error (error);
-	g_assert (GDATA_IS_FEED (feed));
-	g_clear_error (&error);
-
-	/* TODO: Tests? */
-	g_main_loop_quit (main_loop);
 
-	g_object_unref (feed);
-}
+	feed = gdata_service_query_finish (GDATA_SERVICE (obj), async_result, &error);
 
-static void
-test_query_standard_feed_async (gconstpointer service)
-{
-	GMainLoop *main_loop = g_main_loop_new (NULL, TRUE);
+	if (error == NULL) {
+		g_assert (GDATA_IS_FEED (feed));
+		/* TODO: Tests? */
 
-	gdata_youtube_service_query_standard_feed_async (GDATA_YOUTUBE_SERVICE (service), GDATA_YOUTUBE_TOP_RATED_FEED, NULL, NULL,
-							 NULL, NULL, NULL, (GAsyncReadyCallback) test_query_standard_feed_async_cb, main_loop);
-
-	g_main_loop_run (main_loop);
-	g_main_loop_unref (main_loop);
-}
+		g_object_unref (feed);
+	} else {
+		g_assert (feed == NULL);
+	}
+} G_STMT_END);
 
 static void
 test_query_standard_feed_async_progress_closure (gconstpointer service)
@@ -266,37 +255,29 @@ test_query_related (gconstpointer service)
 	g_object_unref (feed);
 }
 
-static void
-test_query_related_async_cb (GDataService *service, GAsyncResult *async_result, GMainLoop *main_loop)
-{
-	GDataFeed *feed;
-	GError *error = NULL;
-
-	feed = gdata_service_query_finish (service, async_result, &error);
-	g_assert_no_error (error);
-	g_assert (GDATA_IS_FEED (feed));
-	g_clear_error (&error);
-
-	/* TODO: Tests? */
-	g_main_loop_quit (main_loop);
-
-	g_object_unref (feed);
-}
-
-static void
-test_query_related_async (gconstpointer service)
-{
+GDATA_ASYNC_TEST_FUNCTIONS (query_related, void,
+G_STMT_START {
 	GDataYouTubeVideo *video;
-	GMainLoop *main_loop = g_main_loop_new (NULL, TRUE);
 
 	video = get_video_for_related ();
-	gdata_youtube_service_query_related_async (GDATA_YOUTUBE_SERVICE (service), video, NULL, NULL, NULL,
-						   NULL, NULL, (GAsyncReadyCallback) test_query_related_async_cb, main_loop);
+	gdata_youtube_service_query_related_async (GDATA_YOUTUBE_SERVICE (service), video, NULL, cancellable, NULL,
+	                                           NULL, NULL, async_ready_callback, async_data);
 	g_object_unref (video);
+} G_STMT_END,
+G_STMT_START {
+	GDataFeed *feed;
 
-	g_main_loop_run (main_loop);
-	g_main_loop_unref (main_loop);
-}
+	feed = gdata_service_query_finish (GDATA_SERVICE (obj), async_result, &error);
+
+	if (error == NULL) {
+		g_assert (GDATA_IS_FEED (feed));
+		/* TODO: Tests? */
+
+		g_object_unref (feed);
+	} else {
+		g_assert (feed == NULL);
+	}
+} G_STMT_END);
 
 static void
 test_query_related_async_progress_closure (gconstpointer service)
@@ -336,7 +317,7 @@ typedef struct {
 } UploadData;
 
 static void
-setup_upload (UploadData *data, gconstpointer service)
+set_up_upload (UploadData *data, gconstpointer service)
 {
 	GDataMediaCategory *category;
 	GFileInfo *file_info;
@@ -377,7 +358,7 @@ setup_upload (UploadData *data, gconstpointer service)
 }
 
 static void
-teardown_upload (UploadData *data, gconstpointer service)
+tear_down_upload (UploadData *data, gconstpointer service)
 {
 	/* Delete the uploaded video, if possible */
 	if (data->updated_video != NULL) {
@@ -434,79 +415,71 @@ test_upload_simple (UploadData *data, gconstpointer service)
 	g_assert_cmpstr (tags2[2], ==, tags[2]);
 }
 
-typedef struct {
-	UploadData data;
-	GMainLoop *main_loop;
-} UploadAsyncData;
-
-static void
-setup_upload_async (UploadAsyncData *data, gconstpointer service)
-{
-	setup_upload ((UploadData*) data, service);
-	data->main_loop = g_main_loop_new (NULL, TRUE);
-}
+GDATA_ASYNC_CLOSURE_FUNCTIONS (upload, UploadData);
 
-static void
-teardown_upload_async (UploadAsyncData *data, gconstpointer service)
-{
-	teardown_upload ((UploadData*) data, service);
-	g_main_loop_unref (data->main_loop);
-}
-
-static void
-test_upload_async_cb (GOutputStream *stream, GAsyncResult *result, UploadAsyncData *data)
-{
-	const gchar * const *tags, * const *tags2;
-	gssize transfer_size;
-	GError *error = NULL;
-
-	/* Finish off the transfer */
-	transfer_size = g_output_stream_splice_finish (stream, result, &error);
-	g_assert_no_error (error);
-	g_assert_cmpint (transfer_size, >, 0);
-
-	/* Finish off the upload */
-	data->data.updated_video = gdata_youtube_service_finish_video_upload (data->data.service, GDATA_UPLOAD_STREAM (stream), &error);
-	g_assert_no_error (error);
-	g_assert (GDATA_IS_YOUTUBE_VIDEO (data->data.updated_video));
-
-	/* Check the video's properties */
-	g_assert (gdata_entry_is_inserted (GDATA_ENTRY (data->data.updated_video)));
-	g_assert_cmpstr (gdata_entry_get_title (GDATA_ENTRY (data->data.updated_video)), ==, gdata_entry_get_title (GDATA_ENTRY (data->data.video)));
-	g_assert_cmpstr (gdata_youtube_video_get_description (data->data.updated_video), ==, gdata_youtube_video_get_description (data->data.video));
-	g_assert_cmpstr (gdata_media_category_get_category (gdata_youtube_video_get_category (data->data.updated_video)), ==,
-	                 gdata_media_category_get_category (gdata_youtube_video_get_category (data->data.video)));
-
-	tags = gdata_youtube_video_get_keywords (data->data.video);
-	tags2 = gdata_youtube_video_get_keywords (data->data.updated_video);
-	g_assert_cmpuint (g_strv_length ((gchar**) tags2), ==, g_strv_length ((gchar**) tags));
-	g_assert_cmpstr (tags2[0], ==, tags[0]);
-	g_assert_cmpstr (tags2[1], ==, tags[1]);
-	g_assert_cmpstr (tags2[2], ==, tags[2]);
-
-	g_main_loop_quit (data->main_loop);
-}
-
-static void
-test_upload_async (UploadAsyncData *data, gconstpointer service)
-{
+GDATA_ASYNC_TEST_FUNCTIONS (upload, UploadData,
+G_STMT_START {
 	GDataUploadStream *upload_stream;
 	GError *error = NULL;
 
 	/* Prepare the upload stream */
-	upload_stream = gdata_youtube_service_upload_video (GDATA_YOUTUBE_SERVICE (service), data->data.video, data->data.slug,
-	                                                    data->data.content_type, NULL, &error);
+	upload_stream = gdata_youtube_service_upload_video (GDATA_YOUTUBE_SERVICE (service), data->video, data->slug,
+	                                                    data->content_type, cancellable, &error);
 	g_assert_no_error (error);
 	g_assert (GDATA_IS_UPLOAD_STREAM (upload_stream));
 
 	/* Upload the video asynchronously */
-	g_output_stream_splice_async (G_OUTPUT_STREAM (upload_stream), G_INPUT_STREAM (data->data.file_stream),
-	                              G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET, G_PRIORITY_DEFAULT, NULL,
-	                              (GAsyncReadyCallback) test_upload_async_cb, data);
-	g_main_loop_run (data->main_loop);
+	g_output_stream_splice_async (G_OUTPUT_STREAM (upload_stream), G_INPUT_STREAM (data->file_stream),
+	                              G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET, G_PRIORITY_DEFAULT, NULL,
+	                              async_ready_callback, async_data);
 
 	g_object_unref (upload_stream);
-}
+
+	/* Reset the input stream to the beginning. */
+	g_assert (g_seekable_seek (G_SEEKABLE (data->file_stream), 0, G_SEEK_SET, NULL, NULL) == TRUE);
+} G_STMT_END,
+G_STMT_START {
+	GOutputStream *stream = G_OUTPUT_STREAM (obj);
+	const gchar * const *tags;
+	const gchar * const *tags2;
+	gssize transfer_size;
+	GError *upload_error = NULL;
+
+	/* Finish off the transfer */
+	transfer_size = g_output_stream_splice_finish (stream, async_result, &error);
+
+	if (error == NULL) {
+		g_assert_cmpint (transfer_size, >, 0);
+
+		/* Finish off the upload */
+		data->updated_video = gdata_youtube_service_finish_video_upload (data->service, GDATA_UPLOAD_STREAM (stream), &upload_error);
+		g_assert_no_error (upload_error);
+		g_assert (GDATA_IS_YOUTUBE_VIDEO (data->updated_video));
+
+		/* Check the video's properties */
+		g_assert (gdata_entry_is_inserted (GDATA_ENTRY (data->updated_video)));
+		g_assert_cmpstr (gdata_entry_get_title (GDATA_ENTRY (data->updated_video)), ==, gdata_entry_get_title (GDATA_ENTRY (data->video)));
+		g_assert_cmpstr (gdata_youtube_video_get_description (data->updated_video), ==, gdata_youtube_video_get_description (data->video));
+		g_assert_cmpstr (gdata_media_category_get_category (gdata_youtube_video_get_category (data->updated_video)), ==,
+		                 gdata_media_category_get_category (gdata_youtube_video_get_category (data->video)));
+
+		tags = gdata_youtube_video_get_keywords (data->video);
+		tags2 = gdata_youtube_video_get_keywords (data->updated_video);
+		g_assert_cmpuint (g_strv_length ((gchar**) tags2), ==, g_strv_length ((gchar**) tags));
+		g_assert_cmpstr (tags2[0], ==, tags[0]);
+		g_assert_cmpstr (tags2[1], ==, tags[1]);
+		g_assert_cmpstr (tags2[2], ==, tags[2]);
+	} else {
+		g_assert_cmpint (transfer_size, ==, -1);
+
+		/* Finish off the upload */
+		data->updated_video = gdata_youtube_service_finish_video_upload (data->service, GDATA_UPLOAD_STREAM (stream), &upload_error);
+		g_assert_no_error (upload_error);
+		g_assert (data->updated_video == NULL);
+	}
+
+	g_clear_error (&upload_error);
+} G_STMT_END);
 
 static void
 test_parsing_app_control (void)
@@ -1368,36 +1341,27 @@ test_query_single (gconstpointer service)
 	g_object_unref (video);
 }
 
-static void
-test_query_single_async_cb (GDataService *service, GAsyncResult *async_result, GMainLoop *main_loop)
-{
+GDATA_ASYNC_TEST_FUNCTIONS (query_single, void,
+G_STMT_START {
+	gdata_service_query_single_entry_async (GDATA_SERVICE (service), gdata_youtube_service_get_primary_authorization_domain (),
+	                                        "tag:youtube.com,2008:video:_LeQuMpwbW4", NULL, GDATA_TYPE_YOUTUBE_VIDEO,
+	                                        cancellable, async_ready_callback, async_data);
+} G_STMT_END,
+G_STMT_START {
 	GDataYouTubeVideo *video;
-	GError *error = NULL;
 
-	video = GDATA_YOUTUBE_VIDEO (gdata_service_query_single_entry_finish (GDATA_SERVICE (service), async_result, &error));
-
-	g_assert_no_error (error);
-	g_assert (GDATA_IS_YOUTUBE_VIDEO (video));
-	g_assert_cmpstr (gdata_youtube_video_get_video_id (video), ==, "_LeQuMpwbW4");
-	g_assert_cmpstr (gdata_entry_get_id (GDATA_ENTRY (video)), ==, "tag:youtube.com,2008:video:_LeQuMpwbW4");
-	g_clear_error (&error);
+	video = GDATA_YOUTUBE_VIDEO (gdata_service_query_single_entry_finish (GDATA_SERVICE (obj), async_result, &error));
 
-	g_main_loop_quit (main_loop);
-	g_object_unref (video);
-}
-
-static void
-test_query_single_async (gconstpointer service)
-{
-	GMainLoop *main_loop = g_main_loop_new (NULL, TRUE);
-
-	gdata_service_query_single_entry_async (GDATA_SERVICE (service), gdata_youtube_service_get_primary_authorization_domain (),
-	                                        "tag:youtube.com,2008:video:_LeQuMpwbW4", NULL, GDATA_TYPE_YOUTUBE_VIDEO,
-	                                        NULL, (GAsyncReadyCallback) test_query_single_async_cb, main_loop);
+	if (error == NULL) {
+		g_assert (GDATA_IS_YOUTUBE_VIDEO (video));
+		g_assert_cmpstr (gdata_youtube_video_get_video_id (video), ==, "_LeQuMpwbW4");
+		g_assert_cmpstr (gdata_entry_get_id (GDATA_ENTRY (video)), ==, "tag:youtube.com,2008:video:_LeQuMpwbW4");
 
-	g_main_loop_run (main_loop);
-	g_main_loop_unref (main_loop);
-}
+		g_object_unref (video);
+	} else {
+		g_assert (video == NULL);
+	}
+} G_STMT_END);
 
 typedef struct {
 	GDataYouTubeVideo *video;
@@ -1464,92 +1428,38 @@ test_comment_query (CommentData *data, gconstpointer service)
 	g_object_unref (comments_feed);
 }
 
-typedef struct {
-	CommentData parent;
-	GMainLoop *main_loop;
-} CommentAsyncData;
-
-static void
-set_up_comment_async (CommentAsyncData *data, gconstpointer service)
-{
-	set_up_comment ((CommentData*) data, service);
-	data->main_loop = g_main_loop_new (NULL, FALSE);
-}
-
-static void
-tear_down_comment_async (CommentAsyncData *data, gconstpointer service)
-{
-	g_main_loop_unref (data->main_loop);
-	tear_down_comment ((CommentData*) data, service);
-}
-
-static void
-test_comment_query_async_cb (GDataCommentable *commentable, GAsyncResult *result, CommentAsyncData *data)
-{
-	GDataFeed *comments_feed;
-	GError *error = NULL;
-
-	comments_feed = gdata_commentable_query_comments_finish (commentable, result, &error);
-	g_assert_no_error (error);
-	g_clear_error (&error);
-
-	assert_comments_feed (comments_feed);
-
-	g_object_unref (comments_feed);
-
-	g_main_loop_quit (data->main_loop);
-}
-
-static void
-test_comment_query_async (CommentAsyncData *data, gconstpointer service)
-{
-	gdata_commentable_query_comments_async (GDATA_COMMENTABLE (data->parent.video), GDATA_SERVICE (service), NULL, NULL, NULL, NULL, NULL,
-	                                        (GAsyncReadyCallback) test_comment_query_async_cb, data);
-
-	g_main_loop_run (data->main_loop);
-}
+GDATA_ASYNC_CLOSURE_FUNCTIONS (comment, CommentData);
 
-static void
-test_comment_query_async_cancellation_cb (GDataCommentable *commentable, GAsyncResult *result, CommentAsyncData *data)
-{
+GDATA_ASYNC_TEST_FUNCTIONS (comment_query, CommentData,
+G_STMT_START {
+	gdata_commentable_query_comments_async (GDATA_COMMENTABLE (data->video), GDATA_SERVICE (service), NULL, cancellable, NULL, NULL, NULL,
+	                                        async_ready_callback, async_data);
+} G_STMT_END,
+G_STMT_START {
 	GDataFeed *comments_feed;
-	GError *error = NULL;
-
-	comments_feed = gdata_commentable_query_comments_finish (commentable, result, &error);
-	g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
-	g_assert (comments_feed == NULL);
-	g_clear_error (&error);
 
-	g_main_loop_quit (data->main_loop);
-}
+	comments_feed = gdata_commentable_query_comments_finish (GDATA_COMMENTABLE (obj), async_result, &error);
 
-static void
-test_comment_query_async_cancellation (CommentAsyncData *data, gconstpointer service)
-{
-	GCancellable *cancellable;
+	if (error == NULL) {
+		assert_comments_feed (comments_feed);
 
-	cancellable = g_cancellable_new ();
-	g_cancellable_cancel (cancellable);
-
-	gdata_commentable_query_comments_async (GDATA_COMMENTABLE (data->parent.video), GDATA_SERVICE (service), NULL, cancellable, NULL, NULL, NULL,
-	                                        (GAsyncReadyCallback) test_comment_query_async_cancellation_cb, data);
-
-	g_main_loop_run (data->main_loop);
-
-	g_object_unref (cancellable);
-}
+		g_object_unref (comments_feed);
+	} else {
+		g_assert (comments_feed == NULL);
+	}
+} G_STMT_END);
 
 /* Test that the progress callbacks from gdata_commentable_query_comments_async() are called correctly.
- * We take a CommentAsyncData so that we can guarantee the video exists, but we don't use it much as we don't actually care about the specific
+ * We take a CommentData so that we can guarantee the video exists, but we don't use it much as we don't actually care about the specific
  * video. */
 static void
-test_comment_query_async_progress_closure (CommentAsyncData *query_data, gconstpointer service)
+test_comment_query_async_progress_closure (CommentData *query_data, gconstpointer service)
 {
 	GDataAsyncProgressClosure *data = g_slice_new0 (GDataAsyncProgressClosure);
 
 	data->main_loop = g_main_loop_new (NULL, TRUE);
 
-	gdata_commentable_query_comments_async (GDATA_COMMENTABLE (query_data->parent.video), GDATA_SERVICE (service), NULL, NULL,
+	gdata_commentable_query_comments_async (GDATA_COMMENTABLE (query_data->video), GDATA_SERVICE (service), NULL, NULL,
 	                                        (GDataQueryProgressCallback) gdata_test_async_progress_callback,
 	                                        data, (GDestroyNotify) gdata_test_async_progress_closure_free,
 	                                        (GAsyncReadyCallback) gdata_test_async_progress_finish_callback, data);
@@ -1612,7 +1522,7 @@ assert_comments_equal (GDataComment *new_comment, GDataYouTubeComment *original_
 	author = GDATA_AUTHOR (authors->data);
 
 	g_assert_cmpstr (gdata_author_get_name (author), ==, "GDataTest");
-	g_assert_cmpstr (gdata_author_get_uri (author), ==, "https://gdata.youtube.com/feeds/api/users/gdatatest";);
+	g_assert_cmpstr (gdata_author_get_uri (author), ==, "http://gdata.youtube.com/feeds/api/users/gdatatest";);
 }
 
 static void
@@ -1631,81 +1541,26 @@ test_comment_insert (InsertCommentData *data, gconstpointer service)
 	g_object_unref (new_comment);
 }
 
-typedef struct {
-	InsertCommentData parent;
-	GMainLoop *main_loop;
-} InsertCommentAsyncData;
+GDATA_ASYNC_CLOSURE_FUNCTIONS (insert_comment, InsertCommentData);
 
-static void
-set_up_insert_comment_async (InsertCommentAsyncData *data, gconstpointer service)
-{
-	set_up_insert_comment ((InsertCommentData*) data, service);
-	data->main_loop = g_main_loop_new (NULL, FALSE);
-}
-
-static void
-tear_down_insert_comment_async (InsertCommentAsyncData *data, gconstpointer service)
-{
-	g_main_loop_unref (data->main_loop);
-	tear_down_insert_comment ((InsertCommentData*) data, service);
-}
-
-static void
-test_comment_insert_async_cb (GDataCommentable *commentable, GAsyncResult *result, InsertCommentAsyncData *data)
-{
+GDATA_ASYNC_TEST_FUNCTIONS (comment_insert, InsertCommentData,
+G_STMT_START {
+	gdata_commentable_insert_comment_async (GDATA_COMMENTABLE (data->parent.video), GDATA_SERVICE (service),
+	                                        GDATA_COMMENT (data->comment), cancellable, async_ready_callback, async_data);
+} G_STMT_END,
+G_STMT_START {
 	GDataComment *new_comment;
-	GError *error = NULL;
-
-	new_comment = gdata_commentable_insert_comment_finish (commentable, result, &error);
-	g_assert_no_error (error);
-	g_clear_error (&error);
-
-	assert_comments_equal (new_comment, data->parent.comment);
-
-	g_object_unref (new_comment);
-
-	g_main_loop_quit (data->main_loop);
-}
 
-static void
-test_comment_insert_async (InsertCommentAsyncData *data, gconstpointer service)
-{
-	gdata_commentable_insert_comment_async (GDATA_COMMENTABLE (data->parent.parent.video), GDATA_SERVICE (service),
-	                                        GDATA_COMMENT (data->parent.comment), NULL, (GAsyncReadyCallback) test_comment_insert_async_cb, data);
-
-	g_main_loop_run (data->main_loop);
-}
+	new_comment = gdata_commentable_insert_comment_finish (GDATA_COMMENTABLE (obj), async_result, &error);
 
-static void
-test_comment_insert_async_cancellation_cb (GDataCommentable *commentable, GAsyncResult *result, InsertCommentAsyncData *data)
-{
-	GDataComment *new_comment;
-	GError *error = NULL;
+	if (error == NULL) {
+		assert_comments_equal (new_comment, data->comment);
 
-	new_comment = gdata_commentable_insert_comment_finish (commentable, result, &error);
-	g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
-	g_assert (new_comment == NULL);
-	g_clear_error (&error);
-
-	g_main_loop_quit (data->main_loop);
-}
-
-static void
-test_comment_insert_async_cancellation (InsertCommentAsyncData *data, gconstpointer service)
-{
-	GCancellable *cancellable;
-
-	cancellable = g_cancellable_new ();
-	g_cancellable_cancel (cancellable);
-
-	gdata_commentable_insert_comment_async (GDATA_COMMENTABLE (data->parent.parent.video), GDATA_SERVICE (service),
-	                                        GDATA_COMMENT (data->parent.comment), cancellable,
-	                                        (GAsyncReadyCallback) test_comment_insert_async_cancellation_cb, data);
-
-	g_main_loop_run (data->main_loop);
-
-	g_object_unref (cancellable);
-}
+		g_object_unref (new_comment);
+	} else {
+		g_assert (new_comment == NULL);
+	}
+} G_STMT_END);
 
 static void
 test_comment_delete (InsertCommentData *data, gconstpointer service)
@@ -1722,29 +1577,26 @@ test_comment_delete (InsertCommentData *data, gconstpointer service)
 	g_clear_error (&error);
 }
 
-static void
-test_comment_delete_async_cb (GDataCommentable *commentable, GAsyncResult *result, InsertCommentAsyncData *data)
-{
+GDATA_ASYNC_TEST_FUNCTIONS (comment_delete, InsertCommentData,
+G_STMT_START {
+	gdata_commentable_delete_comment_async (GDATA_COMMENTABLE (data->parent.video), GDATA_SERVICE (service),
+	                                        GDATA_COMMENT (data->comment), cancellable, async_ready_callback, async_data);
+} G_STMT_END,
+G_STMT_START {
 	gboolean success;
-	GError *error = NULL;
 
-	success = gdata_commentable_delete_comment_finish (commentable, result, &error);
-	g_assert_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_FORBIDDEN);
-	g_assert (success == FALSE);
-	g_clear_error (&error);
+	success = gdata_commentable_delete_comment_finish (GDATA_COMMENTABLE (obj), async_result, &error);
 
-	g_main_loop_quit (data->main_loop);
-}
+	g_assert (error != NULL);
+	g_assert (success == FALSE);
 
-static void
-test_comment_delete_async (InsertCommentAsyncData *data, gconstpointer service)
-{
 	/* See the note above in test_comment_delete(). */
-	gdata_commentable_delete_comment_async (GDATA_COMMENTABLE (data->parent.parent.video), GDATA_SERVICE (service),
-	                                        GDATA_COMMENT (data->parent.comment), NULL, (GAsyncReadyCallback) test_comment_delete_async_cb, data);
-
-	g_main_loop_run (data->main_loop);
-}
+	if (g_error_matches (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_FORBIDDEN) == TRUE) {
+		/* Pretend no error happened so that the test succeeds. */
+		g_clear_error (&error);
+		async_data->cancellation_timeout = 13;
+	}
+} G_STMT_END);
 
 static void
 test_parsing_video_id_from_uri (void)
@@ -1826,36 +1678,28 @@ test_categories (gconstpointer service)
 	g_free (old_locale);
 }
 
-static void
-test_categories_async_cb (GDataService *service, GAsyncResult *async_result, GMainLoop *main_loop)
-{
+GDATA_ASYNC_TEST_FUNCTIONS (categories, void,
+G_STMT_START {
+	gdata_youtube_service_get_categories_async (GDATA_YOUTUBE_SERVICE (service), cancellable, async_ready_callback, async_data);
+} G_STMT_END,
+G_STMT_START {
 	GDataAPPCategories *app_categories;
 	GList *categories;
-	GError *error = NULL;
 
-	app_categories = gdata_youtube_service_get_categories_finish (GDATA_YOUTUBE_SERVICE (service), async_result, &error);
-	g_assert_no_error (error);
-	g_assert (GDATA_IS_APP_CATEGORIES (app_categories));
-	g_clear_error (&error);
+	app_categories = gdata_youtube_service_get_categories_finish (GDATA_YOUTUBE_SERVICE (obj), async_result, &error);
 
-	categories = gdata_app_categories_get_categories (app_categories);
-	g_assert_cmpint (g_list_length (categories), >, 0);
-	g_assert (GDATA_IS_YOUTUBE_CATEGORY (categories->data));
+	if (error == NULL) {
+		g_assert (GDATA_IS_APP_CATEGORIES (app_categories));
 
-	g_main_loop_quit (main_loop);
-	g_object_unref (app_categories);
-}
+		categories = gdata_app_categories_get_categories (app_categories);
+		g_assert_cmpint (g_list_length (categories), >, 0);
+		g_assert (GDATA_IS_YOUTUBE_CATEGORY (categories->data));
 
-static void
-test_categories_async (gconstpointer service)
-{
-	GMainLoop *main_loop = g_main_loop_new (NULL, TRUE);
-
-	gdata_youtube_service_get_categories_async (GDATA_YOUTUBE_SERVICE (service), NULL, (GAsyncReadyCallback) test_categories_async_cb, main_loop);
-
-	g_main_loop_run (main_loop);
-	g_main_loop_unref (main_loop);
-}
+		g_object_unref (app_categories);
+	} else {
+		g_assert (app_categories == NULL);
+	}
+} G_STMT_END);
 
 typedef struct {
 	GDataEntry *new_video;
@@ -2036,43 +1880,62 @@ main (int argc, char *argv[])
 		service = GDATA_SERVICE (gdata_youtube_service_new (DEVELOPER_KEY, authorizer));
 
 		g_test_add_func ("/youtube/authentication", test_authentication);
-		g_test_add_func ("/youtube/authentication_async", test_authentication_async);
+		g_test_add ("/youtube/authentication/async", GDataAsyncTestData, NULL, gdata_set_up_async_test_data, test_authentication_async,
+		            gdata_tear_down_async_test_data);
+		g_test_add ("/youtube/authentication/async/cancellation", GDataAsyncTestData, NULL, gdata_set_up_async_test_data,
+		            test_authentication_async_cancellation, gdata_tear_down_async_test_data);
 
 		g_test_add_data_func ("/youtube/query/standard_feed", service, test_query_standard_feed);
-		g_test_add_data_func ("/youtube/query/standard_feed_async", service, test_query_standard_feed_async);
-		g_test_add_data_func ("/youtube/query/standard_feed_async_progress_closure", service, test_query_standard_feed_async_progress_closure);
+		g_test_add ("/youtube/query/standard_feed/async", GDataAsyncTestData, service, gdata_set_up_async_test_data,
+		            test_query_standard_feed_async, gdata_tear_down_async_test_data);
+		g_test_add_data_func ("/youtube/query/standard_feed/async/progress_closure", service, test_query_standard_feed_async_progress_closure);
+		g_test_add ("/youtube/query/standard_feed/async/cancellation", GDataAsyncTestData, service, gdata_set_up_async_test_data,
+		            test_query_standard_feed_async_cancellation, gdata_tear_down_async_test_data);
 		g_test_add_data_func ("/youtube/query/related", service, test_query_related);
-		g_test_add_data_func ("/youtube/query/related_async", service, test_query_related_async);
-		g_test_add_data_func ("/youtube/query/related_async_progress_closure", service, test_query_related_async_progress_closure);
+		g_test_add ("/youtube/query/related/async", GDataAsyncTestData, service, gdata_set_up_async_test_data, test_query_related_async,
+		            gdata_tear_down_async_test_data);
+		g_test_add_data_func ("/youtube/query/related/async/progress_closure", service, test_query_related_async_progress_closure);
+		g_test_add ("/youtube/query/related/async/cancellation", GDataAsyncTestData, service, gdata_set_up_async_test_data,
+		            test_query_related_async_cancellation, gdata_tear_down_async_test_data);
 
-		g_test_add ("/youtube/upload/simple", UploadData, service, setup_upload, test_upload_simple, teardown_upload);
-		g_test_add ("/youtube/upload/async", UploadAsyncData, service, setup_upload_async, test_upload_async, teardown_upload_async);
+		g_test_add ("/youtube/upload/simple", UploadData, service, set_up_upload, test_upload_simple, tear_down_upload);
+		g_test_add ("/youtube/upload/async", GDataAsyncTestData, service, set_up_upload_async, test_upload_async, tear_down_upload_async);
+		g_test_add ("/youtube/upload/async", GDataAsyncTestData, service, set_up_upload_async, test_upload_async_cancellation,
+		            tear_down_upload_async);
 
 		g_test_add_data_func ("/youtube/query/single", service, test_query_single);
-		g_test_add_data_func ("/youtube/query/single_async", service, test_query_single_async);
+		g_test_add ("/youtube/query/single/async", GDataAsyncTestData, service, gdata_set_up_async_test_data, test_query_single_async,
+		            gdata_tear_down_async_test_data);
+		g_test_add ("/youtube/query/single/async/cancellation", GDataAsyncTestData, service, gdata_set_up_async_test_data,
+		            test_query_single_async_cancellation, gdata_tear_down_async_test_data);
 
 		g_test_add ("/youtube/comment/query", CommentData, service, set_up_comment, test_comment_query, tear_down_comment);
-		g_test_add ("/youtube/comment/query/async", CommentAsyncData, service, set_up_comment_async, test_comment_query_async,
+		g_test_add ("/youtube/comment/query/async", GDataAsyncTestData, service, set_up_comment_async, test_comment_query_async,
 		            tear_down_comment_async);
-		g_test_add ("/youtube/comment/query/async/cancellation", CommentAsyncData, service, set_up_comment_async,
+		g_test_add ("/youtube/comment/query/async/cancellation", GDataAsyncTestData, service, set_up_comment_async,
 		            test_comment_query_async_cancellation, tear_down_comment_async);
-		g_test_add ("/youtube/comment/query/async/progress_closure", CommentAsyncData, service, set_up_comment_async,
-		            test_comment_query_async_progress_closure, tear_down_comment_async);
+		g_test_add ("/youtube/comment/query/async/progress_closure", CommentData, service, set_up_comment,
+		            test_comment_query_async_progress_closure, tear_down_comment);
 
 		g_test_add ("/youtube/comment/insert", InsertCommentData, service, set_up_insert_comment, test_comment_insert,
 		            tear_down_insert_comment);
-		g_test_add ("/youtube/comment/insert/async", InsertCommentAsyncData, service, set_up_insert_comment_async, test_comment_insert_async,
+		g_test_add ("/youtube/comment/insert/async", GDataAsyncTestData, service, set_up_insert_comment_async, test_comment_insert_async,
 		            tear_down_insert_comment_async);
-		g_test_add ("/youtube/comment/insert/async/cancellation", InsertCommentAsyncData, service, set_up_insert_comment_async,
+		g_test_add ("/youtube/comment/insert/async/cancellation", GDataAsyncTestData, service, set_up_insert_comment_async,
 		            test_comment_insert_async_cancellation, tear_down_insert_comment_async);
 
 		g_test_add ("/youtube/comment/delete", InsertCommentData, service, set_up_insert_comment, test_comment_delete,
 		            tear_down_insert_comment);
-		g_test_add ("/youtube/comment/delete/async", InsertCommentAsyncData, service, set_up_insert_comment_async, test_comment_delete_async,
+		g_test_add ("/youtube/comment/delete/async", GDataAsyncTestData, service, set_up_insert_comment_async, test_comment_delete_async,
 		            tear_down_insert_comment_async);
+		g_test_add ("/youtube/comment/delete/async/cancellation", GDataAsyncTestData, service, set_up_insert_comment_async,
+		            test_comment_delete_async_cancellation, tear_down_insert_comment_async);
 
 		g_test_add_data_func ("/youtube/categories", service, test_categories);
-		g_test_add_data_func ("/youtube/categories/async", service, test_categories_async);
+		g_test_add ("/youtube/categories/async", GDataAsyncTestData, service, gdata_set_up_async_test_data, test_categories_async,
+		            gdata_tear_down_async_test_data);
+		g_test_add ("/youtube/categories/async/cancellation", GDataAsyncTestData, service, gdata_set_up_async_test_data,
+		            test_categories_async_cancellation, gdata_tear_down_async_test_data);
 
 		g_test_add ("/youtube/batch", BatchData, service, setup_batch, test_batch, teardown_batch);
 		g_test_add ("/youtube/batch/async", BatchData, service, setup_batch, test_batch_async, teardown_batch);



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