/**
* 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;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Rect;
import android.view.ViewGroup;
import com.facebook.litho.testing.testrunner.ComponentsTestRunner;
import com.facebook.litho.testing.TestDrawableComponent;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.powermock.reflect.Whitebox;
import org.robolectric.RuntimeEnvironment;
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;
@RunWith(ComponentsTestRunner.class)
public class ComponentTreeIncrementalMountTest {
private LithoView mLithoView;
private ComponentTree mComponentTree;
private final Rect mMountedRect = new Rect();
@Before
public void setup() {
ComponentContext context = new ComponentContext(RuntimeEnvironment.application);
mComponentTree = ComponentTree.create(
context,
TestDrawableComponent.create(context)
.color(Color.BLACK)
.build())
.layoutDiffing(false)
.build();
mLithoView = mock(TestLithoView.class);
Whitebox.setInternalState(mComponentTree, "mLithoView", mLithoView);
// 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()[1]);
return null;
}
}).when(mLithoView).mount(any(LayoutState.class), any(Rect.class));
}
@Test
public void testIncrementalMountBoundsSameAsParent() {
setupIncrementalMountTest(new Rect(0, 0, 10, 10), new Rect(0, 0, 10, 10));
mComponentTree.incrementalMountComponent();
assertThat(mMountedRect).isEqualTo(new Rect(0, 0, 10, 10));
}
@Test
public void testIncrementalMountBoundsWithNoParent() {
setupIncrementalMountTest(new Rect(0, 0, 10, 10), null);
mComponentTree.incrementalMountComponent();
assertThat(mMountedRect).isEqualTo(new Rect(0, 0, 10, 10));
}
@Test
public void testIncrementalMountBoundsInsideParent() {
setupIncrementalMountTest(new Rect(10, 10, 20, 20), new Rect(0, 0, 30, 30));
mComponentTree.incrementalMountComponent();
assertThat(mMountedRect).isEqualTo(new Rect(0, 0, 10, 10));
}
@Test
public void testIncrementalMountBoundsOutsideParent() {
setupIncrementalMountTest(new Rect(10, 10, 20, 20), new Rect(20, 10, 30, 30));
mComponentTree.incrementalMountComponent();
verify(mLithoView, never()).mount(any(LayoutState.class), any(Rect.class));
}
@Test
public void testIncrementalMountBoundsToLeftOfParent() {
setupIncrementalMountTest(new Rect(10, 10, 20, 20), new Rect(15, 10, 25, 20));
mComponentTree.incrementalMountComponent();
assertThat(mMountedRect).isEqualTo(new Rect(5, 0, 10, 10));
}
@Test
public void testIncrementalMountBoundsToTopOfParent() {
setupIncrementalMountTest(new Rect(10, 10, 20, 20), new Rect(10, 15, 20, 25));
mComponentTree.incrementalMountComponent();
assertThat(mMountedRect).isEqualTo(new Rect(0, 5, 10, 10));
}
@Test
public void testIncrementalMountBoundsToRightOfParent() {
setupIncrementalMountTest(new Rect(10, 10, 20, 20), new Rect(5, 10, 15, 20));
mComponentTree.incrementalMountComponent();
assertThat(mMountedRect).isEqualTo(new Rect(0, 0, 5, 10));
}
@Test
public void testIncrementalMountBoundsToBottomOfParent() {
setupIncrementalMountTest(new Rect(10, 10, 20, 20), new Rect(10, 5, 20, 15));
mComponentTree.incrementalMountComponent();
assertThat(mMountedRect).isEqualTo(new Rect(0, 0, 10, 5));
}
private void setupIncrementalMountTest(
final Rect lithoViewBoundsInScreen,
final Rect parentBoundsInScreen) {
doAnswer(
new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
int[] location = (int[]) invocation.getArguments()[0];
location[0] = lithoViewBoundsInScreen.left;
location[1] = lithoViewBoundsInScreen.top;
return null;
}
}).when(mLithoView).getLocationOnScreen(any(int[].class));
when(mLithoView.getWidth())
.thenReturn(lithoViewBoundsInScreen.right - lithoViewBoundsInScreen.left);
when(mLithoView.getHeight())
.thenReturn(lithoViewBoundsInScreen.bottom - lithoViewBoundsInScreen.top);
if (parentBoundsInScreen == null) {
return;
}
ViewGroup parentView = mock(ViewGroup.class);
when(mLithoView.getParent()).thenReturn(parentView);
doAnswer(
new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
int[] location = (int[]) invocation.getArguments()[0];
location[0] = parentBoundsInScreen.left;
location[1] = parentBoundsInScreen.top;
return null;
}
}).when(parentView).getLocationOnScreen(any(int[].class));
when(parentView.getWidth()).thenReturn(parentBoundsInScreen.right - parentBoundsInScreen.left);
when(parentView.getHeight()).thenReturn(parentBoundsInScreen.bottom - parentBoundsInScreen.top);
}
/**
* Required in order to ensure that {@link LithoView#mount(LayoutState, Rect)} is
* mocked correctly (it needs protected access to be mocked).
*/
public static class TestLithoView extends LithoView {
public TestLithoView(Context context) {
super(context);
}
protected void mount(
LayoutState layoutState,
Rect currentVisibleArea) {
super.mount(layoutState, currentVisibleArea);
}
}
}