Testing graphics generation with JUnit
Asked Answered
H

4

9

I'm using Java's Graphics2D to generate a graphical representation of a graph. I'm also using ImageIO to write a PNG file. (ImageIO.write(image, "png", out);)

I'm wondering how should I write JUnit tests to test whether the generated graphics is what is expected. I could pre-generate the PNG files but what if the font is a bit different on a different machine?

Houselights answered 3/8, 2010 at 20:48 Comment(2)
Well, what would you like to be certain of?Estremadura
Good question :-) Now, as I think of it, maybe it is not that important to test whether the image is exactly as expected but whether some expected features are present.Houselights
I
1

You could try testing for specific, known features of the output e.g.:

  • It there a white pixel at (100,100)?
  • It the border completely black?
  • Is the image the expected size?

And/or you could write tests for some "aggregate properties" that allow for some fuzziness in the results:

  • Does the image at least 90% match the reference image? (to allow for different fonts, antialiasing differences etc.)
  • Is the most common colour in the image equal to the background colour?
Indigenous answered 3/8, 2010 at 21:8 Comment(1)
Thanks. It looks like a good idea to test some features instead of bitmap pixels. As Vineet noticed, there could be also a difference in antialiasing.Houselights
I
2

For me, this concrete implementation seems to work:

private void compareRasterImages(BufferedImage expectedPngIo, BufferedImage actualPngIo) throws AssertionError {
    int minX = expectedPngIo.getMinX();
    int minY = expectedPngIo.getMinY();
    int maxX = expectedPngIo.getMinX() + expectedPngIo.getWidth();
    int maxY = expectedPngIo.getMinY()+ expectedPngIo.getHeight();

    assertEquals(minX, actualPngIo.getMinX()); 
    assertEquals(minY, actualPngIo.getMinY()); 
    assertEquals(expectedPngIo.getHeight(), actualPngIo.getHeight()); 
    assertEquals(expectedPngIo.getWidth(), actualPngIo.getWidth()); 
    for (int x_i = minX; x_i < maxX; x_i++){
        for (int y_i = minY; y_i < maxY; y_i++) {
            assertEquals(expectedPngIo.getRGB(x_i, y_i), actualPngIo.getRGB(x_i, y_i));
        }
    }
}

I retrieve the BufferedImage from my PNG (as byte[]) using ImageIO:

BufferedImage expectedPngIo = ImageIO.read(new ByteArrayInputStream(expectedPng));
enter code here
Individualist answered 23/6, 2014 at 10:58 Comment(0)
I
1

You could try testing for specific, known features of the output e.g.:

  • It there a white pixel at (100,100)?
  • It the border completely black?
  • Is the image the expected size?

And/or you could write tests for some "aggregate properties" that allow for some fuzziness in the results:

  • Does the image at least 90% match the reference image? (to allow for different fonts, antialiasing differences etc.)
  • Is the most common colour in the image equal to the background colour?
Indigenous answered 3/8, 2010 at 21:8 Comment(1)
Thanks. It looks like a good idea to test some features instead of bitmap pixels. As Vineet noticed, there could be also a difference in antialiasing.Houselights
P
1

You could read all the RGB values of the generated images into an array and compare that 2D-array against one representing a pre-generated image, if you are really specific about the complete image.

If you wish to ignore the fonts, you could do the same for the same for regions of the image that do not contain any variable data depending on the environment where the images are generated. Building in correction and normalization routines for unit tests would be a waste of time, unless the application is expected to generate images of such high accuracy as warranted.

Postprandial answered 3/8, 2010 at 21:21 Comment(1)
You can supply a font file to the unit test, and disable anti-aliasing for the test. In my experience this yields identical results even cross platform.Abridge
A
1

I've found this to be effective at making rendered fonts identical cross-platform for pixel perfect graphics unit tests of simple things like text overlaid on a static image.

  1. Inject the Font and FontRenderContext into the class rendering the fonts, so they can be controlled under test.
  2. Supply a .ttf file to the unit test and use Font.createFont() to generate a font from the file.
  3. In the unit test, disable anti-aliasing in the FontRenderContext used. If you skip this step, the results do vary cross platform it seems.

I'm curious if others think this is brittle or failure-prone for any reason, but I've had good results with it so far.

Abridge answered 19/11, 2016 at 20:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.