[Spice-devel] [spice-gtk v5 20/23] tests: file-transfer include simple tests

Victor Toso victortoso at redhat.com
Tue Jul 5 13:07:27 UTC 2016


This only includes a simple test for file-transfer with a small
summary of the possible situations of the test.

As the test is specifically for SpiceFileTransferTask, we don't create
a SpiceMainChannel.
---
 tests/Makefile.am     |   2 +
 tests/file-transfer.c | 190 ++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 192 insertions(+)
 create mode 100644 tests/file-transfer.c

diff --git a/tests/Makefile.am b/tests/Makefile.am
index 1a8b768..6d9cfeb 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -5,6 +5,7 @@ TESTS = coroutine				\
 	util					\
 	session					\
 	test-spice-uri				\
+	test-file-transfer			\
 	$(NULL)
 
 if WITH_PHODAV
@@ -38,6 +39,7 @@ coroutine_SOURCES = coroutine.c
 session_SOURCES = session.c
 pipe_SOURCES = pipe.c
 test_spice_uri_SOURCES = uri.c
+test_file_transfer_SOURCES = file-transfer.c
 usb_acl_helper_SOURCES = usb-acl-helper.c
 usb_acl_helper_CFLAGS = -DTESTDIR=\"$(abs_builddir)\"
 mock_acl_helper_SOURCES = mock-acl-helper.c
