[Pixman] [PATCH] test: Add new fuzz tester targeting solid images

Pekka Paalanen ppaalanen at gmail.com
Wed May 6 05:25:43 PDT 2015


On Wed, 22 Apr 2015 15:12:57 +0100
Ben Avison <bavison at riscosopen.org> wrote:

> This places a heavier emphasis on solid images than the other fuzz testers,
> and tests both single-pixel repeating bitmap images as well as those created
> using pixman_image_create_solid_fill(). In the former case, it also
> exercises the case where the bitmap contents are written to after the
> image's first use, which is not a use-case that any other test has
> previously covered.

Hi Ben,

this is written based on blitters-test.c, right? That would have been
interesting to mention, so new reviewers like me can compare with
existing code you mirrored.

This test hits the case of fully transparent/opaque -> arbitrary color
change, but very rarely -> fully transparent/opaque, never starting
with an arbitrary color. I understand the two latter cases are not
really testable: setting the fast path flags for an arbitrary color
when you could use fully transparent/opaque is not detectable via
correctness tests. Are there any cases where more symmetric test case
selection would be useful? If not, nevermind.

Here's how I understand the code.

Source image:
- 50% chance for multi-pixel bits image
- 25% chance for 1x1 bits image
- 25% chance for solid image

Mask image:
- 50% chance to not be used
- 12.5% chance for multi-pixel bits image without CA
- 6.25% chance for 1x1 bits image without CA
- 6.25% chance for solid image without CA
- 12.5% chance for multi-pixel bits image with CA
- 6.25% chance for 1x1 bits image with CA
- 6.25% chance for solid image with CA

Destination image:
- always multi-pixel

Both source and mask, when they are 1x1 bits images, have:
- 50% chance to start fully opaque white
- 50% chance to start fully transparent
and then switch to arbitrary random color for the actual test.

The problems this test attempts to reveal happen only with 1x1 bits
images, so shouldn't the chances for 1x1 bits images be higher?

Multi-pixel source or mask images are never written after the initial
use, so aren't those useless in this test? IOW, since this test is
about changing image contents after it has already been used once,
shouldn't all cases be changing the contents?

I would propose:
- increase the chances of 1x1 bits images
- do the init-use-change-use test also with multi-pixel images
- more of fully opaque colors, e.g. [r=0, g=0.3, b=1.0], so that result
  clamping cannot accidentally produce correct results for wrong
  computation.

pixman_image_create_solid_fill() is being used in existing tests, at
least according to grep, but I think it's fine to have it here too. I'm
not sure if any test uses solid masks, but this one does and even
with/without CA. That's good.

> ---
>  .gitignore            |    1 +
>  test/Makefile.sources |    1 +
>  test/solid-test.c     |  332 +++++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 334 insertions(+), 0 deletions(-)
>  create mode 100644 test/solid-test.c
> 
> diff --git a/.gitignore b/.gitignore
> index 0f11496..8bfb48c 100644
> --- a/.gitignore
> +++ b/.gitignore
> @@ -77,6 +77,7 @@ test/scaling-crash-test
>  test/scaling-helpers-test
>  test/scaling-test
>  test/screen-test
> +test/solid-test
>  test/stress-test
>  test/trap-crasher
>  test/trap-test

I removed this hunk, because I merged Bill's .gitignore patch first,
which makes this addition unnecessary.

