Java Code Examples for org.robolectric.shadows.ShadowLooper#runUiThreadTasks()

The following examples show how to use org.robolectric.shadows.ShadowLooper#runUiThreadTasks() . You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example 1
Source File: ChoreographerCompatTest.java    From litho with Apache License 2.0 6 votes vote down vote up
@Test
public void testCreationFromMainThread() {
  ShadowLooper.pauseMainLooper();

  final AtomicBoolean firstCallback = new AtomicBoolean(false);
  final ChoreographerCompatImpl choreographerCompat = new ChoreographerCompatImpl();
  assertThat(choreographerCompat.isUsingChoreographer()).isTrue();
  new ChoreographerCompatImpl()
      .postFrameCallback(
          new FrameCallback() {
            @Override
            public void doFrame(long frameTimeNanos) {
              firstCallback.set(true);
            }
          });

  assertThat(firstCallback.get()).isFalse();

  ShadowLooper.runUiThreadTasks();

  assertThat(firstCallback.get()).isTrue();
}
 
Example 2
Source File: MountSpecLifecycleTest.java    From litho with Apache License 2.0 5 votes vote down vote up
@Test
public void onSetRootWithPreallocatedMountContent_shouldCallLifecycleMethods() {
  final Looper looper = ShadowLooper.getLooperForThread(Thread.currentThread());
  final ComponentTree tree =
      ComponentTree.create(mLithoViewRule.getContext())
          .shouldPreallocateMountContentPerMountSpec(true)
          .preAllocateMountContentHandler(new LithoHandler.DefaultLithoHandler(looper))
          .build();
  mLithoViewRule.useComponentTree(tree);

  final List<LifecycleStep.StepInfo> info = new ArrayList<>();
  final Component component =
      PreallocatedMountSpecLifecycleTester.create(mLithoViewRule.getContext())
          .steps(info)
          .build();

  mLithoViewRule
      .getComponentTree()
      .setRootAndSizeSpec(
          component, mLithoViewRule.getWidthSpec(), mLithoViewRule.getHeightSpec());

  mLithoViewRule.measure();

  ShadowLooper.runUiThreadTasks();

  assertThat(getSteps(info))
      .describedAs("Should call the lifecycle methods on new instance in expected order")
      .containsExactly(
          LifecycleStep.ON_PREPARE,
          LifecycleStep.ON_MEASURE,
          LifecycleStep.ON_BOUNDS_DEFINED,
          LifecycleStep.ON_ATTACHED);

  assertThat(ComponentsPools.getMountContentPools().size())
      .describedAs("Should contain only 1 content pool")
      .isEqualTo(1);
  assertThat(ComponentsPools.getMountContentPools().get(0).getName())
      .describedAs("Should contain content pool from PreallocatedMountSpecLifecycleTester")
      .isEqualTo("PreallocatedMountSpecLifecycleTester");
}
 
Example 3
Source File: ComponentTreeTest.java    From litho with Apache License 2.0 5 votes vote down vote up
@Test
public void testSetRootWithTreePropsThenUpdateState() {
  SimpleStateUpdateEmulatorSpec.Caller caller = new SimpleStateUpdateEmulatorSpec.Caller();
  ComponentTree componentTree =
      create(mContext, SimpleStateUpdateEmulator.create(mContext).caller(caller).build()).build();
  componentTree.setLithoView(new LithoView(mContext));
  componentTree.attach();

  componentTree.setRootAndSizeSpec(
      SimpleStateUpdateEmulator.create(mContext).caller(caller).build(),
      makeSizeSpec(100, EXACTLY),
      makeSizeSpec(100, EXACTLY),
      new Size());

  final TreeProps treeProps = new TreeProps();
  treeProps.put(Object.class, "hello world");

  componentTree.setRootAndSizeSpecAsync(
      SimpleStateUpdateEmulator.create(mContext).caller(caller).build(),
      makeSizeSpec(100, EXACTLY),
      makeSizeSpec(100, EXACTLY),
      treeProps);

  caller.increment();
  ShadowLooper.runUiThreadTasks();

  final ComponentContext c =
      getInternalState(componentTree.getMainThreadLayoutState(), "mContext");
  assertThat(c.getTreeProps()).isNotNull();
  assertThat(c.getTreeProps().get(Object.class)).isEqualTo(treeProps.get(Object.class));
}
 
