libei/test/test-ei.c
Peter Hutterer 1225bcb0e1 Add a property API for generic key/value exchanges
There is data that libei and the EIS implementation will want to
exchange that is not covered by the immediate API.

To avoid having to add APIs for all of these, let's provide a generic
property API that both server and client can use to exchange this info.

The property API provides read/write/delete permissions but those only
apply to the client, not the server. The idea is that a server can
create (or restrict) properties that the client can read but not modify
and/or delete. A special-case are properties filled in automatically by
libei: ei.application.pid and ei.application.cmdline. These could be
used by e.g. the portal implementation to match permissions.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2021-08-25 13:35:36 +10:00

919 lines
25 KiB
C

/*
* 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 <unistd.h>
#include <fcntl.h>
#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), "%u", getpid());
xsnprintf(cmdline, sizeof(cmdline), "/proc/%u/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();
_unref_(eis_client) *client = NULL;
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();
_unref_(eis_client) *client = NULL;
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();
_unref_(eis_client) *client = NULL;
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();
_unref_(eis_client) *client = NULL;
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();
_unref_(eis_client) *client = NULL;
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();
_unref_(eis_client) *client = NULL;
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_device) *device = NULL;
_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_device) *device = NULL;
_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;
}