package freenet.client.filter; import junit.framework.TestCase; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Arrays; import freenet.support.HexUtil; import freenet.support.api.Bucket; import freenet.support.io.ArrayBucket; import freenet.support.io.BucketTools; import freenet.support.io.NullOutputStream; public class GIFFilterTest extends TestCase { private static final String RESOURCE_PATH = "gif/"; /** Known good files, should pass filter unaltered. */ private static final String[] GOOD = { /* * Various samples from Firefox testcases. */ // GIF image data, version 87a, 40 x 40 "animated-gif-finalframe.gif", // GIF image data, version 87a, 92 x 129 "clean.gif", // GIF image data, version 87a, 85 x 140 "bethlehem.gif", // GIF image data, version 87a, 92 x 140 "bill.gif", // GIF image data, version 87a, 90 x 140 "charing.gif", // GIF image data, version 87a, 92 x 140 "welville.gif", // GIF image data, version 89a, 100 x 100 "animated1.gif", // GIF image data, version 89a, 40 x 40 "animated-gif2.gif", // GIF image data, version 89a, 40 x 40 "animated-gif.gif", // GIF image data, version 89a, 200 x 200 "bug1132427.gif", // GIF image data, version 89a, 100 x 75 "clear2.gif", // GIF image data, version 89a, 100 x 75 "clear2-results.gif", // GIF image data, version 89a, 100 x 100 "clear.gif", // GIF image data, version 89a, 16 x 16 "first-frame-padding.gif", // GIF image data, version 89a, 100 x 100 "keep.gif", // GIF image data, version 89a, 40 x 40 "purple.gif", // GIF image data, version 89a, 1 x 1 "red.gif", // GIF image data, version 89a, 100 x 100 "restore-previous.gif", // GIF image data, version 89a, 16 x 16 "transparent.gif" }; /** Pairs of unfiltered file and their expected output file. */ private static final String[][] FILTER_PAIRS = { // Check if we strip garbage after the end of the file. new String[] { // GIF image data, version 89a, 40 x 40 "animated-gif_trailing-garbage.gif", "animated-gif_trailing-garbage.filtered.gif", }, // Check if we strip short application extensions. new String[] { // GIF image data, version 89a, 353 x 25 "short_header.gif", "short_header.filtered.gif", }, // Stripping of long application extensions. new String[] { // GIF image data, version 89a, 60 x 60 "share-the-safety-like.gif", "share-the-safety-like.filtered.gif" }, // Stripping of graphic control appearing in GIF87a. new String[] { // GIF image data, version 87a, 101 x 140 "road.gif", "road.filtered.gif" } }; private static final String[] REJECT = { // MPEG ADTS, layer III, v2.5, 48 kbps, 11.025 kHz, Stereo "11khz-48kbps-cbr-stereo.mp3", // PNG image data, 32 x 32, 1-bit grayscale, non-interlaced "basn0g01.png", // Truncated GIF file (share-the-safety-like.gif with removed terminator) "share-the-safety-like.truncated.gif" }; public void testKnownGood() { for (String good : GOOD) { assertEqualAfterFilter(good, good); } } public void testFilterPairs() { for (String[] pair : FILTER_PAIRS) { assertEqualAfterFilter(pair[0], pair[1]); } } public void testReject() { for (String reject : REJECT) { final InputStream inStream; final NullOutputStream outStream; try { inStream = resourceToBucket(reject).getInputStream(); outStream = new NullOutputStream(); } catch (IOException e) { throw new AssertionError(e); } ContentDataFilter filter = new GIFFilter(); try { filter.readFilter(inStream, outStream, "", null, null); fail("Filter did not fail on reject sample " + reject); } catch (DataFilterException e) { // Expected. } catch (Exception e) { throw new AssertionError("Unexpected exception in the content filter.", e); } try { inStream.close(); outStream.close(); } catch (IOException e) { throw new AssertionError(e); } } } /** * Asserts that the test file in the first argument, after passing through the content filter, * is equal to the reference file in the second argument. * * @param fileUnfiltered the test file * @param fileExpected the reference file */ private static void assertEqualAfterFilter(String fileUnfiltered, String fileExpected) { Bucket input = resourceToBucket(fileUnfiltered); Bucket expected = resourceToBucket(fileExpected); Bucket filtered = filterGIF(input); assertTrue("Filtered and expected output are not identical. " + "Input = " + fileUnfiltered + ", expected = " + fileExpected, equalBuckets(filtered, expected) ); } /** * Checks for equality of Bucket contents. */ private static boolean equalBuckets(Bucket a, Bucket b) { try { return Arrays.equals(BucketTools.toByteArray(a), BucketTools.toByteArray(b)); } catch (IOException e) { throw new AssertionError(e); } } /** * Runs a Bucket through the content filter. * * @throws AssertionError on failure */ private static Bucket filterGIF(Bucket input) { ContentDataFilter filter = new GIFFilter(); Bucket output = new ArrayBucket(); InputStream inStream; OutputStream outStream; try { inStream = input.getInputStream(); outStream = output.getOutputStream(); } catch (IOException e) { throw new AssertionError(e); } try { filter.readFilter(inStream, outStream, "", null, null); } catch (Exception e) { throw new AssertionError("Unexpected exception in the content filter.", e); } try { inStream.close(); outStream.close(); } catch (IOException e) { throw new AssertionError(e); } return output; } /** * Loads a resource relative to the resource path into a Bucket. * * @throws AssertionError on failure */ private static Bucket resourceToBucket(String filename) { InputStream is = GIFFilterTest.class.getResourceAsStream(RESOURCE_PATH + filename); if (is == null) { throw new AssertionError("Test resource could not be opened: " + filename); } Bucket ab = new ArrayBucket(); try { BucketTools.copyFrom(ab, is, Long.MAX_VALUE); } catch (Exception e) { throw new AssertionError(e); } return ab; } }