diff options
Diffstat (limited to 'src/tests/meta-ref-test.c')
-rw-r--r-- | src/tests/meta-ref-test.c | 610 |
1 files changed, 0 insertions, 610 deletions
diff --git a/src/tests/meta-ref-test.c b/src/tests/meta-ref-test.c deleted file mode 100644 index 84ac8876d..000000000 --- a/src/tests/meta-ref-test.c +++ /dev/null @@ -1,610 +0,0 @@ -/* - * 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; -} |