/*
* 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.drawee.view;
import android.graphics.drawable.Drawable;
import android.view.MotionEvent;
import com.facebook.drawee.drawable.DrawableTestUtils;
import com.facebook.drawee.interfaces.DraweeHierarchy;
import com.facebook.drawee.interfaces.DraweeController;
import com.facebook.drawee.testing.DraweeMocks;
import org.robolectric.RobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InOrder;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
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;
@RunWith(RobolectricTestRunner.class)
public class DraweeHolderTest {
private DraweeHolder mDraweeHolder;
private Drawable mTopLevelDrawable;
private DraweeHierarchy mDraweeHierarchy;
private DraweeController mController;
private InOrder mInOrderVerifier;
@Before
public void setUp() {
mTopLevelDrawable = DrawableTestUtils.mockDrawable();
mDraweeHierarchy = DraweeMocks.mockDraweeHierarchyOf(mTopLevelDrawable);
mController = DraweeMocks.mockController();
mDraweeHolder = new DraweeHolder(mDraweeHierarchy);
mInOrderVerifier = inOrder(mController);
}
@Test
public void testOverrideControllerHierarchy() {
DraweeHierarchy otherHierarchy = mock(DraweeHierarchy.class);
mController.setHierarchy(otherHierarchy);
assertSame(otherHierarchy, mController.getHierarchy());
mDraweeHolder.setController(mController);
assertSame(mController, mDraweeHolder.getController());
assertSame(mDraweeHierarchy, mDraweeHolder.getHierarchy());
assertSame(mDraweeHierarchy, mController.getHierarchy());
}
@Test
public void testSetControllerWithoutHierarchy() {
mDraweeHolder.setController(mController);
assertSame(mController, mDraweeHolder.getController());
assertSame(mDraweeHierarchy, mDraweeHolder.getHierarchy());
assertSame(mDraweeHierarchy, mController.getHierarchy());
}
@Test
public void testSetControllerBeforeHierarchy() {
mDraweeHolder = new DraweeHolder(null);
mDraweeHolder.setController(mController);
mDraweeHolder.setHierarchy(mDraweeHierarchy);
assertSame(mController, mDraweeHolder.getController());
assertSame(mDraweeHierarchy, mDraweeHolder.getHierarchy());
assertSame(mDraweeHierarchy, mController.getHierarchy());
}
@Test
public void testClearControllerKeepsHierarchy() {
mDraweeHolder.setController(mController);
mDraweeHolder.setController(null);
assertSame(mDraweeHierarchy, mDraweeHolder.getHierarchy());
assertNull(mDraweeHolder.getController());
assertNull(mController.getHierarchy());
}
@Test
public void testNewControllerKeepsHierarchy() {
mDraweeHolder.setController(mController);
assertSame(mDraweeHierarchy, mDraweeHolder.getHierarchy());
DraweeController another = DraweeMocks.mockController();
mDraweeHolder.setController(another);
assertSame(mDraweeHierarchy, mDraweeHolder.getHierarchy());
assertSame(another, mDraweeHolder.getController());
assertNull(mController.getHierarchy());
assertSame(mDraweeHierarchy, another.getHierarchy());
}
@Test
public void testLifecycle() {
mDraweeHolder.setController(mController);
assertFalse(mDraweeHolder.isAttached());
mDraweeHolder.onAttach();
assertTrue(mDraweeHolder.isAttached());
mDraweeHolder.onDetach();
assertFalse(mDraweeHolder.isAttached());
verify(mController).onAttach();
verify(mController).onDetach();
}
@Test
public void testSetControllerWhenAlreadyAttached() {
mDraweeHolder.onAttach();
mDraweeHolder.setController(mController);
mDraweeHolder.onDetach();
verify(mController).onAttach();
verify(mController).onDetach();
}
@Test
public void testSetNullController() {
mDraweeHolder.setController(null);
mDraweeHolder.onAttach();
mDraweeHolder.onDetach();
mDraweeHolder.onAttach();
}
@Test
public void testSetNewControllerWithInvalidController() {
final DraweeHierarchy draweeHierarchy2 = DraweeMocks.mockDraweeHierarchyOf(mTopLevelDrawable);
final DraweeHolder draweeHolder2 = new DraweeHolder(draweeHierarchy2);
mDraweeHolder.onAttach();
mDraweeHolder.setController(mController);
draweeHolder2.setController(mController);
mDraweeHolder.setController(null);
verify(mController, never()).onDetach();
assertEquals(draweeHierarchy2, mController.getHierarchy());
}
@Test
public void testSetNewHierarchyWithInvalidController() {
final DraweeHierarchy draweeHierarchy2 = DraweeMocks.mockDraweeHierarchyOf(mTopLevelDrawable);
final DraweeHolder draweeHolder2 = new DraweeHolder(draweeHierarchy2);
mDraweeHolder.setController(mController);
draweeHolder2.setController(mController);
final DraweeHierarchy draweeHierarchy3 = DraweeMocks.mockDraweeHierarchyOf(mTopLevelDrawable);
mDraweeHolder.setHierarchy(draweeHierarchy3);
assertEquals(draweeHierarchy2, mController.getHierarchy());
}
@Test
public void testOnDetachWithInvalidController() {
final DraweeHierarchy draweeHierarchy2 = DraweeMocks.mockDraweeHierarchyOf(mTopLevelDrawable);
final DraweeHolder draweeHolder2 = new DraweeHolder(draweeHierarchy2);
mDraweeHolder.onAttach();
mDraweeHolder.setController(mController);
draweeHolder2.setController(mController);
mDraweeHolder.onDetach();
verify(mController, never()).onDetach();
}
@Test
public void testTouchEventWithInvalidController() {
final DraweeHierarchy draweeHierarchy2 = DraweeMocks.mockDraweeHierarchyOf(mTopLevelDrawable);
final DraweeHolder draweeHolder2 = new DraweeHolder(draweeHierarchy2);
mDraweeHolder.setController(mController);
draweeHolder2.setController(mController);
mDraweeHolder.onTouchEvent(mock(MotionEvent.class));
verify(mController, never()).onTouchEvent(any(MotionEvent.class));
}
/** There are 8 possible state transitions with two variables
* 1. (visible, unattached) -> (visible, attached)
* 2. (visible, attached) -> (invisible, attached)
* 3. (invisible, attached) -> (invisible, unattached)
* 4. (invisible, unattached) -> (visible, unattached)
* 5. (visible, unattached) -> (invisible, unattached)
* 6. (invisible, unattached) -> (invisible, attached)
* 7. (invisible, attached) -> (visible, attached)
* 8. (visible, attached) -> (visible, unattached)
*/
@Test
public void testVisibilityStateTransitions() {
boolean restart = true;
// Initial state (mIsVisible, !mIsHolderAttached)
mDraweeHolder.setController(mController);
verifyControllerLifecycleCalls(0, 0);
/** 1 */
mDraweeHolder.onAttach();
verifyControllerLifecycleCalls(1, 0);
/** 2 */
mTopLevelDrawable.setVisible(false, restart);
verifyControllerLifecycleCalls(0, 1);
/** 3 */
mDraweeHolder.onDetach();
verifyControllerLifecycleCalls(0, 0);
/** 4 */
mTopLevelDrawable.setVisible(true, restart);
verifyControllerLifecycleCalls(0, 0);
/** 5 */
mTopLevelDrawable.setVisible(false, restart);
verifyControllerLifecycleCalls(0, 0);
/** 6 */
mDraweeHolder.onAttach();
verifyControllerLifecycleCalls(0, 0);
/** 7 */
mTopLevelDrawable.setVisible(true, restart);
verifyControllerLifecycleCalls(1, 0);
/** 8 */
mDraweeHolder.onDetach();
verifyControllerLifecycleCalls(0, 1);
}
private void verifyControllerLifecycleCalls(int numOnAttach, int numOnDetach) {
mInOrderVerifier.verify(mController, times(numOnAttach)).onAttach();
mInOrderVerifier.verify(mController, times(numOnDetach)).onDetach();
}
}