/* * Copyright 2022 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 #include "weston-test-client-helper.h" #include "weston-test-fixture-compositor.h" #include "weston-test-assert.h" #include "weston-private.h" #include "libweston-internal.h" #include "backend.h" #include "color.h" #include "id-number-allocator.h" #include "shared/string-helpers.h" #include "shared/xalloc.h" struct config_testcase { bool has_characteristics_key; const char *output_characteristics_name; const char *characteristics_name; const char *red_x; const char *green_y; const char *white_y; const char *min_L; int expected_retval; const char *expected_error; }; static const struct config_testcase config_cases[] = { { false, "fred", "fred", "red_x=0.9", "green_y=0.8", "white_y=0.323", "min_L=1e-4", 0, "" }, { true, "fred", "fred", "red_x=0.9", "green_y= 0.8 ", "white_y=0.323", "min_L=1e-4", 0, "" }, { true, "fred", "fred", "red_x=0.9", "green_y= 0.8 ", "white_y=0.323", "", 0, "" }, { true, "notexisting", "fred", "red_x=0.9", "green_y=0.8", "white_y=0.323", "min_L=1e-4", -1, "Config error in weston.ini, output mockoutput: no [color_characteristics] section with 'name=notexisting' found.\n" }, { true, "fr:ed", "fr:ed", "red_x=0.9", "green_y=0.8", "white_y=0.323", "min_L=1e-4", -1, "Config error in weston.ini [color_characteristics] name=fr:ed is a reserved name. Do not use ':' character in the name.\n" }, { true, "fred", "fred", "red_x=-5", "green_y=1.01", "white_y=0.323", "min_L=1e-4", -1, "Config error in weston.ini [color_characteristics] name=fred: red_x value -5.000000 is outside of the range 0.000000 - 1.000000.\n" "Config error in weston.ini [color_characteristics] name=fred: green_y value 1.010000 is outside of the range 0.000000 - 1.000000.\n" }, { true, "fred", "fred", "red_x=haahaa", "green_y=-", "white_y=0.323", "min_L=1e-4", -1, "Config error in weston.ini [color_characteristics] name=fred: failed to parse the value of key red_x.\n" "Config error in weston.ini [color_characteristics] name=fred: failed to parse the value of key green_y.\n" }, { true, "fred", "fred", "", "", "white_y=0.323", "min_L=1e-4", -1, "Config error in weston.ini [color_characteristics] name=fred: group 1 key red_x is missing. You must set either none or all keys of a group.\n" "Config error in weston.ini [color_characteristics] name=fred: group 1 key red_y is set. You must set either none or all keys of a group.\n" "Config error in weston.ini [color_characteristics] name=fred: group 1 key green_x is set. You must set either none or all keys of a group.\n" "Config error in weston.ini [color_characteristics] name=fred: group 1 key green_y is missing. You must set either none or all keys of a group.\n" "Config error in weston.ini [color_characteristics] name=fred: group 1 key blue_x is set. You must set either none or all keys of a group.\n" "Config error in weston.ini [color_characteristics] name=fred: group 1 key blue_y is set. You must set either none or all keys of a group.\n" }, { true, "fred", "fred", "red_x=0.9", "green_y=0.8", "", "min_L=1e-4", -1, "Config error in weston.ini [color_characteristics] name=fred: group 2 key white_x is set. You must set either none or all keys of a group.\n" "Config error in weston.ini [color_characteristics] name=fred: group 2 key white_y is missing. You must set either none or all keys of a group.\n" }, }; static FILE *logfile; static int logger(const char *fmt, va_list arg) { return vfprintf(logfile, fmt, arg); } static int no_logger(const char *fmt, va_list arg) { return 0; } 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("[output]"), cfgln("name=mockoutput"), t->has_characteristics_key ? cfgln("color_characteristics=%s", t->output_characteristics_name) : cfgln(""), cfgln("eotf-mode=st2084"), cfgln("[color_characteristics]"), cfgln("name=%s", t->characteristics_name), cfgln("maxFALL=1000"), cfgln("%s", t->red_x), cfgln("red_y=0.3"), cfgln("blue_x=0.1"), cfgln("blue_y=0.11"), cfgln("green_x=0.1771"), cfgln("%s", t->green_y), cfgln("white_x=0.313"), cfgln("%s", t->white_y), cfgln("%s", t->min_L), cfgln("max_L=65535.0"), cfgln("[core]"), cfgln("color-management=true")); wc = weston_config_parse(setup.config_file); free(setup.config_file); return wc; } struct mock_color_manager { struct weston_color_manager base; struct weston_hdr_metadata_type1 *test_hdr_meta; }; static struct weston_output_color_outcome * mock_create_output_color_outcome(struct weston_color_manager *cm_base, struct weston_output *output) { struct mock_color_manager *cm = container_of(cm_base, typeof(*cm), base); struct weston_output_color_outcome *co; co = xzalloc(sizeof *co); co->hdr_meta = *cm->test_hdr_meta; return co; } static struct weston_color_profile * mock_cm_ref_stock_sRGB_color_profile(struct weston_color_manager *mock_cm) { struct weston_color_profile *mock_cprof; mock_cprof = xzalloc(sizeof(*mock_cprof)); weston_color_profile_init(mock_cprof, mock_cm); str_printf(&mock_cprof->description, "mock cprof"); return mock_cprof; } static bool mock_cm_get_color_profile_from_params(struct weston_color_manager *cm, const struct weston_color_profile_params *params, const char *name_part, struct weston_color_profile **cprof_out, char **errmsg) { test_assert_not_reached("This cannot be a valid parametric profile."); } static void mock_cm_destroy_color_profile(struct weston_color_profile *mock_cprof) { free(mock_cprof->description); free(mock_cprof); } /* * Manufacture various weston.ini and check what * wet_output_set_color_characteristics() says. Tests for the return value and * the error messages logged. */ TEST_P(color_characteristics_config_error, config_cases) { const struct config_testcase *t = data; struct weston_config *wc; struct weston_config_section *section; int retval; char *logbuf; size_t logsize; struct mock_color_manager mock_cm = { .base.create_output_color_outcome = mock_create_output_color_outcome, .base.ref_stock_sRGB_color_profile = mock_cm_ref_stock_sRGB_color_profile, .base.destroy_color_profile = mock_cm_destroy_color_profile, }; struct weston_compositor mock_compositor = { .color_manager = &mock_cm.base, .color_profile_id_generator = weston_idalloc_create(&mock_compositor), }; struct weston_output mock_output = {}; mock_cm.base.compositor = &mock_compositor; wl_list_init(&mock_compositor.plane_list); weston_output_init(&mock_output, &mock_compositor, "mockoutput"); logfile = open_memstream(&logbuf, &logsize); weston_log_set_handler(logger, logger); wc = create_config(t); section = weston_config_get_section(wc, "output", "name", "mockoutput"); test_assert_ptr_not_null(section); retval = wet_output_set_color_characteristics(&mock_output, wc, section); test_assert_int_eq(fclose(logfile), 0); logfile = NULL; testlog("retval %d, logs:\n%s\n", retval, logbuf); test_assert_int_eq(retval, t->expected_retval); test_assert_int_eq(strcmp(logbuf, t->expected_error), 0); weston_config_destroy(wc); free(logbuf); weston_output_release(&mock_output); weston_idalloc_destroy(mock_compositor.color_profile_id_generator); return RESULT_OK; } /* Setting NULL resets group_mask */ TEST(weston_output_set_color_characteristics_null) { struct mock_color_manager mock_cm = { .base.create_output_color_outcome = mock_create_output_color_outcome, .base.ref_stock_sRGB_color_profile = mock_cm_ref_stock_sRGB_color_profile, .base.destroy_color_profile = mock_cm_destroy_color_profile, }; struct weston_compositor mock_compositor = { .color_manager = &mock_cm.base, .color_profile_id_generator = weston_idalloc_create(&mock_compositor), }; struct weston_output mock_output = {}; mock_cm.base.compositor = &mock_compositor; wl_list_init(&mock_compositor.plane_list); weston_output_init(&mock_output, &mock_compositor, "mockoutput"); mock_output.color_characteristics.group_mask = 1; weston_output_set_color_characteristics(&mock_output, NULL); test_assert_u32_eq(mock_output.color_characteristics.group_mask, 0); weston_output_release(&mock_output); weston_idalloc_destroy(mock_compositor.color_profile_id_generator); return RESULT_OK; } struct value_testcase { unsigned field_index; float value; bool retval; }; static const struct value_testcase value_cases[] = { { 0, 0.0, true }, { 0, 1.0, true }, { 0, -0.001, false }, { 0, 1.01, false }, { 0, NAN, false }, { 0, HUGE_VALF, false }, { 0, -HUGE_VALF, false }, { 1, -1.0, false }, { 2, 2.0, false }, { 3, 2.0, false }, { 4, 2.0, false }, { 5, 2.0, false }, { 6, 2.0, false }, { 7, 2.0, false }, { 8, 0.99, false }, { 8, 65535.1, false }, { 9, 0.000099, false }, { 9, 6.55351, false }, { 10, 0.99, false }, { 10, 65535.1, false }, { 11, 0.99, false }, { 11, 65535.1, false }, }; /* * Modify one value in a known good metadata structure, and see how * validation reacts to it. */ TEST_P(hdr_metadata_type1_errors, value_cases) { struct value_testcase *t = data; struct weston_hdr_metadata_type1 meta = { .group_mask = WESTON_HDR_METADATA_TYPE1_GROUP_ALL_MASK, .primary[0] = { 0.6650, 0.3261 }, .primary[1] = { 0.2890, 0.6435 }, .primary[2] = { 0.1491, 0.0507 }, .white = { 0.3134, 0.3291 }, .maxDML = 600.0, .minDML = 0.0001, .maxCLL = 600.0, .maxFALL = 400.0, }; float *fields[] = { &meta.primary[0].x, &meta.primary[0].y, &meta.primary[1].x, &meta.primary[1].y, &meta.primary[2].x, &meta.primary[2].y, &meta.white.x, &meta.white.y, &meta.maxDML, &meta.minDML, &meta.maxCLL, &meta.maxFALL, }; struct mock_color_manager mock_cm = { .base.create_output_color_outcome = mock_create_output_color_outcome, .base.ref_stock_sRGB_color_profile = mock_cm_ref_stock_sRGB_color_profile, .base.destroy_color_profile = mock_cm_destroy_color_profile, .test_hdr_meta = &meta, }; struct weston_compositor mock_compositor = { .color_manager = &mock_cm.base, .color_profile_id_generator = weston_idalloc_create(&mock_compositor), }; struct weston_output mock_output = {}; bool ret; weston_log_set_handler(no_logger, no_logger); mock_cm.base.compositor = &mock_compositor; wl_list_init(&mock_compositor.plane_list); weston_output_init(&mock_output, &mock_compositor, "mockoutput"); test_assert_uint_lt(t->field_index, ARRAY_LENGTH(fields)); *fields[t->field_index] = t->value; ret = weston_output_set_color_outcome(&mock_output); test_assert_int_eq(ret, t->retval); weston_output_color_outcome_destroy(&mock_output.color_outcome); weston_output_release(&mock_output); weston_idalloc_destroy(mock_compositor.color_profile_id_generator); return RESULT_OK; } /* Unflagged members are ignored in validity check */ TEST(hdr_metadata_type1_ignore_unflagged) { /* All values invalid, but also empty mask so none actually used. */ struct weston_hdr_metadata_type1 meta = { .group_mask = 0, .primary[0] = { -1.0, -1.0 }, .primary[1] = { -1.0, -1.0 }, .primary[2] = { -1.0, -1.0 }, .white = { -1.0, -1.0 }, .maxDML = -1.0, .minDML = -1.0, .maxCLL = -1.0, .maxFALL = -1.0, }; struct mock_color_manager mock_cm = { .base.create_output_color_outcome = mock_create_output_color_outcome, .base.ref_stock_sRGB_color_profile = mock_cm_ref_stock_sRGB_color_profile, .base.destroy_color_profile = mock_cm_destroy_color_profile, .test_hdr_meta = &meta, }; struct weston_compositor mock_compositor = { .color_manager = &mock_cm.base, .color_profile_id_generator = weston_idalloc_create(&mock_compositor), }; struct weston_output mock_output = {}; bool ret; mock_cm.base.compositor = &mock_compositor; wl_list_init(&mock_compositor.plane_list); weston_log_set_handler(no_logger, no_logger); weston_output_init(&mock_output, &mock_compositor, "mockoutput"); ret = weston_output_set_color_outcome(&mock_output); test_assert_true(ret); weston_output_color_outcome_destroy(&mock_output.color_outcome); weston_output_release(&mock_output); weston_idalloc_destroy(mock_compositor.color_profile_id_generator); return RESULT_OK; } struct mode_testcase { bool color_management; uint32_t supported_eotf_mask; uint32_t supported_colorimetry_mask; const char *eotf_mode; const char *colorimetry_mode; enum weston_eotf_mode expected_eotf_mode; enum weston_colorimetry_mode expected_colorimetry_mode; int expected_retval; const char *expected_error; }; static const struct mode_testcase mode_config_cases[] = { /* Defaults */ { false, WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_DEFAULT, NULL, NULL, WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_DEFAULT, 0, "" }, /* Color management off, EOTF modes */ { false, WESTON_EOTF_MODE_ALL_MASK, WESTON_COLORIMETRY_MODE_DEFAULT, "sdr", NULL, WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_DEFAULT, 0, "" }, { false, WESTON_EOTF_MODE_ALL_MASK, WESTON_COLORIMETRY_MODE_DEFAULT, "hdr-gamma", NULL, WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_DEFAULT, -1, "Error: EOTF mode hdr-gamma on output 'mockoutput' requires color-management=true in weston.ini\n" }, { false, WESTON_EOTF_MODE_ALL_MASK, WESTON_COLORIMETRY_MODE_DEFAULT, "st2084", NULL, WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_DEFAULT, -1, "Error: EOTF mode st2084 on output 'mockoutput' requires color-management=true in weston.ini\n" }, { false, WESTON_EOTF_MODE_ALL_MASK, WESTON_COLORIMETRY_MODE_DEFAULT, "hlg", NULL, WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_DEFAULT, -1, "Error: EOTF mode hlg on output 'mockoutput' requires color-management=true in weston.ini\n" }, { false, WESTON_EOTF_MODE_ALL_MASK, WESTON_COLORIMETRY_MODE_DEFAULT, "nonosense", NULL, WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_DEFAULT, -1, "Error in config for output 'mockoutput': 'nonosense' is not a valid EOTF mode. Try one of: sdr hdr-gamma st2084 hlg\n" }, /* Color management on, EOTF modes */ { true, WESTON_EOTF_MODE_ALL_MASK, WESTON_COLORIMETRY_MODE_DEFAULT, "sdr", NULL, WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_DEFAULT, 0, "" }, { true, WESTON_EOTF_MODE_ALL_MASK, WESTON_COLORIMETRY_MODE_DEFAULT, "hdr-gamma", NULL, WESTON_EOTF_MODE_TRADITIONAL_HDR, WESTON_COLORIMETRY_MODE_DEFAULT, 0, "" }, { true, WESTON_EOTF_MODE_ALL_MASK, WESTON_COLORIMETRY_MODE_DEFAULT, "st2084", NULL, WESTON_EOTF_MODE_ST2084, WESTON_COLORIMETRY_MODE_DEFAULT, 0, "" }, { true, WESTON_EOTF_MODE_ALL_MASK, WESTON_COLORIMETRY_MODE_DEFAULT, "hlg", NULL, WESTON_EOTF_MODE_HLG, WESTON_COLORIMETRY_MODE_DEFAULT, 0, "" }, { true, WESTON_EOTF_MODE_ALL_MASK, WESTON_COLORIMETRY_MODE_DEFAULT, "nonosense", NULL, WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_DEFAULT, -1, "Error in config for output 'mockoutput': 'nonosense' is not a valid EOTF mode. Try one of: sdr hdr-gamma st2084 hlg\n" }, /* unsupported EOTF mode */ { true, WESTON_EOTF_MODE_SDR | WESTON_EOTF_MODE_TRADITIONAL_HDR | WESTON_EOTF_MODE_ST2084, WESTON_COLORIMETRY_MODE_DEFAULT, "hlg", NULL, WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_DEFAULT, -1, "Error: output 'mockoutput' does not support EOTF mode hlg.\n" }, /* Color management off, colorimetry modes */ { false, WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_ALL_MASK, NULL, "default", WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_DEFAULT, 0, "" }, { false, WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_ALL_MASK, NULL, "bt2020cycc", WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_DEFAULT, -1, "Error: Colorimetry mode bt2020cycc on output 'mockoutput' requires color-management=true in weston.ini\n" }, { false, WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_ALL_MASK, NULL, "bt2020ycc", WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_DEFAULT, -1, "Error: Colorimetry mode bt2020ycc on output 'mockoutput' requires color-management=true in weston.ini\n" }, { false, WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_ALL_MASK, NULL, "bt2020rgb", WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_DEFAULT, -1, "Error: Colorimetry mode bt2020rgb on output 'mockoutput' requires color-management=true in weston.ini\n" }, { false, WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_ALL_MASK, NULL, "p3d65", WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_DEFAULT, -1, "Error: Colorimetry mode p3d65 on output 'mockoutput' requires color-management=true in weston.ini\n" }, { false, WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_ALL_MASK, NULL, "p3dci", WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_DEFAULT, -1, "Error: Colorimetry mode p3dci on output 'mockoutput' requires color-management=true in weston.ini\n" }, { false, WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_ALL_MASK, NULL, "ictcp", WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_DEFAULT, -1, "Error: Colorimetry mode ictcp on output 'mockoutput' requires color-management=true in weston.ini\n" }, { false, WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_ALL_MASK, NULL, "imagine that", WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_DEFAULT, -1, "Error in config for output 'mockoutput': 'imagine that' is not a valid colorimetry mode. Try one of: default bt2020cycc bt2020ycc bt2020rgb p3d65 p3dci ictcp\n" }, /* Color management on, colorimetry modes */ { true, WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_ALL_MASK, NULL, "default", WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_DEFAULT, 0, "" }, { true, WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_ALL_MASK, NULL, "bt2020cycc", WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_BT2020_CYCC, 0, "" }, { true, WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_ALL_MASK, NULL, "bt2020ycc", WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_BT2020_YCC, 0, "" }, { true, WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_ALL_MASK, NULL, "bt2020rgb", WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_BT2020_RGB, 0, "" }, { true, WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_ALL_MASK, NULL, "p3d65", WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_P3D65, 0, "" }, { true, WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_ALL_MASK, NULL, "p3dci", WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_P3DCI, 0, "" }, { true, WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_ALL_MASK, NULL, "ictcp", WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_ICTCP, 0, "" }, { true, WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_ALL_MASK, NULL, "imagine that", WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_DEFAULT, -1, "Error in config for output 'mockoutput': 'imagine that' is not a valid colorimetry mode. Try one of: default bt2020cycc bt2020ycc bt2020rgb p3d65 p3dci ictcp\n" }, /* Unsupported colorimetry mode */ { true, WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_DEFAULT | WESTON_COLORIMETRY_MODE_BT2020_RGB | WESTON_COLORIMETRY_MODE_BT2020_CYCC | WESTON_COLORIMETRY_MODE_P3D65, NULL, "ictcp", WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_DEFAULT, -1, "Error: output 'mockoutput' does not support colorimetry mode ictcp.\n" }, }; static struct weston_config * create_mode_config(const struct mode_testcase *t) { struct compositor_setup setup; struct weston_config *wc; compositor_setup_defaults(&setup); weston_ini_setup(&setup, cfgln("[output]"), cfgln("name=mockoutput"), t->eotf_mode ? cfgln("eotf-mode=%s", t->eotf_mode) : cfgln(""), t->colorimetry_mode ? cfgln("colorimetry-mode=%s", t->colorimetry_mode) : cfgln("") ); wc = weston_config_parse(setup.config_file); free(setup.config_file); return wc; } /* * Manufacture various weston.ini and check what * wet_output_set_eotf_mode() and wet_output_set_colorimetry_mode() says. * Tests for the return value and the error messages logged. */ TEST_P(mode_config_error, mode_config_cases) { const struct mode_testcase *t = data; struct mock_color_manager mock_cm = { .base.create_output_color_outcome = mock_create_output_color_outcome, .base.ref_stock_sRGB_color_profile = mock_cm_ref_stock_sRGB_color_profile, .base.destroy_color_profile = mock_cm_destroy_color_profile, }; struct weston_compositor mock_compositor = { .color_manager = &mock_cm.base, .color_profile_id_generator = weston_idalloc_create(&mock_compositor), }; struct weston_config *wc; struct weston_config_section *section; int retval; int attached; char *logbuf; size_t logsize; struct weston_head mock_head = {}; struct weston_output mock_output = {}; mock_cm.base.compositor = &mock_compositor; wl_list_init(&mock_compositor.plane_list); weston_output_init(&mock_output, &mock_compositor, "mockoutput"); weston_head_init(&mock_head, "mockhead"); weston_head_set_supported_eotf_mask(&mock_head, t->supported_eotf_mask); weston_head_set_supported_colorimetry_mask(&mock_head, t->supported_colorimetry_mask); attached = weston_output_attach_head(&mock_output, &mock_head); test_assert_int_eq(attached, 0); logfile = open_memstream(&logbuf, &logsize); weston_log_set_handler(logger, logger); wc = create_mode_config(t); section = weston_config_get_section(wc, "output", "name", "mockoutput"); test_assert_ptr_not_null(section); retval = wet_output_set_eotf_mode(&mock_output, section, t->color_management); if (retval == 0) { retval = wet_output_set_colorimetry_mode(&mock_output, section, t->color_management); } test_assert_int_eq(fclose(logfile), 0); logfile = NULL; testlog("retval %d, logs:\n%s\n", retval, logbuf); test_assert_int_eq(retval, t->expected_retval); test_assert_int_eq(strcmp(logbuf, t->expected_error), 0); test_assert_enum(weston_output_get_eotf_mode(&mock_output), t->expected_eotf_mode); test_assert_enum(weston_output_get_colorimetry_mode(&mock_output), t->expected_colorimetry_mode); weston_config_destroy(wc); free(logbuf); weston_output_release(&mock_output); weston_head_release(&mock_head); weston_idalloc_destroy(mock_compositor.color_profile_id_generator); return RESULT_OK; } static void test_creating_output_color_profile(struct weston_config *wc, const char *profile_name, uint32_t supported_color_features, uint32_t supported_primaries_named, uint32_t supported_tf_named, const char *expected_error) { struct weston_color_profile *cprof; char *logbuf; size_t logsize; 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 = supported_color_features, .base.supported_primaries_named = supported_primaries_named, .base.supported_tf_named = supported_tf_named, }; struct weston_compositor mock_compositor = { .color_manager = &mock_cm.base, .color_profile_id_generator = weston_idalloc_create(&mock_compositor), }; struct weston_output mock_output = {}; mock_cm.base.compositor = &mock_compositor; wl_list_init(&mock_compositor.plane_list); logfile = open_memstream(&logbuf, &logsize); weston_log_set_handler(logger, logger); weston_output_init(&mock_output, &mock_compositor, "mockoutput"); cprof = wet_create_output_color_profile(&mock_output, wc, profile_name); test_assert_ptr_null(cprof); test_assert_int_eq(fclose(logfile), 0); logfile = NULL; testlog("logs:\n%s\n------\n", logbuf); test_assert_str_eq(logbuf, expected_error); free(logbuf); weston_output_release(&mock_output); weston_idalloc_destroy(mock_compositor.color_profile_id_generator); } struct color_profile_name_testcase { const char *profile_name; const char *expected_error; }; static const struct color_profile_name_testcase color_profile_name_cases[] = { { "notexists", "Config error in weston.ini, output mockoutput: no [color-profile] section with 'name=notexists' found.\n", }, { "boo:faa", "Config error in weston.ini, output mockoutput, color-profile=boo:faa is illegal. The ':' character is legal only for 'srgb:' and 'auto:'.\n", }, { "auto:kek", "Config error in weston.ini, output mockoutput, key color-profile=auto: invalid flag 'kek'.\n", }, }; /* * Manufacture various weston.ini and check the error messages that * wet_create_output_color_profile() generates for bad color-profile names. */ TEST_P(parametric_color_profile_name_errors, color_profile_name_cases) { const struct color_profile_name_testcase *t = data; test_creating_output_color_profile(NULL, t->profile_name, 0xffffffff, 0xffffffff, 0xffffffff, t->expected_error); return RESULT_OK; } struct parameters_testcase { const char *profile_string; const char *expected_error; }; static const struct parameters_testcase param_config_cases[] = { { "", "Config error in weston.ini [color-profile] name=mydisp, invalid parameter set:\n" " primaries not set\n" " transfer function not set\n", }, { "tf_named=gamma22\n", "Config error in weston.ini [color-profile] name=mydisp, invalid parameter set:\n" " primaries not set\n" }, { "prim_named=srgb\n", "Config error in weston.ini [color-profile] name=mydisp, invalid parameter set:\n" " transfer function not set\n", }, { "tf_named=kukkuu\n" "prim_named=jeejee\n", "Config error in weston.ini [color-profile] name=mydisp, prim_named has unknown value 'jeejee'.\n" "Config error in weston.ini [color-profile] name=mydisp, tf_named has unknown value 'kukkuu'.\n", }, { "prim_named=pal\n" "tf_named=gamma28\n" "tf_power=2.4\n", "Config error in weston.ini [color-profile] name=mydisp, invalid parameter set:\n" " tf was already set\n", }, { "prim_named=pal_m\n" "prim_red=0.67 0.33\n" "prim_green=0.21 0.71\n" "prim_blue=0.14 0.08\n" "prim_white=0.31 0.32\n" "tf_power=2.4\n", "Config error in weston.ini [color-profile] name=mydisp, invalid parameter set:\n" " primaries were already set\n", }, { "prim_red=0.6 0.3\n" "prim_blue=0.1 0.05\n" "min_lum=0\n" "target_white=0.33 0.33\n" "target_max_lum=1200\n", "Config error in weston.ini [color-profile] name=mydisp:\n" " group: signaling primaries\n" " prim_red is set.\n" " prim_green is missing.\n" " prim_blue is set.\n" " prim_white is missing.\n" " group: signaling luminances\n" " min_lum is set.\n" " max_lum is missing.\n" " ref_lum is missing.\n" " group: target primaries\n" " target_red is missing.\n" " target_green is missing.\n" " target_blue is missing.\n" " target_white is set.\n" " group: target luminances\n" " target_min_lum is missing.\n" " target_max_lum is set.\n" "You must set either none or all keys of a group.\n", }, { "prim_red=0.67 0.33 0.4\n" "prim_green=0.21\n" "prim_blue=0,14 k\n" "prim_white=\n" "tf_power=xx\n", "Config error in weston.ini [color-profile] name=mydisp, parsing prim_red: Needed exactly 2 numbers separated by whitespace, got 3.\n" "Config error in weston.ini [color-profile] name=mydisp, parsing prim_green: Needed exactly 2 numbers separated by whitespace, got 1.\n" "Config error in weston.ini [color-profile] name=mydisp, parsing prim_blue: '0,14' is not a number.\n" "Config error in weston.ini [color-profile] name=mydisp, parsing prim_white: Needed exactly 2 numbers separated by whitespace, got 0.\n" "Config error in weston.ini [color-profile] name=mydisp, parsing tf_power: 'xx' is not a number.\n", }, { "tf_power=50\n", "Config error in weston.ini [color-profile] name=mydisp, invalid parameter set:\n" " tf power exponent 50.000000 is not in the range [1.0, 10.0]\n" " primaries not set\n" " transfer function not set\n", }, { "prim_red=Inf 0.33\n" "prim_green=0.21 7\n" "prim_blue=-1 NaN\n" "prim_white=0 -2\n" "tf_power=3\n", "Config error in weston.ini [color-profile] name=mydisp, invalid parameter set:\n" " invalid primary color volume, the red primary CIE x value inf is out of range [-1.0, 2.0]\n" " invalid primary color volume, the green primary CIE y value 7.000000 is out of range [-1.0, 2.0]\n" " invalid primary color volume, the blue primary CIE y value nan is out of range [-1.0, 2.0]\n" " invalid primary color volume, the white point CIE y value -2.000000 is out of range [-1.0, 2.0]\n" " white point out of primary volume\n" }, { "prim_named=bt2020\n" "tf_named=bt1886\n" "min_lum=10\n" "ref_lum=5\n" "max_lum=2\n" "target_min_lum=55\n" "target_max_lum=1\n" "max_fall=-7\n" "max_cll=0\n", "Config error in weston.ini [color-profile] name=mydisp, invalid parameter set:\n" " reference luminance (5.000000) must be greater than primary minimum luminance (10.000000)\n" " primary minimum luminance (10.000000) must be less than primary maximum luminance (2.000000)\n" " target min luminance (55.000000) must be less than target max luminance (1.000000)\n" " maxCLL (0.000000) must be in the range (0.0, 1e+6]\n" " maxCLL (0.000000) should be greater than target min luminance (0.010000)\n" " maxFALL (-7.000000) must be in the range (0.0, 1e+6]\n" " maxFALL (-7.000000) must be greater than min luminance (0.010000)\n", }, }; /* * Manufacture various weston.ini and check the error messages that * wet_create_output_color_profile() generates for invalid * color-profile sections. */ TEST_P(parametric_color_profile_parsing_errors, param_config_cases) { const struct parameters_testcase *t = data; 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); test_creating_output_color_profile(wc, "mydisp", 0xffffffff, 0xffffffff, 0xffffffff, t->expected_error); weston_config_destroy(wc); return RESULT_OK; } static const struct parameters_testcase param_unsupported_cases[] = { { "prim_named=ntsc\n" "tf_named=log100\n", "Config error in weston.ini [color-profile] name=mydisp, invalid parameter set:\n" " primaries named NTSC (BT.601) not supported by the color manager\n" " logarithmic 100:1 not supported by the color manager\n" " primaries not set\n" " transfer function not set\n", }, { "prim_named=srgb\n" "tf_power=2.3\n", "Config error in weston.ini [color-profile] name=mydisp, invalid parameter set:\n" " set_tf_power not supported by the color manager\n" " transfer function not set\n", }, }; /* * Manufacture various weston.ini and check the error messages that * wet_create_output_color_profile() generates for valid * color-profile sections that use things the color manager does not * support. */ TEST_P(parametric_color_profile_parsing_unsupported, param_unsupported_cases) { const struct parameters_testcase *t = data; 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); test_creating_output_color_profile(wc, "mydisp", 0, (1u << WESTON_PRIMARIES_CICP_SRGB), 0, t->expected_error); weston_config_destroy(wc); return RESULT_OK; }