libgimp, libgimpcolor: make real unit test of old (compiled but unused) …

… test-color-parser.c file.

The file libgimpcolor/test-color-parser.c was compiled but never actually called
by the build. Now that we have a nice infrastructure to test libgimp API, I am
moving it there with the new format. Doing this also allowed me to discover some
bugs in CSS parsing, as well as discover Python binding was failing here (cf.
the few previous commits).

Only one test is disabled so far, the one where 4 digits are used per channel in
hexadecimal notation: "#64649595eded". This format simply doesn't appear
anywhere in the spec, and also the result values in the samples listing don't
even fit. So far, I'm just unsure what to do with it, if we want to keep this
support (of some kind of higher precision hex notation, but not specified, so is
it even used by anyone?) or not.

All the other tests just work in both C and Python!
This commit is contained in:
Jehan 2024-04-20 12:39:52 +02:00
parent 106d18605a
commit 62ab8e2604
5 changed files with 269 additions and 136 deletions

View file

@ -7,6 +7,7 @@ if not meson.can_run_host_binaries()
endif endif
tests = [ tests = [
'color-parser',
'palette', 'palette',
'selection-float' 'selection-float'
] ]

View file

@ -0,0 +1,121 @@
#define DBL(c) ((gdouble)(c) / 255.0)
#define EPSILON 1e-6
typedef struct
{
const gchar *str;
gboolean fail;
const gdouble r;
const gdouble g;
const gdouble b;
const gdouble a;
} ColorSample;
static const ColorSample samples[] =
{
/* sample fail red green blue alpha */
{ "#000000", FALSE, 0.0, 0.0, 0.0, 1.0 },
{ "#FFff00", FALSE, 1.0, 1.0, 0.0, 1.0 },
{ "#6495ed", FALSE, DBL(100), DBL(149), DBL(237), 1.0 },
{ "#fff", FALSE, 1.0, 1.0, 1.0, 1.0 },
/* Very unsure about this one. Our code has some support for values on 3 or 4
* hexa per channel, but this doesn't exist in CSS specs.
* On the other hand, we should add support for alpha.
* And in any case, the result of the below should not be (1, 0, 0).
* See: https://drafts.csswg.org/css-color/#hex-notation
*/
/*{ "#64649595eded", FALSE, 1.0, 1.0, 0.0, 1.0 },*/
{ "rgb(0,0,0)", FALSE, 0.0, 0.0, 0.0, 1.0 },
{ "rgb(100,149,237)", FALSE, DBL(100), DBL(149), DBL(237), 1.0 },
{ "rgba(100%,0,100%,0.5)", FALSE, 1.0, 0.0, 1.0, 0.5 },
{ "rgb(100%,149,20%)", FALSE, 1.0, DBL(149), 0.2, 1.0 },
{ "rgb(foobar)", TRUE, 0.0, 0.0, 0.0, 1.0 },
{ "rgb(100,149,237", TRUE, 0.0, 0.0, 0.0, 1.0 },
{ "rED", FALSE, 1.0, 0.0, 0.0, 1.0 },
{ "cornflowerblue", FALSE, DBL(100), DBL(149), DBL(237), 1.0 },
{ " red", FALSE, 1.0, 0.0, 0.0, 1.0 },
{ "red ", FALSE, 1.0, 0.0, 0.0, 1.0 },
{ "red", FALSE, 1.0, 0.0, 0.0, 1.0 },
{ "red blue", TRUE, 0.0, 0.0, 0.0, 0.0 },
{ "transparent", FALSE, 0.0, 0.0, 0.0, 0.0 },
{ "23foobar", TRUE, 0.0, 0.0, 0.0, 0.0 },
{ "", TRUE, 0.0, 0.0, 0.0, 0.0 }
};
static gint
check_failure (const ColorSample *sample,
GeglColor *color)
{
gdouble red;
gdouble green;
gdouble blue;
gdouble alpha;
if (color && sample->fail)
{
gegl_color_get_rgba_with_space (color, &red, &green, &blue, &alpha,
babl_format ("R'G'B'A double"));
g_printerr ("\n\tParser succeeded for sample \"%s\" but should have failed!\n"
"\t parsed color: (%g, %g, %g, %g)\n",
sample->str, red, green, blue, alpha);
return 1;
}
else if (! color && ! sample->fail)
{
g_printerr ("\n\tParser failed for sample \"%s\" but should have succeeded!\n"
"\t expected color: (%g, %g, %g, %g)\n",
sample->str, sample->r, sample->g, sample->b, sample->a);
return 1;
}
else if (! color && sample->fail)
{
return 0;
}
gegl_color_get_rgba_with_space (color, &red, &green, &blue, &alpha,
babl_format ("R'G'B'A double"));
if (fabs (red - sample->r) > EPSILON || fabs (green - sample->g) > EPSILON ||
fabs (blue - sample->b) > EPSILON || fabs (alpha - sample->a) > EPSILON)
{
g_printerr ("\nParser succeeded for sample \"%s\" but found the wrong values!\n"
" parsed color: (%g, %g, %g, %g)\n"
" expected color: (%g, %g, %g, %g)\n",
sample->str, red, green, blue, alpha,
sample->r, sample->g, sample->b, sample->a);
return 1;
}
return 0;
}
static GimpValueArray *
gimp_c_test_run (GimpProcedure *procedure,
GimpRunMode run_mode,
GimpImage *image,
gint n_drawables,
GimpDrawable **drawables,
GimpProcedureConfig *config,
gpointer run_data)
{
gint i;
g_print ("\nTesting the GIMP color parser ...\n");
for (i = 0; i < G_N_ELEMENTS (samples); i++)
{
GeglColor *color = NULL;
gchar *test;
test = g_strdup_printf ("Parsing [%d] \"%s\" (should %s)",
i, samples[i].str,
samples[i].fail ? "fail" : "succeed");
GIMP_TEST_START(test)
color = gimp_color_parse_css (samples[i].str);
GIMP_TEST_END(check_failure (samples + i, color) == 0)
g_free (test);
}
GIMP_TEST_RETURN
}

