4

The following test fails on Java 9 while passes in Java 8:

@Test
public void getImage_SetValueUsingConstructor_ShouldReturnCorrectValue() throws Exception {
        String base64ImageString = "iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAIAAAAmkwkpAAAAEUlEQVR42mNgQAP/wQAbBw4ANwsL9Zo6V30AAAAASUVORK5CYII=";
    byte[] rawImageBytes = Base64.getDecoder().decode(base64ImageString);

    ByteArrayInputStream bis = new ByteArrayInputStream(rawImageBytes);
    RenderedImage image = ImageIO.read(bis);

    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ImageIO.write(image, "PNG", bos);
    byte[] imageBytesFromImage = bos.toByteArray();

    assertArrayEquals(imageBytesFromImage, rawImageBytes);
}

Java 9 output: arrays first differed at element [42]; Expected :94 Actual :-38

Can anyone help me understand what was changed in Java 9, and is there a way to write this code so that it will work for both Java 8 & 9?

AntonKam
  • 163
  • 11
  • Probably the representation of Strings? – Naman Oct 17 '17 at 12:56
  • @nullpointer It doesn't look like it, you get the some `rawImageBytes` on both versions – Jorn Vernee Oct 17 '17 at 13:07
  • 2
    From this discussion, the compression settings have changed in Java 9 PNG writer, so that might be why you're seeing different results from Java 8. https://github.com/gredler/jdk9-png-writer-backport – Nicholas Hirras Oct 17 '17 at 13:33
  • 6
    There never was a reason to expect the output to be bit-by-bit identical. The encoder could have added a `created with Java 8` comment which changed to `created with Java 9`, for example. Besides that, what’s the point of writing a test case for JRE code? That’s outside your responsibility. – Holger Oct 17 '17 at 13:49
  • @Holger, this test only represents a task that my program needs to perform: taking an image as a parameter, encoding it and sending it to some server, until now the encoded string sent to the server was identical to the base64 string the image was created from. – AntonKam Oct 18 '17 at 05:56
  • 3
    Of course, getting identical contents makes things easy, but not getting identical content is not a prove of incorrectness. A PNG file consists of chunks; not only can there be irrelevant information in optional (“non-critical”) chunks, the order of the chunks may be different without affecting the semantics. So the only reliable test is to decode the image and verify that the actual image content is the same. – Holger Oct 18 '17 at 06:51
  • what do you mean by decode the image? can you provide a code example? – AntonKam Oct 18 '17 at 07:20
  • 2
    @AntonKam "Decode image" is what you do when invoke `ImageIO.read(input)`. In the same sense, "encode image" can be done by `ImageIO.write(image, format, output)`. – Harald K Oct 18 '17 at 08:54

2 Answers2

2

As @Holger has pointed out in the comments, it is really the test that is flawed. While identical Base64 representations will give identical images, different Base64 representations does not mean the image data is different. It could mean only that the same image data is encoded differently, and will decode to the exact same image (which is the case here).

The reason your test used to pass without error, is probably that you used the Java 8 PNGImageWriter (or earlier, it hasn't really changed much since Java 1.4), which is the writer plugin used if you do ImageIO.write(image, "PNG", output), to encode the image and created the Base64 representation from it. If you had created the Base64 representation of the bytes from a file created by a different program/library, it would almost certainly be different.

You should rewrite your test, it is however not really clear to me what you are trying to test here.

If you only care about pixel data, you could just loop over the pixels and test for equality:

BufferedImage original = ImageIO.read(..);
BufferedImage current = ImageIO.read(..);

assertEquals(original.getWidth(), current.getWidth());
assertEquals(original.getHeight(), current.getHeight());

for (int y = 0; y < original.getHeight(); y++) {
    for (int x = 0; x < original.getWidth(); x++) {
        assertEquals(original.getRGB(x, y), current.getRGB(x, y));
    }
}

If you also need the metadata to be preserved, you also need to test for equality there. But PNG doesn't really contain much interesting metadata, so I doubt you need that.

Harald K
  • 26,314
  • 7
  • 65
  • 111
0

Thanks to Holger for the comments, what I did is to decode an image from the byte array and then compare the dataBuffer of both images.

The test below passed on both Java 8 and

@Test
public void imageTest() throws Exception {
    String base64ImageString = "iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAIAAAAmkwkpAAAAEUlEQVR42mNgQAP/wQAbBw4ANwsL9Zo6V30AAAAASUVORK5CYII=";
    byte[] rawImageBytes = Base64.getDecoder().decode(base64ImageString);

    ByteArrayInputStream bis = new ByteArrayInputStream(rawImageBytes);
    RenderedImage image = ImageIO.read(bis);

    ByteArrayOutputStream bos = new ByteArrayOutputStream();

    ImageIO.write(image, "PNG", bos);
    byte[] imageBytesFromImage = bos.toByteArray();

    //assertArrayEquals(imageBytesFromImage, rawImageBytes); //fails on Java 9!

    bis = new ByteArrayInputStream(imageBytesFromImage);
    RenderedImage image2 = ImageIO.read(bis);

    DataBuffer dbA = image.getData().getDataBuffer();
    int sizeA = dbA.getSize();
    DataBuffer dbB = image2.getData().getDataBuffer();
    int sizeB = dbB.getSize();
    // compare data-buffer objects //

    assertEquals(sizeA, sizeB);
    for (int i = 0; i < sizeA; i++) {
        assertEquals(dbA.getElem(i), dbB.getElem(i));

    }
}

The compare images code was taken from: How to compare images for similarity using java

AntonKam
  • 163
  • 11