diff --git a/docs/reference/gdata-sections.txt b/docs/reference/gdata-sections.txt
index fc95a95..fe69069 100644
--- a/docs/reference/gdata-sections.txt
+++ b/docs/reference/gdata-sections.txt
@@ -1180,12 +1180,15 @@ GDataYouTubeStatePrivate
 <TITLE>GDataPicasaWebService</TITLE>
 GDataPicasaWebService
 GDataPicasaWebServiceClass
+UploadFileAsyncCallback
 gdata_picasaweb_service_new
 gdata_picasaweb_service_get_user
 gdata_picasaweb_service_query_all_albums
 gdata_picasaweb_service_query_all_albums_async
 gdata_picasaweb_service_query_files
 gdata_picasaweb_service_upload_file
+gdata_picasaweb_service_upload_file_async
+gdata_picasaweb_service_upload_file_finish
 <SUBSECTION Standard>
 gdata_picasaweb_service_get_type
 GDATA_IS_PICASAWEB_SERVICE
diff --git a/gdata/gdata.symbols b/gdata/gdata.symbols
index b41f44f..4293ffb 100644
--- a/gdata/gdata.symbols
+++ b/gdata/gdata.symbols
@@ -597,6 +597,8 @@ gdata_picasaweb_service_query_all_albums
 gdata_picasaweb_service_query_all_albums_async
 gdata_picasaweb_service_query_files
 gdata_picasaweb_service_upload_file
+gdata_picasaweb_service_upload_file_async
+gdata_picasaweb_service_upload_file_finish
 gdata_picasaweb_feed_get_type
 gdata_picasaweb_user_get_type
 gdata_picasaweb_user_get_user
diff --git a/gdata/services/picasaweb/gdata-picasaweb-service.c b/gdata/services/picasaweb/gdata-picasaweb-service.c
index 7b29311..9850807 100644
--- a/gdata/services/picasaweb/gdata-picasaweb-service.c
+++ b/gdata/services/picasaweb/gdata-picasaweb-service.c
@@ -295,6 +295,57 @@ gdata_picasaweb_service_query_files (GDataPicasaWebService *self, GDataPicasaWeb
 				    progress_callback, progress_user_data, error);
 }
 
