From e860d2476321813fd42b1427f7c8fe0bbac6fe7f Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 5 Aug 2020 17:12:54 +1000 Subject: [PATCH] Add a test suite --- meson.build | 15 ++ test/eierpecken.c | 356 ++++++++++++++++++++++++++++++++++++++++++++++ test/eierpecken.h | 137 ++++++++++++++++++ test/test-ei.c | 180 +++++++++++++++++++++++ 4 files changed, 688 insertions(+) create mode 100644 test/eierpecken.c create mode 100644 test/eierpecken.h create mode 100644 test/test-ei.c diff --git a/meson.build b/meson.build index 31a485a..a60dc73 100644 --- a/meson.build +++ b/meson.build @@ -112,6 +112,21 @@ test('libei-unit-test', c_args: ['-D_enable_tests_'], dependencies: [munit, dep_libutil, dep_protobuf])) +lib_eierpecken = static_library('eierpecken', + 'test/eierpecken.h', + 'test/eierpecken.c', + include_directories: 'src', + dependencies: [munit, dep_libutil], +) + +test('libei-integration-test', + executable('test-ei', + 'test/test-ei.c', + link_with: lib_eierpecken, + dependencies: [munit, dep_libei, dep_libeis])) + + + valgrind = find_program('valgrind', required : false) if valgrind.found() add_test_setup('valgrind', diff --git a/test/eierpecken.c b/test/eierpecken.c new file mode 100644 index 0000000..4509f4e --- /dev/null +++ b/test/eierpecken.c @@ -0,0 +1,356 @@ +/* + * 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 + +#include "eierpecken.h" + +#include "util-bits.h" +#include "util-mem.h" +#include "util-tristate.h" +#include "util-logger.h" +#include "util-object.h" + +DEFINE_TRISTATE(yes, no, unset); + +struct peck { + struct object object; + struct ei *ei; + struct eis *eis; + uint32_t eis_behavior; + uint32_t ei_behavior; + struct logger *logger; +}; + +static void +peck_destroy(struct peck *peck) +{ + ei_unref(peck->ei); + eis_unref(peck->eis); + logger_unref(peck->logger); +} + +OBJECT_IMPLEMENT_CREATE(peck); +OBJECT_IMPLEMENT_UNREF(peck); +OBJECT_IMPLEMENT_GETTER(peck, ei, struct ei*); +OBJECT_IMPLEMENT_GETTER(peck, eis, struct eis*); + +struct peck * +peck_new(void) +{ + struct peck *peck = peck_create(NULL); + + int sv[2]; + int rc = socketpair(AF_UNIX, SOCK_DGRAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0, sv); + munit_assert_int(rc, ==, 0); + + struct ei *ei = ei_new(NULL); + ei_configure_name(ei, "eierpecken test context"); + rc = ei_setup_backend_fd(ei, sv[0]); + munit_assert_int(rc, ==, 0); + peck->ei = ei; + peck->ei_behavior = PECK_EI_BEHAVIOR_AUTOCONNNECT; + + struct eis *eis = eis_new(NULL); + rc = eis_setup_backend_fd(eis); + munit_assert_int(rc, ==, 0); + rc = eis_backend_fd_add_fd(eis, sv[1]); + peck->eis = eis; + peck->eis_behavior = PECK_EIS_BEHAVIOR_NONE; + + peck->logger = logger_new(NULL); + logger_set_priority(peck->logger, LOGGER_DEBUG); + + return peck; +} + +void +peck_enable_eis_behavior(struct peck *peck, enum peck_eis_behavior behavior) +{ + switch (behavior) { + case PECK_EIS_BEHAVIOR_NONE: + peck->eis_behavior = 0; + break; + case PECK_EIS_BEHAVIOR_ACCEPT_ALL: + peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_CLIENT); + peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_DEVICE); + break; + case PECK_EIS_BEHAVIOR_REJECT_ALL: + peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_REJECT_CLIENT); + peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_REJECT_DEVICE); + break; + case PECK_EIS_BEHAVIOR_ACCEPT_DEVICE: + peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_POINTER); + peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_KEYBOARD); + peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_TOUCH); + break; + case PECK_EIS_BEHAVIOR_REJECT_DEVICE: + peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_REJECT_POINTER); + peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_REJECT_KEYBOARD); + peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_REJECT_TOUCH); + break; + case PECK_EIS_BEHAVIOR_REJECT_CLIENT: + case PECK_EIS_BEHAVIOR_ACCEPT_CLIENT: + case PECK_EIS_BEHAVIOR_ACCEPT_POINTER: + case PECK_EIS_BEHAVIOR_REJECT_POINTER: + case PECK_EIS_BEHAVIOR_ACCEPT_KEYBOARD: + case PECK_EIS_BEHAVIOR_REJECT_KEYBOARD: + case PECK_EIS_BEHAVIOR_ACCEPT_TOUCH: + case PECK_EIS_BEHAVIOR_REJECT_TOUCH: + flag_set(peck->eis_behavior, behavior); + break; + } +} + +void +peck_enable_ei_behavior(struct peck *peck, enum peck_ei_behavior behavior) +{ + switch (behavior) { + case PECK_EI_BEHAVIOR_NONE: + peck->ei_behavior = 0; + break; + case PECK_EI_BEHAVIOR_AUTOCONNNECT: + flag_set(peck->ei_behavior, behavior); + break; + case PECK_EI_BEHAVIOR_HANDLE_ADDED: + peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_ADDED_POINTER); + peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_ADDED_KEYBOARD); + peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_ADDED_TOUCH); + break; + case PECK_EI_BEHAVIOR_HANDLE_ADDED_POINTER: + case PECK_EI_BEHAVIOR_HANDLE_ADDED_KEYBOARD: + case PECK_EI_BEHAVIOR_HANDLE_ADDED_TOUCH: + flag_set(peck->ei_behavior, behavior); + break; + } +} + +static inline void +peck_handle_eis_connect(struct peck *peck, struct eis_event *e) +{ + struct eis_client *client = eis_event_get_client(e); + if (flag_is_set(peck->eis_behavior, PECK_EIS_BEHAVIOR_ACCEPT_CLIENT)) { + log_debug(peck, "Accepting client: %s\n", eis_client_get_name(client)); + eis_client_connect(client); + } else { + log_debug(peck, "Disconnecting client: %s\n", eis_client_get_name(client)); + eis_client_disconnect(client); + } +} + +static inline tristate +peck_check_eis_added(struct peck *peck, struct eis_event *e) +{ + struct eis_device *device = eis_event_get_device(e); + tristate accept = tristate_unset; + + if (eis_device_has_capability(device, EIS_DEVICE_CAP_POINTER)) { + if (flag_is_set(peck->eis_behavior, PECK_EIS_BEHAVIOR_ACCEPT_POINTER)) + accept = tristate_yes; + else if (flag_is_set(peck->eis_behavior, PECK_EIS_BEHAVIOR_REJECT_POINTER)) + accept = tristate_no; + } + + if (eis_device_has_capability(device, EIS_DEVICE_CAP_KEYBOARD)) { + if (flag_is_set(peck->eis_behavior, PECK_EIS_BEHAVIOR_ACCEPT_KEYBOARD)) + accept = tristate_yes; + else if (flag_is_set(peck->eis_behavior, PECK_EIS_BEHAVIOR_REJECT_KEYBOARD)) + accept = tristate_no; + } + + if (eis_device_has_capability(device, EIS_DEVICE_CAP_TOUCH)) { + if (flag_is_set(peck->eis_behavior, PECK_EIS_BEHAVIOR_ACCEPT_TOUCH)) + accept = tristate_yes; + else if (flag_is_set(peck->eis_behavior, PECK_EIS_BEHAVIOR_REJECT_TOUCH)) + accept = tristate_no; + } + + return accept; +} + +void +peck_dispatch_eis(struct peck *peck) +{ + struct eis *eis = peck->eis; + + while(true) { + eis_dispatch(eis); + tristate process_event = tristate_unset; + + _cleanup_eis_event_ struct eis_event *e = eis_peek_event(eis); + if (!e) + break; + + switch (eis_event_get_type(e)) { + case EIS_EVENT_NONE: + abort(); + case EIS_EVENT_CLIENT_CONNECT: + case EIS_EVENT_CLIENT_DISCONNECT: + if (flag_is_set(peck->eis_behavior, PECK_EIS_BEHAVIOR_ACCEPT_CLIENT) || + flag_is_set(peck->eis_behavior, PECK_EIS_BEHAVIOR_REJECT_CLIENT)) + process_event = tristate_yes; + break; + case EIS_EVENT_DEVICE_ADDED: + case EIS_EVENT_DEVICE_REMOVED: + if (tristate_is_unset(peck_check_eis_added(peck, e))) + process_event = tristate_no; + else + process_event = tristate_yes; + break; + default: + break; + } + + if (!tristate_is_yes(process_event)) + break; + + /* manual unref, _cleanup_ will take care of the real event */ + eis_event_unref(e); + e = eis_get_event(eis); + + switch (eis_event_get_type(e)) { + case EIS_EVENT_NONE: + abort(); + case EIS_EVENT_CLIENT_CONNECT: + peck_handle_eis_connect(peck, e); + break; + case EIS_EVENT_CLIENT_DISCONNECT: + log_debug(peck, "Disconnecting client: %s\n", + eis_client_get_name(eis_event_get_client(e))); + eis_client_disconnect(eis_event_get_client(e)); + break; + case EIS_EVENT_DEVICE_ADDED: + if (tristate_is_yes(peck_check_eis_added(peck, e))) { + eis_device_connect(eis_event_get_device(e)); + log_debug(peck, "Connecting device\n"); + } else { + log_debug(peck, "Disconnecting device\n"); + eis_device_disconnect(eis_event_get_device(e)); + } + break; + case EIS_EVENT_DEVICE_REMOVED: + log_debug(peck, "Disconnecting device\n"); + eis_device_disconnect(eis_event_get_device(e)); + break; + default: + break; + } + } +} + +static inline tristate +peck_check_ei_added(struct peck *peck, struct ei_event *e) +{ + struct ei_device *device = ei_event_get_device(e); + + if (ei_device_has_capability(device, EI_DEVICE_CAP_POINTER) && + flag_is_set(peck->ei_behavior, PECK_EI_BEHAVIOR_HANDLE_ADDED_POINTER)) + return tristate_yes; + + if (ei_device_has_capability(device, EI_DEVICE_CAP_KEYBOARD) && + flag_is_set(peck->ei_behavior, PECK_EI_BEHAVIOR_HANDLE_ADDED_KEYBOARD)) + return tristate_yes; + + if (ei_device_has_capability(device, EI_DEVICE_CAP_TOUCH) & + flag_is_set(peck->ei_behavior, PECK_EI_BEHAVIOR_HANDLE_ADDED_TOUCH)) + return tristate_yes; + + return tristate_unset; +} + +void +peck_dispatch_ei(struct peck *peck) +{ + struct ei *ei = peck->ei; + + while (true) { + ei_dispatch(ei); + tristate process_event = tristate_no; + + _cleanup_ei_event_ struct ei_event *e = ei_peek_event(ei); + if (!e) + break; + + switch (ei_event_get_type(e)) { + case EI_EVENT_NONE: + abort(); + case EI_EVENT_CONNECT: + if (flag_is_set(peck->ei_behavior, + PECK_EI_BEHAVIOR_AUTOCONNNECT)) + process_event = tristate_yes; + break; + case EI_EVENT_DEVICE_ADDED: + process_event = peck_check_ei_added(peck, e); + default: + break; + } + + if (!tristate_is_yes(process_event)) + break; + + /* manual unref, _cleanup_ will take care of the real event */ + ei_event_unref(e); + e = ei_get_event(ei); + + switch (ei_event_get_type(e)) { + case EI_EVENT_NONE: + abort(); + case EI_EVENT_CONNECT: + /* Nothing to do here */ + break; + case EI_EVENT_DEVICE_ADDED: + /* Nothing to do here */ + break; + default: + break; + } + } +} + +void +peck_drain_eis(struct eis *eis) +{ + eis_dispatch(eis); + + while (true) { + _cleanup_eis_event_ struct eis_event *e = eis_get_event(eis); + if (e == NULL) + break; + } +} + +void +peck_drain_ei(struct ei *ei) +{ + ei_dispatch(ei); + while (true) { + _cleanup_ei_event_ struct ei_event *e = ei_get_event(ei); + if (e == NULL) + break; + } +} diff --git a/test/eierpecken.h b/test/eierpecken.h new file mode 100644 index 0000000..b95a9ac --- /dev/null +++ b/test/eierpecken.h @@ -0,0 +1,137 @@ +/* + * 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" + +#pragma once + +#include "libei.h" +#include "libeis.h" + +#include "util-mem.h" + +/** + * An enum to define basic server behavior in peck_dispatch_eis(). + * Where a flag is **not** set for any specific behaviour, that event will + * remain on the event queue after peck_dispatch_eis(). For example, a + * caller setting @ref PECK_EIS_BEHAVIOR_ACCEPT_CLIENT will see + * the device added event as first event in the queue. + */ +enum peck_eis_behavior { + /** + * Behavior of EIS is implemented in the test case. This is the + * default. + */ + PECK_EIS_BEHAVIOR_NONE, + /** + * Accept all client connection requests and device additions. + */ + PECK_EIS_BEHAVIOR_ACCEPT_ALL, + /** + * Reject any client connection requests or device additions. + */ + PECK_EIS_BEHAVIOR_REJECT_ALL, + + /** + * Process connect/disconnect requests from the client. + */ + PECK_EIS_BEHAVIOR_ACCEPT_CLIENT, + PECK_EIS_BEHAVIOR_REJECT_CLIENT, + + /** + * Process add/remove requests from the client. + */ + PECK_EIS_BEHAVIOR_ACCEPT_DEVICE, + PECK_EIS_BEHAVIOR_REJECT_DEVICE, + + PECK_EIS_BEHAVIOR_ACCEPT_POINTER, + PECK_EIS_BEHAVIOR_REJECT_POINTER, + + PECK_EIS_BEHAVIOR_ACCEPT_KEYBOARD, + PECK_EIS_BEHAVIOR_REJECT_KEYBOARD, + + PECK_EIS_BEHAVIOR_ACCEPT_TOUCH, + PECK_EIS_BEHAVIOR_REJECT_TOUCH, +}; + +enum peck_ei_behavior { + PECK_EI_BEHAVIOR_NONE, + /* the default - handle the Connect event */ + PECK_EI_BEHAVIOR_AUTOCONNNECT, + PECK_EI_BEHAVIOR_HANDLE_ADDED, + PECK_EI_BEHAVIOR_HANDLE_ADDED_POINTER, + PECK_EI_BEHAVIOR_HANDLE_ADDED_KEYBOARD, + PECK_EI_BEHAVIOR_HANDLE_ADDED_TOUCH, +}; + +struct peck; + +struct peck * +peck_new(void); + +void +peck_enable_eis_behavior(struct peck *peck, enum peck_eis_behavior behavior); + +void +peck_enable_ei_behavior(struct peck *peck, enum peck_ei_behavior behavior); + +struct ei * +peck_get_ei(struct peck *peck); + +struct eis * +peck_get_eis(struct peck *peck); + +void +peck_dispatch_eis(struct peck *peck); + +void +peck_dispatch_ei(struct peck *peck); + +struct peck * +peck_unref(struct peck *peck); + +DEFINE_TRIVIAL_CLEANUP_FUNC(struct peck*, peck_unref); +#define _cleanup_peck_ _cleanup_(peck_unrefp) + +void +peck_drain_eis(struct eis *eis); + +void +peck_drain_ei(struct ei *ei); + +/* Define a bunch of _cleanup_foo_ macros for a struct foo */ +DEFINE_TRIVIAL_CLEANUP_FUNC(struct ei *, ei_unref); +#define _cleanup_ei_ _cleanup_(ei_unrefp) +DEFINE_TRIVIAL_CLEANUP_FUNC(struct ei_event *, ei_event_unref); +#define _cleanup_ei_event_ _cleanup_(ei_event_unrefp) +DEFINE_TRIVIAL_CLEANUP_FUNC(struct ei_device *, ei_device_unref); +#define _cleanup_ei_device_ _cleanup_(ei_device_unrefp) + +DEFINE_TRIVIAL_CLEANUP_FUNC(struct eis *, eis_unref); +#define _cleanup_eis_ _cleanup_(eis_unrefp) +DEFINE_TRIVIAL_CLEANUP_FUNC(struct eis_client *, eis_client_unref); +#define _cleanup_eis_client_ _cleanup_(eis_client_unrefp) +DEFINE_TRIVIAL_CLEANUP_FUNC(struct eis_event *, eis_event_unref); +#define _cleanup_eis_event_ _cleanup_(eis_event_unrefp) +DEFINE_TRIVIAL_CLEANUP_FUNC(struct eis_device *, eis_device_unref); +#define _cleanup_eis_device_ _cleanup_(eis_device_unrefp) diff --git a/test/test-ei.c b/test/test-ei.c new file mode 100644 index 0000000..f424dd9 --- /dev/null +++ b/test/test-ei.c @@ -0,0 +1,180 @@ + +#include "config.h" + +#include +#include +#include + +#include "libei.h" +#include "eierpecken.h" + +/* Macros intended just for readability to make it more obvious which part + of a test handles server vs client */ +#define with_server(peck_) for (struct eis *eis = peck_get_eis(peck_); eis; eis = NULL) +#define with_client(peck_) for (struct ei *ei = peck_get_ei(peck_); ei; ei = NULL) + +static MunitResult +test_ei_ref_unref(const MunitParameter params[], void *user_data) +{ + 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; +} + +static MunitResult +test_ei_reject(const MunitParameter params[], void *user_data) +{ + _cleanup_peck_ struct peck *peck = peck_new(); + + /* Client is immediately rejected */ + peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_REJECT_CLIENT); + peck_dispatch_ei(peck); + peck_dispatch_eis(peck); + + /* Expect the client to get a disconnect event */ + with_client(peck) { + ei_dispatch(ei); + _cleanup_ei_event_ struct ei_event *e = ei_get_event(ei); + munit_assert_ptr_not_null(e); + munit_assert_int(ei_event_get_type(e), ==, EI_EVENT_DISCONNECT); + } + + return MUNIT_OK; +} + +static MunitResult +test_ei_reject_after_connect(const MunitParameter params[], void *user_data) +{ + _cleanup_peck_ struct peck *peck = peck_new(); + _cleanup_eis_client_ struct eis_client *client = NULL; + + peck_dispatch_ei(peck); + + with_server(peck) { + eis_dispatch(eis); + _cleanup_eis_event_ struct eis_event *e = eis_get_event(eis); + munit_assert_ptr_not_null(e); + munit_assert_int(eis_event_get_type(e), ==, EIS_EVENT_CLIENT_CONNECT); + client = eis_client_ref(eis_event_get_client(e)); + eis_client_connect(client); + } + + with_client(peck) { + ei_dispatch(ei); + _cleanup_ei_event_ struct ei_event *e = ei_get_event(ei); + munit_assert_ptr_not_null(e); + munit_assert_int(ei_event_get_type(e), ==, EI_EVENT_CONNECT); + } + + with_server(peck) { + eis_client_disconnect(client); + } + + with_client(peck) { + ei_dispatch(ei); + _cleanup_ei_event_ struct ei_event *e = ei_get_event(ei); + munit_assert_ptr_not_null(e); + munit_assert_int(ei_event_get_type(e), ==, EI_EVENT_DISCONNECT); + } + + return MUNIT_OK; +} + +static MunitResult +test_ei_device_basics(const MunitParameter params[], void *user_data) +{ + _cleanup_peck_ struct peck *peck = peck_new(); + _cleanup_ei_device_ struct ei_device *device = NULL; + + peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_ALL); + peck_dispatch_eis(peck); + peck_dispatch_ei(peck); + + /* device creation and getters/setters test */ + with_client(peck) { + device = ei_device_new(ei); + munit_assert_not_null(device); + ei_device_configure_name(device, __func__); + munit_assert_string_equal(ei_device_get_name(device), __func__); + + bool success = ei_device_configure_capability(device, EI_DEVICE_CAP_POINTER); + munit_assert(success); + munit_assert(ei_device_has_capability(device, EI_DEVICE_CAP_POINTER)); + + /* Add it, but we don't care about whether it worked correctly in this test */ + ei_device_add(device); + + /* Device is immutable after ei_device_add() */ + bool failed_caps = ei_device_configure_capability(device, EI_DEVICE_CAP_POINTER); + munit_assert(failed_caps == false); + munit_assert(ei_device_has_capability(device, EI_DEVICE_CAP_POINTER)); + } + + /* Drain both sides, we don't care about the events themselves */ + with_server(peck) { + peck_drain_eis(eis); + } + with_client(peck) { + peck_drain_ei(ei); + } + + /* device is still immutable */ + with_client(peck) { + bool failed_caps = ei_device_configure_capability(device, EI_DEVICE_CAP_POINTER); + munit_assert(failed_caps == false); + munit_assert(ei_device_has_capability(device, EI_DEVICE_CAP_POINTER)); + } + + return MUNIT_OK; +} + +static MunitResult +test_ei_device_add(const MunitParameter params[], void *user_data) +{ + _cleanup_peck_ struct peck *peck = peck_new(); + struct ei *ei = peck_get_ei(peck); + + peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_ALL); + peck_dispatch_eis(peck); + + peck_drain_ei(ei); + + _cleanup_ei_device_ struct ei_device *device = ei_device_new(ei); + ei_device_configure_name(device, __func__); + ei_device_configure_capability(device, EI_DEVICE_CAP_POINTER); + ei_device_add(device); + + return MUNIT_OK; +} + +static MunitTest ei_tests[] = { + { .name = "/ei/ref", .test = test_ei_ref_unref }, + { .name = "/ei/reject", .test = test_ei_reject }, + { .name = "/ei/reject_after_connect", .test = test_ei_reject_after_connect }, + { .name = "/device/basics", .test = test_ei_device_basics }, + { .name = "/device/add", .test = test_ei_device_add }, +}; + +static const MunitSuite iotest_suite = { + "/ei", + ei_tests, + NULL, + 1, + MUNIT_SUITE_OPTION_NONE, +}; + +int +main(int argc, char* argv[MUNIT_ARRAY_PARAM(argc + 1)]) +{ + return munit_suite_main(&iotest_suite, NULL, argc, argv); +}