View file

@ -0,0 +1,147 @@
#!/usr/bin/env python3
EPSILON=1e-6
def DBL(c):
return c / 255.0
samples = [
{ 'str': "#000000",
'fail': False,
'red': 0.0,
'green': 0.0,
'blue': 0.0,
'alpha': 1.0 },
{ 'str': "#FFff00",
'fail': False,
'red': 1.0,
'green': 1.0,
'blue': 0.0,
'alpha': 1.0 },
{ 'str': "#6495ed",
'fail': False,
'red': DBL(100),
'green': DBL(149),
'blue': DBL(237),
'alpha': 1.0 },
{ 'str': "#fff",
'fail': False,
'red': 1.0,
'green': 1.0,
'blue': 1.0,
'alpha': 1.0 },
#{ 'str': "#64649595eded",
#'fail': False,
#'red': 1.0,
#'green': 1.0,
#'blue': 0.0,
#'alpha': 1.0 },
{ 'str': "rgb(0,0,0)",
'fail': False,
'red': 0.0,
'green': 0.0,
'blue': 0.0,
'alpha': 1.0 },
{ 'str': "rgb(100,149,237)",
'fail': False,
'red': DBL(100),
'green': DBL(149),
'blue': DBL(237),
'alpha': 1.0 },
{ 'str': "rgba(100%,0,100%,0.5)",
'fail': False,
'red': 1.0,
'green': 0.0,
'blue': 1.0,
'alpha': 0.5 },
{ 'str': "rgb(100%,149,20%)",
'fail': False,
'red': 1.0,
'green': DBL(149),
'blue': 0.2,
'alpha': 1.0 },
{ 'str': "rgb(foobar)",
'fail': True,
'red': 0.0,
'green': 0.0,
'blue': 0.0,
'alpha': 1.0 },
{ 'str': "rgb(100,149,237",
'fail': True,
'red': 0.0,
'green': 0.0,
'blue': 0.0,
'alpha': 1.0 },
{ 'str': "rED",
'fail': False,
'red': 1.0,
'green': 0.0,
'blue': 0.0,
'alpha': 1.0 },
{ 'str': "cornflowerblue",
'fail': False,
'red': DBL(100),
'green': DBL(149),
'blue': DBL(237),
'alpha': 1.0 },
{ 'str': " red",
'fail': False,
'red': 1.0,
'green': 0.0,
'blue': 0.0,
'alpha': 1.0 },
{ 'str': "red ",
'fail': False,
'red': 1.0,
'green': 0.0,
'blue': 0.0,
'alpha': 1.0 },
{ 'str': "red",
'fail': False,
'red': 1.0,
'green': 0.0,
'blue': 0.0,
'alpha': 1.0 },
{ 'str': "red blue",
'fail': True,
'red': 0.0,
'green': 0.0,
'blue': 0.0,
'alpha': 0.0 },
{ 'str': "transparent",
'fail': False,
'red': 0.0,
'green': 0.0,
'blue': 0.0,
'alpha': 0.0 },
{ 'str': "23foobar",
'fail': True,
'red': 0.0,
'green': 0.0,
'blue': 0.0,
'alpha': 0.0 },
{ 'str': "",
'fail': True,
'red': 0.0,
'green': 0.0,
'blue': 0.0,
'alpha': 0.0 }
]
for sample in samples:
color = Gimp.color_parse_css(sample['str'])
print(color)
if sample['fail']:
gimp_assert('Parsing "{}" should fail.'.format(sample['str']), color is None)
else:
gimp_assert('Parsing "{}" should generate a color.'.format(sample['str']), color is not None)
r, g, b, a = color.get_rgba_with_space(Babl.format("R'G'B'A double"))
gimp_assert('"{}" generated ({}, {}, {}, {}) - expected ({}, {}, {}, {}).'.format(sample['str'],
r, g, b, a,
sample['red'],
sample['green'],
sample['blue'],
sample['alpha']),
abs(r - sample['red']) < EPSILON and abs(g - sample['green']) < EPSILON and
abs(b - sample['blue']) < EPSILON and abs(a - sample['alpha']) < EPSILON)

