tests: Add remote desktop tests

This requires a newer libei, for ei_ping.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/4210>
This commit is contained in:
Jonas Ådahl 2025-01-10 17:28:05 +01:00
parent e6e725591e
commit 01c9c428b7
13 changed files with 505 additions and 3 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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);
}

View File

@ -22,8 +22,10 @@
#include <glib-object.h>
#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);

View File

@ -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,

View File

@ -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);

View File

@ -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,
],
},
]

View File

@ -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 <http://www.gnu.org/licenses/>.
*
*/
#include "config.h"
#include <gio/gunixinputstream.h>
#include <glib.h>
#include <libei.h>
#include <linux/input.h>
#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;
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*
*/
#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);
}

View File

@ -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)

View File

@ -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);