package com.bumptech.glide.gifdecoder; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import com.bumptech.glide.gifdecoder.test.GifBytesTestUtil; import com.bumptech.glide.testutil.TestUtil; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** * Tests for {@link com.bumptech.glide.gifdecoder.GifHeaderParser}. */ @RunWith(JUnit4.class) public class GifHeaderParserTest { private GifHeaderParser parser; @Before public void setUp() { parser = new GifHeaderParser(); } @Test public void testReturnsHeaderWithFormatErrorIfDoesNotStartWithGifHeader() { parser.setData("wrong_header".getBytes()); GifHeader result = parser.parseHeader(); assertEquals(GifDecoder.STATUS_FORMAT_ERROR, result.status); } @Test public void testCanReadValidHeaderAndLSD() { final int width = 10; final int height = 20; ByteBuffer buffer = ByteBuffer.allocate(GifBytesTestUtil.HEADER_LENGTH).order(ByteOrder.LITTLE_ENDIAN); GifBytesTestUtil.writeHeaderAndLsd(buffer, width, height, false, 0); parser.setData(buffer.array()); GifHeader header = parser.parseHeader(); assertEquals(width, header.width); assertEquals(height, header.height); assertFalse(header.gctFlag); // 2^(1+0) == 2^1 == 2. assertEquals(2, header.gctSize); assertEquals(0, header.bgIndex); assertEquals(0, header.pixelAspect); } @Test public void testCanParseHeaderOfTestImageWithoutGraphicalExtension() throws IOException { byte[] data = TestUtil.resourceToBytes(getClass(), "gif_without_graphical_control_extension.gif"); parser.setData(data); GifHeader header = parser.parseHeader(); assertEquals(1, header.frameCount); assertNotNull(header.frames.get(0)); assertEquals(GifDecoder.STATUS_OK, header.status); } @Test public void testCanReadNetscapeIterationCountIfNetscapeIterationCountIsZero() throws IOException { byte[] data = TestUtil.resourceToBytes(getClass(), "gif_netscape_iteration_0.gif"); parser.setData(data); GifHeader header = parser.parseHeader(); assertEquals(GifHeader.NETSCAPE_LOOP_COUNT_FOREVER, header.loopCount); } @Test public void testCanReadNetscapeIterationCountIfNetscapeIterationCountIs_1() throws IOException { byte[] data = TestUtil.resourceToBytes(getClass(), "gif_netscape_iteration_1.gif"); parser.setData(data); GifHeader header = parser.parseHeader(); assertEquals(1, header.loopCount); } @Test public void testCanReadNetscapeIterationCountIfNetscapeIterationCountIs_0x0F() throws IOException { byte[] data = TestUtil.resourceToBytes(getClass(), "gif_netscape_iteration_255.gif"); parser.setData(data); GifHeader header = parser.parseHeader(); assertEquals(255, header.loopCount); } @Test public void testCanReadNetscapeIterationCountIfNetscapeIterationCountIs_0x10() throws IOException { byte[] data = TestUtil.resourceToBytes(getClass(), "gif_netscape_iteration_256.gif"); parser.setData(data); GifHeader header = parser.parseHeader(); assertEquals(256, header.loopCount); } @Test public void testCanReadNetscapeIterationCountIfNetscapeIterationCountIs_0xFF() throws IOException { byte[] data = TestUtil.resourceToBytes(getClass(), "gif_netscape_iteration_65535.gif"); parser.setData(data); GifHeader header = parser.parseHeader(); assertEquals(65535, header.loopCount); } @Test public void testLoopCountReturnsMinusOneWithoutNetscapeIterationCount() throws IOException { byte[] data = TestUtil.resourceToBytes(getClass(), "gif_without_netscape_iteration.gif"); parser.setData(data); GifHeader header = parser.parseHeader(); assertEquals(GifHeader.NETSCAPE_LOOP_COUNT_DOES_NOT_EXIST, header.loopCount); } @Test public void testCanReadImageDescriptorWithoutGraphicalExtension() { final int lzwMinCodeSize = 2; ByteBuffer buffer = ByteBuffer.allocate( GifBytesTestUtil.HEADER_LENGTH + GifBytesTestUtil.IMAGE_DESCRIPTOR_LENGTH + GifBytesTestUtil .getImageDataSize(lzwMinCodeSize)).order(ByteOrder.LITTLE_ENDIAN); GifBytesTestUtil.writeHeaderAndLsd(buffer, 1, 1, false, 0); GifBytesTestUtil.writeImageDescriptor(buffer, 0, 0, 1, 1, false /*hasLct*/, 0); GifBytesTestUtil.writeFakeImageData(buffer, lzwMinCodeSize); parser.setData(buffer.array()); GifHeader header = parser.parseHeader(); assertEquals(1, header.width); assertEquals(1, header.height); assertEquals(1, header.frameCount); assertNotNull(header.frames.get(0)); } private static ByteBuffer writeHeaderWithGceAndFrameDelay(short frameDelay) { final int lzwMinCodeSize = 2; ByteBuffer buffer = ByteBuffer.allocate( GifBytesTestUtil.HEADER_LENGTH + GifBytesTestUtil.GRAPHICS_CONTROL_EXTENSION_LENGTH + GifBytesTestUtil.IMAGE_DESCRIPTOR_LENGTH + GifBytesTestUtil .getImageDataSize(lzwMinCodeSize)).order(ByteOrder.LITTLE_ENDIAN); GifBytesTestUtil.writeHeaderAndLsd(buffer, 1, 1, false, 0); GifBytesTestUtil.writeGraphicsControlExtension(buffer, frameDelay); GifBytesTestUtil.writeImageDescriptor(buffer, 0, 0, 1, 1, false /*hasLct*/, 0); GifBytesTestUtil.writeFakeImageData(buffer, lzwMinCodeSize); return buffer; } @Test public void testCanParseFrameDelay() { final short frameDelay = 50; ByteBuffer buffer = writeHeaderWithGceAndFrameDelay(frameDelay); parser.setData(buffer.array()); GifHeader header = parser.parseHeader(); GifFrame frame = header.frames.get(0); // Convert delay in 100ths of a second to ms. assertEquals(frameDelay * 10, frame.delay); } @Test public void testSetsDefaultFrameDelayIfFrameDelayIsZero() { ByteBuffer buffer = writeHeaderWithGceAndFrameDelay((short) 0); parser.setData(buffer.array()); GifHeader header = parser.parseHeader(); GifFrame frame = header.frames.get(0); // Convert delay in 100ths of a second to ms. assertEquals(GifHeaderParser.DEFAULT_FRAME_DELAY * 10, frame.delay); } @Test public void testSetsDefaultFrameDelayIfFrameDelayIsLessThanMinimum() { final short frameDelay = GifHeaderParser.MIN_FRAME_DELAY - 1; ByteBuffer buffer = writeHeaderWithGceAndFrameDelay(frameDelay); parser.setData(buffer.array()); GifHeader header = parser.parseHeader(); GifFrame frame = header.frames.get(0); // Convert delay in 100ths of a second to ms. assertEquals(GifHeaderParser.DEFAULT_FRAME_DELAY * 10, frame.delay); } @Test public void testObeysFrameDelayIfFrameDelayIsAtMinimum() { final short frameDelay = GifHeaderParser.MIN_FRAME_DELAY; ByteBuffer buffer = writeHeaderWithGceAndFrameDelay(frameDelay); parser.setData(buffer.array()); GifHeader header = parser.parseHeader(); GifFrame frame = header.frames.get(0); // Convert delay in 100ths of a second to ms. assertEquals(frameDelay * 10, frame.delay); } @Test public void testSetsFrameLocalColorTableToNullIfNoColorTable() { final int lzwMinCodeSize = 2; ByteBuffer buffer = ByteBuffer.allocate( GifBytesTestUtil.HEADER_LENGTH + GifBytesTestUtil.IMAGE_DESCRIPTOR_LENGTH + GifBytesTestUtil .getImageDataSize(lzwMinCodeSize)).order(ByteOrder.LITTLE_ENDIAN); GifBytesTestUtil.writeHeaderAndLsd(buffer, 1, 1, false, 0); GifBytesTestUtil.writeImageDescriptor(buffer, 0, 0, 1, 1, false /*hasLct*/, 0); GifBytesTestUtil.writeFakeImageData(buffer, lzwMinCodeSize); parser.setData(buffer.array()); GifHeader header = parser.parseHeader(); assertEquals(1, header.width); assertEquals(1, header.height); assertEquals(1, header.frameCount); assertNotNull(header.frames.get(0)); assertNull(header.frames.get(0).lct); } @Test public void testSetsFrameLocalColorTableIfHasColorTable() { final int lzwMinCodeSize = 2; final int numColors = 4; ByteBuffer buffer = ByteBuffer.allocate( GifBytesTestUtil.HEADER_LENGTH + GifBytesTestUtil.IMAGE_DESCRIPTOR_LENGTH + GifBytesTestUtil .getImageDataSize(lzwMinCodeSize) + GifBytesTestUtil.getColorTableLength(numColors)) .order(ByteOrder.LITTLE_ENDIAN); GifBytesTestUtil.writeHeaderAndLsd(buffer, 1, 1, false, 0); GifBytesTestUtil.writeImageDescriptor(buffer, 0, 0, 1, 1, true /*hasLct*/, numColors); GifBytesTestUtil.writeColorTable(buffer, numColors); GifBytesTestUtil.writeFakeImageData(buffer, 2); parser.setData(buffer.array()); GifHeader header = parser.parseHeader(); assertEquals(1, header.width); assertEquals(1, header.height); assertEquals(1, header.frameCount); assertNotNull(header.frames.get(0)); GifFrame frame = header.frames.get(0); assertNotNull(frame.lct); } @Test public void testCanParseMultipleFrames() { final int lzwMinCodeSize = 2; final int expectedFrames = 3; final int frameSize = GifBytesTestUtil.IMAGE_DESCRIPTOR_LENGTH + GifBytesTestUtil .getImageDataSize(lzwMinCodeSize); ByteBuffer buffer = ByteBuffer.allocate(GifBytesTestUtil.HEADER_LENGTH + expectedFrames * frameSize) .order(ByteOrder.LITTLE_ENDIAN); GifBytesTestUtil.writeHeaderAndLsd(buffer, 1, 1, false, 0); for (int i = 0; i < expectedFrames; i++) { GifBytesTestUtil.writeImageDescriptor(buffer, 0, 0, 1, 1, false /*hasLct*/, 0 /*numColors*/); GifBytesTestUtil.writeFakeImageData(buffer, 2); } parser.setData(buffer.array()); GifHeader header = parser.parseHeader(); assertEquals(expectedFrames, header.frameCount); assertEquals(expectedFrames, header.frames.size()); } @Test public void testIsAnimatedMultipleFrames() { final int lzwMinCodeSize = 2; final int numFrames = 3; final int frameSize = GifBytesTestUtil.IMAGE_DESCRIPTOR_LENGTH + GifBytesTestUtil.getImageDataSize(lzwMinCodeSize); ByteBuffer buffer = ByteBuffer.allocate(GifBytesTestUtil.HEADER_LENGTH + numFrames * frameSize) .order(ByteOrder.LITTLE_ENDIAN); GifBytesTestUtil.writeHeaderAndLsd(buffer, 1, 1, false, 0); for (int i = 0; i < numFrames; i++) { GifBytesTestUtil.writeImageDescriptor(buffer, 0, 0, 1, 1, false /*hasLct*/, 0 /*numColors*/); GifBytesTestUtil.writeFakeImageData(buffer, 2); } parser.setData(buffer.array()); assertTrue(parser.isAnimated()); } @Test public void testIsNotAnimatedOneFrame() { final int lzwMinCodeSize = 2; final int frameSize = GifBytesTestUtil.IMAGE_DESCRIPTOR_LENGTH + GifBytesTestUtil.getImageDataSize(lzwMinCodeSize); ByteBuffer buffer = ByteBuffer.allocate(GifBytesTestUtil.HEADER_LENGTH + frameSize) .order(ByteOrder.LITTLE_ENDIAN); GifBytesTestUtil.writeHeaderAndLsd(buffer, 1, 1, false, 0); GifBytesTestUtil.writeImageDescriptor(buffer, 0, 0, 1, 1, false /*hasLct*/, 0 /*numColors*/); GifBytesTestUtil.writeFakeImageData(buffer, 2); parser.setData(buffer.array()); assertFalse(parser.isAnimated()); } @Test(expected = IllegalStateException.class) public void testThrowsIfParseHeaderCalledBeforeSetData() { GifHeaderParser parser = new GifHeaderParser(); parser.parseHeader(); } }