Example 4
Source File: ComponentTreeTest.java    From litho with Apache License 2.0 5 votes vote down vote up
@Test
public void testSetSizeSpecAsyncFollowedBySetSizeSpecSyncBeforeStartReturnsCorrectSize() {
  final Component component =
      SimpleMountSpecTester.create(mContext).flexGrow(1).color(1234).build();
  ComponentTree componentTree = ComponentTree.create(mContext, component).build();
  componentTree.setLithoView(new LithoView(mContext));

  int widthSpec1 = SizeSpec.makeSizeSpec(1000, SizeSpec.EXACTLY);
  int heightSpec1 = SizeSpec.makeSizeSpec(1000, SizeSpec.AT_MOST);
  int widthSpec2 = SizeSpec.makeSizeSpec(500, SizeSpec.EXACTLY);
  int heightSpec2 = SizeSpec.makeSizeSpec(500, SizeSpec.AT_MOST);

  componentTree.attach();
  componentTree.measure(widthSpec1, heightSpec1, new int[2], false);

  componentTree.setSizeSpecAsync(widthSpec2, heightSpec2);

  Size size = new Size();
  componentTree.setSizeSpec(widthSpec2, heightSpec2, size);
  assertThat(size).isEqualToComparingFieldByField(new Size(500, 500));

  mBackgroundLayoutLooperRule.runToEndOfTasksSync();
  ShadowLooper.runUiThreadTasks();

  assertThat(componentTree.getRoot()).isEqualTo(component);
  assertThat(componentTree.hasCompatibleLayout(widthSpec2, heightSpec2)).isTrue();
  assertThat(componentTree.getMainThreadLayoutState().getHeight()).isEqualTo(500);
  assertThat(componentTree.getMainThreadLayoutState().getWidth()).isEqualTo(500);

  assertThat(mLithoStatsRule.getComponentCalculateLayoutCount())
      .describedAs("We expect one initial layout and the async layout.")
      .isEqualTo(2);
}
 
Example 5
Source File: PredecessorConditionUnitTest.java    From ActivityDiary with GNU General Public License v3.0 4 votes vote down vote up
@Test
    public void sorting() throws Exception {
        PredecessorCondition pc = new PredecessorCondition(helper);

        cleanActivities();

        List<Condition.Likelihood> list = pc.likelihoods();
        assertTrue("Empty List if not activity", list.isEmpty());

        DiaryActivity a1 = new DiaryActivity(1, "AAA", 13);
        DiaryActivity a2 = new DiaryActivity(2, "ZZZ", 14);
        DiaryActivity a3 = new DiaryActivity(3, "MMM", 15);

        helper.insertActivity(a1);
        helper.insertActivity(a2);
        helper.insertActivity(a3);
        long now = System.currentTimeMillis();
        Thread.sleep(4000);
//        ShadowSystemClock.setCurrentTimeMillis(now - 1000 * 60 * 5);
        helper.setCurrentActivity(a1);
        Thread.sleep(4000);
//        ShadowSystemClock.setCurrentTimeMillis(now - 1000 * 60 * 4);
        helper.setCurrentActivity(a2);
        Thread.sleep(4000);
//        ShadowSystemClock.setCurrentTimeMillis(now - 1000 * 60 * 3);
        helper.setCurrentActivity(a1);
        Thread.sleep(4000);
//        ShadowSystemClock.setCurrentTimeMillis(now - 1000 * 60 * 2);
        helper.setCurrentActivity(a3);
        Thread.sleep(4000);
//        ShadowSystemClock.setCurrentTimeMillis(now - 1000 * 60 * 1);
        helper.setCurrentActivity(a2);
        Thread.sleep(4000);
        ShadowLooper.runUiThreadTasks();
        Robolectric.flushForegroundThreadScheduler();
        Robolectric.flushBackgroundThreadScheduler();
        pc.refresh();
        boolean finished = false;
        while(!finished){
            Thread.sleep(100);
            if(!pc.isActive()){
                finished = true;
            }
        }

        Robolectric.flushForegroundThreadScheduler();
        Robolectric.flushBackgroundThreadScheduler();
        ShadowLooper.runUiThreadTasks();

        Robolectric.flushForegroundThreadScheduler();
        Robolectric.flushBackgroundThreadScheduler();
        ShadowLooper.runUiThreadTasks();

        Robolectric.flushForegroundThreadScheduler();
        Robolectric.flushBackgroundThreadScheduler();
        ShadowLooper.runUiThreadTasks();

        Robolectric.flushForegroundThreadScheduler();
        Robolectric.flushBackgroundThreadScheduler();
        ShadowLooper.runUiThreadTasks();

        list = pc.likelihoods();

        /* TODO: this is currently not easily testable, due to robolectrics
         * handling of multi-threading.
         * For now those tests are disabled. */
//        assertTrue("all activites are in likelihood list", list.size() == 3);

        /* TODO: this test needs to be adapted...
        * the diary is not written and read, and also the following conditions are valid only for the alphabetical Condition */
        Collections.sort(list, new Comparator<Condition.Likelihood>() {
                    @Override
                    public int compare(Condition.Likelihood o1, Condition.Likelihood o2) {
                        return Double.compare(o1.likelihood, o2.likelihood);
                    }
                });

        if(!list.isEmpty()) {
            Condition.Likelihood last = list.get(0);
            for (Condition.Likelihood l : list) {
                assertTrue("last is before", last.likelihood <= l.likelihood);
                assertTrue("last has a bigger text", last.activity.getName().compareTo(l.activity.getName()) >= 0);
                last = l;
            }
        }
    }
 
