/* * Copyright (c) 2015-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.imagepipeline.producers; import java.util.Map; import com.facebook.cache.common.CacheKey; import com.facebook.common.internal.ImmutableMap; import com.facebook.common.references.CloseableReference; import com.facebook.imagepipeline.cache.BitmapMemoryCacheKey; import com.facebook.imagepipeline.cache.CacheKeyFactory; import com.facebook.imagepipeline.cache.MemoryCache; import com.facebook.imagepipeline.image.CloseableImage; import com.facebook.imagepipeline.image.ImmutableQualityInfo; import com.facebook.imagepipeline.request.ImageRequest; import org.junit.*; import org.junit.runner.*; import org.mockito.*; import org.mockito.invocation.*; import org.mockito.stubbing.*; import org.robolectric.*; import org.robolectric.annotation.*; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.*; /** * Checks basic properties of bitmap memory cache producer operation, that is: * - it delegates to the {@link MemoryCache#get(Object)}. * - if get is successful, then returned reference is closed. * - if {@link MemoryCache#get(Object)} is unsuccessful, then it passes the * request to the next producer in the sequence. * - responses from the next producer are passed directly to the consumer. * - listener methods are called as expected. */ @RunWith(RobolectricTestRunner.class) @Config(manifest= Config.NONE) public class BitmapMemoryCacheGetProducerTest { private static final String PRODUCER_NAME = BitmapMemoryCacheGetProducer.PRODUCER_NAME; @Mock public MemoryCache<CacheKey, CloseableImage> mMemoryCache; @Mock public CacheKeyFactory mCacheKeyFactory; @Mock public Producer<CloseableReference<CloseableImage>> mInputProducer; @Mock public Consumer<CloseableReference<CloseableImage>> mConsumer; @Mock public ProducerContext mProducerContext; @Mock public ImageRequest mImageRequest; @Mock public ProducerListener mProducerListener; @Mock public Exception mException; @Mock public BitmapMemoryCacheKey mCacheKey; @Mock public Object mCallerContext; private final String mRequestId = "mRequestId"; private CloseableImage mCloseableImage1; private CloseableReference<CloseableImage> mFinalImageReference; private BitmapMemoryCacheGetProducer mBitmapMemoryCacheGetProducer; @Before public void setUp() { MockitoAnnotations.initMocks(this); mBitmapMemoryCacheGetProducer = new BitmapMemoryCacheGetProducer(mMemoryCache, mCacheKeyFactory, mInputProducer); mCloseableImage1 = mock(CloseableImage.class); mFinalImageReference = CloseableReference.of(mCloseableImage1); when(mCloseableImage1.getQualityInfo()).thenReturn(ImmutableQualityInfo.FULL_QUALITY); when(mProducerContext.getImageRequest()).thenReturn(mImageRequest); when(mProducerContext.getListener()).thenReturn(mProducerListener); when(mProducerContext.getId()).thenReturn(mRequestId); when(mProducerListener.requiresExtraMap(mRequestId)).thenReturn(true); when(mProducerContext.getLowestPermittedRequestLevel()) .thenReturn(ImageRequest.RequestLevel.FULL_FETCH); when(mProducerContext.getCallerContext()) .thenReturn(PRODUCER_NAME); when(mCacheKeyFactory.getBitmapCacheKey(mImageRequest, PRODUCER_NAME)).thenReturn(mCacheKey); } @Test public void testBitmapMemoryCacheGetSuccessful() { setupBitmapCacheGetSuccess(); when(mProducerContext.getLowestPermittedRequestLevel()) .thenReturn(ImageRequest.RequestLevel.BITMAP_MEMORY_CACHE); mBitmapMemoryCacheGetProducer.produceResults(mConsumer, mProducerContext); verify(mConsumer).onNewResult(mFinalImageReference, Consumer.IS_LAST); verify(mProducerListener).onProducerStart(mRequestId, PRODUCER_NAME); Map<String, String> extraMap = ImmutableMap.of(BitmapMemoryCacheProducer.EXTRA_CACHED_VALUE_FOUND, "true"); verify(mProducerListener).onProducerFinishWithSuccess(mRequestId, PRODUCER_NAME, extraMap); verify(mProducerListener).onUltimateProducerReached(mRequestId, PRODUCER_NAME, true); Assert.assertTrue(!mFinalImageReference.isValid()); } @Test public void testBitmapMemoryCacheGetNotFoundInputProducerSuccess() { setupBitmapCacheGetNotFound(); setupInputProducerStreamingSuccess(); mBitmapMemoryCacheGetProducer.produceResults(mConsumer, mProducerContext); verify(mConsumer).onNewResult(mFinalImageReference, Consumer.IS_LAST); verify(mProducerListener).onProducerStart(mRequestId, PRODUCER_NAME); Map<String, String> extraMap = ImmutableMap.of(BitmapMemoryCacheProducer.EXTRA_CACHED_VALUE_FOUND, "false"); verify(mProducerListener).onProducerFinishWithSuccess(mRequestId, PRODUCER_NAME, extraMap); verify(mProducerListener, never()) .onUltimateProducerReached(anyString(), anyString(), anyBoolean()); } @Test public void testBitmapMemoryCacheGetNotFoundInputProducerNotFound() { setupBitmapCacheGetNotFound(); setupInputProducerNotFound(); mBitmapMemoryCacheGetProducer.produceResults(mConsumer, mProducerContext); verify(mConsumer).onNewResult(null, Consumer.IS_LAST); verify(mProducerListener).onProducerStart(mRequestId, PRODUCER_NAME); Map<String, String> extraMap = ImmutableMap.of(BitmapMemoryCacheProducer.EXTRA_CACHED_VALUE_FOUND, "false"); verify(mProducerListener).onProducerFinishWithSuccess(mRequestId, PRODUCER_NAME, extraMap); verify(mProducerListener, never()) .onUltimateProducerReached(anyString(), anyString(), anyBoolean()); } @Test public void testBitmapMemoryCacheGetNotFoundInputProducerFailure() { setupBitmapCacheGetNotFound(); setupInputProducerFailure(); mBitmapMemoryCacheGetProducer.produceResults(mConsumer, mProducerContext); verify(mConsumer).onFailure(mException); verify(mProducerListener).onProducerStart(mRequestId, PRODUCER_NAME); Map<String, String> extraMap = ImmutableMap.of(BitmapMemoryCacheProducer.EXTRA_CACHED_VALUE_FOUND, "false"); verify(mProducerListener).onProducerFinishWithSuccess(mRequestId, PRODUCER_NAME, extraMap); verify(mProducerListener, never()) .onUltimateProducerReached(anyString(), anyString(), anyBoolean()); } @Test public void testBitmapMemoryCacheGetNotFoundLowestLevelReached() { setupBitmapCacheGetNotFound(); when(mProducerContext.getLowestPermittedRequestLevel()) .thenReturn(ImageRequest.RequestLevel.BITMAP_MEMORY_CACHE); mBitmapMemoryCacheGetProducer.produceResults(mConsumer, mProducerContext); verify(mConsumer).onNewResult(null, Consumer.IS_LAST); verify(mProducerListener).onProducerStart(mRequestId, PRODUCER_NAME); Map<String, String> extraMap = ImmutableMap.of(BitmapMemoryCacheProducer.EXTRA_CACHED_VALUE_FOUND, "false"); verify(mProducerListener).onProducerFinishWithSuccess(mRequestId, PRODUCER_NAME, extraMap); verify(mProducerListener).onUltimateProducerReached(mRequestId, PRODUCER_NAME, false); verifyNoMoreInteractions(mInputProducer); } private void setupBitmapCacheGetSuccess() { when(mMemoryCache.get(eq(mCacheKey))).thenReturn(mFinalImageReference); } private void setupBitmapCacheGetNotFound() { when(mMemoryCache.get(eq(mCacheKey))).thenReturn(null); } private void setupInputProducerStreamingSuccess() { doAnswer(new ProduceResultsNewResultAnswer(mFinalImageReference)) .when(mInputProducer).produceResults(any(Consumer.class), eq(mProducerContext)); } private void setupInputProducerNotFound() { doAnswer(new ProduceResultsNewResultAnswer(null)) .when(mInputProducer).produceResults(any(Consumer.class), eq(mProducerContext)); } private void setupInputProducerFailure() { doAnswer(new ProduceResultsFailureAnswer()). when(mInputProducer).produceResults(any(Consumer.class), eq(mProducerContext)); } private static class ProduceResultsNewResultAnswer implements Answer<Void> { private final CloseableReference<CloseableImage> mResult; private ProduceResultsNewResultAnswer(CloseableReference<CloseableImage> result) { mResult = result; } @Override public Void answer(InvocationOnMock invocation) throws Throwable { Consumer consumer = (Consumer) invocation.getArguments()[0]; consumer.onNewResult(mResult, Consumer.IS_LAST); return null; } } private class ProduceResultsFailureAnswer implements Answer<Void> { @Override public Void answer(InvocationOnMock invocation) throws Throwable { Consumer consumer = (Consumer) invocation.getArguments()[0]; consumer.onFailure(mException); return null; } } }