/*
Copyright (C) 2016 Red Hat, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see .
*/
#include
#include "usb-acl-helper.h"
typedef struct {
SpiceUsbAclHelper *acl_helper;
GCancellable *cancellable;
GMainLoop *loop;
guint timeout_source;
} Fixture;
gboolean abort_test(gpointer user_data)
{
Fixture *fixture = user_data;
g_cancellable_cancel(fixture->cancellable);
fixture->timeout_source = 0;
return G_SOURCE_REMOVE;
}
gboolean cancel_test(gpointer user_data)
{
Fixture *fixture = user_data;
g_cancellable_cancel(fixture->cancellable);
return G_SOURCE_REMOVE;
}
static void data_setup(Fixture *fixture, gconstpointer user_data G_GNUC_UNUSED)
{
g_setenv("SPICE_USB_ACL_BINARY", TESTDIR"/test-mock-acl-helper", TRUE);
fixture->cancellable = g_cancellable_new();
fixture->acl_helper = spice_usb_acl_helper_new();
fixture->loop = g_main_loop_new(NULL, FALSE);
/* abort test after 2 seconds if it hasn't yet completed */
fixture->timeout_source = g_timeout_add_seconds(2, abort_test, fixture);
}
static void data_teardown(Fixture *fixture, gconstpointer user_data G_GNUC_UNUSED)
{
if (fixture->timeout_source)
g_source_remove(fixture->timeout_source);
g_object_unref(fixture->cancellable);
g_object_unref(fixture->acl_helper);
g_main_loop_unref(fixture->loop);
g_unsetenv("SPICE_USB_ACL_BINARY");
}
static void success_cb(GObject *source, GAsyncResult *result, gpointer user_data)
{
Fixture *f = user_data;
GError *error = NULL;
if (!spice_usb_acl_helper_open_acl_finish(SPICE_USB_ACL_HELPER(source), result, &error))
g_error("%s", error->message);
g_main_loop_quit(f->loop);
}
static void test_acl_helper_success(Fixture *fixture, gconstpointer user_data G_GNUC_UNUSED)
{
spice_usb_acl_helper_open_acl_async(fixture->acl_helper, 1, 1,
fixture->cancellable, success_cb, fixture);
g_main_loop_run(fixture->loop);
}
static void spawn_fail_cb(GObject *source, GAsyncResult *result, gpointer user_data)
{
Fixture *f = user_data;
GError *error = NULL;
gboolean success = spice_usb_acl_helper_open_acl_finish(SPICE_USB_ACL_HELPER(source), result, &error);
g_assert(!success);
g_assert (error->domain == G_SPAWN_ERROR);
g_clear_error(&error);
g_main_loop_quit(f->loop);
}
static void test_acl_helper_spawn_fail(Fixture *fixture, gconstpointer user_data G_GNUC_UNUSED)
{
g_setenv("SPICE_USB_ACL_BINARY", "does-not-exist", TRUE);
spice_usb_acl_helper_open_acl_async(fixture->acl_helper, 1, 1,
fixture->cancellable, spawn_fail_cb,
fixture);
g_main_loop_run(fixture->loop);
}
static void early_eof_cb(GObject *source, GAsyncResult *result, gpointer user_data)
{
Fixture *f = user_data;
GError *error = NULL;
gboolean success = spice_usb_acl_helper_open_acl_finish(SPICE_USB_ACL_HELPER(source), result, &error);
g_assert(!success);
g_assert(error->domain == SPICE_CLIENT_ERROR);
g_assert(error->code == SPICE_CLIENT_ERROR_FAILED);
g_clear_error(&error);
g_main_loop_quit(f->loop);
}
/* helper sends EOF before sending a response */
static void test_acl_helper_early_eof(Fixture *fixture, gconstpointer user_data G_GNUC_UNUSED)
{
g_setenv("TEST_EOF", "1", TRUE);
spice_usb_acl_helper_open_acl_async(fixture->acl_helper, 1, 1,
fixture->cancellable, early_eof_cb, fixture);
g_main_loop_run(fixture->loop);
g_unsetenv("TEST_EOF");
}
static void helper_canceled_cb(GObject *source, GAsyncResult *result, gpointer user_data)
{
Fixture *f = user_data;
GError *error = NULL;
gboolean success = spice_usb_acl_helper_open_acl_finish(SPICE_USB_ACL_HELPER(source), result, &error);
g_assert(!success);
g_assert(error->domain == G_IO_ERROR);
g_assert(error->code == G_IO_ERROR_CANCELLED);
g_clear_error(&error);
g_main_loop_quit(f->loop);
}
static void test_acl_helper_helper_canceled(Fixture *fixture, gconstpointer user_data G_GNUC_UNUSED)
{
g_setenv("TEST_RESPONSE", "CANCELED", TRUE);
spice_usb_acl_helper_open_acl_async(fixture->acl_helper, 1, 1,
fixture->cancellable, helper_canceled_cb, fixture);
g_main_loop_run(fixture->loop);
g_unsetenv("TEST_RESPONSE");
}
static void helper_error_response_cb(GObject *source, GAsyncResult *result, gpointer user_data)
{
Fixture *f = user_data;
GError *error = NULL;
gboolean success = spice_usb_acl_helper_open_acl_finish(SPICE_USB_ACL_HELPER(source), result, &error);
g_assert(!success);
g_assert(error->domain == SPICE_CLIENT_ERROR);
g_assert(error->code == SPICE_CLIENT_ERROR_FAILED);
g_clear_error(&error);
g_main_loop_quit(f->loop);
}
static void test_acl_helper_error_response(Fixture *fixture, gconstpointer user_data G_GNUC_UNUSED)
{
g_setenv("TEST_RESPONSE", "Not authorized", TRUE);
spice_usb_acl_helper_open_acl_async(fixture->acl_helper, 1, 1,
fixture->cancellable, helper_error_response_cb, fixture);
g_main_loop_run(fixture->loop);
g_unsetenv("TEST_RESPONSE");
}
static void client_canceled_cb(GObject *source, GAsyncResult *result, gpointer user_data)
{
Fixture *f = user_data;
GError *error = NULL;
gboolean success = spice_usb_acl_helper_open_acl_finish(SPICE_USB_ACL_HELPER(source), result, &error);
g_assert(!success);
g_assert(error->domain == G_IO_ERROR);
g_assert(error->code == G_IO_ERROR_CANCELLED);
g_clear_error(&error);
g_main_loop_quit(f->loop);
}
static void test_acl_helper_client_canceled(Fixture *fixture, gconstpointer user_data G_GNUC_UNUSED)
{
/* ensure that the acl-helper does not have respond, so we can cancel the
* task before we get a response from the helper binary */
g_setenv("TEST_NORESPONSE", "1", TRUE);
spice_usb_acl_helper_open_acl_async(fixture->acl_helper, 1, 1,
fixture->cancellable, client_canceled_cb, fixture);
g_idle_add(cancel_test, fixture);
g_main_loop_run(fixture->loop);
g_unsetenv("TEST_NORESPONSE");
}
static void test_acl_helper_no_response(Fixture *fixture, gconstpointer user_data G_GNUC_UNUSED)
{
/* ensure that the acl-helper does not have respond, so we can cancel the
* task before we get a response from the helper binary */
g_setenv("TEST_NORESPONSE", "1", TRUE);
spice_usb_acl_helper_open_acl_async(fixture->acl_helper, 1, 1,
fixture->cancellable, client_canceled_cb, fixture);
g_main_loop_run(fixture->loop);
g_unsetenv("TEST_NORESPONSE");
}
int main(int argc, char* argv[])
{
g_test_init(&argc, &argv, NULL);
g_test_add("/usb-acl-helper/success", Fixture, NULL,
data_setup, test_acl_helper_success, data_teardown);
g_test_add("/usb-acl-helper/spawn-fail", Fixture, NULL,
data_setup, test_acl_helper_spawn_fail, data_teardown);
g_test_add("/usb-acl-helper/early-eof", Fixture, NULL,
data_setup, test_acl_helper_early_eof, data_teardown);
g_test_add("/usb-acl-helper/helper-canceled", Fixture, NULL,
data_setup, test_acl_helper_helper_canceled, data_teardown);
g_test_add("/usb-acl-helper/helper-error", Fixture, NULL,
data_setup, test_acl_helper_error_response, data_teardown);
g_test_add("/usb-acl-helper/client-canceled", Fixture, NULL,
data_setup, test_acl_helper_client_canceled, data_teardown);
g_test_add("/usb-acl-helper/no-response", Fixture, NULL,
data_setup, test_acl_helper_no_response, data_teardown);
/* additional possible test cases:
* - unable to set nonblocking flag on io channel?
* - unable to write bus number to helper binary
* - unable to flush channel
* - read_line from helper binary returns something other than G_IO_STATUS_NORMAL
*/
return g_test_run ();
}