mutter/src/tests/wayland-unit-tests.c
Jonas Ådahl d2c798838e wayland/xdg-shell: Warn when invalid geometry is set
A client is not allowed to send an empty window geometry, and it is
specified that if it does so an error should be raised. Respect this
rule, ignore bogus geometries sent by clients with a warning.

Also add a soft assert that we don't try to "resend" a configuration
that was never sent, as doing so would result in SIGFPE as the geometry
scale is 0.

This fixes a SIGFPE crash occurring when a client did this.

Related: https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/2808
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/1527
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1557>
2020-12-03 21:39:13 +00:00

234 lines
7.3 KiB
C

/*
* Copyright (C) 2019 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 "tests/wayland-unit-tests.h"
#include <gio/gio.h>
#include <wayland-server.h>
#include "wayland/meta-wayland.h"
#include "wayland/meta-wayland-actor-surface.h"
#include "wayland/meta-wayland-surface.h"
#include "wayland/meta-wayland-private.h"
#include "test-driver-server-protocol.h"
typedef struct _WaylandTestClient
{
GSubprocess *subprocess;
char *path;
GMainLoop *main_loop;
} WaylandTestClient;
static char *
get_test_client_path (const char *test_client_name)
{
return g_test_build_filename (G_TEST_BUILT,
"src",
"tests",
"wayland-test-clients",
test_client_name,
NULL);
}
static WaylandTestClient *
wayland_test_client_new (const char *test_client_name)
{
MetaWaylandCompositor *compositor;
const char *wayland_display_name;
g_autofree char *test_client_path = NULL;
g_autoptr (GSubprocessLauncher) launcher = NULL;
GSubprocess *subprocess;
GError *error = NULL;
WaylandTestClient *wayland_test_client;
compositor = meta_wayland_compositor_get_default ();
wayland_display_name = meta_wayland_get_wayland_display_name (compositor);
test_client_path = get_test_client_path (test_client_name);
launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_NONE);
g_subprocess_launcher_setenv (launcher,
"WAYLAND_DISPLAY", wayland_display_name,
TRUE);
subprocess = g_subprocess_launcher_spawn (launcher,
&error,
test_client_path,
NULL);
if (!subprocess)
{
g_error ("Failed to launch Wayland test client '%s': %s",
test_client_path, error->message);
}
wayland_test_client = g_new0 (WaylandTestClient, 1);
wayland_test_client->subprocess = subprocess;
wayland_test_client->path = g_strdup (test_client_name);
wayland_test_client->main_loop = g_main_loop_new (NULL, FALSE);
return wayland_test_client;
}
static void
wayland_test_client_finished (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
WaylandTestClient *wayland_test_client = user_data;
GError *error = NULL;
if (!g_subprocess_wait_finish (wayland_test_client->subprocess,
res,
&error))
{
g_error ("Failed to wait for Wayland test client '%s': %s",
wayland_test_client->path, error->message);
}
g_main_loop_quit (wayland_test_client->main_loop);
}
static void
wayland_test_client_finish (WaylandTestClient *wayland_test_client)
{
g_subprocess_wait_async (wayland_test_client->subprocess, NULL,
wayland_test_client_finished, wayland_test_client);
g_main_loop_run (wayland_test_client->main_loop);
g_assert_true (g_subprocess_get_successful (wayland_test_client->subprocess));
g_main_loop_unref (wayland_test_client->main_loop);
g_free (wayland_test_client->path);
g_object_unref (wayland_test_client->subprocess);
g_free (wayland_test_client);
}
static void
subsurface_remap_toplevel (void)
{
WaylandTestClient *wayland_test_client;
wayland_test_client = wayland_test_client_new ("subsurface-remap-toplevel");
wayland_test_client_finish (wayland_test_client);
}
static void
subsurface_invalid_subsurfaces (void)
{
WaylandTestClient *wayland_test_client;
wayland_test_client = wayland_test_client_new ("invalid-subsurfaces");
g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING,
"WL: error in client communication*");
g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING,
"WL: error in client communication*");
wayland_test_client_finish (wayland_test_client);
g_test_assert_expected_messages ();
}
static void
subsurface_invalid_xdg_shell_actions (void)
{
WaylandTestClient *wayland_test_client;
wayland_test_client = wayland_test_client_new ("invalid-xdg-shell-actions");
g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING,
"Invalid geometry * set on xdg_surface*");
wayland_test_client_finish (wayland_test_client);
g_test_assert_expected_messages ();
}
static void
on_actor_destroyed (ClutterActor *actor,
struct wl_resource *callback)
{
wl_callback_send_done (callback, 0);
wl_resource_destroy (callback);
}
static void
sync_actor_destroy (struct wl_client *client,
struct wl_resource *resource,
uint32_t id,
struct wl_resource *surface_resource)
{
MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource);
MetaWaylandActorSurface *actor_surface;
MetaSurfaceActor *actor;
struct wl_resource *callback;
g_assert_nonnull (surface);
actor_surface = (MetaWaylandActorSurface *) surface->role;
g_assert_nonnull (actor_surface);
actor = meta_wayland_actor_surface_get_actor (actor_surface);
g_assert_nonnull (actor);
callback = wl_resource_create (client, &wl_callback_interface, 1, id);
g_signal_connect (actor, "destroy", G_CALLBACK (on_actor_destroyed),
callback);
}
static const struct test_driver_interface meta_test_driver_interface = {
sync_actor_destroy,
};
static void
bind_test_driver (struct wl_client *client,
void *data,
uint32_t version,
uint32_t id)
{
struct wl_resource *resource;
resource = wl_resource_create (client, &test_driver_interface,
version, id);
wl_resource_set_implementation (resource, &meta_test_driver_interface,
NULL, NULL);
}
void
pre_run_wayland_tests (void)
{
MetaWaylandCompositor *compositor;
compositor = meta_wayland_compositor_get_default ();
g_assert_nonnull (compositor);
if (wl_global_create (compositor->wayland_display,
&test_driver_interface,
1,
NULL, bind_test_driver) == NULL)
g_error ("Failed to register a global wl-subcompositor object");
}
void
init_wayland_tests (void)
{
g_test_add_func ("/wayland/subsurface/remap-toplevel",
subsurface_remap_toplevel);
g_test_add_func ("/wayland/subsurface/invalid-subsurfaces",
subsurface_invalid_subsurfaces);
g_test_add_func ("/wayland/subsurface/invalid-xdg-shell-actions",
subsurface_invalid_xdg_shell_actions);
}