diff --git a/frontend/main.c b/frontend/main.c index d396a2df0..1c7f363b4 100644 --- a/frontend/main.c +++ b/frontend/main.c @@ -1944,7 +1944,7 @@ wet_parse_auto_profile(struct weston_output *output, return NULL; } -static struct weston_color_profile * +WESTON_EXPORT_FOR_TESTS struct weston_color_profile * wet_create_output_color_profile(struct weston_output *output, struct weston_config *wc, const char *prof_name) diff --git a/frontend/weston-private.h b/frontend/weston-private.h index e0b4bffe4..f82d66b79 100644 --- a/frontend/weston-private.h +++ b/frontend/weston-private.h @@ -53,3 +53,8 @@ wet_output_set_colorimetry_mode(struct weston_output *output, typedef void (*wet_head_additional_setup)(struct weston_head *head, struct weston_head *head_to_mirror); + +struct weston_color_profile * +wet_create_output_color_profile(struct weston_output *output, + struct weston_config *wc, + const char *prof_name); diff --git a/tests/color-output-parsing-test.c b/tests/color-output-parsing-test.c new file mode 100644 index 000000000..85da03c26 --- /dev/null +++ b/tests/color-output-parsing-test.c @@ -0,0 +1,654 @@ +/* + * Copyright 2025 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. + */ + +#include "config.h" + +#include + +#include "weston-test-client-helper.h" +#include "weston-test-assert.h" + +#include "weston-private.h" +#include "backend.h" +#include "color.h" +#include "color-properties.h" +#include "id-number-allocator.h" +#include "shared/string-helpers.h" +#include "shared/xalloc.h" + +struct expected_params { + struct weston_color_profile_params template; + + /* Cannot statically initialize these in the template: */ + enum weston_transfer_function tf; + enum weston_color_primaries named_prim; + bool use_named_prim; +}; + +struct config_testcase { + enum weston_eotf_mode eotf_mode; + enum weston_colorimetry_mode colorimetry_mode; + const char *profile_name; + const char *profile_string; + + const struct expected_params expected; +}; + +#define NO_VALUE -1.f +#define D65 { 0.3127f, 0.3290f } +#define prim_bt709 ((struct weston_color_gamut){ \ + .primary = { \ + { 0.640f, 0.330f }, \ + { 0.300f, 0.600f }, \ + { 0.150f, 0.060f }, \ + }, \ + .white_point = D65, \ +}) +#define prim_bt2020 ((struct weston_color_gamut){ \ + .primary = { \ + { 0.708f, 0.292f }, \ + { 0.170f, 0.797f }, \ + { 0.131f, 0.046f }, \ + }, \ + .white_point = D65, \ +}) +#define prim_display_p3 ((struct weston_color_gamut){ \ + .primary = { \ + { 0.680f, 0.320f }, \ + { 0.265f, 0.690f }, \ + { 0.150f, 0.060f }, \ + }, \ + .white_point = D65, \ +}) +#define prim_hp_5dq99aa ((struct weston_color_gamut){ \ + .primary = { \ + { 0.6650f, 0.3261f }, \ + { 0.2890f, 0.6435f }, \ + { 0.1494f, 0.0507f }, \ + }, \ + .white_point = { 0.3134f, 0.3291f }, \ +}) + +static const struct config_testcase config_cases[] = { + { + WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_DEFAULT, + "auto:", + "", + { + .template = { + .primaries = prim_bt709, + .target_primaries = prim_bt709, + .min_luminance = 0.2f, + .max_luminance = 80.f, + .reference_white_luminance = 80.f, + .target_min_luminance = 0.2f, + .target_max_luminance = 80.f, + .maxCLL = NO_VALUE, + .maxFALL = NO_VALUE, + }, + .tf = WESTON_TF_GAMMA22, + .named_prim = WESTON_PRIMARIES_CICP_SRGB, + .use_named_prim = true, + }, + }, + { + WESTON_EOTF_MODE_TRADITIONAL_HDR, WESTON_COLORIMETRY_MODE_DEFAULT, + "auto:", + "", + { + .template = { + .primaries = prim_bt709, + .target_primaries = prim_bt709, + .min_luminance = 0.2f, + .max_luminance = 80.f, + .reference_white_luminance = 80.f, + .target_min_luminance = 0.2f, + .target_max_luminance = 80.f, + .maxCLL = NO_VALUE, + .maxFALL = NO_VALUE, + }, + .tf = WESTON_TF_GAMMA22, + .named_prim = WESTON_PRIMARIES_CICP_SRGB, + .use_named_prim = true, + }, + }, + { + WESTON_EOTF_MODE_ST2084, WESTON_COLORIMETRY_MODE_DEFAULT, + "auto:", + "", + { + .template = { + .primaries = prim_bt709, + .target_primaries = prim_bt709, + .min_luminance = 0.005f, + .max_luminance = 10000.f, + .reference_white_luminance = 203.f, + .target_min_luminance = 0.005f, + .target_max_luminance = 10000.f, + .maxCLL = NO_VALUE, + .maxFALL = NO_VALUE, + }, + .tf = WESTON_TF_ST2084_PQ, + .named_prim = WESTON_PRIMARIES_CICP_SRGB, + .use_named_prim = true, + }, + }, + { + WESTON_EOTF_MODE_HLG, WESTON_COLORIMETRY_MODE_DEFAULT, + "auto:", + "", + { + .template = { + .primaries = prim_bt709, + .target_primaries = prim_bt709, + .min_luminance = 0.005f, + .max_luminance = 1000.f, + .reference_white_luminance = 203.f, + .target_min_luminance = 0.005f, + .target_max_luminance = 1000.f, + .maxCLL = NO_VALUE, + .maxFALL = NO_VALUE, + }, + .tf = WESTON_TF_HLG, + .named_prim = WESTON_PRIMARIES_CICP_SRGB, + .use_named_prim = true, + }, + }, + { + WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_BT2020_RGB, + "auto:", + "", + { + .template = { + .primaries = prim_bt2020, + .target_primaries = prim_bt2020, + .min_luminance = 0.010f, + .max_luminance = 100.f, + .reference_white_luminance = 100.f, + .target_min_luminance = 0.010f, + .target_max_luminance = 100.f, + .maxCLL = NO_VALUE, + .maxFALL = NO_VALUE, + }, + .tf = WESTON_TF_BT1886, + .named_prim = WESTON_PRIMARIES_CICP_BT2020, + .use_named_prim = true, + }, + }, + { + WESTON_EOTF_MODE_ST2084, WESTON_COLORIMETRY_MODE_BT2020_RGB, + "auto:", + "", + { + .template = { + .primaries = prim_bt2020, + .target_primaries = prim_bt2020, + .min_luminance = 0.005f, + .max_luminance = 10000.f, + .reference_white_luminance = 203.f, + .target_min_luminance = 0.005f, + .target_max_luminance = 10000.f, + .maxCLL = NO_VALUE, + .maxFALL = NO_VALUE, + }, + .tf = WESTON_TF_ST2084_PQ, + .named_prim = WESTON_PRIMARIES_CICP_BT2020, + .use_named_prim = true, + }, + }, + { + WESTON_EOTF_MODE_HLG, WESTON_COLORIMETRY_MODE_BT2020_YCC, + "auto:", + "", + { + .template = { + .primaries = prim_bt2020, + .target_primaries = prim_bt2020, + .min_luminance = 0.005f, + .max_luminance = 1000.f, + .reference_white_luminance = 203.f, + .target_min_luminance = 0.005f, + .target_max_luminance = 1000.f, + .maxCLL = NO_VALUE, + .maxFALL = NO_VALUE, + }, + .tf = WESTON_TF_HLG, + .named_prim = WESTON_PRIMARIES_CICP_BT2020, + .use_named_prim = true, + }, + }, + { + WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_DEFAULT, + "auto:edid-primaries edid-tf edid-dr", + "", + { + .template = { + .primaries = prim_hp_5dq99aa, + .target_primaries = prim_hp_5dq99aa, + .tf.params = { 2.2f, }, + .min_luminance = 0.2f, + .max_luminance = 80.f, + .reference_white_luminance = 80.f, + .target_min_luminance = 0.2f, + .target_max_luminance = 80.f, + .maxCLL = NO_VALUE, + .maxFALL = NO_VALUE, + }, + .tf = WESTON_TF_POWER, + }, + }, + { + WESTON_EOTF_MODE_ST2084, WESTON_COLORIMETRY_MODE_DEFAULT, + "auto:edid-primaries edid-tf edid-dr", + "", + { + .template = { + .primaries = prim_hp_5dq99aa, + .target_primaries = prim_hp_5dq99aa, + .min_luminance = 0.005f, + .max_luminance = 10000.f, + .reference_white_luminance = 203.f, + .target_min_luminance = 0.f, + .target_max_luminance = 603.6657f, + .maxCLL = NO_VALUE, + .maxFALL = 351.2504f, + }, + .tf = WESTON_TF_ST2084_PQ, + }, + }, + { + WESTON_EOTF_MODE_ST2084, WESTON_COLORIMETRY_MODE_BT2020_RGB, + "auto:edid-primaries edid-tf edid-dr", + "", + { + .template = { + .primaries = prim_bt2020, + .target_primaries = prim_bt2020, + .min_luminance = 0.005f, + .max_luminance = 10000.f, + .reference_white_luminance = 203.f, + .target_min_luminance = 0.f, + .target_max_luminance = 603.6657f, + .maxCLL = NO_VALUE, + .maxFALL = 351.2504f, + }, + .tf = WESTON_TF_ST2084_PQ, + .named_prim = WESTON_PRIMARIES_CICP_BT2020, + .use_named_prim = true, + }, + }, + { + WESTON_EOTF_MODE_HLG, WESTON_COLORIMETRY_MODE_P3D65, + "srgb:", + "", + { + .template = { + .primaries = prim_bt709, + .target_primaries = prim_bt709, + .min_luminance = 0.2f, + .max_luminance = 80.f, + .reference_white_luminance = 80.f, + .target_min_luminance = 0.2f, + .target_max_luminance = 80.f, + .maxCLL = NO_VALUE, + .maxFALL = NO_VALUE, + }, + .tf = WESTON_TF_GAMMA22, + .named_prim = WESTON_PRIMARIES_CICP_SRGB, + .use_named_prim = true, + }, + }, + { + WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_DEFAULT, + "mydisp", + "prim_named=display_p3\n" + "target_named=srgb\n" + "tf_named=gamma22\n", + { + .template = { + .primaries = prim_display_p3, + .target_primaries = prim_bt709, + .min_luminance = 0.2f, + .max_luminance = 80.f, + .reference_white_luminance = 80.f, + .target_min_luminance = 0.2f, + .target_max_luminance = 80.f, + .maxCLL = NO_VALUE, + .maxFALL = NO_VALUE, + }, + .tf = WESTON_TF_GAMMA22, + .named_prim = WESTON_PRIMARIES_CICP_DISPLAY_P3, + .use_named_prim = true, + }, + }, + { + WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_DEFAULT, + "mydisp", + "prim_red=1.0 0\n" + "prim_green=0.0 1\n" + "prim_blue=0 0\n" + "prim_white=0.333333 0.333333\n" + "min_lum=0\n" + "ref_lum=150\n" + "max_lum=860\n" + "target_red=0.681 0.319\n" + "target_green=24.3e-2 6.92e-1\n" + "target_blue= 0.155\t0.07\n" + "target_white= \t 0.310 \t 0.316 \t\n" + "target_min_lum=1e-1\n" + "target_max_lum=555.5\n" + "max_fall=213\n" + "max_cll=550\n" + "tf_power=2.35\n", + { + .template = { + .primaries = { + .primary = { + { 1.f, 0.f }, + { 0.f, 1.f }, + { 0.f, 0.f }, + }, + .white_point = { 1.f / 3, 1.f / 3 }, + }, + .target_primaries = { + .primary = { + { 0.681f, 0.319f }, + { 0.243f, 0.692f }, + { 0.155f, 0.070f }, + }, + .white_point = { 0.310f, 0.316f }, + }, + .tf.params = { 2.35f, }, + .min_luminance = 0.f, + .max_luminance = 860.f, + .reference_white_luminance = 150.f, + .target_min_luminance = 0.1f, + .target_max_luminance = 555.5f, + .maxCLL = 550.f, + .maxFALL = 213.f, + }, + .tf = WESTON_TF_POWER, + }, + }, +}; + +static struct di_info *display_edid; + +static int +logger(const char *fmt, va_list arg) +{ + return vfprintf(stderr, fmt, arg); +} + +static enum test_result_code +fixture_setup(struct weston_test_harness *harness) +{ + enum test_result_code ret; + char *fname; + size_t len; + char *edid_data; + + str_printf(&fname, "%s/hp-5dq99aa-hdmi.edid", reference_path()); + abort_oom_if_null(fname); + + len = read_blob_from_file(fname, &edid_data); + free(fname); + if (!test_assert_u64_gt(len, 0)) + return RESULT_HARD_ERROR; + + display_edid = di_info_parse_edid(edid_data, len); + free(edid_data); + abort_oom_if_null(display_edid); + + ret = weston_test_harness_execute_standalone(harness); + + di_info_destroy(display_edid); + display_edid = NULL; + + return ret; +} +DECLARE_FIXTURE_SETUP(fixture_setup) + +static struct weston_config * +create_config(const struct config_testcase *t) +{ + struct compositor_setup setup; + struct weston_config *wc; + + compositor_setup_defaults(&setup); + weston_ini_setup(&setup, + cfgln("[color-profile]"), + cfgln("name=mydisp"), + cfgln("%s", t->profile_string)); + + wc = weston_config_parse(setup.config_file); + test_assert_ptr_not_null(wc); + free(setup.config_file); + + return wc; +} + +struct mock_color_manager { + struct weston_color_manager base; +}; + +struct mock_color_profile { + struct weston_color_profile base; + struct weston_color_profile_params params; +}; + +static struct mock_color_profile * +to_mock_cprof(struct weston_color_profile *cprof) +{ + return container_of(cprof, struct mock_color_profile, base); +} + +static struct weston_color_profile * +mock_cm_ref_stock_sRGB_color_profile(struct weston_color_manager *mock_cm) +{ + struct mock_color_profile *mock_cprof; + + mock_cprof = xzalloc(sizeof(*mock_cprof)); + + weston_color_profile_init(&mock_cprof->base, mock_cm); + str_printf(&mock_cprof->base.description, "Mock sRGB profile"); + + return &mock_cprof->base; +} + +static bool +mock_cm_get_color_profile_from_params(struct weston_color_manager *mock_cm, + const struct weston_color_profile_params *params, + const char *name_part, + struct weston_color_profile **cprof_out, + char **errmsg) +{ + struct mock_color_profile *mock_cprof; + + mock_cprof = xzalloc(sizeof(*mock_cprof)); + + weston_color_profile_init(&mock_cprof->base, mock_cm); + str_printf(&mock_cprof->base.description, "Mock profile %s", name_part); + mock_cprof->params = *params; + + *cprof_out = &mock_cprof->base; + return true; +} + +static void +mock_cm_destroy_color_profile(struct weston_color_profile *cprof) +{ + struct mock_color_profile *mock_cprof = to_mock_cprof(cprof); + + free(mock_cprof->base.description); + free(mock_cprof); +} + +static bool +test_assert_CIExy_eq(const struct weston_CIExy *ref, + const struct weston_CIExy *tst, + float tolerance, + int indent, + const char *desc) +{ + bool r = true; + + r = test_assert_f32_absdiff_lt(ref->x, tst->x, tolerance) && r; + r = test_assert_f32_absdiff_lt(ref->y, tst->y, tolerance) && r; + + if (!r) + testlog("%*sin %s\n", indent, "", desc); + + return r; +} + +static bool +test_assert_color_gamut_eq(const struct weston_color_gamut *ref, + const struct weston_color_gamut *tst, + float tolerance, + int indent, + const char *desc) +{ + static const char *chan[] = { "red", "green", "blue" }; + bool r = true; + unsigned i; + + for (i = 0; i < ARRAY_LENGTH(tst->primary); i++) { + r = test_assert_CIExy_eq(&ref->primary[i], &tst->primary[i], + tolerance, indent + 2, chan[i]) && r; + } + + r = test_assert_CIExy_eq(&ref->white_point, &tst->white_point, + tolerance, indent + 2, "white point") && r; + + if (!r) + testlog("%*sin %s\n", indent, "", desc); + + return r; +} + +static void +assert_params_equal(const struct weston_color_profile_params *ref, + const struct weston_color_profile_params *tst) +{ + float tol = 0.0001; + int indent = 4; + unsigned i; + + test_assert_color_gamut_eq(&tst->primaries, &ref->primaries, tol, indent, "primaries"); + test_assert_ptr_eq(tst->primaries_info, ref->primaries_info); + + test_assert_ptr_eq(tst->tf.info, ref->tf.info); + for (i = 0; i < ARRAY_LENGTH(tst->tf.params); i++) { + if (!test_assert_f32_absdiff_lt(ref->tf.params[i], tst->tf.params[i], tol)) + testlog("%*sin tf.params[%d]\n", indent, "", i); + } + + test_assert_f32_absdiff_lt(ref->min_luminance, tst->min_luminance, tol); + test_assert_f32_absdiff_lt(ref->max_luminance, tst->max_luminance, tol); + test_assert_f32_absdiff_lt(ref->reference_white_luminance, tst->reference_white_luminance, tol); + + test_assert_color_gamut_eq(&ref->target_primaries, &tst->target_primaries, + tol, indent, "target primaries"); + + test_assert_f32_absdiff_lt(ref->target_min_luminance, tst->target_min_luminance, tol); + test_assert_f32_absdiff_lt(ref->target_max_luminance, tst->target_max_luminance, tol); + test_assert_f32_absdiff_lt(ref->maxCLL, tst->maxCLL, tol); + test_assert_f32_absdiff_lt(ref->maxFALL, tst->maxFALL, tol); +} + +static void +compare_results(struct weston_color_profile *tst, + const struct expected_params *expected) +{ + const struct mock_color_profile *mock_cprof = to_mock_cprof(tst); + struct weston_color_profile_params ref = expected->template; + + ref.tf.info = weston_color_tf_info_from(NULL, expected->tf); + + if (expected->use_named_prim) { + ref.primaries_info = weston_color_primaries_info_from(NULL, expected->named_prim); + } + + assert_params_equal(&ref, &mock_cprof->params); +} + +/* + * Manufacture various weston.ini and check what + * wet_create_output_color_profile() says. Tests for the return value and + * the error messages logged. + */ +TEST_P(parametric_color_profile_parsing, config_cases) +{ + const struct config_testcase *t = data; + struct weston_color_profile *cprof; + struct weston_config *wc; + struct mock_color_manager mock_cm = { + .base.ref_stock_sRGB_color_profile = mock_cm_ref_stock_sRGB_color_profile, + .base.get_color_profile_from_params = mock_cm_get_color_profile_from_params, + .base.destroy_color_profile = mock_cm_destroy_color_profile, + .base.supported_color_features = 0xffffffff, + .base.supported_primaries_named = 0xffffffff, + .base.supported_tf_named = 0xffffffff, + }; + struct weston_compositor mock_compositor = { + .color_manager = &mock_cm.base, + .color_profile_id_generator = weston_idalloc_create(&mock_compositor), + }; + struct weston_output mock_output = {}; + struct weston_head mock_head = {}; + + mock_cm.base.compositor = &mock_compositor; + + wl_list_init(&mock_compositor.plane_list); + + weston_log_set_handler(logger, logger); + + weston_head_init(&mock_head, "mock head"); + weston_head_set_supported_eotf_mask(&mock_head, WESTON_EOTF_MODE_ALL_MASK); + weston_head_set_supported_colorimetry_mask(&mock_head, WESTON_COLORIMETRY_MODE_ALL_MASK); + mock_head.display_info = display_edid; /* from fixture_setup() */ + + weston_output_init(&mock_output, &mock_compositor, "mockoutput"); + weston_output_attach_head(&mock_output, &mock_head); + weston_output_set_eotf_mode(&mock_output, t->eotf_mode); + weston_output_set_colorimetry_mode(&mock_output, t->colorimetry_mode); + + wc = create_config(t); + cprof = wet_create_output_color_profile(&mock_output, wc, t->profile_name); + test_assert_ptr_not_null(cprof); + + compare_results(cprof, &t->expected); + + weston_color_profile_unref(cprof); + + weston_config_destroy(wc); + weston_output_release(&mock_output); + mock_head.display_info = NULL; /* freed in fixture_setup() */ + weston_head_release(&mock_head); + weston_idalloc_destroy(mock_compositor.color_profile_id_generator); + + return RESULT_OK; +} diff --git a/tests/meson.build b/tests/meson.build index d485f5647..50dce9795 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -125,6 +125,13 @@ tests = [ 'name': 'color-metadata-errors', 'dep_objs': dep_libexec_weston, }, + { + 'name': 'color-output-parsing', + 'dep_objs': [ + dep_libexec_weston, + dep_libdisplay_info, + ], + }, { 'name': 'config-parser', }, { 'name': 'constraints', diff --git a/tests/reference/hp-5dq99aa-hdmi.edid b/tests/reference/hp-5dq99aa-hdmi.edid new file mode 100644 index 000000000..c414ad63b Binary files /dev/null and b/tests/reference/hp-5dq99aa-hdmi.edid differ diff --git a/tests/reference/hp-5dq99aa-hdmi.edid.license b/tests/reference/hp-5dq99aa-hdmi.edid.license new file mode 100644 index 000000000..58630b5cc --- /dev/null +++ b/tests/reference/hp-5dq99aa-hdmi.edid.license @@ -0,0 +1,5 @@ +not copyrightable, no license + +Pekka Paalanen captured this EDID from a HP Pavilion 27 Quantum Dot +display in 2022. + diff --git a/tests/weston-test-assert.h b/tests/weston-test-assert.h index 4337b2d3c..f61232232 100644 --- a/tests/weston-test-assert.h +++ b/tests/weston-test-assert.h @@ -157,6 +157,19 @@ test_assert_fail(void *compositor, const char *fmt, ...) #define test_assert_f32_lt(a, b) weston_assert_(NULL, a, b, float, "%.10g", <) #define test_assert_f32_le(a, b) weston_assert_(NULL, a, b, float, "%.10g", <=) +#define test_assert_f32_absdiff_lt(a, b, tol) \ +({ \ + float a_ = (a); \ + float b_ = (b); \ + float tol_ = (tol); \ + float absdiff = fabsf(a_ - b_); \ + bool cond = absdiff < tol_; \ + if (!cond) \ + custom_assert_fail_(NULL, "%s:%u: Assertion %s ≈≈ %s (|%.10g - %.10g| < %.10g) failed!\n", \ + __FILE__, __LINE__, #a, #b, a_, b_, tol_); \ + cond; \ +}) + #define test_assert_f64_eq(a, b) weston_assert_(NULL, a, b, double, "%.10g", ==) #define test_assert_f64_ne(a, b) weston_assert_(NULL, a, b, double, "%.10g", !=) #define test_assert_f64_gt(a, b) weston_assert_(NULL, a, b, double, "%.10g", >) @@ -164,6 +177,19 @@ test_assert_fail(void *compositor, const char *fmt, ...) #define test_assert_f64_lt(a, b) weston_assert_(NULL, a, b, double, "%.10g", <) #define test_assert_f64_le(a, b) weston_assert_(NULL, a, b, double, "%.10g", <=) +#define test_assert_f64_absdiff_lt(a, b, tol) \ +({ \ + double a_ = (a); \ + double b_ = (b); \ + double tol_ = (tol); \ + double absdiff = fabs(a_ - b_); \ + bool cond = absdiff < tol_; \ + if (!cond) \ + custom_assert_fail_(NULL, "%s:%u: Assertion %s ≈≈ %s (|%.10g - %.10g| < %.10g) failed!\n", \ + __FILE__, __LINE__, #a, #b, a_, b_, tol_); \ + cond; \ +}) + /* Various helpers. */ #define test_assert_bit_set(a, bit) weston_assert_bit_set(NULL, a, bit) diff --git a/tests/weston-test-client-helper.c b/tests/weston-test-client-helper.c index 9d7d05733..230a25c3f 100644 --- a/tests/weston-test-client-helper.c +++ b/tests/weston-test-client-helper.c @@ -1251,7 +1251,7 @@ output_path(void) return path; } -static const char* +const char * reference_path(void) { char *path = getenv("WESTON_TEST_REFERENCE_PATH"); @@ -1407,6 +1407,58 @@ fopen_dump_file(const char *suffix) return fp; } +/** + * Read a file into a newly malloc'd memory. + * + * \param fname Full name of the file to read. + * \param data_out Pointer where to store the pointer to the read data. + * \return Length of the data, or 0 on error. + * + * On error, *data_out is not modified. On success, *data_out contains a + * pointer to the data and must be free()'d by the caller. + */ +size_t +read_blob_from_file(const char *fname, char **data_out) +{ + struct wl_array data; + char tmpbuf[64]; + FILE *fp; + size_t len; + void *p; + int fer; + + testlog("%s: %s\n", __func__, fname); + + wl_array_init(&data); + + fp = fopen(fname, "rb"); + if (!test_assert_ptr_not_null(fp)) { + wl_array_release(&data); + return 0; + } + + while (!feof(fp)) { + len = fread(tmpbuf, 1, sizeof tmpbuf, fp); + if (len == 0) + break; + + p = wl_array_add(&data, len); + abort_oom_if_null(p); + memcpy(p, tmpbuf, len); + } + + fer = ferror(fp); + fclose(fp); + + if (fer) { + wl_array_release(&data); + return 0; + } + + *data_out = data.data; + return data.size; +} + struct format_map_entry { cairo_format_t cairo; pixman_format_code_t pixman; diff --git a/tests/weston-test-client-helper.h b/tests/weston-test-client-helper.h index bcb6d5a54..6db0df835 100644 --- a/tests/weston-test-client-helper.h +++ b/tests/weston-test-client-helper.h @@ -267,6 +267,9 @@ void expect_protocol_error(struct client *client, const struct wl_interface *intf, uint32_t code); +const char * +reference_path(void); + char * screenshot_reference_filename(const char *basename, uint32_t seq); @@ -289,6 +292,9 @@ output_filename_for_test_case(const char *suffix, uint32_t seq_number, FILE * fopen_dump_file(const char *suffix); +size_t +read_blob_from_file(const char *fname, char **data_out); + bool check_images_match(pixman_image_t *img_a, pixman_image_t *img_b, const struct rectangle *clip,