Skip to content

Commit

Permalink
Improve image acquisition tests.
Browse files Browse the repository at this point in the history
For all valid combinations of test pattern, depth and mode,
ImageAcquisitionTest checks that the output of JFreeSane matches exactly
the output of scanimage as recorded in src/test/resources.
  • Loading branch information
sjamesr committed Aug 20, 2019
1 parent a6b6bfb commit 49c3f66
Show file tree
Hide file tree
Showing 18 changed files with 124 additions and 115 deletions.
107 changes: 107 additions & 0 deletions src/test/java/au/com/southsky/jfreesane/ImageAcquisitionTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package au.com.southsky.jfreesane;

import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.common.io.Resources;
import com.google.common.net.HostAndPort;
import com.google.common.truth.Truth;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.util.Set;

import static org.junit.Assert.assertEquals;

/**
* Acquires a series of images from the test device and compares them against reference images
* obtained from the "scanimage" SANE util.
*/
@RunWith(Parameterized.class)
public class ImageAcquisitionTest {
private SanePasswordProvider correctPasswordProvider =
SanePasswordProvider.forUsernameAndPassword("testuser", "goodpass");

@Before
public void initSession() throws Exception {
HostAndPort hostAndPort;
String address = System.getenv("SANE_TEST_SERVER_ADDRESS");
if (address == null) {
address = "localhost";
}
hostAndPort = HostAndPort.fromString(address);
this.session =
SaneSession.withRemoteSane(
InetAddress.getByName(hostAndPort.getHost()), hostAndPort.getPortOrDefault(6566));
session.setPasswordProvider(correctPasswordProvider);
}

@Parameterized.Parameters(name = "device={0},mode={1},depth={2},pattern={3}")
public static Iterable<Object[]> parameters() {
Set<String> devices = ImmutableSet.of("test");
Set<String> modes = ImmutableSet.of("Gray", "Color");
Set<Integer> depths = ImmutableSet.of(1, 8, 16);
Set<String> patterns = ImmutableSet.of("Solid white", "Solid black", "Color pattern");
return Sets.cartesianProduct(devices, modes, depths, patterns)
.stream()
.map(e -> e.toArray(new Object[] {}))
::iterator;
}

private SaneSession session;

private final String device;
private final String mode;
private final int depth;
private final String pattern;

public ImageAcquisitionTest(String device, String mode, int depth, String pattern) {
this.device = device;
this.mode = mode;
this.depth = depth;
this.pattern = pattern;
}

@Test
public void testImageAcquisition() throws IOException, SaneException {
Assume.assumeFalse("color tests at depth 1 are skipped", depth == 1 && "Color".equals(mode));
BufferedImage expectedImage = getExpectedImage();

try (SaneDevice dev = session.getDevice(device)) {
dev.open();
Truth.assertThat(dev.getOption("mode").setStringValue(mode)).isEqualTo(mode);
Truth.assertThat(dev.getOption("depth").setIntegerValue(depth)).isEqualTo(depth);
Truth.assertThat(dev.getOption("test-picture").setStringValue(pattern)).isEqualTo(pattern);
BufferedImage actualImage = dev.acquireImage();
assertImagesEqual(expectedImage, actualImage);
}
}

/**
* Reads the expected image from test resources. If those resources are not present, scanimage is
* invoked to read the reference image.
*/
private BufferedImage getExpectedImage() throws IOException {
String resourceName = String.format("%s-%d-%s.png", mode, depth, pattern.replace(" ", "_"));
InputStream resource = Resources.getResource(resourceName).openStream();
return ImageIO.read(resource);
}

private void assertImagesEqual(BufferedImage expected, BufferedImage actual) {
assertEquals("image widths differ", expected.getWidth(), actual.getWidth());
assertEquals("image heights differ", expected.getHeight(), actual.getHeight());

for (int x = 0; x < expected.getWidth(); x++) {
for (int y = 0; y < expected.getHeight(); y++) {
Truth.assertThat(actual.getRGB(x, y)).isEqualTo(expected.getRGB(x, y));
}
}
}
}
125 changes: 10 additions & 115 deletions src/test/java/au/com/southsky/jfreesane/SaneSessionTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,6 @@
import com.google.common.io.Files;
import com.google.common.net.HostAndPort;
import com.google.common.util.concurrent.SettableFuture;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.awt.image.Raster;
import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
Expand All @@ -30,6 +18,16 @@
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.net.InetAddress;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Logger;

import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
Expand Down Expand Up @@ -231,48 +229,6 @@ public void acquireMonoImage() throws Exception {
}
}

