/**
* 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 java.util.ArrayList;
import java.util.List;
import android.support.v4.util.LongSparseArray;
import com.facebook.litho.testing.ComponentTestHelper;
import com.facebook.litho.testing.TestComponent;
import com.facebook.litho.testing.TestDrawableComponent;
import com.facebook.litho.testing.TestViewComponent;
import com.facebook.litho.testing.testrunner.ComponentsTestRunner;
import com.facebook.litho.testing.util.InlineLayoutSpec;
import com.facebook.yoga.YogaAlign;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.reflect.Whitebox;
import org.robolectric.RuntimeEnvironment;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@RunWith(ComponentsTestRunner.class)
public class MountStateRemountTest {
private ComponentContext mContext;
@Before
public void setup() {
mContext = new ComponentContext(RuntimeEnvironment.application);
}
@Test
public void testRemountSameLayoutState() {
final TestComponent component1 = TestDrawableComponent.create(mContext)
.build();
final TestComponent component2 = TestDrawableComponent.create(mContext)
.build();
final TestComponent component3 = TestDrawableComponent.create(mContext)
.build();
final TestComponent component4 = TestDrawableComponent.create(mContext)
.build();
final LithoView lithoView = ComponentTestHelper.mountComponent(
mContext,
new InlineLayoutSpec() {
@Override
protected ComponentLayout onCreateLayout(ComponentContext c) {
return Column.create(c)
.child(component1)
.child(component2)
.build();
}
});
assertTrue(component1.isMounted());
assertTrue(component2.isMounted());
ComponentTestHelper.mountComponent(
mContext,
lithoView,
new InlineLayoutSpec() {
@Override
protected ComponentLayout onCreateLayout(ComponentContext c) {
return Column.create(c)
.child(component3)
.child(component4)
.build();
}
});
assertTrue(component1.isMounted());
assertTrue(component2.isMounted());
assertFalse(component3.isMounted());
assertFalse(component4.isMounted());
final MountState mountState = Whitebox.getInternalState(lithoView,"mMountState");
final LongSparseArray<MountItem> indexToItemMap =
Whitebox.getInternalState(mountState,"mIndexToItemMap");
final List<Component> components = new ArrayList<>();
for (int i = 0; i < indexToItemMap.size(); i++) {
components.add(indexToItemMap.valueAt(i).getComponent());
}
assertFalse(containsRef(components, component1));
assertFalse(containsRef(components, component2));
assertTrue(containsRef(components, component3));
assertTrue(containsRef(components, component4));
}
/**
* There was a crash when mounting a drawing in place of a view. This test is here to make sure
* this does not regress. To reproduce this crash the pools needed to be in a specific state
* as view layout outputs and mount items were being re-used for drawables.
*/
@Test
public void testRemountDifferentMountType() throws IllegalAccessException, NoSuchFieldException {
clearPool("sLayoutOutputPool");
clearPool("sViewNodeInfoPool");
final LithoView lithoView = ComponentTestHelper.mountComponent(
mContext,
new InlineLayoutSpec() {
@Override
protected ComponentLayout onCreateLayout(ComponentContext c) {
return TestViewComponent.create(c).buildWithLayout();
}
});
ComponentTestHelper.mountComponent(
mContext,
lithoView,
new InlineLayoutSpec() {
@Override
protected ComponentLayout onCreateLayout(ComponentContext c) {
return TestDrawableComponent.create(c).buildWithLayout();
}
});
}
@Test
public void testRemountNewLayoutState() {
final TestComponent component1 = TestDrawableComponent.create(mContext)
.unique()
.build();
final TestComponent component2 = TestDrawableComponent.create(mContext)
.unique()
.build();
final TestComponent component3 = TestDrawableComponent.create(mContext)
.unique()
.build();
final TestComponent component4 = TestDrawableComponent.create(mContext)
.unique()
.build();
final LithoView lithoView = ComponentTestHelper.mountComponent(
mContext,
new InlineLayoutSpec() {
@Override
protected ComponentLayout onCreateLayout(ComponentContext c) {
return Column.create(c)
.child(component1)
.child(component2)
.build();
}
});
assertTrue(component1.isMounted());
assertTrue(component2.isMounted());
ComponentTestHelper.mountComponent(
mContext,
lithoView,
new InlineLayoutSpec() {
@Override
protected ComponentLayout onCreateLayout(ComponentContext c) {
return Column.create(c)
.child(component3)
.child(component4)
.build();
}
});
assertFalse(component1.isMounted());
assertFalse(component2.isMounted());
assertTrue(component3.isMounted());
assertTrue(component4.isMounted());
}
@Test
public void testRemountPartiallyDifferentLayoutState() {
final TestComponent component1 = TestDrawableComponent.create(mContext)
.build();
final TestComponent component2 = TestDrawableComponent.create(mContext)
.build();
final TestComponent component3 = TestDrawableComponent.create(mContext)
.build();
final TestComponent component4 = TestDrawableComponent.create(mContext)
.build();
final LithoView lithoView = ComponentTestHelper.mountComponent(
mContext,
new InlineLayoutSpec() {
@Override
protected ComponentLayout onCreateLayout(ComponentContext c) {
return Column.create(c)
.child(component1)
.child(component2)
.build();
}
});
assertTrue(component1.isMounted());
assertTrue(component2.isMounted());
ComponentTestHelper.mountComponent(
mContext,
lithoView,
new InlineLayoutSpec() {
@Override
protected ComponentLayout onCreateLayout(ComponentContext c) {
return Column.create(c)
.child(component3)
.child(
Column.create(c)
.wrapInView()
.child(component4))
.build();
}
});
assertTrue(component1.isMounted());
assertFalse(component2.isMounted());
assertFalse(component3.isMounted());
assertTrue(component4.isMounted());
}
private boolean containsRef(List<?> list, Object object) {
for (Object o : list) {
if (o == object) {
return true;
}
}
return false;
}
private static void clearPool(String name) {
final RecyclePool<?> pool =
Whitebox.getInternalState(ComponentsPools.class, name);
while (pool.acquire() != null) {
// Run.
}
}
}