From 01c9c428b71cf6721ddddf2e276f88b2a8749dc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Fri, 10 Jan 2025 17:28:05 +0100 Subject: [PATCH] tests: Add remote desktop tests This requires a newer libei, for ei_ping. Part-of: --- .gitlab-ci.yml | 2 +- .gitlab-ci/install-common-dependencies.sh | 8 + meson.build | 2 +- src/backends/meta-backend-private.h | 1 + src/backends/meta-dbus-session-manager.c | 9 + src/backends/meta-dbus-session-manager.h | 6 + src/backends/meta-remote-desktop-session.c | 6 + src/backends/meta-remote-desktop-session.h | 5 + src/tests/meson.build | 29 ++- src/tests/remote-desktop-tests-client.c | 261 +++++++++++++++++++++ src/tests/remote-desktop-tests.c | 168 +++++++++++++ src/tests/remote-desktop-utils.c | 9 + src/tests/remote-desktop-utils.h | 2 + 13 files changed, 505 insertions(+), 3 deletions(-) create mode 100644 src/tests/remote-desktop-tests-client.c create mode 100644 src/tests/remote-desktop-tests.c diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2474f579d..2947a8406 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -106,7 +106,7 @@ variables: - .skip-git-clone variables: FDO_DISTRIBUTION_VERSION: 41 - BASE_TAG: '2025-01-30.0' + BASE_TAG: '2025-02-04.0' MUTTER_USER: 'meta-user' FDO_DISTRIBUTION_PACKAGES: clang diff --git a/.gitlab-ci/install-common-dependencies.sh b/.gitlab-ci/install-common-dependencies.sh index e89db9669..57d8ae8b8 100755 --- a/.gitlab-ci/install-common-dependencies.sh +++ b/.gitlab-ci/install-common-dependencies.sh @@ -101,4 +101,12 @@ then master fi +if ! pkgconf --atleast-version 1.3.901 libeis-1.0 +then + ./$SCRIPTS_DIR/install-meson-project.sh \ + "${OPTIONS[@]}" \ + https://gitlab.freedesktop.org/libinput/libei.git \ + 1.3.901 +fi + pip_install argcomplete diff --git a/meson.build b/meson.build index e53431f32..12962a1ce 100644 --- a/meson.build +++ b/meson.build @@ -38,7 +38,7 @@ libcanberra_req = '>= 0.26' libwacom_req = '>= 0.13' atk_req = '>= 2.5.3' harfbuzz_req = '>= 2.6' -libei_req = '>= 1.0.901' +libei_req = '>= 1.3.901' libdisplay_info_req = '>= 0.2' # optional version requirements diff --git a/src/backends/meta-backend-private.h b/src/backends/meta-backend-private.h index a32cfae1d..9a647458f 100644 --- a/src/backends/meta-backend-private.h +++ b/src/backends/meta-backend-private.h @@ -183,6 +183,7 @@ MetaEgl * meta_backend_get_egl (MetaBackend *backend); MetaDbusSessionWatcher * meta_backend_get_dbus_session_watcher (MetaBackend *backend); #ifdef HAVE_REMOTE_DESKTOP +META_EXPORT_TEST MetaRemoteDesktop * meta_backend_get_remote_desktop (MetaBackend *backend); MetaScreenCast * meta_backend_get_screen_cast (MetaBackend *backend); diff --git a/src/backends/meta-dbus-session-manager.c b/src/backends/meta-dbus-session-manager.c index 6b9443e60..d5730f304 100644 --- a/src/backends/meta-dbus-session-manager.c +++ b/src/backends/meta-dbus-session-manager.c @@ -490,3 +490,12 @@ meta_dbus_session_manager_get_interface_skeleton (MetaDbusSessionManager *sessio return priv->interface_skeleton; } + +size_t +meta_dbus_session_manager_get_num_sessions (MetaDbusSessionManager *session_manager) +{ + MetaDbusSessionManagerPrivate *priv = + meta_dbus_session_manager_get_instance_private (session_manager); + + return g_hash_table_size (priv->sessions); +} diff --git a/src/backends/meta-dbus-session-manager.h b/src/backends/meta-dbus-session-manager.h index a07c28e17..d3054a483 100644 --- a/src/backends/meta-dbus-session-manager.h +++ b/src/backends/meta-dbus-session-manager.h @@ -22,8 +22,10 @@ #include #include "backends/meta-backend-types.h" +#include "core/util-private.h" #define META_TYPE_DBUS_SESSION_MANAGER (meta_dbus_session_manager_get_type ()) +META_EXPORT_TEST G_DECLARE_DERIVABLE_TYPE (MetaDbusSessionManager, meta_dbus_session_manager, META, DBUS_SESSION_MANAGER, @@ -39,6 +41,7 @@ MetaDbusSession * meta_dbus_session_manager_create_session (MetaDbusSessionManag GError **error, ...); +META_EXPORT_TEST MetaDbusSession * meta_dbus_session_manager_get_session (MetaDbusSessionManager *session_manager, const char *session_id); @@ -51,3 +54,6 @@ MetaBackend * meta_dbus_session_manager_get_backend (MetaDbusSessionManager *ses GDBusConnection * meta_dbus_session_manager_get_connection (MetaDbusSessionManager *session_manager); GDBusInterfaceSkeleton * meta_dbus_session_manager_get_interface_skeleton (MetaDbusSessionManager *session_manager); + +META_EXPORT_TEST +size_t meta_dbus_session_manager_get_num_sessions (MetaDbusSessionManager *session_manager); diff --git a/src/backends/meta-remote-desktop-session.c b/src/backends/meta-remote-desktop-session.c index a7a402341..f680987c9 100644 --- a/src/backends/meta-remote-desktop-session.c +++ b/src/backends/meta-remote-desktop-session.c @@ -1920,6 +1920,12 @@ device_types_to_eis_device_types (MetaRemoteDesktopDeviceTypes device_types) return eis_device_types; } +MetaEis * +meta_remote_desktop_session_get_eis (MetaRemoteDesktopSession *session) +{ + return session->eis; +} + static gboolean handle_connect_to_eis (MetaDBusRemoteDesktopSession *skeleton, GDBusMethodInvocation *invocation, diff --git a/src/backends/meta-remote-desktop-session.h b/src/backends/meta-remote-desktop-session.h index 2c6405a4e..de7935b65 100644 --- a/src/backends/meta-remote-desktop-session.h +++ b/src/backends/meta-remote-desktop-session.h @@ -26,6 +26,7 @@ #include "backends/meta-screen-cast-session.h" #define META_TYPE_REMOTE_DESKTOP_SESSION (meta_remote_desktop_session_get_type ()) +META_EXPORT_TEST G_DECLARE_FINAL_TYPE (MetaRemoteDesktopSession, meta_remote_desktop_session, META, REMOTE_DESKTOP_SESSION, MetaDBusRemoteDesktopSessionSkeleton) @@ -47,6 +48,10 @@ const char * meta_remote_desktop_session_acquire_mapping_id (MetaRemoteDesktopSe void meta_remote_desktop_session_release_mapping_id (MetaRemoteDesktopSession *session, const char *mapping_id); +META_EXPORT_TEST void meta_remote_desktop_session_request_transfer (MetaRemoteDesktopSession *session, const char *mime_type, GTask *task); + +META_EXPORT_TEST +MetaEis * meta_remote_desktop_session_get_eis (MetaRemoteDesktopSession *session); diff --git a/src/tests/meson.build b/src/tests/meson.build index 04b731a0f..38d52dc2b 100644 --- a/src/tests/meson.build +++ b/src/tests/meson.build @@ -431,6 +431,23 @@ cursor_screen_cast_client = executable('mutter-cursor-tests-screen-cast-client', install_rpath: pkglibdir, ) +remote_desktop_tests_client = executable('mutter-remote-desktop-tests-client', + sources: [ + 'remote-desktop-tests-client.c', + remote_desktop_utils, + ], + include_directories: tests_includes, + c_args: [ + tests_c_args, + '-DG_LOG_DOMAIN="mutter-remote-desktop-tests-client"', + ], + dependencies: [ + remote_desktop_utils_deps, + ], + install: have_installed_tests, + install_dir: mutter_installed_tests_libexecdir, +) + # Native backend tests test_cases += [ { @@ -515,7 +532,17 @@ test_cases += [ ], 'depends': [ test_client_executables.get('cursor-tests-client'), - ] + ], + }, + { + 'name': 'remote-desktop-tests', + 'suite': 'backends/native', + 'sources': [ + 'remote-desktop-tests.c', + ], + 'depends': [ + remote_desktop_tests_client, + ], }, ] diff --git a/src/tests/remote-desktop-tests-client.c b/src/tests/remote-desktop-tests-client.c new file mode 100644 index 000000000..0dea7a270 --- /dev/null +++ b/src/tests/remote-desktop-tests-client.c @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2025 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + */ + +#include "config.h" + +#include +#include +#include +#include + +#include "tests/remote-desktop-utils.h" + +static RemoteDesktop *remote_desktop; +static ScreenCast *screen_cast; +static Session *session; +static Stream *stream; +static GDataInputStream *stdin_stream; + +static void +emit_after_unbind_test (void) +{ + g_debug ("Binding keyboard capability"); + session_add_seat_capability (session, EI_DEVICE_CAP_KEYBOARD); + while (!session->keyboard) + g_main_context_iteration (NULL, TRUE); + + g_debug ("Pressing space"); + ei_device_keyboard_key (session->keyboard, KEY_SPACE, true); + ei_device_frame (session->keyboard, g_get_monotonic_time ()); + ei_device_keyboard_key (session->keyboard, KEY_SPACE, false); + ei_device_frame (session->keyboard, g_get_monotonic_time ()); + + g_debug ("Unbinding keyboard capability"); + session_remove_seat_capability (session, EI_DEVICE_CAP_KEYBOARD); + + g_debug ("Pressing Esc"); + ei_device_keyboard_key (session->keyboard, KEY_ESC, true); + ei_device_frame (session->keyboard, g_get_monotonic_time ()); + ei_device_keyboard_key (session->keyboard, KEY_ESC, false); + ei_device_frame (session->keyboard, g_get_monotonic_time ()); + + g_debug ("Binding pointer capability"); + session_add_seat_capability (session, + EI_DEVICE_CAP_POINTER); + while (!session->pointer) + g_main_context_iteration (NULL, TRUE); + + g_debug ("Move pointer"); + ei_device_pointer_motion (session->pointer, 1.0, 1.0); + ei_device_frame (session->pointer, g_get_monotonic_time ()); + + g_debug ("Unbinding pointer capability"); + session_remove_seat_capability (session, EI_DEVICE_CAP_POINTER); + + g_debug ("Move pointer again"); + ei_device_pointer_motion (session->pointer, 1.0, 1.0); + ei_device_frame (session->pointer, g_get_monotonic_time ()); + + while (session->pointer) + g_main_context_iteration (NULL, TRUE); + + g_debug ("Binding absolute pointer capability"); + session_add_seat_capability (session, + EI_DEVICE_CAP_POINTER_ABSOLUTE); + while (!session->pointer) + g_main_context_iteration (NULL, TRUE); + + g_debug ("Move absolute pointer"); + ei_device_pointer_motion_absolute (session->pointer, 1.0, 1.0); + ei_device_frame (session->pointer, g_get_monotonic_time ()); + + g_debug ("Unbinding absolute pointer capability"); + session_remove_seat_capability (session, EI_DEVICE_CAP_POINTER_ABSOLUTE); + + g_debug ("Move absolute pointer again"); + ei_device_pointer_motion_absolute (session->pointer, 1.0, 1.0); + ei_device_frame (session->pointer, g_get_monotonic_time ()); + + while (session->pointer) + g_main_context_iteration (NULL, TRUE); +} + +G_GNUC_NULL_TERMINATED +static void +send_command (const char *command, + ...) +{ + g_autoptr (GStrvBuilder) args_builder = NULL; + g_auto (GStrv) args = NULL; + va_list vap; + g_autofree char *response = NULL; + g_autoptr (GError) error = NULL; + + args_builder = g_strv_builder_new (); + g_strv_builder_add (args_builder, command); + + va_start (vap, command); + while (TRUE) + { + char *arg; + + arg = va_arg (vap, char *); + if (!arg) + break; + + g_strv_builder_add (args_builder, arg); + } + va_end (vap); + + args = g_strv_builder_end (args_builder); + command = g_strjoinv (" ", args); + + fprintf (stdout, "%s\n", command); + fflush (stdout); + + response = g_data_input_stream_read_line (stdin_stream, NULL, NULL, &error); + g_assert_no_error (error); + + g_assert_cmpstr (response, ==, "OK"); +} + +static void +keyboard_layout_test (void) +{ + struct ei_device *old_keyboard; + + g_debug ("Binding keyboard capability"); + session_add_seat_capability (session, EI_DEVICE_CAP_KEYBOARD); + while (!session->keyboard) + g_main_context_iteration (NULL, TRUE); + + ei_device_keyboard_key (session->keyboard, KEY_B, true); + ei_device_frame (session->keyboard, g_get_monotonic_time ()); + ei_device_keyboard_key (session->keyboard, KEY_B, false); + ei_device_frame (session->keyboard, g_get_monotonic_time ()); + + session_ei_roundtrip (session); + send_command ("flush_input", NULL); + + old_keyboard = ei_device_ref (session->keyboard); + + send_command ("switch_keyboard_layout", "us", "dvorak-alt-intl", NULL); + + ei_device_keyboard_key (old_keyboard, KEY_B, true); + ei_device_frame (old_keyboard, g_get_monotonic_time ()); + ei_device_keyboard_key (old_keyboard, KEY_B, false); + ei_device_frame (old_keyboard, g_get_monotonic_time ()); + + ei_device_unref (old_keyboard); +} + +static void +change_viewport_test (void) +{ + struct ei_device *old_pointer; + + g_debug ("Binding absolute pointer capability"); + session_add_seat_capability (session, + EI_DEVICE_CAP_POINTER_ABSOLUTE); + while (!session->pointer) + g_main_context_iteration (NULL, TRUE); + + g_debug ("Move absolute pointer"); + ei_device_pointer_motion_absolute (session->pointer, 1.0, 1.0); + ei_device_frame (session->pointer, g_get_monotonic_time ()); + + session_ei_roundtrip (session); + send_command ("flush_input", NULL); + + old_pointer = ei_device_ref (session->pointer); + + send_command ("update_viewports", session_get_id (session), NULL); + + ei_device_pointer_motion_absolute (old_pointer, 10.0, 10.0); + ei_device_frame (old_pointer, g_get_monotonic_time ()); +} + +static void +run_test (int argc, + char **argv) +{ + g_debug ("Running test %s", argv[1]); + + if (strcmp (argv[1], "emit-after-unbind") == 0) + emit_after_unbind_test (); + else if (strcmp (argv[1], "keyboard-layout") == 0) + keyboard_layout_test (); + else if (strcmp (argv[1], "change-viewport") == 0) + change_viewport_test (); + else + g_error ("Unknown test '%s'", argv[1]); +} + +static void +print_to_stderr (const char *text) +{ + fputs (text, stderr); + fflush (stderr); +} + +int +main (int argc, + char **argv) +{ + GInputStream *stdin_unix_stream; + + g_set_print_handler (print_to_stderr); + g_test_init (&argc, &argv, NULL); + g_assert_cmpint (argc, ==, 2); + + stdin_unix_stream = g_unix_input_stream_new (fileno (stdin), FALSE); + stdin_stream = g_data_input_stream_new (stdin_unix_stream); + g_object_unref (stdin_unix_stream); + + g_debug ("Initializing PipeWire"); + init_pipewire (); + + g_debug ("Creating remote desktop session"); + remote_desktop = remote_desktop_new (); + screen_cast = screen_cast_new (); + session = screen_cast_create_session (remote_desktop, screen_cast); + session_connect_to_eis (session); + + stream = session_record_virtual (session, 800, 600, CURSOR_MODE_METADATA); + + g_debug ("Starting remote desktop session"); + session_start (session); + + run_test (argc, argv); + + g_debug ("Stopping session"); + session_stop (session); + + stream_free (stream); + session_free (session); + screen_cast_free (screen_cast); + remote_desktop_free (remote_desktop); + + release_pipewire (); + + g_object_unref (stdin_stream); + + g_debug ("Done"); + + return EXIT_SUCCESS; +} diff --git a/src/tests/remote-desktop-tests.c b/src/tests/remote-desktop-tests.c new file mode 100644 index 000000000..56c0e0f41 --- /dev/null +++ b/src/tests/remote-desktop-tests.c @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2025 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + */ + +#include "config.h" + +#include "backends/meta-backend-private.h" +#include "backends/meta-dbus-session-manager.h" +#include "backends/meta-remote-desktop-session.h" +#include "tests/meta-test-utils.h" +#include "tests/meta-test/meta-context-test.h" + +static MetaContext *test_context; + +static void +meta_test_remote_desktop_emit_after_unbind (void) +{ + g_autoptr (GSubprocess) subprocess = NULL; + + subprocess = meta_launch_test_executable (G_SUBPROCESS_FLAGS_NONE, + "mutter-remote-desktop-tests-client", + "emit-after-unbind", + NULL); + meta_wait_test_process (subprocess); +} + +static gboolean +remote_desktop_test_client_command (int argc, + GStrv argv, + gpointer user_data) +{ + if (argc == 1 && strcmp (argv[0], "flush_input") == 0) + { + g_debug ("Flushing input"); + meta_flush_input (test_context); + return TRUE; + } + else if (argc == 3 && strcmp (argv[0], "switch_keyboard_layout") == 0) + { + MetaBackend *backend = meta_context_get_backend (test_context); + const char *layout = argv[1]; + const char *variant = argv[2]; + + g_debug ("Switching keyboard layout to %s, %s", layout, variant); + meta_backend_set_keymap (backend, layout, variant, "", ""); + + return TRUE; + } + else if (argc == 2 && strcmp (argv[0], "update_viewports") == 0) + { + MetaBackend *backend = meta_context_get_backend (test_context); + MetaRemoteDesktop *remote_desktop = + meta_backend_get_remote_desktop (backend); + MetaDbusSessionManager *session_manager = + META_DBUS_SESSION_MANAGER (remote_desktop); + MetaDbusSession *dbus_session; + MetaRemoteDesktopSession *session; + MetaEis *eis; + + dbus_session = + meta_dbus_session_manager_get_session (session_manager, + argv[1]); + session = + META_REMOTE_DESKTOP_SESSION (dbus_session); + eis = meta_remote_desktop_session_get_eis (session); + + g_signal_emit_by_name (eis, "viewports-changed"); + + return TRUE; + } + else + { + return FALSE; + } +} + +static void +meta_test_remote_desktop_keyboard_layout (void) +{ + g_autoptr (GSubprocess) subprocess = NULL; + + subprocess = meta_launch_test_executable (G_SUBPROCESS_FLAGS_STDOUT_PIPE | + G_SUBPROCESS_FLAGS_STDIN_PIPE, + "mutter-remote-desktop-tests-client", + "keyboard-layout", + NULL); + meta_test_process_watch_commands (subprocess, + remote_desktop_test_client_command, + NULL); + meta_wait_test_process (subprocess); +} + +static void +meta_test_remote_desktop_change_viewport (void) +{ + MetaBackend *backend = meta_context_get_backend (test_context); + MetaRemoteDesktop *remote_desktop = meta_backend_get_remote_desktop (backend); + MetaDbusSessionManager *session_manager = + META_DBUS_SESSION_MANAGER (remote_desktop); + ClutterSeat *seat = meta_backend_get_default_seat (backend); + g_autoptr (GSubprocess) subprocess = NULL; + graphene_point_t pos; + + subprocess = meta_launch_test_executable (G_SUBPROCESS_FLAGS_STDOUT_PIPE | + G_SUBPROCESS_FLAGS_STDIN_PIPE, + "mutter-remote-desktop-tests-client", + "change-viewport", + NULL); + meta_test_process_watch_commands (subprocess, + remote_desktop_test_client_command, + NULL); + meta_wait_test_process (subprocess); + + while (meta_dbus_session_manager_get_num_sessions (session_manager) > 0) + g_main_context_iteration (NULL, TRUE); + + meta_flush_input (test_context); + + clutter_seat_query_state (seat, + clutter_seat_get_pointer (seat), + NULL, &pos, NULL); + + g_assert_cmpfloat_with_epsilon (pos.x, 1.0f, DBL_EPSILON); + g_assert_cmpfloat_with_epsilon (pos.y, 1.0f, DBL_EPSILON); +} + +static void +init_tests (void) +{ + g_test_add_func ("/backends/native/remote-desktop/emit-after-unbind", + meta_test_remote_desktop_emit_after_unbind); + g_test_add_func ("/backends/native/remote-desktop/keyboard-layout", + meta_test_remote_desktop_keyboard_layout); + g_test_add_func ("/backends/native/remote-desktop/change-viewport", + meta_test_remote_desktop_change_viewport); +} + +int +main (int argc, + char **argv) +{ + g_autoptr (MetaContext) context = NULL; + + context = meta_create_test_context (META_CONTEXT_TEST_TYPE_HEADLESS, + META_CONTEXT_TEST_FLAG_NO_X11); + g_assert_true (meta_context_configure (context, &argc, &argv, NULL)); + + test_context = context; + + init_tests (); + + return meta_context_test_run_tests (META_CONTEXT_TEST (context), + META_TEST_RUN_FLAG_NONE); +} diff --git a/src/tests/remote-desktop-utils.c b/src/tests/remote-desktop-utils.c index 6f3515ef1..380cc621c 100644 --- a/src/tests/remote-desktop-utils.c +++ b/src/tests/remote-desktop-utils.c @@ -876,6 +876,15 @@ session_record_monitor (Session *session, return stream; } +const char * +session_get_id (Session *session) +{ + MetaDBusRemoteDesktopSession *proxy = + session->remote_desktop_session_proxy; + + return meta_dbus_remote_desktop_session_get_session_id (proxy); +} + Session * session_new (MetaDBusRemoteDesktopSession *remote_desktop_session_proxy, MetaDBusScreenCastSession *screen_cast_session_proxy) diff --git a/src/tests/remote-desktop-utils.h b/src/tests/remote-desktop-utils.h index 047f5a910..8950a4c7c 100644 --- a/src/tests/remote-desktop-utils.h +++ b/src/tests/remote-desktop-utils.h @@ -146,6 +146,8 @@ Stream * session_record_monitor (Session *session, const char *connector, CursorMode cursor_mode); +const char * session_get_id (Session *session); + Session * session_new (MetaDBusRemoteDesktopSession *remote_desktop_session_proxy, MetaDBusScreenCastSession *screen_cast_session_proxy);