/**
* Tests that this SANE client produces images that match
* {@link "http://www.meier-geinitz.de/sane/test-backend/test-pictures.html"} .
*/
@Test
public void producesCorrectImages() throws Exception {
// Solid black and white
try (SaneDevice device = session.getDevice("test")) {
device.open();
device.getOption("br-x").setFixedValue(200);
device.getOption("br-y").setFixedValue(200);

/*
* assertProducesCorrectImage(device, "Gray", 1, "Solid white");
* assertProducesCorrectImage(device, "Gray", 8, "Solid white");
* assertProducesCorrectImage(device, "Gray", 16, "Solid white");
* assertProducesCorrectImage(device, "Gray", 1, "Solid black");
* assertProducesCorrectImage(device, "Gray", 8, "Solid black");
* assertProducesCorrectImage(device, "Gray", 16, "Solid black");
*
* assertProducesCorrectImage(device, "Color", 1, "Solid white");
* assertProducesCorrectImage(device, "Color", 8, "Solid white");
* assertProducesCorrectImage(device, "Color", 16, "Solid white");
* assertProducesCorrectImage(device, "Color", 1, "Solid black");
* assertProducesCorrectImage(device, "Color", 8, "Solid black");
* assertProducesCorrectImage(device, "Color", 16, "Solid black");
*
* assertProducesCorrectImage(device, "Gray", 1, "Color pattern");
* assertProducesCorrectImage(device, "Color", 1, "Color pattern");
*
* assertProducesCorrectImage(device, "Gray", 8, "Color pattern");
* assertProducesCorrectImage(device, "Color", 8, "Color pattern");
*/

assertProducesCorrectImage(device, "Gray", 1, "Grid");
// assertProducesCorrectImage(device, "Color", 1, "Color pattern");

assertProducesCorrectImage(device, "Color", 8, "Color pattern");
assertProducesCorrectImage(device, "Color", 16, "Color pattern");
}
}

@Test
public void readsAndSetsStringsCorrectly() throws Exception {

Expand Down Expand Up @@ -592,65 +548,4 @@ private void openAndCloseDevice(SaneDevice device) throws Exception {
device.close();
}
}

private void assertProducesCorrectImage(
SaneDevice device, String mode, int sampleDepth, String testPicture)
throws IOException, SaneException {
BufferedImage actualImage = acquireImage(device, mode, sampleDepth, testPicture);

writeImage(mode, sampleDepth, testPicture, actualImage);

if (testPicture.startsWith("Solid")) {
assertImageSolidColor(testPicture.endsWith("black") ? Color.black : Color.white, actualImage);
}
// TODO(sjr): compare with reference images.
}

private void writeImage(
String mode, int sampleDepth, String testPicture, BufferedImage actualImage)
throws IOException {
File file =
File.createTempFile(
String.format("image-%s-%d-%s", mode, sampleDepth, testPicture.replace(' ', '_')),
".png",
tempFolder.getRoot());
ImageIO.write(actualImage, "png", file);
System.out.println("Successfully wrote " + file);
}

private void assertImageSolidColor(Color color, BufferedImage image) {
for (int x = 0; x < image.getWidth(); x++) {
for (int y = 0; y < image.getHeight(); y++) {
assertEquals(color.getRGB(), image.getRGB(x, y));
}
}
}

private BufferedImage acquireImage(
SaneDevice device, String mode, int sampleDepth, String testPicture)
throws IOException, SaneException {
device.getOption("mode").setStringValue(mode);
device.getOption("depth").setIntegerValue(sampleDepth);
device.getOption("test-picture").setStringValue(testPicture);

return device.acquireImage();
}

private void assertImagesEqual(BufferedImage expected, BufferedImage actual) {
assertEquals("image widths differ", expected.getWidth(), actual.getWidth());
assertEquals("image heights differ", expected.getHeight(), actual.getHeight());

Raster expectedRaster = expected.getRaster();
Raster actualRaster = actual.getRaster();

for (int x = 0; x < expected.getWidth(); x++) {
for (int y = 0; y < expected.getHeight(); y++) {
int[] expectedPixels = expectedRaster.getPixel(x, y, (int[]) null);
int[] actualPixels = actualRaster.getPixel(x, y, (int[]) null);

// assert that all the samples are the same for the given pixel
Assert.assertArrayEquals(expectedPixels, actualPixels);
}
}
}
}
Binary file added src/test/resources/Color-16-Color_pattern.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/test/resources/Color-16-Solid_black.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/test/resources/Color-16-Solid_white.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/test/resources/Color-8-Color_pattern.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/test/resources/Color-8-Solid_black.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/test/resources/Color-8-Solid_white.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/test/resources/Gray-1-Color_pattern.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/test/resources/Gray-1-Solid_black.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/test/resources/Gray-1-Solid_white.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/test/resources/Gray-16-Color_pattern.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/test/resources/Gray-16-Solid_black.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/test/resources/Gray-16-Solid_white.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/test/resources/Gray-8-Color_pattern.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/test/resources/Gray-8-Solid_black.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/test/resources/Gray-8-Solid_white.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions src/test/resources/README
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
SANE reference images generated on Tue 20 Aug 2019 02:37:35 PM PDT by:


scanimage (sane-backends) 1.0.27; backend version 1.0.27


To regenerate, run src/test/generateTestResources.sh from the project root directory.

0 comments on commit 49c3f66

Please sign in to comment.