1
0
Fork 0

tests: Add reference test framework

This adds a test framework that makes it possible to compare the result
of painting a view against a reference image. Test reference as PNG
images are stored in src/tests/ref-tests/.

Reference images needs to be created for testing to be able to succeed.
Adding a test reference image is done using the
`MUTTER_REF_TEST_UPDATE` environment variable. See meta-ref-test.c for
details.

The image comparison code is largely based on the reference image test
framework in weston; see meta-ref-test.c for details.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1698>
This commit is contained in:
Jonas Ådahl 2021-01-26 17:07:09 +01:00 committed by Marge Bot
parent 1818d21da5
commit d7ce6a47f8
10 changed files with 852 additions and 0 deletions

View file

@ -44,6 +44,7 @@ void clutter_stage_view_invalidate_projection (ClutterStageView *view);
void clutter_stage_view_set_projection (ClutterStageView *view, void clutter_stage_view_set_projection (ClutterStageView *view,
const graphene_matrix_t *matrix); const graphene_matrix_t *matrix);
CLUTTER_EXPORT
void clutter_stage_view_add_redraw_clip (ClutterStageView *view, void clutter_stage_view_add_redraw_clip (ClutterStageView *view,
const cairo_rectangle_int_t *clip); const cairo_rectangle_int_t *clip);
@ -63,6 +64,7 @@ void clutter_stage_view_transform_rect_to_onscreen (ClutterStageView
int dst_height, int dst_height,
cairo_rectangle_int_t *dst_rect); cairo_rectangle_int_t *dst_rect);
CLUTTER_EXPORT
void clutter_stage_view_schedule_update (ClutterStageView *view); void clutter_stage_view_schedule_update (ClutterStageView *view);
void clutter_stage_view_notify_presented (ClutterStageView *view, void clutter_stage_view_notify_presented (ClutterStageView *view,

View file

@ -392,6 +392,7 @@ gboolean meta_monitor_manager_get_max_screen_size (MetaMonitorManager
MetaLogicalMonitorLayoutMode MetaLogicalMonitorLayoutMode
meta_monitor_manager_get_default_layout_mode (MetaMonitorManager *manager); meta_monitor_manager_get_default_layout_mode (MetaMonitorManager *manager);
META_EXPORT_TEST
MetaVirtualMonitor * meta_monitor_manager_create_virtual_monitor (MetaMonitorManager *manager, MetaVirtualMonitor * meta_monitor_manager_create_virtual_monitor (MetaMonitorManager *manager,
const MetaVirtualMonitorInfo *info, const MetaVirtualMonitorInfo *info,
GError **error); GError **error);

View file

@ -21,6 +21,7 @@
#define META_STAGE_PRIVATE_H #define META_STAGE_PRIVATE_H
#include "backends/meta-cursor.h" #include "backends/meta-cursor.h"
#include "core/util-private.h"
#include "meta/boxes.h" #include "meta/boxes.h"
#include "meta/meta-stage.h" #include "meta/meta-stage.h"
#include "meta/types.h" #include "meta/types.h"
@ -62,12 +63,14 @@ gboolean meta_overlay_is_visible (MetaOverlay *overlay);
void meta_stage_set_active (MetaStage *stage, void meta_stage_set_active (MetaStage *stage,
gboolean is_active); gboolean is_active);
META_EXPORT_TEST
MetaStageWatch * meta_stage_watch_view (MetaStage *stage, MetaStageWatch * meta_stage_watch_view (MetaStage *stage,
ClutterStageView *view, ClutterStageView *view,
MetaStageWatchPhase watch_mode, MetaStageWatchPhase watch_mode,
MetaStageWatchFunc callback, MetaStageWatchFunc callback,
gpointer user_data); gpointer user_data);
META_EXPORT_TEST
void meta_stage_remove_watch (MetaStage *stage, void meta_stage_remove_watch (MetaStage *stage,
MetaStageWatch *watch); MetaStageWatch *watch);

View file

@ -47,6 +47,7 @@ struct _MetaVirtualMonitorClass
GObjectClass parent_class; GObjectClass parent_class;
}; };
META_EXPORT_TEST
MetaVirtualMonitorInfo * meta_virtual_monitor_info_new (int width, MetaVirtualMonitorInfo * meta_virtual_monitor_info_new (int width,
int height, int height,
float refresh_rate, float refresh_rate,
@ -54,6 +55,7 @@ MetaVirtualMonitorInfo * meta_virtual_monitor_info_new (int width,
const char *product, const char *product,
const char *serial); const char *serial);
META_EXPORT_TEST
void meta_virtual_monitor_info_free (MetaVirtualMonitorInfo *info); void meta_virtual_monitor_info_free (MetaVirtualMonitorInfo *info);
MetaCrtc * meta_virtual_monitor_get_crtc (MetaVirtualMonitor *virtual_monitor); MetaCrtc * meta_virtual_monitor_get_crtc (MetaVirtualMonitor *virtual_monitor);

View file

@ -157,6 +157,11 @@ anonymous_file_test = executable('anonymous-file-tests',
install_dir: mutter_installed_tests_libexecdir, install_dir: mutter_installed_tests_libexecdir,
) )
ref_test_sources = [
'meta-ref-test.c',
'meta-ref-test.h',
]
if have_native_tests if have_native_tests
native_headless_tests = executable('mutter-native-headless-tests', native_headless_tests = executable('mutter-native-headless-tests',
sources: [ sources: [
@ -170,6 +175,20 @@ if have_native_tests
install: have_installed_tests, install: have_installed_tests,
install_dir: mutter_installed_tests_libexecdir, 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 endif
stacking_tests = [ stacking_tests = [
@ -246,4 +265,11 @@ if have_native_tests
is_parallel: false, is_parallel: false,
timeout: 60, timeout: 60,
) )
test('ref-test-sanity', ref_test_sanity,
suite: ['core', 'mutter/ref-test/sanity'],
env: test_env,
is_parallel: false,
timeout: 60,
)
endif endif

610
src/tests/meta-ref-test.c Normal file
View file

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

39
src/tests/meta-ref-test.h Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef META_REF_TEST_H
#define META_REF_TEST_H
#include <glib.h>
#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 */

169
src/tests/ref-test-sanity.c Normal file
View file

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB