tests: Add more session management tests

Ensure that windows get restored either in maximized and unmaximized
state. And ensure that monitors being removed result in windows
snapping back to reachable positions.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/4311>
This commit is contained in:
Carlos Garnacho 2025-02-28 15:42:41 +01:00
parent e82cc58614
commit 351f2fddff
5 changed files with 465 additions and 0 deletions

View File

@ -92,6 +92,7 @@ void meta_context_set_trace_file (MetaContext *context,
const char *trace_file);
#endif
META_EXPORT_TEST
MetaSessionManager * meta_context_get_session_manager (MetaContext *context);
META_EXPORT_TEST

View File

@ -660,6 +660,7 @@ gboolean meta_window_has_fullscreen_monitors (MetaWindow *window);
void meta_window_adjust_fullscreen_monitor_rect (MetaWindow *window,
MtkRectangle *monitor_rect);
META_EXPORT_TEST
void meta_window_resize_frame (MetaWindow *window,
gboolean user_op,
int w,

View File

@ -93,6 +93,9 @@ wayland_test_clients = [
{
'name': 'xdg-session-management-replace',
},
{
'name': 'xdg-session-management-restore',
},
{
'name': 'xdg-toplevel-bounds',
},

View File

@ -0,0 +1,220 @@
/*
* Copyright (C) 2024 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 <glib.h>
#include "wayland-test-client-utils.h"
#include "session-management-v1-client-protocol.h"
typedef struct _TestDisplayState
{
struct xx_session_manager_v1 *session_manager;
} TestDisplayState;
static gboolean running = FALSE;
static void
handle_registry_global (void *user_data,
struct wl_registry *registry,
uint32_t id,
const char *interface,
uint32_t version)
{
WaylandDisplay *display = user_data;
TestDisplayState *test_state = display->test_state;
if (strcmp (interface, "xx_session_manager_v1") == 0)
{
test_state->session_manager =
wl_registry_bind (registry, id, &xx_session_manager_v1_interface, 1);
}
}
static void
handle_registry_global_remove (void *user_data,
struct wl_registry *registry,
uint32_t name)
{
}
static const struct wl_registry_listener registry_listener = {
handle_registry_global,
handle_registry_global_remove
};
typedef struct
{
gboolean received_created;
gboolean received_restored;
} TestCreateState;
typedef struct
{
gboolean configured;
gboolean restored;
} ToplevelSessionState;
static void
test_create_created (void *user_data,
struct xx_session_v1 *xdg_session_v1,
const char *id)
{
TestCreateState *state = user_data;
state->received_created = TRUE;
}
static void
test_create_restored (void *user_data,
struct xx_session_v1 *xdg_session_v1)
{
TestCreateState *state = user_data;
state->received_restored = TRUE;
}
static void
test_create_replaced (void *user_data,
struct xx_session_v1 *xdg_session_v1)
{
}
static struct xx_session_v1_listener test_create_session_listener = {
test_create_created,
test_create_restored,
test_create_replaced,
};
static void
toplevel_restored (void *user_data,
struct xx_toplevel_session_v1 *toplevel_session,
struct xdg_toplevel *toplevel)
{
ToplevelSessionState *toplevel_state = user_data;
toplevel_state->restored = TRUE;
}
static struct xx_toplevel_session_v1_listener toplevel_session_listener = {
toplevel_restored,
};
static void
on_toplevel_configured (WaylandSurface *surface,
ToplevelSessionState *toplevel_state)
{
toplevel_state->configured = TRUE;
}
static void
simple (WaylandDisplay *display,
const char *session_id)
{
TestDisplayState *test_state = display->test_state;
g_autoptr (WaylandSurface) toplevel1 = NULL;
struct xx_session_v1 *session;
struct xx_toplevel_session_v1 *toplevel_session1;
TestCreateState state = {};
ToplevelSessionState toplevel_state1 = {};
toplevel1 = wayland_surface_new (display, "toplevel1",
100, 100, 0xff50ff50);
g_signal_connect (toplevel1, "configure",
G_CALLBACK (on_toplevel_configured),
&toplevel_state1);
session =
xx_session_manager_v1_get_session (test_state->session_manager,
XX_SESSION_MANAGER_V1_REASON_LAUNCH,
session_id);
xx_session_v1_add_listener (session, &test_create_session_listener, &state);
while (!state.received_created && !state.received_restored)
wayland_display_dispatch (display);
if (session_id)
g_assert_true (state.received_restored);
else
g_assert_true (state.received_created);
toplevel_session1 = xx_session_v1_restore_toplevel (session,
toplevel1->xdg_toplevel,
"toplevel1");
xx_toplevel_session_v1_add_listener (toplevel_session1,
&toplevel_session_listener,
&toplevel_state1);
wl_surface_commit (toplevel1->wl_surface);
running = TRUE;
while (running)
wayland_display_dispatch (display);
xx_toplevel_session_v1_destroy (toplevel_session1);
xx_session_v1_destroy (session);
}
static void
on_surface_painted (WaylandDisplay *display,
WaylandSurface *surface)
{
static gboolean first_painted = FALSE;
if (first_painted)
return;
first_painted = TRUE;
/* Sync point to let parent test do checks */
test_driver_sync_point (display->test_driver, 0, NULL);
wait_for_sync_event (display, 0);
running = FALSE;
}
int
main (int argc,
char **argv)
{
g_autoptr (WaylandDisplay) display = NULL;
struct wl_registry *registry;
TestDisplayState *test_state;
const char *session_id = NULL;
if (argc > 1)
session_id = argv[1];
display = wayland_display_new (WAYLAND_DISPLAY_CAPABILITY_TEST_DRIVER);
test_state = g_new0 (TestDisplayState, 1);
display->test_state = test_state;
display->destroy_test_state = g_free;
g_signal_connect (display, "surface-painted",
G_CALLBACK (on_surface_painted), NULL);
registry = wl_display_get_registry (display->display);
wl_registry_add_listener (registry, &registry_listener, display);
wl_display_roundtrip (display->display);
g_assert_nonnull (test_state->session_manager);
simple (display, session_id);
return EXIT_SUCCESS;
}

View File

@ -37,6 +37,7 @@
#include "wayland/meta-wayland-client-private.h"
#include "wayland/meta-wayland-filter-manager.h"
#include "wayland/meta-wayland-surface-private.h"
#include "wayland/meta-window-wayland.h"
#include "dummy-client-protocol.h"
#include "dummy-server-protocol.h"
@ -629,6 +630,17 @@ wait_until_after_paint (void)
g_main_context_iteration (NULL, FALSE);
}
static void
on_session_instantiated (MetaSessionManager *session_manager,
const char *name,
MetaSessionState *state,
gpointer user_data)
{
char **session_id = user_data;
*session_id = g_strdup (name);
}
static void
set_struts (MtkRectangle rect,
MetaSide side)
@ -684,6 +696,226 @@ get_primary_logical_monitor_layout (void)
return meta_logical_monitor_get_layout (logical_monitor);
}
static void
toplevel_sessions_restore (void)
{
MetaSessionManager *session_manager;
MetaWaylandTestClient *wayland_test_client;
MetaBackend *backend = meta_context_get_backend (test_context);
ClutterActor *stage = meta_backend_get_stage (backend);
MetaWindow *window;
MtkRectangle frame_rect;
g_autofree char *session_id = NULL;
session_manager = meta_context_get_session_manager (test_context);
g_signal_connect (session_manager, "session-instantiated",
G_CALLBACK (on_session_instantiated), &session_id);
/* Launch client once, resize window */
wayland_test_client =
meta_wayland_test_client_new (test_context, "xdg-session-management-restore");
wait_for_sync_point (0);
wait_for_paint (stage);
window = find_client_window ("toplevel1");
g_assert_nonnull (window);
g_assert_nonnull (window->monitor);
frame_rect = meta_window_config_get_rect (window->config);
g_assert_cmpint (frame_rect.width, ==, 100);
g_assert_cmpint (frame_rect.height, ==, 100);
meta_window_move_resize_frame (window, FALSE, 123, 234, 200, 200);
wait_for_paint (stage);
meta_wayland_test_driver_emit_sync_event (test_driver, 0);
meta_wayland_test_client_finish (wayland_test_client);
g_assert_nonnull (session_id);
/* Launch client again, check window size persists */
wayland_test_client =
meta_wayland_test_client_new_with_args (test_context,
"xdg-session-management-restore",
session_id,
NULL);
wait_for_sync_point (0);
window = find_client_window ("toplevel1");
g_assert_nonnull (window);
g_assert_nonnull (window->monitor);
frame_rect = meta_window_config_get_rect (window->config);
g_assert_cmpint (frame_rect.x, ==, 123);
g_assert_cmpint (frame_rect.y, ==, 234);
g_assert_cmpint (frame_rect.width, ==, 200);
g_assert_cmpint (frame_rect.height, ==, 200);
meta_wayland_test_driver_emit_sync_event (test_driver, 0);
meta_wayland_test_client_finish (wayland_test_client);
}
static void
toplevel_sessions_restore_fullscreen (void)
{
MetaSessionManager *session_manager;
g_autoptr (MetaVirtualMonitor) second_virtual_monitor = NULL;
MetaBackend *backend = meta_context_get_backend (test_context);
ClutterActor *stage = meta_backend_get_stage (backend);
MetaWaylandTestClient *wayland_test_client;
MetaWindow *window;
MtkRectangle frame_rect, monitor_layout;
g_autofree char *session_id = NULL;
monitor_layout = get_primary_logical_monitor_layout ();
second_virtual_monitor = meta_create_test_monitor (test_context,
800, 600, 60.0);
session_manager = meta_context_get_session_manager (test_context);
g_signal_connect (session_manager, "session-instantiated",
G_CALLBACK (on_session_instantiated), &session_id);
/* Launch client once, resize window */
wayland_test_client =
meta_wayland_test_client_new (test_context, "xdg-session-management-restore");
wait_for_sync_point (0);
wait_for_paint (stage);
window = find_client_window ("toplevel1");
g_assert_nonnull (window);
/* Move to second monitor */
meta_window_move_resize_frame (window, FALSE,
monitor_layout.width + 123, 123, 100, 100);
wait_for_paint (stage);
frame_rect = meta_window_config_get_rect (window->config);
g_assert_cmpint (frame_rect.x, ==, monitor_layout.width + 123);
g_assert_cmpint (frame_rect.y, ==, 123);
g_assert_cmpint (frame_rect.width, ==, 100);
g_assert_cmpint (frame_rect.height, ==, 100);
meta_window_make_fullscreen (window);
while (!meta_window_wayland_is_acked_fullscreen (META_WINDOW_WAYLAND (window)))
g_main_context_iteration (NULL, TRUE);
frame_rect = meta_window_config_get_rect (window->config);
g_assert_cmpint (frame_rect.x, ==, 640);
g_assert_cmpint (frame_rect.y, ==, 0);
g_assert_cmpint (frame_rect.width, ==, 800);
g_assert_cmpint (frame_rect.height, ==, 600);
meta_wayland_test_driver_emit_sync_event (test_driver, 0);
meta_wayland_test_client_finish (wayland_test_client);
g_assert_nonnull (session_id);
/* Launch client again, check window persists maximized on second monitor */
wayland_test_client =
meta_wayland_test_client_new_with_args (test_context,
"xdg-session-management-restore",
session_id,
NULL);
wait_for_sync_point (0);
window = find_client_window ("toplevel1");
g_assert_nonnull (window);
g_assert_nonnull (window->monitor);
frame_rect = meta_window_config_get_rect (window->config);
g_assert_cmpint (frame_rect.x, ==, monitor_layout.width);
g_assert_cmpint (frame_rect.y, ==, 0);
g_assert_cmpint (frame_rect.width, ==, 800);
g_assert_cmpint (frame_rect.height, ==, 600);
meta_wayland_test_driver_emit_sync_event (test_driver, 0);
meta_wayland_test_client_finish (wayland_test_client);
}
static void
toplevel_sessions_restore_fullscreen_monitor_removed (void)
{
MetaSessionManager *session_manager;
g_autoptr (MetaVirtualMonitor) second_virtual_monitor = NULL;
MetaBackend *backend = meta_context_get_backend (test_context);
ClutterActor *stage = meta_backend_get_stage (backend);
MetaWaylandTestClient *wayland_test_client;
MetaWindow *window;
MtkRectangle frame_rect, monitor_layout;
g_autofree char *session_id = NULL;
monitor_layout = get_primary_logical_monitor_layout ();
second_virtual_monitor = meta_create_test_monitor (test_context,
640, 480, 60.0);
session_manager = meta_context_get_session_manager (test_context);
g_signal_connect (session_manager, "session-instantiated",
G_CALLBACK (on_session_instantiated), &session_id);
/* Launch client once, resize window */
wayland_test_client =
meta_wayland_test_client_new (test_context, "xdg-session-management-restore");
wait_for_sync_point (0);
wait_for_paint (stage);
window = find_client_window ("toplevel1");
g_assert_nonnull (window);
/* Move to second monitor */
meta_window_move_resize_frame (window, FALSE,
monitor_layout.width, 123, 100, 100);
meta_window_make_fullscreen (window);
while (!meta_window_wayland_is_acked_fullscreen (META_WINDOW_WAYLAND (window)))
g_main_context_iteration (NULL, TRUE);
frame_rect = meta_window_config_get_rect (window->config);
g_assert_cmpint (frame_rect.x, ==, monitor_layout.width);
g_assert_cmpint (frame_rect.y, ==, 0);
g_assert_cmpint (frame_rect.width, ==, 640);
g_assert_cmpint (frame_rect.height, ==, 480);
meta_wayland_test_driver_emit_sync_event (test_driver, 0);
meta_wayland_test_client_finish (wayland_test_client);
g_assert_nonnull (session_id);
/* Destroy second monitor */
g_clear_object (&second_virtual_monitor);
wait_for_paint (stage);
/* Launch client again, check window moves to first monitor */
wayland_test_client =
meta_wayland_test_client_new_with_args (test_context,
"xdg-session-management-restore",
session_id,
NULL);
wait_for_sync_point (0);
wait_for_paint (stage);
window = find_client_window ("toplevel1");
g_assert_nonnull (window);
g_assert_nonnull (window->monitor);
frame_rect = meta_window_config_get_rect (window->config);
g_assert_cmpint (frame_rect.x, ==, 0);
g_assert_cmpint (frame_rect.y, ==, 0);
g_assert_cmpint (frame_rect.width, ==, 640);
g_assert_cmpint (frame_rect.height, ==, 480);
meta_wayland_test_driver_emit_sync_event (test_driver, 0);
meta_wayland_test_client_finish (wayland_test_client);
}
static void
toplevel_bounds_struts (void)
{
@ -1070,10 +1302,18 @@ init_tests (void)
toplevel_sessions);
g_test_add_func ("/wayland/toplevel/sessions-replace",
toplevel_sessions_replace);
g_test_add_func ("/wayland/toplevel/sessions-restore",
toplevel_sessions_restore);
#ifdef MUTTER_PRIVILEGED_TEST
(void)(toplevel_sessions_restore_fullscreen);
(void)(toplevel_sessions_restore_fullscreen_monitor_removed);
(void)(toplevel_bounds_struts);
(void)(toplevel_bounds_monitors);
#else
g_test_add_func ("/wayland/toplevel/sessions-restore-fullscreen",
toplevel_sessions_restore_fullscreen);
g_test_add_func ("/wayland/toplevel/sessions-restore-fullscreen-monitor-removed",
toplevel_sessions_restore_fullscreen_monitor_removed);
g_test_add_func ("/wayland/toplevel/bounds/struts",
toplevel_bounds_struts);
g_test_add_func ("/wayland/toplevel/bounds/monitors",