> diff --git a/test/Makefile.sources b/test/Makefile.sources
> index 8b0e855..f09c3e4 100644
> --- a/test/Makefile.sources
> +++ b/test/Makefile.sources
> @@ -23,6 +23,7 @@ TESTPROGRAMS =			      \
>  	composite-traps-test	      \
>  	region-contains-test	      \
>  	glyph-test		      \
> +	solid-test                    \
>  	stress-test		      \
>  	blitters-test		      \
>  	affine-test		      \
> diff --git a/test/solid-test.c b/test/solid-test.c
> new file mode 100644
> index 0000000..070652a
> --- /dev/null
> +++ b/test/solid-test.c
> @@ -0,0 +1,332 @@
> +/*
> + * Copyright © 2015 RISC OS Open Ltd
> + *
> + * Permission to use, copy, modify, distribute, and sell this software and its
> + * documentation for any purpose is hereby granted without fee, provided that
> + * the above copyright notice appear in all copies and that both that
> + * copyright notice and this permission notice appear in supporting
> + * documentation, and that the name of the copyright holders not be used in
> + * advertising or publicity pertaining to distribution of the software without
> + * specific, written prior permission.  The copyright holders make no
> + * representations about the suitability of this software for any purpose.  It
> + * is provided "as is" without express or implied warranty.
> + *
> + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
> + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
> + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
> + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
> + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
> + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
> + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
> + * SOFTWARE.
> + *
> + * Author:  Ben Avison (bavison at riscosopen.org)
> + *
> + */
> +
> +#include "utils.h"
> +#include <stdlib.h>
> +#include <stdio.h>
> +
> +#define WIDTH 32
> +#define HEIGHT 32
> +
> +static const pixman_op_t op_list[] = {
> +    PIXMAN_OP_SRC,
> +    PIXMAN_OP_OVER,
> +    PIXMAN_OP_ADD,
> +    PIXMAN_OP_CLEAR,
> +    PIXMAN_OP_SRC,
> +    PIXMAN_OP_DST,
> +    PIXMAN_OP_OVER,
> +    PIXMAN_OP_OVER_REVERSE,
> +    PIXMAN_OP_IN,
> +    PIXMAN_OP_IN_REVERSE,
> +    PIXMAN_OP_OUT,
> +    PIXMAN_OP_OUT_REVERSE,
> +    PIXMAN_OP_ATOP,
> +    PIXMAN_OP_ATOP_REVERSE,
> +    PIXMAN_OP_XOR,
> +    PIXMAN_OP_ADD,
> +    PIXMAN_OP_MULTIPLY,
> +    PIXMAN_OP_SCREEN,
> +    PIXMAN_OP_OVERLAY,
> +    PIXMAN_OP_DARKEN,
> +    PIXMAN_OP_LIGHTEN,
> +    PIXMAN_OP_HARD_LIGHT,
> +    PIXMAN_OP_DIFFERENCE,
> +    PIXMAN_OP_EXCLUSION,
> +#if 0 /* these use floating point math and are not always bitexact on different platforms */
> +    PIXMAN_OP_SATURATE,
> +    PIXMAN_OP_DISJOINT_CLEAR,
> +    PIXMAN_OP_DISJOINT_SRC,
> +    PIXMAN_OP_DISJOINT_DST,
> +    PIXMAN_OP_DISJOINT_OVER,
> +    PIXMAN_OP_DISJOINT_OVER_REVERSE,
> +    PIXMAN_OP_DISJOINT_IN,
> +    PIXMAN_OP_DISJOINT_IN_REVERSE,
> +    PIXMAN_OP_DISJOINT_OUT,
> +    PIXMAN_OP_DISJOINT_OUT_REVERSE,
> +    PIXMAN_OP_DISJOINT_ATOP,
> +    PIXMAN_OP_DISJOINT_ATOP_REVERSE,
> +    PIXMAN_OP_DISJOINT_XOR,
> +    PIXMAN_OP_CONJOINT_CLEAR,
> +    PIXMAN_OP_CONJOINT_SRC,
> +    PIXMAN_OP_CONJOINT_DST,
> +    PIXMAN_OP_CONJOINT_OVER,
> +    PIXMAN_OP_CONJOINT_OVER_REVERSE,
> +    PIXMAN_OP_CONJOINT_IN,
> +    PIXMAN_OP_CONJOINT_IN_REVERSE,
> +    PIXMAN_OP_CONJOINT_OUT,
> +    PIXMAN_OP_CONJOINT_OUT_REVERSE,
> +    PIXMAN_OP_CONJOINT_ATOP,
> +    PIXMAN_OP_CONJOINT_ATOP_REVERSE,
> +    PIXMAN_OP_CONJOINT_XOR,
> +    PIXMAN_OP_COLOR_DODGE,
> +    PIXMAN_OP_COLOR_BURN,
> +    PIXMAN_OP_SOFT_LIGHT,
> +    PIXMAN_OP_HSL_HUE,
> +    PIXMAN_OP_HSL_SATURATION,
> +    PIXMAN_OP_HSL_COLOR,
> +    PIXMAN_OP_HSL_LUMINOSITY,
> +#endif
> +};
> +
> +/* The first eight format in the list are by far the most widely
> + * used formats, so we test those more than the others
> + */
> +#define N_MOST_LIKELY_FORMATS 8
> +
> +static const pixman_format_code_t img_fmt_list[] = {
> +    PIXMAN_a8r8g8b8,
> +    PIXMAN_a8b8g8r8,
> +    PIXMAN_x8r8g8b8,
> +    PIXMAN_x8b8g8r8,
> +    PIXMAN_r5g6b5,
> +    PIXMAN_b5g6r5,
> +    PIXMAN_a8,
> +    PIXMAN_a1,
> +    PIXMAN_r3g3b2,
> +    PIXMAN_b8g8r8a8,
> +    PIXMAN_b8g8r8x8,
> +    PIXMAN_r8g8b8a8,
> +    PIXMAN_r8g8b8x8,
> +    PIXMAN_x14r6g6b6,
> +    PIXMAN_r8g8b8,
> +    PIXMAN_b8g8r8,
> +#if 0 /* These are going to use floating point in the near future */
> +    PIXMAN_x2r10g10b10,
> +    PIXMAN_a2r10g10b10,
> +    PIXMAN_x2b10g10r10,
> +    PIXMAN_a2b10g10r10,
> +#endif
> +    PIXMAN_a1r5g5b5,
> +    PIXMAN_x1r5g5b5,
> +    PIXMAN_a1b5g5r5,
> +    PIXMAN_x1b5g5r5,
> +    PIXMAN_a4r4g4b4,
> +    PIXMAN_x4r4g4b4,
> +    PIXMAN_a4b4g4r4,
> +    PIXMAN_x4b4g4r4,
> +    PIXMAN_r3g3b2,
> +    PIXMAN_b2g3r3,
> +    PIXMAN_a2r2g2b2,
> +    PIXMAN_a2b2g2r2,
> +    PIXMAN_c8,
> +    PIXMAN_g8,
> +    PIXMAN_x4c4,
> +    PIXMAN_x4g4,
> +    PIXMAN_c4,
> +    PIXMAN_g4,
> +    PIXMAN_g1,
> +    PIXMAN_x4a4,
> +    PIXMAN_a4,
> +    PIXMAN_r1g2b1,
> +    PIXMAN_b1g2r1,
> +    PIXMAN_a1r1g1b1,
> +    PIXMAN_a1b1g1r1,
> +    PIXMAN_null
> +};
> +
> +static const pixman_format_code_t mask_fmt_list[] = {
> +    PIXMAN_a8r8g8b8,
> +    PIXMAN_a8,
> +    PIXMAN_a4,
> +    PIXMAN_a1,
> +    PIXMAN_null
> +};
> +
> +static pixman_indexed_t rgb_palette[9];
> +static pixman_indexed_t y_palette[9];
> +
> +static pixman_format_code_t
> +random_format (const pixman_format_code_t *allowed_formats)
> +{
> +    int n = 0;
> +
> +    while (allowed_formats[n] != PIXMAN_null)
> +        n++;
> +
> +    if (n > N_MOST_LIKELY_FORMATS && prng_rand_n (4) != 0)
> +        n = N_MOST_LIKELY_FORMATS;
> +
> +    return allowed_formats[prng_rand_n (n)];
> +}
> +
> +static pixman_image_t *
> +create_multi_pixel_image (const pixman_format_code_t *allowed_formats,
> +                          uint32_t                   *buffer,
> +                          pixman_format_code_t       *used_fmt)
> +{
> +    pixman_format_code_t fmt;
> +    pixman_image_t *img;
> +    int stride;
> +
> +    fmt = random_format (allowed_formats);
> +    stride = (WIDTH * PIXMAN_FORMAT_BPP (fmt) + 31) / 32 * 4;
> +    img = pixman_image_create_bits (fmt, WIDTH, HEIGHT, buffer, stride);
> +
> +    if (PIXMAN_FORMAT_TYPE (fmt) == PIXMAN_TYPE_COLOR)
> +        pixman_image_set_indexed (img, &(rgb_palette[PIXMAN_FORMAT_BPP (fmt)]));
> +    else if (PIXMAN_FORMAT_TYPE (fmt) == PIXMAN_TYPE_GRAY)
> +        pixman_image_set_indexed (img, &(y_palette[PIXMAN_FORMAT_BPP (fmt)]));
> +
> +    prng_randmemset (buffer, WIDTH * HEIGHT * 4, 0);
> +    image_endian_swap (img);
> +
> +    if (used_fmt)
> +        *used_fmt = fmt;
> +
> +    return img;
> +}
> +
> +static pixman_image_t *
> +create_image (const pixman_format_code_t *allowed_formats,
> +              uint32_t                   *buffer,
> +              pixman_format_code_t       *used_fmt)

