| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| #define spice_usbredir_write mock_spice_usbredir_write |
| #define spice_channel_get_state mock_spice_channel_get_state |
| #include "../src/usb-backend.c" |
| |
| #include "usb-device-cd.h" |
| |
| static SpiceUsbDevice *device = NULL; |
| |
| |
| static void |
| test_hotplug_callback(void *user_data, SpiceUsbDevice *dev, gboolean added) |
| { |
| |
| const UsbDeviceInformation *info = spice_usb_backend_device_get_info(dev); |
| if (info->bus != BUS_NUMBER_FOR_EMULATED_USB) { |
| return; |
| } |
| |
| if (added) { |
| g_assert_null(device); |
| device = spice_usb_backend_device_ref(dev); |
| } else { |
| g_assert_nonnull(device); |
| g_assert(device == dev); |
| spice_usb_backend_device_unref(dev); |
| device = NULL; |
| } |
| } |
| |
| static void multiple(const void *param) |
| { |
| guint limit = GPOINTER_TO_UINT(param); |
| CdEmulationParams params = { "test-cd-emu.iso", 1 }; |
| GError *err = NULL; |
| SpiceUsbBackend * be = spice_usb_backend_new(&err); |
| g_assert_nonnull(be); |
| g_assert_null(err); |
| spice_usb_backend_register_hotplug(be, NULL, test_hotplug_callback, &err); |
| g_assert_null(err); |
| for (int i = 0; i < limit; i++) { |
| |
| g_assert_true(create_emulated_cd(be, ¶ms, &err)); |
| g_assert_null(err); |
| g_assert_nonnull(device); |
| |
| |
| |
| spice_usb_backend_device_eject(be, device); |
| g_assert_null(device); |
| } |
| spice_usb_backend_deregister_hotplug(be); |
| spice_usb_backend_delete(be); |
| } |
| |
| static unsigned int messages_sent = 0; |
| static unsigned int hellos_sent = 0; |
| static SpiceUsbBackendChannel *usb_ch; |
| |
| int |
| mock_spice_usbredir_write(SpiceUsbredirChannel *channel, uint8_t *data, int count) |
| { |
| messages_sent++; |
| g_assert_cmpint(count, >=, 4); |
| const uint32_t type = data[0] + data[1] * 0x100u + data[2] * 0x10000u + data[3] * 0x1000000u; |
| if (type == usb_redir_hello) { |
| hellos_sent++; |
| } |
| |
| |
| spice_usb_backend_return_write_data(usb_ch, data); |
| return count; |
| } |
| |
| |
| static enum spice_channel_state ch_state = SPICE_CHANNEL_STATE_UNCONNECTED; |
| |
| enum spice_channel_state |
| mock_spice_channel_get_state(SpiceChannel *channel) |
| { |
| return ch_state; |
| } |
| |
| |
| static unsigned gobjects_allocated = 0; |
| static void decrement_allocated(gpointer data G_GNUC_UNUSED, GObject *old_gobject G_GNUC_UNUSED) |
| { |
| g_assert_cmpint(gobjects_allocated, !=, 0); |
| gobjects_allocated--; |
| } |
| |
| #define DATA_START \ |
| do { static const uint8_t data[] = { |
| #define DATA_SEND \ |
| }; \ |
| spice_usb_backend_read_guest_data(usb_ch, (uint8_t*)data, G_N_ELEMENTS(data)); \ |
| } while(0) |
| |
| static void |
| device_iteration(const int loop, const bool attach_on_connect) |
| { |
| GError *err = NULL; |
| unsigned int hellos_expected, messages_expected; |
| |
| hellos_expected = hellos_sent; |
| messages_expected = messages_sent; |
| |
| if (ch_state == SPICE_CHANNEL_STATE_UNCONNECTED) { |
| ch_state = SPICE_CHANNEL_STATE_CONNECTING; |
| } |
| if (attach_on_connect) { |
| g_assert_true(spice_usb_backend_channel_attach(usb_ch, device, &err)); |
| g_assert_null(err); |
| if (ch_state == SPICE_CHANNEL_STATE_READY) { |
| hellos_expected = MIN(hellos_expected + 1, 1); |
| messages_expected++; |
| } else { |
| g_assert_cmpint(messages_sent, ==, messages_expected); |
| } |
| } |
| g_assert_cmpint(hellos_sent, ==, hellos_expected); |
| g_assert_cmpint(messages_sent, >=, messages_expected); |
| |
| |
| if (ch_state == SPICE_CHANNEL_STATE_CONNECTING) { |
| ch_state = SPICE_CHANNEL_STATE_READY; |
| spice_usb_backend_channel_flush_writes(usb_ch); |
| hellos_expected = MIN(hellos_expected + 1, 1); |
| messages_expected++; |
| } |
| |
| |
| g_assert_cmpint(hellos_sent, ==, hellos_expected); |
| g_assert_cmpint(messages_sent, >=, messages_expected); |
| |
| spice_usb_backend_channel_flush_writes(usb_ch); |
| g_assert_cmpint(hellos_sent, ==, hellos_expected); |
| g_assert_cmpint(messages_sent, >=, messages_expected); |
| |
| |
| if (loop == 0) { |
| DATA_START |
| 0x00,0x00,0x00,0x00,0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
| 0x71,0x65,0x6d,0x75,0x20,0x75,0x73,0x62,0x2d,0x72,0x65,0x64, |
| 0x69,0x72,0x20,0x67,0x75,0x65,0x73,0x74,0x20,0x33,0x2e,0x30, |
| 0x2e,0x31,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
| 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
| 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
| 0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00, |
| DATA_SEND; |
| } |
| |
| if (!attach_on_connect) { |
| g_assert_true(spice_usb_backend_channel_attach(usb_ch, device, &err)); |
| g_assert_null(err); |
| } |
| g_assert_cmpint(hellos_sent, ==, 1); |
| g_assert_cmpint(messages_sent, >, 1); |
| |
| spice_usb_backend_channel_detach(usb_ch); |
| } |
| |
| static void attach(const void *param) |
| { |
| const bool attach_on_connect = !!(GPOINTER_TO_UINT(param) & 1); |
| const bool libusb_enabled = !!(GPOINTER_TO_UINT(param) & 2); |
| |
| hellos_sent = 0; |
| messages_sent = 0; |
| ch_state = SPICE_CHANNEL_STATE_UNCONNECTED; |
| |
| SpiceSession *session = spice_session_new(); |
| g_assert_nonnull(session); |
| g_object_weak_ref(G_OBJECT(session), decrement_allocated, NULL); |
| SpiceChannel *ch = spice_channel_new(session, SPICE_CHANNEL_USBREDIR, 0); |
| g_assert_nonnull(ch); |
| g_object_weak_ref(G_OBJECT(ch), decrement_allocated, NULL); |
| gobjects_allocated = 2; |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| GError *err = NULL; |
| SpiceUsbBackend * be = spice_usb_backend_new(&err); |
| g_assert_nonnull(be); |
| g_assert_null(err); |
| spice_usb_backend_register_hotplug(be, NULL, test_hotplug_callback, &err); |
| g_assert_null(err); |
| |
| CdEmulationParams params = { "test-cd-emu.iso", 1 }; |
| g_assert_true(create_emulated_cd(be, ¶ms, &err)); |
| g_assert_null(err); |
| g_assert_nonnull(device); |
| g_assert_false(device->edev_configured); |
| |
| void *libusb_context_saved = be->libusb_context; |
| if (!libusb_enabled) { |
| spice_usb_backend_deregister_hotplug(be); |
| be->libusb_context = NULL; |
| } |
| usb_ch = spice_usb_backend_channel_new(be, SPICE_USBREDIR_CHANNEL(ch)); |
| if (!libusb_enabled) { |
| be->libusb_context = libusb_context_saved; |
| spice_usb_backend_register_hotplug(be, NULL, test_hotplug_callback, &err); |
| g_assert_null(err); |
| } |
| g_assert_nonnull(usb_ch); |
| |
| for (int loop = 0; loop < 2; loop++) { |
| device_iteration(loop, attach_on_connect); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| spice_usb_backend_device_unref(device); |
| device = NULL; |
| spice_usb_backend_channel_delete(usb_ch); |
| usb_ch = NULL; |
| spice_usb_backend_deregister_hotplug(be); |
| spice_usb_backend_delete(be); |
| |
| |
| |
| spice_session_disconnect(session); |
| g_object_unref(session); |
| while (g_main_context_iteration(NULL, FALSE)) { |
| continue; |
| } |
| |
| g_assert_cmpint(gobjects_allocated, ==, 0); |
| } |
| |
| #define TEST_CD_ISO_FILE "test-cd-emu.iso" |
| |
| static void |
| write_test_iso(void) |
| { |
| uint8_t sector[2048]; |
| FILE *f = fopen(TEST_CD_ISO_FILE, "wb"); |
| g_assert_nonnull(f); |
| memset(sector, 0, sizeof(sector)); |
| strcpy((char*) sector, "sector 0"); |
| fwrite(sector, sizeof(sector), 1, f); |
| fclose(f); |
| } |
| |
| int main(int argc, char* argv[]) |
| { |
| write_test_iso(); |
| |
| g_test_init(&argc, &argv, NULL); |
| |
| g_test_add_data_func("/cd-emu/simple", GUINT_TO_POINTER(1), multiple); |
| g_test_add_data_func("/cd-emu/multiple", GUINT_TO_POINTER(128), multiple); |
| #define ATTACH_PARAM(auto_attach, libusb) \ |
| GUINT_TO_POINTER(!!(auto_attach) + 2 * !!(libusb)) |
| g_test_add_data_func("/cd-emu/attach_no_auto", ATTACH_PARAM(0, 1), attach); |
| g_test_add_data_func("/cd-emu/attach_auto", ATTACH_PARAM(1, 1), attach); |
| g_test_add_data_func("/cd-emu/attach_no_auto_no_libusb", ATTACH_PARAM(0, 0), attach); |
| g_test_add_data_func("/cd-emu/attach_auto_no_libusb", ATTACH_PARAM(1, 0), attach); |
| |
| int ret = g_test_run(); |
| |
| unlink(TEST_CD_ISO_FILE); |
| return ret; |
| } |