+static GOutputStream *
+get_file_output_stream (GDataPicasaWebService *self, GDataPicasaWebAlbum *album, GDataPicasaWebFile *file_entry, GFile *file_data, GError **error)
+{
+	GDataCategory *category;
+	GOutputStream *output_stream;
+	const gchar *slug = NULL, *content_type = NULL, *user_id = NULL, *album_id = NULL;
+	gchar *upload_uri;
+	GFileInfo *file_info = NULL;
+
+	file_info = g_file_query_info (file_data, "standard::display-name,standard::content-type", G_FILE_QUERY_INFO_NONE, NULL, error);
+	if (file_info == NULL)
+		return NULL;
+
+	slug = g_file_info_get_display_name (file_info);
+	content_type = g_file_info_get_content_type (file_info);
+
+	/* Add the "photo" kind if the entry is missing it. If it already has the kind category, no duplicate is added. */
+	category = gdata_category_new ("http://schemas.google.com/photos/2007#photo", "http://schemas.google.com/g/2005#kind", NULL);
+	gdata_entry_add_category (GDATA_ENTRY (file_entry), category);
+	g_object_unref (category);
+
+	/* PicasaWeb allows you to post to a default Dropbox */
+	album_id = (album != NULL) ? gdata_entry_get_id (GDATA_ENTRY (album)) : "default";
+	user_id = gdata_service_get_username (GDATA_SERVICE (self));
+
+	/* Build the upload URI and upload stream */
+	upload_uri = g_strdup_printf ("http://picasaweb.google.com/data/feed/api/user/%s/albumid/%s", user_id, album_id);
+	output_stream = gdata_upload_stream_new (GDATA_SERVICE (self), SOUP_METHOD_POST, upload_uri, GDATA_ENTRY (file_entry), slug, content_type);
+	g_free (upload_uri);
+	g_object_unref (file_info);
+
+	return output_stream;
+}
+
+static GDataPicasaWebFile *
+parse_spliced_stream (GOutputStream *output_stream, GError **error)
+{
+	gssize response_length;
+	const gchar *response_body = NULL;
+	GDataPicasaWebFile *new_entry;
+
+	/* Get the response from the server */
+	response_body = gdata_upload_stream_get_response (GDATA_UPLOAD_STREAM (output_stream), &response_length);
+	g_assert (response_body != NULL && response_length > 0);
+		
+	/* Parse the response to produce a GDataPicasaWebFile */
+	new_entry = GDATA_PICASAWEB_FILE (gdata_parsable_new_from_xml (GDATA_TYPE_PICASAWEB_FILE, response_body, (gint) response_length, error));
+
+	return new_entry;
+}
+
 /**
  * gdata_picasaweb_service_upload_file:
  * @self: a #GDataPicasaWebService
@@ -321,18 +372,15 @@ GDataPicasaWebFile *
 gdata_picasaweb_service_upload_file (GDataPicasaWebService *self, GDataPicasaWebAlbum *album, GDataPicasaWebFile *file_entry, GFile *file_data,
 				     GCancellable *cancellable, GError **error)
 {
-	GDataPicasaWebFile *new_entry;
-	GDataCategory *category;
 	GOutputStream *output_stream;
 	GInputStream *input_stream;
-	const gchar *slug = NULL, *content_type = NULL, *response_body = NULL, *user_id = NULL, *album_id = NULL;
-	gchar *upload_uri;
-	gssize response_length;
-	GFileInfo *file_info = NULL;
+	GDataPicasaWebFile *new_entry;
+	GError *child_error = NULL;
 
 	g_return_val_if_fail (GDATA_IS_PICASAWEB_SERVICE (self), NULL);
 	g_return_val_if_fail (GDATA_IS_PICASAWEB_FILE (file_entry), NULL);
 	g_return_val_if_fail (G_IS_FILE (file_data), NULL);
+	g_return_val_if_fail (error == NULL || *error == NULL, NULL);
 
 	if (gdata_entry_is_inserted (GDATA_ENTRY (file_entry)) == TRUE) {
 		g_set_error_literal (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_ENTRY_ALREADY_INSERTED,
@@ -346,28 +394,7 @@ gdata_picasaweb_service_upload_file (GDataPicasaWebService *self, GDataPicasaWeb
 		return NULL;
 	}
 
-	file_info = g_file_query_info (file_data, "standard::display-name,standard::content-type", G_FILE_QUERY_INFO_NONE, NULL, error);
-	if (file_info == NULL)
-		return NULL;
-
-	slug = g_file_info_get_display_name (file_info);
-	content_type = g_file_info_get_content_type (file_info);
-
-	/* Add the "photo" kind if the entry is missing it. If it already has the kind category, no duplicate is added. */
-	category = gdata_category_new ("http://schemas.google.com/photos/2007#photo", "http://schemas.google.com/g/2005#kind", NULL);
-	gdata_entry_add_category (GDATA_ENTRY (file_entry), category);
-	g_object_unref (category);
-
-	/* PicasaWeb allows you to post to a default Dropbox */
-	album_id = (album != NULL) ? gdata_entry_get_id (GDATA_ENTRY (album)) : "default";
-	user_id = gdata_service_get_username (GDATA_SERVICE (self));
-
-	/* Build the upload URI and upload stream */
-	upload_uri = g_strdup_printf ("http://picasaweb.google.com/data/feed/api/user/%s/albumid/%s", user_id, album_id);
-	output_stream = gdata_upload_stream_new (GDATA_SERVICE (self), SOUP_METHOD_POST, upload_uri, GDATA_ENTRY (file_entry), slug, content_type);
-	g_free (upload_uri);
-	g_object_unref (file_info);
-
+	output_stream = get_file_output_stream (self, album, file_entry, file_data, error);
 	if (output_stream == NULL)
 		return NULL;
 
