fix transition tests race condition
Transition tests are reliant upon layout running, which sets various
values that are then checked The mechanism used to ensure that layout
had run (Instrumentation.waitForIdleSync()) doesn't do what we need, and
it is possible for the idle condition to succeed even while the queue still
has the pending layout request on it.
The fix is to introduce a new method in WidgetTestUtils to do what we need
here: wait on an actual layout to run.
Issue #31249295 CTS: ChangeBoundsTest failures
Change-Id: Iec7a5735a9d86fbe98c58acd1f32e325e9fdcab7
diff --git a/libs/deviceutil/src/android/cts/util/WidgetTestUtils.java b/libs/deviceutil/src/android/cts/util/WidgetTestUtils.java
index a3bf821..5c2c0e6 100644
--- a/libs/deviceutil/src/android/cts/util/WidgetTestUtils.java
+++ b/libs/deviceutil/src/android/cts/util/WidgetTestUtils.java
@@ -209,21 +209,25 @@
* @param runner the runnable to run on the main thread, or {@code null} to
* simply force invalidation and a draw pass
*/
- public static void runOnMainAndDrawSync(@NonNull ActivityTestRule activityTestRule,
+ public static void runOnMainAndDrawSync(@NonNull final ActivityTestRule activityTestRule,
@NonNull final View view, @Nullable final Runnable runner) throws Throwable {
final CountDownLatch latch = new CountDownLatch(1);
activityTestRule.runOnUiThread(() -> {
- final ViewTreeObserver observer = view.getViewTreeObserver();
final OnDrawListener listener = new OnDrawListener() {
@Override
public void onDraw() {
- observer.removeOnDrawListener(this);
- view.post(() -> latch.countDown());
+ // posting so that the sync happens after the draw that's about to happen
+ view.post(() -> {
+ activityTestRule.getActivity().getWindow().getDecorView().
+ getViewTreeObserver().removeOnDrawListener(this);
+ latch.countDown();
+ });
}
};
- observer.addOnDrawListener(listener);
+ activityTestRule.getActivity().getWindow().getDecorView().
+ getViewTreeObserver().addOnDrawListener(listener);
if (runner != null) {
runner.run();
@@ -240,4 +244,50 @@
}
}
+ /**
+ * Runs the specified Runnable on the main thread and ensures that the activity's view tree is
+ * laid out before returning.
+ *
+ * @param activityTestRule the activity test rule used to run the test
+ * @param runner the runnable to run on the main thread. {@code null} is
+ * allowed, and simply means that there no runnable is required.
+ * @param forceLayout true if there should be an explicit call to requestLayout(),
+ * false otherwise
+ */
+ public static void runOnMainAndLayoutSync(@NonNull final ActivityTestRule activityTestRule,
+ @Nullable final Runnable runner, boolean forceLayout)
+ throws Throwable {
+ final CountDownLatch latch = new CountDownLatch(1);
+
+ activityTestRule.runOnUiThread(() -> {
+ final OnGlobalLayoutListener listener = new ViewTreeObserver.OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ activityTestRule.getActivity().getWindow().getDecorView().
+ getViewTreeObserver().removeOnGlobalLayoutListener(this);
+ // countdown immediately since the layout we were waiting on has happened
+ latch.countDown();
+ }
+ };
+
+ activityTestRule.getActivity().getWindow().getDecorView().
+ getViewTreeObserver().addOnGlobalLayoutListener(listener);
+
+ if (runner != null) {
+ runner.run();
+ }
+
+ if (forceLayout) {
+ activityTestRule.getActivity().getWindow().getDecorView().requestLayout();
+ }
+ });
+
+ try {
+ Assert.assertTrue("Expected layout pass within 5 seconds",
+ latch.await(5, TimeUnit.SECONDS));
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
}
diff --git a/tests/tests/transition/src/android/transition/cts/BaseTransitionTest.java b/tests/tests/transition/src/android/transition/cts/BaseTransitionTest.java
index c5fb3eb..6b7f5ae 100644
--- a/tests/tests/transition/src/android/transition/cts/BaseTransitionTest.java
+++ b/tests/tests/transition/src/android/transition/cts/BaseTransitionTest.java
@@ -25,6 +25,7 @@
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.app.Instrumentation;
+import android.cts.util.WidgetTestUtils;
import android.support.test.InstrumentationRegistry;
import android.support.test.rule.ActivityTestRule;
import android.transition.Scene;
@@ -128,8 +129,7 @@
}
protected void enterScene(final Scene scene) throws Throwable {
- mActivityRule.runOnUiThread(scene::enter);
- mInstrumentation.waitForIdleSync();
+ WidgetTestUtils.runOnMainAndLayoutSync(mActivityRule, scene::enter, false);
}
protected void exitScene(final Scene scene) throws Throwable {