/* * 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.ArrayList; import java.util.List; import java.util.Map; import android.graphics.Bitmap; import com.facebook.common.internal.ImmutableMap; import com.facebook.common.references.CloseableReference; import com.facebook.common.references.ResourceReleaser; import com.facebook.imagepipeline.bitmaps.PlatformBitmapFactory; import com.facebook.imagepipeline.image.CloseableImage; import com.facebook.imagepipeline.image.CloseableStaticBitmap; import com.facebook.imagepipeline.producers.PostprocessorProducer.SingleUsePostprocessorConsumer; import com.facebook.imagepipeline.request.ImageRequest; import com.facebook.imagepipeline.request.Postprocessor; import com.facebook.imagepipeline.testing.FakeClock; import com.facebook.imagepipeline.testing.TestExecutorService; 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.junit.Assert.*; import static org.mockito.Mockito.*; @RunWith(RobolectricTestRunner.class) @Config(manifest= Config.NONE) public class SingleUsePostprocessorProducerTest { private static final String POSTPROCESSOR_NAME = "postprocessor_name"; private static final Map<String, String> mExtraMap = ImmutableMap.of(PostprocessorProducer.POSTPROCESSOR, POSTPROCESSOR_NAME); @Mock public PlatformBitmapFactory mPlatformBitmapFactory; @Mock public ProducerContext mProducerContext; @Mock public ProducerListener mProducerListener; @Mock public Producer<CloseableReference<CloseableImage>> mInputProducer; @Mock public Consumer<CloseableReference<CloseableImage>> mConsumer; @Mock public Postprocessor mPostprocessor; @Mock public ResourceReleaser<Bitmap> mBitmapResourceReleaser; @Mock public ImageRequest mImageRequest; private String mRequestId = "mRequestId"; private Bitmap mSourceBitmap; private CloseableStaticBitmap mSourceCloseableStaticBitmap; private CloseableReference<CloseableImage> mSourceCloseableImageRef; private Bitmap mDestinationBitmap; private CloseableReference<Bitmap> mDestinationCloseableBitmapRef; private TestExecutorService mTestExecutorService; private PostprocessorProducer mPostprocessorProducer; private List<CloseableReference<CloseableImage>> mResults; private InOrder mInOrder; @Before public void setUp() { MockitoAnnotations.initMocks(this); mTestExecutorService = new TestExecutorService(new FakeClock()); mPostprocessorProducer = new PostprocessorProducer( mInputProducer, mPlatformBitmapFactory, mTestExecutorService); when(mImageRequest.getPostprocessor()).thenReturn(mPostprocessor); when(mProducerContext.getId()).thenReturn(mRequestId); when(mProducerContext.getListener()).thenReturn(mProducerListener); when(mProducerContext.getImageRequest()).thenReturn(mImageRequest); mResults = new ArrayList<>(); when(mPostprocessor.getName()).thenReturn(POSTPROCESSOR_NAME); when(mProducerListener.requiresExtraMap(mRequestId)).thenReturn(true); doAnswer( new Answer<Object>() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { mResults.add( ((CloseableReference<CloseableImage>) invocation.getArguments()[0]).clone()); return null; } } ).when(mConsumer).onNewResult(any(CloseableReference.class), anyInt()); mInOrder = inOrder(mPostprocessor, mProducerListener, mConsumer); mSourceBitmap = mock(Bitmap.class); mSourceCloseableStaticBitmap = mock(CloseableStaticBitmap.class); when(mSourceCloseableStaticBitmap.getUnderlyingBitmap()).thenReturn(mSourceBitmap); mSourceCloseableImageRef = CloseableReference.<CloseableImage>of(mSourceCloseableStaticBitmap); mDestinationBitmap = mock(Bitmap.class); mDestinationCloseableBitmapRef = CloseableReference.of(mDestinationBitmap, mBitmapResourceReleaser); } @Test public void testIntermediateImageIsNotProcessed() { SingleUsePostprocessorConsumer postprocessorConsumer = produceResults(); postprocessorConsumer.onNewResult(mSourceCloseableImageRef, Consumer.NO_FLAGS); mSourceCloseableImageRef.close(); mTestExecutorService.runUntilIdle(); mInOrder.verifyNoMoreInteractions(); assertEquals(0, mResults.size()); verify(mSourceCloseableStaticBitmap).close(); } @Test public void testSuccess() { SingleUsePostprocessorConsumer postprocessorConsumer = produceResults(); doReturn(mDestinationCloseableBitmapRef) .when(mPostprocessor).process(mSourceBitmap, mPlatformBitmapFactory); postprocessorConsumer.onNewResult(mSourceCloseableImageRef, Consumer.IS_LAST); mSourceCloseableImageRef.close(); mTestExecutorService.runUntilIdle(); mInOrder.verify(mProducerListener).onProducerStart(mRequestId, PostprocessorProducer.NAME); mInOrder.verify(mPostprocessor).process(mSourceBitmap, mPlatformBitmapFactory); mInOrder.verify(mProducerListener).requiresExtraMap(mRequestId); mInOrder.verify(mProducerListener) .onProducerFinishWithSuccess(mRequestId, PostprocessorProducer.NAME, mExtraMap); mInOrder.verify(mConsumer).onNewResult(any(CloseableReference.class), eq(Consumer.IS_LAST)); mInOrder.verifyNoMoreInteractions(); assertEquals(1, mResults.size()); CloseableReference<CloseableImage> res0 = mResults.get(0); assertTrue(CloseableReference.isValid(res0)); assertSame(mDestinationBitmap, ((CloseableStaticBitmap) res0.get()).getUnderlyingBitmap()); res0.close(); verify(mBitmapResourceReleaser).release(mDestinationBitmap); verify(mSourceCloseableStaticBitmap).close(); } @Test public void testFailure() { SingleUsePostprocessorConsumer postprocessorConsumer = produceResults(); doThrow(new RuntimeException()) .when(mPostprocessor).process(eq(mSourceBitmap), eq(mPlatformBitmapFactory)); postprocessorConsumer.onNewResult(mSourceCloseableImageRef, Consumer.IS_LAST); mSourceCloseableImageRef.close(); mTestExecutorService.runUntilIdle(); mInOrder.verify(mProducerListener).onProducerStart(mRequestId, PostprocessorProducer.NAME); mInOrder.verify(mPostprocessor).process(mSourceBitmap, mPlatformBitmapFactory); mInOrder.verify(mProducerListener).requiresExtraMap(mRequestId); mInOrder.verify(mProducerListener).onProducerFinishWithFailure( eq(mRequestId), eq(PostprocessorProducer.NAME), any(RuntimeException.class), eq(mExtraMap)); mInOrder.verify(mConsumer).onFailure(any(IllegalStateException.class)); mInOrder.verifyNoMoreInteractions(); assertEquals(0, mResults.size()); verify(mSourceCloseableStaticBitmap).close(); } private SingleUsePostprocessorConsumer produceResults() { mPostprocessorProducer.produceResults(mConsumer, mProducerContext); ArgumentCaptor<Consumer> consumerCaptor = ArgumentCaptor.forClass(Consumer.class); verify(mInputProducer).produceResults(consumerCaptor.capture(), eq(mProducerContext)); return (SingleUsePostprocessorConsumer) consumerCaptor.getValue(); } }