/**
* 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 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.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Matchers;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.rule.PowerMockRule;
import org.powermock.reflect.Whitebox;
import org.robolectric.RuntimeEnvironment;
import static junit.framework.Assert.assertNull;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.verify;
import static org.powermock.api.mockito.PowerMockito.mock;
@PrepareForTest(ComponentsPools.class)
@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*"})
@RunWith(ComponentsTestRunner.class)
public class LayoutStateRecyclingTest {
@Rule
public PowerMockRule mPowerMockRule = new PowerMockRule();
private int mUnspecifiedSizeSpec;
@Before
public void setUp() throws Exception {
mUnspecifiedSizeSpec = SizeSpec.makeSizeSpec(0, SizeSpec.UNSPECIFIED);
}
@Test
public void testNodeRecycling() {
RecyclePool<InternalNode> internalNodePool = mock(RecyclePool.class);
Whitebox.setInternalState(
ComponentsPools.class,
"sInternalNodePool",
internalNodePool);
// We want to verify that we never recycle a node with a non-null parent, since that would
// mean that the parent retains a dangling reference to a recycled node.
Mockito.doAnswer(
new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
InternalNode node = (InternalNode) invocation.getArguments()[0];
assertNull("Internal node parent must be null before releasing", node.getParent());
return null;
}
}).when(internalNodePool).release(Matchers.<InternalNode>any());
// Create a layout state and release it.
final Component input = new InlineLayoutSpec() {
@Override
protected ComponentLayout onCreateLayout(ComponentContext c) {
return Column.create(c)
.child(
Column.create(c))
.build();
}
};
LayoutState layoutState = LayoutState.calculate(
new ComponentContext(RuntimeEnvironment.application),
input,
-1,
mUnspecifiedSizeSpec,
mUnspecifiedSizeSpec,
false,
false,
null,
false);
layoutState.releaseRef();
// Verify that the nodes did get recycled
verify(internalNodePool, atLeast(2)).release(Matchers.<InternalNode>any());
}
}