Example 6
Source File: ComponentTreeTest.java    From litho with Apache License 2.0 4 votes vote down vote up
@Test
public void testSetRootAsyncWithIncompatibleMeasureBeforeStart() {
  ComponentTree componentTree = ComponentTree.create(mContext, mComponent).build();
  componentTree.setLithoView(new LithoView(mContext));

  int widthSpec1 = SizeSpec.makeSizeSpec(1000, SizeSpec.EXACTLY);
  int heightSpec1 = SizeSpec.makeSizeSpec(1000, SizeSpec.EXACTLY);
  int widthSpec2 = SizeSpec.makeSizeSpec(1000, SizeSpec.EXACTLY);
  int heightSpec2 = SizeSpec.makeSizeSpec(0, SizeSpec.UNSPECIFIED);

  componentTree.attach();
  componentTree.measure(widthSpec1, heightSpec1, new int[2], false);

  Component newComponent = SimpleMountSpecTester.create(mContext).color(1234).build();
  componentTree.setRootAsync(newComponent);

  assertThat(componentTree.hasCompatibleLayout(widthSpec2, heightSpec2))
      .describedAs("Asserting test setup, second set of specs should not be compatible.")
      .isFalse();
  componentTree.measure(widthSpec2, heightSpec2, new int[2], false);

  // Since the layout thread hasn't started the async layout, we know it will capture the updated
  // size specs when it does run

  assertThat(componentTree.getRoot()).isEqualTo(newComponent);
  assertThat(componentTree.hasCompatibleLayout(widthSpec2, heightSpec2)).isTrue();
  assertThat(componentTree.getMainThreadLayoutState().isForComponentId(newComponent.getId()))
      .describedAs(
          "The old component spec is not compatible so we should do a sync layout with the new root.")
      .isTrue();

  mBackgroundLayoutLooperRule.runToEndOfTasksSync();
  ShadowLooper.runUiThreadTasks();

  // Once the async layout finishes, the main thread should have the updated layout.

  assertThat(componentTree.hasCompatibleLayout(widthSpec2, heightSpec2)).isTrue();
  assertThat(componentTree.getMainThreadLayoutState().isForComponentId(newComponent.getId()))
      .isTrue();
  assertThat(componentTree.getMainThreadLayoutState().getHeight()).isEqualTo(0);

  assertThat(mLithoStatsRule.getComponentCalculateLayoutCount())
      .describedAs(
          "We expect one initial layout and one layout after measure. The async layout shouldn't happen.")
      .isEqualTo(2);
}
 
Example 7
Source File: ComponentTreeTest.java    From litho with Apache License 2.0 4 votes vote down vote up
@Test
public void testSetRootAsyncWithCompatibleMeasureBeforeStart() {
  Component oldComponent =
      SimpleMountSpecTester.create(mContext).widthPx(100).heightPx(100).color(1234).build();
  ComponentTree componentTree = ComponentTree.create(mContext, oldComponent).build();
  componentTree.setLithoView(new LithoView(mContext));

  int widthSpec1 = SizeSpec.makeSizeSpec(1000, SizeSpec.EXACTLY);
  int heightSpec1 = SizeSpec.makeSizeSpec(1000, SizeSpec.AT_MOST);
  int widthSpec2 = SizeSpec.makeSizeSpec(1000, SizeSpec.EXACTLY);
  int heightSpec2 = SizeSpec.makeSizeSpec(500, SizeSpec.AT_MOST);

  componentTree.attach();
  componentTree.measure(widthSpec1, heightSpec1, new int[2], false);

  Component newComponent =
      SimpleMountSpecTester.create(mContext).widthPx(100).heightPx(100).color(1234).build();
  componentTree.setRootAsync(newComponent);

  assertThat(componentTree.hasCompatibleLayout(widthSpec2, heightSpec2))
      .describedAs("Asserting test setup, second set of specs should be compatible already.")
      .isTrue();
  componentTree.measure(widthSpec2, heightSpec2, new int[2], false);

  assertThat(componentTree.getRoot()).isEqualTo(newComponent);
  assertThat(componentTree.hasCompatibleLayout(widthSpec2, heightSpec2)).isTrue();
  assertThat(componentTree.getMainThreadLayoutState().isForComponentId(newComponent.getId()))
      .isTrue();

  mBackgroundLayoutLooperRule.runToEndOfTasksSync();
  ShadowLooper.runUiThreadTasks();

  assertThat(componentTree.hasCompatibleLayout(widthSpec2, heightSpec2)).isTrue();
  assertThat(componentTree.getMainThreadLayoutState().isForComponentId(newComponent.getId()))
      .isTrue();
  assertThat(componentTree.getMainThreadLayoutState().getHeight()).isEqualTo(100);

  assertThat(mLithoStatsRule.getComponentCalculateLayoutCount())
      .describedAs("We expect one initial layout and the async layout.")
      .isEqualTo(2);
}
 