View file

@ -1,4 +1,3 @@
libgimpcolor_sources = files( libgimpcolor_sources = files(
'gimpadaptivesupersample.c', 'gimpadaptivesupersample.c',
'gimpbilinear.c', 'gimpbilinear.c',
@ -44,7 +43,6 @@ libgimpcolor_introspectable = [
libgimpcolor_headers_introspectable, libgimpcolor_headers_introspectable,
] ]
libgimpcolor = library('gimpcolor-' + gimp_api_version, libgimpcolor = library('gimpcolor-' + gimp_api_version,
libgimpcolor_sources, libgimpcolor_sources,
include_directories: rootInclude, include_directories: rootInclude,
@ -62,18 +60,3 @@ install_headers(
libgimpcolor_headers, libgimpcolor_headers,
subdir: gimp_api_name / 'libgimpcolor', subdir: gimp_api_name / 'libgimpcolor',
) )
# Test program, not installed
executable('test-color-parser',
'test-color-parser.c',
include_directories: rootInclude,
dependencies: [
cairo, gdk_pixbuf, gegl, lcms, math,
babl,
# glib,
],
c_args: '-DG_LOG_DOMAIN="LibGimpColor"',
link_with: [ libgimpbase, libgimpcolor, ],
install: false,
)

View file

@ -1,119 +0,0 @@
/* unit tests for the color parsing routines in gimprgb-parse.c
*/
#include "config.h"
#include <stdlib.h>
#include <babl/babl.h>
#include <gegl.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <glib-object.h>
#include <cairo.h>
#include "gimpcolor.h"
#define DBL(c) ((gdouble)(c) / 255.0)
typedef struct
{
const gchar *str;
gboolean alpha;
gboolean fail;
const gdouble r;
const gdouble g;
const gdouble b;
const gdouble a;
} ColorSample;
static const ColorSample samples[] =
{
/* sample alpha fail red green blue alpha */
{ "#000000", FALSE, FALSE, 0.0, 0.0, 0.0, 0.0 },
{ "#FFff00", FALSE, FALSE, 1.0, 1.0, 0.0, 0.0 },
{ "#6495ed", FALSE, FALSE, DBL(100), DBL(149), DBL(237), 0.0 },
{ "#fff", FALSE, FALSE, 1.0, 1.0, 1.0, 0.0 },
{ "#64649595eded", FALSE, FALSE, 1.0, 1.0, 0.0, 0.0 },
{ "rgb(0,0,0)", FALSE, FALSE, 0.0, 0.0, 0.0, 0.0 },
{ "rgb(100,149,237)", FALSE, FALSE, DBL(100), DBL(149), DBL(237), 0.0 },
{ "rgba(100%,0,100%,0.5)", TRUE, FALSE, 255.0, 0.0, 255.0, 0.5 },
{ "rgba(100%,0,100%,0.5)", FALSE, TRUE, 255.0, 0.0, 255.0, 0.5 },
{ "rgb(100%,149,20%)", FALSE, FALSE, 1.0, DBL(149), 0.2, 0.0 },
{ "rgb(100%,149,20%)", TRUE, TRUE, 1.0, DBL(149), 0.2, 0.0 },
{ "rgb(foobar)", FALSE, TRUE, 0.0, 0.0, 0.0, 0.0 },
{ "rgb(100,149,237", FALSE, TRUE, 0.0, 0.0, 0.0, 0.0 },
{ "rED", FALSE, FALSE, 1.0, 0.0, 0.0, 0.0 },
{ "cornflowerblue", FALSE, FALSE, DBL(100), DBL(149), DBL(237), 0.0 },
{ " red", FALSE, FALSE, 1.0, 0.0, 0.0, 0.0 },
{ "red ", FALSE, FALSE, 1.0, 0.0, 0.0, 0.0 },
{ "red", TRUE, TRUE, 1.0, 0.0, 0.0, 0.0 },
{ "red blue", FALSE, TRUE, 0.0, 0.0, 0.0, 0.0 },
{ "transparent", FALSE, TRUE, 0.0, 0.0, 0.0, 0.0 },
{ "transparent", TRUE, FALSE, 0.0, 0.0, 0.0, 0.0 },
{ "23foobar", FALSE, TRUE, 0.0, 0.0, 0.0, 0.0 },
{ "", FALSE, TRUE, 0.0, 0.0, 0.0, 0.0 }
};
static gint
check_failure (const ColorSample *sample,
gboolean success,
GimpRGB *rgb)
{
if (success && sample->fail)
{
g_print ("Parser succeeded for sample \"%s\" but should have failed!\n"
" parsed color: (%g, %g, %g, %g)\n",
sample->str, rgb->r, rgb->g, rgb->b, rgb->a);
return 1;
}
if (!success && !sample->fail)
{
g_print ("Parser failed for sample \"%s\" but should have succeeded!\n"
" parsed color: (%g, %g, %g, %g)\n",
sample->str, rgb->r, rgb->g, rgb->b, rgb->a);
return 1;
}
return 0;
}
int
main (void)
{
gint failures = 0;
gint i;
g_print ("\nTesting the GIMP color parser ...\n");
for (i = 0; i < G_N_ELEMENTS (samples); i++)
{
GimpRGB rgb = { 0.0, 0.0, 0.0, 0.0 };
gboolean success;
if (samples[i].alpha)
success = gimp_rgba_parse_css (&rgb, samples[i].str, -1);
else
success = gimp_rgb_parse_css (&rgb, samples[i].str, -1);
failures += check_failure (samples + i, success, &rgb);
}
if (failures)
{
g_print ("%d out of %d samples failed!\n\n",
failures, (int)G_N_ELEMENTS (samples));
return EXIT_FAILURE;
}
else
{
g_print ("All %d samples passed.\n\n", (int)G_N_ELEMENTS (samples));
return EXIT_SUCCESS;
}
}