diff --git a/clutter/clutter/clutter-stage-view-private.h b/clutter/clutter/clutter-stage-view-private.h index feee44e9e..345b59564 100644 --- a/clutter/clutter/clutter-stage-view-private.h +++ b/clutter/clutter/clutter-stage-view-private.h @@ -44,6 +44,7 @@ void clutter_stage_view_invalidate_projection (ClutterStageView *view); void clutter_stage_view_set_projection (ClutterStageView *view, const graphene_matrix_t *matrix); +CLUTTER_EXPORT void clutter_stage_view_add_redraw_clip (ClutterStageView *view, const cairo_rectangle_int_t *clip); @@ -63,6 +64,7 @@ void clutter_stage_view_transform_rect_to_onscreen (ClutterStageView int dst_height, cairo_rectangle_int_t *dst_rect); +CLUTTER_EXPORT void clutter_stage_view_schedule_update (ClutterStageView *view); void clutter_stage_view_notify_presented (ClutterStageView *view, diff --git a/src/backends/meta-monitor-manager-private.h b/src/backends/meta-monitor-manager-private.h index fd30ebb58..61bfead0f 100644 --- a/src/backends/meta-monitor-manager-private.h +++ b/src/backends/meta-monitor-manager-private.h @@ -392,6 +392,7 @@ gboolean meta_monitor_manager_get_max_screen_size (MetaMonitorManager MetaLogicalMonitorLayoutMode meta_monitor_manager_get_default_layout_mode (MetaMonitorManager *manager); +META_EXPORT_TEST MetaVirtualMonitor * meta_monitor_manager_create_virtual_monitor (MetaMonitorManager *manager, const MetaVirtualMonitorInfo *info, GError **error); diff --git a/src/backends/meta-stage-private.h b/src/backends/meta-stage-private.h index 03214218f..07534f6e3 100644 --- a/src/backends/meta-stage-private.h +++ b/src/backends/meta-stage-private.h @@ -21,6 +21,7 @@ #define META_STAGE_PRIVATE_H #include "backends/meta-cursor.h" +#include "core/util-private.h" #include "meta/boxes.h" #include "meta/meta-stage.h" #include "meta/types.h" @@ -62,12 +63,14 @@ gboolean meta_overlay_is_visible (MetaOverlay *overlay); void meta_stage_set_active (MetaStage *stage, gboolean is_active); +META_EXPORT_TEST MetaStageWatch * meta_stage_watch_view (MetaStage *stage, ClutterStageView *view, MetaStageWatchPhase watch_mode, MetaStageWatchFunc callback, gpointer user_data); +META_EXPORT_TEST void meta_stage_remove_watch (MetaStage *stage, MetaStageWatch *watch); diff --git a/src/backends/meta-virtual-monitor.h b/src/backends/meta-virtual-monitor.h index f2d102f00..d1fd43356 100644 --- a/src/backends/meta-virtual-monitor.h +++ b/src/backends/meta-virtual-monitor.h @@ -47,6 +47,7 @@ struct _MetaVirtualMonitorClass GObjectClass parent_class; }; +META_EXPORT_TEST MetaVirtualMonitorInfo * meta_virtual_monitor_info_new (int width, int height, float refresh_rate, @@ -54,6 +55,7 @@ MetaVirtualMonitorInfo * meta_virtual_monitor_info_new (int width, const char *product, const char *serial); +META_EXPORT_TEST void meta_virtual_monitor_info_free (MetaVirtualMonitorInfo *info); MetaCrtc * meta_virtual_monitor_get_crtc (MetaVirtualMonitor *virtual_monitor); diff --git a/src/tests/meson.build b/src/tests/meson.build index fbdca617c..816ae9810 100644 --- a/src/tests/meson.build +++ b/src/tests/meson.build @@ -157,6 +157,11 @@ anonymous_file_test = executable('anonymous-file-tests', install_dir: mutter_installed_tests_libexecdir, ) +ref_test_sources = [ + 'meta-ref-test.c', + 'meta-ref-test.h', +] + if have_native_tests native_headless_tests = executable('mutter-native-headless-tests', sources: [ @@ -170,6 +175,20 @@ if have_native_tests install: have_installed_tests, install_dir: mutter_installed_tests_libexecdir, ) + + ref_test_sanity = executable('mutter-ref-test-sanity', + sources: [ + 'ref-test-sanity.c', + 'test-utils.c', + 'test-utils.h', + ref_test_sources, + ], + include_directories: tests_includepath, + c_args: tests_c_args, + dependencies: [tests_deps], + install: have_installed_tests, + install_dir: mutter_installed_tests_libexecdir, + ) endif stacking_tests = [ @@ -246,4 +265,11 @@ if have_native_tests is_parallel: false, timeout: 60, ) + + test('ref-test-sanity', ref_test_sanity, + suite: ['core', 'mutter/ref-test/sanity'], + env: test_env, + is_parallel: false, + timeout: 60, + ) endif diff --git a/src/tests/meta-ref-test.c b/src/tests/meta-ref-test.c new file mode 100644 index 000000000..84ac8876d --- /dev/null +++ b/src/tests/meta-ref-test.c @@ -0,0 +1,610 @@ +/* + * Copyright (C) 2021 Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +/* + * The image difference code is originally a reformatted and simplified + * copy of weston-test-client-helper.c from the weston repository, with + * the following copyright and license note: + * + * Copyright © 2012 Intel Corporation + * Copyright © 2015 Samsung Electronics Co., Ltd + * Copyright 2016, 2017 Collabora, Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * To update or initialize reference images for tests, set the + * MUTTER_REF_TEST_UPDATE environment variable. + * + * The MUTTER_REF_TEST_UPDATE is interpreted as a comma seperated list of + * regular expressions. If the test path matches any of the regular + * expressions, the test reference image will be updated, unless the + * existing reference image is pixel identical to the newly created one. + * + * Updating test reference images also requires using a software OpenGL + * renderer, which can be achieved using LIBGL_ALWAYS_SOFTWARE=1 + * + * For example, for the test case '/path/to/test/case', run the test + * inside + * + * ``` + * env LIBGL_ALWAYS_SOFTWARE=1 MUTTER_REF_TEST_UPDATE='/path/to/test/case` + * ``` + * + */ + +#include "config.h" + +#include "tests/meta-ref-test.h" + +#include +#include + +#include "backends/meta-backend-private.h" +#include "backends/meta-stage-private.h" +#include "clutter/clutter/clutter-stage-view-private.h" + +typedef struct _Range +{ + int a; + int b; +} Range; + +typedef struct _ImageIterator +{ + uint8_t *data; + int stride; +} ImageIterator; + +typedef struct _PixelDiffStat +{ + /* Pixel diff stat channel */ + struct { + int min_diff; + int max_diff; + } ch[4]; +} PixelDiffStat; + +/** + * range_get: + * @range: Range to validate or NULL. + * + * Validate and get range. + * + * Returns the given range, or {0, 0} for NULL. + * + * Will abort if range is invalid, that is a > b. + */ +static Range +range_get (const Range *range) +{ + if (!range) + return (Range) { 0, 0 }; + + g_assert_cmpint (range->a, <=, range->b); + return *range; +} + +static void +image_iterator_init (ImageIterator *it, + cairo_surface_t *image) +{ + it->stride = cairo_image_surface_get_stride (image); + it->data = cairo_image_surface_get_data (image); + + g_assert_cmpint (cairo_image_surface_get_format (image), ==, + CAIRO_FORMAT_ARGB32); +} + +static uint32_t * +image_iterator_get_row (ImageIterator *it, + int y) +{ + return (uint32_t *) (it->data + y * it->stride); +} + +static gboolean +fuzzy_match_pixels (uint32_t pix_a, + uint32_t pix_b, + const Range *fuzz, + PixelDiffStat *diff_stat) +{ + gboolean ret = TRUE; + int shift; + int i; + + for (shift = 0, i = 0; i < 4; shift += 8, i++) + { + int val_a = (pix_a >> shift) & 0xffu; + int val_b = (pix_b >> shift) & 0xffu; + int d = val_b - val_a; + + if (diff_stat) + { + diff_stat->ch[i].min_diff = MIN (diff_stat->ch[i].min_diff, d); + diff_stat->ch[i].max_diff = MAX (diff_stat->ch[i].max_diff, d); + } + + if (d < fuzz->a || d > fuzz->b) + ret = FALSE; + } + + return ret; +} + +static gboolean +compare_images (cairo_surface_t *ref_image, + cairo_surface_t *result_image, + const Range *precision, + PixelDiffStat *diff_stat) +{ + Range fuzz = range_get (precision); + ImageIterator it_ref; + ImageIterator it_result; + int x, y; + uint32_t *pix_ref; + uint32_t *pix_result; + + g_assert_cmpint (cairo_image_surface_get_width (ref_image), ==, + cairo_image_surface_get_width (result_image)); + g_assert_cmpint (cairo_image_surface_get_height (ref_image), ==, + cairo_image_surface_get_height (result_image)); + + image_iterator_init (&it_ref, ref_image); + image_iterator_init (&it_result, result_image); + + for (y = 0; y < cairo_image_surface_get_height (ref_image); y++) + { + pix_ref = image_iterator_get_row (&it_ref, y); + pix_result = image_iterator_get_row (&it_result, y); + + for (x = 0; x < cairo_image_surface_get_width (ref_image); x++) + { + if (!fuzzy_match_pixels (*pix_ref, *pix_result, + &fuzz, diff_stat)) + return FALSE; + + pix_ref++; + pix_result++; + } + } + + return TRUE; +} + +static void +assert_software_rendered (void) +{ + MetaBackend *backend = meta_get_backend (); + + g_assert_false (meta_backend_is_rendering_hardware_accelerated (backend)); +} + +static void +capture_view_into (ClutterStageView *view, + MetaRectangle *rect, + uint8_t *buffer, + int stride) +{ + CoglFramebuffer *framebuffer; + ClutterBackend *backend; + CoglContext *context; + CoglBitmap *bitmap; + cairo_rectangle_int_t view_layout; + float view_scale; + float texture_width; + float texture_height; + int x, y; + + framebuffer = clutter_stage_view_get_framebuffer (view); + + view_scale = clutter_stage_view_get_scale (view); + texture_width = roundf (rect->width * view_scale); + texture_height = roundf (rect->height * view_scale); + + backend = clutter_get_default_backend (); + context = clutter_backend_get_cogl_context (backend); + bitmap = cogl_bitmap_new_for_data (context, + texture_width, texture_height, + CLUTTER_CAIRO_FORMAT_ARGB32, + stride, + buffer); + + clutter_stage_view_get_layout (view, &view_layout); + + x = roundf ((rect->x - view_layout.x) * view_scale); + y = roundf ((rect->y - view_layout.y) * view_scale); + cogl_framebuffer_read_pixels_into_bitmap (framebuffer, + x, y, + COGL_READ_PIXELS_COLOR_BUFFER, + bitmap); + + cogl_object_unref (bitmap); +} + +typedef struct +{ + MetaStageWatch *watch; + GMainLoop *loop; + + cairo_surface_t *out_image; +} CaptureViewData; + +static void +on_after_paint (MetaStage *stage, + ClutterStageView *view, + ClutterPaintContext *paint_context, + gpointer user_data) +{ + CaptureViewData *data = user_data; + MetaRectangle rect; + float view_scale; + int texture_width, texture_height; + cairo_surface_t *image; + uint8_t *buffer; + int stride; + + meta_stage_remove_watch (stage, data->watch); + data->watch = NULL; + + clutter_stage_view_get_layout (view, &rect); + view_scale = clutter_stage_view_get_scale (view); + texture_width = roundf (rect.width * view_scale); + texture_height = roundf (rect.height * view_scale); + image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, + texture_width, texture_height); + cairo_surface_set_device_scale (image, view_scale, view_scale); + + buffer = cairo_image_surface_get_data (image); + stride = cairo_image_surface_get_stride (image); + + capture_view_into (view, &rect, buffer, stride); + + data->out_image = image; + + cairo_surface_mark_dirty (data->out_image); + + g_main_loop_quit (data->loop); +} + +static cairo_surface_t * +capture_view (ClutterStageView *view) +{ + MetaBackend *backend = meta_get_backend (); + MetaStage *stage = META_STAGE (meta_backend_get_stage (backend)); + CaptureViewData data = { 0 }; + + data.loop = g_main_loop_new (NULL, FALSE); + data.watch = meta_stage_watch_view (stage, view, + META_STAGE_WATCH_AFTER_PAINT, + on_after_paint, + &data); + clutter_stage_view_add_redraw_clip (view, NULL); + clutter_stage_view_schedule_update (view); + + g_main_loop_run (data.loop); + g_main_loop_unref (data.loop); + + g_assert_null (data.watch); + g_assert_nonnull (data.out_image); + + return data.out_image; +} + +static void +depathify (char *path) +{ + int len = strlen (path); + int i; + + for (i = 0; i < len; i++) + { + if (path[i] == '/') + path[i] = '_'; + } +} + +static void +ensure_expected_format (cairo_surface_t **ref_image) +{ + int width, height; + cairo_surface_t *target; + cairo_t *cr; + + if (cairo_image_surface_get_format (*ref_image) == + CAIRO_FORMAT_ARGB32) + return; + + width = cairo_image_surface_get_width (*ref_image); + height = cairo_image_surface_get_height (*ref_image); + target = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); + + cr = cairo_create (target); + cairo_set_source_surface (cr, *ref_image, 0.0, 0.0); + cairo_paint (cr); + cairo_destroy (cr); + + cairo_surface_destroy (*ref_image); + *ref_image = target; +} + +/** + * Tint a color: + * @src Source pixel as x8r8g8b8. + * @add The tint as x8r8g8b8, x8 must be zero; r8, g8 and b8 must be + * no greater than 0xc0 to avoid overflow to another channel. + * Returns: The tinted pixel color as x8r8g8b8, x8 guaranteed to be 0xff. + * + * The source pixel RGB values are divided by 4, and then the tint is added. + * To achieve colors outside of the range of src, a tint color channel must be + * at least 0x40. (0xff / 4 = 0x3f, 0xff - 0x3f = 0xc0) + */ +static uint32_t +tint (uint32_t src, + uint32_t add) +{ + uint32_t v; + + v = ((src & 0xfcfcfcfc) >> 2) | 0xff000000; + + return v + add; +} + +static cairo_surface_t * +visualize_difference (cairo_surface_t *ref_image, + cairo_surface_t *result_image, + const Range *precision) +{ + Range fuzz = range_get (precision); + int width, height; + cairo_surface_t *diff_image; + cairo_t *cr; + ImageIterator it_ref; + ImageIterator it_result; + ImageIterator it_diff; + int y; + + width = cairo_image_surface_get_width (ref_image); + height = cairo_image_surface_get_height (ref_image); + + diff_image = cairo_surface_create_similar_image (ref_image, + CAIRO_FORMAT_ARGB32, + width, + height); + cr = cairo_create (diff_image); + cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 1.0); + cairo_paint (cr); + cairo_set_source_surface (cr, ref_image, 0.0, 0.0); + cairo_set_operator (cr, CAIRO_OPERATOR_HSL_LUMINOSITY); + cairo_paint (cr); + cairo_destroy (cr); + + image_iterator_init (&it_ref, ref_image); + image_iterator_init (&it_result, result_image); + image_iterator_init (&it_diff, diff_image); + + for (y = 0; y < cairo_image_surface_get_height (ref_image); y++) + { + uint32_t *ref_pixel; + uint32_t *result_pixel; + uint32_t *diff_pixel; + int x; + + ref_pixel = image_iterator_get_row (&it_ref, y); + result_pixel = image_iterator_get_row (&it_result, y); + diff_pixel = image_iterator_get_row (&it_diff, y); + + for (x = 0; x < cairo_image_surface_get_width (ref_image); x++) + { + if (fuzzy_match_pixels (*ref_pixel, *result_pixel, + &fuzz, NULL)) + *diff_pixel = tint (*diff_pixel, 0x00008000); /* green */ + else + *diff_pixel = tint (*diff_pixel, 0x00c00000); /* red */ + + ref_pixel++; + result_pixel++; + diff_pixel++; + } + } + + return diff_image; +} + +void +meta_ref_test_verify_view (ClutterStageView *view, + const char *test_name_unescaped, + int test_seq_no, + MetaReftestFlag flags) +{ + cairo_surface_t *view_image; + const char *dist_dir; + g_autofree char *test_name = NULL; + g_autofree char *ref_image_path = NULL; + cairo_surface_t *ref_image; + cairo_status_t ref_status; + + if (flags & META_REFTEST_FLAG_UPDATE_REF) + assert_software_rendered (); + + view_image = capture_view (view); + + test_name = g_strdup (test_name_unescaped + 1); + depathify (test_name); + + dist_dir = g_test_get_dir (G_TEST_DIST); + ref_image_path = g_strdup_printf ("%s/tests/ref-tests/%s_%d.ref.png", + dist_dir, + test_name, test_seq_no); + + ref_image = cairo_image_surface_create_from_png (ref_image_path); + g_assert_nonnull (ref_image); + ref_status = cairo_surface_status (ref_image); + + if (flags & META_REFTEST_FLAG_UPDATE_REF) + { + g_assert (ref_status == CAIRO_STATUS_FILE_NOT_FOUND || + ref_status == CAIRO_STATUS_SUCCESS); + + if (ref_status == CAIRO_STATUS_SUCCESS) + ensure_expected_format (&ref_image); + + if (ref_status == CAIRO_STATUS_SUCCESS && + cairo_image_surface_get_width (ref_image) == + cairo_image_surface_get_width (view_image) && + cairo_image_surface_get_height (ref_image) == + cairo_image_surface_get_height (view_image) && + compare_images (ref_image, view_image, NULL, NULL)) + { + g_message ("Not updating '%s', it didn't change.", ref_image_path); + } + else + { + g_message ("Updating '%s'.", ref_image_path); + g_assert_cmpint (cairo_surface_write_to_png (view_image, ref_image_path), + ==, + CAIRO_STATUS_SUCCESS); + } + } + else + { + const Range gl_fuzz = { -3, 4 }; + PixelDiffStat diff_stat = {}; + + g_assert_cmpint (ref_status, ==, CAIRO_STATUS_SUCCESS); + ensure_expected_format (&ref_image); + + if (!compare_images (ref_image, view_image, &gl_fuzz, + &diff_stat)) + { + cairo_surface_t *diff_image; + const char *build_dir; + g_autofree char *ref_image_copy_path = NULL; + g_autofree char *result_image_path = NULL; + g_autofree char *diff_image_path = NULL; + + diff_image = visualize_difference (ref_image, view_image, + &gl_fuzz); + + build_dir = g_test_get_dir (G_TEST_BUILT); + ref_image_copy_path = + g_strdup_printf ("%s/meson-logs/tests/ref-tests/%s_%d.ref.png", + build_dir, + test_name, test_seq_no); + result_image_path = + g_strdup_printf ("%s/meson-logs/tests/ref-tests/%s_%d.result.png", + build_dir, + test_name, test_seq_no); + diff_image_path = + g_strdup_printf ("%s/meson-logs/tests/ref-tests/%s_%d.diff.png", + build_dir, + test_name, test_seq_no); + + g_mkdir_with_parents (g_path_get_dirname (ref_image_copy_path), + 0755); + + g_assert_cmpint (cairo_surface_write_to_png (ref_image, + ref_image_copy_path), + ==, + CAIRO_STATUS_SUCCESS); + g_assert_cmpint (cairo_surface_write_to_png (view_image, + result_image_path), + ==, + CAIRO_STATUS_SUCCESS); + g_assert_cmpint (cairo_surface_write_to_png (diff_image, + diff_image_path), + ==, + CAIRO_STATUS_SUCCESS); + + g_critical ("Pixel difference exceeds limits " + "(min: [%d, %d, %d, %d], " + "max: [%d, %d, %d, %d])\n" + "See %s, %s, and %s for details.", + diff_stat.ch[0].min_diff, + diff_stat.ch[1].min_diff, + diff_stat.ch[2].min_diff, + diff_stat.ch[3].min_diff, + diff_stat.ch[0].max_diff, + diff_stat.ch[1].max_diff, + diff_stat.ch[2].max_diff, + diff_stat.ch[3].max_diff, + ref_image_copy_path, + result_image_path, + diff_image_path); + } + } + + cairo_surface_destroy (view_image); + cairo_surface_destroy (ref_image); +} + +MetaReftestFlag +meta_ref_test_determine_ref_test_flag (void) +{ + const char *update_tests; + char **update_test_rules; + int n_update_test_rules; + MetaReftestFlag flags; + int i; + + update_tests = g_getenv ("MUTTER_REF_TEST_UPDATE"); + if (!update_tests) + return META_REFTEST_FLAG_NONE; + + if (strcmp (update_tests, "all") == 0) + return META_REFTEST_FLAG_UPDATE_REF; + + update_test_rules = g_strsplit (update_tests, ",", -1); + n_update_test_rules = g_strv_length (update_test_rules); + g_assert_cmpint (n_update_test_rules, >, 0); + + flags = META_REFTEST_FLAG_NONE; + for (i = 0; i < n_update_test_rules; i++) + { + char *rule = update_test_rules[i]; + + if (g_regex_match_simple (rule, g_test_get_path (), 0, 0)) + { + flags |= META_REFTEST_FLAG_UPDATE_REF; + break; + } + } + + g_strfreev (update_test_rules); + + return flags; +} diff --git a/src/tests/meta-ref-test.h b/src/tests/meta-ref-test.h new file mode 100644 index 000000000..7a71e388f --- /dev/null +++ b/src/tests/meta-ref-test.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#ifndef META_REF_TEST_H +#define META_REF_TEST_H + +#include + +#include "clutter/clutter/clutter.h" +#include "meta/boxes.h" + +typedef enum _MetaReftestFlag +{ + META_REFTEST_FLAG_NONE = 0, + META_REFTEST_FLAG_UPDATE_REF = 1 << 0, +} MetaReftestFlag; + +void meta_ref_test_verify_view (ClutterStageView *view, + const char *test_name, + int test_seq_no, + MetaReftestFlag flags); + +MetaReftestFlag meta_ref_test_determine_ref_test_flag (void); + +#endif /* META_REF_TEST_H */ diff --git a/src/tests/ref-test-sanity.c b/src/tests/ref-test-sanity.c new file mode 100644 index 000000000..91710feeb --- /dev/null +++ b/src/tests/ref-test-sanity.c @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2021 Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + */ + +#include "config.h" + +#include "backends/meta-virtual-monitor.h" +#include "backends/native/meta-renderer-native.h" +#include "compositor/meta-plugin-manager.h" +#include "core/main-private.h" +#include "meta/main.h" +#include "tests/meta-ref-test.h" +#include "tests/test-utils.h" + +static MetaVirtualMonitor *virtual_monitor; + +static void +setup_test_environment (void) +{ + MetaBackend *backend = meta_get_backend (); + MetaSettings *settings = meta_backend_get_settings (backend); + MetaMonitorManager *monitor_manager = + meta_backend_get_monitor_manager (backend); + MetaRenderer *renderer = meta_backend_get_renderer (backend); + g_autoptr (MetaVirtualMonitorInfo) monitor_info = NULL; + GError *error = NULL; + GList *views; + + meta_settings_override_experimental_features (settings); + meta_settings_enable_experimental_feature ( + settings, + META_EXPERIMENTAL_FEATURE_SCALE_MONITOR_FRAMEBUFFER); + + monitor_info = meta_virtual_monitor_info_new (100, 100, 60.0, + "MetaTestVendor", + "MetaVirtualMonitor", + "0x1234"); + virtual_monitor = meta_monitor_manager_create_virtual_monitor (monitor_manager, + monitor_info, + &error); + if (!virtual_monitor) + g_error ("Failed to create virtual monitor: %s", error->message); + + meta_monitor_manager_reload (monitor_manager); + + views = meta_renderer_get_views (renderer); + g_assert_cmpint (g_list_length (views), ==, 1); +} + +static void +tear_down_test_environment (void) +{ + MetaBackend *backend = meta_get_backend (); + MetaMonitorManager *monitor_manager = + meta_backend_get_monitor_manager (backend); + + g_object_unref (virtual_monitor); + meta_monitor_manager_reload (monitor_manager); +} + +static gboolean +run_tests (gpointer data) +{ + int ret; + + setup_test_environment (); + + ret = g_test_run (); + + tear_down_test_environment (); + + meta_quit (ret != 0); + + return ret; +} + +static ClutterStageView * +get_view (void) +{ + MetaBackend *backend = meta_get_backend (); + MetaRenderer *renderer = meta_backend_get_renderer (backend); + + return CLUTTER_STAGE_VIEW (meta_renderer_get_views (renderer)->data); +} + +static void +meta_test_ref_test_sanity (void) +{ + MetaBackend *backend = meta_get_backend (); + ClutterActor *stage = meta_backend_get_stage (backend); + ClutterActor *actor1; + ClutterActor *actor2; + + meta_ref_test_verify_view (get_view (), + g_test_get_path (), 0, + meta_ref_test_determine_ref_test_flag ()); + + actor1 = clutter_actor_new (); + clutter_actor_set_position (actor1, 10, 10); + clutter_actor_set_size (actor1, 50, 50); + clutter_actor_set_background_color (actor1, CLUTTER_COLOR_Orange); + clutter_actor_add_child (stage, actor1); + + meta_ref_test_verify_view (get_view (), + g_test_get_path (), 1, + meta_ref_test_determine_ref_test_flag ()); + + actor2 = clutter_actor_new (); + clutter_actor_set_position (actor2, 20, 20); + clutter_actor_set_size (actor2, 50, 50); + clutter_actor_set_background_color (actor2, CLUTTER_COLOR_SkyBlue); + clutter_actor_add_child (stage, actor2); + + g_test_expect_message (G_LOG_DOMAIN, + G_LOG_LEVEL_CRITICAL, + "Pixel difference exceeds limits*"); + + meta_ref_test_verify_view (get_view (), + g_test_get_path (), 1, + meta_ref_test_determine_ref_test_flag ()); + + g_test_assert_expected_messages (); + + clutter_actor_destroy (actor2); + clutter_actor_destroy (actor1); +} + +static void +init_ref_test_sanity_tests (void) +{ + g_test_add_func ("/tests/ref-test/sanity", + meta_test_ref_test_sanity); +} + +int +main (int argc, + char **argv) +{ + test_init (&argc, &argv); + init_ref_test_sanity_tests (); + + meta_plugin_manager_load (test_get_plugin_name ()); + + meta_override_compositor_configuration (META_COMPOSITOR_TYPE_WAYLAND, + META_TYPE_BACKEND_NATIVE, + "headless", TRUE, + NULL); + + meta_init (); + meta_register_with_session (); + + g_idle_add (run_tests, NULL); + + return meta_run (); +} diff --git a/src/tests/ref-tests/tests_ref-test_sanity_0.ref.png b/src/tests/ref-tests/tests_ref-test_sanity_0.ref.png new file mode 100644 index 000000000..f434827f8 Binary files /dev/null and b/src/tests/ref-tests/tests_ref-test_sanity_0.ref.png differ diff --git a/src/tests/ref-tests/tests_ref-test_sanity_1.ref.png b/src/tests/ref-tests/tests_ref-test_sanity_1.ref.png new file mode 100644 index 000000000..29567348e Binary files /dev/null and b/src/tests/ref-tests/tests_ref-test_sanity_1.ref.png differ