Example 8
Source File: ComponentTreeTest.java    From litho with Apache License 2.0 4 votes vote down vote up
@Test
public void
    testSetRootAsyncWithIncompatibleMeasureButCompatibleMeasureForExistingLayoutBeforeStart() {
  Component oldComponent =
      SimpleMountSpecTester.create(mContext).widthPx(100).heightPx(100).color(1234).build();
  ComponentTree componentTree = ComponentTree.create(mContext, oldComponent).build();
  componentTree.setLithoView(new LithoView(mContext));

  int widthSpec1 = SizeSpec.makeSizeSpec(1000, SizeSpec.EXACTLY);
  int heightSpec1 = SizeSpec.makeSizeSpec(1000, SizeSpec.AT_MOST);
  int widthSpec2 = SizeSpec.makeSizeSpec(1000, SizeSpec.EXACTLY);
  int heightSpec2 = SizeSpec.makeSizeSpec(500, SizeSpec.AT_MOST);

  componentTree.attach();
  componentTree.measure(widthSpec1, heightSpec1, new int[2], false);

  Component newComponent = SimpleMountSpecTester.create(mContext).flexGrow(1).color(1234).build();
  componentTree.setRootAsync(newComponent);

  assertThat(componentTree.hasCompatibleLayout(widthSpec2, heightSpec2))
      .describedAs("Asserting test setup, second set of specs should be compatible already.")
      .isTrue();
  componentTree.measure(widthSpec2, heightSpec2, new int[2], false);

  assertThat(componentTree.getRoot()).isEqualTo(newComponent);
  assertThat(componentTree.hasCompatibleLayout(widthSpec2, heightSpec2)).isTrue();
  assertThat(componentTree.getMainThreadLayoutState().isForComponentId(newComponent.getId()))
      .isTrue();
  assertThat(componentTree.getMainThreadLayoutState().getHeight()).isEqualTo(500);

  mBackgroundLayoutLooperRule.runToEndOfTasksSync();
  ShadowLooper.runUiThreadTasks();

  assertThat(componentTree.hasCompatibleLayout(widthSpec2, heightSpec2)).isTrue();
  assertThat(componentTree.getMainThreadLayoutState().isForComponentId(newComponent.getId()))
      .isTrue();
  assertThat(componentTree.getMainThreadLayoutState().getHeight()).isEqualTo(500);

  assertThat(mLithoStatsRule.getComponentCalculateLayoutCount())
      .describedAs(
          "We expect one initial layout and one layout after measure. The async layout shouldn't happen.")
      .isEqualTo(2);
}
 
Example 9
Source File: ComponentTreeTest.java    From litho with Apache License 2.0 4 votes vote down vote up
@Test
public void testSetRootAsyncWithIncompatibleMeasureDuringLayout() throws InterruptedException {
  ComponentTree componentTree = ComponentTree.create(mContext, mComponent).build();
  componentTree.setLithoView(new LithoView(mContext));

  int widthSpec1 = SizeSpec.makeSizeSpec(1000, SizeSpec.EXACTLY);
  int heightSpec1 = SizeSpec.makeSizeSpec(1000, SizeSpec.EXACTLY);
  int widthSpec2 = SizeSpec.makeSizeSpec(1000, SizeSpec.EXACTLY);
  int heightSpec2 = SizeSpec.makeSizeSpec(0, SizeSpec.UNSPECIFIED);

  componentTree.attach();
  componentTree.measure(widthSpec1, heightSpec1, new int[2], false);

  final TestDrawableComponent.BlockInPrepareComponentListener blockInPrepare =
      new TestDrawableComponent.BlockInPrepareComponentListener();
  blockInPrepare.setDoNotBlockOnThisThread();
  TestDrawableComponent newComponent = TestDrawableComponent.create(mContext).color(1234).build();
  newComponent.setTestComponentListener(blockInPrepare);

  componentTree.setRootAsync(newComponent);

  final TimeOutSemaphore asyncLayoutFinish =
      runOnBackgroundThread(
          new Runnable() {
            @Override
            public void run() {
              mLayoutThreadShadowLooper.runToEndOfTasks();
            }
          });

  blockInPrepare.awaitPrepareStart();

  // At this point, the Layout thread is blocked in prepare (waiting for unblockAsyncPrepare) and
  // will have already captured the "bad" specs, but not completed its layout. We expect the main
  // thread to determine that this async layout will not be correct and that it needs to compute
  // one in measure

  assertThat(componentTree.hasCompatibleLayout(widthSpec2, heightSpec2))
      .describedAs("Asserting test setup, second set of specs should not be compatible.")
      .isFalse();
  componentTree.measure(widthSpec2, heightSpec2, new int[2], false);

  assertThat(componentTree.getRoot()).isEqualTo(newComponent);
  assertThat(componentTree.hasCompatibleLayout(widthSpec2, heightSpec2)).isTrue();
  assertThat(componentTree.getMainThreadLayoutState().isForComponentId(newComponent.getId()))
      .isTrue()
      .withFailMessage(
          "The main thread should calculate a new layout synchronously because the async layout will not have compatible size specs");
  assertThat(componentTree.getMainThreadLayoutState().getHeight()).isEqualTo(0);

  // Finally, let the async layout finish and make sure it doesn't replace the layout from measure

  blockInPrepare.allowPrepareToComplete();
  asyncLayoutFinish.acquire();

  ShadowLooper.runUiThreadTasks();

  assertThat(componentTree.getRoot()).isEqualTo(newComponent);
  assertThat(componentTree.hasCompatibleLayout(widthSpec2, heightSpec2)).isTrue();
  assertThat(componentTree.getMainThreadLayoutState().isForComponentId(newComponent.getId()))
      .isTrue();
  assertThat(componentTree.getMainThreadLayoutState().getHeight()).isEqualTo(0);

  assertThat(mLithoStatsRule.getComponentCalculateLayoutCount())
      .describedAs(
          "We expect one initial layout, the async layout (thrown away), and a final layout after measure.")
      .isEqualTo(3);
}
 
