/* 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" DEFINE_UNREF_CLEANUP_FUNC(peck); 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_initial_properties) { _unref_(peck) *peck = peck_new(); char pid[64]; char cmdline[PATH_MAX]; xsnprintf(pid, sizeof(pid), "%i", getpid()); xsnprintf(cmdline, sizeof(cmdline), "/proc/%i/cmdline", getpid()); int fd = open(cmdline, O_RDONLY); munit_assert_int(fd, >=, 0); int len = read(fd, cmdline, sizeof(cmdline) - 1); munit_assert_int(len, >=, 0); cmdline[len] = '\0'; peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_NONE); 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); const char *pidprop = eis_client_property_get(client, "ei.application.pid"); munit_assert_ptr_not_null(pidprop); munit_assert_string_equal(pidprop, pid); const char *cmdprop = eis_client_property_get(client, "ei.application.cmdline"); munit_assert_ptr_not_null(cmdprop); munit_assert_string_equal(cmdprop, cmdline); } return MUNIT_OK; } MUNIT_TEST(test_ei_properties) { _unref_(peck) *peck = peck_new(); peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_ALL); peck_dispatch_until_stable(peck); with_client(peck) { ei_property_set(ei, "foo.bar", "value"); } peck_dispatch_until_stable(peck); with_server(peck) { _unref_(eis_event) *e = peck_eis_next_event(eis, EIS_EVENT_CLIENT_PROPERTY); const char *name = eis_event_property_get_name(e); const char *value = eis_event_property_get_value(e); munit_assert_string_equal(name, "foo.bar"); munit_assert_string_equal(value, "value"); } with_client(peck) { int rc = ei_property_set(ei, "foo.bar", "newval"); munit_assert_int(rc, ==, 0); } peck_dispatch_until_stable(peck); with_server(peck) { _unref_(eis_event) *e = peck_eis_next_event(eis, EIS_EVENT_CLIENT_PROPERTY); const char *name = eis_event_property_get_name(e); const char *value = eis_event_property_get_value(e); munit_assert_string_equal(name, "foo.bar"); munit_assert_string_equal(value, "newval"); } with_client(peck) { int rc = ei_property_set(ei, "foo.bar", NULL); munit_assert_int(rc, ==, 0); } peck_dispatch_until_stable(peck); with_server(peck) { _unref_(eis_event) *e = peck_eis_next_event(eis, EIS_EVENT_CLIENT_PROPERTY); const char *name = eis_event_property_get_name(e); const char *value = eis_event_property_get_value(e); munit_assert_string_equal(name, "foo.bar"); munit_assert_ptr_null(value); } return MUNIT_OK; } MUNIT_TEST(test_ei_properties_reserved) { _unref_(peck) *peck = peck_new(); peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_ALL); peck_dispatch_until_stable(peck); with_client(peck) { int rc; rc = ei_property_set(ei, "ei.bar", "value"); munit_assert_int(rc, ==, -EACCES); rc = ei_property_set(ei, "eis.bar", "value"); munit_assert_int(rc, ==, -EACCES); } peck_dispatch_until_stable(peck); with_server(peck) { peck_assert_no_eis_events(eis); } peck_dispatch_until_stable(peck); return MUNIT_OK; } MUNIT_TEST(test_ei_properties_permissions) { _unref_(peck) *peck = peck_new(); peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_ALL); peck_dispatch_until_stable(peck); with_client(peck) { ei_property_set_with_permissions(ei, "cannot.delete", "v1", EI_PROPERTY_PERM_READ|EI_PROPERTY_PERM_WRITE); ei_property_set_with_permissions(ei, "cannot.write", "v2", EI_PROPERTY_PERM_READ|EI_PROPERTY_PERM_DELETE); ei_property_set_with_permissions(ei, "cannot.read", "v3", EI_PROPERTY_PERM_DELETE|EI_PROPERTY_PERM_WRITE); const char *value; int rc; /* can't delete but we can write to it */ value = ei_property_get(ei, "cannot.delete"); munit_assert_string_equal(value, "v1"); rc = ei_property_set(ei, "cannot.delete", NULL); munit_assert_int(rc, ==, -EACCES); value = ei_property_get(ei, "cannot.delete"); munit_assert_string_equal(value, "v1"); rc = ei_property_set(ei, "cannot.delete", "newv1"); munit_assert_int(rc, ==, 0); value = ei_property_get(ei, "cannot.delete"); munit_assert_string_equal(value, "newv1"); /* can't modify it but we can delete it */ value = ei_property_get(ei, "cannot.write"); munit_assert_string_equal(value, "v2"); rc = ei_property_set(ei, "cannot.write", "newv2"); munit_assert_int(rc, ==, -EACCES); value = ei_property_get(ei, "cannot.write"); munit_assert_string_equal(value, "v2"); rc = ei_property_set(ei, "cannot.write", NULL); munit_assert_int(rc, ==, 0); value = ei_property_get(ei, "cannot.write"); munit_assert_ptr_equal(value, NULL); value = ei_property_get(ei, "cannot.read"); munit_assert_ptr_equal(value, NULL); rc = ei_property_set(ei, "cannot.read", "newv3"); munit_assert_int(rc, ==, -EACCES); value = ei_property_get(ei, "cannot.read"); munit_assert_ptr_equal(value, NULL); rc = ei_property_set(ei, "cannot.read", NULL); munit_assert_int(rc, ==, -EACCES); } return MUNIT_OK; } MUNIT_TEST(test_ei_properties_permissions_drop) { _unref_(peck) *peck = peck_new(); peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_ALL); peck_dispatch_until_stable(peck); with_client(peck) { int rc; ei_property_set_with_permissions(ei, "test.perms", "v1", EI_PROPERTY_PERM_READ|EI_PROPERTY_PERM_WRITE|EI_PROPERTY_PERM_DELETE); /* downgrading permissions, can't delete anymore */ rc = ei_property_set_with_permissions(ei, "test.perms", "v1", EI_PROPERTY_PERM_READ|EI_PROPERTY_PERM_WRITE); munit_assert_int(rc, ==, 0); rc = ei_property_set(ei, "test.perms", NULL); munit_assert_int(rc, ==, -EACCES); /* upgrading permissions should fail */ rc = ei_property_set_with_permissions(ei, "test.perms", "v1", EI_PROPERTY_PERM_READ|EI_PROPERTY_PERM_WRITE|EI_PROPERTY_PERM_DELETE); munit_assert_int(rc, ==, -EPERM); /* Drop the write permission */ rc = ei_property_set_with_permissions(ei, "test.perms", "v1", EI_PROPERTY_PERM_READ); munit_assert_int(rc, ==, 0); rc = ei_property_set(ei, "test.perms", "new value"); munit_assert_int(rc, ==, -EACCES); /* We need write permissions to change the permissions */ rc = ei_property_set_with_permissions(ei, "test.perms", "v1", EI_PROPERTY_PERM_NONE); munit_assert_int(rc, ==, -EACCES); } return MUNIT_OK; } MUNIT_TEST(test_ei_properties_events) { _unref_(peck) *peck = peck_new(); peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_ALL); peck_dispatch_until_stable(peck); with_client(peck) { ei_property_set(ei, "test.events", "v1"); ei_property_set(ei, "test.events", "v2"); } peck_dispatch_until_stable(peck); with_server(peck) { _unref_(eis_event) *e1 = peck_eis_next_event(eis, EIS_EVENT_CLIENT_PROPERTY); const char *name = eis_event_property_get_name(e1); const char *value = eis_event_property_get_value(e1); munit_assert_string_equal(name, "test.events"); munit_assert_string_equal(value, "v1"); struct eis_client *client = eis_event_get_client(e1); const char *propval = eis_client_property_get(client, "test.events"); /* * This probably needs fixing: because we processed the second * event already, the actual property value is different to * our current event value. */ munit_assert_string_equal(propval, "v2"); _unref_(eis_event) *e2 = peck_eis_next_event(eis, EIS_EVENT_CLIENT_PROPERTY); name = eis_event_property_get_name(e2); value = eis_event_property_get_value(e2); munit_assert_string_equal(name, "test.events"); munit_assert_string_equal(value, "v2"); } /* delete it */ with_client(peck) { ei_property_set(ei, "test.events", "v3"); ei_property_set(ei, "test.events", NULL); } peck_dispatch_until_stable(peck); with_server(peck) { _unref_(eis_event) *e3 = peck_eis_next_event(eis, EIS_EVENT_CLIENT_PROPERTY); const char *name = eis_event_property_get_name(e3); const char *value = eis_event_property_get_value(e3); munit_assert_string_equal(name, "test.events"); munit_assert_string_equal(value, "v3"); struct eis_client *client = eis_event_get_client(e3); const char *propval = eis_client_property_get(client, "test.events"); /* * This probably needs fixing: because we processed the second * event already, the actual property value is different to * our current event value. */ munit_assert_ptr_null(propval); _unref_(eis_event) *e4 = peck_eis_next_event(eis, EIS_EVENT_CLIENT_PROPERTY); name = eis_event_property_get_name(e4); value = eis_event_property_get_value(e4); munit_assert_string_equal(name, "test.events"); munit_assert_ptr_null(value); } return MUNIT_OK; } MUNIT_TEST(test_ei_properties_events_server) { _unref_(peck) *peck = peck_new(); peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_ALL); peck_dispatch_until_stable(peck); with_server(peck) { struct eis_client *client = peck_eis_get_default_client(peck); eis_client_property_set(client, "test.events", "v1"); eis_client_property_set(client, "test.events", "v2"); } peck_dispatch_until_stable(peck); with_client(peck) { _unref_(ei_event) *e1 = peck_ei_next_event(ei, EI_EVENT_PROPERTY); const char *name = ei_event_property_get_name(e1); const char *value = ei_event_property_get_value(e1); munit_assert_string_equal(name, "test.events"); munit_assert_string_equal(value, "v1"); const char *propval = ei_property_get(ei, "test.events"); /* * This probably needs fixing: because we processed the second * event already, the actual property value is different to * our current event value. */ munit_assert_string_equal(propval, "v2"); _unref_(ei_event) *e2 = peck_ei_next_event(ei, EI_EVENT_PROPERTY); name = ei_event_property_get_name(e2); value = ei_event_property_get_value(e2); munit_assert_string_equal(name, "test.events"); munit_assert_string_equal(value, "v2"); } /* delete it */ with_server(peck) { struct eis_client *client = peck_eis_get_default_client(peck); eis_client_property_set(client, "test.events", "v3"); eis_client_property_set(client, "test.events", NULL); } peck_dispatch_until_stable(peck); with_client(peck) { _unref_(ei_event) *e3 = peck_ei_next_event(ei, EI_EVENT_PROPERTY); const char *name = ei_event_property_get_name(e3); const char *value = ei_event_property_get_value(e3); munit_assert_string_equal(name, "test.events"); munit_assert_string_equal(value, "v3"); const char *propval = ei_property_get(ei, "test.events"); /* * This probably needs fixing: because we processed the second * event already, the actual property value is different to * our current event value. */ munit_assert_ptr_null(propval); _unref_(ei_event) *e4 = peck_ei_next_event(ei, EI_EVENT_PROPERTY); name = ei_event_property_get_name(e4); value = ei_event_property_get_value(e4); munit_assert_string_equal(name, "test.events"); munit_assert_ptr_null(value); } return MUNIT_OK; } MUNIT_TEST(test_ei_properties_events_pingpong) { _unref_(peck) *peck = peck_new(); peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_ALL); peck_dispatch_until_stable(peck); with_server(peck) { struct eis_client *client = peck_eis_get_default_client(peck); eis_client_property_set(client, "test.events", "ping"); } peck_dispatch_until_stable(peck); with_client(peck) { _unref_(ei_event) *e = peck_ei_next_event(ei, EI_EVENT_PROPERTY); const char *name = ei_event_property_get_name(e); const char *value = ei_event_property_get_value(e); munit_assert_string_equal(name, "test.events"); munit_assert_string_equal(value, "ping"); const char *propval = ei_property_get(ei, "test.events"); munit_assert_string_equal(propval, "ping"); ei_property_set(ei, "test.events", "pong"); propval = ei_property_get(ei, "test.events"); munit_assert_string_equal(propval, "pong"); } peck_dispatch_until_stable(peck); with_server(peck) { _unref_(eis_event) *e = peck_eis_next_event(eis, EIS_EVENT_CLIENT_PROPERTY); const char *name = eis_event_property_get_name(e); const char *value = eis_event_property_get_value(e); munit_assert_string_equal(name, "test.events"); munit_assert_string_equal(value, "pong"); struct eis_client *client = eis_event_get_client(e); const char *propval = eis_client_property_get(client, "test.events"); munit_assert_string_equal(propval, "pong"); } 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_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_AUTOCONNECT); 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(seat); } /* 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_AUTOCONNECT); 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(seat); /* 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_UNBIND); _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_AUTOCONNECT); 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(seat); } /* 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_AUTOCONNECT); 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(seat); } /* 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_UNBIND); _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_AUTOCONNECT); 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(seat); } 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(seat); } /* 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_AUTOCONNECT); 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(seat); } /* server has the Bind event now */ peck_dispatch_until_stable(peck); with_client(peck) { ei_seat_unbind(seat); } /* 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; } /* 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); 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_UNBIND); _unref_(eis_event) *disconnect = peck_eis_next_event(eis, EIS_EVENT_CLIENT_DISCONNECT); peck_assert_no_eis_events(eis); } return MUNIT_OK; }