diff --git a/tests/file-transfer.c b/tests/file-transfer.c
new file mode 100644
index 0000000..e59069e
--- /dev/null
+++ b/tests/file-transfer.c
@@ -0,0 +1,190 @@
+#include <gio/gio.h>
+
+#include "spice-file-transfer-task-priv.h"
+
+typedef struct _Fixture {
+    GFile         **files;
+    guint           num_files;
+    guint           num_files_done;
+    GCancellable   *cancellable;
+    GMainLoop      *loop;
+    GHashTable     *xfer_tasks;
+} Fixture;
+
+typedef struct _AgentAsync {
+    SpiceFileTransferTask          *xfer_task;
+    VDAgentFileXferStatusMessage    msg;
+} AgentAsync;
+
+#define SINGLE_FILE     1
+#define MULTIPLE_FILES  10
+
+#define T10ns (G_TIME_SPAN_MILLISECOND / 100)
+
+const gchar content[] = "0123456789_spice-file-transfer-task";
+
+static void
+f_setup(Fixture *f, gconstpointer user_data)
+{
+    gint i;
+    GError *err = NULL;
+
+    f->loop = g_main_loop_new(NULL, FALSE);
+    f->num_files = GPOINTER_TO_UINT(user_data);
+    f->num_files_done = 0;
+    f->files = g_new0(GFile *, f->num_files + 1);
+    f->cancellable = g_cancellable_new();
+    for (i = 0; i < f->num_files; i++) {
+        gboolean success;
+        GFileIOStream *iostream;
+
+        f->files[i] = g_file_new_tmp("spice-file-transfer-XXXXXX", &iostream, &err);
+        g_assert_no_error(err);
+        g_assert_nonnull(iostream);
+        g_clear_object(&iostream);
+
+        success = g_file_replace_contents (f->files[i], content, strlen(content), NULL, FALSE,
+                                           G_FILE_CREATE_NONE, NULL, f->cancellable, &err);
+        g_assert_no_error(err);
+        g_assert_true(success);
+    }
+}
+
+static void
+f_teardown(Fixture *f, gconstpointer user_data)
+{
+    gint i;
+    GError *err = NULL;
+
+    g_main_loop_unref(f->loop);
+    g_clear_object(&f->cancellable);
+    g_clear_pointer(&f->xfer_tasks, g_hash_table_unref);
+
+    for (i = 0; i < f->num_files; i++) {
+        g_file_delete(f->files[i], NULL, &err);
+        g_assert_no_error(err);
+        g_object_unref(f->files[i]);
+    }
+    g_clear_pointer(&f->files, g_free);
+}
+
+/*******************************************************************************
+ * TEST SIMPLE TRANSFER
+ ******************************************************************************/
+static void
+transfer_xfer_task_on_finished(SpiceFileTransferTask *xfer_task,
+                               GError *error,
+                               gpointer user_data)
+{
+    Fixture *f = user_data;
+
+    f->num_files_done++;
+    if (f->num_files == f->num_files_done)
+        g_main_loop_quit(f->loop);
+}
+
+static void
+transfer_read_async_cb(GObject *source_object,
+                       GAsyncResult *res,
+                       gpointer user_data)
+{
+    SpiceFileTransferTask *xfer_task;
+    gssize count;
+    char *buffer;
+    GError *error = NULL;
+
+    xfer_task = SPICE_FILE_TRANSFER_TASK(source_object);
+    count = spice_file_transfer_task_read_finish(xfer_task, res, &buffer, &error);
+    g_assert_no_error(error);
+
+    if (count == 0) {
+        spice_file_transfer_task_completed(xfer_task, NULL);
+        return;
+    }
+
+    spice_file_transfer_task_read_async(xfer_task, transfer_read_async_cb, NULL);
+}
+
+static void
+transfer_init_async_cb(GObject *obj, GAsyncResult *res, gpointer data)
+{
+    GFileInfo *info;
+    SpiceFileTransferTask *xfer_task;
+    GError *error = NULL;
+
+    xfer_task = SPICE_FILE_TRANSFER_TASK(obj);
+    info = spice_file_transfer_task_init_task_finish(xfer_task, res, &error);
+    g_assert_no_error(error);
+    g_assert_nonnull(info);
+
+    /* read file loop */
+    spice_file_transfer_task_read_async(xfer_task, transfer_read_async_cb, NULL);
+}
+
+static void
+test_simple_transfer(Fixture *f, gconstpointer user_data)
+{
+    GHashTableIter iter;
+    gpointer key, value;
+
+    f->xfer_tasks = spice_file_transfer_task_create_tasks(f->files, NULL, G_FILE_COPY_NONE, f->cancellable);
+    g_hash_table_iter_init(&iter, f->xfer_tasks);
+    while (g_hash_table_iter_next(&iter, &key, &value)) {
+        SpiceFileTransferTask *xfer_task = SPICE_FILE_TRANSFER_TASK(value);
+        g_signal_connect(xfer_task, "finished", G_CALLBACK(transfer_xfer_task_on_finished), f);
+        spice_file_transfer_task_init_task_async(xfer_task, transfer_init_async_cb, NULL);
+    }
+    g_main_loop_run (f->loop);
+}
+
+/* Tests summary:
+ *
+ * This tests are specific to SpiceFileTransferTask in order to verify:
+ * - Cancelation from User;
+ * - Error/Cancelation from Agent;
+ * - Bad behavior from Agent;
+ *
+ * SpiceFileTransferTask is in charge of initializing, reading and finalizing
+ * all IO for the file user wants to transfer but being unaware of how the
+ * protocol works. As there are several combinations of events, these tests
+ * intend to find errors, leaks and crashes on SpiceFileTransferTask in common
+ * set of events in order to avoid regression in the drag-and-drop feature.
+ *
+ * Small overview of how File Transfer works.
+ *
+ * 1.) User calls spice_main_file_copy_async with a list of files to send to the
+ *     guest
+ * 2.) SpiceMainChannel creates a SpiceFileTransferTask per File and request its
+ *     initialization using spice_file_transfer_task_init_task_async(). The init
+ *     function will open a GFileInpustStream and request the GFileInfo of file;
+ * 3.) Using the GFileInfo object, SpiceMainChannel starts the File Transfer
+ *     protocol with VD_AGENT_FILE_XFER_START. Agent responds with
+ *     VD_AGENT_FILE_XFER_STATUS_CAN_SEND_DATA which starts the read IO using
+ *     spice_file_transfer_task_read_async()
+ * 4.) After the read is done, SpiceMainChannel does an async flush to the
+ *     agent, using the buffer provided by SpiceFileTransferTask; The read IO
+ *     will be kept going while SpiceMainChannel has agent tokens to use.
+ * 5-) After SpiceMainChannel sends enough data, it can always receive:
+ *     - VD_AGENT_FILE_XFER_STATUS_CAN_SEND_DATA: to send more data;
+ *     - VD_AGENT_FILE_XFER_STATUS_SUCCESS: all data was sent;
+ *     - VD_AGENT_FILE_XFER_STATUS_ERROR: unexpected behavior on agent side;
+ *     - VD_AGENT_FILE_XFER_STATUS_CANCELLED: transfer was cancelled by agent;
+ * 6-) In any case of termination, the following SpiceFileTransferTask function
+ *     should always be called: spice_file_transfer_task_completed(); This will
+ *     trigger from SpiceFileTransferTask all necessary events to finalize user
+ *     side, memory allocation and so on.
+ */
+int main(int argc, char* argv[])
+{
+    g_test_init(&argc, &argv, NULL);
+
+    g_test_add("/spice-file-transfer-task/single/simple-transfer",
+               Fixture, GUINT_TO_POINTER(SINGLE_FILE),
+               f_setup, test_simple_transfer, f_teardown);
+
+    g_test_add("/spice-file-transfer-task/multiple/simple-transfer",
+               Fixture, GUINT_TO_POINTER(MULTIPLE_FILES),
+               f_setup, test_simple_transfer, f_teardown);
+
+    return g_test_run();
+}
-- 
2.7.4



More information about the Spice-devel mailing list