Example 10
Source File: ComponentTreeTest.java    From litho with Apache License 2.0 4 votes vote down vote up
@Test
public void testSetRootAsyncWithCompatibleMeasureDuringLayout() throws InterruptedException {
  Component oldComponent =
      SimpleMountSpecTester.create(mContext).widthPx(100).heightPx(100).color(1234).build();
  ComponentTree componentTree = ComponentTree.create(mContext, oldComponent).build();
  componentTree.setLithoView(new LithoView(mContext));

  int widthSpec1 = SizeSpec.makeSizeSpec(1000, SizeSpec.EXACTLY);
  int heightSpec1 = SizeSpec.makeSizeSpec(1000, SizeSpec.AT_MOST);
  int widthSpec2 = SizeSpec.makeSizeSpec(1000, SizeSpec.EXACTLY);
  int heightSpec2 = SizeSpec.makeSizeSpec(500, SizeSpec.AT_MOST);

  componentTree.attach();
  componentTree.measure(widthSpec1, heightSpec1, new int[2], false);

  final TestDrawableComponent.BlockInPrepareComponentListener blockInPrepare =
      new TestDrawableComponent.BlockInPrepareComponentListener();
  blockInPrepare.setDoNotBlockOnThisThread();
  TestDrawableComponent newComponent =
      TestDrawableComponent.create(mContext).widthPx(100).heightPx(100).color(1234).build();
  newComponent.setTestComponentListener(blockInPrepare);

  componentTree.setRootAsync(newComponent);

  final TimeOutSemaphore asyncLayoutFinish =
      runOnBackgroundThread(
          new Runnable() {
            @Override
            public void run() {
              mLayoutThreadShadowLooper.runToEndOfTasks();
            }
          });

  blockInPrepare.awaitPrepareStart();

  // At this point, the Layout thread is blocked in prepare (waiting for unblockAsyncPrepare) and
  // will have already captured the "bad" specs, but not completed its layout.

  // This is a bit of a hack: Robolectric's ShadowLegacyLooper implementation is synchronized
  // on runToEndOfTasks and post(). Since we are simulating being in the middle of calculating a
  // layout, this means that we can't post() to the same Handler (as we will try to do in measure)
  // The "fix" here is to update the layout thread to a new handler/looper that can be controlled
  // separately.
  HandlerThread newHandlerThread = createAndStartNewHandlerThread();
  componentTree.updateLayoutThreadHandler(
      new LithoHandler.DefaultLithoHandler(newHandlerThread.getLooper()));
  ShadowLooper newThreadLooper = Shadows.shadowOf(newHandlerThread.getLooper());

  assertThat(componentTree.hasCompatibleLayout(widthSpec2, heightSpec2))
      .describedAs("Asserting test setup, second set of specs should be compatible already.")
      .isTrue();
  componentTree.measure(widthSpec2, heightSpec2, new int[2], false);

  assertThat(componentTree.getRoot()).isEqualTo(newComponent);
  assertThat(componentTree.hasCompatibleLayout(widthSpec2, heightSpec2)).isTrue();
  assertThat(componentTree.getMainThreadLayoutState().isForComponentId(newComponent.getId()))
      .isTrue()
      .withFailMessage(
          "The main thread should calculate a new layout synchronously because the async layout will not have compatible size specs");
  assertThat(componentTree.getMainThreadLayoutState().getHeight()).isEqualTo(100);

  // Finally, let the async layout finish and make sure it doesn't replace the layout from measure

  blockInPrepare.allowPrepareToComplete();
  asyncLayoutFinish.acquire();

  newComponent.setTestComponentListener(null);
  newThreadLooper.runToEndOfTasks();
  mLayoutThreadShadowLooper.runToEndOfTasks();
  ShadowLooper.runUiThreadTasks();

  assertThat(componentTree.getRoot()).isEqualTo(newComponent);
  assertThat(componentTree.hasCompatibleLayout(widthSpec2, heightSpec2)).isTrue();
  assertThat(componentTree.getMainThreadLayoutState().isForComponentId(newComponent.getId()))
      .isTrue();
  assertThat(componentTree.getMainThreadLayoutState().getHeight()).isEqualTo(100);

  assertThat(mLithoStatsRule.getComponentCalculateLayoutCount())
      .describedAs(
          "We expect one initial layout, the async layout (thrown away), and a final layout after measure.")
      .isEqualTo(3);

  newHandlerThread.quit();
}
 
