/******************************************************************************* * Copyright (c) 2013 Rene Schneider, GEBIT Solutions GmbH and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html *******************************************************************************/ package de.gebit.integrity.fixtures; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.List; import javax.imageio.IIOImage; import javax.imageio.ImageIO; import javax.imageio.ImageWriteParam; import javax.imageio.ImageWriter; import javax.imageio.stream.ImageOutputStream; import javax.imageio.stream.MemoryCacheImageOutputStream; /** * Fixtures can implement this optional interface to provide additional result information after the main fixture method * call(s) have been performed. The method {@link #provideExtendedResults()} is called once on each single fixture * instance, right after the fixture main method was invoked - even in case an exception was thrown in that method. For * tabletests, the method is invoked only once per tabletest statement, and NOT once per tabletest fixture method * invocation (which is invoked once per line).<br> * <br> * The method may return null or an empty list if no extended results shall be returned. Otherwise, an arbitrary number * of extended result instances - either {@link ExtendedResultText} for textual data or {@link ExtendedResultImage} for * images - may be returned, which will be integrated into the test results. * * @author Rene Schneider - initial API and implementation * */ public interface ExtendedResultFixture { /** * This method must determine the extended results to be integrated into the test results. The list may contain: * <ul> * <li> * {@link ExtendedResultText} for textual results</li> * <li> * {@link ExtendedResultImage} for images</li> * </ul> * * @param anInvocationResult * the result of the fixture invocation * * @return the list of extended results, or null if no results are to be provided */ List<ExtendedResult> provideExtendedResults(FixtureInvocationResult anInvocationResult); /** * The possible results of a fixture method invocation. * * * @author Rene Schneider - initial API and implementation * */ public static enum FixtureInvocationResult { /** * The method invocation was successful (in case of tests: the test comparison was successful). */ SUCCESS, /** * The invocation was successful, but the test comparison did fail (result doesn't match the expected value). * This value is not valid for call fixtures. */ FAILURE, /** * An exception was thrown during invocation. */ EXCEPTION; } /** * Abstract base class for extended result types. You should NOT implement subclasses of this class, but instead use * the provided subclasses: * <ul> * <li> * {@link ExtendedResultText} for textual results</li> * <li> * {@link ExtendedResultImage} for images</li> * </ul> * * * @author Rene Schneider - initial API and implementation * */ public static abstract class ExtendedResult { /** * The title of this extended result. */ private String title; /** * Not directly invokable, but from subclasses. * * @param aTitle * the title of this extended result. */ public ExtendedResult(String aTitle) { title = aTitle; } public String getTitle() { return title; } } /** * Encapsulates a textual extended result. * * * @author Rene Schneider - initial API and implementation * */ public static final class ExtendedResultText extends ExtendedResult { /** * The text. */ private String text; /** * Creates an instance. * * @param aTitle * the title of this extended result. * @param aText * the text of the result */ public ExtendedResultText(String aTitle, String aText) { super(aTitle); text = aText; } /** * Creates an instance with no title. * * @param aText * the text of the result */ public ExtendedResultText(String aText) { this(null, aText); } public String getText() { return text; } } /** * Encapsulates a "plain HTML" extended result. This HTML will be directly included into the test result file. USE * AT YOUR OWN RISK - you can easily junk your result file by providing invalid HTML, and Integrity can and will NOT * prevent you from doing so! But if used wisely, this extended result type allows you to include pretty much * anything in the result document. * * * @author Rene Schneider - initial API and implementation * */ public static final class ExtendedResultHTML extends ExtendedResult { /** * The plain HTML to include. */ private String hypertext; /** * Creates an instance. * * @param aTitle * the title of this extended result. * @param aHypertext * the HTML text of the result */ public ExtendedResultHTML(String aTitle, String aHypertext) { super(aTitle); hypertext = aHypertext; } /** * Creates an instance with no title. * * @param aHypertext * the HTML text of the result */ public ExtendedResultHTML(String aHypertext) { this(null, aHypertext); } public String getHypertext() { return hypertext; } } /** * Encapsulates an image which is to be provided as extended result. The creator can optionally specify the * preferred compression type to use when the image is encoded to integrate it into the results. * * * @author Rene Schneider - initial API and implementation * */ public static final class ExtendedResultImage extends ExtendedResult { /** * The compressed and encoded image. */ private byte[] encodedImage; /** * The compression type to use when encoding the image. */ private ImageCompressionType type; /** * The width of the image, in pixels. */ private int width; /** * The height of the image, in pixels. */ private int height; /** * Creates a new instance, using the default compression type (PNG) and no title. * * @param anImage * the image to encapsulate * @throws IOException * in case of compression/encoding errors */ public ExtendedResultImage(BufferedImage anImage) throws IOException { this(null, anImage, ImageCompressionType.PNG); } /** * Creates a new instance, using the default compression type (PNG). * * @param aTitle * the title of this extended result (may be null) * @param anImage * the image to encapsulate * @throws IOException * in case of compression/encoding errors */ public ExtendedResultImage(String aTitle, BufferedImage anImage) throws IOException { this(aTitle, anImage, ImageCompressionType.PNG); } /** * Creates a new instance, using the provided image and compression type. * * @param aTitle * the title of this extended result (may be null) * @param anImage * the image to encapsulate * @param aCompressionType * the preferred compression type to use when encoding the image for the results * @throws IOException * in case of compression/encoding errors */ public ExtendedResultImage(String aTitle, BufferedImage anImage, ImageCompressionType aCompressionType) throws IOException { super(aTitle); type = aCompressionType != null ? aCompressionType : ImageCompressionType.PNG; width = anImage.getWidth(); height = anImage.getHeight(); compress(anImage); } public byte[] getEncodedImage() { return encodedImage; } public ImageCompressionType getType() { return type; } public int getWidth() { return width; } public int getHeight() { return height; } /** * This enum contains the supported image compression types, used when encoding the image for test results. * * * @author Rene Schneider - initial API and implementation * */ public static enum ImageCompressionType { /** * Stores the image as a PNG. */ PNG("image/png"), /** * Stores the image as a JPEG with high quality setting (0.9). */ JPEG_HIGH("image/jpeg"), /** * Stores the image as a JPEG with low quality setting (0.4). */ JPEG_LOW("image/jpeg"); /** * The mime type string. */ private String mimeType; private ImageCompressionType(String aMimeType) { mimeType = aMimeType; } public String getMimeType() { return mimeType; } } private void compress(BufferedImage anImage) throws IOException { ImageWriter tempWriter = ImageIO.getImageWritersByMIMEType(type.getMimeType()).next(); ImageOutputStream tempImageOut = null; try { ImageWriteParam tempParam = tempWriter.getDefaultWriteParam(); switch (type) { case JPEG_HIGH: tempParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); tempParam.setCompressionQuality(0.9f); break; case JPEG_LOW: tempParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); tempParam.setCompressionQuality(0.4f); break; default: // nothing to do } ByteArrayOutputStream tempBuffer = new ByteArrayOutputStream(); tempImageOut = new MemoryCacheImageOutputStream(tempBuffer); tempWriter.setOutput(tempImageOut); tempWriter.write(null, new IIOImage(anImage, null, null), tempParam); tempImageOut.flush(); encodedImage = tempBuffer.toByteArray(); } finally { tempWriter.dispose(); if (tempImageOut != null) { tempImageOut.close(); } } } } }