[Spice-devel] [spice-gtk v2 09/16] tests: file-transfer include simple tests
Victor Toso
victortoso at redhat.com
Mon May 23 11:50:46 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. That could cause a simple crash on CHANNEL_DEBUG
which this patch addresses.
---
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 c1d95c1..7f3a6b9 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -4,6 +4,7 @@ noinst_PROGRAMS =
TESTS = coroutine \
util \
session \
+ test-file-transfer \
$(NULL)
if WITH_PHODAV
@@ -35,6 +36,7 @@ util_SOURCES = util.c
coroutine_SOURCES = coroutine.c
session_SOURCES = session.c
pipe_SOURCES = pipe.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..5ef11a7
--- /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;
+ GList *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->tasks, g_list_free);
+
+ 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);
+}
+
+static gboolean
+agent_send_msg(gpointer user_data)
+{
+ AgentAsync *aa = user_data;
+ spice_file_transfer_task_handle_status(aa->xfer_task, &aa->msg);
+ g_free(aa);
+ return G_SOURCE_REMOVE;
+}
+
+static void
+agent_send_msg_async(SpiceFileTransferTask *xfer_task, guint32 result)
+{
+ AgentAsync *aa = g_new0(AgentAsync, 1);
+ aa->xfer_task = xfer_task;
+ aa->msg.result = result;
+ g_object_get(xfer_task, "id", &aa->msg.id, NULL);
+ g_timeout_add_full(G_PRIORITY_LOW, T10ns, agent_send_msg, aa, NULL);
+}
+
+#define agent_send_success_async(t) agent_send_msg_async(t, VD_AGENT_FILE_XFER_STATUS_SUCCESS)
+#define agent_send_more_data_async(t) agent_send_msg_async(t, VD_AGENT_FILE_XFER_STATUS_CAN_SEND_DATA)
+#define agent_send_cancel_async(t) agent_send_msg_async(t, VD_AGENT_FILE_XFER_STATUS_CANCELLED)
+#define agent_send_error_async(t) agent_send_msg_async(t, VD_AGENT_FILE_XFER_STATUS_ERROR)
+
+/*******************************************************************************
+ * TEST SIMPLE TRANSFER
+ ******************************************************************************/
+static void
+transfer_done(GObject *source_object, GAsyncResult *res, 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_flush_callback(SpiceFileTransferTask *xfer_task,
+ void *buffer,
+ gssize count,
+ gpointer user_data)
+{
+ spice_file_transfer_task_flush_done(xfer_task, NULL);
+ agent_send_success_async(xfer_task);
+}
+
+static void
+transfer_task_on_file_info(SpiceFileTransferTask *xfer_task, GFileInfo *info, gpointer data)
+{
+ guint32 id;
+ g_object_get (xfer_task, "id", &id, NULL);
+ agent_send_more_data_async(xfer_task);
+}
+
+static void
+test_simple_transfer(Fixture *f, gconstpointer user_data)
+{
+ GList *it;
+
+ f->tasks = spice_file_transfer_task_create_tasks(NULL,
+ f->files,
+ G_FILE_COPY_NONE,
+ f->cancellable,
+ transfer_flush_callback,
+ NULL,
+ transfer_done,
+ f);
+ for (it = f->tasks; it != NULL; it = it->next) {
+ SpiceFileTransferTask *xfer_task = SPICE_FILE_TRANSFER_TASK(it->data);
+ g_signal_connect(xfer_task, "file-info", G_CALLBACK(transfer_task_on_file_info), f);
+ spice_file_transfer_task_start_task(xfer_task);
+ }
+ g_main_loop_run (f->loop);
+}
+
+/* Tests summary:
+ *
+ * This tests are specific to SpiceFileTransferTask and how it handles the
+ * Cancelation from the client and Cancelation/Error that it can receive from
+ * the agent.
+ *
+ * Thanks to the helper spice_file_transfer_task_handle_status, we can simulate
+ * the agent responde fairly easy by calling this function with the expected
+ * message from agent in the test case.
+ *
+ * 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.) Channel-Main creates a SpiceFileTransferTask per File, with a
+ * flush_callback() function which is used to send data to the Agent.
+ * 3.) Channel-Main attach handlers to SpiceFileTransferTask signals such as
+ * - "file-info": needed to retrieve file information and to send this
+ * information to the Agent. The file-transfer protocol starts here with
+ * VD_AGENT_FILE_XFER_START message;
+ * - "finalized": needed to finalize the operation and resources used;
+ * 4-) Channel-Main start each task by calling spice_file_transfer_task_start_task()
+ * which starts the async IO read from the file.
+ * 5-) SpiceFileTransferTask calls flush_callback() everytime data is ready
+ * 6-) Channel-Main sends data to the agent and waits for
+ * VD_AGENT_FILE_XFER_STATUS_CAN_SEND_DATA (to send more data) or
+ * VD_AGENT_FILE_XFER_STATUS_SUCCESS in case transfer of specific file is over.
+ * 7-) SpiceFileTransferTask only finalizes its operation when receives success,
+ * error or cancel from Agent or upon cancel from client.
+ */
+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.5.5
More information about the Spice-devel
mailing list