/* * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ package com.facebook.fresco.animation.bitmap.preparation; import android.graphics.Bitmap; import com.facebook.common.references.CloseableReference; import com.facebook.fresco.animation.backend.AnimationBackend; import com.facebook.fresco.animation.bitmap.BitmapAnimationBackend; import com.facebook.fresco.animation.bitmap.BitmapFrameCache; import com.facebook.fresco.animation.bitmap.BitmapFrameRenderer; import com.facebook.imagepipeline.bitmaps.PlatformBitmapFactory; import com.facebook.imagepipeline.testing.FakeClock; import com.facebook.imagepipeline.testing.TestExecutorService; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import static org.fest.assertions.api.Assertions.assertThat; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; /** * Tests {@link DefaultBitmapFramePreparer}. */ @RunWith(PowerMockRunner.class) @PrepareForTest(CloseableReference.class) public class DefaultBitmapFramePreparerTest { private static final int FRAME_COUNT = 10; private static final int BACKEND_INTRINSIC_WIDTH = 160; private static final int BACKEND_INTRINSIC_HEIGHT = 90; private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888; @Mock public AnimationBackend mAnimationBackend; @Mock public BitmapFrameCache mBitmapFrameCache; @Mock public PlatformBitmapFactory mPlatformBitmapFactory; @Mock public BitmapFrameRenderer mBitmapFrameRenderer; @Mock public CloseableReference<Bitmap> mBitmapReference; @Mock public Bitmap mBitmap; private FakeClock mFakeClock; private TestExecutorService mExecutorService; private DefaultBitmapFramePreparer mDefaultBitmapFramePreparer; @Before public void setup() { MockitoAnnotations.initMocks(this); mFakeClock = new FakeClock(); mExecutorService = new TestExecutorService(mFakeClock); mDefaultBitmapFramePreparer = new DefaultBitmapFramePreparer( mPlatformBitmapFactory, mBitmapFrameRenderer, BITMAP_CONFIG, mExecutorService); when(mAnimationBackend.getFrameCount()).thenReturn(FRAME_COUNT); when(mAnimationBackend.getIntrinsicWidth()).thenReturn(BACKEND_INTRINSIC_WIDTH); when(mAnimationBackend.getIntrinsicHeight()).thenReturn(BACKEND_INTRINSIC_HEIGHT); when(mBitmapReference.isValid()).thenReturn(true); when(mBitmapReference.get()).thenReturn(mBitmap); } @Test public void testPrepareFrame_whenBitmapAlreadyCached_thenDoNothing() { when(mBitmapFrameCache.contains(1)).thenReturn(true); when(mBitmapFrameRenderer.renderFrame(1, mBitmap)).thenReturn(true); mDefaultBitmapFramePreparer.prepareFrame(mBitmapFrameCache, mAnimationBackend, 1); assertThat(mExecutorService.getScheduledQueue().isIdle()).isTrue(); verify(mBitmapFrameCache).contains(1); verifyNoMoreInteractions(mBitmapFrameCache); verifyZeroInteractions(mPlatformBitmapFactory, mBitmapFrameRenderer, mBitmapReference); } @Test public void testPrepareFrame_whenNoBitmapAvailable_thenDoNothing() { mDefaultBitmapFramePreparer.prepareFrame(mBitmapFrameCache, mAnimationBackend, 1); verify(mBitmapFrameCache).contains(1); verifyNoMoreInteractions(mBitmapFrameCache); reset(mBitmapFrameCache); mExecutorService.getScheduledQueue().runNextPendingCommand(); verify(mBitmapFrameCache).contains(1); verify(mBitmapFrameCache).getBitmapToReuseForFrame( 1, BACKEND_INTRINSIC_WIDTH, BACKEND_INTRINSIC_HEIGHT); verifyNoMoreInteractions(mBitmapFrameCache); verify(mPlatformBitmapFactory).createBitmap( BACKEND_INTRINSIC_WIDTH, BACKEND_INTRINSIC_HEIGHT, BITMAP_CONFIG); verifyZeroInteractions(mBitmapFrameRenderer); } @Test public void testPrepareFrame_whenReusedBitmapAvailable_thenCacheReusedBitmap() { when(mBitmapFrameCache.getBitmapToReuseForFrame( 1, BACKEND_INTRINSIC_WIDTH, BACKEND_INTRINSIC_HEIGHT)) .thenReturn(mBitmapReference); when(mBitmapFrameRenderer.renderFrame(1, mBitmap)).thenReturn(true); mDefaultBitmapFramePreparer.prepareFrame(mBitmapFrameCache, mAnimationBackend, 1); mExecutorService.getScheduledQueue().runNextPendingCommand(); verify(mBitmapFrameCache, times(2)).contains(1); verify(mBitmapFrameCache).getBitmapToReuseForFrame( 1, BACKEND_INTRINSIC_WIDTH, BACKEND_INTRINSIC_HEIGHT); verify(mBitmapFrameRenderer).renderFrame(1, mBitmap); verify(mBitmapFrameCache).onFramePrepared( 1, mBitmapReference, BitmapAnimationBackend.FRAME_TYPE_REUSED); verifyZeroInteractions(mPlatformBitmapFactory); } @Test public void testPrepareFrame_whenPlatformBitmapAvailable_thenCacheCreatedBitmap() { when(mPlatformBitmapFactory.createBitmap( BACKEND_INTRINSIC_WIDTH, BACKEND_INTRINSIC_HEIGHT, BITMAP_CONFIG)) .thenReturn(mBitmapReference); when(mBitmapFrameRenderer.renderFrame(1, mBitmap)).thenReturn(true); mDefaultBitmapFramePreparer.prepareFrame(mBitmapFrameCache, mAnimationBackend, 1); mExecutorService.getScheduledQueue().runNextPendingCommand(); verify(mBitmapFrameCache, times(2)).contains(1); verify(mBitmapFrameCache).getBitmapToReuseForFrame( 1, BACKEND_INTRINSIC_WIDTH, BACKEND_INTRINSIC_HEIGHT); verify(mPlatformBitmapFactory).createBitmap( BACKEND_INTRINSIC_WIDTH, BACKEND_INTRINSIC_HEIGHT, BITMAP_CONFIG); verify(mBitmapFrameRenderer).renderFrame(1, mBitmap); verify(mBitmapFrameCache).onFramePrepared( 1, mBitmapReference, BitmapAnimationBackend.FRAME_TYPE_CREATED); verifyNoMoreInteractions(mPlatformBitmapFactory); } @Test public void testPrepareFrame_whenReusedAndPlatformBitmapAvailable_thenCacheReusedBitmap() { when(mBitmapFrameCache.getBitmapToReuseForFrame( 1, BACKEND_INTRINSIC_WIDTH, BACKEND_INTRINSIC_HEIGHT)) .thenReturn(mBitmapReference); when(mPlatformBitmapFactory.createBitmap( BACKEND_INTRINSIC_WIDTH, BACKEND_INTRINSIC_HEIGHT, BITMAP_CONFIG)) .thenReturn(mBitmapReference); when(mBitmapFrameRenderer.renderFrame(1, mBitmap)).thenReturn(true); mDefaultBitmapFramePreparer.prepareFrame(mBitmapFrameCache, mAnimationBackend, 1); mExecutorService.getScheduledQueue().runNextPendingCommand(); verify(mBitmapFrameCache, times(2)).contains(1); verify(mBitmapFrameCache).getBitmapToReuseForFrame( 1, BACKEND_INTRINSIC_WIDTH, BACKEND_INTRINSIC_HEIGHT); verify(mBitmapFrameRenderer).renderFrame(1, mBitmap); verify(mBitmapFrameCache).onFramePrepared( 1, mBitmapReference, BitmapAnimationBackend.FRAME_TYPE_REUSED); verifyZeroInteractions(mPlatformBitmapFactory); } @Test public void testPrepareFrame_whenRenderingFails_thenDoNothing() { when(mBitmapFrameCache.getBitmapToReuseForFrame( 1, BACKEND_INTRINSIC_WIDTH, BACKEND_INTRINSIC_HEIGHT)) .thenReturn(mBitmapReference); when(mPlatformBitmapFactory.createBitmap( BACKEND_INTRINSIC_WIDTH, BACKEND_INTRINSIC_HEIGHT, BITMAP_CONFIG)) .thenReturn(mBitmapReference); when(mBitmapFrameRenderer.renderFrame(1, mBitmap)).thenReturn(false); mDefaultBitmapFramePreparer.prepareFrame(mBitmapFrameCache, mAnimationBackend, 1); mExecutorService.getScheduledQueue().runNextPendingCommand(); verify(mBitmapFrameCache, times(2)).contains(1); verify(mBitmapFrameCache).getBitmapToReuseForFrame( 1, BACKEND_INTRINSIC_WIDTH, BACKEND_INTRINSIC_HEIGHT); verify(mPlatformBitmapFactory).createBitmap( BACKEND_INTRINSIC_WIDTH, BACKEND_INTRINSIC_HEIGHT, BITMAP_CONFIG); verify(mBitmapFrameRenderer, times(2)).renderFrame(1, mBitmap); verifyNoMoreInteractions(mBitmapFrameCache); } }