/*
* #%L
* OME Bio-Formats manual and automated test suite.
* %%
* Copyright (C) 2006 - 2015 Open Microscopy Environment:
* - Board of Regents of the University of Wisconsin-Madison
* - Glencoe Software, Inc.
* - University of Dundee
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/gpl-2.0.html>.
* #L%
*/
package loci.tests.testng;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import loci.common.DataTools;
import loci.common.services.ServiceFactory;
import loci.formats.FormatException;
import loci.formats.FormatTools;
import loci.formats.IFormatWriter;
import loci.formats.ImageWriter;
import loci.formats.gui.BufferedImageReader;
import loci.formats.meta.IMetadata;
import loci.formats.meta.MetadataRetrieve;
import loci.formats.out.JPEG2000Writer;
import loci.formats.out.JPEGWriter;
import loci.formats.services.OMEXMLService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
/**
* TestNG tester for Bio-Formats file format writers.
* Details on failed tests are written to a log file, for easier processing.
*
* NB: {@link loci.formats.ome} and ome-xml.jar
* are required for some of the tests.
*
* To run tests:
* ant -Dtestng.directory="/path" -Dtestng.multiplier="1.0" test-all
*/
public class FormatWriterTest {
private static final Logger LOGGER =
LoggerFactory.getLogger(FormatWriterTest.class);
/** Message to give for why a test was skipped. */
private static final String SKIP_MESSAGE = "Dataset already tested.";
// -- Static fields --
/** Configuration tree structure containing dataset metadata. */
public static ConfigurationTree configTree;
/** List of files to skip. */
public static List skipFiles = new LinkedList();
/** Reader for input file. */
private static BufferedImageReader reader = new BufferedImageReader();
/** Reader for converted files. */
private static BufferedImageReader convertedReader =
new BufferedImageReader();
private static Configuration config;
// -- Fields --
private String id;
private boolean skip = false;
// -- Constructor --
public FormatWriterTest(String filename) {
id = filename;
try {
reader.setId(id);
}
catch (FormatException e) { LOGGER.info("", e); }
catch (IOException e) { LOGGER.info("", e); }
}
// -- Data provider --
@DataProvider(name = "getWriterList")
public Object[][] getWriterList() {
IFormatWriter[] writers = new ImageWriter().getWriters();
List tmp = new ArrayList();
for (int i=0; i<writers.length; i++) {
String[] compressionTypes = writers[i].getCompressionTypes();
if (compressionTypes == null) {
try {
IFormatWriter w = (IFormatWriter) writers[i].getClass().newInstance();
tmp.add(w);
}
catch (InstantiationException ie) { }
catch (IllegalAccessException iae) { }
continue;
}
for (int q=0; q<compressionTypes.length; q++) {
try {
IFormatWriter w = (IFormatWriter) writers[i].getClass().newInstance();
if (DataTools.containsValue(w.getPixelTypes(compressionTypes[q]),
reader.getPixelType()))
{
w.setCompression(compressionTypes[q]);
tmp.add(w);
}
}
catch (FormatException fe) { }
catch (InstantiationException ie) { }
catch (IllegalAccessException iae) { }
}
}
IFormatWriter[][] writersToUse = new IFormatWriter[tmp.size()][1];
for (int i=0; i<tmp.size(); i++) {
writersToUse[i][0] = (IFormatWriter) tmp.get(i);
}
return writersToUse;
}
// -- Tests --
@Test(groups = {"all"}, dataProvider = "getWriterList")
public void testWriterConsistency(IFormatWriter writer) {
String testName = TestTools.shortClassName(writer) + " " +
writer.getCompression() + " testWriterConsistency";
boolean success = true;
String msg = null;
try {
reader.close();
ServiceFactory factory = new ServiceFactory();
OMEXMLService service = factory.getInstance(OMEXMLService.class);
reader.setMetadataStore(service.createOMEXMLMetadata());
reader.setId(id);
int type = reader.getPixelType();
if (!writer.isSupportedType(type)) {
success = true;
result(testName, success, msg);
return;
}
config = configTree.get(id);
String prefix = id.substring(id.lastIndexOf(File.separator) + 1,
id.lastIndexOf("."));
// prefix must be at least 3 chars, or File.createTempFile(String, String)
// will throw an exception
while (prefix.length() < 3) prefix = "x" + prefix;
String suffix = "." + writer.getSuffixes()[0];
File tmpFile = File.createTempFile(prefix, suffix);
tmpFile.deleteOnExit();
String convertedFile = tmpFile.getAbsolutePath();
IMetadata meta = (IMetadata) reader.getMetadataStore();
writer.close();
writer.setMetadataRetrieve((MetadataRetrieve) meta);
// convert the input file
writer.setId(convertedFile);
int seriesCount = writer.canDoStacks() ? reader.getSeriesCount() : 1;
for (int series=0; series<seriesCount; series++) {
reader.setSeries(series);
writer.setSeries(series);
int imageCount = writer.canDoStacks() ? reader.getImageCount() : 1;
for (int image=0; image<imageCount; image++) {
writer.saveBytes(image, reader.openBytes(image));
}
}
writer.close();
// verify that the dimensions are accurate
convertedReader.setId(convertedFile);
boolean seriesMatch =
convertedReader.getSeriesCount() == config.getSeriesCount();
boolean expectRGB = config.isRGB();
int expectedCount = config.getSizeZ() * config.getSizeT() *
(expectRGB ? 1 : config.getSizeC());
boolean imageMatch = convertedReader.getImageCount() == expectedCount;
if (!seriesMatch && writer.canDoStacks()) {
int totalImages = 0;
for (int i=0; i<reader.getSeriesCount(); i++) {
reader.setSeries(i);
totalImages += reader.getImageCount();
}
reader.setSeries(0);
if (convertedReader.getImageCount() != totalImages) {
success = false;
msg = "Series counts do not match (found " +
convertedReader.getSeriesCount() + ", expected " +
config.getSeriesCount() + ")";
}
else imageMatch = true;
}
if (success) {
for (int series=0; series<seriesCount; series++) {
if (series >= convertedReader.getSeriesCount()) {
break;
}
convertedReader.setSeries(series);
config.setSeries(series);
int expectedX = config.getSizeX();
int expectedY = config.getSizeY();
expectRGB = config.isRGB();
if (TestTools.shortClassName(writer).equals("OMEXMLWriter")) {
expectRGB = false;
}
else if (TestTools.shortClassName(writer).equals("JPEGWriter")) {
expectRGB = expectRGB || config.isIndexed();
}
int expectedPixelType =
FormatTools.pixelTypeFromString(config.getPixelType());
expectedCount = config.getSizeZ() * config.getSizeT() *
(expectRGB ? 1 : config.getSizeC());
String expectedMD5 = config.getMD5();
int x = convertedReader.getSizeX();
int y = convertedReader.getSizeY();
int count = convertedReader.getImageCount();
boolean rgb = convertedReader.isRGB();
int pixelType = convertedReader.getPixelType();
boolean isQuicktime =
TestTools.shortClassName(writer).equals("QTWriter");
String md5 = TestTools.md5(convertedReader.openBytes(0));
if (msg == null) msg = checkMismatch(x, expectedX, series, "X");
if (msg == null) msg = checkMismatch(y, expectedY, series, "Y");
if (msg == null && writer.canDoStacks() && !imageMatch) {
msg = checkMismatch(count, expectedCount, series, "Image count");
}
if (msg == null && !isQuicktime) {
msg = checkMismatch(rgb, expectRGB, series, "RGB");
}
if (msg == null && !isQuicktime) {
msg =
checkMismatch(pixelType, expectedPixelType, series, "Pixel type");
}
if (msg == null && isLosslessWriter(writer) &&
config.isRGB() == expectRGB)
{
msg = checkMismatch(md5, expectedMD5, series, "Pixels hash");
}
success = msg == null;
if (!success) break;
}
}
convertedReader.close();
}
catch (Throwable t) {
LOGGER.info("", t);
success = false;
}
result(testName, success, msg);
}
// -- Helper methods --
private static String checkMismatch(boolean i1, boolean i2, int series,
String label)
{
return checkMismatch(String.valueOf(i1), String.valueOf(i2), series, label);
}
private static String checkMismatch(int i1, int i2, int series, String label)
{
return checkMismatch(String.valueOf(i1), String.valueOf(i2), series, label);
}
private static String checkMismatch(String s1, String s2, int series,
String label)
{
if (s1.equals(s2)) return null;
return label + " mismatch [got " + s1 + ", expected " + s2 +
"] in series " + series;
}
/**
* Outputs test result with optional extra message
* and generates appropriate assertion.
*/
private static void result(String testName, boolean success, String msg) {
LOGGER.info("\t{}: {} ({})", new Object[] {testName,
success ? "PASSED" : "FAILED", msg == null ? "" : msg});
if (msg == null) assert success;
else assert success : msg;
}
private static boolean isLosslessWriter(IFormatWriter writer) {
if ((writer instanceof JPEGWriter) || (writer instanceof JPEG2000Writer)) {
return false;
}
String compression = writer.getCompression();
if (compression != null) compression = compression.toLowerCase();
if (compression == null || compression.equals("lzw") ||
compression.equals("zlib") || compression.equals("uncompressed"))
{
return true;
}
return false;
}
}