/**
* 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 com.facebook.litho.testing.TestDrawableComponent;
import com.facebook.litho.testing.TestLayoutComponent;
import com.facebook.litho.testing.TestNullLayoutComponent;
import com.facebook.litho.testing.testrunner.ComponentsTestRunner;
import com.facebook.litho.testing.util.InlineLayoutSpec;
import com.facebook.yoga.YogaAlign;
import com.facebook.yoga.YogaEdge;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.rule.PowerMockRule;
import org.robolectric.RuntimeEnvironment;
import static org.junit.Assert.assertEquals;
@PrepareForTest({Component.class})
@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*"})
@RunWith(ComponentsTestRunner.class)
public class LayoutStateCalculateVisibilityOutputsTest {
@Rule
public PowerMockRule mPowerMockRule = new PowerMockRule();
@Before
public void setup() throws Exception {
}
@Test
public void testNoUnnecessaryVisibilityOutputs() {
final Component component = new InlineLayoutSpec() {
@Override
protected ComponentLayout onCreateLayout(ComponentContext c) {
return Column.create(c)
.child(
Column.create(c)
.child(
TestDrawableComponent.create(c)
.withLayout()
.visibleHandler(c.newEventHandler(1))))
.child(
TestDrawableComponent.create(c)
.withLayout()
.invisibleHandler(c.newEventHandler(2)))
.child(TestDrawableComponent.create(c))
.build();
}
};
LayoutState layoutState = calculateLayoutState(
RuntimeEnvironment.application,
component,
-1,
SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY),
SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY));
assertEquals(2, layoutState.getVisibilityOutputCount());
}
@Test
public void testNoUnnecessaryVisibilityOutputsWithFullImpression() {
final Component component = new InlineLayoutSpec() {
@Override
protected ComponentLayout onCreateLayout(ComponentContext c) {
return Column.create(c)
.child(
Column.create(c)
.child(
TestDrawableComponent.create(c)
.withLayout()
.visibleHandler(c.newEventHandler(1))))
.child(
TestDrawableComponent.create(c)
.withLayout()
.fullImpressionHandler(c.newEventHandler(3)))
.child(TestDrawableComponent.create(c))
.build();
}
};
LayoutState layoutState = calculateLayoutState(
RuntimeEnvironment.application,
component,
-1,
SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY),
SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY));
assertEquals(2, layoutState.getVisibilityOutputCount());
}
@Test
public void testNoUnnecessaryVisibilityOutputsWithFocused() {
final Component component = new InlineLayoutSpec() {
@Override
protected ComponentLayout onCreateLayout(ComponentContext c) {
return Column.create(c)
.child(
Column.create(c)
.child(
TestDrawableComponent.create(c)
.withLayout()
.visibleHandler(c.newEventHandler(1))))
.child(
TestDrawableComponent.create(c)
.withLayout()
.focusedHandler(c.newEventHandler(4)))
.child(TestDrawableComponent.create(c))
.build();
}
};
LayoutState layoutState = calculateLayoutState(
RuntimeEnvironment.application,
component,
-1,
SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY),
SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY));
assertEquals(2, layoutState.getVisibilityOutputCount());
}
@Test
public void testVisibilityOutputsForDelegateComponents() {
final boolean isDelegate = true;
final Component component = new InlineLayoutSpec() {
@Override
protected ComponentLayout onCreateLayout(ComponentContext c) {
return Column.create(c)
.child(
TestLayoutComponent.create(c, 0, 0, true, true, false, isDelegate)
.withLayout()
.visibleHandler(c.newEventHandler(1)))
.wrapInView()
.build();
}
};
LayoutState layoutState = calculateLayoutState(
RuntimeEnvironment.application,
component,
-1,
SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY),
SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY));
assertEquals(1, layoutState.getVisibilityOutputCount());
}
@Test
public void testLayoutOutputsForDeepLayoutSpecs() {
final Component component = new InlineLayoutSpec() {
@Override
protected ComponentLayout onCreateLayout(ComponentContext c) {
return Column.create(c)
.child(
Column.create(c)
.child(
TestLayoutComponent.create(c)
.withLayout()
.visibleHandler(c.newEventHandler(1)))
.invisibleHandler(c.newEventHandler(2)))
.child(
Column.create(c)
.child(
TestLayoutComponent.create(c)
.withLayout()
.invisibleHandler(c.newEventHandler(1)))
.visibleHandler(c.newEventHandler(2)))
.wrapInView()
.build();
}
};
LayoutState layoutState = calculateLayoutState(
RuntimeEnvironment.application,
component,
-1,
SizeSpec.makeSizeSpec(350, SizeSpec.EXACTLY),
SizeSpec.makeSizeSpec(200, SizeSpec.EXACTLY));
// Check total layout outputs.
assertEquals(4, layoutState.getVisibilityOutputCount());
// Check number of Components with VisibleEvent handlers.
int visibleHandlerCount = 0;
for (int i = 0; i < layoutState.getVisibilityOutputCount(); i++) {
if (layoutState.getVisibilityOutputAt(i).getVisibleEventHandler() != null) {
visibleHandlerCount += 1;
}
}
assertEquals(2, visibleHandlerCount);
}
@Test
public void testLayoutOutputsForForceWrappedComponent() {
final Component component = new InlineLayoutSpec() {
@Override
protected ComponentLayout onCreateLayout(ComponentContext c) {
return Column.create(c)
.child(
TestDrawableComponent.create(c)
.withLayout()
.visibleHandler(c.newEventHandler(1))
.wrapInView())
.build();
}
};
final LayoutState layoutState = calculateLayoutState(
RuntimeEnvironment.application,
component,
-1,
SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY),
SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY));
assertEquals(1, layoutState.getVisibilityOutputCount());
}
@Test
public void testLayoutOutputForRootWithNullLayout() {
final Component componentWithNullLayout = new InlineLayoutSpec() {
@Override
protected ComponentLayout onCreateLayout(ComponentContext c) {
return null;
}
};
LayoutState layoutState = calculateLayoutState(
RuntimeEnvironment.application,
componentWithNullLayout,
-1,
SizeSpec.makeSizeSpec(350, SizeSpec.EXACTLY),
SizeSpec.makeSizeSpec(200, SizeSpec.EXACTLY));
assertEquals(0, layoutState.getVisibilityOutputCount());
}
@Test
public void testLayoutComponentForNestedTreeChildWithNullLayout() {
final Component component = new InlineLayoutSpec() {
@Override
protected ComponentLayout onCreateLayout(ComponentContext c) {
return Column.create(c)
.paddingPx(YogaEdge.ALL, 2)
.child(new TestNullLayoutComponent())
.invisibleHandler(c.newEventHandler(2))
.build();
}
};
LayoutState layoutState = calculateLayoutState(
RuntimeEnvironment.application,
component,
-1,
SizeSpec.makeSizeSpec(350, SizeSpec.EXACTLY),
SizeSpec.makeSizeSpec(200, SizeSpec.EXACTLY));
assertEquals(1, layoutState.getVisibilityOutputCount());
}
private static LayoutState calculateLayoutState(
Context context,
Component<?> component,
int componentTreeId,
int widthSpec,
int heightSpec) {
return LayoutState.calculate(
new ComponentContext(context),
component,
componentTreeId,
widthSpec,
heightSpec,
false,
false,
null,
false);
}
}