mirror of
https://github.com/brl/mutter.git
synced 2024-11-24 17:10:40 -05:00
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:
parent
1818d21da5
commit
d7ce6a47f8
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
610
src/tests/meta-ref-test.c
Normal file
610
src/tests/meta-ref-test.c
Normal 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
39
src/tests/meta-ref-test.h
Normal 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
169
src/tests/ref-test-sanity.c
Normal 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 ();
|
||||
}
|
BIN
src/tests/ref-tests/tests_ref-test_sanity_0.ref.png
Normal file
BIN
src/tests/ref-tests/tests_ref-test_sanity_0.ref.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.6 KiB |
BIN
src/tests/ref-tests/tests_ref-test_sanity_1.ref.png
Normal file
BIN
src/tests/ref-tests/tests_ref-test_sanity_1.ref.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.3 KiB |
Loading…
Reference in New Issue
Block a user