@@ -379,22 +406,220 @@ gdata_picasaweb_service_upload_file (GDataPicasaWebService *self, GDataPicasaWeb
 	}
 
 	g_output_stream_splice (output_stream, input_stream, G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
-				cancellable, error);
+				cancellable, &child_error);
 
 	g_object_unref (input_stream);
-	if (error != NULL && *error != NULL) {
-		/* Error! */
+	if (child_error != NULL) {
+		g_output_stream_close (output_stream, cancellable, NULL);
 		g_object_unref (output_stream);
+		g_propagate_error (error, child_error);
 		return NULL;
 	}
 
-	/* Get the response from the server */
-	response_body = gdata_upload_stream_get_response (GDATA_UPLOAD_STREAM (output_stream), &response_length);
-	g_assert (response_body != NULL && response_length > 0);
-
-	/* Parse the response to produce a GDataPicasaWebFile */
-	new_entry = GDATA_PICASAWEB_FILE (gdata_parsable_new_from_xml (GDATA_TYPE_PICASAWEB_FILE, response_body, (gint) response_length, error));
+	new_entry = parse_spliced_stream (output_stream, error);
 	g_object_unref (output_stream);
 
 	return new_entry;
 }
+
+/**
+ * gdata_picasaweb_service_upload_file_finish:
+ * @self: a #GDataPicasaWebService
+ * @result: a #GSimpleAsyncResult
+ * @error: a #GError, or %NULL
+ *
+ * This should be called to obtain the result of a call to
+ * gdata_picasaweb_service_upload_file_async() and to check for
+ * errors.
+ *
+ * If there is a problem reading the subect file's data, an error
+ * from g_output_stream_splice() or g_file_query_info() will be
+ * returned. Other errors from #GDataServiceError can be returned for
+ * other exceptional conditions, as determined by the server.  
+ *
+ * If the file to upload has already been inserted, a
+ * %GDATA_SERVICE_ERROR_ENTRY_ALREADY_INSERTED error will be set. If
+ * no user is authenticated with the service when trying to upload it,
+ * %GDATA_SERVICE_ERROR_AUTHENTICATION_REQUIRED will be set.
+ *
+ * Return value: the inserted #GDataPicasaWebFile; unref with
+ * g_object_unref()
+ *
+ * Since: 0.6.0
+ */
+GDataPicasaWebFile *
+gdata_picasaweb_service_upload_file_finish (GDataPicasaWebService *self, GSimpleAsyncResult *result, GError **error)
+{
+	GDataPicasaWebFile *file;
+	
+	/* TODO: this seems like something nice to check,
+	         but I can't seem to set the source_tag when the GSimpleAsyncResult is created from an error
+		 which means that when someone tries to call this function to get their errors, the result,
+		 when an error had occurred, we would trip the assert, since the source_tag wouldn't have been set.
+		 I think I'm doing this wrong :( 
+	g_assert (g_simple_async_result_get_source_tag (result) == gdata_picasaweb_service_upload_file_async);
+	*/
+
+	/* propagate any potential errors we might have encountered in g_output_stream_splice */
+	g_simple_async_result_propagate_error (result, error);
+
+	file = GDATA_PICASAWEB_FILE (g_simple_async_result_get_op_res_gpointer (result)); 
+	/* TODO: is this something we actually want to do?  GSimpleAsyncResult wanted a destroy function
+	         for this gpointer, so I assume it'll want to call it, but we don't want this to die on the user,
+		 so I suppose need to ref it again and let the user unref it when they're done? */
+	if (file != NULL) 
+		g_object_ref (file); /* increase the ref count since GSimpleAsyncResult also wants to unref it */
+
+	return file;
+}
+
+typedef struct {
+	GDataPicasaWebService *service;
+	UploadFileAsyncCallback callback;
+	gpointer user_data;
+} UploadFileAsyncData;
+
+static void
+upload_file_async_data_free (UploadFileAsyncData *data)
+{
+	g_object_unref (data->service);
+	g_slice_free (UploadFileAsyncData, data);
+}
+
+static void
+upload_file_async_cb (GOutputStream *output_stream, GSimpleAsyncResult *result, UploadFileAsyncData *data)
+{
+	GSimpleAsyncResult *async_result;
+	GError *error = NULL;
+	GDataPicasaWebFile *file = NULL;
+
+	/* TODO: I think @result is supposed to be a GAsyncResult according to GAsyncReadyCallback's signature,
+	   but I can't do much with that, and it seems to be a GSimpleAsyncResult anyway, so I'm treating it like 
+	   one, but it fills me with anxiety to do so :| */
+
+	/* We check if the result reports success, because our #GOutputStream might never have
+	   even gotten defined due to errors captured in gdata_picasaweb_service_upload_file_async */
+	if (g_simple_async_result_get_op_res_gboolean (result) == TRUE)
+		g_output_stream_splice_finish (output_stream, G_ASYNC_RESULT (result), &error);
+	else
+		g_simple_async_result_propagate_error (result, &error);
+
+	/* If we're error free, parse the file from the stream */
+	if (error == NULL) 
+		file = parse_spliced_stream (output_stream, &error);
+
+	if (error == NULL && file != NULL)
+		async_result = g_simple_async_result_new (G_OBJECT (data->service), (GAsyncReadyCallback)data->callback,
+							  data->user_data, gdata_picasaweb_service_upload_file_async);
+	else 
+		async_result = g_simple_async_result_new_from_error (G_OBJECT (data->service), (GAsyncReadyCallback)data->callback,
+								     data->user_data, error);
+
+	g_simple_async_result_set_op_res_gpointer (async_result, file, g_object_unref);
+
+	upload_file_async_data_free (data);
+	
+	g_simple_async_result_complete (async_result);
+}
+
+/**
+ * gdata_picasaweb_service_upload_file_async:
+ * @self: a #GDataPicasaWebService
+ * @album: a #GDataPicasaWebAlbum into which to insert the file, or %NULL
+ * @file_entry: a #GDataPicasaWebFile to insert
+ * @file_data: the actual file to upload
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @callback: a #UploadFileAsyncCallback to call when authentication is finished
+ * @user_data: data to pass to the @callback function
+ *
+ * Uploads a file (photo or video) to the given PicasaWeb @album
+ * asynchronously, using the @actual_file from disk and the metadata
+ * from @file. If @album is %NULL, the file will be uploaded to the
+ * currently-authenticated user's "Drop Box" album. A user must be
+ * authenticated to use this function.
+ *
+ * @callback should call gdata_picasaweb_service_upload_file_finish()
+ * to obtain a #GDataPicasaWebFile representing the uploaded file and
+ * check for possible errors.
+ *
+ * Since: 0.6.0
+ **/
+void
+gdata_picasaweb_service_upload_file_async (GDataPicasaWebService *self, GDataPicasaWebAlbum *album, GDataPicasaWebFile *file_entry,
+					   GFile *file_data, GCancellable *cancellable, UploadFileAsyncCallback callback, gpointer user_data)
+{
+	GOutputStream *output_stream;
+	GInputStream *input_stream;
+	UploadFileAsyncData *data;
+	GSimpleAsyncResult *result;
+	GError *error = NULL;
+
+	g_return_if_fail (GDATA_IS_PICASAWEB_SERVICE (self));
+	g_return_if_fail (GDATA_IS_PICASAWEB_FILE (file_entry));
+	g_return_if_fail (G_IS_FILE (file_data));
+	g_return_if_fail (callback != NULL);
+
+	data = g_slice_new (UploadFileAsyncData);
+	g_object_ref (self);
+	data->service = self;
+	data->callback = callback;
+	data->user_data = user_data;
+
+	/* TODO:
+	   how I'm handling these errors doesn't seem ideal.
+
+	   Is the correct way to propagate them to the user's callback
+	   to do this prompt our upload_file_async_cb myself, without
+	   the source object, and just check whether the result was
+	   successful or not in upload_file_async_cb?
+
+	   Also, GAsyncReadyCallback is only supposed to take a
+	   GAsyncResult, but if I want to propagate the errors I'm
+	   trying to pass from here, I seem to need a
+	   GSimpleAsyncResult.  I could set the error in data, and
+	   then create a GSimpleAsyncResult with upload_file_async_cb
+	   as the callback and complete the Result, and then know that
+	   whether g_output_stream_splice_async's GAsyncResult called
+	   upload_file_async_cb or my GSimpleAsyncResult called it,
+	   error could be found in my data, but that seems kind of ugly.
+	*/
+	if (gdata_entry_is_inserted (GDATA_ENTRY (file_entry)) == TRUE) {
+		g_set_error_literal (&error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_ENTRY_ALREADY_INSERTED,
+				     _("The entry has already been inserted."));
+		result = g_simple_async_result_new_from_error (NULL, (GAsyncReadyCallback)upload_file_async_cb, data, error);
+		g_simple_async_result_complete (result);
+		return;
+	}
+
+	if (gdata_service_is_authenticated (GDATA_SERVICE (self)) == FALSE) {
+		g_set_error_literal (&error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_AUTHENTICATION_REQUIRED,
+				     _("You must be authenticated to upload a file."));
+		result = g_simple_async_result_new_from_error (NULL, (GAsyncReadyCallback)upload_file_async_cb, data, error);
+		g_simple_async_result_complete (result);
+		return;
+	}
+
+	output_stream = get_file_output_stream (self, album, file_entry, file_data, &error);
+	if (output_stream == NULL) {
+		result = g_simple_async_result_new_from_error (NULL, (GAsyncReadyCallback)upload_file_async_cb, data, error);
+		g_simple_async_result_complete (result);
+		return;
+	}
+	
+	/* Pipe the input file to the upload stream */
+	input_stream = G_INPUT_STREAM (g_file_read (file_data, cancellable, &error));
+	if (input_stream == NULL) {
+		g_object_unref (output_stream);
+		result = g_simple_async_result_new_from_error (NULL, (GAsyncReadyCallback)upload_file_async_cb, data, error);
+		g_simple_async_result_complete (result);
+		return;
+	}
+
+	/* Actually transfer the data */
+	g_output_stream_splice_async (output_stream, input_stream, G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
+				      0, cancellable, (GAsyncReadyCallback)upload_file_async_cb, data);
+
+	g_object_unref (input_stream);
+	g_object_unref (output_stream);
+}
+
diff --git a/gdata/services/picasaweb/gdata-picasaweb-service.h b/gdata/services/picasaweb/gdata-picasaweb-service.h
index 4dc227d..8edef41 100644
--- a/gdata/services/picasaweb/gdata-picasaweb-service.h
+++ b/gdata/services/picasaweb/gdata-picasaweb-service.h
@@ -83,7 +83,52 @@ GDataFeed *gdata_picasaweb_service_query_files (GDataPicasaWebService *self, GDa
 
 GDataPicasaWebFile *gdata_picasaweb_service_upload_file (GDataPicasaWebService *self, GDataPicasaWebAlbum *album, GDataPicasaWebFile *file_entry,
 							 GFile *file_data, GCancellable *cancellable, GError **error) G_GNUC_WARN_UNUSED_RESULT;
-/* TODO: async version */
+
+/**
+ * UploadFileAsyncCallback:
+ * @self: a #GDataPicasaWebService
+ * @result: a #GSimpleAsyncResult
+ * @user_data: data from the user
+ *
+ * Pass a function of this type to
+ * gdata_picasaweb_service_upload_fine_async() to retrieve the upload
+ * result and check for errors.
+ *
+ * TODO: example might be done wrong, but will worry about that once I know whether any of the _async/_finish stuff is right
+ * TODO: does this symbol have to appear in any other files, like gdata.symbols?
+ * TODO: should this even get its own doc comment?  Other Callback's in devhelp don't seem to have them.  Perhaps its
+ *       usage should only be illustrated in an example for gdata_picasaweb_service_upload_file_async.
+ *
+ * <example>
+ * void my_upload_file_async_cb (GDataPicasaWebService *self, GSimpleAsyncResult *result, gpointer user_data) {
+ *         GError *error = NULL;
+ *         GDataPicasaWebFile *file = gdata_picasaweb_service_upload_file_finish (self, result, &error);
+ *         if (file == NULL) {
+ *                 /<!-- -->*  handle error *<!-- -->/
+ *         }
+ *         /<!-- -->* do something fun with file *<!-- -->/
+ * }
+ *
+ * int main (void) {
+ *         /<!-- -->* pre-upload stuff *<!-- -->/
+ *         gdata_picasaweb_service_upload_file_async (service, dest_album, new_file_entry, file_data, cancellable,
+ *                                                    my_upload_file_async_cb, NULL);
+ *         /<!-- -->* post-upload stuff *<!-- -->/
+ * }
+ * </example>
+ *
+ * Since: 0.6.0
+ */
+typedef void (*UploadFileAsyncCallback)(GDataPicasaWebService *self, GSimpleAsyncResult *result, gpointer user_data);
+   
+void
+gdata_picasaweb_service_upload_file_async (GDataPicasaWebService *self, GDataPicasaWebAlbum *album, GDataPicasaWebFile *file_entry,
+					   GFile *file_data, GCancellable *cancellable, UploadFileAsyncCallback callback, gpointer user_data);
+GDataPicasaWebFile *
+gdata_picasaweb_service_upload_file_finish (GDataPicasaWebService *self, GSimpleAsyncResult *result, GError **error) G_GNUC_WARN_UNUSED_RESULT;
+
+
+
 
 G_END_DECLS
 
diff --git a/gdata/tests/picasaweb.c b/gdata/tests/picasaweb.c
index 018dafa..485a90a 100644
--- a/gdata/tests/picasaweb.c
+++ b/gdata/tests/picasaweb.c
@@ -99,6 +99,102 @@ test_authentication_async (void)
 }
 
 static void
