/** * 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.os.Build; import android.util.SparseArray; import android.view.accessibility.AccessibilityManager; import com.facebook.litho.testing.TestComponent; import com.facebook.litho.testing.TestDrawableComponent; import com.facebook.litho.testing.TestLayoutComponent; import com.facebook.litho.testing.TestNullLayoutComponent; import com.facebook.litho.testing.TestSizeDependentComponent; import com.facebook.litho.testing.TestViewComponent; import com.facebook.litho.testing.testrunner.ComponentsTestRunner; import com.facebook.litho.testing.util.InlineLayoutSpec; import com.facebook.litho.widget.Text; import com.facebook.yoga.YogaAlign; import com.facebook.yoga.YogaEdge; import com.facebook.yoga.YogaJustify; import com.facebook.yoga.YogaPositionType; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; import org.powermock.api.mockito.PowerMockito; 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 org.robolectric.Shadows; import org.robolectric.shadows.ShadowAccessibilityManager; import static android.content.Context.ACCESSIBILITY_SERVICE; import static android.support.v4.view.ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO; import static android.support.v4.view.ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO; import static android.support.v4.view.ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS; import static android.support.v4.view.ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES; import static com.facebook.litho.NodeInfo.FOCUS_SET_TRUE; import static com.facebook.yoga.YogaEdge.BOTTOM; import static com.facebook.yoga.YogaEdge.LEFT; import static com.facebook.yoga.YogaEdge.RIGHT; import static com.facebook.yoga.YogaEdge.TOP; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @PrepareForTest(Component.class) @PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*"}) @RunWith(ComponentsTestRunner.class) public class LayoutStateCalculateTest { @Rule public PowerMockRule mPowerMockRule = new PowerMockRule(); @Before public void setup() throws Exception { } @Test public void testNoUnnecessaryLayoutOutputsForLayoutSpecs() { final Component component = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Column.create(c) .child( Column.create(c) .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.getMountableOutputCount()); } @Test public void testLayoutOutputsForRootInteractiveLayoutSpecs() { final Component component = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Column.create(c) .child(TestDrawableComponent.create(c)) .wrapInView() .build(); } }; LayoutState layoutState = calculateLayoutState( RuntimeEnvironment.application, component, -1, SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY), SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY)); assertEquals(2, layoutState.getMountableOutputCount()); } @Test public void testLayoutOutputsForSpecsWithClickHandling() { final Component component = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Column.create(c) .child( Column.create(c) .child(TestDrawableComponent.create(c)) .clickHandler(c.newEventHandler(1))) .build(); } }; LayoutState layoutState = calculateLayoutState( RuntimeEnvironment.application, component, -1, SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY), SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY)); assertEquals(3, layoutState.getMountableOutputCount()); final NodeInfo nodeInfo = layoutState.getMountableOutputAt(1).getNodeInfo(); assertNotNull(nodeInfo); assertNotNull(nodeInfo.getClickHandler()); assertNull(nodeInfo.getLongClickHandler()); assertNull(nodeInfo.getTouchHandler()); } @Test public void testLayoutOutputsForSpecsWithLongClickHandling() { final Component component = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Column.create(c) .child( Column.create(c) .child(TestDrawableComponent.create(c)) .longClickHandler(c.newEventHandler(1))) .build(); } }; LayoutState layoutState = calculateLayoutState( RuntimeEnvironment.application, component, -1, SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY), SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY)); assertEquals(3, layoutState.getMountableOutputCount()); final NodeInfo nodeInfo = layoutState.getMountableOutputAt(1).getNodeInfo(); assertNotNull(nodeInfo); assertNull(nodeInfo.getClickHandler()); assertNotNull(nodeInfo.getLongClickHandler()); assertNull(nodeInfo.getTouchHandler()); } @Test public void testLayoutOutputsForSpecsWithTouchHandling() { final Component component = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Column.create(c) .child( Column.create(c) .child(TestDrawableComponent.create(c)) .touchHandler(c.newEventHandler(1))) .build(); } }; LayoutState layoutState = calculateLayoutState( RuntimeEnvironment.application, component, -1, SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY), SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY)); assertEquals(3, layoutState.getMountableOutputCount()); final NodeInfo nodeInfo = layoutState.getMountableOutputAt(1).getNodeInfo(); assertNotNull(nodeInfo); assertNotNull(nodeInfo.getTouchHandler()); assertNull(nodeInfo.getClickHandler()); assertNull(nodeInfo.getLongClickHandler()); } @Test public void testLayoutOutputsForDeepLayoutSpecs() { final int paddingSize = 5; final Component component = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Column.create(c) .backgroundColor(0xFFFF0000) .child( Row.create(c) .justifyContent(YogaJustify.SPACE_AROUND) .alignItems(YogaAlign.CENTER) .positionType(YogaPositionType.ABSOLUTE) .positionPx(LEFT, 50) .positionPx(TOP, 50) .positionPx(RIGHT, 200) .positionPx(BOTTOM, 50) .child( Text.create(c) .text("textLeft1")) .child( Text.create(c) .text("textRight1")) .paddingPx(YogaEdge.ALL, paddingSize) .wrapInView()) .child( Row.create(c) .justifyContent(YogaJustify.SPACE_AROUND) .alignItems(YogaAlign.CENTER) .positionType(YogaPositionType.ABSOLUTE) .positionPx(LEFT, 200) .positionPx(TOP, 50) .positionPx(RIGHT, 50) .positionPx(BOTTOM, 50) .child( Text.create(c) .text("textLeft2") .withLayout() .wrapInView() .paddingPx(YogaEdge.ALL, paddingSize)) .child( TestViewComponent.create(c) .withLayout() .wrapInView())) .build(); } }; LayoutState layoutState = calculateLayoutState( RuntimeEnvironment.application, component, -1, SizeSpec.makeSizeSpec(350, SizeSpec.EXACTLY), SizeSpec.makeSizeSpec(200, SizeSpec.EXACTLY)); // Check total layout outputs. assertEquals(8, layoutState.getMountableOutputCount()); // Check quantity of HostComponents. int totalHosts = 0; for (int i = 0; i < layoutState.getMountableOutputCount(); i++) { ComponentLifecycle lifecycle = getComponentAt(layoutState, i); if (isHostComponent(lifecycle)) { totalHosts++; } } assertEquals(3, totalHosts); //Check all the Layouts are in the correct position. assertTrue(isHostComponent(getComponentAt(layoutState, 0))); assertTrue(getComponentAt(layoutState, 1) instanceof DrawableComponent); assertTrue(isHostComponent(getComponentAt(layoutState, 2))); assertTrue(getComponentAt(layoutState, 3) instanceof Text); assertTrue(getComponentAt(layoutState, 4) instanceof Text); assertTrue(isHostComponent(getComponentAt(layoutState, 5))); assertTrue(getComponentAt(layoutState, 6) instanceof Text); assertTrue(getComponentAt(layoutState, 7) instanceof TestViewComponent); // Check the text within the TextComponents. assertEquals("textLeft1", getTextFromTextComponent(layoutState, 3)); assertEquals("textRight1", getTextFromTextComponent(layoutState, 4)); assertEquals("textLeft2", getTextFromTextComponent(layoutState, 6)); Rect textLayoutBounds = layoutState.getMountableOutputAt(6).getBounds(); Rect textBackgroundBounds = layoutState.getMountableOutputAt(5).getBounds(); assertEquals(textBackgroundBounds.left , textLayoutBounds.left - paddingSize); assertEquals(textBackgroundBounds.top , textLayoutBounds.top - paddingSize); assertEquals(textBackgroundBounds.right , textLayoutBounds.right + paddingSize); assertEquals(textBackgroundBounds.bottom , textLayoutBounds.bottom + paddingSize); } @Test public void testLayoutOutputMountBounds() { final Component component = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Column.create(c) .widthPx(30) .heightPx(30) .wrapInView() .child( Column.create(c) .widthPx(10) .heightPx(10) .marginPx(YogaEdge.ALL, 10) .wrapInView() .child( TestDrawableComponent.create(c) .withLayout() .widthPx(10) .heightPx(10))) .build(); } }; LayoutState layoutState = calculateLayoutState( RuntimeEnvironment.application, component, -1, SizeSpec.makeSizeSpec(350, SizeSpec.EXACTLY), SizeSpec.makeSizeSpec(200, SizeSpec.EXACTLY)); Rect mountBounds = new Rect(); layoutState.getMountableOutputAt(0).getMountBounds(mountBounds); assertEquals(new Rect(0, 0, 30, 30), mountBounds); layoutState.getMountableOutputAt(1).getMountBounds(mountBounds); assertEquals(new Rect(10, 10, 20, 20), mountBounds); layoutState.getMountableOutputAt(2).getMountBounds(mountBounds); assertEquals(new Rect(0, 0, 10, 10), mountBounds); } @Test public void testLayoutOutputsForDeepLayoutSpecsWithBackground() { final int paddingSize = 5; final Component component = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Column.create(c) .backgroundColor(0xFFFF0000) .child( Row.create(c) .justifyContent(YogaJustify.SPACE_AROUND) .alignItems(YogaAlign.CENTER) .positionType(YogaPositionType.ABSOLUTE) .positionPx(LEFT, 50) .positionPx(TOP, 50) .positionPx(RIGHT, 200) .positionPx(BOTTOM, 50) .child( Text.create(c) .text("textLeft1")) .child( Text.create(c) .text("textRight1")) .backgroundColor(0xFFFF0000) .foregroundColor(0xFFFF0000) .paddingPx(YogaEdge.ALL, paddingSize) .wrapInView()) .child( Row.create(c) .justifyContent(YogaJustify.SPACE_AROUND) .alignItems(YogaAlign.CENTER) .positionType(YogaPositionType.ABSOLUTE) .positionPx(LEFT, 200) .positionPx(TOP, 50) .positionPx(RIGHT, 50) .positionPx(BOTTOM, 50) .child( Text.create(c) .text("textLeft2") .withLayout() .wrapInView() .backgroundColor(0xFFFF0000) .paddingPx(YogaEdge.ALL, paddingSize)) .child( TestViewComponent.create(c) .withLayout() .backgroundColor(0xFFFF0000) .foregroundColor(0x0000FFFF) .paddingPx(YogaEdge.ALL, paddingSize))) .build(); } }; LayoutState layoutState = calculateLayoutState( RuntimeEnvironment.application, component, -1, SizeSpec.makeSizeSpec(350, SizeSpec.EXACTLY), SizeSpec.makeSizeSpec(200, SizeSpec.EXACTLY)); // Account for Android version in the foreground. If >= M the foreground is part of the // ViewLayoutOutput otherwise it has its own LayoutOutput. boolean foregroundHasOwnOutput = Build.VERSION.SDK_INT < Build.VERSION_CODES.M; // Check total layout outputs. assertEquals( foregroundHasOwnOutput ? 12 : 11, layoutState.getMountableOutputCount()); // Check quantity of HostComponents. int totalHosts = 0; for (int i = 0; i < layoutState.getMountableOutputCount(); i++) { ComponentLifecycle lifecycle = getComponentAt(layoutState, i); if (isHostComponent(lifecycle)) { totalHosts++; } } assertEquals(3, totalHosts); //Check all the Layouts are in the correct position. assertTrue(isHostComponent(getComponentAt(layoutState, 0))); assertTrue(getComponentAt(layoutState, 1) instanceof DrawableComponent); assertTrue(isHostComponent(getComponentAt(layoutState, 2))); assertTrue(getComponentAt(layoutState, 3) instanceof DrawableComponent); assertTrue(getComponentAt(layoutState, 4) instanceof Text); assertTrue(getComponentAt(layoutState, 5) instanceof Text); assertTrue(getComponentAt(layoutState, 6) instanceof DrawableComponent); assertTrue(isHostComponent(getComponentAt(layoutState, 7))); assertTrue(getComponentAt(layoutState, 8) instanceof DrawableComponent); assertTrue(getComponentAt(layoutState, 9) instanceof Text); assertTrue(getComponentAt(layoutState, 10) instanceof TestViewComponent); if (foregroundHasOwnOutput) { assertTrue(getComponentAt(layoutState, 11) instanceof DrawableComponent); } // Check the text within the TextComponents. assertEquals("textLeft1", getTextFromTextComponent(layoutState, 4)); assertEquals("textRight1", getTextFromTextComponent(layoutState, 5)); assertEquals("textLeft2", getTextFromTextComponent(layoutState, 9)); //Check that the backgrounds have the same size of the components to which they are associated assertEquals( layoutState.getMountableOutputAt(2).getBounds(), layoutState.getMountableOutputAt(3).getBounds()); assertEquals( layoutState.getMountableOutputAt(2).getBounds(), layoutState.getMountableOutputAt(6).getBounds()); Rect textLayoutBounds = layoutState.getMountableOutputAt(9).getBounds(); Rect textBackgroundBounds = layoutState.getMountableOutputAt(8).getBounds(); assertEquals(textBackgroundBounds.left , textLayoutBounds.left - paddingSize); assertEquals(textBackgroundBounds.top , textLayoutBounds.top - paddingSize); assertEquals(textBackgroundBounds.right , textLayoutBounds.right + paddingSize); assertEquals(textBackgroundBounds.bottom , textLayoutBounds.bottom + paddingSize); assertEquals(layoutState.getMountableOutputAt(7).getBounds(),layoutState.getMountableOutputAt(8).getBounds()); ViewNodeInfo viewNodeInfo = layoutState.getMountableOutputAt(10).getViewNodeInfo(); assertNotNull(viewNodeInfo); assertTrue(viewNodeInfo.getBackground() != null); if (foregroundHasOwnOutput) { assertTrue(viewNodeInfo.getForeground() == null); } else { assertTrue(viewNodeInfo.getForeground() != null); } assertTrue(viewNodeInfo.getPaddingLeft() == paddingSize); assertTrue(viewNodeInfo.getPaddingTop() == paddingSize); assertTrue(viewNodeInfo.getPaddingRight() == paddingSize); assertTrue(viewNodeInfo.getPaddingBottom() == paddingSize); } @Test public void testLayoutOutputsForMegaDeepLayoutSpecs() { final Component component = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Column.create(c) .child( Column.create(c) .child(TestDrawableComponent.create(c)) .child(TestDrawableComponent.create(c)) .wrapInView()) .child( Column.create(c) .child( TestDrawableComponent.create(c) .withLayout() .wrapInView()) .child( Column.create(c) .child(TestDrawableComponent.create(c)) .child(TestDrawableComponent.create(c)) .wrapInView()) .wrapInView()) .child( Column.create(c) .child(TestDrawableComponent.create(c)) .child( Column.create(c) .child(TestDrawableComponent.create(c)) .child(TestDrawableComponent.create(c))) .wrapInView()) .child( Column.create(c) .child(TestDrawableComponent.create(c)) .child( Column.create(c) .child(TestDrawableComponent.create(c)) .child(TestViewComponent.create(c))) .wrapInView()) .build(); } }; LayoutState layoutState = calculateLayoutState( RuntimeEnvironment.application, component, -1, SizeSpec.makeSizeSpec(350, SizeSpec.EXACTLY), SizeSpec.makeSizeSpec(200, SizeSpec.EXACTLY)); // Check total layout outputs. assertEquals(18, layoutState.getMountableOutputCount()); // Check quantity of HostComponents. int totalHosts = 0; for (int i = 0; i < layoutState.getMountableOutputCount(); i++) { ComponentLifecycle lifecycle = getComponentAt(layoutState, i); if (isHostComponent(lifecycle)) { totalHosts++; } } assertEquals(7, totalHosts); // Check all the Components match the right LayoutOutput positions. // Tree One. assertTrue(isHostComponent(getComponentAt(layoutState, 0))); assertTrue(isHostComponent(getComponentAt(layoutState, 1))); assertTrue(getComponentAt(layoutState, 2) instanceof TestDrawableComponent); assertTrue(getComponentAt(layoutState, 3) instanceof TestDrawableComponent); // Tree Two. assertTrue(isHostComponent(getComponentAt(layoutState, 4))); assertTrue(isHostComponent(getComponentAt(layoutState, 5))); assertTrue(getComponentAt(layoutState, 6) instanceof TestDrawableComponent); assertTrue(isHostComponent(getComponentAt(layoutState, 7))); assertTrue(getComponentAt(layoutState, 8) instanceof TestDrawableComponent); assertTrue(getComponentAt(layoutState, 9) instanceof TestDrawableComponent); // Tree Three. assertTrue(isHostComponent(getComponentAt(layoutState, 10))); assertTrue(getComponentAt(layoutState, 11) instanceof TestDrawableComponent); assertTrue(getComponentAt(layoutState, 12) instanceof TestDrawableComponent); assertTrue(getComponentAt(layoutState, 13) instanceof TestDrawableComponent); // Tree Four. assertTrue(isHostComponent(getComponentAt(layoutState, 14))); assertTrue(getComponentAt(layoutState, 15) instanceof TestDrawableComponent); assertTrue(getComponentAt(layoutState, 16) instanceof TestDrawableComponent); assertTrue(getComponentAt(layoutState, 17) instanceof TestViewComponent); } @Test public void testLayoutOutputStableIds() { final Component component1 = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Column.create(c) .child( Column.create(c) .child(TestDrawableComponent.create(c)) .contentDescription("cd0")) .child( Column.create(c) .child(TestDrawableComponent.create(c)) .child(TestDrawableComponent.create(c)) .contentDescription("cd1")) .child( Column.create(c) .child(TestDrawableComponent.create(c)) .child(TestViewComponent.create(c)) .contentDescription("cd2")) .build(); } }; final Component component2 = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Column.create(c) .child( Column.create(c) .child(TestDrawableComponent.create(c)) .contentDescription("cd0")) .child( Column.create(c) .child(TestDrawableComponent.create(c)) .child(TestDrawableComponent.create(c)) .contentDescription("cd1")) .child( Column.create(c) .child(TestDrawableComponent.create(c)) .child(TestViewComponent.create(c)) .contentDescription("cd2")) .build(); } }; LayoutState layoutState = calculateLayoutState( RuntimeEnvironment.application, component1, -1, SizeSpec.makeSizeSpec(350, SizeSpec.EXACTLY), SizeSpec.makeSizeSpec(20, SizeSpec.EXACTLY)); LayoutState sameComponentLayoutState = calculateLayoutState( RuntimeEnvironment.application, component2, -1, SizeSpec.makeSizeSpec(350, SizeSpec.EXACTLY), SizeSpec.makeSizeSpec(20, SizeSpec.EXACTLY)); assertEquals( layoutState.getMountableOutputCount(), sameComponentLayoutState.getMountableOutputCount()); for (int i = 0; i < layoutState.getMountableOutputCount(); i++) { assertEquals( layoutState.getMountableOutputAt(i).getId(), sameComponentLayoutState.getMountableOutputAt(i).getId()); } } @Test public void testLayoutOutputStableIdsForMegaDeepComponent() { final Component component1 = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Column.create(c) .child( Column.create(c) .child(TestDrawableComponent.create(c)) .child(TestDrawableComponent.create(c)) .wrapInView()) .child( Column.create(c) .child( TestDrawableComponent.create(c) .withLayout() .wrapInView()) .child( Column.create(c) .child(TestDrawableComponent.create(c)) .child(TestDrawableComponent.create(c)) .wrapInView()) .wrapInView()) .child( Column.create(c) .child(TestDrawableComponent.create(c)) .child( Column.create(c) .child(TestDrawableComponent.create(c)) .child(TestDrawableComponent.create(c))) .wrapInView()) .child( Column.create(c) .child(TestDrawableComponent.create(c)) .child( Column.create(c) .child(TestDrawableComponent.create(c)) .child(TestViewComponent.create(c))) .wrapInView()) .build(); } }; final Component component2 = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Column.create(c) .child( Column.create(c) .child(TestDrawableComponent.create(c)) .child(TestDrawableComponent.create(c)) .wrapInView()) .child( Column.create(c) .child( TestDrawableComponent.create(c) .withLayout() .wrapInView()) .child( Column.create(c) .child(TestDrawableComponent.create(c)) .child(TestDrawableComponent.create(c)) .wrapInView()) .wrapInView()) .child( Column.create(c) .child(TestDrawableComponent.create(c)) .child( Column.create(c) .child(TestDrawableComponent.create(c)) .child(TestDrawableComponent.create(c))) .wrapInView()) .child( Column.create(c) .child(TestDrawableComponent.create(c)) .child( Column.create(c) .child(TestDrawableComponent.create(c)) .child(TestViewComponent.create(c))) .wrapInView()) .build(); } }; LayoutState layoutState = calculateLayoutState( RuntimeEnvironment.application, component1, -1, SizeSpec.makeSizeSpec(350, SizeSpec.EXACTLY), SizeSpec.makeSizeSpec(20, SizeSpec.EXACTLY)); LayoutState sameComponentLayoutState = calculateLayoutState( RuntimeEnvironment.application, component2, -1, SizeSpec.makeSizeSpec(350, SizeSpec.EXACTLY), SizeSpec.makeSizeSpec(20, SizeSpec.EXACTLY)); assertEquals( layoutState.getMountableOutputCount(), sameComponentLayoutState.getMountableOutputCount()); for (int i = 0; i < layoutState.getMountableOutputCount(); i++) { assertEquals( layoutState.getMountableOutputAt(i).getId(), sameComponentLayoutState.getMountableOutputAt(i).getId()); } } @Test public void testPartiallyStableIds() { final Component component1 = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Column.create(c) .child(TestDrawableComponent.create(c)) .child(TestDrawableComponent.create(c)) .build(); } }; final Component component2 = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Column.create(c) .child(TestDrawableComponent.create(c)) .child( Column.create(c) .child(TestDrawableComponent.create(c)) .child(TestDrawableComponent.create(c))) .build(); } }; LayoutState layoutState1 = calculateLayoutState( RuntimeEnvironment.application, component1, -1, SizeSpec.makeSizeSpec(350, SizeSpec.EXACTLY), SizeSpec.makeSizeSpec(20, SizeSpec.EXACTLY)); LayoutState layoutState2 = calculateLayoutState( RuntimeEnvironment.application, component2, -1, SizeSpec.makeSizeSpec(350, SizeSpec.EXACTLY), SizeSpec.makeSizeSpec(20, SizeSpec.EXACTLY)); assertEquals( layoutState1.getMountableOutputAt(0).getId(), layoutState2.getMountableOutputAt(0).getId()); assertEquals( layoutState1.getMountableOutputAt(1).getId(), layoutState2.getMountableOutputAt(1).getId()); assertEquals(3, layoutState1.getMountableOutputCount()); assertEquals(4, layoutState2.getMountableOutputCount()); } @Test public void testDifferentIds() { final Component component1 = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Column.create(c) .child(TestDrawableComponent.create(c)) .child(TestDrawableComponent.create(c)) .build(); } }; final Component component2 = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Column.create(c) .child( TestDrawableComponent.create(c) .withLayout() .wrapInView()) .child( Column.create(c) .child(TestDrawableComponent.create(c)) .child(TestDrawableComponent.create(c)) .wrapInView()) .build(); } }; LayoutState layoutState1 = calculateLayoutState( RuntimeEnvironment.application, component1, -1, SizeSpec.makeSizeSpec(350, SizeSpec.EXACTLY), SizeSpec.makeSizeSpec(20, SizeSpec.EXACTLY)); LayoutState layoutState2 = calculateLayoutState( RuntimeEnvironment.application, component2, -1, SizeSpec.makeSizeSpec(350, SizeSpec.EXACTLY), SizeSpec.makeSizeSpec(20, SizeSpec.EXACTLY)); assertNotEquals( layoutState1.getMountableOutputAt(1).getId(), layoutState2.getMountableOutputAt(1).getId()); } @Test public void testLayoutOutputsWithInteractiveLayoutSpecAsLeafs() { final Component component = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Column.create(c) .child(TestDrawableComponent.create(c)) .child( Column.create(c) .child(TestLayoutComponent.create(c)) .wrapInView()) .build(); } }; LayoutState layoutState = calculateLayoutState( RuntimeEnvironment.application, component, -1, SizeSpec.makeSizeSpec(350, SizeSpec.EXACTLY), SizeSpec.makeSizeSpec(200, SizeSpec.EXACTLY)); // Check total layout outputs. assertEquals(3, layoutState.getMountableOutputCount()); assertTrue(isHostComponent(getComponentAt(layoutState, 0))); assertTrue(getComponentAt(layoutState, 1) instanceof TestDrawableComponent); assertTrue(isHostComponent(getComponentAt(layoutState, 2))); } private static ComponentLifecycle getComponentAt(LayoutState layoutState, int index) { return layoutState.getMountableOutputAt(index).getComponent().getLifecycle(); } private static CharSequence getTextFromTextComponent(LayoutState layoutState, int index) { return Whitebox.getInternalState(layoutState.getMountableOutputAt(index).getComponent(), "text"); } private static boolean isHostComponent(ComponentLifecycle component) { return component instanceof HostComponent; } @Test public void testNoMeasureOnNestedComponentWithSameSpecs() { final ComponentContext c = new ComponentContext(RuntimeEnvironment.application); final Size size = new Size(); final TestComponent innerComponent = TestDrawableComponent.create(c, 0, 0, false, true, true, false, false).build(); final int widthSpec = SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY); final int heightSpec = SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY); innerComponent.measure( c, widthSpec, heightSpec, size); InternalNode internalNode = ((Component) innerComponent).getCachedLayout(); internalNode.setLastWidthSpec(widthSpec); internalNode.setLastHeightSpec(heightSpec); internalNode.setLastMeasuredWidth(internalNode.getWidth()); internalNode.setLastMeasuredHeight(internalNode.getHeight()); innerComponent.resetInteractions(); final Component component = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Column.create(c) .child( Layout.create(c, innerComponent) .widthPx(100) .heightPx(100)) .build(); } }; calculateLayoutState( RuntimeEnvironment.application, component, -1, SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY), SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY)); assertFalse(innerComponent.wasMeasureCalled()); } @Test public void testNoMeasureOnNestedComponentWithNewMeasureSpecExact() { final ComponentContext c = new ComponentContext(RuntimeEnvironment.application); final Size size = new Size(); final TestComponent innerComponent = TestDrawableComponent.create(c, 0, 0, false, true, true, false, false).build(); final int widthSpec = SizeSpec.makeSizeSpec(100, SizeSpec.AT_MOST); final int heightSpec = SizeSpec.makeSizeSpec(100, SizeSpec.AT_MOST); innerComponent.measure( c, widthSpec, heightSpec, size); InternalNode internalNode = ((Component) innerComponent).getCachedLayout(); internalNode.setLastWidthSpec(widthSpec); internalNode.setLastHeightSpec(heightSpec); internalNode.setLastMeasuredWidth(100); internalNode.setLastMeasuredHeight(100); innerComponent.resetInteractions(); final Component component = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Column.create(c) .child( Layout.create(c, innerComponent) .widthPx(100) .heightPx(100)) .build(); } }; calculateLayoutState( RuntimeEnvironment.application, component, -1, SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY), SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY)); assertFalse(innerComponent.wasMeasureCalled()); } @Test public void testNoMeasureOnNestedComponentWithNewMeasureSpecOldUnspecified() { final ComponentContext c = new ComponentContext(RuntimeEnvironment.application); final Size size = new Size(); final TestComponent innerComponent = TestDrawableComponent.create(c, 0, 0, false, true, true, false, false).build(); final int widthSpec = SizeSpec.makeSizeSpec(0, SizeSpec.UNSPECIFIED); final int heightSpec = SizeSpec.makeSizeSpec(0, SizeSpec.UNSPECIFIED); innerComponent.measure( c, widthSpec, heightSpec, size); InternalNode internalNode = ((Component) innerComponent).getCachedLayout(); internalNode.setLastWidthSpec(widthSpec); internalNode.setLastHeightSpec(heightSpec); internalNode.setLastMeasuredWidth(99); internalNode.setLastMeasuredHeight(99); innerComponent.resetInteractions(); final Component component = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Column.create(c) .child(innerComponent) .build(); } }; calculateLayoutState( RuntimeEnvironment.application, component, -1, SizeSpec.makeSizeSpec(100, SizeSpec.AT_MOST), SizeSpec.makeSizeSpec(100, SizeSpec.AT_MOST)); assertFalse(innerComponent.wasMeasureCalled()); } @Test public void testNoMeasureOnNestedComponentWithOldAndNewAtMost() { final ComponentContext c = new ComponentContext(RuntimeEnvironment.application); final Size size = new Size(); final TestComponent innerComponent = TestDrawableComponent.create(c, 0, 0, false, true, true, false, false).build(); final int widthSpec = SizeSpec.makeSizeSpec(100, SizeSpec.AT_MOST); final int heightSpec = SizeSpec.makeSizeSpec(100, SizeSpec.AT_MOST); innerComponent.measure( c, widthSpec, heightSpec, size); InternalNode internalNode = ((Component) innerComponent).getCachedLayout(); internalNode.setLastWidthSpec(widthSpec); internalNode.setLastHeightSpec(heightSpec); internalNode.setLastMeasuredWidth(50); internalNode.setLastMeasuredHeight(50); innerComponent.resetInteractions(); final Component component = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Column.create(c) .child(Layout.create(c, innerComponent).flexShrink(0)) .build(); } }; calculateLayoutState( RuntimeEnvironment.application, component, -1, SizeSpec.makeSizeSpec(50, SizeSpec.AT_MOST), SizeSpec.makeSizeSpec(50, SizeSpec.AT_MOST)); assertFalse(innerComponent.wasMeasureCalled()); } @Test public void testLayoutOutputsForTwiceNestedComponent() { final Component component = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Column.create(c) .child( Column.create(c) .child( Column.create(c) .child(TestDrawableComponent.create(c))) .wrapInView()) .child( Column.create(c) .child( Column.create(c) .child(TestDrawableComponent.create(c))) .child( Column.create(c) .child(TestDrawableComponent.create(c)))) .build(); } }; LayoutState layoutState = calculateLayoutState( RuntimeEnvironment.application, component, -1, SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY), SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY)); assertEquals(5, layoutState.getMountableOutputCount()); long hostMarkerRoot = layoutState.getMountableOutputAt(0).getId(); long hostMarkerOne = layoutState.getMountableOutputAt(1).getId(); // First output is the inner host for the click handler assertEquals(hostMarkerRoot, layoutState.getMountableOutputAt(1).getHostMarker()); // Second output is the child of the inner host assertEquals(hostMarkerOne, layoutState.getMountableOutputAt(2).getHostMarker()); // Third and fourth outputs are children of the root view. assertEquals(hostMarkerRoot, layoutState.getMountableOutputAt(3).getHostMarker()); assertEquals(hostMarkerRoot, layoutState.getMountableOutputAt(4).getHostMarker()); } @Test public void testLayoutOutputsForComponentWithBackgrounds() { final Component component = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Column.create(c) .backgroundColor(0xFFFF0000) .foregroundColor(0xFFFF0000) .child(TestDrawableComponent.create(c)) .build(); } }; LayoutState layoutState = calculateLayoutState( RuntimeEnvironment.application, component, -1, SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY), SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY)); assertEquals(4, layoutState.getMountableOutputCount()); // First and third output are the background and the foreground assertTrue(getComponentAt(layoutState, 1) instanceof DrawableComponent); assertTrue(getComponentAt(layoutState, 3) instanceof DrawableComponent); } @Test public void testLayoutOutputsForNonComponentClickableNode() { final Component component = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Column.create(c) .child( Column.create(c) .child(TestDrawableComponent.create(c)) .wrapInView()) .child( Column.create(c) .child(TestDrawableComponent.create(c)) .child(TestDrawableComponent.create(c)) .wrapInView()) .child( Column.create(c) .child(TestDrawableComponent.create(c)) .child(TestViewComponent.create(c)) .wrapInView()) .build(); } }; LayoutState layoutState = calculateLayoutState( RuntimeEnvironment.application, component, -1, SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY), SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY)); assertEquals(9, layoutState.getMountableOutputCount()); long hostMarkerRoot = layoutState.getMountableOutputAt(0).getHostMarker(); long hostMarkerZero = layoutState.getMountableOutputAt(1).getHostMarker(); long hostMarkerTwo = layoutState.getMountableOutputAt(4).getHostMarker(); long hostMarkerThree = layoutState.getMountableOutputAt(7).getHostMarker(); assertEquals(hostMarkerRoot, layoutState.getMountableOutputAt(1).getHostMarker()); assertEquals(hostMarkerZero, layoutState.getMountableOutputAt(3).getHostMarker()); assertEquals(hostMarkerTwo, layoutState.getMountableOutputAt(5).getHostMarker()); assertEquals(hostMarkerZero, layoutState.getMountableOutputAt(6).getHostMarker()); assertEquals(hostMarkerThree, layoutState.getMountableOutputAt(8).getHostMarker()); assertTrue(isHostComponent(getComponentAt(layoutState, 0))); assertTrue(isHostComponent(getComponentAt(layoutState, 1))); assertTrue(getComponentAt(layoutState, 2) instanceof TestDrawableComponent); assertTrue(isHostComponent(getComponentAt(layoutState, 3))); assertTrue(getComponentAt(layoutState, 4) instanceof TestDrawableComponent); assertTrue(getComponentAt(layoutState, 5) instanceof TestDrawableComponent); assertTrue(isHostComponent(getComponentAt(layoutState, 6))); assertTrue(getComponentAt(layoutState, 7) instanceof TestDrawableComponent); assertTrue(getComponentAt(layoutState, 8) instanceof TestViewComponent); } @Test public void testLayoutOutputsForNonComponentContentDescriptionNode() { enableAccessibility(); final Component component = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Column.create(c) .child( Column.create(c) .child(TestDrawableComponent.create(c)) .contentDescription("cd0")) .child( Column.create(c) .child(TestDrawableComponent.create(c)) .child(TestDrawableComponent.create(c)) .contentDescription("cd1")) .child( Column.create(c) .child(TestDrawableComponent.create(c)) .child(TestViewComponent.create(c)) .contentDescription("cd2")) .build(); } }; LayoutState layoutState = calculateLayoutState( RuntimeEnvironment.application, component, -1, SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY), SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY)); assertEquals(9, layoutState.getMountableOutputCount()); long hostMarkerRoot = layoutState.getMountableOutputAt(0).getHostMarker(); long hostMarkerZero = layoutState.getMountableOutputAt(1).getHostMarker(); long hostMarkerTwo = layoutState.getMountableOutputAt(4).getHostMarker(); long hostMarkerThree = layoutState.getMountableOutputAt(7).getHostMarker(); assertEquals(hostMarkerRoot, layoutState.getMountableOutputAt(1).getHostMarker()); assertEquals(hostMarkerZero, layoutState.getMountableOutputAt(3).getHostMarker()); assertEquals(hostMarkerTwo, layoutState.getMountableOutputAt(5).getHostMarker()); assertEquals(hostMarkerZero, layoutState.getMountableOutputAt(6).getHostMarker()); assertEquals(hostMarkerThree, layoutState.getMountableOutputAt(8).getHostMarker()); assertTrue(isHostComponent(getComponentAt(layoutState, 0))); assertTrue(isHostComponent(getComponentAt(layoutState, 1))); assertTrue(getComponentAt(layoutState, 2) instanceof TestDrawableComponent); assertTrue(isHostComponent(getComponentAt(layoutState, 3))); assertTrue(getComponentAt(layoutState, 4) instanceof TestDrawableComponent); assertTrue(getComponentAt(layoutState, 5) instanceof TestDrawableComponent); assertTrue(isHostComponent(getComponentAt(layoutState, 6))); assertTrue(getComponentAt(layoutState, 7) instanceof TestDrawableComponent); assertTrue(getComponentAt(layoutState, 8) instanceof TestViewComponent); } @Test public void testLayoutOutputsForFocusableOnRoot() { final Component component = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Column.create(c) .child(TestDrawableComponent.create(c)) .focusable(true) .build(); } }; LayoutState layoutState = calculateLayoutState( RuntimeEnvironment.application, component, -1, SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY), SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY)); assertEquals(2, layoutState.getMountableOutputCount()); long hostMarkerZero = layoutState.getMountableOutputAt(0).getHostMarker(); assertEquals(hostMarkerZero, layoutState.getMountableOutputAt(1).getHostMarker()); assertTrue(isHostComponent(getComponentAt(layoutState, 0))); assertTrue(getComponentAt(layoutState, 1) instanceof TestDrawableComponent); assertEquals(FOCUS_SET_TRUE, layoutState.getMountableOutputAt(0).getNodeInfo().getFocusState()); } @Test public void testLayoutOutputsForFocusable() { final Component component = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Column.create(c) .child( Column.create(c) .child(TestDrawableComponent.create(c)) .focusable(true)) .build(); } }; LayoutState layoutState = calculateLayoutState( RuntimeEnvironment.application, component, -1, SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY), SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY)); assertEquals(3, layoutState.getMountableOutputCount()); assertNull(layoutState.getMountableOutputAt(0).getNodeInfo()); assertEquals(FOCUS_SET_TRUE, layoutState.getMountableOutputAt(1).getNodeInfo().getFocusState()); } @Test public void testLayoutOutputsForAccessibilityEnabled() { enableAccessibility(); final Component component = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Row.create(c) .alignItems(YogaAlign.CENTER) .paddingDip(YogaEdge.ALL, 10) .contentDescription("This is root view") .child( TestDrawableComponent.create(c) .withLayout() .widthDip(30) .heightDip(30)) .child( TestDrawableComponent.create(c, true, true, true, true, false) .withLayout() .flex(1).flexBasisDip(0) .backgroundColor(Color.RED) .marginDip(YogaEdge.HORIZONTAL, 10)) .child( Row.create(c) .alignItems(YogaAlign.CENTER) .paddingDip(YogaEdge.ALL, 10) .contentDescription("This is a container") .child( TestDrawableComponent.create(c) .withLayout() .widthDip(30) .heightDip(30) .contentDescription("This is an image")) .child( TestDrawableComponent.create(c, true, true, true, true, false) .withLayout() .flex(1).flexBasisDip(0) .marginDip(YogaEdge.HORIZONTAL, 10))) .build(); } }; LayoutState layoutState = calculateLayoutState( RuntimeEnvironment.application, component, -1, SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY), SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY)); assertEquals(10, layoutState.getMountableOutputCount()); long hostMarkerRoot = layoutState.getMountableOutputAt(1).getHostMarker(); long hostMarkerOne = layoutState.getMountableOutputAt(3).getHostMarker(); long hostMarkerTwo = layoutState.getMountableOutputAt(6).getHostMarker(); long hostMarkerThree = layoutState.getMountableOutputAt(7).getHostMarker(); long hostMarkerFour = layoutState.getMountableOutputAt(9).getHostMarker(); assertEquals(hostMarkerRoot, layoutState.getMountableOutputAt(1).getHostMarker()); assertEquals(hostMarkerOne, layoutState.getMountableOutputAt(3).getHostMarker()); assertEquals(hostMarkerOne, layoutState.getMountableOutputAt(4).getHostMarker()); assertEquals(hostMarkerTwo, layoutState.getMountableOutputAt(6).getHostMarker()); assertEquals(hostMarkerThree, layoutState.getMountableOutputAt(7).getHostMarker()); assertEquals(hostMarkerFour, layoutState.getMountableOutputAt(9).getHostMarker()); assertTrue(isHostComponent(getComponentAt(layoutState, 0))); assertTrue(getComponentAt(layoutState, 1) instanceof TestDrawableComponent); assertTrue(isHostComponent(getComponentAt(layoutState, 2))); assertTrue(getComponentAt(layoutState, 3) instanceof DrawableComponent); assertTrue(getComponentAt(layoutState, 4) instanceof TestDrawableComponent); assertTrue(isHostComponent(getComponentAt(layoutState, 5))); assertTrue(isHostComponent(getComponentAt(layoutState, 6))); assertTrue(getComponentAt(layoutState, 7) instanceof TestDrawableComponent); assertTrue(isHostComponent(getComponentAt(layoutState, 8))); assertTrue(getComponentAt(layoutState, 9) instanceof TestDrawableComponent); } @Test public void testLayoutOutputsWithImportantForAccessibility() { enableAccessibility(); final Component component = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Column.create(c) .contentDescription("This is root view") .child( TestDrawableComponent.create(c) .withLayout() .widthDip(30) .heightDip(30)) .child( TestDrawableComponent.create(c, true, true, true, true, false) .withLayout() .flex(1).flexBasisDip(0) .backgroundColor(Color.RED) .marginDip(YogaEdge.HORIZONTAL, 10) .importantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO)) .child( Row.create(c) .alignItems(YogaAlign.CENTER) .paddingDip(YogaEdge.ALL, 10) .importantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS) .child( TestDrawableComponent.create(c) .withLayout() .widthDip(30) .heightDip(30) .importantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES) .contentDescription("This is an image"))) .build(); } }; LayoutState layoutState = calculateLayoutState( RuntimeEnvironment.application, component, -1, SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY), SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY)); assertEquals(7, layoutState.getMountableOutputCount()); long hostMarkerRoot = layoutState.getMountableOutputAt(1).getHostMarker(); long hostMarkerOne = layoutState.getMountableOutputAt(5).getHostMarker(); long hostMarkerTwo = layoutState.getMountableOutputAt(6).getHostMarker(); assertEquals(hostMarkerRoot, layoutState.getMountableOutputAt(1).getHostMarker()); assertEquals(hostMarkerRoot, layoutState.getMountableOutputAt(2).getHostMarker()); assertEquals(hostMarkerRoot, layoutState.getMountableOutputAt(3).getHostMarker()); assertEquals(hostMarkerRoot, layoutState.getMountableOutputAt(4).getHostMarker()); assertEquals(hostMarkerOne, layoutState.getMountableOutputAt(5).getHostMarker()); assertEquals(hostMarkerTwo, layoutState.getMountableOutputAt(6).getHostMarker()); assertTrue(isHostComponent(getComponentAt(layoutState, 0))); assertTrue(getComponentAt(layoutState, 1) instanceof TestDrawableComponent); assertTrue(getComponentAt(layoutState, 2) instanceof DrawableComponent); assertTrue(getComponentAt(layoutState, 3) instanceof TestDrawableComponent); assertTrue(isHostComponent(getComponentAt(layoutState, 4))); assertTrue(isHostComponent(getComponentAt(layoutState, 5))); assertTrue(getComponentAt(layoutState, 6) instanceof TestDrawableComponent); assertEquals( layoutState.getMountableOutputAt(0).getImportantForAccessibility(), IMPORTANT_FOR_ACCESSIBILITY_AUTO); assertEquals( layoutState.getMountableOutputAt(1).getImportantForAccessibility(), IMPORTANT_FOR_ACCESSIBILITY_AUTO); assertEquals( layoutState.getMountableOutputAt(2).getImportantForAccessibility(), IMPORTANT_FOR_ACCESSIBILITY_NO); assertEquals( layoutState.getMountableOutputAt(3).getImportantForAccessibility(), IMPORTANT_FOR_ACCESSIBILITY_NO); assertEquals( layoutState.getMountableOutputAt(4).getImportantForAccessibility(), IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS); assertEquals( layoutState.getMountableOutputAt(5).getImportantForAccessibility(), IMPORTANT_FOR_ACCESSIBILITY_YES); assertEquals( layoutState.getMountableOutputAt(6).getImportantForAccessibility(), IMPORTANT_FOR_ACCESSIBILITY_YES); } @Test public void testLayoutOutputsForClickHandlerAndViewTagsOnRoot() { final Component component = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Column.create(c) .child(TestDrawableComponent.create(c)) .clickHandler(c.newEventHandler(1)) .viewTags(new SparseArray<>()) .build(); } }; LayoutState layoutState = calculateLayoutState( RuntimeEnvironment.application, component, -1, SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY), SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY)); assertEquals(2, layoutState.getMountableOutputCount()); long hostMarkerZero = layoutState.getMountableOutputAt(0).getHostMarker(); assertEquals(hostMarkerZero, layoutState.getMountableOutputAt(1).getHostMarker()); assertTrue(isHostComponent(getComponentAt(layoutState, 0))); assertTrue(getComponentAt(layoutState, 1) instanceof TestDrawableComponent); final NodeInfo nodeInfo = layoutState.getMountableOutputAt(0).getNodeInfo(); assertNotNull(nodeInfo); assertNotNull(nodeInfo.getClickHandler()); assertNotNull(nodeInfo.getViewTags()); } @Test public void testLayoutOutputsForLongClickHandlerAndViewTagsOnRoot() { final Component component = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Column.create(c) .child(TestDrawableComponent.create(c)) .longClickHandler(c.newEventHandler(1)) .viewTags(new SparseArray<>()) .build(); } }; LayoutState layoutState = calculateLayoutState( RuntimeEnvironment.application, component, -1, SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY), SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY)); assertEquals(2, layoutState.getMountableOutputCount()); long hostMarkerZero = layoutState.getMountableOutputAt(0).getHostMarker(); assertEquals(hostMarkerZero, layoutState.getMountableOutputAt(1).getHostMarker()); assertTrue(isHostComponent(getComponentAt(layoutState, 0))); assertTrue(getComponentAt(layoutState, 1) instanceof TestDrawableComponent); final NodeInfo nodeInfo = layoutState.getMountableOutputAt(0).getNodeInfo(); assertNotNull(nodeInfo); assertNotNull(nodeInfo.getLongClickHandler()); assertNotNull(nodeInfo.getViewTags()); } @Test public void testLayoutOutputsForForceWrappedComponent() { final Component component = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Column.create(c) .child( TestDrawableComponent.create(c) .withLayout() .wrapInView()) .build(); } }; final LayoutState layoutState = calculateLayoutState( RuntimeEnvironment.application, component, -1, SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY), SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY)); assertEquals(3, layoutState.getMountableOutputCount()); assertTrue(getComponentAt(layoutState, 0) instanceof HostComponent); assertTrue(getComponentAt(layoutState, 1) instanceof HostComponent); assertTrue(getComponentAt(layoutState, 2) instanceof TestDrawableComponent); } @Test public void testLayoutOutputForRootNestedTreeComponent() { LayoutState layoutState = calculateLayoutState( RuntimeEnvironment.application, TestSizeDependentComponent.create(new ComponentContext(RuntimeEnvironment.application)) .setFixSizes(true) .setDelegate(false) .build(), -1, SizeSpec.makeSizeSpec(350, SizeSpec.EXACTLY), SizeSpec.makeSizeSpec(200, SizeSpec.EXACTLY)); // Check total layout outputs. assertEquals(4, layoutState.getMountableOutputCount()); Rect mountBounds = new Rect(); // Check host. assertTrue(isHostComponent(getComponentAt(layoutState, 0))); layoutState.getMountableOutputAt(0).getMountBounds(mountBounds); assertEquals(new Rect(0, 0, 350, 200), mountBounds); assertEquals(0, layoutState.getMountableOutputAt(0).getHostMarker()); // Check NestedTree assertTrue(getComponentAt(layoutState, 1) instanceof DrawableComponent); layoutState.getMountableOutputAt(1).getMountBounds(mountBounds); assertEquals(new Rect(5, 5, 55, 55), mountBounds); assertTrue(getComponentAt(layoutState, 2) instanceof TestDrawableComponent); layoutState.getMountableOutputAt(2).getMountBounds(mountBounds); assertEquals(new Rect(5, 5, 55, 55), mountBounds); assertTrue(getComponentAt(layoutState, 3) instanceof TestViewComponent); layoutState.getMountableOutputAt(3).getMountBounds(mountBounds); assertEquals(new Rect(8, 58, 342, 78), mountBounds); } @Test public void testLayoutOutputForDelegateNestedTreeComponentDelegate() { final Component component = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Column.create(c) .paddingPx(YogaEdge.ALL, 2) .child( TestSizeDependentComponent.create(c) .setFixSizes(true) .setDelegate(true) .withLayout() .marginPx(YogaEdge.ALL, 11)) .build(); } }; LayoutState layoutState = calculateLayoutState( RuntimeEnvironment.application, component, -1, SizeSpec.makeSizeSpec(350, SizeSpec.EXACTLY), SizeSpec.makeSizeSpec(200, SizeSpec.EXACTLY)); // Check total layout outputs. assertEquals(3, layoutState.getMountableOutputCount()); Rect mountBounds = new Rect(); // Check host. assertTrue(isHostComponent(getComponentAt(layoutState, 0))); layoutState.getMountableOutputAt(0).getMountBounds(mountBounds); assertEquals(new Rect(0, 0, 350, 200), mountBounds); // Check NestedTree assertTrue(getComponentAt(layoutState, 1) instanceof DrawableComponent); layoutState.getMountableOutputAt(1).getMountBounds(mountBounds); assertEquals(new Rect(13, 13, 63, 63), mountBounds); assertTrue(getComponentAt(layoutState, 2) instanceof TestDrawableComponent); layoutState.getMountableOutputAt(2).getMountBounds(mountBounds); assertEquals(new Rect(13, 13, 63, 63), mountBounds); } @Test public void testLayoutOutputForDelegateNestedTreeComponent() { final Component component = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Column.create(c) .paddingPx(YogaEdge.ALL, 2) .child( TestSizeDependentComponent.create(c) .setFixSizes(true) .setDelegate(false) .withLayout() .marginPx(YogaEdge.ALL, 11)) .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.getMountableOutputCount()); Rect mountBounds = new Rect(); // Check host. assertTrue(isHostComponent(getComponentAt(layoutState, 0))); layoutState.getMountableOutputAt(0).getMountBounds(mountBounds); assertEquals(new Rect(0, 0, 350, 200), mountBounds); assertEquals(0, layoutState.getMountableOutputAt(0).getHostMarker()); // Check NestedTree assertTrue(getComponentAt(layoutState, 1) instanceof DrawableComponent); layoutState.getMountableOutputAt(1).getMountBounds(mountBounds); assertEquals(new Rect(18, 18, 68, 68), mountBounds); assertTrue(getComponentAt(layoutState, 2) instanceof TestDrawableComponent); layoutState.getMountableOutputAt(2).getMountBounds(mountBounds); assertEquals(new Rect(18, 18, 68, 68), mountBounds); assertTrue(getComponentAt(layoutState, 3) instanceof TestViewComponent); layoutState.getMountableOutputAt(3).getMountBounds(mountBounds); assertEquals(new Rect(21, 71, 329, 91), mountBounds); } @Test public void testLayoutOutputForRootWithDelegateNestedTreeComponent() { final Component component = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return TestSizeDependentComponent.create(c) .setFixSizes(true) .setDelegate(false) .buildWithLayout(); } }; LayoutState layoutState = calculateLayoutState( RuntimeEnvironment.application, component, -1, SizeSpec.makeSizeSpec(350, SizeSpec.EXACTLY), SizeSpec.makeSizeSpec(200, SizeSpec.EXACTLY)); // Check total layout outputs. assertEquals(4, layoutState.getMountableOutputCount()); Rect mountBounds = new Rect(); // Check host. assertTrue(isHostComponent(getComponentAt(layoutState, 0))); layoutState.getMountableOutputAt(0).getMountBounds(mountBounds); assertEquals(new Rect(0, 0, 350, 200), mountBounds); assertEquals(0, layoutState.getMountableOutputAt(0).getHostMarker()); // Check NestedTree assertTrue(getComponentAt(layoutState, 1) instanceof DrawableComponent); layoutState.getMountableOutputAt(1).getMountBounds(mountBounds); assertEquals(new Rect(5, 5, 55, 55), mountBounds); assertTrue(getComponentAt(layoutState, 2) instanceof TestDrawableComponent); layoutState.getMountableOutputAt(2).getMountBounds(mountBounds); assertEquals(new Rect(5, 5, 55, 55), mountBounds); assertTrue(getComponentAt(layoutState, 3) instanceof TestViewComponent); layoutState.getMountableOutputAt(3).getMountBounds(mountBounds); assertEquals(new Rect(8, 58, 342, 78), mountBounds); } @Test public void testLayoutOutputRootWithPaddingOverridingDelegateNestedTreeComponent() { final Component component = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { final Component<TestSizeDependentComponent> nestedTreeRootComponent = TestSizeDependentComponent.create(c) .setFixSizes(true) .setDelegate(false) .build(); return Layout.create(c, nestedTreeRootComponent) .paddingPx(YogaEdge.ALL, 10) .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.getMountableOutputCount()); Rect mountBounds = new Rect(); // Check host. assertTrue(isHostComponent(getComponentAt(layoutState, 0))); layoutState.getMountableOutputAt(0).getMountBounds(mountBounds); assertEquals(new Rect(0, 0, 350, 200), mountBounds); assertEquals(0, layoutState.getMountableOutputAt(0).getHostMarker()); // Check NestedTree assertTrue(getComponentAt(layoutState, 1) instanceof DrawableComponent); layoutState.getMountableOutputAt(1).getMountBounds(mountBounds); assertEquals(new Rect(10, 10, 60, 60), mountBounds); assertTrue(getComponentAt(layoutState, 2) instanceof TestDrawableComponent); layoutState.getMountableOutputAt(2).getMountBounds(mountBounds); assertEquals(new Rect(10, 10, 60, 60), mountBounds); assertTrue(getComponentAt(layoutState, 3) instanceof TestViewComponent); layoutState.getMountableOutputAt(3).getMountBounds(mountBounds); assertEquals(new Rect(13, 63, 337, 83), mountBounds); } @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.getMountableOutputCount()); } @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()) .build(); } }; LayoutState layoutState = calculateLayoutState( RuntimeEnvironment.application, component, -1, SizeSpec.makeSizeSpec(350, SizeSpec.EXACTLY), SizeSpec.makeSizeSpec(200, SizeSpec.EXACTLY)); assertEquals(1, layoutState.getMountableOutputCount()); Rect mountBounds = new Rect(); assertTrue(isHostComponent(getComponentAt(layoutState, 0))); layoutState.getMountableOutputAt(0).getMountBounds(mountBounds); assertEquals(new Rect(0, 0, 350, 200), mountBounds); } @Test public void testMeasure() { final int width = 50; final int height = 30; final ComponentContext c = new ComponentContext(RuntimeEnvironment.application); final Component component = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Column.create(c) .child( TestDrawableComponent.create(c) .measuredWidth(width) .measuredHeight(height)) .build(); } }; InternalNode node = LayoutState.createAndMeasureTreeForComponent( c, component, SizeSpec.makeSizeSpec(width, SizeSpec.AT_MOST), SizeSpec.makeSizeSpec(height, SizeSpec.AT_MOST)); assertEquals(width, node.getWidth()); assertEquals(height, node.getHeight()); assertEquals(1, node.getChildCount()); assertEquals(width, ((InternalNode) node.getChildAt(0)).getWidth()); assertEquals(height, ((InternalNode) node.getChildAt(0)).getHeight()); } @Test public void testLayoutOutputWithCachedLayoutSpec() { final ComponentContext c = new ComponentContext(RuntimeEnvironment.application); final int widthSpecContainer = SizeSpec.makeSizeSpec(300, SizeSpec.EXACTLY); final int heightSpec = SizeSpec.makeSizeSpec(0, SizeSpec.UNSPECIFIED); final int horizontalPadding = 20; final int widthMeasuredComponent = SizeSpec.makeSizeSpec( SizeSpec.getSize(widthSpecContainer) - horizontalPadding - horizontalPadding, SizeSpec.EXACTLY); final Component<?> componentSpy = PowerMockito.spy( TestLayoutComponent.create(c, 0, 0, true, true, true, false).build()); Size sizeOutput = new Size(); componentSpy.measure( c, widthMeasuredComponent, heightSpec, sizeOutput); // Check the cached measured component tree assertTrue(componentSpy.hasCachedLayout()); final InternalNode cachedLayout = componentSpy.getCachedLayout(); assertEquals(1, cachedLayout.getChildCount()); assertTrue(((InternalNode) cachedLayout.getChildAt(0)) .getRootComponent().getLifecycle() instanceof TestDrawableComponent); // Now embed the measured component in another container and calculate a layout. final Component rootContainer = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Column.create(c) .paddingPx(YogaEdge.HORIZONTAL, horizontalPadding) .child(componentSpy) .build(); } }; LayoutState layoutState = calculateLayoutState( RuntimeEnvironment.application, rootContainer, -1, widthSpecContainer, heightSpec); // Make sure we reused the cached layout and it wasn't released. verify(componentSpy, never()).releaseCachedLayout(); verify(componentSpy, times(1)).clearCachedLayout(); // Check total layout outputs. assertEquals(2, layoutState.getMountableOutputCount()); Rect mountBounds = new Rect(); // Check host. assertTrue(isHostComponent(getComponentAt(layoutState, 0))); layoutState.getMountableOutputAt(0).getMountBounds(mountBounds); assertEquals(new Rect(0, 0, 300, sizeOutput.height), mountBounds); assertTrue(getComponentAt(layoutState, 1) instanceof TestDrawableComponent); layoutState.getMountableOutputAt(1).getMountBounds(mountBounds); assertEquals(new Rect(20, 0, 280, 0), mountBounds); Mockito.validateMockitoUsage(); } @Test public void testLayoutOutputWithCachedLayoutSpecWithMeasure() { final ComponentContext c = new ComponentContext(RuntimeEnvironment.application); final int widthSpecContainer = SizeSpec.makeSizeSpec(300, SizeSpec.EXACTLY); final int heightSpec = SizeSpec.makeSizeSpec(0, SizeSpec.UNSPECIFIED); final int horizontalPadding = 20; final int widthMeasuredComponent = SizeSpec.makeSizeSpec( SizeSpec.getSize(widthSpecContainer) - horizontalPadding - horizontalPadding, SizeSpec.EXACTLY); final Component<?> sizeDependentComponentSpy = PowerMockito.spy( TestSizeDependentComponent.create(c) .setFixSizes(false) .setDelegate(false) .build()); Size sizeOutput = new Size(); sizeDependentComponentSpy.measure( c, widthMeasuredComponent, heightSpec, sizeOutput); // Check the cached measured component tree assertTrue(sizeDependentComponentSpy.hasCachedLayout()); final InternalNode cachedLayout = sizeDependentComponentSpy.getCachedLayout(); assertEquals(2, cachedLayout.getChildCount()); assertTrue(((InternalNode) cachedLayout.getChildAt(0)) .getRootComponent().getLifecycle() instanceof TestDrawableComponent); assertTrue(((InternalNode) cachedLayout.getChildAt(1)) .getRootComponent().getLifecycle() instanceof TestViewComponent); // Now embed the measured component in another container and calculate a layout. final Component rootContainer = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Column.create(c) .flexShrink(0) .paddingPx(YogaEdge.HORIZONTAL, horizontalPadding) .child(Layout.create(c, sizeDependentComponentSpy).flexShrink(0)) .build(); } }; LayoutState layoutState = calculateLayoutState( RuntimeEnvironment.application, rootContainer, -1, widthSpecContainer, heightSpec); // Make sure we reused the cached layout and it wasn't released. verify(sizeDependentComponentSpy, never()).releaseCachedLayout(); verify(sizeDependentComponentSpy, times(1)).clearCachedLayout(); // Check total layout outputs. assertEquals(4, layoutState.getMountableOutputCount()); Rect mountBounds = new Rect(); // Check host. assertTrue(isHostComponent(getComponentAt(layoutState, 0))); layoutState.getMountableOutputAt(0).getMountBounds(mountBounds); assertEquals(new Rect(0, 0, 300, sizeOutput.height), mountBounds); // Check NestedTree assertTrue(getComponentAt(layoutState, 1) instanceof DrawableComponent); layoutState.getMountableOutputAt(1).getMountBounds(mountBounds); assertEquals(new Rect(25, 5, 275, 11), mountBounds); assertTrue(getComponentAt(layoutState, 2) instanceof TestDrawableComponent); layoutState.getMountableOutputAt(2).getMountBounds(mountBounds); assertEquals(new Rect(25, 5, 275, 11), mountBounds); assertTrue(getComponentAt(layoutState, 3) instanceof TestViewComponent); layoutState.getMountableOutputAt(3).getMountBounds(mountBounds); assertEquals(new Rect(28, 14, 272, 14), mountBounds); Mockito.validateMockitoUsage(); } @Test public void testLayoutOutputWithCachedLayoutSpecDelegate() { final ComponentContext c = new ComponentContext(RuntimeEnvironment.application); final int widthSpecContainer = SizeSpec.makeSizeSpec(300, SizeSpec.EXACTLY); final int heightSpec = SizeSpec.makeSizeSpec(0, SizeSpec.UNSPECIFIED); final int horizontalPadding = 20; final int widthMeasuredComponent = SizeSpec.makeSizeSpec( SizeSpec.getSize(widthSpecContainer) - horizontalPadding - horizontalPadding, SizeSpec.EXACTLY); final Component<?> componentSpy = PowerMockito.spy( TestLayoutComponent.create(c, 0, 0, true, true, true, true).build()); Size sizeOutput = new Size(); componentSpy.measure( c, widthMeasuredComponent, heightSpec, sizeOutput); // Check the cached measured component tree assertTrue(componentSpy.hasCachedLayout()); final InternalNode cachedLayout = componentSpy.getCachedLayout(); assertEquals(0, cachedLayout.getChildCount()); assertTrue(cachedLayout.getRootComponent().getLifecycle() instanceof TestDrawableComponent); // Now embed the measured component in another container and calculate a layout. final Component rootContainer = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Column.create(c) .paddingPx(YogaEdge.HORIZONTAL, horizontalPadding) .child(componentSpy) .build(); } }; LayoutState layoutState = calculateLayoutState( RuntimeEnvironment.application, rootContainer, -1, widthSpecContainer, heightSpec); // Make sure we reused the cached layout and it wasn't released. verify(componentSpy, never()).releaseCachedLayout(); verify(componentSpy, times(1)).clearCachedLayout(); // Check total layout outputs. assertEquals(2, layoutState.getMountableOutputCount()); Rect mountBounds = new Rect(); // Check host. assertTrue(isHostComponent(getComponentAt(layoutState, 0))); layoutState.getMountableOutputAt(0).getMountBounds(mountBounds); assertEquals(new Rect(0, 0, 300, sizeOutput.height), mountBounds); assertTrue(getComponentAt(layoutState, 1) instanceof TestDrawableComponent); layoutState.getMountableOutputAt(1).getMountBounds(mountBounds); assertEquals(new Rect(20, 0, 280, 0), mountBounds); Mockito.validateMockitoUsage(); } @Test public void testLayoutOutputWithCachedLayoutSpecWithMeasureDelegate() { final ComponentContext c = new ComponentContext(RuntimeEnvironment.application); final int widthSpecContainer = SizeSpec.makeSizeSpec(300, SizeSpec.EXACTLY); final int heightSpec = SizeSpec.makeSizeSpec(0, SizeSpec.UNSPECIFIED); final int horizontalPadding = 20; final int widthMeasuredComponent = SizeSpec.makeSizeSpec( SizeSpec.getSize(widthSpecContainer) - horizontalPadding - horizontalPadding, SizeSpec.EXACTLY); final Component<?> sizeDependentComponentSpy = PowerMockito.spy( TestSizeDependentComponent.create(c) .setFixSizes(false) .setDelegate(true) .build()); Size sizeOutput = new Size(); sizeDependentComponentSpy.measure( c, widthMeasuredComponent, heightSpec, sizeOutput); // Check the cached measured component tree assertTrue(sizeDependentComponentSpy.hasCachedLayout()); final InternalNode cachedLayout = sizeDependentComponentSpy.getCachedLayout(); assertEquals(0, cachedLayout.getChildCount()); assertTrue(cachedLayout.getRootComponent().getLifecycle() instanceof TestDrawableComponent); // Now embed the measured component in another container and calculate a layout. final Component rootContainer = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Column.create(c) .paddingPx(YogaEdge.HORIZONTAL, horizontalPadding) .child(sizeDependentComponentSpy) .build(); } }; LayoutState layoutState = calculateLayoutState( RuntimeEnvironment.application, rootContainer, -1, widthSpecContainer, heightSpec); // Make sure we reused the cached layout and it wasn't released. verify(sizeDependentComponentSpy, never()).releaseCachedLayout(); verify(sizeDependentComponentSpy, times(1)).clearCachedLayout(); // Check total layout outputs. assertEquals(3, layoutState.getMountableOutputCount()); Rect mountBounds = new Rect(); // Check host. assertTrue(isHostComponent(getComponentAt(layoutState, 0))); layoutState.getMountableOutputAt(0).getMountBounds(mountBounds); assertEquals(new Rect(0, 0, 300, sizeOutput.height), mountBounds); // Check NestedTree assertTrue(getComponentAt(layoutState, 1) instanceof DrawableComponent); layoutState.getMountableOutputAt(1).getMountBounds(mountBounds); assertEquals(new Rect(20, 0, 280, 0), mountBounds); assertTrue(getComponentAt(layoutState, 2) instanceof TestDrawableComponent); layoutState.getMountableOutputAt(2).getMountBounds(mountBounds); assertEquals(new Rect(20, 0, 280, 0), mountBounds); Mockito.validateMockitoUsage(); } @Test public void testNestedTreeComponentWithDoubleMeasurementsDoesntThrow() { final Component component = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Row.create(c) .alignItems(YogaAlign.STRETCH) .paddingPx(YogaEdge.ALL, 2) .child( TestSizeDependentComponent.create(c) .setFixSizes(true) .setDelegate(false) .withLayout() .marginPx(YogaEdge.ALL, 11)) .child( TestDrawableComponent.create(c) .withLayout() .heightPx(200) .widthPx(200)) .build(); } }; calculateLayoutState( RuntimeEnvironment.application, component, -1, SizeSpec.makeSizeSpec(350, SizeSpec.EXACTLY), SizeSpec.makeSizeSpec(0, SizeSpec.UNSPECIFIED)); // Testing that is not throwing an exception. } @Test public void testLayoutOutputForRootNestedTreeComponentWithAspectRatio() { final Component component = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Column.create(c) .child( TestSizeDependentComponent.create(c) .withLayout() .widthPx(100) .aspectRatio(1)) .build(); } }; LayoutState layoutState = calculateLayoutState( RuntimeEnvironment.application, component, -1, SizeSpec.makeSizeSpec(0, SizeSpec.UNSPECIFIED), SizeSpec.makeSizeSpec(0, SizeSpec.UNSPECIFIED)); Rect mountBounds = new Rect(); layoutState.getMountableOutputAt(0).getMountBounds(mountBounds); assertEquals(new Rect(0, 0, 100, 100), mountBounds); } @Test public void testLayoutOutputForRootNestedTreeComponentWithPercentParentSizeDefined() { final Component component = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Column.create(c) .alignItems(YogaAlign.FLEX_START) .widthPx(100) .heightPx(100) .child( TestSizeDependentComponent.create(c) .withLayout() .widthPercent(50) .heightPercent(50) .backgroundColor(0xFFFF0000)) .build(); } }; LayoutState layoutState = calculateLayoutState( RuntimeEnvironment.application, component, -1, SizeSpec.makeSizeSpec(0, SizeSpec.UNSPECIFIED), SizeSpec.makeSizeSpec(0, SizeSpec.UNSPECIFIED)); Rect mountBounds = new Rect(); layoutState.getMountableOutputAt(0).getMountBounds(mountBounds); assertEquals(new Rect(0, 0, 100, 100), mountBounds); assertTrue(getComponentAt(layoutState, 1) instanceof DrawableComponent); layoutState.getMountableOutputAt(1).getMountBounds(mountBounds); assertEquals(new Rect(0, 0, 50, 50), mountBounds); } @Test public void testLayoutOutputForRootNestedTreeComponentWithPercent() { final Component component = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Column.create(c) .alignItems(YogaAlign.FLEX_START) .child( TestSizeDependentComponent.create(c) .setFixSizes(true) .withLayout() .widthPercent(50) .heightPercent(50) .backgroundColor(0xFFFF0000)) .build(); } }; LayoutState layoutState = calculateLayoutState( RuntimeEnvironment.application, component, -1, SizeSpec.makeSizeSpec(0, SizeSpec.UNSPECIFIED), SizeSpec.makeSizeSpec(0, SizeSpec.UNSPECIFIED)); Rect mountBounds = new Rect(); layoutState.getMountableOutputAt(0).getMountBounds(mountBounds); assertEquals(new Rect(0, 0, 60, 86), mountBounds); assertTrue(getComponentAt(layoutState, 1) instanceof DrawableComponent); layoutState.getMountableOutputAt(1).getMountBounds(mountBounds); assertEquals(new Rect(0, 0, 60, 86), mountBounds); } @Test public void testLayoutOutputForRootNestedTreeComponentWithPercentPadding() { final Component component = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Column.create(c) .alignItems(YogaAlign.FLEX_START) .widthPx(50) .heightPx(50) .child( TestSizeDependentComponent.create(c) .setFixSizes(true) .withLayout() .paddingPercent(YogaEdge.ALL, 10) .backgroundColor(0xFFFF0000)) .build(); } }; InternalNode root = LayoutState.createAndMeasureTreeForComponent( new ComponentContext(RuntimeEnvironment.application), component, SizeSpec.makeSizeSpec(0, SizeSpec.UNSPECIFIED), SizeSpec.makeSizeSpec(0, SizeSpec.UNSPECIFIED)); assertEquals(5, root.getChildAt(0).getNestedTree().getPaddingLeft()); assertEquals(5, root.getChildAt(0).getNestedTree().getPaddingTop()); assertEquals(5, root.getChildAt(0).getNestedTree().getPaddingRight()); assertEquals(5, root.getChildAt(0).getNestedTree().getPaddingBottom()); } @Test public void testLayoutOutputsForComponentWithBorderColorNoBorderWidth() { final Component component = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Column.create(c) .child(TestDrawableComponent.create(c)) .borderColor(Color.GREEN) .build(); } }; LayoutState layoutState = calculateLayoutState( RuntimeEnvironment.application, component, -1, SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY), SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY)); // No layout output generated related with borders // if borderColor is supplied but not borderWidth. assertEquals(2, layoutState.getMountableOutputCount()); } @Test public void testLayoutOutputsForComponentWithBorderWidthNoBorderColor() { final Component component = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Column.create(c) .child(TestDrawableComponent.create(c)) .borderWidthPx(YogaEdge.ALL, 10) .build(); } }; LayoutState layoutState = calculateLayoutState( RuntimeEnvironment.application, component, -1, SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY), SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY)); // No layout output generated related with borders // if borderWidth supplied but not borderColor. assertEquals(2, layoutState.getMountableOutputCount()); } @Test public void testLayoutOutputsForComponentWithBorderWidthAllAndBorderColor() { final Component component = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Column.create(c) .child(TestDrawableComponent.create(c)) .borderWidthPx(YogaEdge.ALL, 10) .borderColor(Color.GREEN) .build(); } }; LayoutState layoutState = calculateLayoutState( RuntimeEnvironment.application, component, -1, SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY), SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY)); assertEquals(3, layoutState.getMountableOutputCount()); // Output at index 1 is BorderColorDrawable component. assertTrue(getComponentAt(layoutState, 1) instanceof DrawableComponent); } @Test public void testLayoutOutputsForComponentWithBorderWidthTopAndBorderColor() { final Component component = new InlineLayoutSpec() { @Override protected ComponentLayout onCreateLayout(ComponentContext c) { return Column.create(c) .child(TestDrawableComponent.create(c)) .borderWidthPx(YogaEdge.TOP, 10) .borderColor(Color.GREEN) .build(); } }; LayoutState layoutState = calculateLayoutState( RuntimeEnvironment.application, component, -1, SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY), SizeSpec.makeSizeSpec(100, SizeSpec.EXACTLY)); assertEquals(3, layoutState.getMountableOutputCount()); // Output at index 1 is BorderColorDrawable component. assertTrue(getComponentAt(layoutState, 1) instanceof DrawableComponent); } private void enableAccessibility() { ShadowAccessibilityManager manager = Shadows.shadowOf( (AccessibilityManager) RuntimeEnvironment.application.getSystemService(ACCESSIBILITY_SERVICE)); manager.setEnabled(true); manager.setTouchExplorationEnabled(true); } private 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); } }