diff options
author | Benjamin Berg <bberg@redhat.com> | 2022-06-13 22:59:54 +0200 |
---|---|---|
committer | Richard Hughes <richard@hughsie.com> | 2022-06-15 16:59:45 +0100 |
commit | df0640f1a35f36d923cf11b2b778a5fbbe47f40f (patch) | |
tree | ef4f4d8bd7362f3e6dc129b088cc159087471671 /gusb | |
parent | 0ff5cca64c4ca45053ded5a81eecd1e5745c411d (diff) | |
download | gusb-df0640f1a35f36d923cf11b2b778a5fbbe47f40f.tar.gz |
gusb: Add some umockdev based tests
The primary purpose for this is to test the hotplug codepaths.
Diffstat (limited to 'gusb')
-rw-r--r-- | gusb/gusb-umockdev-test.c | 211 | ||||
-rw-r--r-- | gusb/meson.build | 13 |
2 files changed, 224 insertions, 0 deletions
diff --git a/gusb/gusb-umockdev-test.c b/gusb/gusb-umockdev-test.c new file mode 100644 index 0000000..23ee491 --- /dev/null +++ b/gusb/gusb-umockdev-test.c @@ -0,0 +1,211 @@ +/* + * libusb umockdev based tests + * + * Copyright (C) 2022 Benjamin Berg <bberg@redhat.com> + * + * This program 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 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; If not, see <http://www.gnu.org/licenses/>. + */ + +#include <glib.h> +#include <unistd.h> +#include <string.h> + +#include "gusb.h" + +#include "umockdev.h" + +#define UNUSED_DATA __attribute__ ((unused)) gconstpointer unused_data + +/* avoid leak reports inside assertions; leaking stuff on assertion failures does not matter in tests */ +#if !defined(__clang__) +#pragma GCC diagnostic ignored "-Wanalyzer-malloc-leak" +#pragma GCC diagnostic ignored "-Wanalyzer-file-leak" +#endif + +typedef struct { + UMockdevTestbed *testbed; + GUsbContext *ctx; +} UMockdevTestbedFixture; + +static void +test_fixture_setup (UMockdevTestbedFixture *fixture, UNUSED_DATA) +{ + fixture->testbed = umockdev_testbed_new (); + g_assert (fixture->testbed != NULL); +} + +static void +test_fixture_setup_empty (UMockdevTestbedFixture *fixture, UNUSED_DATA) +{ + test_fixture_setup (fixture, NULL); + fixture->ctx = g_usb_context_new (NULL); +} + +static void +test_fixture_teardown (UMockdevTestbedFixture *fixture, UNUSED_DATA) +{ + /* Break context -> device -> context cycle */ + if (fixture->ctx) + g_object_run_dispose (G_OBJECT (fixture->ctx)); + g_clear_object (&fixture->ctx); + g_clear_object (&fixture->testbed); + + /* Running the mainloop is needed to ensure everything is cleaned up. */ + while (g_main_context_iteration (NULL, FALSE)) { } +} + +static void +test_fixture_add_canon (UMockdevTestbedFixture *fixture) +{ + /* NOTE: There is no device file, so cannot be opened */ + + /* NOTE: add_device would not create a file, needed for device emulation */ + /* XXX: Racy, see https://github.com/martinpitt/umockdev/issues/173 */ + umockdev_testbed_add_from_string (fixture->testbed, + "P: /devices/usb1\n" + "N: bus/usb/001/001\n" + "E: SUBSYSTEM=usb\n" + "E: DRIVER=usb\n" + "E: BUSNUM=001\n" + "E: DEVNUM=001\n" + "E: DEVNAME=/dev/bus/usb/001/001\n" + "E: DEVTYPE=usb_device\n" + "A: bConfigurationValue=1\\n\n" + "A: busnum=1\\n\n" + "A: devnum=1\\n\n" + "A: bConfigurationValue=1\\n\n" + "A: speed=480\\n\n" + /* descriptor from a Canon PowerShot SX200; VID 04a9 PID 31c0 */ + "H: descriptors=" + "1201000200000040a904c03102000102" + "030109022700010100c0010904000003" + "06010100070581020002000705020200" + "020007058303080009\n", + NULL); +} + +static void +test_ctx_enumerate (UMockdevTestbedFixture *fixture, UNUSED_DATA) +{ + g_autoptr(GPtrArray) devices = NULL; + + test_fixture_add_canon (fixture); + + g_usb_context_enumerate (fixture->ctx); + + devices = g_usb_context_get_devices (fixture->ctx); + g_assert_cmpint (devices->len, ==, 1); +} + +static void +count_hotplug_event_cb (GUsbContext *context, GUsbDevice *device, gpointer user_data) +{ + int *counter = user_data; + + *counter += 1; +} + +static void +test_ctx_hotplug (UMockdevTestbedFixture *fixture, UNUSED_DATA) +{ + g_autoptr(GPtrArray) devices = NULL; + gint events = 0; + + g_signal_connect (fixture->ctx, "device-added", G_CALLBACK (count_hotplug_event_cb), &events); + + g_usb_context_enumerate (fixture->ctx); + + devices = g_usb_context_get_devices (fixture->ctx); + g_assert_cmpint (devices->len, ==, 0); + g_assert_cmpint (events, ==, 0); + g_clear_pointer (&devices, g_ptr_array_unref); + + test_fixture_add_canon (fixture); + /* Ensure the event was processed by helper thread. */ + g_usleep (G_USEC_PER_SEC / 2); + + /* Still not returned (and no event fired). */ + devices = g_usb_context_get_devices (fixture->ctx); + g_assert_cmpint (devices->len, ==, 0); + g_assert_cmpint (events, ==, 0); + g_clear_pointer (&devices, g_ptr_array_unref); + + /* Run mainloop, which causes the event to be processed. */ + while (g_main_context_iteration (NULL, FALSE)) { } + + devices = g_usb_context_get_devices (fixture->ctx); + g_assert_cmpint (events, ==, 1); + g_assert_cmpint (devices->len, ==, 1); + g_clear_pointer (&devices, g_ptr_array_unref); +} + +static void +test_ctx_hotplug_dispose (UMockdevTestbedFixture *fixture, UNUSED_DATA) +{ + g_autoptr(GPtrArray) devices = NULL; + gint events = 0; + + g_signal_connect (fixture->ctx, "device-added", G_CALLBACK (count_hotplug_event_cb), &events); + + g_usb_context_enumerate (fixture->ctx); + devices = g_usb_context_get_devices (fixture->ctx); + g_assert_cmpint (devices->len, ==, 0); + g_assert_cmpint (events, ==, 0); + g_clear_pointer (&devices, g_ptr_array_unref); + + test_fixture_add_canon (fixture); + /* Ensure the event was processed by helper thread. */ + g_usleep (G_USEC_PER_SEC / 2); + + /* Still not returned (and no event fired). */ + g_usb_context_enumerate (fixture->ctx); + devices = g_usb_context_get_devices (fixture->ctx); + g_assert_cmpint (devices->len, ==, 0); + g_assert_cmpint (events, ==, 0); + g_clear_pointer (&devices, g_ptr_array_unref); + + /* The idle handler is pending, we dispose our context reference */ + g_object_run_dispose (G_OBJECT (fixture->ctx)); + + /* Run mainloop, which causes the event to be processed. */ + while (g_main_context_iteration (NULL, FALSE)) { } + + /* But no signal is fired. */ + g_assert_cmpint (events, ==, 0); + + g_clear_object (&fixture->ctx); +} + +int +main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add("/gusb/ctx/enumerate", UMockdevTestbedFixture, NULL, + test_fixture_setup_empty, + test_ctx_enumerate, + test_fixture_teardown); + + g_test_add("/gusb/ctx/hotplug", UMockdevTestbedFixture, NULL, + test_fixture_setup_empty, + test_ctx_hotplug, + test_fixture_teardown); + + g_test_add("/gusb/ctx/hotplug-dispose", UMockdevTestbedFixture, NULL, + test_fixture_setup_empty, + test_ctx_hotplug_dispose, + test_fixture_teardown); + + return g_test_run(); +} diff --git a/gusb/meson.build b/gusb/meson.build index b4fa382..243b0be 100644 --- a/gusb/meson.build +++ b/gusb/meson.build @@ -214,4 +214,17 @@ if get_option('tests') ], ) test('gusb-self-test', e) + + # Umockdev based tests + test_env = environment() + test_env.prepend('LD_PRELOAD', 'libumockdev-preload.so.0') + + test_umockdev = executable( + 'gusb-umockdev-test', + 'gusb-umockdev-test.c', + dependencies: [ umockdev, gusb_dep ]) + + test('gusb-umockdev-test', + test_umockdev, + env: test_env) endif |