package com.bumptech.glide.load.resource.gif;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.isA;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.graphics.Bitmap;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.NonNull;
import com.bumptech.glide.Glide;
import com.bumptech.glide.RequestBuilder;
import com.bumptech.glide.RequestManager;
import com.bumptech.glide.gifdecoder.GifDecoder;
import com.bumptech.glide.load.Transformation;
import com.bumptech.glide.load.resource.gif.GifFrameLoader.DelayTarget;
import com.bumptech.glide.load.resource.gif.GifFrameLoader.FrameCallback;
import com.bumptech.glide.request.Request;
import com.bumptech.glide.request.RequestOptions;
import com.bumptech.glide.request.target.Target;
import com.bumptech.glide.tests.Util.ReturnsSelfAnswer;
import com.bumptech.glide.util.Util;
import com.google.common.testing.EqualsTester;
import java.nio.ByteBuffer;
import java.util.UUID;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
@RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE, sdk = 18)
public class GifFrameLoaderTest {
@Mock GifFrameLoader.FrameCallback callback;
@Mock GifDecoder gifDecoder;
@Mock Handler handler;
@Mock Transformation<Bitmap> transformation;
@Mock RequestManager requestManager;
private GifFrameLoader loader;
private RequestBuilder<Bitmap> requestBuilder;
private Bitmap firstFrame;
@SuppressWarnings("unchecked")
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(handler.obtainMessage(anyInt(), isA(DelayTarget.class))).thenReturn(mock(Message.class));
firstFrame = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
ByteBuffer byteBuffer = ByteBuffer.allocate(10);
when(gifDecoder.getData()).thenReturn(byteBuffer);
requestBuilder = mock(RequestBuilder.class, new ReturnsSelfAnswer());
loader = createGifFrameLoader(handler);
}
@After
public void tearDown() {
Glide.tearDown();
}
@NonNull
private GifFrameLoader createGifFrameLoader(Handler handler) {
Glide glide = getGlideSingleton();
return new GifFrameLoader(
glide.getBitmapPool(),
requestManager,
gifDecoder,
handler,
requestBuilder,
transformation,
firstFrame);
}
private static Glide getGlideSingleton() {
return Glide.get(RuntimeEnvironment.application);
}
@SuppressWarnings("unchecked")
@Test
public void testSetFrameTransformationSetsTransformationOnRequestBuilder() {
Transformation<Bitmap> transformation = mock(Transformation.class);
loader.setFrameTransformation(transformation, firstFrame);
verify(requestBuilder, times(2)).apply(isA(RequestOptions.class));
}
@Test(expected = NullPointerException.class)
public void testSetFrameTransformationThrowsIfGivenNullTransformation() {
loader.setFrameTransformation(null, null);
}
@Test
public void testReturnsSizeFromGifDecoderAndCurrentFrame() {
int decoderByteSize = 123456;
when(gifDecoder.getByteSize()).thenReturn(decoderByteSize);
assertThat(loader.getSize()).isEqualTo(decoderByteSize + Util.getBitmapByteSize(firstFrame));
}
@Test
public void testStartGetsNextFrameIfNotStartedAndWithNoLoadPending() {
loader.subscribe(callback);
verify(requestBuilder).into(aTarget());
}
@Test
public void testGetNextFrameIncrementsSignatureAndAdvancesDecoderBeforeStartingLoad() {
loader.subscribe(callback);
InOrder order = inOrder(gifDecoder, requestBuilder);
order.verify(gifDecoder).advance();
order.verify(requestBuilder).apply(isA(RequestOptions.class));
order.verify(requestBuilder).into(aTarget());
}
@Test
public void testGetCurrentFrameReturnsFirstFrameWHenNoLoadHasCompleted() {
assertThat(loader.getCurrentFrame()).isEqualTo(firstFrame);
}
@Test
public void testGetCurrentFrameReturnsCurrentBitmapAfterLoadHasCompleted() {
final Bitmap result = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888);
DelayTarget target = mock(DelayTarget.class);
when(target.getResource()).thenReturn(result);
loader.onFrameReady(target);
assertEquals(result, loader.getCurrentFrame());
}
@Test
public void testStartDoesNotStartIfAlreadyRunning() {
loader.subscribe(callback);
loader.subscribe(mock(FrameCallback.class));
verify(requestBuilder, times(1)).into(aTarget());
}
@Test
public void testGetNextFrameDoesNotStartLoadIfLoaderIsNotRunning() {
loader.onFrameReady(mock(DelayTarget.class));
verify(requestBuilder, never()).into(aTarget());
}
@Test
public void testGetNextFrameDoesNotStartLoadIfLoadIsInProgress() {
loader.subscribe(callback);
loader.unsubscribe(callback);
loader.subscribe(callback);
verify(requestBuilder, times(1)).into(aTarget());
}
@Test
public void testGetNextFrameDoesStartLoadIfRestartedAndNoLoadIsInProgress() {
loader.subscribe(callback);
loader.unsubscribe(callback);
loader.onFrameReady(mock(DelayTarget.class));
loader.subscribe(callback);
verify(requestBuilder, times(2)).into(aTarget());
}
@Test
public void testGetNextFrameDoesStartLoadAfterLoadCompletesIfStarted() {
loader.subscribe(callback);
loader.onFrameReady(mock(DelayTarget.class));
verify(requestBuilder, times(2)).into(aTarget());
}
@Test
public void testOnFrameReadyClearsPreviousFrame() {
// Force the loader to create a real Handler.
loader = createGifFrameLoader(null);
DelayTarget previous = mock(DelayTarget.class);
Request previousRequest = mock(Request.class);
when(previous.getRequest()).thenReturn(previousRequest);
when(previous.getResource()).thenReturn(Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888));
DelayTarget current = mock(DelayTarget.class);
when(current.getResource()).thenReturn(Bitmap.createBitmap(100, 100, Bitmap.Config.RGB_565));
loader.onFrameReady(previous);
loader.onFrameReady(current);
verify(requestManager).clear(eq(previous));
}
@Test
public void testOnFrameReadyWithNullResourceDoesNotClearPreviousFrame() {
// Force the loader to create a real Handler by passing null.
loader = createGifFrameLoader(null);
DelayTarget previous = mock(DelayTarget.class);
Request previousRequest = mock(Request.class);
when(previous.getRequest()).thenReturn(previousRequest);
when(previous.getResource()).thenReturn(Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888));
DelayTarget current = mock(DelayTarget.class);
when(current.getResource()).thenReturn(null);
loader.onFrameReady(previous);
loader.onFrameReady(current);
verify(previousRequest, never()).clear();
}
@Test
public void testDelayTargetSendsMessageWithHandlerDelayed() {
long targetTime = 1234;
DelayTarget delayTarget = new DelayTarget(handler, 1, targetTime);
delayTarget.onResourceReady(Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888), null
/*glideAnimation*/);
verify(handler).sendMessageAtTime(isA(Message.class), eq(targetTime));
}
@Test
public void testDelayTargetSetsResourceOnResourceReady() {
DelayTarget delayTarget = new DelayTarget(handler, 1, 1);
Bitmap expected = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
delayTarget.onResourceReady(expected, null /*glideAnimation*/);
assertEquals(expected, delayTarget.getResource());
}
@Test
public void testClearsCompletedLoadOnFrameReadyIfCleared() {
// Force the loader to create a real Handler by passing null;
loader = createGifFrameLoader(null);
loader.clear();
DelayTarget delayTarget = mock(DelayTarget.class);
Request request = mock(Request.class);
when(delayTarget.getRequest()).thenReturn(request);
loader.onFrameReady(delayTarget);
verify(requestManager).clear(eq(delayTarget));
}
@Test
public void
testDoesNotReturnResourceForCompletedFrameInGetCurrentFrameIfLoadCompletesWhileCleared() {
loader.clear();
DelayTarget delayTarget = mock(DelayTarget.class);
Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
when(delayTarget.getResource()).thenReturn(bitmap);
loader.onFrameReady(delayTarget);
assertNull(loader.getCurrentFrame());
}
@Test
public void testFrameSignatureEquality() {
UUID first = UUID.randomUUID();
new EqualsTester().addEqualityGroup(new GifFrameLoader.FrameSignature(first),
new GifFrameLoader.FrameSignature(first))
.addEqualityGroup(new GifFrameLoader.FrameSignature()).testEquals();
}
@SuppressWarnings("unchecked")
private static Target<Bitmap> aTarget() {
return isA(Target.class);
}
}