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();
}
}