/* * 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.litho.utils; import android.content.Context; import android.graphics.Rect; import android.view.View; import android.view.ViewGroup; import com.facebook.litho.LithoView; import com.facebook.litho.config.ComponentsConfiguration; import com.facebook.litho.testing.testrunner.ComponentsTestRunner; import com.facebook.litho.utils.IncrementalMountUtils.WrapperView; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import static org.assertj.core.api.Java6Assertions.assertThat; import static org.mockito.Matchers.any; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; /** * Tests {@link IncrementalMountUtils} */ @RunWith(ComponentsTestRunner.class) public class IncrementalMountUtilsTest { private static final int SCROLLING_VIEW_WIDTH = 100; private static final int SCROLLING_VIEW_HEIGHT = 1000; public TestWrapperView mWrapperView = mock(TestWrapperView.class); public LithoView mLithoView = mock(LithoView.class); public ViewGroup mViewGroup = mock(ViewGroup.class); private final Rect mMountedRect = new Rect(); @Before public void setUp() { ComponentsConfiguration.isIncrementalMountOnOffsetOrTranslationChangeEnabled = false; when(mLithoView.isIncrementalMountEnabled()).thenReturn(true); when(mWrapperView.getWrappedView()).thenReturn(mLithoView); when(mViewGroup.getChildCount()).thenReturn(1); when(mViewGroup.getChildAt(0)).thenReturn(mLithoView); when(mViewGroup.getWidth()).thenReturn(SCROLLING_VIEW_WIDTH); when(mViewGroup.getHeight()).thenReturn(SCROLLING_VIEW_HEIGHT); // Can't use verify as the rect is reset when it is released back to the pool, which occurs // before we can check it. doAnswer( new Answer() { @Override public Void answer(InvocationOnMock invocation) throws Throwable { mMountedRect.set((Rect) invocation.getArguments()[0]); return null; } }).when(mLithoView).performIncrementalMount(any(Rect.class)); } @After public void tearDown() { ComponentsConfiguration.isIncrementalMountOnOffsetOrTranslationChangeEnabled = true; } @Test public void testIncrementalMountForLithoViewVisibleAtTop() { setupViewBounds(mLithoView, 0, -10, SCROLLING_VIEW_WIDTH, 10); IncrementalMountUtils.performIncrementalMount(mViewGroup); verifyPerformIncrementalMountCalled(new Rect(0, 10, SCROLLING_VIEW_WIDTH, 20)); } @Test public void testIncrementalMountForLithoViewVisibleAtTopWithTranslationYPartialIn() { setupViewBounds(mLithoView, 0, -10, SCROLLING_VIEW_WIDTH, 10); setupViewTranslations(mLithoView, 0, 5); IncrementalMountUtils.performIncrementalMount(mViewGroup); verifyPerformIncrementalMountCalled(new Rect(0, 5, SCROLLING_VIEW_WIDTH, 20)); } @Test public void testIncrementalMountForLithoViewVisibleAtTopWithTranslationYFullyOut() { setupViewBounds(mLithoView, 0, -10, SCROLLING_VIEW_WIDTH, 10); setupViewTranslations(mLithoView, 0, -15); IncrementalMountUtils.performIncrementalMount(mViewGroup); verify(mLithoView, never()).performIncrementalMount(any(Rect.class)); } @Test public void testIncrementalMountForLithoViewVisibleAtLeft() { setupViewBounds(mLithoView, -10, 0, 10, SCROLLING_VIEW_HEIGHT); IncrementalMountUtils.performIncrementalMount(mViewGroup); verifyPerformIncrementalMountCalled(new Rect(10, 0, 20, SCROLLING_VIEW_HEIGHT)); } @Test public void testIncrementalMountForLithoViewVisibleAtLeftWithTranslationXFullyIn() { setupViewBounds(mLithoView, -10, 0, 10, SCROLLING_VIEW_HEIGHT); setupViewTranslations(mLithoView, 15, 0); setupLithoViewPreviousBounds(mLithoView, 20, SCROLLING_VIEW_HEIGHT); IncrementalMountUtils.performIncrementalMount(mViewGroup); verify(mLithoView, never()).performIncrementalMount(any(Rect.class)); } @Test public void testIncrementalMountForLithoViewVisibleAtLeftWithTranslationXPartialOut() { setupViewBounds(mLithoView, -10, 0, 10, SCROLLING_VIEW_HEIGHT); setupViewTranslations(mLithoView, -7, 0); IncrementalMountUtils.performIncrementalMount(mViewGroup); verifyPerformIncrementalMountCalled(new Rect(17, 0, 20, SCROLLING_VIEW_HEIGHT)); } @Test public void testIncrementalMountForLithoViewVisibleAtBottom() { setupViewBounds( mLithoView, 0, SCROLLING_VIEW_HEIGHT - 5, SCROLLING_VIEW_WIDTH, SCROLLING_VIEW_HEIGHT + 5); IncrementalMountUtils.performIncrementalMount(mViewGroup); verifyPerformIncrementalMountCalled(new Rect(0, 0, SCROLLING_VIEW_WIDTH, 5)); } @Test public void testIncrementalMountForLithoViewVisibleAtRight() { setupViewBounds( mLithoView, SCROLLING_VIEW_WIDTH - 5, 0, SCROLLING_VIEW_WIDTH + 5, SCROLLING_VIEW_HEIGHT); IncrementalMountUtils.performIncrementalMount(mViewGroup); verifyPerformIncrementalMountCalled(new Rect(0, 0, 5, SCROLLING_VIEW_HEIGHT)); } @Test public void testIncrementalMountForLithoViewNewlyFullyVisible() { setupViewBounds(mLithoView, 0, 10, SCROLLING_VIEW_WIDTH, 20); setupLithoViewPreviousBounds(mLithoView, SCROLLING_VIEW_WIDTH, 5); IncrementalMountUtils.performIncrementalMount(mViewGroup); verifyPerformIncrementalMountCalled(new Rect(0, 0, SCROLLING_VIEW_WIDTH, 10)); } @Test public void testIncrementalMountForLithoViewAlreadyFullyVisible() { setupViewBounds(mLithoView, 0, 10, SCROLLING_VIEW_WIDTH, 20); setupLithoViewPreviousBounds(mLithoView, SCROLLING_VIEW_WIDTH, 10); IncrementalMountUtils.performIncrementalMount(mViewGroup); verify(mLithoView, never()).performIncrementalMount(any(Rect.class)); } @Test public void testNoIncrementalMountWhenNotEnabled() { setupViewBounds( mLithoView, 0, SCROLLING_VIEW_HEIGHT - 5, SCROLLING_VIEW_WIDTH, SCROLLING_VIEW_HEIGHT + 5); when(mLithoView.isIncrementalMountEnabled()).thenReturn(false); IncrementalMountUtils.performIncrementalMount(mViewGroup); verify(mLithoView, never()).performIncrementalMount(any(Rect.class)); } @Test public void testIncrementalMountForWrappedViewAtTop() { when(mViewGroup.getChildAt(0)).thenReturn(mWrapperView); setupViewBounds(mWrapperView, 0, -10, SCROLLING_VIEW_WIDTH, 10); setupViewBounds(mLithoView, 0, 0, SCROLLING_VIEW_WIDTH, 20); IncrementalMountUtils.performIncrementalMount(mViewGroup); verifyPerformIncrementalMountCalled(new Rect(0, 10, SCROLLING_VIEW_WIDTH, 20)); } @Test public void testIncrementalMountForWrappedViewAtBottom() { when(mViewGroup.getChildAt(0)).thenReturn(mWrapperView); setupViewBounds( mWrapperView, 0, SCROLLING_VIEW_HEIGHT - 5, SCROLLING_VIEW_WIDTH, SCROLLING_VIEW_HEIGHT + 5); setupViewBounds(mLithoView, 0, 0, SCROLLING_VIEW_WIDTH, 10); IncrementalMountUtils.performIncrementalMount(mViewGroup); verifyPerformIncrementalMountCalled(new Rect(0, 0, SCROLLING_VIEW_WIDTH, 5)); } @Test public void testIncrementalMountForWrappedLithoViewNewlyFullyVisible() { when(mViewGroup.getChildAt(0)).thenReturn(mWrapperView); setupViewBounds(mWrapperView, 0, 10, SCROLLING_VIEW_WIDTH, 20); setupViewBounds(mLithoView, 0, 0, SCROLLING_VIEW_WIDTH, 10); setupLithoViewPreviousBounds(mLithoView, SCROLLING_VIEW_WIDTH, 5); IncrementalMountUtils.performIncrementalMount(mViewGroup); verifyPerformIncrementalMountCalled(new Rect(0, 0, SCROLLING_VIEW_WIDTH, 10)); } @Test public void testIncrementalMountForWrappedLithoViewAlreadyFullyVisible() { when(mViewGroup.getChildAt(0)).thenReturn(mWrapperView); setupViewBounds(mWrapperView, 0, 10, SCROLLING_VIEW_WIDTH, 20); setupViewBounds(mLithoView, 0, 0, SCROLLING_VIEW_WIDTH, 10); setupLithoViewPreviousBounds(mLithoView, SCROLLING_VIEW_WIDTH, 10); IncrementalMountUtils.performIncrementalMount(mViewGroup); verify(mLithoView, never()).performIncrementalMount(any(Rect.class)); } private static void setupViewBounds(View view, int l, int t, int r, int b) { when(view.getLeft()).thenReturn(l); when(view.getTop()).thenReturn(t); when(view.getRight()).thenReturn(r); when(view.getBottom()).thenReturn(b); when(view.getWidth()).thenReturn(r - l); when(view.getHeight()).thenReturn(b - t); } private static void setupViewTranslations(View view, float translationX, float translationY) { when(view.getTranslationX()).thenReturn(translationX); when(view.getTranslationY()).thenReturn(translationY); } private static void setupLithoViewPreviousBounds( LithoView lithoView, int width, int height) { when(lithoView.getPreviousMountBounds()).thenReturn(new Rect(0, 0, width, height)); } private void verifyPerformIncrementalMountCalled(Rect rect) { assertThat(mMountedRect).isEqualTo(rect); } public static class TestWrapperView extends View implements WrapperView { public TestWrapperView(Context context) { super(context); } @Override public View getWrappedView() { return null; } } }