@Test public void testSetRootAsyncFollowedByNonCompatibleMeasureComputesSyncLayout() { ComponentTree componentTree = ComponentTree.create(mContext, mComponent).build(); componentTree.setLithoView(new LithoView(mContext)); componentTree.measure(mWidthSpec, mHeightSpec, new int[2], false); componentTree.attach(); Component newComponent = TestDrawableComponent.create(mContext).color(1234).build(); componentTree.setRootAsync(newComponent); componentTree.measure(mWidthSpec2, mHeightSpec2, new int[2], false); assertThat(componentTree.getRoot()).isEqualTo(newComponent); assertThat(componentTree.hasCompatibleLayout(mWidthSpec2, mHeightSpec2)).isTrue(); assertThat(componentTree.getMainThreadLayoutState().isForComponentId(newComponent.getId())) .isTrue(); // Clear tasks mLayoutThreadShadowLooper.runToEndOfTasks(); }
mPreviousComponentSimpleName = mComponentTree.getSimpleName(); && componentTree.getLithoView() != null && mInvalidStateLogParams != null && mInvalidStateLogParams.containsKey(SET_ALREADY_ATTACHED_COMPONENT_TREE)) { mComponentTree.detach(); mComponentTree.clearLithoView(); if (mComponentTree.isReleased()) { throw new IllegalStateException( "Setting a released ComponentTree to a LithoView, " + "released component was: " + mComponentTree.getReleasedComponent()); mComponentTree.setLithoView(this); mComponentTree.attach(); } else { requestLayout();
/** * Show the given tooltip on the component with the given anchorKey. * * @param c * @param lithoTooltip A {@link LithoTooltip} implementation to be shown on the anchor. * @param anchorKey key of the Litho Component that will be used as anchor * @param xOffset horizontal offset from default position where the tooltip shows. * @param yOffset vertical offset from default position where the tooltip shows. */ public static void showTooltip( ComponentContext c, LithoTooltip lithoTooltip, String anchorKey, int xOffset, int yOffset) { final ComponentTree componentTree = c.getComponentTree(); final Component rootComponent = c.getComponentScope(); if (componentTree == null || componentTree.isReleased() || !componentTree.hasMounted()) { return; } final String anchorGlobalKey = rootComponent == null ? anchorKey : ComponentKeyUtils.getKeyWithSeparator(rootComponent.getGlobalKey(), anchorKey); componentTree.showTooltip(lithoTooltip, anchorGlobalKey, xOffset, yOffset); }
public void performIncrementalMount(Rect visibleRect, boolean processVisibilityOutputs) { if (mComponentTree == null || !checkMainThreadLayoutStateForIncrementalMount()) { return; } if (mComponentTree.isIncrementalMountEnabled()) { mComponentTree.mountComponent(visibleRect, processVisibilityOutputs); } else { throw new IllegalStateException("To perform incremental mounting, you need first to enable" + " it when creating the ComponentTree."); } }
private boolean mountComponentIfNeeded() { if (mLithoView.isMountStateDirty() || mLithoView.mountStateNeedsRemount()) { if (mIncrementalMountEnabled) { incrementalMountComponent(); } else { mountComponent(null, true); } return true; } return false; }
/** * Change the root component measuring it on a background thread before updating the UI. * If this {@link LithoView} doesn't have a ComponentTree initialized, the root will be * computed synchronously. */ public void setComponentAsync(Component component) { if (mComponentTree == null) { setComponentTree(ComponentTree.create(getComponentContext(), component).build()); } else { mComponentTree.setRootAsync(component); } }
ComponentTree.create(mContext, root1) .layoutThreadHandler(handler) .useSharedLayoutStateFuture(true) .build(); componentTree.setLithoView(new LithoView(mContext)); componentTree.measure(mWidthSpec, mHeightSpec, new int[2], false); componentTree.attach(); root2.unlockWaitingOnCreateLayout = unlockWaitingOnCreateLayout; componentTree.setRootAsync(root2); assertEquals(1, componentTree.getLayoutStateFutures().size()); ComponentTree.LayoutStateFuture layoutStateFuture = componentTree.getLayoutStateFutures().get(0);
@Test public void testSetSizeSpecWithOutputWhenAttachedToViewWithSameSpec() { ComponentTree componentTree = ComponentTree.create(mContext, mComponent).build(); componentTree.setLithoView(new LithoView(mContext)); Size size = new Size(); componentTree.measure(mWidthSpec, mHeightSpec, new int[2], false); componentTree.attach(); componentTree.setSizeSpec(mWidthSpec, mHeightSpec, size); assertEquals(SizeSpec.getSize(mWidthSpec), size.width, 0.0); assertEquals(SizeSpec.getSize(mHeightSpec), size.height, 0.0); assertThat(componentTree.hasCompatibleLayout(mWidthSpec, mHeightSpec)).isTrue(); assertThat(componentTree.getRoot()).isEqualTo(mComponent); }
@Test public void testRemountAfterSettingNewRootTwice() { final TestComponent component1 = create(mContext).color(Color.RED).returnSelfInMakeShallowCopy().build(); final TestComponent component2 = create(mContext).returnSelfInMakeShallowCopy().color(Color.BLUE).build(); final LithoView lithoView = new LithoView(mContext); final ComponentTree componentTree = ComponentTree.create(mContext, Column.create(mContext).child(component1).build()).build(); mountComponent( lithoView, componentTree, makeMeasureSpec(100, EXACTLY), makeMeasureSpec(100, EXACTLY)); assertThat(component1.isMounted()).isTrue(); componentTree.setRootAndSizeSpec( Column.create(mContext).child(component2).build(), makeMeasureSpec(50, EXACTLY), makeMeasureSpec(50, EXACTLY)); componentTree.setSizeSpec(makeMeasureSpec(100, EXACTLY), makeMeasureSpec(100, EXACTLY)); assertThat(component2.isMounted()).isTrue(); }
@Test public void testSetRootWithTreePropsThenMeasure() { ComponentTree componentTree = create(mContext, mComponent).build(); componentTree.setLithoView(new LithoView(mContext)); componentTree.attach(); final TreeProps treeProps = new TreeProps(); treeProps.put(Object.class, "hello world"); componentTree.setRootAndSizeSpecAsync( TestDrawableComponent.create(mContext).build(), makeSizeSpec(100, EXACTLY), makeSizeSpec(100, EXACTLY), treeProps); assertThat(componentTree.getBackgroundLayoutState()).isNull(); componentTree.measure(makeSizeSpec(100, EXACTLY), makeSizeSpec(100, EXACTLY), new int[2], true); final ComponentContext c = getInternalState(componentTree.getMainThreadLayoutState(), "mContext"); assertThat(c.getTreeProps()).isNotNull(); assertThat(c.getTreeProps().get(Object.class)).isEqualTo(treeProps.get(Object.class)); }
/** * Create a new {@link LithoView} instance and initialize it * with the given {@link Component} root. * * @param context {@link ComponentContext}. * @param component The root component to draw. * @return {@link LithoView} able to render a {@link Component} hierarchy. */ public static LithoView create(ComponentContext context, Component component) { final LithoView lithoView = new LithoView(context); lithoView.setComponentTree(ComponentTree.create(context, component).build()); return lithoView; }
@Test public void testThreadSafeConcurrentPropListComponentAccess() { TestDrawableComponent testComponent = Mockito.spy(TestDrawableComponent.create(mContext).build()); List<Component> componentList = new ArrayList<>(); for (int i = 0; i < 10; i++) { componentList.add(testComponent); } final Component root = TestWrappedComponentProp.create(mContext).componentList(componentList).build(); final ComponentTree componentTree = ComponentTree.create(mContext, root).build(); componentTree.setRootAndSizeSpec( TestWrappedComponentProp.create(mContext).componentList(componentList).build(), makeSizeSpec(100, EXACTLY), makeSizeSpec(100, EXACTLY), null); componentTree.setRootAndSizeSpecAsync( TestWrappedComponentProp.create(mContext).componentList(componentList).build(), makeSizeSpec(100, EXACTLY), makeSizeSpec(100, EXACTLY)); mLayoutThreadShadowLooper.runToEndOfTasks(); verify(testComponent, times(19)).makeShallowCopy(); } }
@Test public void testSetInput() { Component component = TestLayoutComponent.create(mContext) .build(); ComponentTree componentTree = ComponentTree.create(mContext, component) .build(); componentTree.setRoot(mComponent); creationCommonChecks(componentTree); Assert.assertNull(Whitebox.getInternalState(componentTree, "mMainThreadLayoutState")); Assert.assertNull(Whitebox.getInternalState(componentTree, "mBackgroundLayoutState")); componentTree.setSizeSpec(mWidthSpec, mHeightSpec); // Since this happens post creation, it's not in general safe to update the main thread layout // state synchronously, so the result should be in the background layout state postSizeSpecChecks(componentTree, "mBackgroundLayoutState"); }
@Test public void testSetSizeSpec() { ComponentTree componentTree = ComponentTree.create(mContext, mComponent) .build(); componentTree.setSizeSpec(mWidthSpec, mHeightSpec); // Since this happens post creation, it's not in general safe to update the main thread layout // state synchronously, so the result should be in the background layout state postSizeSpecChecks(componentTree, "mBackgroundLayoutState"); }
@Test public void testListenerInvokedForMeasure() { mComponentTree.setNewLayoutStateReadyListener(mListener); mComponentTree.setLithoView(new LithoView(mContext)); mComponentTree.attach(); mComponentTree.setSizeSpec(mWidthSpec, mHeightSpec); verify(mListener).onNewLayoutStateReady(mComponentTree); reset(mListener); mComponentTree.measure(mWidthSpec2, mHeightSpec2, new int[] {0, 0}, false); verify(mListener).onNewLayoutStateReady(mComponentTree); }
@Test public void testSplitBgThreadLayouts() { SplitLayoutResolver.createForTag(splitTag, mMainConfig, mBgConfig, mEnabledComponent); SplitLayoutResolver resolver = SplitLayoutResolver.getForTag(splitTag); when(ThreadUtils.isMainThread()).thenReturn(false); Whitebox.setInternalState(resolver, "mainService", mainService); Whitebox.setInternalState(resolver, "bgService", bgService); final ComponentTree tree = ComponentTree.create(mContext, mComponent).splitLayoutTag(splitTag).build(); tree.setRootAndSizeSpecAsync( mComponent, makeSizeSpec(100, EXACTLY), makeSizeSpec(100, EXACTLY)); mLayoutThreadShadowLooper.runOneTask(); verify(bgService).submit(any(Runnable.class), eq(0)); verify(bgService).submit(any(Runnable.class), eq(1)); verify(bgService, never()).submit(any(Runnable.class), eq(2)); verify(mainService, never()).submit(any(Runnable.class), any()); }
@Test public void testSetCompatibleSizeSpecWithDifferentRoot() { ComponentTree componentTree = create(mContext, mComponent) .build(); Size size = new Size(); componentTree.setSizeSpec( makeSizeSpec(100, AT_MOST), makeSizeSpec(100, AT_MOST), size); assertEquals(100, size.width, 0.0); assertEquals(100, size.height, 0.0); LayoutState firstLayoutState = componentTree.getBackgroundLayoutState(); assertThat(firstLayoutState).isNotNull(); componentTree.setRootAndSizeSpec( TestDrawableComponent.create(mContext).build(), makeSizeSpec(100, EXACTLY), makeSizeSpec(100, EXACTLY), size); assertNotEquals(firstLayoutState, componentTree.getBackgroundLayoutState()); }
@Test public void testClearUnusedEntries() { Component component = mock(Component.class); ComponentTree componentTree = ComponentTree.create(mContext, component).build(); EventHandlersController eventHandlersController = componentTree.getEventHandlersController(); EventHandler eventHandler1 = mContext.newEventHandler(1); when(component.getGlobalKey()).thenReturn("component1"); componentTree.recordEventHandler(component, eventHandler1); eventHandlersController.bindEventHandlers(mContext, component, component.getGlobalKey()); eventHandlersController.clearUnusedEventHandlers(); assertThat(eventHandlersController.getEventHandlers().size()).isEqualTo(1); when(component.getGlobalKey()).thenReturn("component2"); componentTree.setRoot(component); componentTree.recordEventHandler(component, eventHandler1); eventHandlersController.bindEventHandlers(mContext, component, component.getGlobalKey()); eventHandlersController.clearUnusedEventHandlers(); assertThat(eventHandlersController.getEventHandlers().size()).isEqualTo(1); } }