From c1683c34638ad1340fb8f6c000a9fc82d3db94a9 Mon Sep 17 00:00:00 2001 From: Jonathon Jongsma Date: Tue, 8 Mar 2016 14:55:39 -0600 Subject: Add tests for usb-acl-helper --- tests/Makefile.am | 15 ++-- tests/mock-acl-helper.c | 94 ++++++++++++++++++++ tests/usb-acl-helper.c | 226 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 330 insertions(+), 5 deletions(-) create mode 100644 tests/mock-acl-helper.c create mode 100644 tests/usb-acl-helper.c diff --git a/tests/Makefile.am b/tests/Makefile.am index 19c02b6..24d45c9 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,18 +1,21 @@ NULL = -noinst_PROGRAMS = \ - coroutine \ +TESTS = coroutine \ util \ session \ + usb-acl-helper \ $(NULL) if WITH_PHODAV -noinst_PROGRAMS += pipe +TESTS += pipe endif -TESTS = $(noinst_PROGRAMS) +noinst_PROGRAMS = $(TESTS) \ + mock-acl-helper \ + $(NULL) AM_CPPFLAGS = \ + $(COMMON_CFLAGS) \ $(GIO_CFLAGS) \ -I$(top_srcdir)/src \ -I$(top_builddir)/src \ @@ -29,6 +32,8 @@ util_SOURCES = util.c coroutine_SOURCES = coroutine.c session_SOURCES = session.c pipe_SOURCES = pipe.c - +usb_acl_helper_SOURCES = usb-acl-helper.c +usb_acl_helper_CFLAGS = -DTESTDIR=\"$(abs_builddir)\" +mock_acl_helper_SOURCES = mock-acl-helper.c -include $(top_srcdir)/git.mk diff --git a/tests/mock-acl-helper.c b/tests/mock-acl-helper.c new file mode 100644 index 0000000..11268cb --- /dev/null +++ b/tests/mock-acl-helper.c @@ -0,0 +1,94 @@ +/* + Copyright (C) 2016 Red Hat, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + This program 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, see . +*/ + +#include "config.h" + +#include +#include +#include + +static int exit_status; +static int busnum, devnum; +static char path[PATH_MAX]; +static GMainLoop *loop; +static GDataInputStream *stdin_stream; + +static void cleanup(void) +{ + if (loop) + g_main_loop_quit(loop); +} + + +static void stdin_read_complete(GObject *src, GAsyncResult *res, gpointer data) +{ + char *s = NULL; + const char *response = NULL; + GError *err = NULL; + gsize len; + + s = g_data_input_stream_read_line_finish(G_DATA_INPUT_STREAM(src), res, + &len, &err); + + /* exit the program to return an early EOF to the caller */ + if (g_getenv("TEST_EOF")) + goto done; + + /* Don't return any response, but continue running to simulate a + * unresponsive binary */ + if (g_getenv("TEST_NORESPONSE")) + return; + + /* specify a particular resonse to be returned to the caller */ + response = g_getenv("TEST_RESPONSE"); + if (!response) + response = "SUCCESS"; + + fprintf(stdout, "%s\n", response); + fflush(stdout); + +done: + g_clear_error(&err); + g_free(s); + cleanup(); +} + +int main(void) +{ + GInputStream *stdin_unix_stream; + +#if !GLIB_CHECK_VERSION(2,36,0) + g_type_init(); +#endif + + loop = g_main_loop_new(NULL, FALSE); + + stdin_unix_stream = g_unix_input_stream_new(STDIN_FILENO, 0); + stdin_stream = g_data_input_stream_new(stdin_unix_stream); + g_data_input_stream_set_newline_type(stdin_stream, + G_DATA_STREAM_NEWLINE_TYPE_LF); + g_clear_object(&stdin_unix_stream); + g_data_input_stream_read_line_async(stdin_stream, G_PRIORITY_DEFAULT, NULL, + stdin_read_complete, NULL); + + g_main_loop_run(loop); + + g_object_unref(stdin_stream); + g_main_loop_unref(loop); + + return exit_status; +} diff --git a/tests/usb-acl-helper.c b/tests/usb-acl-helper.c new file mode 100644 index 0000000..41dda5d --- /dev/null +++ b/tests/usb-acl-helper.c @@ -0,0 +1,226 @@ +/* + 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_setenv("SPICE_USB_ACL_BINARY", TESTDIR"/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) +{ + 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) +{ + 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_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_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_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_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) +{ + /* 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) +{ + /* 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 (); +} + -- cgit