/* SPDX-License-Identifier: MIT */ /* * Copyright © 2020 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include "config.h" #include #include #include "util-munit.h" #include "util-strings.h" #include "eierpecken.h" MUNIT_TEST(test_ei_ref_unref) { struct ei *ei = ei_new(NULL); struct ei *refd = ei_ref(ei); munit_assert_ptr_equal(ei, refd); struct ei *unrefd = ei_unref(ei); munit_assert_ptr_null(unrefd); unrefd = ei_unref(ei); munit_assert_ptr_null(unrefd); /* memleak only shows up in valgrind */ return MUNIT_OK; } MUNIT_TEST(test_ei_disconnect_immediately) { _unref_(peck) *peck = peck_new(); /* Client is immediately rejected */ peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_REJECT_CLIENT); peck_dispatch_until_stable(peck); /* Expect the client to get a disconnect event */ with_client(peck) { ei_dispatch(ei); _unref_(ei_event) *disconnect = peck_ei_next_event(ei, EI_EVENT_DISCONNECT); } return MUNIT_OK; } MUNIT_TEST(test_ei_disconnect_self_immediately) { _unref_(peck) *peck = peck_new(); peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_NONE); peck_dispatch_until_stable(peck); /* Disconnect before server processed CONNECT */ with_client(peck) { peck_drop_ei(peck); ei_unref(ei); } peck_dispatch_until_stable(peck); /* Expect the client to get a disconnect event */ with_server(peck) { _unref_(eis_event) *connect = peck_eis_next_event(eis, EIS_EVENT_CLIENT_CONNECT); _unref_(eis_event) *disconnect = peck_eis_next_event(eis, EIS_EVENT_CLIENT_DISCONNECT); } return MUNIT_OK; } MUNIT_TEST(test_ei_disconnect_after_connect) { _unref_(peck) *peck = peck_new(); _unref_(eis_client) *client = NULL; peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_NONE); peck_dispatch_until_stable(peck); with_server(peck) { eis_dispatch(eis); _unref_(eis_event) *e = peck_eis_next_event(eis, EIS_EVENT_CLIENT_CONNECT); client = eis_client_ref(eis_event_get_client(e)); eis_client_connect(client); } with_client(peck) { ei_dispatch(ei); _unref_(ei_event) *e = peck_ei_next_event(ei, EI_EVENT_CONNECT); } with_server(peck) { eis_client_disconnect(client); } with_client(peck) { ei_dispatch(ei); _unref_(ei_event) *e = peck_ei_next_event(ei, EI_EVENT_DISCONNECT); } return MUNIT_OK; } MUNIT_TEST(test_ei_disconnect_self_after_connect) { _unref_(peck) *peck = peck_new(); peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_NONE); peck_dispatch_until_stable(peck); with_client(peck) { peck_drop_ei(peck); ei_unref(ei); } peck_dispatch_until_stable(peck); with_server(peck) { _unref_(eis_event) *connect = peck_eis_next_event(eis, EIS_EVENT_CLIENT_CONNECT); _unref_(eis_event) *disconnect = peck_eis_next_event(eis, EIS_EVENT_CLIENT_DISCONNECT); } return MUNIT_OK; } MUNIT_TEST(test_ei_disconnect_after_seat) { _unref_(peck) *peck = peck_new(); peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_CLIENT); peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_DEFAULT_SEAT); peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_NONE); peck_dispatch_until_stable(peck); with_client(peck) { _unref_(ei_event) *connect = peck_ei_next_event(ei, EI_EVENT_CONNECT); _unref_(ei_event) *seat = peck_ei_next_event(ei, EI_EVENT_SEAT_ADDED); } with_server(peck) { struct eis_client *client = peck_eis_get_default_client(peck); eis_client_disconnect(client); } peck_dispatch_until_stable(peck); with_client(peck) { _unref_(ei_event) *seat = peck_ei_next_event(ei, EI_EVENT_SEAT_REMOVED); _unref_(ei_event) *disconnect = peck_ei_next_event(ei, EI_EVENT_DISCONNECT); } return MUNIT_OK; } MUNIT_TEST(test_ei_disconnect_self_after_seat) { _unref_(peck) *peck = peck_new(); peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_CLIENT); peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_DEFAULT_SEAT); peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_NONE); peck_dispatch_until_stable(peck); with_client(peck) { _unref_(ei_event) *connect = peck_ei_next_event(ei, EI_EVENT_CONNECT); _unref_(ei_event) *seat = peck_ei_next_event(ei, EI_EVENT_SEAT_ADDED); peck_drop_ei(peck); /* Disconnect from client */ ei_unref(ei); /* There is no way to disconnect from the server without * destroying the context, so we don't care about the actual * events here */ } peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_NONE); peck_dispatch_until_stable(peck); with_server(peck) { _unref_(eis_event) *disconnect = peck_eis_next_event(eis, EIS_EVENT_CLIENT_DISCONNECT); } return MUNIT_OK; } MUNIT_TEST(test_ei_disconnect_after_bind_before_received) { _unref_(peck) *peck = peck_new(); peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_CLIENT); peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_DEFAULT_SEAT); peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_NONE); peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_CONNECT); peck_dispatch_until_stable(peck); with_client(peck) { _unref_(ei_event) *event = peck_ei_next_event(ei, EI_EVENT_SEAT_ADDED); struct ei_seat *seat = ei_event_get_seat(event); ei_seat_bind_capabilities(seat, EI_DEVICE_CAP_POINTER, NULL); } /* We have *not* called eis_dispatch, so the seat bind hasn't been * processed by the server yet */ with_server(peck) { struct eis_client *client = peck_eis_get_default_client(peck); eis_client_disconnect(client); } with_client(peck) { ei_dispatch(ei); _unref_(ei_event) *seat_removed = peck_ei_next_event(ei, EI_EVENT_SEAT_REMOVED); _unref_(ei_event) *disconnect = peck_ei_next_event(ei, EI_EVENT_DISCONNECT); } return MUNIT_OK; } MUNIT_TEST(test_ei_disconnect_self_after_bind_before_received) { _unref_(peck) *peck = peck_new(); peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_CLIENT); peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_DEFAULT_SEAT); peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_NONE); peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_CONNECT); peck_dispatch_until_stable(peck); with_client(peck) { _unref_(ei_event) *event = peck_ei_next_event(ei, EI_EVENT_SEAT_ADDED); struct ei_seat *seat = ei_event_get_seat(event); ei_seat_bind_capabilities(seat, EI_DEVICE_CAP_POINTER, NULL); /* Disconnect before the server can process the bind event */ peck_drop_ei(peck); ei_unref(ei); } peck_dispatch_eis(peck); with_server(peck) { /* Server got the bind event but client disconnected * immediately after */ _unref_(eis_event) *bind = peck_eis_next_event(eis, EIS_EVENT_SEAT_BIND); _unref_(eis_event) *unbind = peck_eis_next_event(eis, EIS_EVENT_SEAT_BIND); munit_assert_false(eis_event_seat_has_capability(unbind, EIS_DEVICE_CAP_POINTER)); munit_assert_false(eis_event_seat_has_capability(unbind, EIS_DEVICE_CAP_POINTER_ABSOLUTE)); munit_assert_false(eis_event_seat_has_capability(unbind, EIS_DEVICE_CAP_KEYBOARD)); munit_assert_false(eis_event_seat_has_capability(unbind, EIS_DEVICE_CAP_TOUCH)); _unref_(eis_event) *disconnect = peck_eis_next_event(eis, EIS_EVENT_CLIENT_DISCONNECT); } return MUNIT_OK; } MUNIT_TEST(test_ei_disconnect_after_bind_after_received) { _unref_(peck) *peck = peck_new(); peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_CLIENT); peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_DEFAULT_SEAT); peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_NONE); peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_CONNECT); peck_dispatch_until_stable(peck); with_client(peck) { _unref_(ei_event) *event = peck_ei_next_event(ei, EI_EVENT_SEAT_ADDED); struct ei_seat *seat = ei_event_get_seat(event); ei_seat_bind_capabilities(seat, EI_DEVICE_CAP_POINTER, NULL); } /* Receive the Bind event but don't actually add any devices, * disconnect the client instead */ peck_dispatch_eis(peck); with_server(peck) { struct eis_client *client = peck_eis_get_default_client(peck); eis_client_disconnect(client); } with_client(peck) { ei_dispatch(ei); _unref_(ei_event) *seat_removed = peck_ei_next_event(ei, EI_EVENT_SEAT_REMOVED); _unref_(ei_event) *disconnect = peck_ei_next_event(ei, EI_EVENT_DISCONNECT); } return MUNIT_OK; } MUNIT_TEST(test_ei_disconnect_self_after_bind_after_received) { _unref_(peck) *peck = peck_new(); peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_CLIENT); peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_DEFAULT_SEAT); peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_NONE); peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_CONNECT); peck_dispatch_until_stable(peck); with_client(peck) { _unref_(ei_event) *event = peck_ei_next_event(ei, EI_EVENT_SEAT_ADDED); struct ei_seat *seat = ei_event_get_seat(event); ei_seat_bind_capabilities(seat, EI_DEVICE_CAP_POINTER, NULL); } /* Make sure server sees Bind, then disconnect from server */ peck_dispatch_eis(peck); with_client(peck) { peck_drop_ei(peck); ei_unref(ei); } peck_dispatch_eis(peck); with_server(peck) { _unref_(eis_event) *bind = peck_eis_next_event(eis, EIS_EVENT_SEAT_BIND); _unref_(eis_event) *unbind = peck_eis_next_event(eis, EIS_EVENT_SEAT_BIND); munit_assert_false(eis_event_seat_has_capability(unbind, EIS_DEVICE_CAP_POINTER)); munit_assert_false(eis_event_seat_has_capability(unbind, EIS_DEVICE_CAP_POINTER_ABSOLUTE)); munit_assert_false(eis_event_seat_has_capability(unbind, EIS_DEVICE_CAP_KEYBOARD)); munit_assert_false(eis_event_seat_has_capability(unbind, EIS_DEVICE_CAP_TOUCH)); _unref_(eis_event) *disconnect = peck_eis_next_event(eis, EIS_EVENT_CLIENT_DISCONNECT); } return MUNIT_OK; } MUNIT_TEST(test_ei_disconnect_after_unbind_before_received) { _unref_(peck) *peck = peck_new(); _unref_(ei_seat) *seat = NULL; peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_ALL); peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_NONE); peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_CONNECT); peck_dispatch_until_stable(peck); with_client(peck) { _unref_(ei_event) *event = peck_ei_next_event(ei, EI_EVENT_SEAT_ADDED); seat = ei_seat_ref(ei_event_get_seat(event)); ei_seat_bind_capabilities(seat, EI_DEVICE_CAP_POINTER, NULL); } peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_ALL); /* server has the Bind event now */ peck_dispatch_until_stable(peck); with_client(peck) { ei_seat_unbind_capabilities(seat, EI_DEVICE_CAP_POINTER, NULL); } /* No server dispatch here so the server isn't aware of the * ei_seat_unbind() call. Disconnect the client */ with_server(peck) { struct eis_client *client = peck_eis_get_default_client(peck); eis_client_disconnect(client); } with_client(peck) { ei_dispatch(ei); _unref_(ei_event) *seat_removed = peck_ei_next_event(ei, EI_EVENT_SEAT_REMOVED); _unref_(ei_event) *disconnect = peck_ei_next_event(ei, EI_EVENT_DISCONNECT); } return MUNIT_OK; } MUNIT_TEST(test_ei_disconnect_after_unbind_after_received) { _unref_(peck) *peck = peck_new(); _unref_(ei_seat) *seat = NULL; peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_ALL); peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_NONE); peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_CONNECT); peck_dispatch_until_stable(peck); with_client(peck) { _unref_(ei_event) *event = peck_ei_next_event(ei, EI_EVENT_SEAT_ADDED); seat = ei_seat_ref(ei_event_get_seat(event)); ei_seat_bind_capabilities(seat, EI_DEVICE_CAP_POINTER, NULL); } /* server has the Bind event now */ peck_dispatch_until_stable(peck); with_client(peck) { ei_seat_unbind_capabilities(seat, EI_DEVICE_CAP_POINTER, NULL); } /* Dispatch, server is aware of the ei_seat_unbind() */ peck_dispatch_eis(peck); with_server(peck) { struct eis_client *client = peck_eis_get_default_client(peck); eis_client_disconnect(client); } peck_dispatch_ei(peck); with_client(peck) { _unref_(ei_event) *seat_removed = peck_ei_next_event(ei, EI_EVENT_SEAT_REMOVED); _unref_(ei_event) *disconnect = peck_ei_next_event(ei, EI_EVENT_DISCONNECT); } return MUNIT_OK; } MUNIT_TEST(test_client_is_sender) { _unref_(peck) *peck = peck_new_context(PECK_EI_SENDER); peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_NONE); peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_NONE); peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_CONNECT); peck_dispatch_until_stable(peck); with_server(peck) { _unref_(eis_event) *connect = peck_eis_next_event(eis, EIS_EVENT_CLIENT_CONNECT); struct eis_client *client = eis_event_get_client(connect); munit_assert_true(eis_client_is_sender(client)); } return MUNIT_OK; } MUNIT_TEST(test_client_is_receiver) { _unref_(peck) *peck = peck_new_context(PECK_EI_RECEIVER); peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_NONE); peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_NONE); peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_CONNECT); peck_dispatch_until_stable(peck); with_server(peck) { _unref_(eis_event) *connect = peck_eis_next_event(eis, EIS_EVENT_CLIENT_CONNECT); struct eis_client *client = eis_event_get_client(connect); munit_assert_false(eis_client_is_sender(client)); } return MUNIT_OK; } /* Emulates the XWayland behavior for calling * xdotool mousemove_relative -- -1 10 */ MUNIT_TEST(test_xdotool_rel_motion) { _unref_(peck) *peck = peck_new(); peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_ALL); peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ADD_POINTER); peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_AUTODEVICES); peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_AUTOSTART); peck_dispatch_until_stable(peck); with_client(peck) { struct ei_device *device = peck_ei_get_default_pointer(peck); ei_device_pointer_motion(device, -1, 10); ei_device_frame(device, peck_ei_now(peck)); ei_device_close(device); ei_unref(ei); peck_drop_ei(peck); } peck_dispatch_eis(peck); with_server(peck) { _unref_(eis_event) *motion = peck_eis_next_event(eis, EIS_EVENT_POINTER_MOTION); _unref_(eis_event) *stop = peck_eis_next_event(eis, EIS_EVENT_DEVICE_STOP_EMULATING); _unref_(eis_event) *close = peck_eis_next_event(eis, EIS_EVENT_DEVICE_CLOSED); _unref_(eis_event) *unbind = peck_eis_next_event(eis, EIS_EVENT_SEAT_BIND); munit_assert_false(eis_event_seat_has_capability(unbind, EIS_DEVICE_CAP_POINTER)); munit_assert_false(eis_event_seat_has_capability(unbind, EIS_DEVICE_CAP_POINTER_ABSOLUTE)); munit_assert_false(eis_event_seat_has_capability(unbind, EIS_DEVICE_CAP_KEYBOARD)); munit_assert_false(eis_event_seat_has_capability(unbind, EIS_DEVICE_CAP_TOUCH)); _unref_(eis_event) *disconnect = peck_eis_next_event(eis, EIS_EVENT_CLIENT_DISCONNECT); peck_assert_no_eis_events(eis); } return MUNIT_OK; }