From a9a4923c03d5a00ccf4d2eece0a4fa98833b324f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Wed, 20 Nov 2024 21:36:16 +0100 Subject: [PATCH] tests/cursor-tests: Also test cursor rendering in screen casting This uses the ref test infrastructure inside the cursor tests screen cast client, and verifies the content on the screen cast matches the content of the compositor, both when using the 'embedded' cursor mode, and the 'metadata' cursor mode. Part-of: --- src/tests/cursor-tests-screen-cast-client.c | 166 ++++++++++++++++++++ src/tests/cursor-tests.c | 58 +++++++ src/tests/meson.build | 28 ++++ src/tests/remote-desktop-utils.c | 10 ++ src/tests/remote-desktop-utils.h | 15 ++ src/tests/screen-cast-client.c | 10 -- 6 files changed, 277 insertions(+), 10 deletions(-) create mode 100644 src/tests/cursor-tests-screen-cast-client.c diff --git a/src/tests/cursor-tests-screen-cast-client.c b/src/tests/cursor-tests-screen-cast-client.c new file mode 100644 index 000000000..d597e54ff --- /dev/null +++ b/src/tests/cursor-tests-screen-cast-client.c @@ -0,0 +1,166 @@ +/* + * 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 . + * + */ + +#include "config.h" + +#include "tests/remote-desktop-utils.h" + +#include "tests/meta-ref-test-utils.h" + +static MetaReftestFlag +reftest_flags_from_string (const char *flags) +{ + if (strcmp (flags, "update-ref") == 0) + return META_REFTEST_FLAG_UPDATE_REF; + g_assert_cmpstr (flags, ==, ""); + return META_REFTEST_FLAG_NONE; +} + +static void +draw_cursor (cairo_t *cr, + struct spa_meta_cursor *spa_cursor) +{ + struct spa_meta_bitmap *spa_bitmap; + cairo_surface_t *cursor_image; + + spa_bitmap = SPA_MEMBER (spa_cursor, spa_cursor->bitmap_offset, + struct spa_meta_bitmap); + g_assert_nonnull (spa_bitmap); + + cursor_image = + cairo_image_surface_create_for_data (SPA_MEMBER (spa_bitmap, + spa_bitmap->offset, + uint8_t), + CAIRO_FORMAT_ARGB32, + spa_bitmap->size.width, + spa_bitmap->size.height, + spa_bitmap->stride); + cairo_surface_mark_dirty (cursor_image); + + cairo_set_source_surface (cr, cursor_image, + spa_cursor->position.x - spa_cursor->hotspot.x, + spa_cursor->position.y - spa_cursor->hotspot.y); + cairo_paint (cr); +} + +static cairo_surface_t * +screen_cast_adaptor_capture (gpointer adaptor_data) +{ + Stream *stream = adaptor_data; + struct spa_buffer *spa_buffer; + cairo_surface_t *source_image; + cairo_surface_t *surface; + struct spa_meta_cursor *spa_cursor; + cairo_t *cr; + + g_assert_nonnull (stream->buffer); + + spa_buffer = stream->buffer->buffer; + g_assert_nonnull (spa_buffer->datas[0].data); + + source_image = + cairo_image_surface_create_for_data (spa_buffer->datas[0].data, + CAIRO_FORMAT_ARGB32, + stream->spa_format.size.width, + stream->spa_format.size.height, + spa_buffer->datas[0].chunk->stride); + cairo_surface_mark_dirty (source_image); + + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, + stream->spa_format.size.width, + stream->spa_format.size.height); + cr = cairo_create (surface); + cairo_set_source_surface (cr, source_image, 0.0, 0.0); + cairo_paint (cr); + + spa_cursor = spa_buffer_find_meta_data (spa_buffer, SPA_META_Cursor, + sizeof (*spa_cursor)); + + switch (stream->cursor_mode) + { + case CURSOR_MODE_HIDDEN: + case CURSOR_MODE_EMBEDDED: + g_assert_false (spa_meta_cursor_is_valid (spa_cursor)); + break; + case CURSOR_MODE_METADATA: + g_assert_true (spa_meta_cursor_is_valid (spa_cursor)); + draw_cursor (cr, spa_cursor); + break; + } + + cairo_destroy (cr); + + return surface; +} + +int +main (int argc, + char **argv) +{ + ScreenCast *screen_cast; + Session *session; + Stream *stream; + const char *test_name; + int test_seq; + CursorMode cursor_mode; + MetaReftestFlag ref_test_flags; + + g_test_init (&argc, &argv, NULL); + + g_assert_cmpint (argc, ==, 5); + + test_name = argv[1]; + test_seq = atoi (argv[2]); + cursor_mode = cursor_mode_from_string (argv[3]); + ref_test_flags = reftest_flags_from_string (argv[4]); + + g_debug ("Verifying screen cast cursor mode %s for test case %s", + argv[2], test_name); + + g_debug ("Initializing PipeWire"); + init_pipewire (); + + g_debug ("Creating screen cast session"); + screen_cast = screen_cast_new (); + session = screen_cast_create_session (NULL, screen_cast); + stream = session_record_monitor (session, NULL, cursor_mode); + + g_debug ("Starting screen cast stream"); + session_start (session); + + stream_wait_for_render (stream); + + meta_ref_test_verify (screen_cast_adaptor_capture, + stream, + test_name, + test_seq, + ref_test_flags); + + g_debug ("Stopping session"); + + session_stop (session); + stream_free (stream); + session_free (session); + screen_cast_free (screen_cast); + + release_pipewire (); + + g_debug ("Done"); + + return EXIT_SUCCESS; +} diff --git a/src/tests/cursor-tests.c b/src/tests/cursor-tests.c index 47aa18af5..a90525adc 100644 --- a/src/tests/cursor-tests.c +++ b/src/tests/cursor-tests.c @@ -21,6 +21,7 @@ #include "backends/meta-cursor-sprite-xcursor.h" #include "backends/meta-cursor-tracker-private.h" #include "backends/meta-logical-monitor.h" +#include "backends/meta-screen-cast.h" #include "clutter/clutter.h" #include "compositor/meta-window-actor-private.h" #include "core/meta-fraction.h" @@ -254,6 +255,52 @@ layout_mode_to_string (MetaLogicalMonitorLayoutMode layout_mode) g_assert_not_reached (); } +static const char * +cursor_mode_to_string (MetaScreenCastCursorMode cursor_mode) +{ + switch (cursor_mode) + { + case META_SCREEN_CAST_CURSOR_MODE_HIDDEN: + return "hidden"; + case META_SCREEN_CAST_CURSOR_MODE_EMBEDDED: + return "embedded"; + case META_SCREEN_CAST_CURSOR_MODE_METADATA: + return "metadata"; + } + + g_assert_not_reached (); +} + +static const char * +reftest_flags_to_string (MetaReftestFlag flags) +{ + if (flags & META_REFTEST_FLAG_UPDATE_REF) + return "update-ref"; + else + return ""; +} + +static void +verify_screen_cast_content (const char *ref_test_name, + int test_seq_no, + MetaScreenCastCursorMode cursor_mode) +{ + g_autoptr (GSubprocess) subprocess = NULL; + g_autofree char *test_seq_no_string = NULL; + MetaReftestFlag reftest_flags; + + test_seq_no_string = g_strdup_printf ("%d", test_seq_no); + reftest_flags = META_REFTEST_FLAG_NONE; + subprocess = + meta_launch_test_executable ("mutter-cursor-tests-screen-cast-client", + ref_test_name, + test_seq_no_string, + cursor_mode_to_string (cursor_mode), + reftest_flags_to_string (reftest_flags), + NULL); + meta_wait_test_process (subprocess); +} + static void wait_for_no_windows (void) { @@ -318,6 +365,13 @@ test_client_cursor (ClutterStageView *view, ref_test_seq, ref_test_flags); + verify_screen_cast_content (ref_test_name, + ref_test_seq, + META_SCREEN_CAST_CURSOR_MODE_EMBEDDED); + verify_screen_cast_content (ref_test_name, + ref_test_seq, + META_SCREEN_CAST_CURSOR_MODE_METADATA); + meta_wayland_test_driver_emit_sync_event (test_driver, 0); g_object_add_weak_pointer (G_OBJECT (window_actor), @@ -397,6 +451,10 @@ meta_test_native_cursor_scaling (void) ref_test_name, 0, meta_ref_test_determine_ref_test_flag ()); + verify_screen_cast_content (ref_test_name, 0, + META_SCREEN_CAST_CURSOR_MODE_EMBEDDED); + verify_screen_cast_content (ref_test_name, 0, + META_SCREEN_CAST_CURSOR_MODE_METADATA); test_client_cursor (view, CURSOR_SCALE_METHOD_BUFFER_SCALE, diff --git a/src/tests/meson.build b/src/tests/meson.build index ce3ffddec..9e449a2f5 100644 --- a/src/tests/meson.build +++ b/src/tests/meson.build @@ -390,6 +390,33 @@ input_capture_client = executable('mutter-input-capture-test-client', install_dir: mutter_installed_tests_libexecdir, ) +reftest_deps = [ + cairo_dep, + glib_dep, +] + +cursor_screen_cast_client = executable('mutter-cursor-tests-screen-cast-client', + sources: [ + 'cursor-tests-screen-cast-client.c', + 'meta-ref-test-utils.c', + 'meta-ref-test-utils.h', + remote_desktop_utils, + ], + include_directories: tests_includes, + c_args: [ + tests_c_args, + '-DG_LOG_DOMAIN="mutter-cursor-tests-screen-cast-client"', + ], + dependencies: [ + gio_dep, + libpipewire_dep, + reftest_deps, + ], + install: have_installed_tests, + install_dir: mutter_installed_tests_libexecdir, + install_rpath: pkglibdir, +) + # Native backend tests test_cases += [ { @@ -462,6 +489,7 @@ test_cases += [ ], 'depends': [ test_client_executables.get('cursor-tests-client'), + cursor_screen_cast_client, ] }, ] diff --git a/src/tests/remote-desktop-utils.c b/src/tests/remote-desktop-utils.c index 23a2f2594..f1ccb770f 100644 --- a/src/tests/remote-desktop-utils.c +++ b/src/tests/remote-desktop-utils.c @@ -430,6 +430,16 @@ stream_resize (Stream *stream, params, G_N_ELEMENTS (params)); } +void +stream_wait_for_render (Stream *stream) +{ + int initial_buffer_count = stream->buffer_count; + + g_debug ("Waiting for new buffer"); + while (stream->buffer_count == initial_buffer_count) + g_main_context_iteration (NULL, TRUE); +} + static void on_pipewire_stream_added (MetaDBusScreenCastStream *proxy, unsigned int node_id, diff --git a/src/tests/remote-desktop-utils.h b/src/tests/remote-desktop-utils.h index 4017be39d..d3053fba5 100644 --- a/src/tests/remote-desktop-utils.h +++ b/src/tests/remote-desktop-utils.h @@ -82,6 +82,19 @@ typedef struct _ScreenCast g_assert_cmpint (stream->cursor_y, ==, (y)); \ } +static inline CursorMode +cursor_mode_from_string (const char *name) +{ + if (strcmp (name, "hidden") == 0) + return CURSOR_MODE_HIDDEN; + else if (strcmp (name, "embedded") == 0) + return CURSOR_MODE_EMBEDDED; + else if (strcmp (name, "metadata") == 0) + return CURSOR_MODE_METADATA; + + g_assert_not_reached (); +} + void release_pipewire (void); void init_pipewire (void); @@ -90,6 +103,8 @@ void stream_resize (Stream *stream, int width, int height); +void stream_wait_for_render (Stream *stream); + void stream_free (Stream *stream); void session_notify_absolute_pointer (Session *session, diff --git a/src/tests/screen-cast-client.c b/src/tests/screen-cast-client.c index 560740c8f..6a475cf17 100644 --- a/src/tests/screen-cast-client.c +++ b/src/tests/screen-cast-client.c @@ -45,16 +45,6 @@ stream_wait_for_streaming (Stream *stream) g_main_context_iteration (NULL, TRUE); } -static void -stream_wait_for_render (Stream *stream) -{ - int initial_buffer_count = stream->buffer_count; - - g_debug ("Waiting for new buffer"); - while (stream->buffer_count == initial_buffer_count) - g_main_context_iteration (NULL, TRUE); -} - int main (int argc, char **argv)