Example 11
Source File: ComponentTreeTest.java    From litho with Apache License 2.0 4 votes vote down vote up
@Test
public void
    testSetRootAsyncWithIncompatibleMeasureButCompatibleMeasureForExistingLayoutDuringLayout()
        throws InterruptedException {
  Component oldComponent =
      SimpleMountSpecTester.create(mContext).widthPx(100).heightPx(100).color(1234).build();
  ComponentTree componentTree = ComponentTree.create(mContext, oldComponent).build();
  componentTree.setLithoView(new LithoView(mContext));

  int widthSpec1 = SizeSpec.makeSizeSpec(1000, SizeSpec.EXACTLY);
  int heightSpec1 = SizeSpec.makeSizeSpec(1000, SizeSpec.AT_MOST);
  int widthSpec2 = SizeSpec.makeSizeSpec(1000, SizeSpec.EXACTLY);
  int heightSpec2 = SizeSpec.makeSizeSpec(500, SizeSpec.AT_MOST);

  componentTree.attach();
  componentTree.measure(widthSpec1, heightSpec1, new int[2], false);

  final TestDrawableComponent.BlockInPrepareComponentListener blockInPrepare =
      new TestDrawableComponent.BlockInPrepareComponentListener();
  blockInPrepare.setDoNotBlockOnThisThread();
  TestDrawableComponent newComponent =
      TestDrawableComponent.create(mContext).flexGrow(1).color(1234).build();
  newComponent.setTestComponentListener(blockInPrepare);

  componentTree.setRootAsync(newComponent);

  final TimeOutSemaphore asyncLayoutFinish =
      runOnBackgroundThread(
          new Runnable() {
            @Override
            public void run() {
              mLayoutThreadShadowLooper.runToEndOfTasks();
            }
          });

  blockInPrepare.awaitPrepareStart();

  // At this point, the Layout thread is blocked in prepare (waiting for unblockAsyncPrepare) and
  // will have already captured the "bad" specs, but not completed its layout.

  // This is a bit of a hack: Robolectric's ShadowLegacyLooper implementation is synchronized
  // on runToEndOfTasks and post(). Since we are simulating being in the middle of calculating a
  // layout, this means that we can't post() to the same Handler (as we will try to do in measure)
  // The "fix" here is to update the layout thread to a new handler/looper that can be controlled
  // separately.
  HandlerThread newHandlerThread = createAndStartNewHandlerThread();
  componentTree.updateLayoutThreadHandler(
      new LithoHandler.DefaultLithoHandler(newHandlerThread.getLooper()));
  ShadowLooper newThreadLooper = Shadows.shadowOf(newHandlerThread.getLooper());

  assertThat(componentTree.hasCompatibleLayout(widthSpec2, heightSpec2))
      .describedAs("Asserting test setup, second set of specs should be compatible already.")
      .isTrue();
  componentTree.measure(widthSpec2, heightSpec2, new int[2], false);

  assertThat(componentTree.getRoot()).isEqualTo(newComponent);
  assertThat(componentTree.hasCompatibleLayout(widthSpec2, heightSpec2)).isTrue();
  assertThat(componentTree.getMainThreadLayoutState().isForComponentId(newComponent.getId()))
      .isTrue()
      .withFailMessage(
          "The main thread should calculate a new layout synchronously because the async layout will not have compatible size specs");
  assertThat(componentTree.getMainThreadLayoutState().getHeight()).isEqualTo(500);

  // Finally, let the async layout finish and make sure it doesn't replace the layout from measure

  blockInPrepare.allowPrepareToComplete();
  asyncLayoutFinish.acquire();

  newComponent.setTestComponentListener(null);
  newThreadLooper.runToEndOfTasks();
  mLayoutThreadShadowLooper.runToEndOfTasks();
  ShadowLooper.runUiThreadTasks();

  assertThat(componentTree.getRoot()).isEqualTo(newComponent);
  assertThat(componentTree.hasCompatibleLayout(widthSpec2, heightSpec2)).isTrue();
  assertThat(componentTree.getMainThreadLayoutState().isForComponentId(newComponent.getId()))
      .isTrue();
  assertThat(componentTree.getMainThreadLayoutState().getHeight()).isEqualTo(500);

  assertThat(mLithoStatsRule.getComponentCalculateLayoutCount())
      .describedAs(
          "We expect one initial layout, the async layout (thrown away), and a final layout from measure.")
      .isEqualTo(3);

  newHandlerThread.quit();
}
 