The 'buffer' argument here is kind of strange, because the caller does
not know what the image size or format will be. But, it does work here,
with the assumption that the size will be at most WIDTH * HEIGHT *
uint32_t and the caller knows to allocate with that.


> +{
> +    if (prng_rand_n (2))
> +        return create_multi_pixel_image (allowed_formats, buffer, used_fmt);
> +
> +    if (prng_rand_n (2))
> +    {
> +        /* Use a repeating 1x1 bitmap image for solid */
> +        pixman_format_code_t fmt;
> +        pixman_image_t *img, *dummy_img;
> +        uint32_t dummy_buf;
> +
> +        fmt = random_format (allowed_formats);
> +        img = pixman_image_create_bits (fmt, 1, 1, buffer, 4);
> +        pixman_image_set_repeat (img, PIXMAN_REPEAT_NORMAL);
> +
> +        if (PIXMAN_FORMAT_TYPE (fmt) == PIXMAN_TYPE_COLOR)
> +            pixman_image_set_indexed (img, &(rgb_palette[PIXMAN_FORMAT_BPP (fmt)]));
> +        else if (PIXMAN_FORMAT_TYPE (fmt) == PIXMAN_TYPE_GRAY)
> +            pixman_image_set_indexed (img, &(y_palette[PIXMAN_FORMAT_BPP (fmt)]));

If you used a temporary for PIXMAN_FORMAT_BPP (fmt), these would fit on
the lines. Right now they'd need splitting.

> +
> +        /* Force the flags to be calculated for image with initial
> +         * bitmap contents of 0 or -1 by plotting from it into a
> +         * separate throwaway image
> +         */
> +        *buffer = prng_rand_n (2) * -1u;

A neat trick, but I think this would be more obvious:
	if (prng_rand_n (2))
		*buffer = 0x00000000;
	else
		*buffer = 0xffffffff;

And could add more chosen colors by making it a switch.

> +        dummy_img = pixman_image_create_bits (PIXMAN_a8r8g8b8, 1, 1, &dummy_buf, 4);
> +        pixman_image_composite (PIXMAN_OP_SRC, img, NULL, dummy_img, 0, 0, 0, 0, 0, 0, 1, 1);

Long lines to wrap.

> +        pixman_image_unref (dummy_img);
> +
> +        /* Now set the bitmap contents to a random value */
> +        *buffer = prng_rand ();
> +        image_endian_swap (img);
> +
> +        if (used_fmt)
> +            *used_fmt = fmt;
> +
> +        return img;
> +    }
> +    else
> +    {
> +        /* Use a native solid image */
> +        pixman_color_t color;
> +        pixman_image_t *img;
> +
> +        prng_randmemset (&color, sizeof color, 0);
> +        img = pixman_image_create_solid_fill (&color);
> +
> +        if (used_fmt)
> +            *used_fmt = PIXMAN_solid;
> +
> +        return img;
> +    }
> +}
> +
> +static uint32_t
> +test_solid (int testnum, int verbose)
> +{
> +    pixman_op_t          op;
> +    uint32_t             src_buf[WIDTH * HEIGHT];
> +    uint32_t             dst_buf[WIDTH * HEIGHT];
> +    uint32_t             mask_buf[WIDTH * HEIGHT];
> +    pixman_image_t      *src_img;
> +    pixman_image_t      *dst_img;
> +    pixman_image_t      *mask_img = NULL;
> +    pixman_format_code_t src_fmt, dst_fmt, mask_fmt = PIXMAN_null;
> +    pixman_bool_t        ca = 0;
> +    uint32_t             crc32;
> +
> +    prng_srand (testnum);
> +
> +    op = op_list[prng_rand_n (ARRAY_LENGTH (op_list))];
> +
> +    src_img = create_image (img_fmt_list, src_buf, &src_fmt);
> +    dst_img = create_multi_pixel_image (img_fmt_list, dst_buf, &dst_fmt);
> +    if (prng_rand_n (2))
> +    {
> +        mask_img = create_image (mask_fmt_list, mask_buf, &mask_fmt);
> +        ca = prng_rand_n (2);
> +        pixman_image_set_component_alpha (mask_img, ca);
> +    }
> +
> +    if (verbose)
> +    {
> +        printf ("op=%s\n", operator_name (op));
> +        printf ("src_fmt=%s, dst_fmt=%s, mask_fmt=%s\n",
> +                format_name (src_fmt), format_name (dst_fmt),
> +                format_name (mask_fmt));
> +        printf ("src_size=%u, mask_size=%u, component_alpha=%u\n",
> +                src_fmt == PIXMAN_solid ? 1 : src_img->bits.width,
> +                !mask_img || mask_fmt == PIXMAN_solid ? 1 : mask_img->bits.width,
> +                ca);
> +    }
> +
> +    pixman_image_composite (op, src_img, mask_img, dst_img,
> +                            0, 0, 0, 0, 0, 0, WIDTH, HEIGHT);
> +
> +    if (verbose)
> +        print_image (dst_img);
> +
> +    crc32 = compute_crc32_for_image (0, dst_img);
> +
> +    pixman_image_unref (src_img);
> +    pixman_image_unref (dst_img);
> +    if (mask_img)
> +        pixman_image_unref (mask_img);
> +
> +    return crc32;
> +}
> +
> +int
> +main (int argc, const char *argv[])
> +{
> +    int i;
> +
> +    prng_srand (0);
> +
> +    for (i = 1; i <= 8; i++)
> +    {
> +        initialize_palette (&(rgb_palette[i]), i, TRUE);
> +        initialize_palette (&(y_palette[i]), i, FALSE);
> +    }
> +
> +    return fuzzer_test_main ("solid", 500000,
> +			     0xE97AA88F,
> +			     test_solid, argc, argv);
> +}

I tested running this on both a reference build (all fast paths disabled
at ./configure and with -O1, and PIXMAN_DISABLE=fast in environment) and
a normal build, and the test succeeds.


Thanks,
pq


More information about the Pixman mailing list