Split up roundtrip test into separat test cases using the Theories runner to build up test input combinations. This also closes #22 from GitHub.

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/imaging/trunk@1750158 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/test/java/org/apache/commons/imaging/roundtrip/BitmapRoundtripTest.java b/src/test/java/org/apache/commons/imaging/roundtrip/BitmapRoundtripTest.java
new file mode 100644
index 0000000..6c0a049
--- /dev/null
+++ b/src/test/java/org/apache/commons/imaging/roundtrip/BitmapRoundtripTest.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.imaging.roundtrip;
+
+import java.awt.image.BufferedImage;
+
+import org.junit.experimental.theories.DataPoints;
+import org.junit.experimental.theories.Theories;
+import org.junit.experimental.theories.Theory;
+import org.junit.runner.RunWith;
+
+@RunWith(Theories.class)
+public class BitmapRoundtripTest extends RoundtripBase {
+
+    @DataPoints
+    public static BufferedImage[] images = new BufferedImage[] {
+            TestImages.createArgbBitmapImage(1, 1), // minimal
+            TestImages.createArgbBitmapImage(2, 2), //
+            TestImages.createArgbBitmapImage(10, 10), // larger than 8
+            TestImages.createArgbBitmapImage(300, 300), // larger than 256
+
+            TestImages.createBitmapBitmapImage(1, 1), // minimal
+            TestImages.createBitmapBitmapImage(2, 2), //
+            TestImages.createBitmapBitmapImage(10, 10), // larger than 8
+            TestImages.createBitmapBitmapImage(300, 300), // larger than 256
+    };
+
+    @DataPoints
+    public static FormatInfo[] formatInfos = FormatInfo.READ_WRITE_FORMATS;
+
+    @Theory
+    public void testBitmapRoundtrip(BufferedImage testImage, FormatInfo formatInfo) throws Exception {
+        roundtrip(formatInfo, testImage, "bitmap", true);
+    }
+}
diff --git a/src/test/java/org/apache/commons/imaging/roundtrip/FormatInfo.java b/src/test/java/org/apache/commons/imaging/roundtrip/FormatInfo.java
new file mode 100644
index 0000000..89aba8a
--- /dev/null
+++ b/src/test/java/org/apache/commons/imaging/roundtrip/FormatInfo.java
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.imaging.roundtrip;
+
+import org.apache.commons.imaging.ImageFormat;
+import org.apache.commons.imaging.ImageFormats;
+
+class FormatInfo {
+
+    static final int COLOR_FULL_RGB = 0;
+    static final int COLOR_LIMITED_INDEX = 1;
+    static final int COLOR_GRAYSCALE = 2;
+    static final int COLOR_BITMAP = 3;
+
+    static final FormatInfo[] PRESERVING_RESOLUTION_FORMATS = new FormatInfo[] {
+            new FormatInfo(ImageFormats.PNG, true, true,
+                    COLOR_FULL_RGB, true, true), //
+            new FormatInfo(ImageFormats.ICO, true, true,
+                    COLOR_FULL_RGB, true, true), //
+            new FormatInfo(ImageFormats.TIFF, true, true,
+                    COLOR_FULL_RGB, true, true), //
+            new FormatInfo(ImageFormats.BMP, true, true,
+                    COLOR_FULL_RGB, true, true), //
+            new FormatInfo(ImageFormats.PCX, true, true,
+                    COLOR_FULL_RGB, true, true), //
+            new FormatInfo(ImageFormats.DCX, true, true,
+                    COLOR_FULL_RGB, true, true), //
+    };
+
+    static FormatInfo[] READ_WRITE_FORMATS = new FormatInfo[] {
+            new FormatInfo(ImageFormats.PNG, true, true,
+                    COLOR_FULL_RGB, true, true), //
+            new FormatInfo(ImageFormats.GIF, true, true,
+                    COLOR_LIMITED_INDEX, true, false), //
+            new FormatInfo(ImageFormats.ICO, true, true,
+                    COLOR_FULL_RGB, true, true), //
+            new FormatInfo(ImageFormats.TIFF, true, true,
+                    COLOR_FULL_RGB, true, true), //
+            new FormatInfo(ImageFormats.BMP, true, true,
+                    COLOR_FULL_RGB, true, true), //
+            new FormatInfo(ImageFormats.PBM, true, true,
+                    COLOR_BITMAP, true, false), //
+            new FormatInfo(ImageFormats.PGM, true, true,
+                    COLOR_GRAYSCALE, true, false), //
+            new FormatInfo(ImageFormats.PPM, true, true,
+                    COLOR_FULL_RGB, true, false), //
+            new FormatInfo(ImageFormats.PAM, true, true,
+                    COLOR_FULL_RGB, true, false),//
+            new FormatInfo(ImageFormats.WBMP, true, true,
+                    COLOR_BITMAP, true, false), //
+            new FormatInfo(ImageFormats.PCX, true, true,
+                    COLOR_FULL_RGB, true, true), //
+            new FormatInfo(ImageFormats.DCX, true, true,
+                    COLOR_FULL_RGB, true, true), //
+            new FormatInfo(ImageFormats.XBM, true, true,
+                    COLOR_BITMAP, false, false), //
+            new FormatInfo(ImageFormats.XPM, true, true,
+                    COLOR_FULL_RGB, false, false), //
+    };
+
+    static final FormatInfo[] ALL_FORMATS = { //
+            new FormatInfo(ImageFormats.PNG, true, true,
+                    COLOR_FULL_RGB, true, true), //
+            new FormatInfo(ImageFormats.GIF, true, true,
+                    COLOR_LIMITED_INDEX, true, false), //
+            new FormatInfo(ImageFormats.ICO, true, true,
+                    COLOR_FULL_RGB, true, true), //
+            new FormatInfo(ImageFormats.TIFF, true, true,
+                    COLOR_FULL_RGB, true, true), //
+            new FormatInfo(ImageFormats.JPEG, true, false,
+                    COLOR_FULL_RGB, true, true), //
+            new FormatInfo(ImageFormats.BMP, true, true,
+                    COLOR_FULL_RGB, true, true), //
+            new FormatInfo(ImageFormats.PSD, true, false,
+                    COLOR_FULL_RGB, true, true), //
+            new FormatInfo(ImageFormats.PBM, true, true,
+                    COLOR_BITMAP, true, false), //
+            new FormatInfo(ImageFormats.PGM, true, true,
+                    COLOR_GRAYSCALE, true, false), //
+            new FormatInfo(ImageFormats.PPM, true, true,
+                    COLOR_FULL_RGB, true, false), //
+            new FormatInfo(ImageFormats.PAM, true, true,
+                    COLOR_FULL_RGB, true, false),//
+            new FormatInfo(ImageFormats.PNM, true, true,
+                    COLOR_FULL_RGB, true, false), //
+            new FormatInfo(ImageFormats.TGA, false, false,
+                    COLOR_FULL_RGB, true, true), //
+            new FormatInfo(ImageFormats.WBMP, true, true,
+                    COLOR_BITMAP, true, false), //
+            new FormatInfo(ImageFormats.PCX, true, true,
+                    COLOR_FULL_RGB, true, true), //
+            new FormatInfo(ImageFormats.DCX, true, true,
+                    COLOR_FULL_RGB, true, true), //
+            new FormatInfo(ImageFormats.XBM, true, true,
+                    COLOR_BITMAP, false, false), //
+            new FormatInfo(ImageFormats.XPM, true, true,
+                    COLOR_FULL_RGB, false, false), //
+    };
+
+    final ImageFormat format;
+    final boolean canRead;
+    final boolean canWrite;
+    final int colorSupport;
+    final boolean identicalSecondWrite;
+    final boolean preservesResolution;
+
+    FormatInfo(final ImageFormat format, final boolean canRead,
+                      final boolean canWrite, final int colorSupport,
+                      final boolean identicalSecondWrite,
+                      final boolean preservesResolution) {
+        this.canRead = canRead;
+        this.canWrite = canWrite;
+        this.colorSupport = colorSupport;
+        this.format = format;
+        this.identicalSecondWrite = identicalSecondWrite;
+        this.preservesResolution = preservesResolution;
+    }
+}
diff --git a/src/test/java/org/apache/commons/imaging/roundtrip/FullColorRoundtrip.java b/src/test/java/org/apache/commons/imaging/roundtrip/FullColorRoundtrip.java
new file mode 100644
index 0000000..cf75c14
--- /dev/null
+++ b/src/test/java/org/apache/commons/imaging/roundtrip/FullColorRoundtrip.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.imaging.roundtrip;
+
+import java.awt.image.BufferedImage;
+
+import org.junit.experimental.theories.DataPoints;
+import org.junit.experimental.theories.Theories;
+import org.junit.experimental.theories.Theory;
+import org.junit.runner.RunWith;
+
+@RunWith(Theories.class)
+public class FullColorRoundtrip extends RoundtripBase {
+
+    @DataPoints
+    public static BufferedImage[] images = new BufferedImage[]{
+            TestImages.createFullColorImage(1, 1), // minimal
+            TestImages.createFullColorImage(2, 2), //
+            TestImages.createFullColorImage(10, 10), // larger than 8
+            TestImages.createFullColorImage(300, 300), // larger than 256
+    };
+
+    @DataPoints
+    public static FormatInfo[] formatInfos = FormatInfo.READ_WRITE_FORMATS;
+
+    @Theory
+    public void testFullColorRoundtrip(final BufferedImage testImage, final FormatInfo formatInfo) throws Exception {
+        boolean imageExact = true;
+        if (formatInfo.colorSupport == FormatInfo.COLOR_BITMAP) {
+            imageExact = false;
+        }
+        if (formatInfo.colorSupport == FormatInfo.COLOR_GRAYSCALE) {
+            imageExact = false;
+        }
+        if (formatInfo.colorSupport == FormatInfo.COLOR_LIMITED_INDEX) {
+            imageExact = false;
+        }
+
+        roundtrip(formatInfo, testImage, "fullColor", imageExact);
+    }
+}
diff --git a/src/test/java/org/apache/commons/imaging/roundtrip/GrayscaleRountripTest.java b/src/test/java/org/apache/commons/imaging/roundtrip/GrayscaleRountripTest.java
new file mode 100644
index 0000000..f5de332
--- /dev/null
+++ b/src/test/java/org/apache/commons/imaging/roundtrip/GrayscaleRountripTest.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.imaging.roundtrip;
+
+import java.awt.image.BufferedImage;
+
+import org.junit.experimental.theories.DataPoints;
+import org.junit.experimental.theories.Theories;
+import org.junit.experimental.theories.Theory;
+import org.junit.runner.RunWith;
+
+@RunWith(Theories.class)
+public class GrayscaleRountripTest extends RoundtripBase {
+
+    @DataPoints
+    public static BufferedImage[] images = new BufferedImage[]{
+            TestImages.createArgbBitmapImage(1, 1), // minimal
+            TestImages.createArgbGrayscaleImage(2, 2), //
+            TestImages.createArgbGrayscaleImage(10, 10), // larger than 8
+            TestImages.createArgbGrayscaleImage(300, 300), // larger than 256
+
+            TestImages.createGrayscaleGrayscaleImage(1, 1), // minimal
+            TestImages.createGrayscaleGrayscaleImage(2, 2), //
+            TestImages.createGrayscaleGrayscaleImage(10, 10), // larger than 8
+            TestImages.createGrayscaleGrayscaleImage(300, 300), // larger than 256
+    };
+
+    @DataPoints
+    public static FormatInfo[] formatInfos = FormatInfo.READ_WRITE_FORMATS;
+
+    @Theory
+    public void testGrayscaleRoundtrip(final BufferedImage testImage, final FormatInfo formatInfo) throws Exception {
+            boolean imageExact = formatInfo.colorSupport != FormatInfo.COLOR_BITMAP;
+
+            roundtrip(formatInfo, testImage, "gray", imageExact);
+    }
+}
diff --git a/src/test/java/org/apache/commons/imaging/roundtrip/ImageAsserts.java b/src/test/java/org/apache/commons/imaging/roundtrip/ImageAsserts.java
new file mode 100644
index 0000000..eeabf90
--- /dev/null
+++ b/src/test/java/org/apache/commons/imaging/roundtrip/ImageAsserts.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.imaging.roundtrip;
+
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.commons.imaging.util.Debug;
+import org.apache.commons.io.FileUtils;
+import org.junit.Assert;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+final class ImageAsserts {
+
+    private ImageAsserts() {
+    }
+    
+    static void assertEquals(final BufferedImage a, final BufferedImage b) {
+        assertEquals(a, b, 0);
+    }
+
+    static void assertEquals(final BufferedImage a, final BufferedImage b, final int tolerance) {
+        Assert.assertEquals(a.getWidth(), b.getWidth());
+        Assert.assertEquals(a.getHeight(), b.getHeight());
+
+        for (int x = 0; x < a.getWidth(); x++) {
+            for (int y = 0; y < a.getHeight(); y++) {
+                final int a_argb = a.getRGB(x, y);
+                final int b_argb = b.getRGB(x, y);
+                if (a_argb != b_argb) {
+                    if (calculateARGBDistance(a_argb, b_argb) <= tolerance) {
+                        continue; // ignore.
+                    }
+                }
+                if (a_argb != b_argb) {
+                    Debug.debug("width: " + a.getWidth());
+                    Debug.debug("height: " + a.getHeight());
+                    Debug.debug("distance: " + calculateARGBDistance(a_argb, b_argb));
+                    Debug.debug("x: " + x);
+                    Debug.debug("y: " + y);
+                    Debug.debug("a_argb: " + a_argb + " (0x" + Integer.toHexString(a_argb) + ")");
+                    Debug.debug("b_argb: " + b_argb + " (0x" + Integer.toHexString(b_argb) + ")");
+                }
+                Assert.assertEquals(a_argb, b_argb);
+            }
+        }
+    }
+
+    static int calculateARGBDistance(final int a, final int b) {
+        final int aAlpha = 0xff & (a >> 24);
+        final int aRed = 0xff & (a >> 16);
+        final int aGreen = 0xff & (a >> 8);
+        final int aBlue = 0xff & (a >> 0);
+        final int bAlpha = 0xff & (b >> 24);
+        final int bRed = 0xff & (b >> 16);
+        final int bGreen = 0xff & (b >> 8);
+        final int bBlue = 0xff & (b >> 0);
+        final int diff = Math.abs(aAlpha - bAlpha) + Math.abs(aRed - bRed)
+                + Math.abs(aGreen - bGreen) + Math.abs(aBlue - bBlue);
+        return diff;
+
+    }
+
+    static void assertEquals(final File a, final File b) throws IOException {
+        assertTrue(a.exists() && a.isFile());
+        assertTrue(b.exists() && b.isFile());
+        Assert.assertEquals(a.length(), b.length());
+
+        final byte aData[] = FileUtils.readFileToByteArray(a);
+        final byte bData[] = FileUtils.readFileToByteArray(b);
+
+        for (int i = 0; i < a.length(); i++) {
+            final int aByte = 0xff & aData[i];
+            final int bByte = 0xff & bData[i];
+
+            if (aByte != bByte) {
+                Debug.debug("a: " + a);
+                Debug.debug("b: " + b);
+                Debug.debug("i: " + i);
+                Debug.debug("aByte: " + aByte + " (0x" + Integer.toHexString(aByte) + ")");
+                Debug.debug("bByte: " + bByte + " (0x" + Integer.toHexString(bByte) + ")");
+            }
+            Assert.assertEquals(aByte, bByte);
+        }
+    }
+}
diff --git a/src/test/java/org/apache/commons/imaging/roundtrip/LimitedColorRoundtripTest.java b/src/test/java/org/apache/commons/imaging/roundtrip/LimitedColorRoundtripTest.java
new file mode 100644
index 0000000..db2e5be
--- /dev/null
+++ b/src/test/java/org/apache/commons/imaging/roundtrip/LimitedColorRoundtripTest.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.imaging.roundtrip;
+
+import java.awt.image.BufferedImage;
+
+import org.junit.experimental.theories.DataPoints;
+import org.junit.experimental.theories.Theories;
+import org.junit.experimental.theories.Theory;
+import org.junit.runner.RunWith;
+
+@RunWith(Theories.class)
+public class LimitedColorRoundtripTest extends RoundtripBase {
+
+    @DataPoints
+    public static BufferedImage[] images = new BufferedImage[] {
+            TestImages.createLimitedColorImage(1, 1), // minimal
+            TestImages.createLimitedColorImage(2, 2), //
+            TestImages.createLimitedColorImage(10, 10), // larger than 8
+            TestImages.createLimitedColorImage(300, 300), // larger than 256
+    };
+
+    @DataPoints
+    public static FormatInfo[] formatInfos = FormatInfo.READ_WRITE_FORMATS;
+
+    @Theory
+    public void testLimitedColorRoundtrip(final BufferedImage testImage, final FormatInfo formatInfo) throws Exception {
+            boolean imageExact = true;
+            if (formatInfo.colorSupport == FormatInfo.COLOR_BITMAP) {
+                imageExact = false;
+            }
+            if (formatInfo.colorSupport == FormatInfo.COLOR_GRAYSCALE) {
+                imageExact = false;
+            }
+
+            roundtrip(formatInfo, testImage, "indexable", imageExact);
+    }
+
+}
diff --git a/src/test/java/org/apache/commons/imaging/roundtrip/NullParametersRoundtripTest.java b/src/test/java/org/apache/commons/imaging/roundtrip/NullParametersRoundtripTest.java
new file mode 100644
index 0000000..c5aad6a
--- /dev/null
+++ b/src/test/java/org/apache/commons/imaging/roundtrip/NullParametersRoundtripTest.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.imaging.roundtrip;
+
+import java.awt.image.BufferedImage;
+import java.io.File;
+
+import org.apache.commons.imaging.Imaging;
+import org.junit.experimental.theories.DataPoints;
+import org.junit.experimental.theories.Theories;
+import org.junit.experimental.theories.Theory;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.assertNotNull;
+
+@RunWith(Theories.class)
+public class NullParametersRoundtripTest extends RoundtripBase {
+
+    @DataPoints
+    public static FormatInfo[] formatInfos = FormatInfo.READ_WRITE_FORMATS;
+
+    @Theory
+    public void testNullParametersRoundtrip(final FormatInfo formatInfo) throws Exception {
+        final BufferedImage testImage = TestImages.createFullColorImage(1, 1);
+        final File temp1 = createTempFile("nullParameters.", "." + formatInfo.format.getExtension());
+        Imaging.writeImage(testImage, temp1, formatInfo.format, null);
+        Imaging.getImageInfo(temp1, null);
+        Imaging.getImageSize(temp1, null);
+        Imaging.getMetadata(temp1, null);
+        Imaging.getICCProfile(temp1, null);
+        BufferedImage imageRead = Imaging.getBufferedImage(temp1, null);
+
+        assertNotNull(imageRead);
+    }
+
+}
diff --git a/src/test/java/org/apache/commons/imaging/roundtrip/PixelDensityRoundtrip.java b/src/test/java/org/apache/commons/imaging/roundtrip/PixelDensityRoundtrip.java
new file mode 100644
index 0000000..96c6e8a
--- /dev/null
+++ b/src/test/java/org/apache/commons/imaging/roundtrip/PixelDensityRoundtrip.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.imaging.roundtrip;
+
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.imaging.ImageInfo;
+import org.apache.commons.imaging.Imaging;
+import org.apache.commons.imaging.ImagingConstants;
+import org.apache.commons.imaging.PixelDensity;
+import org.junit.experimental.theories.DataPoints;
+import org.junit.experimental.theories.Theories;
+import org.junit.experimental.theories.Theory;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.assertTrue;
+
+@RunWith(Theories.class)
+public class PixelDensityRoundtrip extends RoundtripBase {
+
+    @DataPoints
+    public static FormatInfo[] formatInfos = FormatInfo.PRESERVING_RESOLUTION_FORMATS;
+
+    @Theory
+    public void testPixelDensityRoundtrip(final FormatInfo formatInfo) throws Exception {
+        final BufferedImage testImage = TestImages.createFullColorImage(2, 2);
+
+        final File temp1 = createTempFile("pixeldensity.", "."
+                + formatInfo.format.getExtension());
+
+        final Map<String, Object> params = new HashMap<>();
+        final PixelDensity pixelDensity = PixelDensity.createFromPixelsPerInch(75, 150);
+        params.put(ImagingConstants.PARAM_KEY_PIXEL_DENSITY, pixelDensity);
+        Imaging.writeImage(testImage, temp1, formatInfo.format, params);
+
+        final ImageInfo imageInfo = Imaging.getImageInfo(temp1);
+        if (imageInfo != null) {
+            final int xReadDPI = imageInfo.getPhysicalWidthDpi();
+            final int yReadDPI = imageInfo.getPhysicalHeightDpi();
+            // allow a 5% margin of error in storage and conversion
+            assertTrue("horizontal pixel density stored wrongly for " + formatInfo.format +
+                            " in=" + pixelDensity.horizontalDensityInches() + ", out=" + xReadDPI,
+                    Math.abs((xReadDPI - pixelDensity.horizontalDensityInches()) /
+                            pixelDensity.horizontalDensityInches()) <= 0.05);
+            assertTrue("vertical pixel density stored wrongly for " + formatInfo.format +
+                            " in=" + pixelDensity.verticalDensityInches() + ", out=" + yReadDPI,
+                    Math.abs((yReadDPI - pixelDensity.verticalDensityInches()) /
+                            pixelDensity.verticalDensityInches()) <= 0.05);
+        }
+    }
+
+}
diff --git a/src/test/java/org/apache/commons/imaging/roundtrip/RoundtripBase.java b/src/test/java/org/apache/commons/imaging/roundtrip/RoundtripBase.java
new file mode 100644
index 0000000..5b39c25
--- /dev/null
+++ b/src/test/java/org/apache/commons/imaging/roundtrip/RoundtripBase.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.imaging.roundtrip;
+
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.imaging.ImageReadException;
+import org.apache.commons.imaging.ImageWriteException;
+import org.apache.commons.imaging.Imaging;
+import org.apache.commons.imaging.ImagingConstants;
+import org.apache.commons.imaging.common.RgbBufferedImageFactory;
+import org.apache.commons.imaging.util.Debug;
+import org.junit.Rule;
+import org.junit.rules.TemporaryFolder;
+
+import static org.junit.Assert.assertNotNull;
+
+public class RoundtripBase {
+
+    @Rule
+    public TemporaryFolder folder = new TemporaryFolder();
+
+    protected void roundtrip(final FormatInfo formatInfo, final BufferedImage testImage,
+                             final String tempPrefix, final boolean imageExact) throws IOException,
+            ImageReadException, ImageWriteException {
+        final File temp1 = createTempFile(tempPrefix + ".", "."
+                + formatInfo.format.getExtension());
+        Debug.debug("tempFile: " + temp1.getName());
+
+        final Map<String, Object> params = new HashMap<>();
+        Imaging.writeImage(testImage, temp1, formatInfo.format, params);
+
+        final Map<String, Object> readParams = new HashMap<>();
+        readParams.put(ImagingConstants.BUFFERED_IMAGE_FACTORY,
+                new RgbBufferedImageFactory());
+        final BufferedImage image2 = Imaging.getBufferedImage(temp1, readParams);
+        assertNotNull(image2);
+
+        if (imageExact) {
+            // note tolerance when comparing grayscale images
+            // BufferedImages of
+            ImageAsserts.assertEquals(testImage, image2);
+        }
+
+        if (formatInfo.identicalSecondWrite) {
+            final File temp2 = createTempFile(tempPrefix + ".", "."
+                    + formatInfo.format.getExtension());
+            // Debug.debug("tempFile: " + tempFile.getName());
+            Imaging.writeImage(image2, temp2, formatInfo.format, params);
+
+            ImageAsserts.assertEquals(temp1, temp2);
+        }
+    }
+
+    protected File createTempFile(final String prefix, final String suffix)
+            throws IOException {
+        return File.createTempFile(prefix, suffix, folder.newFolder());
+    }
+
+}
diff --git a/src/test/java/org/apache/commons/imaging/roundtrip/RoundtripTest.java b/src/test/java/org/apache/commons/imaging/roundtrip/RoundtripTest.java
deleted file mode 100644
index c5c78d3..0000000
--- a/src/test/java/org/apache/commons/imaging/roundtrip/RoundtripTest.java
+++ /dev/null
@@ -1,487 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.commons.imaging.roundtrip;
-
-import java.awt.image.BufferedImage;
-import java.io.File;
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.apache.commons.imaging.ImageFormat;
-import org.apache.commons.imaging.ImageFormats;
-import org.apache.commons.imaging.ImageInfo;
-import org.apache.commons.imaging.ImageReadException;
-import org.apache.commons.imaging.ImageWriteException;
-import org.apache.commons.imaging.Imaging;
-import org.apache.commons.imaging.ImagingConstants;
-import org.apache.commons.imaging.ImagingTest;
-import org.apache.commons.imaging.PixelDensity;
-import org.apache.commons.imaging.common.RgbBufferedImageFactory;
-import org.apache.commons.imaging.util.Debug;
-import org.apache.commons.io.FileUtils;
-import org.junit.Test;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-public class RoundtripTest extends ImagingTest {
-    private static final int COLOR_FULL_RGB = 0;
-    private static final int COLOR_LIMITED_INDEX = 1;
-    private static final int COLOR_GRAYSCALE = 2;
-    private static final int COLOR_BITMAP = 3;
-
-    private static class FormatInfo {
-
-        public final ImageFormat format;
-        public final boolean canRead;
-        public final boolean canWrite;
-        public final int colorSupport;
-        public final boolean identicalSecondWrite;
-        public final boolean preservesResolution;
-
-        public FormatInfo(final ImageFormat format, final boolean canRead,
-                          final boolean canWrite, final int colorSupport,
-                          final boolean identicalSecondWrite,
-                          final boolean preservesResolution) {
-            this.canRead = canRead;
-            this.canWrite = canWrite;
-            this.colorSupport = colorSupport;
-            this.format = format;
-            this.identicalSecondWrite = identicalSecondWrite;
-            this.preservesResolution = preservesResolution;
-        }
-    }
-
-    private static final FormatInfo FORMAT_INFOS[] = { //
-            new FormatInfo(ImageFormats.PNG, true, true,
-                    COLOR_FULL_RGB, true, true), //
-            new FormatInfo(ImageFormats.GIF, true, true,
-                    COLOR_LIMITED_INDEX, true, false), //
-            new FormatInfo(ImageFormats.ICO, true, true,
-                    COLOR_FULL_RGB, true, true), //
-            new FormatInfo(ImageFormats.TIFF, true, true,
-                    COLOR_FULL_RGB, true, true), //
-            new FormatInfo(ImageFormats.JPEG, true, false,
-                    COLOR_FULL_RGB, true, true), //
-            new FormatInfo(ImageFormats.BMP, true, true,
-                    COLOR_FULL_RGB, true, true), //
-            new FormatInfo(ImageFormats.PSD, true, false,
-                    COLOR_FULL_RGB, true, true), //
-            new FormatInfo(ImageFormats.PBM, true, true,
-                    COLOR_BITMAP, true, false), //
-            new FormatInfo(ImageFormats.PGM, true, true,
-                    COLOR_GRAYSCALE, true, false), //
-            new FormatInfo(ImageFormats.PPM, true, true,
-                    COLOR_FULL_RGB, true, false), //
-            new FormatInfo(ImageFormats.PAM, true, true,
-                    COLOR_FULL_RGB, true, false),//
-            // new FormatInfo(ImageFormat.IMAGE_FORMAT_PNM, true, true,
-            // COLOR_FULL_RGB, true), //
-            new FormatInfo(ImageFormats.TGA, false, false,
-                    COLOR_FULL_RGB, true, true), //
-            new FormatInfo(ImageFormats.WBMP, true, true,
-                    COLOR_BITMAP, true, false), //
-            new FormatInfo(ImageFormats.PCX, true, true,
-                    COLOR_FULL_RGB, true, true), //
-            new FormatInfo(ImageFormats.DCX, true, true,
-                    COLOR_FULL_RGB, true, true), //
-            new FormatInfo(ImageFormats.XBM, true, true,
-                    COLOR_BITMAP, false, false), //
-            new FormatInfo(ImageFormats.XPM, true, true,
-                    COLOR_FULL_RGB, false, false), //
-    };
-
-    private BufferedImage createArgbBitmapImage(final int width, final int height) {
-        final BufferedImage result = new BufferedImage(width, height,
-                BufferedImage.TYPE_INT_ARGB);
-        for (int x = 0; x < width; x++) {
-            for (int y = 0; y < height; y++) {
-                // alternating black and white.
-                final int modulator = y + 2; // make sure lines vary.
-                final int argb = (x + y) % modulator == 0 ? 0xff000000 : 0xffffffff;
-                result.setRGB(x, y, argb);
-            }
-        }
-        return result;
-    }
-
-    private BufferedImage createBitmapBitmapImage(final int width, final int height) {
-        final BufferedImage result = new BufferedImage(width, height,
-                BufferedImage.TYPE_BYTE_BINARY);
-        for (int x = 0; x < width; x++) {
-            for (int y = 0; y < height; y++) {
-                // alternating black and white.
-                final int modulator = y + 2; // make sure lines vary.
-                final int argb = (x + y) % modulator == 0 ? 0xff000000 : 0xffffffff;
-                result.setRGB(x, y, argb);
-            }
-        }
-        return result;
-    }
-
-    private BufferedImage createArgbGrayscaleImage(final int width, final int height) {
-        final BufferedImage result = new BufferedImage(width, height,
-                BufferedImage.TYPE_INT_ARGB);
-        for (int x = 0; x < width; x++) {
-            for (int y = 0; y < height; y++) {
-                final int value = (256 * (x + y)) / (width + height);
-                final int argb = (0xff << 24) | (value << 16) | (value << 8)
-                        | (value << 0);
-
-                result.setRGB(x, y, argb);
-            }
-        }
-        return result;
-    }
-
-    private BufferedImage createGrayscaleGrayscaleImage(final int width, final int height) {
-        final BufferedImage result = new BufferedImage(width, height,
-                BufferedImage.TYPE_BYTE_GRAY);
-        for (int x = 0; x < width; x++) {
-            for (int y = 0; y < height; y++) {
-                final int value = (256 * (x + y)) / (width + height);
-                final int argb = (0xff << 24) | (value << 16) | (value << 8)
-                        | (value << 0);
-
-                result.setRGB(x, y, argb);
-            }
-        }
-        return result;
-    }
-
-    private BufferedImage createLimitedColorImage(final int width, final int height) {
-        final int colors[] = {0xffffffff, 0xff000000, 0xfff00000, 0xff0000ff,
-                0xff123456, 0xfffefeff, 0xff7f817f,};
-
-        final BufferedImage result = new BufferedImage(width, height,
-                BufferedImage.TYPE_INT_ARGB);
-        for (int x = 0; x < width; x++) {
-            for (int y = 0; y < height; y++) {
-                final int argb = colors[(x + y) % colors.length];
-                result.setRGB(x, y, argb);
-            }
-        }
-        return result;
-    }
-
-    private BufferedImage createFullColorImage(final int width, final int height) {
-        final BufferedImage result = new BufferedImage(width, height,
-                BufferedImage.TYPE_INT_ARGB);
-        for (int x = 0; x < width; x++) {
-            for (int y = 0; y < height; y++) {
-                final int red = (x * 255) / width;
-                final int green = (y * 255) / height;
-                final int blue = ((x + y) * 255) / (width + height);
-                final int argb = (0xff << 24) | (red << 16) | (green << 8)
-                        | (blue << 0);
-                result.setRGB(x, y, argb);
-            }
-        }
-        return result;
-    }
-
-    private void compareImagesExact(final BufferedImage a, final BufferedImage b) {
-        compareImages(a, b, 0);
-    }
-
-    private void compareImages(final BufferedImage a, final BufferedImage b, final int tolerance) {
-        assertEquals(a.getWidth(), b.getWidth());
-        assertEquals(a.getHeight(), b.getHeight());
-
-        for (int x = 0; x < a.getWidth(); x++) {
-            for (int y = 0; y < a.getHeight(); y++) {
-                final int a_argb = a.getRGB(x, y);
-                final int b_argb = b.getRGB(x, y);
-                if (a_argb != b_argb) {
-                    if (calculateARGBDistance(a_argb, b_argb) <= tolerance) {
-                        continue; // ignore.
-                    }
-                }
-                if (a_argb != b_argb) {
-                    Debug.debug("width: " + a.getWidth());
-                    Debug.debug("height: " + a.getHeight());
-                    Debug.debug("distance: " + calculateARGBDistance(a_argb, b_argb));
-                    Debug.debug("x: " + x);
-                    Debug.debug("y: " + y);
-                    Debug.debug("a_argb: " + a_argb + " (0x" + Integer.toHexString(a_argb) + ")");
-                    Debug.debug("b_argb: " + b_argb + " (0x" + Integer.toHexString(b_argb) + ")");
-                }
-                assertEquals(a_argb, b_argb);
-            }
-        }
-    }
-
-    private int calculateARGBDistance(final int a, final int b) {
-        final int aAlpha = 0xff & (a >> 24);
-        final int aRed = 0xff & (a >> 16);
-        final int aGreen = 0xff & (a >> 8);
-        final int aBlue = 0xff & (a >> 0);
-        final int bAlpha = 0xff & (b >> 24);
-        final int bRed = 0xff & (b >> 16);
-        final int bGreen = 0xff & (b >> 8);
-        final int bBlue = 0xff & (b >> 0);
-        final int diff = Math.abs(aAlpha - bAlpha) + Math.abs(aRed - bRed)
-                + Math.abs(aGreen - bGreen) + Math.abs(aBlue - bBlue);
-        return diff;
-
-    }
-
-    private void compareFilesExact(final File a, final File b) throws IOException {
-        assertTrue(a.exists() && a.isFile());
-        assertTrue(b.exists() && b.isFile());
-        assertEquals(a.length(), b.length());
-
-        final byte aData[] = FileUtils.readFileToByteArray(a);
-        final byte bData[] = FileUtils.readFileToByteArray(b);
-
-        for (int i = 0; i < a.length(); i++) {
-            final int aByte = 0xff & aData[i];
-            final int bByte = 0xff & bData[i];
-
-            if (aByte != bByte) {
-                Debug.debug("a: " + a);
-                Debug.debug("b: " + b);
-                Debug.debug("i: " + i);
-                Debug.debug("aByte: " + aByte + " (0x" + Integer.toHexString(aByte) + ")");
-                Debug.debug("bByte: " + bByte + " (0x" + Integer.toHexString(bByte) + ")");
-            }
-            assertEquals(aByte, bByte);
-        }
-    }
-
-    @Test
-    public void testBitmapRoundtrip() throws Exception {
-        final BufferedImage testImages[] = { //
-
-                createArgbBitmapImage(1, 1), // minimal
-                createArgbBitmapImage(2, 2), //
-                createArgbBitmapImage(10, 10), // larger than 8
-                createArgbBitmapImage(300, 300), // larger than 256
-
-                createBitmapBitmapImage(1, 1), // minimal
-                createBitmapBitmapImage(2, 2), //
-                createBitmapBitmapImage(10, 10), // larger than 8
-                createBitmapBitmapImage(300, 300), // larger than 256
-        };
-
-        for (final BufferedImage testImage : testImages) {
-            for (final FormatInfo element : FORMAT_INFOS) {
-                final FormatInfo formatInfo = element;
-                if ((!formatInfo.canRead) || (!formatInfo.canWrite)) {
-                    continue;
-                }
-
-                Debug.debug("bitmap test: " + formatInfo.format.getName());
-
-                roundtrip(formatInfo, testImage, "bitmap", true);
-            }
-        }
-    }
-
-    @Test
-    public void testGrayscaleRoundtrip() throws Exception {
-        final BufferedImage testImages[] = { //
-
-                createArgbBitmapImage(1, 1), // minimal
-                createArgbGrayscaleImage(2, 2), //
-                createArgbGrayscaleImage(10, 10), // larger than 8
-                createArgbGrayscaleImage(300, 300), // larger than 256
-
-                createGrayscaleGrayscaleImage(1, 1), // minimal
-                createGrayscaleGrayscaleImage(2, 2), //
-                createGrayscaleGrayscaleImage(10, 10), // larger than 8
-                createGrayscaleGrayscaleImage(300, 300), // larger than 256
-        };
-
-        for (final BufferedImage testImage : testImages) {
-            for (final FormatInfo element : FORMAT_INFOS) {
-                final FormatInfo formatInfo = element;
-                if ((!formatInfo.canRead) || (!formatInfo.canWrite)) {
-                    continue;
-                }
-
-                Debug.debug("grayscale test: " + formatInfo.format.getName());
-
-                boolean imageExact = true;
-                if (formatInfo.colorSupport == COLOR_BITMAP) {
-                    imageExact = false;
-                }
-
-                roundtrip(formatInfo, testImage, "gray", imageExact);
-            }
-        }
-    }
-
-    @Test
-    public void testLimitedColorRoundtrip() throws Exception {
-        final BufferedImage testImages[] = { //
-
-                createLimitedColorImage(1, 1), // minimal
-                createLimitedColorImage(2, 2), //
-                createLimitedColorImage(10, 10), // larger than 8
-                createLimitedColorImage(300, 300), // larger than 256
-        };
-
-        for (final BufferedImage testImage : testImages) {
-            for (final FormatInfo element : FORMAT_INFOS) {
-                final FormatInfo formatInfo = element;
-                if ((!formatInfo.canRead) || (!formatInfo.canWrite)) {
-                    continue;
-                }
-
-                Debug.debug("indexable test: " + formatInfo.format.getName());
-
-                boolean imageExact = true;
-                if (formatInfo.colorSupport == COLOR_BITMAP) {
-                    imageExact = false;
-                }
-                if (formatInfo.colorSupport == COLOR_GRAYSCALE) {
-                    imageExact = false;
-                }
-
-                roundtrip(formatInfo, testImage, "indexable", imageExact);
-            }
-        }
-    }
-
-    @Test
-    public void testFullColorRoundtrip() throws Exception {
-        final BufferedImage testImages[] = { //
-
-                createFullColorImage(1, 1), // minimal
-                createFullColorImage(2, 2), //
-                createFullColorImage(10, 10), // larger than 8
-                createFullColorImage(300, 300), // larger than 256
-        };
-
-        for (final BufferedImage testImage : testImages) {
-            for (final FormatInfo element : FORMAT_INFOS) {
-                final FormatInfo formatInfo = element;
-                if ((!formatInfo.canRead) || (!formatInfo.canWrite)) {
-                    continue;
-                }
-
-                Debug.debug("fullColor test: " + formatInfo.format.getName());
-
-                boolean imageExact = true;
-                if (formatInfo.colorSupport == COLOR_BITMAP) {
-                    imageExact = false;
-                }
-                if (formatInfo.colorSupport == COLOR_GRAYSCALE) {
-                    imageExact = false;
-                }
-                if (formatInfo.colorSupport == COLOR_LIMITED_INDEX) {
-                    imageExact = false;
-                }
-
-                roundtrip(formatInfo, testImage, "fullColor", imageExact);
-            }
-        }
-    }
-
-    @Test
-    public void testPixelDensityRoundtrip() throws IOException,
-            ImageReadException, ImageWriteException {
-        final BufferedImage testImage = createFullColorImage(2, 2);
-        for (final FormatInfo formatInfo : FORMAT_INFOS) {
-            if (!formatInfo.canRead || !formatInfo.canWrite || !formatInfo.preservesResolution) {
-                continue;
-            }
-
-            Debug.debug("pixel density test: " + formatInfo.format.getName());
-
-            final File temp1 = createTempFile("pixeldensity.", "."
-                    + formatInfo.format.getExtension());
-
-            final Map<String, Object> params = new HashMap<>();
-            final PixelDensity pixelDensity = PixelDensity.createFromPixelsPerInch(75, 150);
-            params.put(ImagingConstants.PARAM_KEY_PIXEL_DENSITY, pixelDensity);
-            Imaging.writeImage(testImage, temp1, formatInfo.format, params);
-
-            final ImageInfo imageInfo = Imaging.getImageInfo(temp1);
-            if (imageInfo == null) {
-                continue;
-            }
-            final int xReadDPI = imageInfo.getPhysicalWidthDpi();
-            final int yReadDPI = imageInfo.getPhysicalHeightDpi();
-            // allow a 5% margin of error in storage and conversion
-            assertTrue("horizontal pixel density stored wrongly for " + formatInfo.format +
-                            " in=" + pixelDensity.horizontalDensityInches() + ", out=" + xReadDPI,
-                    Math.abs((xReadDPI - pixelDensity.horizontalDensityInches()) /
-                            pixelDensity.horizontalDensityInches()) <= 0.05);
-            assertTrue("vertical pixel density stored wrongly for " + formatInfo.format +
-                            " in=" + pixelDensity.verticalDensityInches() + ", out=" + yReadDPI,
-                    Math.abs((yReadDPI - pixelDensity.verticalDensityInches()) /
-                            pixelDensity.verticalDensityInches()) <= 0.05);
-        }
-    }
-
-    @Test
-    public void testNullParametersRoundtrip() throws IOException, ImageReadException, ImageWriteException {
-        final BufferedImage testImage = createFullColorImage(1, 1);
-        for (final FormatInfo formatInfo : FORMAT_INFOS) {
-            if (!formatInfo.canRead || !formatInfo.canWrite) {
-                continue;
-            }
-            final File temp1 = createTempFile("nullParameters.", "."
-                    + formatInfo.format.getExtension());
-            Imaging.writeImage(testImage, temp1, formatInfo.format, null);
-            Imaging.getImageInfo(temp1, null);
-            Imaging.getImageSize(temp1, null);
-            Imaging.getMetadata(temp1, null);
-            Imaging.getICCProfile(temp1, null);
-            BufferedImage imageRead = Imaging.getBufferedImage(temp1, null);
-            assertNotNull(imageRead);
-        }
-    }
-
-    private void roundtrip(final FormatInfo formatInfo, final BufferedImage testImage,
-                           final String tempPrefix, final boolean imageExact) throws IOException,
-            ImageReadException, ImageWriteException {
-        final File temp1 = createTempFile(tempPrefix + ".", "."
-                + formatInfo.format.getExtension());
-        Debug.debug("tempFile: " + temp1.getName());
-
-        final Map<String, Object> params = new HashMap<>();
-        Imaging.writeImage(testImage, temp1, formatInfo.format, params);
-
-        final Map<String, Object> readParams = new HashMap<>();
-        readParams.put(ImagingConstants.BUFFERED_IMAGE_FACTORY,
-                new RgbBufferedImageFactory());
-        final BufferedImage image2 = Imaging.getBufferedImage(temp1, readParams);
-        assertNotNull(image2);
-
-        if (imageExact) {
-            // note tolerance when comparing grayscale images
-            // BufferedImages of
-            compareImagesExact(testImage, image2);
-        }
-
-        if (formatInfo.identicalSecondWrite) {
-            final File temp2 = createTempFile(tempPrefix + ".", "."
-                    + formatInfo.format.getExtension());
-            // Debug.debug("tempFile: " + tempFile.getName());
-            Imaging.writeImage(image2, temp2, formatInfo.format, params);
-
-            compareFilesExact(temp1, temp2);
-        }
-    }
-
-}
diff --git a/src/test/java/org/apache/commons/imaging/roundtrip/TestImages.java b/src/test/java/org/apache/commons/imaging/roundtrip/TestImages.java
new file mode 100644
index 0000000..5cb125e
--- /dev/null
+++ b/src/test/java/org/apache/commons/imaging/roundtrip/TestImages.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.imaging.roundtrip;
+
+import java.awt.image.BufferedImage;
+
+final class TestImages {
+
+    private TestImages() {
+    }
+
+    static BufferedImage createArgbBitmapImage(final int width, final int height) {
+        final BufferedImage result = new BufferedImage(width, height,
+                BufferedImage.TYPE_INT_ARGB);
+        for (int x = 0; x < width; x++) {
+            for (int y = 0; y < height; y++) {
+                // alternating black and white.
+                final int modulator = y + 2; // make sure lines vary.
+                final int argb = (x + y) % modulator == 0 ? 0xff000000 : 0xffffffff;
+                result.setRGB(x, y, argb);
+            }
+        }
+        return result;
+    }
+
+    static BufferedImage createBitmapBitmapImage(final int width, final int height) {
+        final BufferedImage result = new BufferedImage(width, height,
+                BufferedImage.TYPE_BYTE_BINARY);
+        for (int x = 0; x < width; x++) {
+            for (int y = 0; y < height; y++) {
+                // alternating black and white.
+                final int modulator = y + 2; // make sure lines vary.
+                final int argb = (x + y) % modulator == 0 ? 0xff000000 : 0xffffffff;
+                result.setRGB(x, y, argb);
+            }
+        }
+        return result;
+    }
+
+    static BufferedImage createArgbGrayscaleImage(final int width, final int height) {
+        final BufferedImage result = new BufferedImage(width, height,
+                BufferedImage.TYPE_INT_ARGB);
+        for (int x = 0; x < width; x++) {
+            for (int y = 0; y < height; y++) {
+                final int value = (256 * (x + y)) / (width + height);
+                final int argb = (0xff << 24) | (value << 16) | (value << 8)
+                        | (value << 0);
+
+                result.setRGB(x, y, argb);
+            }
+        }
+        return result;
+    }
+
+    static BufferedImage createGrayscaleGrayscaleImage(final int width, final int height) {
+        final BufferedImage result = new BufferedImage(width, height,
+                BufferedImage.TYPE_BYTE_GRAY);
+        for (int x = 0; x < width; x++) {
+            for (int y = 0; y < height; y++) {
+                final int value = (256 * (x + y)) / (width + height);
+                final int argb = (0xff << 24) | (value << 16) | (value << 8)
+                        | (value << 0);
+
+                result.setRGB(x, y, argb);
+            }
+        }
+        return result;
+    }
+
+    static BufferedImage createLimitedColorImage(final int width, final int height) {
+        final int colors[] = {0xffffffff, 0xff000000, 0xfff00000, 0xff0000ff,
+                0xff123456, 0xfffefeff, 0xff7f817f,};
+
+        final BufferedImage result = new BufferedImage(width, height,
+                BufferedImage.TYPE_INT_ARGB);
+        for (int x = 0; x < width; x++) {
+            for (int y = 0; y < height; y++) {
+                final int argb = colors[(x + y) % colors.length];
+                result.setRGB(x, y, argb);
+            }
+        }
+        return result;
+    }
+
+    static BufferedImage createFullColorImage(final int width, final int height) {
+        final BufferedImage result = new BufferedImage(width, height,
+                BufferedImage.TYPE_INT_ARGB);
+        for (int x = 0; x < width; x++) {
+            for (int y = 0; y < height; y++) {
+                final int red = (x * 255) / width;
+                final int green = (y * 255) / height;
+                final int blue = ((x + y) * 255) / (width + height);
+                final int argb = (0xff << 24) | (red << 16) | (green << 8)
+                        | (blue << 0);
+                result.setRGB(x, y, argb);
+            }
+        }
+        return result;
+    }
+}