Example 12
Source File: ComponentTreeTest.java    From litho with Apache License 2.0 4 votes vote down vote up
@Test
public void testMeasureWithUpdateStateThatCompletesFirst() throws InterruptedException {
  SimpleStateUpdateEmulatorSpec.Caller caller = new SimpleStateUpdateEmulatorSpec.Caller();
  TestDrawableComponent blockingComponent =
      TestDrawableComponent.create(mContext).flexGrow(1).color(1234).build();
  Component rootComponent =
      Column.create(mContext)
          .child(SimpleStateUpdateEmulator.create(mContext).caller(caller).prefix("Counter: "))
          .child(blockingComponent)
          .minHeightPx(100)
          .build();
  LithoView lithoView = new LithoView(mContext);
  ComponentTree componentTree =
      ComponentTree.create(mContext, rootComponent).isReconciliationEnabled(true).build();
  lithoView.setComponentTree(componentTree);

  int widthSpec1 = SizeSpec.makeSizeSpec(1000, SizeSpec.AT_MOST);
  int heightSpec1 = SizeSpec.makeSizeSpec(500, SizeSpec.EXACTLY);
  int widthSpec2 = SizeSpec.makeSizeSpec(1000, SizeSpec.AT_MOST);
  int heightSpec2 = SizeSpec.makeSizeSpec(1000, SizeSpec.EXACTLY);

  componentTree.attach();
  lithoView.measure(widthSpec1, heightSpec1);

  final TestDrawableComponent.BlockInPrepareComponentListener blockInPrepare =
      new TestDrawableComponent.BlockInPrepareComponentListener();
  blockingComponent.setTestComponentListener(blockInPrepare);

  // This is necessary because we end up posting the synchronous state update to the layout looper
  componentTree
      .getLayoutThreadHandler()
      .post(
          new Runnable() {
            @Override
            public void run() {
              blockInPrepare.setDoNotBlockOnThisThread();
            }
          },
          "test");

  final TimeOutSemaphore asyncStateUpdate =
      runOnBackgroundThread(
          new Runnable() {
            @Override
            public void run() {
              blockInPrepare.awaitPrepareStart();
              blockInPrepare.setDoNotBlockOnThisThread();
              caller.increment();
              mBackgroundLayoutLooperRule.runToEndOfTasksSync();
              blockInPrepare.allowPrepareToComplete();
            }
          });

  assertThat(componentTree.hasCompatibleLayout(widthSpec2, heightSpec2))
      .describedAs("Asserting test setup, second set of specs should not be compatible.")
      .isFalse();
  lithoView.measure(widthSpec2, heightSpec2);
  asyncStateUpdate.acquire();
  ShadowLooper.runUiThreadTasks();

  lithoView.layout(0, 0, lithoView.getMeasuredWidth(), lithoView.getMeasuredHeight());

  // We now want to assert that we are the right size and also the state update was applied.
  assertThat(componentTree.hasCompatibleLayout(widthSpec2, heightSpec2)).isTrue();
  assertThat(componentTree.getMainThreadLayoutState().getHeight()).isEqualTo(1000);
  assertThat(lithoView.getHeight()).isEqualTo(1000);

  ViewAssertions.assertThat(lithoView)
      .matches(
          ViewMatchNode.forType(LithoView.class)
              .prop(
                  "drawables",
                  MatchNode.list(
                      MatchNode.forType(TextDrawable.class).prop("text", "Counter: 2"),
                      MatchNode.forType(ColorDrawable.class))));

  assertThat(mLithoStatsRule.getComponentCalculateLayoutCount())
      .describedAs(
          "We expect one layout during setup, one from measure that will be thrown away and one from the state update.")
      .isEqualTo(3);
}
 