+test_upload_async_cb (GDataPicasaWebService *service, GSimpleAsyncResult *result, GMainLoop *main_loop)
+{
+	GDataPicasaWebFile *photo_new;
+	GError *error = NULL;
+
+	photo_new = gdata_picasaweb_service_upload_file_finish (service, result, &error);
+	g_assert_no_error (error);
+	g_assert (GDATA_IS_PICASAWEB_FILE (photo_new));
+	g_clear_error (&error);
+	g_assert (gdata_entry_is_inserted (GDATA_ENTRY (photo_new)));
+	
+	g_main_loop_quit (main_loop);
+
+	/* TODO: check entries and feed properties */
+
+	g_object_unref (photo_new);
+}
+
+static void
+test_upload_async (GDataService *service)
+{
+	GDataPicasaWebFile *photo;
+	GFile *photo_file;
+	GTimeVal timeval;
+	gchar *xml, *time_str, *summary, *expected_xml, *parsed_time_str;
+	GRegex *regex;
+	GMatchInfo *match_info;
+	guint64 delta;
+	GMainLoop *main_loop = g_main_loop_new (NULL, TRUE);
+
+
+	g_get_current_time (&timeval);
+	time_str = g_time_val_to_iso8601 (&timeval);
+	summary = g_strdup_printf ("Async Photo Summary (%s)", time_str);
+
+	expected_xml = g_strdup_printf ("<entry "
+						"xmlns='http://www.w3.org/2005/Atom' "
+						"xmlns:gphoto='http://schemas.google.com/photos/2007' "
+						"xmlns:media='http://search.yahoo.com/mrss/' "
+						"xmlns:gd='http://schemas.google.com/g/2005' "
+						"xmlns:exif='http://schemas.google.com/photos/exif/2007' "
+						"xmlns:app='http://www.w3.org/2007/app' "
+						"xmlns:georss='http://www.georss.org/georss' "
+						"xmlns:gml='http://www.opengis.net/gml'>"
+						"<title type='text'>Photo Entry Title</title>"
+						"<summary type='text'>Async Photo Summary \\(%s\\)</summary>"
+						"<gphoto:position>0</gphoto:position>"
+						"<gphoto:timestamp>([0-9]+)</gphoto:timestamp>"
+						"<gphoto:commentingEnabled>true</gphoto:commentingEnabled>"
+						"<media:group>"
+							"<media:title type='plain'>Photo Entry Title</media:title>"
+							"<media:description type='plain'>Async Photo Summary \\(%s\\)</media:description>"
+						"</media:group>"
+					"</entry>", time_str, time_str);
+	g_free (time_str);
+
+	/* Build a regex to match the timestamp from the XML, since we can't definitely say what it'll be */
+	regex = g_regex_new (expected_xml, 0, 0, NULL);
+	g_free (expected_xml);
+
+	/* Build the photo */
+	photo = gdata_picasaweb_file_new (NULL);
+	gdata_entry_set_title (GDATA_ENTRY (photo), "Photo Entry Title");
+	gdata_picasaweb_file_set_caption (photo, summary);
+
+	/* Check the XML: match it against the regex built above, then check that the timestamp is within 100ms of the current time at the start of
+	 * the test function. We can't check it exactly, as a few milliseconds may have passed inbetween building the expected_xml and building the XML
+	 * for the photo. */
+	xml = gdata_parsable_get_xml (GDATA_PARSABLE (photo));
+	g_assert (g_regex_match (regex, xml, 0, &match_info) == TRUE);
+	parsed_time_str = g_match_info_fetch (match_info, 1);
+	delta = g_ascii_strtoull (parsed_time_str, NULL, 10) - (((guint64) timeval.tv_sec) * 1000 + ((guint64) timeval.tv_usec) / 1000);
+	g_assert_cmpuint (abs (delta), <, 100);
+
+	g_free (parsed_time_str);
+	g_free (xml);
+	g_regex_unref (regex);
+	g_match_info_free (match_info);
+
+	gdata_picasaweb_file_set_coordinates (photo, 17.127, -110.35);
+
+	/* File is public domain: http://en.wikipedia.org/wiki/File:German_garden_gnome_cropped.jpg */
+	photo_file = g_file_new_for_path (TEST_FILE_DIR "photo.jpg");
+
+	/* Upload the photo */
+	gdata_picasaweb_service_upload_file_async (GDATA_PICASAWEB_SERVICE (service), NULL, photo, photo_file, NULL, (UploadFileAsyncCallback)test_upload_async_cb, main_loop);
+
+	g_main_loop_run (main_loop);
+	g_main_loop_unref (main_loop);
+
+	g_free (summary);
+	g_object_unref (photo);
+	g_object_unref (photo_file);
+}
+
+static void
 test_upload_simple (GDataService *service)
 {
 	GDataPicasaWebFile *photo, *photo_new;
@@ -776,6 +872,8 @@ main (int argc, char *argv[])
 	if (g_test_thorough () == TRUE)
 		g_test_add_func ("/picasaweb/authentication_async", test_authentication_async);
 	g_test_add_data_func ("/picasaweb/upload/photo", service, test_upload_simple);
+	if (g_test_thorough () == TRUE)
+		g_test_add_data_func ("/picasaweb/upload/photo_async", service, test_upload_async);
 	g_test_add_data_func ("/picasaweb/query/all_albums", service, test_query_all_albums);
 	g_test_add_data_func ("/picasaweb/query/user", service, test_query_user);
 	if (g_test_thorough () == TRUE)