Example 13
Source File: ComponentTreeTest.java    From litho with Apache License 2.0 4 votes vote down vote up
@Test
public void testUpdateStateWithMeasureThatStartsBeforeUpdateStateCompletes()
    throws InterruptedException {
  SimpleStateUpdateEmulatorSpec.Caller caller = new SimpleStateUpdateEmulatorSpec.Caller();
  TestDrawableComponent blockingComponent =
      TestDrawableComponent.create(mContext).flexGrow(1).color(1234).build();
  Component rootComponent =
      Column.create(mContext)
          .child(SimpleStateUpdateEmulator.create(mContext).caller(caller).prefix("Counter: "))
          .child(blockingComponent)
          .minHeightPx(100)
          .build();
  LithoView lithoView = new LithoView(mContext);

  // We need to turn reconciliation off so that the BlockInPrepare always executes
  ComponentTree componentTree =
      ComponentTree.create(mContext, rootComponent).isReconciliationEnabled(false).build();
  lithoView.setComponentTree(componentTree);

  int widthSpec1 = SizeSpec.makeSizeSpec(1000, SizeSpec.AT_MOST);
  int heightSpec1 = SizeSpec.makeSizeSpec(500, SizeSpec.EXACTLY);
  int widthSpec2 = SizeSpec.makeSizeSpec(1000, SizeSpec.AT_MOST);
  int heightSpec2 = SizeSpec.makeSizeSpec(1000, SizeSpec.EXACTLY);

  componentTree.attach();
  lithoView.measure(widthSpec1, heightSpec1);

  final TestDrawableComponent.BlockInPrepareComponentListener blockInPrepare =
      new TestDrawableComponent.BlockInPrepareComponentListener();
  blockInPrepare.setDoNotBlockOnThisThread();
  blockingComponent.setTestComponentListener(blockInPrepare);

  caller.incrementAsync();
  final TimeOutSemaphore asyncStateUpdate = mBackgroundLayoutLooperRule.runToEndOfTasksAsync();

  blockInPrepare.awaitPrepareStart();
  assertThat(componentTree.hasCompatibleLayout(widthSpec2, heightSpec2))
      .describedAs("Asserting test setup, second set of specs should not be compatible.")
      .isFalse();
  lithoView.measure(widthSpec2, heightSpec2);
  blockInPrepare.allowPrepareToComplete();

  asyncStateUpdate.acquire();
  ShadowLooper.runUiThreadTasks();

  lithoView.layout(0, 0, lithoView.getMeasuredWidth(), lithoView.getMeasuredHeight());

  // We now want to assert that we are the right size and also the state update was applied.
  assertThat(componentTree.hasCompatibleLayout(widthSpec2, heightSpec2)).isTrue();
  assertThat(componentTree.getMainThreadLayoutState().getHeight()).isEqualTo(1000);
  assertThat(lithoView.getHeight()).isEqualTo(1000);

  ViewAssertions.assertThat(lithoView)
      .matches(
          ViewMatchNode.forType(LithoView.class)
              .prop(
                  "drawables",
                  MatchNode.list(
                      MatchNode.forType(TextDrawable.class).prop("text", "Counter: 2"),
                      MatchNode.forType(ColorDrawable.class))));

  assertThat(mLithoStatsRule.getComponentCalculateLayoutCount())
      .describedAs(
          "We expect one layout during setup, one from measure and one from the state update that will be thrown away.")
      .isEqualTo(3);
}
 
Example 14
Source File: ComponentTreeTest.java    From litho with Apache License 2.0 4 votes vote down vote up
@Test
public void testSetRootAndSetSizeSpecInParallelProduceCorrectResult()
    throws InterruptedException {
  Component oldComponent =
      SimpleMountSpecTester.create(mContext).widthPx(100).heightPx(100).color(1234).build();
  ComponentTree componentTree = ComponentTree.create(mContext, oldComponent).build();
  componentTree.setLithoView(new LithoView(mContext));

  int widthSpec1 = SizeSpec.makeSizeSpec(1000, SizeSpec.EXACTLY);
  int heightSpec1 = SizeSpec.makeSizeSpec(1000, SizeSpec.AT_MOST);
  int widthSpec2 = SizeSpec.makeSizeSpec(1000, SizeSpec.EXACTLY);
  int heightSpec2 = SizeSpec.makeSizeSpec(500, SizeSpec.AT_MOST);

  componentTree.attach();
  componentTree.measure(widthSpec1, heightSpec1, new int[2], false);

  // This new component will produce a layout compatible with the first specs but not the second
  // specs.
  TestDrawableComponent newComponent =
      TestDrawableComponent.create(mContext).flexGrow(1).color(1234).build();
  TestDrawableComponent.BlockInPrepareComponentListener blockInPrepare =
      new TestDrawableComponent.BlockInPrepareComponentListener();
  blockInPrepare.setDoNotBlockOnThisThread();
  newComponent.setTestComponentListener(blockInPrepare);

  componentTree.setRootAsync(newComponent);

  final TimeOutSemaphore asyncLayout = mBackgroundLayoutLooperRule.runToEndOfTasksAsync();
  blockInPrepare.awaitPrepareStart();

  assertThat(componentTree.hasCompatibleLayout(widthSpec2, heightSpec2))
      .describedAs("Asserting test setup, second set of specs should be compatible.")
      .isTrue();
  componentTree.setSizeSpec(widthSpec2, heightSpec2);

  blockInPrepare.allowPrepareToComplete();
  asyncLayout.acquire();
  ShadowLooper.runUiThreadTasks();

  assertThat(componentTree.getRoot()).isEqualTo(newComponent);
  assertThat(componentTree.hasCompatibleLayout(widthSpec2, heightSpec2)).isTrue();
  assertThat(componentTree.getMainThreadLayoutState().isForComponentId(newComponent.getId()))
      .isTrue();
  assertThat(componentTree.getMainThreadLayoutState().getHeight()).isEqualTo(500);

  assertThat(mLithoStatsRule.getComponentCalculateLayoutCount())
      .describedAs(
          "We expect one initial layout, the async layout (thrown away), and a layout from setSizeSpec.")
      .isEqualTo(3);
}