[go: nahoru, domu]

Introduce frames control into Compose test API.

Several changes:
- Defragmented TestCase from benchmark into ComposeTestCase and
  AndroidTestCaseRunner and moved them to androidx.ui.testing. This
  enabled to introduce time controlled tests as part of regular compose
  testing API. We can also keep building on this to enable animations
  testing.
- Made hasPendingChanges to work again as part of time controlled tests.
  This will be particulary useful to bring in the regular testing APIs
  such as doClick, findByTag and others.
- Less boiler-plate when writing benchmarks thanks to
  ComposeBenchmarkRule.

Test: Added
Bug: b/122349846

Change-Id: Ie91d2854af48eb6799a1dc1f84fb0e4a1d5f8d02
diff --git a/compose/compose-runtime/src/androidAndroidTest/kotlin/androidx/compose/ViewComposerTests.kt b/compose/compose-runtime/src/androidAndroidTest/kotlin/androidx/compose/ViewComposerTests.kt
index faa8f263..d6b0494 100644
--- a/compose/compose-runtime/src/androidAndroidTest/kotlin/androidx/compose/ViewComposerTests.kt
+++ b/compose/compose-runtime/src/androidAndroidTest/kotlin/androidx/compose/ViewComposerTests.kt
@@ -892,6 +892,8 @@
         fun then(block: (activity: Activity) -> Unit): ActiveTest {
             val composition = ViewComposition(
                 ViewComposer(activity.root, activity, object : Recomposer() {
+                    override fun recomposeSync() {}
+
                     override fun scheduleChangesDispatch() {}
 
                     override fun hasPendingChanges(): Boolean = false
diff --git a/compose/compose-runtime/src/androidMain/kotlin/androidx/compose/Recomposer.kt b/compose/compose-runtime/src/androidMain/kotlin/androidx/compose/Recomposer.kt
index 6f92528..93ca713 100644
--- a/compose/compose-runtime/src/androidMain/kotlin/androidx/compose/Recomposer.kt
+++ b/compose/compose-runtime/src/androidMain/kotlin/androidx/compose/Recomposer.kt
@@ -35,6 +35,13 @@
     }
 
     override fun hasPendingChanges(): Boolean = frameScheduled
+
+    override fun recomposeSync() {
+        if (frameScheduled) {
+            Choreographer.getInstance().removeFrameCallback(frameCallback)
+            frameCallback.doFrame(0)
+        }
+    }
 }
 
 internal actual fun createRecomposer(): Recomposer {
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Recomposer.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Recomposer.kt
index 810198d..7a000b00f 100644
--- a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Recomposer.kt
+++ b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Recomposer.kt
@@ -27,7 +27,11 @@
          */
         fun hasPendingChanges() = current().hasPendingChanges()
 
-        internal fun current(): Recomposer {
+        /**
+         * Retrieves [Recomposer] for the current thread. Needs to be the main thread.
+         */
+        @TestOnly
+        fun current(): Recomposer {
             require(isMainThread()) {
                 "No Recomposer for this Thread"
             }
@@ -58,13 +62,14 @@
                     composer.endGroup()
                     composer.endRoot()
                 }
-                composer.applyChanges()
-                if (!composerWasComposing) {
-                    FrameManager.nextFrame()
-                }
             } finally {
                 composer.isComposing = composerWasComposing
             }
+            // TODO(b/143755743)
+            composer.applyChanges()
+            if (!composerWasComposing) {
+                FrameManager.nextFrame()
+            }
         }
     }
 
@@ -102,6 +107,14 @@
         cs.forEach { performRecompose(it) }
         FrameManager.nextFrame()
     }
+
+    /**
+     * Used to recompose changes from [scheduleChangesDispatch] immediately without waiting.
+     *
+     * This is supposed to be used in tests only.
+     */
+    @TestOnly
+    abstract fun recomposeSync()
 }
 
 internal expect fun createRecomposer(): Recomposer
\ No newline at end of file
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/frames/Frames.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/frames/Frames.kt
index b4ce4f0..c9b2a41 100644
--- a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/frames/Frames.kt
+++ b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/frames/Frames.kt
@@ -145,6 +145,11 @@
             readObservers -= readObserver
         }
     }
+
+    /**
+     * Whether there are any pending changes in this frame.
+     */
+    fun hasPendingChanges(): Boolean = (modified?.size ?: 0) > 0
 }
 
 private fun validateNotInFrame() {
diff --git a/compose/compose-runtime/src/commonTest/kotlin/androidx/compose/mock/ViewComposer.kt b/compose/compose-runtime/src/commonTest/kotlin/androidx/compose/mock/ViewComposer.kt
index 8e49659..b41cfd5 100644
--- a/compose/compose-runtime/src/commonTest/kotlin/androidx/compose/mock/ViewComposer.kt
+++ b/compose/compose-runtime/src/commonTest/kotlin/androidx/compose/mock/ViewComposer.kt
@@ -70,8 +70,9 @@
 ) : Composer<View>(
     SlotTable(),
     Applier(root, ViewApplierAdapter), object : Recomposer() {
-        override fun scheduleChangesDispatch() {
-        }
+        override fun recomposeSync() {}
+
+        override fun scheduleChangesDispatch() {}
 
         override fun hasPendingChanges(): Boolean = false
     }) {
diff --git a/ui/integration-tests/benchmark/build.gradle b/ui/integration-tests/benchmark/build.gradle
index 3160fcd..0c0f31d 100644
--- a/ui/integration-tests/benchmark/build.gradle
+++ b/ui/integration-tests/benchmark/build.gradle
@@ -31,18 +31,25 @@
 
 dependencies {
     kotlinPlugin project(path: ":compose:compose-compiler", configuration: "embeddablePlugin")
+
     implementation project(":benchmark:benchmark-junit4")
     implementation project(":compose:compose-runtime")
     implementation project(":ui:integration-tests:test")
+    implementation project(":ui:ui-test")
     implementation(KOTLIN_STDLIB)
+    implementation(KOTLIN_REFLECT)
+    implementation(ANDROIDX_TEST_RULES)
     implementation(JUNIT)
+
     androidTestImplementation project(":ui:ui-core")
     androidTestImplementation project(":ui:ui-framework")
     androidTestImplementation project(":ui:ui-layout")
     androidTestImplementation project(":ui:ui-material")
     androidTestImplementation project(":ui:ui-platform")
     androidTestImplementation project(":ui:ui-test")
+    androidTestImplementation project(":compose:compose-runtime")
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
+    androidTestImplementation(KOTLIN_TEST_COMMON)
     androidTestImplementation(ANDROIDX_TEST_RULES)
     androidTestImplementation(TRUTH)
 }
diff --git a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/CheckboxesInRowsBenchmark.kt b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/CheckboxesInRowsBenchmark.kt
index 58e7938..ee471bd 100644
--- a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/CheckboxesInRowsBenchmark.kt
+++ b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/CheckboxesInRowsBenchmark.kt
@@ -16,22 +16,19 @@
 
 package androidx.ui.benchmark.test
 
-import android.app.Activity
-import androidx.benchmark.junit4.BenchmarkRule
 import androidx.test.filters.LargeTest
-import androidx.test.rule.ActivityTestRule
-import androidx.ui.benchmark.measureDrawPerf
-import androidx.ui.benchmark.measureFirstCompose
-import androidx.ui.benchmark.measureFirstDraw
-import androidx.ui.benchmark.measureFirstLayout
-import androidx.ui.benchmark.measureFirstMeasure
-import androidx.ui.benchmark.measureLayoutPerf
-import androidx.ui.benchmark.toggleStateMeasureDraw
-import androidx.ui.benchmark.toggleStateMeasureLayout
-import androidx.ui.benchmark.toggleStateMeasureMeasure
-import androidx.ui.benchmark.toggleStateMeasureRecompose
+import androidx.ui.benchmark.ComposeBenchmarkRule
+import androidx.ui.benchmark.benchmarkDrawPerf
+import androidx.ui.benchmark.benchmarkFirstCompose
+import androidx.ui.benchmark.benchmarkFirstDraw
+import androidx.ui.benchmark.benchmarkFirstLayout
+import androidx.ui.benchmark.benchmarkFirstMeasure
+import androidx.ui.benchmark.benchmarkLayoutPerf
+import androidx.ui.benchmark.toggleStateBenchmarkDraw
+import androidx.ui.benchmark.toggleStateBenchmarkLayout
+import androidx.ui.benchmark.toggleStateBenchmarkMeasure
+import androidx.ui.benchmark.toggleStateBenchmarkRecompose
 import androidx.ui.test.cases.CheckboxesInRowsTestCase
-import androidx.ui.test.DisableTransitions
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -51,73 +48,55 @@
     }
 
     @get:Rule
-    val benchmarkRule = BenchmarkRule()
-
-    @get:Rule
-    val activityRule = ActivityTestRule(Activity::class.java)
-
-    @get:Rule
-    val disableAnimationRule = DisableTransitions()
-
-    private val activity: Activity get() = activityRule.activity
+    val benchmarkRule = ComposeBenchmarkRule()
 
     @Test
     fun first_compose() {
-        benchmarkRule.measureFirstCompose(activity,
-            CheckboxesInRowsTestCase(activity, numberOfCheckboxes))
+        benchmarkRule.benchmarkFirstCompose(CheckboxesInRowsTestCase(numberOfCheckboxes))
     }
 
     @Test
     fun first_measure() {
-        benchmarkRule.measureFirstMeasure(activity,
-            CheckboxesInRowsTestCase(activity, numberOfCheckboxes))
+        benchmarkRule.benchmarkFirstMeasure(CheckboxesInRowsTestCase(numberOfCheckboxes))
     }
 
     @Test
     fun first_layout() {
-        benchmarkRule.measureFirstLayout(activity,
-            CheckboxesInRowsTestCase(activity, numberOfCheckboxes))
+        benchmarkRule.benchmarkFirstLayout(CheckboxesInRowsTestCase(numberOfCheckboxes))
     }
 
     @Test
     fun first_draw() {
-        benchmarkRule.measureFirstDraw(activity,
-            CheckboxesInRowsTestCase(activity, numberOfCheckboxes))
+        benchmarkRule.benchmarkFirstDraw(CheckboxesInRowsTestCase(numberOfCheckboxes))
     }
 
     @Test
     fun toggleCheckbox_recompose() {
-        benchmarkRule.toggleStateMeasureRecompose(activity,
-            CheckboxesInRowsTestCase(activity, numberOfCheckboxes))
+        benchmarkRule.toggleStateBenchmarkRecompose(CheckboxesInRowsTestCase(numberOfCheckboxes))
     }
 
     @Test
     fun toggleCheckbox_measure() {
-        benchmarkRule.toggleStateMeasureMeasure(activity,
-            CheckboxesInRowsTestCase(activity, numberOfCheckboxes))
+        benchmarkRule.toggleStateBenchmarkMeasure(CheckboxesInRowsTestCase(numberOfCheckboxes))
     }
 
     @Test
     fun toggleCheckbox_layout() {
-        benchmarkRule.toggleStateMeasureLayout(activity,
-            CheckboxesInRowsTestCase(activity, numberOfCheckboxes))
+        benchmarkRule.toggleStateBenchmarkLayout(CheckboxesInRowsTestCase(numberOfCheckboxes))
     }
 
     @Test
     fun toggleCheckbox_draw() {
-        benchmarkRule.toggleStateMeasureDraw(activity,
-            CheckboxesInRowsTestCase(activity, numberOfCheckboxes))
+        benchmarkRule.toggleStateBenchmarkDraw(CheckboxesInRowsTestCase(numberOfCheckboxes))
     }
 
     @Test
     fun layout() {
-        benchmarkRule.measureLayoutPerf(activity,
-            CheckboxesInRowsTestCase(activity, numberOfCheckboxes))
+        benchmarkRule.benchmarkLayoutPerf(CheckboxesInRowsTestCase(numberOfCheckboxes))
     }
 
     @Test
     fun draw() {
-        benchmarkRule.measureDrawPerf(activity,
-            CheckboxesInRowsTestCase(activity, numberOfCheckboxes))
+        benchmarkRule.benchmarkDrawPerf(CheckboxesInRowsTestCase(numberOfCheckboxes))
     }
 }
\ No newline at end of file
diff --git a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/NestedScrollerBenchmark.kt b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/NestedScrollerBenchmark.kt
index 472ff08..b758707 100644
--- a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/NestedScrollerBenchmark.kt
+++ b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/NestedScrollerBenchmark.kt
@@ -16,20 +16,17 @@
 
 package androidx.ui.benchmark.test
 
-import android.app.Activity
-import androidx.benchmark.junit4.BenchmarkRule
 import androidx.test.filters.LargeTest
-import androidx.test.rule.ActivityTestRule
-import androidx.ui.benchmark.measureDrawPerf
-import androidx.ui.benchmark.measureFirstCompose
-import androidx.ui.benchmark.measureFirstDraw
-import androidx.ui.benchmark.measureFirstLayout
-import androidx.ui.benchmark.measureFirstMeasure
-import androidx.ui.benchmark.measureLayoutPerf
-import androidx.ui.benchmark.toggleStateMeasureDraw
-import androidx.ui.benchmark.toggleStateMeasureLayout
-import androidx.ui.benchmark.toggleStateMeasureMeasure
-import androidx.ui.test.DisableTransitions
+import androidx.ui.benchmark.ComposeBenchmarkRule
+import androidx.ui.benchmark.benchmarkDrawPerf
+import androidx.ui.benchmark.benchmarkFirstCompose
+import androidx.ui.benchmark.benchmarkFirstDraw
+import androidx.ui.benchmark.benchmarkFirstLayout
+import androidx.ui.benchmark.benchmarkFirstMeasure
+import androidx.ui.benchmark.benchmarkLayoutPerf
+import androidx.ui.benchmark.toggleStateBenchmarkDraw
+import androidx.ui.benchmark.toggleStateBenchmarkLayout
+import androidx.ui.benchmark.toggleStateBenchmarkMeasure
 import androidx.ui.test.cases.NestedScrollerTestCase
 import org.junit.Rule
 import org.junit.Test
@@ -40,61 +37,53 @@
 @RunWith(JUnit4::class)
 class NestedScrollerBenchmark {
     @get:Rule
-    val benchmarkRule = BenchmarkRule()
-
-    @get:Rule
-    val activityRule = ActivityTestRule(Activity::class.java)
-
-    @get:Rule
-    val disableAnimationRule = DisableTransitions()
-
-    private val activity: Activity get() = activityRule.activity
+    val benchmarkRule = ComposeBenchmarkRule()
 
     @Test
     fun first_compose() {
-        benchmarkRule.measureFirstCompose(activity, NestedScrollerTestCase(activity))
+        benchmarkRule.benchmarkFirstCompose(NestedScrollerTestCase())
     }
 
     @Test
     fun first_measure() {
-        benchmarkRule.measureFirstMeasure(activity, NestedScrollerTestCase(activity))
+        benchmarkRule.benchmarkFirstMeasure(NestedScrollerTestCase())
     }
 
     @Test
     fun first_layout() {
-        benchmarkRule.measureFirstLayout(activity, NestedScrollerTestCase(activity))
+        benchmarkRule.benchmarkFirstLayout(NestedScrollerTestCase())
     }
 
     @Test
     fun first_draw() {
-        benchmarkRule.measureFirstDraw(activity, NestedScrollerTestCase(activity))
+        benchmarkRule.benchmarkFirstDraw(NestedScrollerTestCase())
     }
 
     @Test
     fun changeScroll_measure() {
-        benchmarkRule.toggleStateMeasureMeasure(activity, NestedScrollerTestCase(activity),
-            toggleCausesRecompose = false, firstDrawCausesRecompose = true)
+        benchmarkRule.toggleStateBenchmarkMeasure(NestedScrollerTestCase(),
+            toggleCausesRecompose = false)
     }
 
     @Test
     fun changeScroll_layout() {
-        benchmarkRule.toggleStateMeasureLayout(activity, NestedScrollerTestCase(activity),
-            toggleCausesRecompose = false, firstDrawCausesRecompose = true)
+        benchmarkRule.toggleStateBenchmarkLayout(NestedScrollerTestCase(),
+            toggleCausesRecompose = false)
     }
 
     @Test
     fun changeScroll_draw() {
-        benchmarkRule.toggleStateMeasureDraw(activity, NestedScrollerTestCase(activity),
-            toggleCausesRecompose = false, firstDrawCausesRecompose = true)
+        benchmarkRule.toggleStateBenchmarkDraw(NestedScrollerTestCase(),
+            toggleCausesRecompose = false)
     }
 
     @Test
     fun layout() {
-        benchmarkRule.measureLayoutPerf(activity, NestedScrollerTestCase(activity))
+        benchmarkRule.benchmarkLayoutPerf(NestedScrollerTestCase())
     }
 
     @Test
     fun draw() {
-        benchmarkRule.measureDrawPerf(activity, NestedScrollerTestCase(activity))
+        benchmarkRule.benchmarkDrawPerf(NestedScrollerTestCase())
     }
 }
diff --git a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/RectsInColumnBenchmark.kt b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/RectsInColumnBenchmark.kt
index 85a5559..01aecd6 100644
--- a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/RectsInColumnBenchmark.kt
+++ b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/RectsInColumnBenchmark.kt
@@ -16,21 +16,18 @@
 
 package androidx.ui.benchmark.test
 
-import android.app.Activity
-import androidx.benchmark.junit4.BenchmarkRule
 import androidx.test.filters.LargeTest
-import androidx.test.rule.ActivityTestRule
-import androidx.ui.benchmark.measureDrawPerf
-import androidx.ui.benchmark.measureFirstCompose
-import androidx.ui.benchmark.measureFirstDraw
-import androidx.ui.benchmark.measureFirstLayout
-import androidx.ui.benchmark.measureFirstMeasure
-import androidx.ui.benchmark.measureLayoutPerf
-import androidx.ui.benchmark.toggleStateMeasureDraw
-import androidx.ui.benchmark.toggleStateMeasureLayout
-import androidx.ui.benchmark.toggleStateMeasureMeasure
-import androidx.ui.benchmark.toggleStateMeasureRecompose
-import androidx.ui.test.DisableTransitions
+import androidx.ui.benchmark.ComposeBenchmarkRule
+import androidx.ui.benchmark.benchmarkDrawPerf
+import androidx.ui.benchmark.benchmarkFirstCompose
+import androidx.ui.benchmark.benchmarkFirstDraw
+import androidx.ui.benchmark.benchmarkFirstLayout
+import androidx.ui.benchmark.benchmarkFirstMeasure
+import androidx.ui.benchmark.benchmarkLayoutPerf
+import androidx.ui.benchmark.toggleStateBenchmarkDraw
+import androidx.ui.benchmark.toggleStateBenchmarkLayout
+import androidx.ui.benchmark.toggleStateBenchmarkMeasure
+import androidx.ui.benchmark.toggleStateBenchmarkRecompose
 import androidx.ui.test.cases.RectsInColumnTestCase
 import org.junit.Rule
 import org.junit.Test
@@ -51,73 +48,55 @@
     }
 
     @get:Rule
-    val benchmarkRule = BenchmarkRule()
-
-    @get:Rule
-    val activityRule = ActivityTestRule(Activity::class.java)
-
-    @get:Rule
-    val disableAnimationRule = DisableTransitions()
-
-    private val activity: Activity get() = activityRule.activity
+    val benchmarkRule = ComposeBenchmarkRule()
 
     @Test
     fun first_compose() {
-        benchmarkRule.measureFirstCompose(activity,
-            RectsInColumnTestCase(activity, numberOfRectangles))
+        benchmarkRule.benchmarkFirstCompose(RectsInColumnTestCase(numberOfRectangles))
     }
 
     @Test
     fun first_measure() {
-        benchmarkRule.measureFirstMeasure(activity,
-            RectsInColumnTestCase(activity, numberOfRectangles))
+        benchmarkRule.benchmarkFirstMeasure(RectsInColumnTestCase(numberOfRectangles))
     }
 
     @Test
     fun first_layout() {
-        benchmarkRule.measureFirstLayout(activity,
-            RectsInColumnTestCase(activity, numberOfRectangles))
+        benchmarkRule.benchmarkFirstLayout(RectsInColumnTestCase(numberOfRectangles))
     }
 
     @Test
     fun first_draw() {
-        benchmarkRule.measureFirstDraw(activity,
-            RectsInColumnTestCase(activity, numberOfRectangles))
+        benchmarkRule.benchmarkFirstDraw(RectsInColumnTestCase(numberOfRectangles))
     }
 
     @Test
     fun toggleRectangleColor_recompose() {
-        benchmarkRule.toggleStateMeasureRecompose(activity,
-            RectsInColumnTestCase(activity, numberOfRectangles))
+        benchmarkRule.toggleStateBenchmarkRecompose(RectsInColumnTestCase(numberOfRectangles))
     }
 
     @Test
     fun toggleRectangleColor_measure() {
-        benchmarkRule.toggleStateMeasureMeasure(activity,
-            RectsInColumnTestCase(activity, numberOfRectangles))
+        benchmarkRule.toggleStateBenchmarkMeasure(RectsInColumnTestCase(numberOfRectangles))
     }
 
     @Test
     fun toggleRectangleColor_layout() {
-        benchmarkRule.toggleStateMeasureLayout(activity,
-            RectsInColumnTestCase(activity, numberOfRectangles))
+        benchmarkRule.toggleStateBenchmarkLayout(RectsInColumnTestCase(numberOfRectangles))
     }
 
     @Test
     fun toggleRectangleColor_draw() {
-        benchmarkRule.toggleStateMeasureDraw(activity,
-            RectsInColumnTestCase(activity, numberOfRectangles))
+        benchmarkRule.toggleStateBenchmarkDraw(RectsInColumnTestCase(numberOfRectangles))
     }
 
     @Test
     fun layout() {
-        benchmarkRule.measureLayoutPerf(activity,
-            RectsInColumnTestCase(activity, numberOfRectangles))
+        benchmarkRule.benchmarkLayoutPerf(RectsInColumnTestCase(numberOfRectangles))
     }
 
     @Test
     fun draw() {
-        benchmarkRule.measureDrawPerf(activity,
-            RectsInColumnTestCase(activity, numberOfRectangles))
+        benchmarkRule.benchmarkDrawPerf(RectsInColumnTestCase(numberOfRectangles))
     }
 }
diff --git a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/RectsInColumnSharedModelBenchmark.kt b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/RectsInColumnSharedModelBenchmark.kt
index 17a739a..09fda4c 100644
--- a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/RectsInColumnSharedModelBenchmark.kt
+++ b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/RectsInColumnSharedModelBenchmark.kt
@@ -16,21 +16,18 @@
 
 package androidx.ui.benchmark.test
 
-import android.app.Activity
-import androidx.benchmark.junit4.BenchmarkRule
 import androidx.test.filters.LargeTest
-import androidx.test.rule.ActivityTestRule
-import androidx.ui.benchmark.measureDrawPerf
-import androidx.ui.benchmark.measureFirstCompose
-import androidx.ui.benchmark.measureFirstDraw
-import androidx.ui.benchmark.measureFirstLayout
-import androidx.ui.benchmark.measureFirstMeasure
-import androidx.ui.benchmark.measureLayoutPerf
-import androidx.ui.benchmark.toggleStateMeasureDraw
-import androidx.ui.benchmark.toggleStateMeasureLayout
-import androidx.ui.benchmark.toggleStateMeasureMeasure
-import androidx.ui.benchmark.toggleStateMeasureRecompose
-import androidx.ui.test.DisableTransitions
+import androidx.ui.benchmark.ComposeBenchmarkRule
+import androidx.ui.benchmark.benchmarkDrawPerf
+import androidx.ui.benchmark.benchmarkFirstCompose
+import androidx.ui.benchmark.benchmarkFirstDraw
+import androidx.ui.benchmark.benchmarkFirstLayout
+import androidx.ui.benchmark.benchmarkFirstMeasure
+import androidx.ui.benchmark.benchmarkLayoutPerf
+import androidx.ui.benchmark.toggleStateBenchmarkDraw
+import androidx.ui.benchmark.toggleStateBenchmarkLayout
+import androidx.ui.benchmark.toggleStateBenchmarkMeasure
+import androidx.ui.benchmark.toggleStateBenchmarkRecompose
 import androidx.ui.test.cases.RectsInColumnSharedModelTestCase
 import org.junit.Rule
 import org.junit.Test
@@ -51,73 +48,58 @@
     }
 
     @get:Rule
-    val benchmarkRule = BenchmarkRule()
-
-    @get:Rule
-    val activityRule = ActivityTestRule(Activity::class.java)
-
-    @get:Rule
-    val disableAnimationRule = DisableTransitions()
-
-    private val activity: Activity get() = activityRule.activity
+    val benchmarkRule = ComposeBenchmarkRule()
 
     @Test
     fun first_compose() {
-        benchmarkRule.measureFirstCompose(activity,
-            RectsInColumnSharedModelTestCase(activity, numberOfRectangles))
+        benchmarkRule.benchmarkFirstCompose(RectsInColumnSharedModelTestCase(numberOfRectangles))
     }
 
     @Test
     fun first_measure() {
-        benchmarkRule.measureFirstMeasure(activity,
-            RectsInColumnSharedModelTestCase(activity, numberOfRectangles))
+        benchmarkRule.benchmarkFirstMeasure(RectsInColumnSharedModelTestCase(numberOfRectangles))
     }
 
     @Test
     fun first_layout() {
-        benchmarkRule.measureFirstLayout(activity,
-            RectsInColumnSharedModelTestCase(activity, numberOfRectangles))
+        benchmarkRule.benchmarkFirstLayout(RectsInColumnSharedModelTestCase(numberOfRectangles))
     }
 
     @Test
     fun first_draw() {
-        benchmarkRule.measureFirstDraw(activity,
-            RectsInColumnSharedModelTestCase(activity, numberOfRectangles))
+        benchmarkRule.benchmarkFirstDraw(RectsInColumnSharedModelTestCase(numberOfRectangles))
     }
 
     @Test
     fun toggleRectangleColor_recompose() {
-        benchmarkRule.toggleStateMeasureRecompose(activity,
-            RectsInColumnSharedModelTestCase(activity, numberOfRectangles))
+        benchmarkRule.toggleStateBenchmarkRecompose(
+            RectsInColumnSharedModelTestCase(numberOfRectangles))
     }
 
     @Test
     fun toggleRectangleColor_measure() {
-        benchmarkRule.toggleStateMeasureMeasure(activity,
-            RectsInColumnSharedModelTestCase(activity, numberOfRectangles))
+        benchmarkRule.toggleStateBenchmarkMeasure(
+            RectsInColumnSharedModelTestCase(numberOfRectangles))
     }
 
     @Test
     fun toggleRectangleColor_layout() {
-        benchmarkRule.toggleStateMeasureLayout(activity,
-            RectsInColumnSharedModelTestCase(activity, numberOfRectangles))
+        benchmarkRule.toggleStateBenchmarkLayout(
+            RectsInColumnSharedModelTestCase(numberOfRectangles))
     }
 
     @Test
     fun toggleRectangleColor_draw() {
-        benchmarkRule.toggleStateMeasureDraw(activity,
-            RectsInColumnSharedModelTestCase(activity, numberOfRectangles))
+        benchmarkRule.toggleStateBenchmarkDraw(RectsInColumnSharedModelTestCase(numberOfRectangles))
     }
 
     @Test
     fun layout() {
-        benchmarkRule.measureLayoutPerf(activity,
-            RectsInColumnSharedModelTestCase(activity, numberOfRectangles))
+        benchmarkRule.benchmarkLayoutPerf(RectsInColumnSharedModelTestCase(numberOfRectangles))
     }
 
     @Test
     fun draw() {
-        benchmarkRule.measureDrawPerf(activity,
-            RectsInColumnSharedModelTestCase(activity, numberOfRectangles))
+        benchmarkRule.benchmarkDrawPerf(RectsInColumnSharedModelTestCase(numberOfRectangles))
     }
 }
diff --git a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/ScrollerBenchmark.kt b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/ScrollerBenchmark.kt
index 95f6760..8c9f70a 100644
--- a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/ScrollerBenchmark.kt
+++ b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/ScrollerBenchmark.kt
@@ -16,20 +16,17 @@
 
 package androidx.ui.benchmark.test
 
-import android.app.Activity
-import androidx.benchmark.junit4.BenchmarkRule
 import androidx.test.filters.LargeTest
-import androidx.test.rule.ActivityTestRule
-import androidx.ui.benchmark.measureDrawPerf
-import androidx.ui.benchmark.measureFirstCompose
-import androidx.ui.benchmark.measureFirstDraw
-import androidx.ui.benchmark.measureFirstLayout
-import androidx.ui.benchmark.measureFirstMeasure
-import androidx.ui.benchmark.measureLayoutPerf
-import androidx.ui.benchmark.toggleStateMeasureDraw
-import androidx.ui.benchmark.toggleStateMeasureLayout
-import androidx.ui.benchmark.toggleStateMeasureMeasure
-import androidx.ui.test.DisableTransitions
+import androidx.ui.benchmark.ComposeBenchmarkRule
+import androidx.ui.benchmark.benchmarkDrawPerf
+import androidx.ui.benchmark.benchmarkFirstCompose
+import androidx.ui.benchmark.benchmarkFirstDraw
+import androidx.ui.benchmark.benchmarkFirstLayout
+import androidx.ui.benchmark.benchmarkFirstMeasure
+import androidx.ui.benchmark.benchmarkLayoutPerf
+import androidx.ui.benchmark.toggleStateBenchmarkDraw
+import androidx.ui.benchmark.toggleStateBenchmarkLayout
+import androidx.ui.benchmark.toggleStateBenchmarkMeasure
 import androidx.ui.test.cases.ScrollerTestCase
 import org.junit.Rule
 import org.junit.Test
@@ -40,61 +37,50 @@
 @RunWith(JUnit4::class)
 class ScrollerBenchmark {
     @get:Rule
-    val benchmarkRule = BenchmarkRule()
-
-    @get:Rule
-    val activityRule = ActivityTestRule(Activity::class.java)
-
-    @get:Rule
-    val disableAnimationRule = DisableTransitions()
-
-    private val activity: Activity get() = activityRule.activity
+    val benchmarkRule = ComposeBenchmarkRule()
 
     @Test
     fun first_compose() {
-        benchmarkRule.measureFirstCompose(activity, ScrollerTestCase(activity))
+        benchmarkRule.benchmarkFirstCompose(ScrollerTestCase())
     }
 
     @Test
     fun first_measure() {
-        benchmarkRule.measureFirstMeasure(activity, ScrollerTestCase(activity))
+        benchmarkRule.benchmarkFirstMeasure(ScrollerTestCase())
     }
 
     @Test
     fun first_layout() {
-        benchmarkRule.measureFirstLayout(activity, ScrollerTestCase(activity))
+        benchmarkRule.benchmarkFirstLayout(ScrollerTestCase())
     }
 
     @Test
     fun first_draw() {
-        benchmarkRule.measureFirstDraw(activity, ScrollerTestCase(activity))
+        benchmarkRule.benchmarkFirstDraw(ScrollerTestCase())
     }
 
     @Test
     fun changeScroll_measure() {
-        benchmarkRule.toggleStateMeasureMeasure(activity, ScrollerTestCase(activity),
-            toggleCausesRecompose = false, firstDrawCausesRecompose = true)
+        benchmarkRule.toggleStateBenchmarkMeasure(ScrollerTestCase(), toggleCausesRecompose = false)
     }
 
     @Test
     fun changeScroll_layout() {
-        benchmarkRule.toggleStateMeasureLayout(activity, ScrollerTestCase(activity),
-            toggleCausesRecompose = false, firstDrawCausesRecompose = true)
+        benchmarkRule.toggleStateBenchmarkLayout(ScrollerTestCase(), toggleCausesRecompose = false)
     }
 
     @Test
     fun changeScroll_draw() {
-        benchmarkRule.toggleStateMeasureDraw(activity, ScrollerTestCase(activity),
-            toggleCausesRecompose = false, firstDrawCausesRecompose = true)
+        benchmarkRule.toggleStateBenchmarkDraw(ScrollerTestCase(), toggleCausesRecompose = false)
     }
 
     @Test
     fun layout() {
-        benchmarkRule.measureLayoutPerf(activity, ScrollerTestCase(activity))
+        benchmarkRule.benchmarkLayoutPerf(ScrollerTestCase())
     }
 
     @Test
     fun draw() {
-        benchmarkRule.measureDrawPerf(activity, ScrollerTestCase(activity))
+        benchmarkRule.benchmarkDrawPerf(ScrollerTestCase())
     }
 }
diff --git a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/SimpleRadioButtonBenchmark.kt b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/SimpleRadioButtonBenchmark.kt
index 347f2b2..e7ec3a7 100644
--- a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/SimpleRadioButtonBenchmark.kt
+++ b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/SimpleRadioButtonBenchmark.kt
@@ -16,20 +16,18 @@
 
 package androidx.ui.benchmark.test
 
-import android.app.Activity
-import androidx.benchmark.junit4.BenchmarkRule
 import androidx.test.filters.LargeTest
-import androidx.test.rule.ActivityTestRule
-import androidx.ui.benchmark.measureDrawPerf
-import androidx.ui.benchmark.measureFirstCompose
-import androidx.ui.benchmark.measureFirstDraw
-import androidx.ui.benchmark.measureFirstLayout
-import androidx.ui.benchmark.measureFirstMeasure
-import androidx.ui.benchmark.measureLayoutPerf
-import androidx.ui.benchmark.toggleStateMeasureDraw
-import androidx.ui.benchmark.toggleStateMeasureLayout
-import androidx.ui.benchmark.toggleStateMeasureMeasure
-import androidx.ui.benchmark.toggleStateMeasureRecompose
+import androidx.ui.benchmark.ComposeBenchmarkRule
+import androidx.ui.benchmark.benchmarkDrawPerf
+import androidx.ui.benchmark.benchmarkFirstCompose
+import androidx.ui.benchmark.benchmarkFirstDraw
+import androidx.ui.benchmark.benchmarkFirstLayout
+import androidx.ui.benchmark.benchmarkFirstMeasure
+import androidx.ui.benchmark.benchmarkLayoutPerf
+import androidx.ui.benchmark.toggleStateBenchmarkDraw
+import androidx.ui.benchmark.toggleStateBenchmarkLayout
+import androidx.ui.benchmark.toggleStateBenchmarkMeasure
+import androidx.ui.benchmark.toggleStateBenchmarkRecompose
 import androidx.ui.test.cases.SimpleRadioButton1TestCase
 import androidx.ui.test.cases.SimpleRadioButton2TestCase
 import androidx.ui.test.cases.SimpleRadioButton3TestCase
@@ -42,158 +40,153 @@
 @RunWith(JUnit4::class)
 class SimpleRadioButtonBenchmark {
     @get:Rule
-    val benchmarkRule = BenchmarkRule()
-
-    @get:Rule
-    val activityRule = ActivityTestRule(Activity::class.java)
-
-    private val activity: Activity get() = activityRule.activity
+    val benchmarkRule = ComposeBenchmarkRule(enableTransitions = true)
 
     @Test
     fun radio_button_1_first_compose() {
-        benchmarkRule.measureFirstCompose(activity, SimpleRadioButton1TestCase(activity))
+        benchmarkRule.benchmarkFirstCompose(SimpleRadioButton1TestCase())
     }
 
     @Test
     fun radio_button_1_first_measure() {
-        benchmarkRule.measureFirstMeasure(activity, SimpleRadioButton1TestCase(activity))
+        benchmarkRule.benchmarkFirstMeasure(SimpleRadioButton1TestCase())
     }
 
     @Test
     fun radio_button_1_first_layout() {
-        benchmarkRule.measureFirstLayout(activity, SimpleRadioButton1TestCase(activity))
+        benchmarkRule.benchmarkFirstLayout(SimpleRadioButton1TestCase())
     }
 
     @Test
     fun radio_button_1_first_draw() {
-        benchmarkRule.measureFirstDraw(activity, SimpleRadioButton1TestCase(activity))
+        benchmarkRule.benchmarkFirstDraw(SimpleRadioButton1TestCase())
     }
 
     @Test
     fun radio_button_1_update_recompose() {
-        benchmarkRule.toggleStateMeasureRecompose(activity, SimpleRadioButton1TestCase(activity))
+        benchmarkRule.toggleStateBenchmarkRecompose(SimpleRadioButton1TestCase())
     }
 
     @Test
     fun radio_button_1_update_measure() {
-        benchmarkRule.toggleStateMeasureMeasure(activity, SimpleRadioButton1TestCase(activity))
+        benchmarkRule.toggleStateBenchmarkMeasure(SimpleRadioButton1TestCase())
     }
 
     @Test
     fun radio_button_1_update_layout() {
-        benchmarkRule.toggleStateMeasureLayout(activity, SimpleRadioButton1TestCase(activity))
+        benchmarkRule.toggleStateBenchmarkLayout(SimpleRadioButton1TestCase())
     }
 
     @Test
     fun radio_button_1_update_draw() {
-        benchmarkRule.toggleStateMeasureDraw(activity, SimpleRadioButton1TestCase(activity))
+        benchmarkRule.toggleStateBenchmarkDraw(SimpleRadioButton1TestCase())
     }
 
     @Test
     fun radio_button_1_layout() {
-        benchmarkRule.measureLayoutPerf(activity, SimpleRadioButton1TestCase(activity))
+        benchmarkRule.benchmarkLayoutPerf(SimpleRadioButton1TestCase())
     }
 
     @Test
     fun radio_button_1_draw() {
-        benchmarkRule.measureDrawPerf(activity, SimpleRadioButton1TestCase(activity))
+        benchmarkRule.benchmarkDrawPerf(SimpleRadioButton1TestCase())
     }
 
     @Test
     fun radio_button_2_first_compose() {
-        benchmarkRule.measureFirstCompose(activity, SimpleRadioButton2TestCase(activity))
+        benchmarkRule.benchmarkFirstCompose(SimpleRadioButton2TestCase())
     }
 
     @Test
     fun radio_button_2_first_measure() {
-        benchmarkRule.measureFirstMeasure(activity, SimpleRadioButton2TestCase(activity))
+        benchmarkRule.benchmarkFirstMeasure(SimpleRadioButton2TestCase())
     }
 
     @Test
     fun radio_button_2_first_layout() {
-        benchmarkRule.measureFirstLayout(activity, SimpleRadioButton2TestCase(activity))
+        benchmarkRule.benchmarkFirstLayout(SimpleRadioButton2TestCase())
     }
 
     @Test
     fun radio_button_2_first_draw() {
-        benchmarkRule.measureFirstDraw(activity, SimpleRadioButton2TestCase(activity))
+        benchmarkRule.benchmarkFirstDraw(SimpleRadioButton2TestCase())
     }
 
     @Test
     fun radio_button_2_update_recompose() {
-        benchmarkRule.toggleStateMeasureRecompose(activity, SimpleRadioButton2TestCase(activity))
+        benchmarkRule.toggleStateBenchmarkRecompose(SimpleRadioButton2TestCase())
     }
 
     @Test
     fun radio_button_2_update_measure() {
-        benchmarkRule.toggleStateMeasureMeasure(activity, SimpleRadioButton2TestCase(activity))
+        benchmarkRule.toggleStateBenchmarkMeasure(SimpleRadioButton2TestCase())
     }
 
     @Test
     fun radio_button_2_update_layout() {
-        benchmarkRule.toggleStateMeasureLayout(activity, SimpleRadioButton2TestCase(activity))
+        benchmarkRule.toggleStateBenchmarkLayout(SimpleRadioButton2TestCase())
     }
 
     @Test
     fun radio_button_2_update_draw() {
-        benchmarkRule.toggleStateMeasureDraw(activity, SimpleRadioButton2TestCase(activity))
+        benchmarkRule.toggleStateBenchmarkDraw(SimpleRadioButton2TestCase())
     }
 
     @Test
     fun radio_button_2_layout() {
-        benchmarkRule.measureLayoutPerf(activity, SimpleRadioButton2TestCase(activity))
+        benchmarkRule.benchmarkLayoutPerf(SimpleRadioButton2TestCase())
     }
 
     @Test
     fun radio_button_2_draw() {
-        benchmarkRule.measureDrawPerf(activity, SimpleRadioButton2TestCase(activity))
+        benchmarkRule.benchmarkDrawPerf(SimpleRadioButton2TestCase())
     }
 
     @Test
     fun radio_button_3_first_compose() {
-        benchmarkRule.measureFirstCompose(activity, SimpleRadioButton3TestCase(activity))
+        benchmarkRule.benchmarkFirstCompose(SimpleRadioButton3TestCase())
     }
 
     @Test
     fun radio_button_3_first_measure() {
-        benchmarkRule.measureFirstMeasure(activity, SimpleRadioButton3TestCase(activity))
+        benchmarkRule.benchmarkFirstMeasure(SimpleRadioButton3TestCase())
     }
 
     @Test
     fun radio_button_3_first_layout() {
-        benchmarkRule.measureFirstLayout(activity, SimpleRadioButton3TestCase(activity))
+        benchmarkRule.benchmarkFirstLayout(SimpleRadioButton3TestCase())
     }
 
     @Test
     fun radio_button_3_first_draw() {
-        benchmarkRule.measureFirstDraw(activity, SimpleRadioButton3TestCase(activity))
+        benchmarkRule.benchmarkFirstDraw(SimpleRadioButton3TestCase())
     }
 
     @Test
     fun radio_button_3_update_measure() {
-        benchmarkRule.toggleStateMeasureMeasure(activity, SimpleRadioButton3TestCase(activity),
+        benchmarkRule.toggleStateBenchmarkMeasure(SimpleRadioButton3TestCase(),
             toggleCausesRecompose = false)
     }
 
     @Test
     fun radio_button_3_update_layout() {
-        benchmarkRule.toggleStateMeasureLayout(activity, SimpleRadioButton3TestCase(activity),
+        benchmarkRule.toggleStateBenchmarkLayout(SimpleRadioButton3TestCase(),
             toggleCausesRecompose = false)
     }
 
     @Test
     fun radio_button_3_update_draw() {
-        benchmarkRule.toggleStateMeasureDraw(activity, SimpleRadioButton3TestCase(activity),
+        benchmarkRule.toggleStateBenchmarkDraw(SimpleRadioButton3TestCase(),
             toggleCausesRecompose = false)
     }
 
     @Test
     fun radio_button_3_layout() {
-        benchmarkRule.measureLayoutPerf(activity, SimpleRadioButton3TestCase(activity))
+        benchmarkRule.benchmarkLayoutPerf(SimpleRadioButton3TestCase())
     }
 
     @Test
     fun radio_button_3_draw() {
-        benchmarkRule.measureDrawPerf(activity, SimpleRadioButton3TestCase(activity))
+        benchmarkRule.benchmarkDrawPerf(SimpleRadioButton3TestCase())
     }
 }
diff --git a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/SpacingBenchmark.kt b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/SpacingBenchmark.kt
index 2e20e89..2c5a013 100644
--- a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/SpacingBenchmark.kt
+++ b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/SpacingBenchmark.kt
@@ -16,33 +16,28 @@
 
 package androidx.ui.benchmark.test
 
-import android.app.Activity
-import androidx.benchmark.junit4.BenchmarkRule
 import androidx.compose.Composable
-import androidx.compose.FrameManager
 import androidx.compose.State
 import androidx.compose.state
 import androidx.compose.unaryPlus
 import androidx.test.filters.LargeTest
-import androidx.test.rule.ActivityTestRule
-import androidx.ui.benchmark.measureDrawPerf
-import androidx.ui.benchmark.measureFirstCompose
-import androidx.ui.benchmark.measureFirstDraw
-import androidx.ui.benchmark.measureFirstLayout
-import androidx.ui.benchmark.measureFirstMeasure
-import androidx.ui.benchmark.measureLayoutPerf
-import androidx.ui.benchmark.toggleStateMeasureDraw
-import androidx.ui.benchmark.toggleStateMeasureLayout
-import androidx.ui.benchmark.toggleStateMeasureMeasure
-import androidx.ui.benchmark.toggleStateMeasureRecompose
+import androidx.ui.benchmark.ComposeBenchmarkRule
+import androidx.ui.benchmark.benchmarkDrawPerf
+import androidx.ui.benchmark.benchmarkFirstCompose
+import androidx.ui.benchmark.benchmarkFirstDraw
+import androidx.ui.benchmark.benchmarkFirstLayout
+import androidx.ui.benchmark.benchmarkFirstMeasure
+import androidx.ui.benchmark.benchmarkLayoutPerf
+import androidx.ui.benchmark.toggleStateBenchmarkDraw
+import androidx.ui.benchmark.toggleStateBenchmarkLayout
+import androidx.ui.benchmark.toggleStateBenchmarkMeasure
+import androidx.ui.benchmark.toggleStateBenchmarkRecompose
 import androidx.ui.core.Dp
 import androidx.ui.core.dp
-import androidx.ui.core.setContent
 import androidx.ui.layout.Container
 import androidx.ui.layout.Padding
 import androidx.ui.layout.Spacing
 import androidx.ui.test.ComposeTestCase
-import androidx.ui.test.DisableTransitions
 import androidx.ui.test.ToggleableTestCase
 import org.junit.Rule
 import org.junit.Test
@@ -53,118 +48,110 @@
 @RunWith(JUnit4::class)
 class PaddingBenchmark {
     @get:Rule
-    val benchmarkRule = BenchmarkRule()
-
-    @get:Rule
-    val activityRule = ActivityTestRule(Activity::class.java)
-
-    @get:Rule
-    val disableAnimationRule = DisableTransitions()
-
-    private val activity: Activity get() = activityRule.activity
+    val benchmarkRule = ComposeBenchmarkRule()
 
     @Test
     fun noModifier_first_compose() {
-        benchmarkRule.measureFirstCompose(activity, NoModifierTestCase(activity))
+        benchmarkRule.benchmarkFirstCompose(NoModifierTestCase())
     }
 
     @Test
     fun noModifier_first_measure() {
-        benchmarkRule.measureFirstMeasure(activity, NoModifierTestCase(activity))
+        benchmarkRule.benchmarkFirstMeasure(NoModifierTestCase())
     }
 
     @Test
     fun noModifier_first_layout() {
-        benchmarkRule.measureFirstLayout(activity, NoModifierTestCase(activity))
+        benchmarkRule.benchmarkFirstLayout(NoModifierTestCase())
     }
 
     @Test
     fun noModifier_first_draw() {
-        benchmarkRule.measureFirstDraw(activity, NoModifierTestCase(activity))
+        benchmarkRule.benchmarkFirstDraw(NoModifierTestCase())
     }
 
     @Test
     fun noModifier_togglePadding_recompose() {
-        benchmarkRule.toggleStateMeasureRecompose(activity, NoModifierTestCase(activity))
+        benchmarkRule.toggleStateBenchmarkRecompose(NoModifierTestCase())
     }
 
     @Test
     fun noModifier_togglePadding_measure() {
-        benchmarkRule.toggleStateMeasureMeasure(activity, NoModifierTestCase(activity))
+        benchmarkRule.toggleStateBenchmarkMeasure(NoModifierTestCase())
     }
 
     @Test
     fun noModifier_togglePadding_layout() {
-        benchmarkRule.toggleStateMeasureLayout(activity, NoModifierTestCase(activity))
+        benchmarkRule.toggleStateBenchmarkLayout(NoModifierTestCase())
     }
 
     @Test
     fun noModifier_togglePadding_draw() {
-        benchmarkRule.toggleStateMeasureDraw(activity, NoModifierTestCase(activity))
+        benchmarkRule.toggleStateBenchmarkDraw(NoModifierTestCase())
     }
 
     @Test
     fun noModifier_layout() {
-        benchmarkRule.measureLayoutPerf(activity, NoModifierTestCase(activity))
+        benchmarkRule.benchmarkLayoutPerf(NoModifierTestCase())
     }
 
     @Test
     fun noModifier_draw() {
-        benchmarkRule.measureDrawPerf(activity, NoModifierTestCase(activity))
+        benchmarkRule.benchmarkDrawPerf(NoModifierTestCase())
     }
 
     @Test
     fun modifier_first_compose() {
-        benchmarkRule.measureFirstCompose(activity, ModifierTestCase(activity))
+        benchmarkRule.benchmarkFirstCompose(ModifierTestCase())
     }
 
     @Test
     fun modifier_first_measure() {
-        benchmarkRule.measureFirstMeasure(activity, ModifierTestCase(activity))
+        benchmarkRule.benchmarkFirstMeasure(ModifierTestCase())
     }
 
     @Test
     fun modifier_first_layout() {
-        benchmarkRule.measureFirstLayout(activity, ModifierTestCase(activity))
+        benchmarkRule.benchmarkFirstLayout(ModifierTestCase())
     }
 
     @Test
     fun modifier_first_draw() {
-        benchmarkRule.measureFirstDraw(activity, ModifierTestCase(activity))
+        benchmarkRule.benchmarkFirstDraw(ModifierTestCase())
     }
 
     @Test
     fun modifier_togglePadding_recompose() {
-        benchmarkRule.toggleStateMeasureRecompose(activity, ModifierTestCase(activity))
+        benchmarkRule.toggleStateBenchmarkRecompose(ModifierTestCase())
     }
 
     @Test
     fun modifier_togglePadding_measure() {
-        benchmarkRule.toggleStateMeasureMeasure(activity, ModifierTestCase(activity))
+        benchmarkRule.toggleStateBenchmarkMeasure(ModifierTestCase())
     }
 
     @Test
     fun modifier_togglePadding_layout() {
-        benchmarkRule.toggleStateMeasureLayout(activity, ModifierTestCase(activity))
+        benchmarkRule.toggleStateBenchmarkLayout(ModifierTestCase())
     }
 
     @Test
     fun modifier_togglePadding_draw() {
-        benchmarkRule.toggleStateMeasureDraw(activity, ModifierTestCase(activity))
+        benchmarkRule.toggleStateBenchmarkDraw(ModifierTestCase())
     }
 
     @Test
     fun modifier_layout() {
-        benchmarkRule.measureLayoutPerf(activity, ModifierTestCase(activity))
+        benchmarkRule.benchmarkLayoutPerf(ModifierTestCase())
     }
 
     @Test
     fun modifier_draw() {
-        benchmarkRule.measureDrawPerf(activity, ModifierTestCase(activity))
+        benchmarkRule.benchmarkDrawPerf(ModifierTestCase())
     }
 }
 
-private sealed class PaddingTestCase(activity: Activity) : ComposeTestCase(activity),
+private sealed class PaddingTestCase : ComposeTestCase,
     ToggleableTestCase {
 
     var paddingState: State<Dp>? = null
@@ -173,10 +160,10 @@
         with(paddingState!!) {
             value = if (value == 5.dp) 10.dp else 5.dp
         }
-        FrameManager.nextFrame()
     }
 
-    override fun setComposeContent(activity: Activity) = activity.setContent {
+    @Composable
+    override fun emitContent() {
         val padding = +state { 5.dp }
         paddingState = padding
 
@@ -191,13 +178,13 @@
                 }
             }
         }
-    }!!
+    }
 
     @Composable
     abstract fun emitPaddedContainer(padding: Dp, child: @Composable() () -> Unit)
 }
 
-private class ModifierTestCase(activity: Activity) : PaddingTestCase(activity) {
+private class ModifierTestCase : PaddingTestCase() {
 
     @Composable
     override fun emitPaddedContainer(padding: Dp, child: @Composable() () -> Unit) {
@@ -205,7 +192,7 @@
     }
 }
 
-private class NoModifierTestCase(activity: Activity) : PaddingTestCase(activity) {
+private class NoModifierTestCase : PaddingTestCase() {
 
     @Composable
     override fun emitPaddedContainer(padding: Dp, child: @Composable() () -> Unit) {
diff --git a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/TrailingLambdaBenchmark.kt b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/TrailingLambdaBenchmark.kt
index 58a4eef..1a1ac95 100644
--- a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/TrailingLambdaBenchmark.kt
+++ b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/TrailingLambdaBenchmark.kt
@@ -16,19 +16,15 @@
 
 package androidx.ui.benchmark.test
 
-import android.app.Activity
-import androidx.benchmark.junit4.BenchmarkRule
 import androidx.compose.Composable
-import androidx.compose.FrameManager
 import androidx.compose.State
 import androidx.test.filters.LargeTest
 import androidx.compose.state
 import androidx.compose.unaryPlus
-import androidx.test.rule.ActivityTestRule
-import androidx.ui.benchmark.measureFirstCompose
-import androidx.ui.benchmark.toggleStateMeasureRecompose
+import androidx.ui.benchmark.ComposeBenchmarkRule
+import androidx.ui.benchmark.benchmarkFirstCompose
+import androidx.ui.benchmark.toggleStateBenchmarkRecompose
 import androidx.ui.core.dp
-import androidx.ui.core.setContent
 import androidx.ui.layout.Column
 import androidx.ui.layout.Container
 import androidx.ui.test.ComposeTestCase
@@ -42,40 +38,36 @@
 @RunWith(JUnit4::class)
 class TrailingLambdaBenchmark {
     @get:Rule
-    val benchmarkRule = BenchmarkRule()
-
-    @get:Rule
-    val activityRule = ActivityTestRule(Activity::class.java)
-
-    private val activity: Activity get() = activityRule.activity
+    val benchmarkRule = ComposeBenchmarkRule()
 
     @Test
     fun withTrailingLambdas_compose() {
-        benchmarkRule.measureFirstCompose(activity, WithTrailingLambdas(activity))
+        benchmarkRule.benchmarkFirstCompose(WithTrailingLambdas())
     }
 
     @Test
     fun withTrailingLambdas_recompose() {
-        benchmarkRule.toggleStateMeasureRecompose(activity, WithTrailingLambdas(activity))
+        benchmarkRule.toggleStateBenchmarkRecompose(WithTrailingLambdas())
     }
 
     @Test
     fun withoutTrailingLambdas_compose() {
-        benchmarkRule.measureFirstCompose(activity, WithoutTrailingLambdas(activity))
+        benchmarkRule.benchmarkFirstCompose(WithoutTrailingLambdas())
     }
 
     @Test
     fun withoutTrailingLambdas_recompose() {
-        benchmarkRule.toggleStateMeasureRecompose(activity, WithoutTrailingLambdas(activity))
+        benchmarkRule.toggleStateBenchmarkRecompose(WithoutTrailingLambdas())
     }
 }
 
-private sealed class TrailingLambdaTestCase(activity: Activity) : ComposeTestCase(activity),
+private sealed class TrailingLambdaTestCase() : ComposeTestCase,
     ToggleableTestCase {
 
     var numberState: State<Int>? = null
 
-    override fun setComposeContent(activity: Activity) = activity.setContent {
+    @Composable
+    override fun emitContent() {
         val number = +state { 5 }
         numberState = number
 
@@ -88,20 +80,19 @@
                 emitContent(number = number.value, content = content)
             }
         }
-    }!!
+    }
 
     override fun toggleState() {
         with(numberState!!) {
             value = if (value == 5) 10 else 5
         }
-        FrameManager.nextFrame()
     }
 
     @Composable
     abstract fun emitContent(number: Int, content: @Composable() () -> Unit)
 }
 
-private class WithTrailingLambdas(activity: Activity) : TrailingLambdaTestCase(activity) {
+private class WithTrailingLambdas : TrailingLambdaTestCase() {
     @Composable
     override fun emitContent(number: Int, content: @Composable() () -> Unit) {
         EmptyComposable(number = number) {
@@ -110,7 +101,7 @@
     }
 }
 
-private class WithoutTrailingLambdas(activity: Activity) : TrailingLambdaTestCase(activity) {
+private class WithoutTrailingLambdas : TrailingLambdaTestCase() {
     @Composable
     override fun emitContent(number: Int, content: @Composable() () -> Unit) {
         EmptyComposable(number = number, children = content)
diff --git a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/view/AndroidCheckboxesInLinearLayoutBenchmark.kt b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/view/AndroidCheckboxesInLinearLayoutBenchmark.kt
index d431b29..c6a4f75 100644
--- a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/view/AndroidCheckboxesInLinearLayoutBenchmark.kt
+++ b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/view/AndroidCheckboxesInLinearLayoutBenchmark.kt
@@ -16,17 +16,14 @@
 
 package androidx.ui.benchmark.test.view
 
-import android.app.Activity
-import androidx.benchmark.junit4.BenchmarkRule
 import androidx.test.filters.LargeTest
-import androidx.test.rule.ActivityTestRule
-import androidx.ui.benchmark.measureDrawPerf
-import androidx.ui.benchmark.measureFirstDraw
-import androidx.ui.benchmark.measureFirstLayout
-import androidx.ui.benchmark.measureFirstMeasure
-import androidx.ui.benchmark.measureFirstSetContent
-import androidx.ui.benchmark.measureLayoutPerf
-import androidx.ui.test.DisableTransitions
+import androidx.ui.benchmark.ComposeBenchmarkRule
+import androidx.ui.benchmark.benchmarkDrawPerf
+import androidx.ui.benchmark.benchmarkFirstDraw
+import androidx.ui.benchmark.benchmarkFirstLayout
+import androidx.ui.benchmark.benchmarkFirstMeasure
+import androidx.ui.benchmark.benchmarkFirstSetContent
+import androidx.ui.benchmark.benchmarkLayoutPerf
 import androidx.ui.test.cases.view.AndroidCheckboxesInLinearLayoutTestCase
 import org.junit.Rule
 import org.junit.Test
@@ -47,49 +44,41 @@
     }
 
     @get:Rule
-    val benchmarkRule = BenchmarkRule()
-
-    @get:Rule
-    val activityRule = ActivityTestRule(Activity::class.java)
-
-    @get:Rule
-    val disableAnimationRule = DisableTransitions()
-
-    private val activity: Activity get() = activityRule.activity
+    val benchmarkRule = ComposeBenchmarkRule()
 
     @Test
     fun first_setContent() {
-        benchmarkRule.measureFirstSetContent(activity,
-            AndroidCheckboxesInLinearLayoutTestCase(activity, numberOfCheckboxes))
+        benchmarkRule.benchmarkFirstSetContent(
+            AndroidCheckboxesInLinearLayoutTestCase(numberOfCheckboxes))
     }
 
     @Test
     fun first_measure() {
-        benchmarkRule.measureFirstMeasure(activity,
-            AndroidCheckboxesInLinearLayoutTestCase(activity, numberOfCheckboxes))
+        benchmarkRule.benchmarkFirstMeasure(
+            AndroidCheckboxesInLinearLayoutTestCase(numberOfCheckboxes))
     }
 
     @Test
     fun first_layout() {
-        benchmarkRule.measureFirstLayout(activity,
-            AndroidCheckboxesInLinearLayoutTestCase(activity, numberOfCheckboxes))
+        benchmarkRule.benchmarkFirstLayout(
+            AndroidCheckboxesInLinearLayoutTestCase(numberOfCheckboxes))
     }
 
     @Test
     fun first_draw() {
-        benchmarkRule.measureFirstDraw(activity,
-            AndroidCheckboxesInLinearLayoutTestCase(activity, numberOfCheckboxes))
+        benchmarkRule.benchmarkFirstDraw(
+            AndroidCheckboxesInLinearLayoutTestCase(numberOfCheckboxes))
     }
 
     @Test
     fun layout() {
-        benchmarkRule.measureLayoutPerf(activity,
-            AndroidCheckboxesInLinearLayoutTestCase(activity, numberOfCheckboxes))
+        benchmarkRule.benchmarkLayoutPerf(
+            AndroidCheckboxesInLinearLayoutTestCase(numberOfCheckboxes))
     }
 
     @Test
     fun draw() {
-        benchmarkRule.measureDrawPerf(activity,
-            AndroidCheckboxesInLinearLayoutTestCase(activity, numberOfCheckboxes))
+        benchmarkRule.benchmarkDrawPerf(
+            AndroidCheckboxesInLinearLayoutTestCase(numberOfCheckboxes))
     }
 }
\ No newline at end of file
diff --git a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/view/AndroidNestedScrollViewBenchmark.kt b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/view/AndroidNestedScrollViewBenchmark.kt
index 54b1c4c..817a54b 100644
--- a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/view/AndroidNestedScrollViewBenchmark.kt
+++ b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/view/AndroidNestedScrollViewBenchmark.kt
@@ -16,20 +16,17 @@
 
 package androidx.ui.benchmark.test.view
 
-import android.app.Activity
-import androidx.benchmark.junit4.BenchmarkRule
 import androidx.test.filters.LargeTest
-import androidx.test.rule.ActivityTestRule
-import androidx.ui.benchmark.measureDrawPerf
-import androidx.ui.benchmark.measureFirstDraw
-import androidx.ui.benchmark.measureFirstLayout
-import androidx.ui.benchmark.measureFirstMeasure
-import androidx.ui.benchmark.measureFirstSetContent
-import androidx.ui.benchmark.measureLayoutPerf
-import androidx.ui.benchmark.toggleStateMeasureDraw
-import androidx.ui.benchmark.toggleStateMeasureLayout
-import androidx.ui.benchmark.toggleStateMeasureMeasure
-import androidx.ui.test.DisableTransitions
+import androidx.ui.benchmark.ComposeBenchmarkRule
+import androidx.ui.benchmark.benchmarkDrawPerf
+import androidx.ui.benchmark.benchmarkFirstDraw
+import androidx.ui.benchmark.benchmarkFirstLayout
+import androidx.ui.benchmark.benchmarkFirstMeasure
+import androidx.ui.benchmark.benchmarkFirstSetContent
+import androidx.ui.benchmark.benchmarkLayoutPerf
+import androidx.ui.benchmark.toggleStateBenchmarkDraw
+import androidx.ui.benchmark.toggleStateBenchmarkLayout
+import androidx.ui.benchmark.toggleStateBenchmarkMeasure
 import androidx.ui.test.cases.view.AndroidNestedScrollViewTestCase
 import org.junit.Rule
 import org.junit.Test
@@ -44,67 +41,50 @@
 class AndroidNestedScrollViewBenchmark {
 
     @get:Rule
-    val benchmarkRule = BenchmarkRule()
-
-    @get:Rule
-    val activityRule = ActivityTestRule(Activity::class.java)
-
-    @get:Rule
-    val disableAnimationRule = DisableTransitions()
-
-    private val activity: Activity get() = activityRule.activity
+    val benchmarkRule = ComposeBenchmarkRule()
 
     @Test
     fun first_setContent() {
-        benchmarkRule.measureFirstSetContent(activity,
-            AndroidNestedScrollViewTestCase(activity))
+        benchmarkRule.benchmarkFirstSetContent(AndroidNestedScrollViewTestCase())
     }
 
     @Test
     fun first_measure() {
-        benchmarkRule.measureFirstMeasure(activity,
-            AndroidNestedScrollViewTestCase(activity))
+        benchmarkRule.benchmarkFirstMeasure(AndroidNestedScrollViewTestCase())
     }
 
     @Test
     fun first_layout() {
-        benchmarkRule.measureFirstLayout(activity,
-            AndroidNestedScrollViewTestCase(activity))
+        benchmarkRule.benchmarkFirstLayout(AndroidNestedScrollViewTestCase())
     }
 
     @Test
     fun first_draw() {
-        benchmarkRule.measureFirstDraw(activity,
-            AndroidNestedScrollViewTestCase(activity))
+        benchmarkRule.benchmarkFirstDraw(AndroidNestedScrollViewTestCase())
     }
 
     @Test
     fun changeScroll_measure() {
-        benchmarkRule.toggleStateMeasureMeasure(activity,
-            AndroidNestedScrollViewTestCase(activity))
+        benchmarkRule.toggleStateBenchmarkMeasure(AndroidNestedScrollViewTestCase())
     }
 
     @Test
     fun changeScroll_layout() {
-        benchmarkRule.toggleStateMeasureLayout(activity,
-            AndroidNestedScrollViewTestCase(activity))
+        benchmarkRule.toggleStateBenchmarkLayout(AndroidNestedScrollViewTestCase())
     }
 
     @Test
     fun changeScroll_draw() {
-        benchmarkRule.toggleStateMeasureDraw(activity,
-            AndroidNestedScrollViewTestCase(activity))
+        benchmarkRule.toggleStateBenchmarkDraw(AndroidNestedScrollViewTestCase())
     }
 
     @Test
     fun layout() {
-        benchmarkRule.measureLayoutPerf(activity,
-            AndroidNestedScrollViewTestCase(activity))
+        benchmarkRule.benchmarkLayoutPerf(AndroidNestedScrollViewTestCase())
     }
 
     @Test
     fun draw() {
-        benchmarkRule.measureDrawPerf(activity,
-            AndroidNestedScrollViewTestCase(activity))
+        benchmarkRule.benchmarkDrawPerf(AndroidNestedScrollViewTestCase())
     }
 }
\ No newline at end of file
diff --git a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/core/OnPositionedBenchmark.kt b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/core/OnPositionedBenchmark.kt
index 291e683..a73d755 100644
--- a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/core/OnPositionedBenchmark.kt
+++ b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/core/OnPositionedBenchmark.kt
@@ -16,16 +16,13 @@
 
 package androidx.ui.core
 
-import android.app.Activity
-import androidx.benchmark.junit4.BenchmarkRule
 import androidx.compose.Composable
-import androidx.compose.FrameManager
 import androidx.compose.State
 import androidx.compose.state
 import androidx.compose.unaryPlus
 import androidx.test.filters.LargeTest
-import androidx.test.rule.ActivityTestRule
-import androidx.ui.benchmark.toggleStateMeasureLayout
+import androidx.ui.benchmark.ComposeBenchmarkRule
+import androidx.ui.benchmark.toggleStateBenchmarkLayout
 import androidx.ui.layout.Center
 import androidx.ui.layout.Container
 import androidx.ui.test.ComposeTestCase
@@ -40,29 +37,23 @@
 class OnPositionedBenchmark {
 
     @get:Rule
-    val benchmarkRule = BenchmarkRule()
-
-    @get:Rule
-    val activityRule = ActivityTestRule(Activity::class.java)
-
-    private val activity: Activity get() = activityRule.activity
+    val benchmarkRule = ComposeBenchmarkRule()
 
     @Test
     fun deepHierarchyOnPositioned_layout() {
-        benchmarkRule.toggleStateMeasureLayout(
-            activity,
-            DeepHierarchyOnPositionedTestCase(activity)
+        benchmarkRule.toggleStateBenchmarkLayout(
+            DeepHierarchyOnPositionedTestCase()
         )
     }
 }
 
-private class DeepHierarchyOnPositionedTestCase(
-    activity: Activity
-) : ComposeTestCase(activity), ToggleableTestCase {
+private class DeepHierarchyOnPositionedTestCase :
+    ComposeTestCase, ToggleableTestCase {
 
     private lateinit var state: State<Dp>
 
-    override fun setComposeContent(activity: Activity) = activity.setContent {
+    @Composable
+    override fun emitContent() {
         val size = +state { 200.dp }
         this.state = size
         Center {
@@ -70,7 +61,7 @@
                 StaticChildren(100)
             }
         }
-    }!!
+    }
 
     @Composable
     private fun StaticChildren(count: Int) {
@@ -85,6 +76,5 @@
 
     override fun toggleState() {
         state.value = if (state.value == 200.dp) 150.dp else 200.dp
-        FrameManager.nextFrame()
     }
 }
diff --git a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/core/TextBasicBenchmark.kt b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/core/TextBasicBenchmark.kt
index 4a9d1e6..4545be8 100644
--- a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/core/TextBasicBenchmark.kt
+++ b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/core/TextBasicBenchmark.kt
@@ -16,17 +16,14 @@
 
 package androidx.ui.core
 
-import android.app.Activity
-import androidx.benchmark.junit4.BenchmarkRule
 import androidx.test.filters.LargeTest
-import androidx.test.rule.ActivityTestRule
-import androidx.ui.benchmark.measureDrawPerf
-import androidx.ui.benchmark.measureFirstCompose
-import androidx.ui.benchmark.measureFirstDraw
-import androidx.ui.benchmark.measureFirstLayout
-import androidx.ui.benchmark.measureFirstMeasure
-import androidx.ui.benchmark.measureLayoutPerf
-import androidx.ui.test.DisableTransitions
+import androidx.ui.benchmark.ComposeBenchmarkRule
+import androidx.ui.benchmark.benchmarkDrawPerf
+import androidx.ui.benchmark.benchmarkFirstCompose
+import androidx.ui.benchmark.benchmarkFirstDraw
+import androidx.ui.benchmark.benchmarkFirstLayout
+import androidx.ui.benchmark.benchmarkFirstMeasure
+import androidx.ui.benchmark.benchmarkLayoutPerf
 import androidx.ui.test.TextBenchmarkTestRule
 import org.junit.Rule
 import org.junit.Test
@@ -41,19 +38,12 @@
 class TextBasicBenchmark(
     private val textLength: Int
 ) {
-    @get:Rule
-    val benchmarkRule = BenchmarkRule()
-
-    @get:Rule
-    val activityRule = ActivityTestRule(Activity::class.java)
-
-    @get:Rule
-    val disableAnimationRule = DisableTransitions()
 
     @get:Rule
     val textBenchmarkRule = TextBenchmarkTestRule()
 
-    private val activity: Activity get() = activityRule.activity
+    @get:Rule
+    val benchmarkRule = ComposeBenchmarkRule()
 
     companion object {
         @JvmStatic
@@ -68,9 +58,8 @@
     @Test
     fun first_compose() {
         textBenchmarkRule.generator { textGenerator ->
-            benchmarkRule.measureFirstCompose(
-                activity,
-                TextBasicTestCase(activity, textLength, textGenerator)
+            benchmarkRule.benchmarkFirstCompose(
+                TextBasicTestCase(textLength, textGenerator)
             )
         }
     }
@@ -82,9 +71,8 @@
     @Test
     fun first_measure() {
         textBenchmarkRule.generator { textGenerator ->
-            benchmarkRule.measureFirstMeasure(
-                activity,
-                TextBasicTestCase(activity, textLength, textGenerator)
+            benchmarkRule.benchmarkFirstMeasure(
+                TextBasicTestCase(textLength, textGenerator)
             )
         }
     }
@@ -96,9 +84,8 @@
     @Test
     fun first_layout() {
         textBenchmarkRule.generator { textGenerator ->
-            benchmarkRule.measureFirstLayout(
-                activity,
-                TextBasicTestCase(activity, textLength, textGenerator)
+            benchmarkRule.benchmarkFirstLayout(
+                TextBasicTestCase(textLength, textGenerator)
             )
         }
     }
@@ -109,9 +96,8 @@
     @Test
     fun first_draw() {
         textBenchmarkRule.generator { textGenerator ->
-            benchmarkRule.measureFirstDraw(
-                activity,
-                TextBasicTestCase(activity, textLength, textGenerator)
+            benchmarkRule.benchmarkFirstDraw(
+                TextBasicTestCase(textLength, textGenerator)
             )
         }
     }
@@ -123,9 +109,8 @@
     @Test
     fun layout() {
         textBenchmarkRule.generator { textGenerator ->
-            benchmarkRule.measureLayoutPerf(
-                activity,
-                TextBasicTestCase(activity, textLength, textGenerator)
+            benchmarkRule.benchmarkLayoutPerf(
+                TextBasicTestCase(textLength, textGenerator)
             )
         }
     }
@@ -136,9 +121,8 @@
     @Test
     fun draw() {
         textBenchmarkRule.generator { textGenerator ->
-            benchmarkRule.measureDrawPerf(
-                activity,
-                TextBasicTestCase(activity, textLength, textGenerator)
+            benchmarkRule.benchmarkDrawPerf(
+                TextBasicTestCase(textLength, textGenerator)
             )
         }
     }
diff --git a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/core/TextMultiStyleBenchmark.kt b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/core/TextMultiStyleBenchmark.kt
index 972564b..cd0ec11 100644
--- a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/core/TextMultiStyleBenchmark.kt
+++ b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/core/TextMultiStyleBenchmark.kt
@@ -16,17 +16,14 @@
 
 package androidx.ui.core
 
-import android.app.Activity
-import androidx.benchmark.junit4.BenchmarkRule
 import androidx.test.filters.LargeTest
-import androidx.test.rule.ActivityTestRule
-import androidx.ui.benchmark.measureDrawPerf
-import androidx.ui.benchmark.measureFirstCompose
-import androidx.ui.benchmark.measureFirstDraw
-import androidx.ui.benchmark.measureFirstLayout
-import androidx.ui.benchmark.measureFirstMeasure
-import androidx.ui.benchmark.measureLayoutPerf
-import androidx.ui.test.DisableTransitions
+import androidx.ui.benchmark.ComposeBenchmarkRule
+import androidx.ui.benchmark.benchmarkDrawPerf
+import androidx.ui.benchmark.benchmarkFirstCompose
+import androidx.ui.benchmark.benchmarkFirstDraw
+import androidx.ui.benchmark.benchmarkFirstLayout
+import androidx.ui.benchmark.benchmarkFirstMeasure
+import androidx.ui.benchmark.benchmarkLayoutPerf
 import androidx.ui.test.TextBenchmarkTestRule
 import androidx.ui.test.cartesian
 import org.junit.Rule
@@ -45,18 +42,10 @@
     private val styleCount: Int
 ) {
     @get:Rule
-    val benchmarkRule = BenchmarkRule()
-
-    @get:Rule
-    val activityRule = ActivityTestRule(Activity::class.java)
-
-    @get:Rule
-    val disableAnimationRule = DisableTransitions()
-
-    @get:Rule
     val textBenchmarkRule = TextBenchmarkTestRule()
 
-    private val activity: Activity get() = activityRule.activity
+    @get:Rule
+    val benchmarkRule = ComposeBenchmarkRule()
 
     companion object {
         @JvmStatic
@@ -74,10 +63,8 @@
     @Test
     fun first_compose() {
         textBenchmarkRule.generator { textGenerator ->
-            benchmarkRule.measureFirstCompose(
-                activity,
+            benchmarkRule.benchmarkFirstCompose(
                 TextMultiStyleTestCase(
-                    activity,
                     textLength,
                     styleCount,
                     textGenerator
@@ -93,10 +80,8 @@
     @Test
     fun first_measure() {
         textBenchmarkRule.generator { textGenerator ->
-            benchmarkRule.measureFirstMeasure(
-                activity,
+            benchmarkRule.benchmarkFirstMeasure(
                 TextMultiStyleTestCase(
-                    activity,
                     textLength,
                     styleCount,
                     textGenerator
@@ -112,10 +97,8 @@
     @Test
     fun first_layout() {
         textBenchmarkRule.generator { textGenerator ->
-            benchmarkRule.measureFirstLayout(
-                activity,
+            benchmarkRule.benchmarkFirstLayout(
                 TextMultiStyleTestCase(
-                    activity,
                     textLength,
                     styleCount,
                     textGenerator
@@ -131,10 +114,8 @@
     @Test
     fun first_draw() {
         textBenchmarkRule.generator { textGenerator ->
-            benchmarkRule.measureFirstDraw(
-                activity,
+            benchmarkRule.benchmarkFirstDraw(
                 TextMultiStyleTestCase(
-                    activity,
                     textLength,
                     styleCount,
                     textGenerator
@@ -151,10 +132,8 @@
     @Test
     fun layout() {
         textBenchmarkRule.generator { textGenerator ->
-            benchmarkRule.measureLayoutPerf(
-                activity,
+            benchmarkRule.benchmarkLayoutPerf(
                 TextMultiStyleTestCase(
-                    activity,
                     textLength,
                     styleCount,
                     textGenerator
@@ -169,10 +148,8 @@
     @Test
     fun draw() {
         textBenchmarkRule.generator { textGenerator ->
-            benchmarkRule.measureDrawPerf(
-                activity,
+            benchmarkRule.benchmarkDrawPerf(
                 TextMultiStyleTestCase(
-                    activity,
                     textLength,
                     styleCount,
                     textGenerator
diff --git a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/core/TextWithSpanBenchmark.kt b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/core/TextWithSpanBenchmark.kt
index 7b098f8..7571613 100644
--- a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/core/TextWithSpanBenchmark.kt
+++ b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/core/TextWithSpanBenchmark.kt
@@ -16,17 +16,14 @@
 
 package androidx.ui.core
 
-import android.app.Activity
-import androidx.benchmark.junit4.BenchmarkRule
 import androidx.test.filters.LargeTest
-import androidx.test.rule.ActivityTestRule
-import androidx.ui.benchmark.measureDrawPerf
-import androidx.ui.benchmark.measureFirstCompose
-import androidx.ui.benchmark.measureFirstDraw
-import androidx.ui.benchmark.measureFirstLayout
-import androidx.ui.benchmark.measureFirstMeasure
-import androidx.ui.benchmark.measureLayoutPerf
-import androidx.ui.test.DisableTransitions
+import androidx.ui.benchmark.ComposeBenchmarkRule
+import androidx.ui.benchmark.benchmarkDrawPerf
+import androidx.ui.benchmark.benchmarkFirstCompose
+import androidx.ui.benchmark.benchmarkFirstDraw
+import androidx.ui.benchmark.benchmarkFirstLayout
+import androidx.ui.benchmark.benchmarkFirstMeasure
+import androidx.ui.benchmark.benchmarkLayoutPerf
 import androidx.ui.test.TextBenchmarkTestRule
 import androidx.ui.test.cartesian
 import org.junit.Rule
@@ -43,19 +40,11 @@
     private val textLength: Int
 ) {
     @get:Rule
-    val benchmarkRule = BenchmarkRule()
-
-    @get:Rule
-    val activityRule = ActivityTestRule(Activity::class.java)
-
-    @get:Rule
-    val disableAnimationRule = DisableTransitions()
+    val benchmarkRule = ComposeBenchmarkRule()
 
     @get:Rule
     val textBenchmarkRule = TextBenchmarkTestRule()
 
-    private val activity: Activity get() = activityRule.activity
-
     companion object {
         @JvmStatic
         @Parameterized.Parameters(name = "length={0}")
@@ -72,10 +61,8 @@
     @Test
     fun first_compose() {
         textBenchmarkRule.generator { textGenerator ->
-            benchmarkRule.measureFirstCompose(
-                activity,
+            benchmarkRule.benchmarkFirstCompose(
                 TextWithSpanTestCase(
-                    activity,
                     textLength,
                     textGenerator
                 )
@@ -90,10 +77,8 @@
     @Test
     fun first_measure() {
         textBenchmarkRule.generator { textGenerator ->
-            benchmarkRule.measureFirstMeasure(
-                activity,
+            benchmarkRule.benchmarkFirstMeasure(
                 TextWithSpanTestCase(
-                    activity,
                     textLength,
                     textGenerator
                 )
@@ -108,10 +93,8 @@
     @Test
     fun first_layout() {
         textBenchmarkRule.generator { textGenerator ->
-            benchmarkRule.measureFirstLayout(
-                activity,
+            benchmarkRule.benchmarkFirstLayout(
                 TextWithSpanTestCase(
-                    activity,
                     textLength,
                     textGenerator
                 )
@@ -125,10 +108,8 @@
     @Test
     fun first_draw() {
         textBenchmarkRule.generator { textGenerator ->
-            benchmarkRule.measureFirstDraw(
-                activity,
+            benchmarkRule.benchmarkFirstDraw(
                 TextWithSpanTestCase(
-                    activity,
                     textLength,
                     textGenerator
                 )
@@ -144,10 +125,8 @@
     @Test
     fun layout() {
         textBenchmarkRule.generator { textGenerator ->
-            benchmarkRule.measureLayoutPerf(
-                activity,
+            benchmarkRule.benchmarkLayoutPerf(
                 TextWithSpanTestCase(
-                    activity,
                     textLength,
                     textGenerator
                 )
@@ -161,10 +140,8 @@
     @Test
     fun draw() {
         textBenchmarkRule.generator { textGenerator ->
-            benchmarkRule.measureDrawPerf(
-                activity,
+            benchmarkRule.benchmarkDrawPerf(
                 TextWithSpanTestCase(
-                    activity,
                     textLength,
                     textGenerator
                 )
diff --git a/ui/integration-tests/benchmark/src/main/java/androidx/ui/benchmark/BenchmarksExtensions.kt b/ui/integration-tests/benchmark/src/main/java/androidx/ui/benchmark/BenchmarksExtensions.kt
index 6797ff0..6e593ff 100644
--- a/ui/integration-tests/benchmark/src/main/java/androidx/ui/benchmark/BenchmarksExtensions.kt
+++ b/ui/integration-tests/benchmark/src/main/java/androidx/ui/benchmark/BenchmarksExtensions.kt
@@ -16,40 +16,31 @@
 
 package androidx.ui.benchmark
 
-import android.app.Activity
 import android.view.View
-import androidx.benchmark.junit4.BenchmarkRule
-import androidx.benchmark.junit4.measureRepeated
-import androidx.compose.FrameManager
-import androidx.compose.disposeComposition
-import androidx.ui.test.AndroidTestCase
 import androidx.ui.test.ComposeTestCase
-import androidx.ui.test.TestCase
 import androidx.ui.test.ToggleableTestCase
-import androidx.ui.test.invalidateViews
-import androidx.ui.test.recomposeSyncAssert
-import androidx.ui.test.recomposeSyncAssertHadChanges
-import androidx.ui.test.recomposeSyncAssertNoChanges
-import androidx.ui.test.requestLayout
-import androidx.ui.test.runOnUiThreadSync
+import androidx.ui.test.assertNoPendingChanges
+import androidx.ui.test.benchmark.android.AndroidTestCase
+import androidx.ui.test.doFramesUntilNoChangesPending
+import androidx.ui.test.recomposeAssertHadChanges
 
 /**
  * Measures measure and layout performance of the given test case by toggling measure constraints.
  */
-fun BenchmarkRule.measureLayoutPerf(activity: Activity, testCase: TestCase) {
-    activity.runOnUiThreadSync {
-        testCase.runToFirstDraw()
+fun ComposeBenchmarkRule.benchmarkLayoutPerf(testCase: ComposeTestCase) {
+    runBenchmarkFor(testCase) {
+        doFramesUntilNoChangesPending()
 
-        val width = testCase.view.measuredWidth
-        val height = testCase.view.measuredHeight
+        val width = measuredWidth
+        val height = measuredHeight
         var widthSpec = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY)
         var heightSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY)
 
-        testCase.requestLayout()
-        testCase.measureWithSpec(widthSpec, heightSpec)
-        testCase.layout()
+        requestLayout()
+        measureWithSpec(widthSpec, heightSpec)
+        layout()
 
-        var lastWidth = testCase.view.measuredWidth
+        var lastWidth = measuredWidth
         var lastHeight: Int
         measureRepeated {
             runWithTimingDisabled {
@@ -65,14 +56,47 @@
                     View.MeasureSpec.makeMeasureSpec(lastWidth, View.MeasureSpec.EXACTLY)
                 heightSpec =
                     View.MeasureSpec.makeMeasureSpec(lastHeight, View.MeasureSpec.EXACTLY)
-                testCase.requestLayout()
+                requestLayout()
             }
-            testCase.measureWithSpec(widthSpec, heightSpec)
-            testCase.layout()
+            measureWithSpec(widthSpec, heightSpec)
+            layout()
         }
+    }
+}
 
-        if (testCase is ComposeTestCase) {
-            activity.disposeComposition()
+fun ComposeBenchmarkRule.benchmarkLayoutPerf(testCase: AndroidTestCase) {
+    runBenchmarkFor(testCase) {
+        doFrame()
+
+        val width = measuredWidth
+        val height = measuredHeight
+        var widthSpec = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY)
+        var heightSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY)
+
+        requestLayout()
+        measureWithSpec(widthSpec, heightSpec)
+        layout()
+
+        var lastWidth = measuredWidth
+        var lastHeight: Int
+        measureRepeated {
+            runWithTimingDisabled {
+                if (lastWidth == width) {
+                    lastWidth = width - 10
+                    lastHeight = height - 10
+                } else {
+
+                    lastWidth = width
+                    lastHeight = height
+                }
+                widthSpec =
+                    View.MeasureSpec.makeMeasureSpec(lastWidth, View.MeasureSpec.EXACTLY)
+                heightSpec =
+                    View.MeasureSpec.makeMeasureSpec(lastHeight, View.MeasureSpec.EXACTLY)
+                requestLayout()
+            }
+            measureWithSpec(widthSpec, heightSpec)
+            layout()
         }
     }
 }
@@ -80,23 +104,39 @@
 /**
  * Measures draw performance of the given test case by invalidating the view hierarchy.
  */
-fun BenchmarkRule.measureDrawPerf(activity: Activity, testCase: TestCase) {
-    activity.runOnUiThreadSync {
-        testCase.runToFirstDraw()
+fun ComposeBenchmarkRule.benchmarkDrawPerf(testCase: AndroidTestCase) {
+    runBenchmarkFor(testCase) {
+        doFrame()
 
         measureRepeated {
             runWithTimingDisabled {
-                testCase.invalidateViews()
-                testCase.prepareDraw()
+                invalidateViews()
+                drawPrepare()
             }
-            testCase.draw()
+            draw()
             runWithTimingDisabled {
-                testCase.finishDraw()
+                drawFinish()
             }
         }
+    }
+}
 
-        if (testCase is ComposeTestCase) {
-            activity.disposeComposition()
+/**
+ * Measures draw performance of the given test case by invalidating the view hierarchy.
+ */
+fun ComposeBenchmarkRule.benchmarkDrawPerf(testCase: ComposeTestCase) {
+    runBenchmarkFor(testCase) {
+        doFramesUntilNoChangesPending()
+
+        measureRepeated {
+            runWithTimingDisabled {
+                invalidateViews()
+                drawPrepare()
+            }
+            draw()
+            runWithTimingDisabled {
+                drawFinish()
+            }
         }
     }
 }
@@ -104,16 +144,13 @@
 /**
  * Measures the time of the first composition of the given compose test case.
  */
-fun BenchmarkRule.measureFirstCompose(
-    activity: Activity,
-    testCase: ComposeTestCase
-) {
-    activity.runOnUiThreadSync {
+fun ComposeBenchmarkRule.benchmarkFirstCompose(testCase: ComposeTestCase) {
+    runBenchmarkFor(testCase) {
         measureRepeated {
-            testCase.setupContent(activity)
+            setupContent()
             runWithTimingDisabled {
-                testCase.recomposeSyncAssertNoChanges()
-                activity.disposeComposition()
+                assertNoPendingChanges()
+                disposeContent()
             }
         }
     }
@@ -122,13 +159,13 @@
 /**
  * Measures the time of the first set content of the given Android test case.
  */
-fun BenchmarkRule.measureFirstSetContent(
-    activity: Activity,
-    testCase: AndroidTestCase
-) {
-    activity.runOnUiThreadSync {
+fun ComposeBenchmarkRule.benchmarkFirstSetContent(testCase: AndroidTestCase) {
+    runBenchmarkFor(testCase) {
         measureRepeated {
-            testCase.setupContent(activity)
+            setupContent()
+            runWithTimingDisabled {
+                disposeContent()
+            }
         }
     }
 }
@@ -136,24 +173,38 @@
 /**
  * Measures the time of the first measure of the given test case.
  */
-fun BenchmarkRule.measureFirstMeasure(
-    activity: Activity,
-    testCase: TestCase
-) {
-    activity.runOnUiThreadSync {
+fun ComposeBenchmarkRule.benchmarkFirstMeasure(testCase: ComposeTestCase) {
+    runBenchmarkFor(testCase) {
         measureRepeated {
             runWithTimingDisabled {
-                testCase.setupContent(activity)
-                testCase.requestLayout()
+                setupContent()
+                requestLayout()
             }
 
-            testCase.measure()
+            measure()
 
             runWithTimingDisabled {
-                if (testCase is ComposeTestCase) {
-                    testCase.recomposeSyncAssertNoChanges()
-                    activity.disposeComposition()
-                }
+                disposeContent()
+            }
+        }
+    }
+}
+
+/**
+ * Measures the time of the first measure of the given test case.
+ */
+fun ComposeBenchmarkRule.benchmarkFirstMeasure(testCase: AndroidTestCase) {
+    runBenchmarkFor(testCase) {
+        measureRepeated {
+            runWithTimingDisabled {
+                setupContent()
+                requestLayout()
+            }
+
+            measure()
+
+            runWithTimingDisabled {
+                disposeContent()
             }
         }
     }
@@ -162,25 +213,40 @@
 /**
  * Measures the time of the first layout of the given test case.
  */
-fun BenchmarkRule.measureFirstLayout(
-    activity: Activity,
-    testCase: TestCase
-) {
-    activity.runOnUiThreadSync {
+fun ComposeBenchmarkRule.benchmarkFirstLayout(testCase: ComposeTestCase) {
+    runBenchmarkFor(testCase) {
         measureRepeated {
             runWithTimingDisabled {
-                testCase.setupContent(activity)
-                testCase.requestLayout()
-                testCase.measure()
+                setupContent()
+                requestLayout()
+                measure()
             }
 
-            testCase.layout()
+            layout()
 
             runWithTimingDisabled {
-                if (testCase is ComposeTestCase) {
-                    testCase.recomposeSyncAssertNoChanges()
-                    activity.disposeComposition()
-                }
+                disposeContent()
+            }
+        }
+    }
+}
+
+/**
+ * Measures the time of the first layout of the given test case.
+ */
+fun ComposeBenchmarkRule.benchmarkFirstLayout(testCase: AndroidTestCase) {
+    runBenchmarkFor(testCase) {
+        measureRepeated {
+            runWithTimingDisabled {
+                setupContent()
+                requestLayout()
+                measure()
+            }
+
+            layout()
+
+            runWithTimingDisabled {
+                disposeContent()
             }
         }
     }
@@ -189,28 +255,46 @@
 /**
  * Measures the time of the first draw of the given test case.
  */
-fun BenchmarkRule.measureFirstDraw(
-    activity: Activity,
-    testCase: TestCase
-) {
-    activity.runOnUiThreadSync {
+fun ComposeBenchmarkRule.benchmarkFirstDraw(testCase: ComposeTestCase) {
+    runBenchmarkFor(testCase) {
         measureRepeated {
             runWithTimingDisabled {
-                testCase.setupContent(activity)
-                testCase.requestLayout()
-                testCase.measure()
-                testCase.layout()
-                testCase.prepareDraw()
+                setupContent()
+                requestLayout()
+                measure()
+                layout()
+                drawPrepare()
             }
 
-            testCase.draw()
+            draw()
 
             runWithTimingDisabled {
-                testCase.finishDraw()
-                if (testCase is ComposeTestCase) {
-                    testCase.recomposeSyncAssertNoChanges()
-                    activity.disposeComposition()
-                }
+                drawFinish()
+                disposeContent()
+            }
+        }
+    }
+}
+
+/**
+ * Measures the time of the first draw of the given test case.
+ */
+fun ComposeBenchmarkRule.benchmarkFirstDraw(testCase: AndroidTestCase) {
+    runBenchmarkFor(testCase) {
+        measureRepeated {
+            runWithTimingDisabled {
+                setupContent()
+                requestLayout()
+                measure()
+                layout()
+                drawPrepare()
+            }
+
+            draw()
+
+            runWithTimingDisabled {
+                drawFinish()
+                disposeContent()
             }
         }
     }
@@ -219,180 +303,160 @@
 /**
  *  Measures recomposition time of the hierarchy after changing a state.
  */
-fun <T> BenchmarkRule.toggleStateMeasureRecompose(
-    activity: Activity,
+fun <T> ComposeBenchmarkRule.toggleStateBenchmarkRecompose(
     testCase: T
 ) where T : ComposeTestCase, T : ToggleableTestCase {
-    activity.runOnUiThreadSync {
-        testCase.runToFirstDraw()
-        testCase.recomposeSyncAssertNoChanges()
+    runBenchmarkFor(testCase) {
+        doFramesUntilNoChangesPending()
 
         measureRepeated {
             runWithTimingDisabled {
                 testCase.toggleState()
             }
-            testCase.recomposeSyncAssertHadChanges()
+            recomposeAssertHadChanges()
+            assertNoPendingChanges()
         }
-        activity.disposeComposition()
     }
 }
 
 /**
  *  Measures measure time of the hierarchy after changing a state.
  */
-fun <T> BenchmarkRule.toggleStateMeasureMeasure(
-    activity: Activity,
+fun <T> ComposeBenchmarkRule.toggleStateBenchmarkMeasure(
     testCase: T,
-    toggleCausesRecompose: Boolean = true,
-    firstDrawCausesRecompose: Boolean = false
+    toggleCausesRecompose: Boolean = true
 ) where T : ComposeTestCase, T : ToggleableTestCase {
-    activity.runOnUiThreadSync {
-        runToFirstDraw(testCase, firstDrawCausesRecompose)
+    runBenchmarkFor(testCase) {
+        doFramesUntilNoChangesPending()
 
         measureRepeated {
             runWithTimingDisabled {
                 testCase.toggleState()
-                testCase.recomposeSyncAssert(toggleCausesRecompose)
-                testCase.requestLayout()
+                if (toggleCausesRecompose) {
+                    recomposeAssertHadChanges()
+                }
+                requestLayout()
+                assertNoPendingChanges()
             }
-            testCase.measure()
+            measure()
+            assertNoPendingChanges()
         }
-        activity.disposeComposition()
     }
 }
 
 /**
  *  Measures layout time of the hierarchy after changing a state.
  */
-fun <T> BenchmarkRule.toggleStateMeasureLayout(
-    activity: Activity,
+fun <T> ComposeBenchmarkRule.toggleStateBenchmarkLayout(
     testCase: T,
-    toggleCausesRecompose: Boolean = true,
-    firstDrawCausesRecompose: Boolean = false
+    toggleCausesRecompose: Boolean = true
 ) where T : ComposeTestCase, T : ToggleableTestCase {
-    activity.runOnUiThreadSync {
-        runToFirstDraw(testCase, firstDrawCausesRecompose)
+    runBenchmarkFor(testCase) {
+        doFramesUntilNoChangesPending()
 
         measureRepeated {
             runWithTimingDisabled {
                 testCase.toggleState()
-                testCase.recomposeSyncAssert(toggleCausesRecompose)
-                testCase.requestLayout()
-                testCase.measure()
+                if (toggleCausesRecompose) {
+                    recomposeAssertHadChanges()
+                }
+                requestLayout()
+                measure()
+                assertNoPendingChanges()
             }
-            testCase.layout()
+            layout()
+            assertNoPendingChanges()
         }
-        activity.disposeComposition()
     }
 }
 
 /**
  *  Measures draw time of the hierarchy after changing a state.
  */
-fun <T> BenchmarkRule.toggleStateMeasureDraw(
-    activity: Activity,
+fun <T> ComposeBenchmarkRule.toggleStateBenchmarkDraw(
     testCase: T,
-    toggleCausesRecompose: Boolean = true,
-    firstDrawCausesRecompose: Boolean = false
+    toggleCausesRecompose: Boolean = true
 ) where T : ComposeTestCase, T : ToggleableTestCase {
-    activity.runOnUiThreadSync {
-        runToFirstDraw(testCase, firstDrawCausesRecompose)
+    runBenchmarkFor(testCase) {
+        doFramesUntilNoChangesPending()
 
         measureRepeated {
             runWithTimingDisabled {
                 testCase.toggleState()
-                testCase.recomposeSyncAssert(toggleCausesRecompose)
-                testCase.requestLayout()
-                testCase.measure()
-                testCase.layout()
-                testCase.prepareDraw()
+                if (toggleCausesRecompose) {
+                    recomposeAssertHadChanges()
+                }
+                assertNoPendingChanges()
+                requestLayout()
+                measure()
+                layout()
+                drawPrepare()
             }
-            testCase.draw()
+            draw()
             runWithTimingDisabled {
-                testCase.finishDraw()
+                drawFinish()
             }
         }
-        activity.disposeComposition()
     }
 }
 
 /**
- * Runs first draw on the test case and runs recomposition. Some layout/draw cycles
- * cause recomposition changes ([firstDrawCausesRecompose]). Changes are expected only
- * when [firstDrawCausesRecompose] is `true`.
- */
-private fun <T> runToFirstDraw(
-    testCase: T,
-    firstDrawCausesRecompose: Boolean
-) where T : ComposeTestCase {
-    testCase.runToFirstDraw()
-    FrameManager.nextFrame()
-    testCase.recomposeSyncAssert(firstDrawCausesRecompose)
-}
-
-/**
  *  Measures measure time of the hierarchy after changing a state.
  */
-fun <T> BenchmarkRule.toggleStateMeasureMeasure(
-    activity: Activity,
+fun <T> ComposeBenchmarkRule.toggleStateBenchmarkMeasure(
     testCase: T
 ) where T : AndroidTestCase, T : ToggleableTestCase {
-    activity.runOnUiThreadSync {
-        testCase.runToFirstDraw()
+    runBenchmarkFor(testCase) {
+        doFrame()
 
         measureRepeated {
             runWithTimingDisabled {
                 testCase.toggleState()
             }
-            testCase.measure()
+            measure()
         }
-        activity.disposeComposition()
     }
 }
 
 /**
  *  Measures layout time of the hierarchy after changing a state.
  */
-fun <T> BenchmarkRule.toggleStateMeasureLayout(
-    activity: Activity,
+fun <T> ComposeBenchmarkRule.toggleStateBenchmarkLayout(
     testCase: T
 ) where T : AndroidTestCase, T : ToggleableTestCase {
-    activity.runOnUiThreadSync {
-        testCase.runToFirstDraw()
+    runBenchmarkFor(testCase) {
+        doFrame()
 
         measureRepeated {
             runWithTimingDisabled {
                 testCase.toggleState()
-                testCase.measure()
+                measure()
             }
-            testCase.layout()
+            layout()
         }
-        activity.disposeComposition()
     }
 }
 
 /**
  *  Measures draw time of the hierarchy after changing a state.
  */
-fun <T> BenchmarkRule.toggleStateMeasureDraw(
-    activity: Activity,
+fun <T> ComposeBenchmarkRule.toggleStateBenchmarkDraw(
     testCase: T
 ) where T : AndroidTestCase, T : ToggleableTestCase {
-    activity.runOnUiThreadSync {
-        testCase.runToFirstDraw()
+    runBenchmarkFor(testCase) {
+        doFrame()
 
         measureRepeated {
             runWithTimingDisabled {
                 testCase.toggleState()
-                testCase.measure()
-                testCase.layout()
-                testCase.prepareDraw()
+                measure()
+                layout()
+                drawPrepare()
             }
-            testCase.draw()
+            draw()
             runWithTimingDisabled {
-                testCase.finishDraw()
+                drawFinish()
             }
         }
-        activity.disposeComposition()
     }
 }
diff --git a/ui/integration-tests/benchmark/src/main/java/androidx/ui/benchmark/ComposeBenchmarkRule.kt b/ui/integration-tests/benchmark/src/main/java/androidx/ui/benchmark/ComposeBenchmarkRule.kt
new file mode 100644
index 0000000..3855697
--- /dev/null
+++ b/ui/integration-tests/benchmark/src/main/java/androidx/ui/benchmark/ComposeBenchmarkRule.kt
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.benchmark
+
+import android.app.Activity
+import androidx.benchmark.junit4.BenchmarkRule
+import androidx.benchmark.junit4.measureRepeated
+import androidx.test.rule.ActivityTestRule
+import androidx.ui.test.benchmark.android.AndroidTestCaseRunner
+import androidx.ui.test.ComposeBenchmarkScope
+import androidx.ui.test.ComposeTestCase
+import androidx.ui.test.DisableTransitions
+import androidx.ui.test.android.createAndroidComposeBenchmarkRunner
+import androidx.ui.test.benchmark.android.AndroidTestCase
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+
+/**
+ * Rule to be used to run Compose / Android benchmarks.
+ */
+// TODO(pavlis): Move this to a separate module, something like androidx.ui.test-benchmark
+class ComposeBenchmarkRule(
+    private val enableTransitions: Boolean = false
+) : TestRule {
+
+    private val activityTestRule = ActivityTestRule<Activity>(Activity::class.java)
+
+    val benchmarkRule = BenchmarkRule()
+
+    private val disableTransitionsRule = DisableTransitions()
+
+    override fun apply(base: Statement, description: Description?): Statement {
+        val statement = benchmarkRule.apply(
+            activityTestRule.apply(base, description), description!!)
+        if (!enableTransitions) {
+            return disableTransitionsRule.apply(statement, description)
+        }
+        return statement
+    }
+
+    /**
+     * Runs benchmark for the given [ComposeTestCase].
+     *
+     * Note that benchmark by default runs on the ui thread and disposes composition afterwards.
+     *
+     * @param givenTestCase The test case to be executed
+     * @param block The benchmark instruction to be performed over the given test case
+     */
+    fun runBenchmarkFor(givenTestCase: ComposeTestCase, block: ComposeBenchmarkScope.() -> Unit) {
+        require(givenTestCase !is AndroidTestCase) {
+            "Expected ${ComposeTestCase::class.simpleName}!"
+        }
+
+        activityTestRule.runOnUiThread {
+            // TODO(pavlis): Assert that there is no existing composition before we run benchmark
+            val runner = createAndroidComposeBenchmarkRunner(givenTestCase,
+                activityTestRule.activity)
+            try {
+                block(runner)
+            } finally {
+                runner.disposeContent()
+            }
+        }
+    }
+
+    /**
+     * Runs benchmark for the given [AndroidTestCase].
+     *
+     * Note that benchmark by default runs on the ui thread.
+     *
+     * @param givenTestCase The test case to be executed
+     * @param block The benchmark instruction to be performed over the given test case
+     */
+    fun runBenchmarkFor(givenTestCase: AndroidTestCase, block: AndroidTestCaseRunner.() -> Unit) {
+        require(givenTestCase !is ComposeTestCase) {
+            "Expected ${AndroidTestCase::class.simpleName}!"
+        }
+
+        activityTestRule.runOnUiThread {
+            val runner = AndroidTestCaseRunner(
+                givenTestCase,
+                activityTestRule.activity
+            )
+            block(runner)
+        }
+    }
+
+    /**
+     * Convenience proxy for [BenchmarkRule.measureRepeated].
+     */
+    fun measureRepeated(block: BenchmarkRule.Scope.() -> Unit) {
+        benchmarkRule.measureRepeated(block)
+    }
+}
\ No newline at end of file
diff --git a/ui/integration-tests/test/src/androidTest/java/androidx/ui/test/CheckboxesInRowsTest.kt b/ui/integration-tests/test/src/androidTest/java/androidx/ui/test/CheckboxesInRowsTest.kt
index 9d7f8a8..321e982 100644
--- a/ui/integration-tests/test/src/androidTest/java/androidx/ui/test/CheckboxesInRowsTest.kt
+++ b/ui/integration-tests/test/src/androidTest/java/androidx/ui/test/CheckboxesInRowsTest.kt
@@ -16,12 +16,8 @@
 
 package androidx.ui.test
 
-import android.app.Activity
 import androidx.test.filters.MediumTest
-import androidx.test.rule.ActivityTestRule
 import androidx.ui.test.cases.CheckboxesInRowsTestCase
-import androidx.ui.test.cases.RectsInColumnSharedModelTestCase
-import androidx.ui.test.cases.RectsInColumnTestCase
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -41,19 +37,20 @@
     }
 
     @get:Rule
-    val activityRule = ActivityTestRule(Activity::class.java)
-
-    @get:Rule
-    val disableAnimationRule = DisableTransitions()
+    val composeTestRule = createComposeRule(disableTransitions = true)
 
     @Test
     fun toggleRectangleColor_compose() {
-        activityRule.runOnUiThreadSync {
-            val testCase = CheckboxesInRowsTestCase(activityRule.activity,
-                numberOfCheckboxes)
-            runComposeTestWithStateToggleAndAssertRecompositions(testCase) {
+        val testCase = CheckboxesInRowsTestCase(numberOfCheckboxes)
+        composeTestRule
+            .forGivenTestCase(testCase)
+            .performTestWithEventsControl {
+                doFrame()
+                assertNoPendingChanges()
+                assertMeasureSizeIsPositive()
                 testCase.toggleState()
+                doFrame()
+                assertNoPendingChanges()
             }
-        }
     }
 }
\ No newline at end of file
diff --git a/ui/integration-tests/test/src/androidTest/java/androidx/ui/test/RectsInColumnSharedModelTest.kt b/ui/integration-tests/test/src/androidTest/java/androidx/ui/test/RectsInColumnSharedModelTest.kt
index cf724d8..6c7b876 100644
--- a/ui/integration-tests/test/src/androidTest/java/androidx/ui/test/RectsInColumnSharedModelTest.kt
+++ b/ui/integration-tests/test/src/androidTest/java/androidx/ui/test/RectsInColumnSharedModelTest.kt
@@ -16,9 +16,7 @@
 
 package androidx.ui.test
 
-import android.app.Activity
 import androidx.test.filters.MediumTest
-import androidx.test.rule.ActivityTestRule
 import androidx.ui.test.cases.RectsInColumnSharedModelTestCase
 import org.junit.Rule
 import org.junit.Test
@@ -39,19 +37,20 @@
     }
 
     @get:Rule
-    val activityRule = ActivityTestRule(Activity::class.java)
-
-    @get:Rule
-    val disableAnimationRule = DisableTransitions()
+    val composeTestRule = createComposeRule(disableTransitions = true)
 
     @Test
     fun toggleRectangleColor_compose() {
-        activityRule.runOnUiThreadSync {
-            val testCase = RectsInColumnSharedModelTestCase(activityRule.activity,
-                numberOfRectangles)
-            runComposeTestWithStateToggleAndAssertRecompositions(testCase) {
+        val testCase = RectsInColumnSharedModelTestCase(numberOfRectangles)
+        composeTestRule
+            .forGivenTestCase(testCase)
+            .performTestWithEventsControl {
+                doFrame()
+                assertNoPendingChanges()
+                assertMeasureSizeIsPositive()
                 testCase.toggleState()
+                doFrame()
+                assertNoPendingChanges()
             }
-        }
     }
 }
\ No newline at end of file
diff --git a/ui/integration-tests/test/src/androidTest/java/androidx/ui/test/RectsInColumnTest.kt b/ui/integration-tests/test/src/androidTest/java/androidx/ui/test/RectsInColumnTest.kt
index ba8c6e6..fc6174e 100644
--- a/ui/integration-tests/test/src/androidTest/java/androidx/ui/test/RectsInColumnTest.kt
+++ b/ui/integration-tests/test/src/androidTest/java/androidx/ui/test/RectsInColumnTest.kt
@@ -16,9 +16,7 @@
 
 package androidx.ui.test
 
-import android.app.Activity
 import androidx.test.filters.MediumTest
-import androidx.test.rule.ActivityTestRule
 import androidx.ui.test.cases.RectsInColumnTestCase
 import org.junit.Rule
 import org.junit.Test
@@ -39,18 +37,20 @@
     }
 
     @get:Rule
-    val activityRule = ActivityTestRule(Activity::class.java)
-
-    @get:Rule
-    val disableAnimationRule = DisableTransitions()
+    val composeTestRule = createComposeRule(disableTransitions = true)
 
     @Test
     fun toggleRectangleColor_compose() {
-        activityRule.runOnUiThreadSync {
-            val testCase = RectsInColumnTestCase(activityRule.activity, numberOfRectangles)
-            runComposeTestWithStateToggleAndAssertRecompositions(testCase) {
+        val testCase = RectsInColumnTestCase(numberOfRectangles)
+        composeTestRule
+            .forGivenTestCase(testCase)
+            .performTestWithEventsControl {
+                doFrame()
+                assertNoPendingChanges()
+                assertMeasureSizeIsPositive()
                 testCase.toggleState()
+                doFrame()
+                assertNoPendingChanges()
             }
-        }
     }
 }
\ No newline at end of file
diff --git a/ui/integration-tests/test/src/androidTest/java/androidx/ui/test/TableRecompositionTest.kt b/ui/integration-tests/test/src/androidTest/java/androidx/ui/test/TableRecompositionTest.kt
index dd34c51..a1a352c 100644
--- a/ui/integration-tests/test/src/androidTest/java/androidx/ui/test/TableRecompositionTest.kt
+++ b/ui/integration-tests/test/src/androidTest/java/androidx/ui/test/TableRecompositionTest.kt
@@ -16,18 +16,21 @@
 
 package androidx.ui.test
 
-import android.app.Activity
-import androidx.compose.FrameManager
 import androidx.test.filters.MediumTest
-import androidx.test.rule.ActivityTestRule
-import androidx.ui.test.cases.TableRecompositionTestCase
+import androidx.ui.core.dp
+import androidx.ui.foundation.ColoredRect
+import androidx.ui.graphics.Color
+import androidx.ui.layout.Padding
+import androidx.ui.layout.Table
+import androidx.ui.material.MaterialTheme
+import androidx.ui.material.surface.Surface
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
 
 /**
- * Ensure correctness of [TableRecompositionTestCase].
+ * Assert table recompositions.
  */
 @MediumTest
 @RunWith(Parameterized::class)
@@ -40,27 +43,28 @@
     }
 
     @get:Rule
-    val activityRule = ActivityTestRule(Activity::class.java)
-
-    @get:Rule
-    val disableAnimationRule = DisableTransitions()
+    val composeRule = createComposeRule(disableTransitions = true)
 
     @Test
     fun testTable_recomposition() {
-        activityRule.runOnUiThreadSync {
-            val testCase = TableRecompositionTestCase(activityRule.activity, numberOfCells)
-            testCase.runToFirstDraw()
-
-            testCase.assertMeasureSizeIsPositive()
-
-            FrameManager.nextFrame()
-            testCase.recomposeSyncAssertNoChanges()
-
-            testCase.measure()
-            testCase.layout()
-            testCase.drawSlow()
-            FrameManager.nextFrame()
-            testCase.recomposeSyncAssertNoChanges()
+        composeRule.forGivenContent {
+            MaterialTheme {
+                Surface {
+                    Table(columns = numberOfCells) {
+                        tableDecoration(overlay = false) { }
+                        repeat(numberOfCells) {
+                            tableRow {
+                                Padding(2.dp) {
+                                    ColoredRect(color = Color.Black, width = 100.dp, height = 50.dp)
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }.performTestWithEventsControl {
+            doFrame()
+            assertNoPendingChanges()
         }
     }
 }
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/core/TextBasicTestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/core/TextBasicTestCase.kt
index 338748f..f656b1d 100644
--- a/ui/integration-tests/test/src/main/java/androidx/ui/core/TextBasicTestCase.kt
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/core/TextBasicTestCase.kt
@@ -16,8 +16,7 @@
 
 package androidx.ui.core
 
-import android.app.Activity
-import android.view.ViewGroup
+import androidx.compose.Composable
 import androidx.ui.graphics.Color
 import androidx.ui.layout.ConstrainedBox
 import androidx.ui.layout.DpConstraints
@@ -30,11 +29,9 @@
  * The benchmark test case for [Text], where the input is a plain string.
  */
 class TextBasicTestCase(
-    activity: Activity,
-    private val textLength: Int,
-    private val randomTextGenerator: RandomTextGenerator
-) : ComposeTestCase(activity) {
-    private var text: String = ""
+    textLength: Int,
+    randomTextGenerator: RandomTextGenerator
+) : ComposeTestCase {
 
     /**
      * Text render has a word cache in the underlying system. To get a proper metric of its
@@ -44,16 +41,14 @@
      * is recreated. This helps to make sure that the text composable created later won't benefit
      * from the previous result.
      */
-    override fun setupContentInternal(activity: Activity): ViewGroup {
-        text = randomTextGenerator.nextParagraph(textLength)
-        return super.setupContentInternal(activity)
-    }
+    private val text = randomTextGenerator.nextParagraph(textLength)
 
-    override fun setComposeContent(activity: Activity) = activity.setContent {
+    @Composable
+    override fun emitContent() {
         Wrap {
             ConstrainedBox(constraints = DpConstraints.tightConstraintsForWidth(160.dp)) {
                 Text(text = text, style = TextStyle(color = Color.Black, fontSize = 8.sp))
             }
         }
-    }!!
+    }
 }
\ No newline at end of file
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/core/TextMultiStyleTestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/core/TextMultiStyleTestCase.kt
index 8bf9ce3..e3f4acc 100644
--- a/ui/integration-tests/test/src/main/java/androidx/ui/core/TextMultiStyleTestCase.kt
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/core/TextMultiStyleTestCase.kt
@@ -16,15 +16,13 @@
 
 package androidx.ui.core
 
-import android.app.Activity
-import android.view.ViewGroup
+import androidx.compose.Composable
 import androidx.ui.graphics.Color
 import androidx.ui.layout.ConstrainedBox
 import androidx.ui.layout.DpConstraints
 import androidx.ui.layout.Wrap
 import androidx.ui.test.ComposeTestCase
 import androidx.ui.test.RandomTextGenerator
-import androidx.ui.text.AnnotatedString
 import androidx.ui.text.TextStyle
 
 /**
@@ -32,32 +30,27 @@
  * on it.
  */
 class TextMultiStyleTestCase(
-    activity: Activity,
-    private val textLength: Int,
-    private val styleCount: Int,
-    private val randomTextGenerator: RandomTextGenerator
-) : ComposeTestCase(activity) {
-
-    private lateinit var text: AnnotatedString
+    textLength: Int,
+    styleCount: Int,
+    randomTextGenerator: RandomTextGenerator
+) : ComposeTestCase {
 
     /**
      * Trick to avoid the text word cache.
-     * @see TextBasicTestCase.setupContentInternal
+     * @see TextBasicTestCase.text
      */
-    override fun setupContentInternal(activity: Activity): ViewGroup {
-        text = randomTextGenerator.nextAnnotatedString(
-            length = textLength,
-            styleCount = styleCount,
-            hasMetricAffectingStyle = true
-        )
-        return super.setupContentInternal(activity)
-    }
+    private val text = randomTextGenerator.nextAnnotatedString(
+        length = textLength,
+        styleCount = styleCount,
+        hasMetricAffectingStyle = true
+    )
 
-    override fun setComposeContent(activity: Activity) = activity.setContent {
+    @Composable
+    override fun emitContent() {
         Wrap {
             ConstrainedBox(constraints = DpConstraints.tightConstraintsForWidth(160.dp)) {
                 Text(text = text, style = TextStyle(color = Color.Black, fontSize = 8.sp))
             }
         }
-    }!!
+    }
 }
\ No newline at end of file
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/core/TextWithSpanTestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/core/TextWithSpanTestCase.kt
index 163a0da..47ee220 100644
--- a/ui/integration-tests/test/src/main/java/androidx/ui/core/TextWithSpanTestCase.kt
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/core/TextWithSpanTestCase.kt
@@ -16,8 +16,7 @@
 
 package androidx.ui.core
 
-import android.app.Activity
-import android.view.ViewGroup
+import androidx.compose.Composable
 import androidx.ui.graphics.Color
 import androidx.ui.layout.ConstrainedBox
 import androidx.ui.layout.DpConstraints
@@ -30,26 +29,21 @@
  * The benchmark test case for [Text], where the input is some [Span]s with [TextStyle]s on it.
  */
 class TextWithSpanTestCase(
-    activity: Activity,
-    private val textLength: Int,
-    private val randomTextGenerator: RandomTextGenerator
-) : ComposeTestCase(activity) {
-
-    private lateinit var textPieces: List<Pair<String, TextStyle>>
+    textLength: Int,
+    randomTextGenerator: RandomTextGenerator
+) : ComposeTestCase {
 
     /**
      * Trick to avoid the text word cache.
-     * @see TextBasicTestCase.setupContentInternal
+     * @see TextBasicTestCase.text
      */
-    override fun setupContentInternal(activity: Activity): ViewGroup {
-        textPieces = randomTextGenerator.nextStyledWordList(
-            length = textLength,
-            hasMetricAffectingStyle = true
-        )
-        return super.setupContentInternal(activity)
-    }
+    private val textPieces = randomTextGenerator.nextStyledWordList(
+        length = textLength,
+        hasMetricAffectingStyle = true
+    )
 
-    override fun setComposeContent(activity: Activity) = activity.setContent {
+    @Composable
+    override fun emitContent() {
         Wrap {
             ConstrainedBox(constraints = DpConstraints.tightConstraintsForWidth(160.dp)) {
                 Text(style = TextStyle(color = Color.Black, fontSize = 8.sp)) {
@@ -59,5 +53,5 @@
                 }
             }
         }
-    }!!
+    }
 }
\ No newline at end of file
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/TestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/TestCase.kt
deleted file mode 100644
index a54f226..0000000
--- a/ui/integration-tests/test/src/main/java/androidx/ui/test/TestCase.kt
+++ /dev/null
@@ -1,319 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.ui.test
-
-import android.annotation.TargetApi
-import android.app.Activity
-import android.graphics.Bitmap
-import android.graphics.Canvas
-import android.graphics.Picture
-import android.graphics.RenderNode
-import android.os.Build
-import android.util.DisplayMetrics
-import android.view.View
-import android.view.ViewGroup
-import android.widget.ImageView
-import androidx.compose.CompositionContext
-import androidx.compose.FrameManager
-import androidx.ui.core.AndroidComposeView
-import androidx.ui.core.ComponentNode
-import androidx.ui.core.DrawNode
-import com.google.common.truth.Truth
-import org.junit.Assert
-
-abstract class TestCase(
-    val activity: Activity
-) {
-    private val screenWithSpec: Int
-    private val screenHeightSpec: Int
-    private val capture = if (SupportsRenderNode) RenderNodeCapture() else PictureCapture()
-    private var canvas: Canvas? = null
-
-    lateinit var view: ViewGroup
-        private set
-
-    init {
-        val displayMetrics = DisplayMetrics()
-        activity.windowManager.defaultDisplay.getMetrics(displayMetrics)
-        val height = displayMetrics.heightPixels
-        val width = displayMetrics.widthPixels
-
-        screenWithSpec = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.AT_MOST)
-        screenHeightSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.AT_MOST)
-    }
-
-    fun setupContent(activity: Activity) {
-        view = setupContentInternal(activity)
-    }
-
-    protected abstract fun setupContentInternal(activity: Activity): ViewGroup
-
-    /**
-     * Runs all the steps leading into drawing first pixels. Useful to get into the initial state
-     * before you benchmark a change of the state.
-     */
-    fun runToFirstDraw() {
-        setupContent(activity)
-        measure()
-        layout()
-        drawSlow()
-    }
-
-    /**
-     * To be run in benchmark.
-     */
-    fun measure() {
-        view.measure(screenWithSpec, screenHeightSpec)
-    }
-
-    /**
-     * To be run in benchmark.
-     */
-    fun measureWithSpec(widthSpec: Int, heightSpec: Int) {
-        view.measure(widthSpec, heightSpec)
-    }
-
-    /**
-     * Do not measure this in benchmark.
-     */
-    fun prepareDraw() {
-        canvas = capture.beginRecording(view.width, view.height)
-    }
-
-    /**
-     * To be run in benchmark. Call [prepareDraw] before and [finishDraw] after.
-     */
-    fun draw() {
-        view.draw(canvas)
-    }
-
-    /**
-     * Do not measure this in benchmark.
-     */
-    fun finishDraw() {
-        capture.endRecording()
-    }
-
-    /**
-     * Do not measure this in benchmark.
-     */
-    fun drawSlow() {
-        prepareDraw()
-        draw()
-        finishDraw()
-    }
-
-    /**
-     * To be run in benchmark.
-     */
-    fun layout() {
-        view.layout(view.left, view.top, view.right, view.bottom)
-    }
-
-    companion object {
-        private val SupportsRenderNode = Build.VERSION.SDK_INT >= 29
-    }
-}
-
-abstract class AndroidTestCase(
-    activity: Activity
-) : TestCase(activity) {
-
-    override fun setupContentInternal(activity: Activity) = createViewContent(activity)
-        .also { activity.setContentView(it) }
-
-    abstract fun createViewContent(activity: Activity): ViewGroup
-}
-
-abstract class ComposeTestCase(
-    activity: Activity
-) : TestCase(activity) {
-
-    lateinit var compositionContext: CompositionContext
-        private set
-
-    override fun setupContentInternal(activity: Activity): ViewGroup {
-        compositionContext = setComposeContent(activity)
-        FrameManager.nextFrame()
-        return findComposeView()!!
-    }
-
-    abstract fun setComposeContent(activity: Activity): CompositionContext
-}
-
-/**
- * Test case that can trigger a change of state.
- */
-interface ToggleableTestCase {
-    fun toggleState()
-}
-
-fun TestCase.assertMeasureSizeIsPositive() {
-    Truth.assertThat(view.measuredWidth).isAtLeast(1)
-    Truth.assertThat(view.measuredHeight).isAtLeast(1)
-}
-
-fun TestCase.invalidateViews() {
-    invalidateViews(view)
-}
-
-/**
- * Invalidate the layout so that measure() and layout() do something
- */
-fun TestCase.requestLayout() {
-    view.requestLayout()
-}
-
-private fun invalidateViews(view: View) {
-    view.invalidate()
-    if (view is ViewGroup) {
-        for (i in 0 until view.childCount) {
-            val child = view.getChildAt(i)
-            invalidateViews(child)
-        }
-    }
-    if (view is AndroidComposeView) {
-        invalidateComponentNodes(view.root)
-    }
-}
-
-private fun invalidateComponentNodes(node: ComponentNode) {
-    if (node is DrawNode) {
-        node.invalidate()
-    }
-    node.visitChildren { child ->
-        invalidateComponentNodes(child)
-    }
-}
-
-/**
- * Performs recomposition and asserts that there were some pending changes.
- */
-fun ComposeTestCase.recomposeSyncAssertHadChanges() {
-    recomposeSyncAssert(expectingChanges = true)
-}
-
-/**
- * Performs recomposition and asserts that there were no pending changes.
- */
-fun ComposeTestCase.recomposeSyncAssertNoChanges() {
-    recomposeSyncAssert(expectingChanges = false)
-}
-
-/**
- * Performs recomposition and asserts that there were or weren't pending changes based on
- * [expectingChanges].
- */
-fun ComposeTestCase.recomposeSyncAssert(expectingChanges: Boolean) {
-    val message = if (expectingChanges) {
-        "Expected pending changes on recomposition but there were none. Did " +
-                "you forget to call FrameManager.next()?"
-    } else {
-        "Expected no pending changes on recomposition but there were some."
-    }
-    val hadChanges = compositionContext.recomposeSync()
-    Assert.assertEquals(message, expectingChanges, hadChanges)
-}
-
-/**
- * This is only for tests debugging purposes.
- *
- * Draws the given view into [Picture] and presents it in [ImageView] in the given Activity. This is
- * useful for one time verification that your benchmark is producing excepted results and doing
- * useful work.
- */
-fun TestCase.capturePreviewPictureToActivity() {
-    val picture = Picture()
-    val canvas = picture.beginRecording(view.width, view.height)
-    view.draw(canvas)
-    picture.endRecording()
-    val imageView = ImageView(activity)
-    val bitmap: Bitmap
-    if (Build.VERSION.SDK_INT >= 28) {
-        bitmap = Bitmap.createBitmap(picture)
-    } else {
-        val width = picture.width.coerceAtLeast(1)
-        val height = picture.height.coerceAtLeast(1)
-        bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
-        Canvas(bitmap).drawPicture(picture)
-    }
-    imageView.setImageBitmap(bitmap)
-    activity.setContentView(imageView)
-}
-
-/**
- * Returns the first found [AndroidComposeView] in the content view hierarchy:
- *
- *     override fun setupContent(activity: Activity) {
- *         activity.setContent { ... }
- *         view = findComposeView()!!
- *         FrameManager.nextFrame()
- *     }
- */
-fun ComposeTestCase.findComposeView(): AndroidComposeView? {
-    return findComposeView(activity.findViewById(android.R.id.content))
-}
-
-private fun findComposeView(view: View): AndroidComposeView? {
-    if (view is AndroidComposeView) {
-        return view
-    }
-
-    if (view is ViewGroup) {
-        for (i in 0 until view.childCount) {
-            val composeView = findComposeView(view.getChildAt(i))
-            if (composeView != null) {
-                return composeView
-            }
-        }
-    }
-    return null
-}
-
-// We must separate the use of RenderNode so that it isn't referenced in any
-// way on platforms that don't have it. This extracts RenderNode use to a
-// potentially unloaded class, RenderNodeCapture.
-private interface DrawCapture {
-    fun beginRecording(width: Int, height: Int): Canvas
-    fun endRecording()
-}
-
-@TargetApi(Build.VERSION_CODES.Q)
-private class RenderNodeCapture : DrawCapture {
-    private val renderNode = RenderNode("Test")
-
-    override fun beginRecording(width: Int, height: Int): Canvas {
-        renderNode.setPosition(0, 0, width, height)
-        return renderNode.beginRecording()
-    }
-
-    override fun endRecording() {
-        renderNode.endRecording()
-    }
-}
-
-private class PictureCapture : DrawCapture {
-    private val picture = Picture()
-
-    override fun beginRecording(width: Int, height: Int): Canvas {
-        return picture.beginRecording(width, height)
-    }
-
-    override fun endRecording() {
-        picture.endRecording()
-    }
-}
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/TestExecutors.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/TestExecutors.kt
deleted file mode 100644
index 909a8e6..0000000
--- a/ui/integration-tests/test/src/main/java/androidx/ui/test/TestExecutors.kt
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.ui.test
-
-/**
- * Runs the given test case and toggle its state using the given lambda. Asserts that recomposition
- * happens and has changes and also asserts that the no more compositions is needed.
- */
-fun runComposeTestWithStateToggleAndAssertRecompositions(
-    testCase: ComposeTestCase,
-    toggleState: () -> Unit
-) {
-    testCase.runToFirstDraw()
-
-    testCase.assertMeasureSizeIsPositive()
-
-    testCase.recomposeSyncAssertNoChanges()
-
-    // Change state
-    toggleState.invoke()
-
-    // Recompose our changes
-    testCase.recomposeSyncAssertHadChanges()
-
-    // No other compositions should be pending
-    testCase.recomposeSyncAssertNoChanges()
-}
\ No newline at end of file
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/TestUtils.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/TestUtils.kt
deleted file mode 100644
index fa49907..0000000
--- a/ui/integration-tests/test/src/main/java/androidx/ui/test/TestUtils.kt
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.ui.test
-
-import android.app.Activity
-import androidx.test.rule.ActivityTestRule
-
-fun <T : Activity> ActivityTestRule<T>.runOnUiThreadSync(action: () -> Unit) {
-    // Workaround for lambda bug in IR
-    runOnUiThread(object : Runnable {
-        override fun run() {
-            action.invoke()
-        }
-    })
-}
-
-fun Activity.runOnUiThreadSync(action: () -> Unit) {
-    // Workaround for lambda bug in IR
-    runOnUiThread(object : Runnable {
-        override fun run() {
-            action.invoke()
-        }
-    })
-}
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/ToggleableTestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/ToggleableTestCase.kt
new file mode 100644
index 0000000..24abf2a
--- /dev/null
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/ToggleableTestCase.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.test
+
+/**
+ * Test case that allows to trigger a change of state. This is run multiple times by the benchmarks.
+ * So it needs to do back and forth changes (like check, un-check checkbox) or scroll back and forth
+ * to run indefinitely.
+ */
+interface ToggleableTestCase {
+    fun toggleState()
+}
\ No newline at end of file
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/benchmark/android/AndroidTestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/benchmark/android/AndroidTestCase.kt
new file mode 100644
index 0000000..25f10d0
--- /dev/null
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/benchmark/android/AndroidTestCase.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.test.benchmark.android
+
+import android.app.Activity
+import android.view.ViewGroup
+
+/**
+ * To be implemented to provide a test case that can be executed in benchmarks.
+ */
+interface AndroidTestCase {
+    fun getContent(activity: Activity): ViewGroup
+}
\ No newline at end of file
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/benchmark/android/AndroidTestCaseRunner.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/benchmark/android/AndroidTestCaseRunner.kt
new file mode 100644
index 0000000..e732976
--- /dev/null
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/benchmark/android/AndroidTestCaseRunner.kt
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.test.benchmark.android
+
+import android.R
+import android.annotation.TargetApi
+import android.app.Activity
+import android.graphics.Canvas
+import android.graphics.Picture
+import android.graphics.RenderNode
+import android.os.Build
+import android.util.DisplayMetrics
+import android.view.View
+import android.view.ViewGroup
+
+class AndroidTestCaseRunner(
+    private val testCase: AndroidTestCase,
+    private val activity: Activity
+) {
+
+    val measuredWidth: Int
+        get() = view!!.measuredWidth
+    val measuredHeight: Int
+        get() = view!!.measuredHeight
+
+    private var view: ViewGroup? = null
+
+    private val supportsRenderNode = Build.VERSION.SDK_INT >= 29
+
+    private val screenWithSpec: Int
+    private val screenHeightSpec: Int
+    private val capture = if (supportsRenderNode) RenderNodeCapture() else PictureCapture()
+    private var canvas: Canvas? = null
+
+    init {
+        val displayMetrics = DisplayMetrics()
+        activity.windowManager.defaultDisplay.getMetrics(displayMetrics)
+        val height = displayMetrics.heightPixels
+        val width = displayMetrics.widthPixels
+
+        screenWithSpec = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.AT_MOST)
+        screenHeightSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.AT_MOST)
+    }
+
+    fun setupContent() {
+        require(view == null) { "Content was already set!" }
+        view = setupContentInternal(activity)
+    }
+
+    private fun setupContentInternal(activity: Activity): ViewGroup {
+        return testCase.getContent(activity).also { activity.setContentView(it) }
+    }
+
+    fun measure() {
+        getView().measure(screenWithSpec, screenHeightSpec)
+    }
+
+    fun measureWithSpec(widthSpec: Int, heightSpec: Int) {
+        getView().measure(widthSpec, heightSpec)
+    }
+
+    fun drawPrepare() {
+        canvas = capture.beginRecording(getView().width, getView().height)
+    }
+
+    fun draw() {
+        getView().draw(canvas)
+    }
+
+    fun drawFinish() {
+        capture.endRecording()
+    }
+
+    fun requestLayout() {
+        getView().requestLayout()
+    }
+
+    fun layout() {
+        val view = getView()
+        view.layout(view.left, view.top, view.right, view.bottom)
+    }
+
+    fun doFrame() {
+        if (view == null) {
+            setupContent()
+        }
+
+        measure()
+        layout()
+        drawPrepare()
+        draw()
+        drawFinish()
+    }
+
+    fun invalidateViews() {
+        invalidateViews(getView())
+    }
+
+    fun disposeContent() {
+        if (view == null) {
+            // Already disposed or never created any content
+            return
+        }
+
+        // Clear the view
+        val rootView = activity.findViewById(R.id.content) as ViewGroup
+        rootView.removeAllViews()
+        // Important so we can set the content again.
+        view = null
+    }
+
+    private fun getView(): ViewGroup {
+        require(view != null) { "View was not set! Call setupContent first!" }
+        return view!!
+    }
+}
+
+private fun invalidateViews(view: View) {
+    view.invalidate()
+    if (view is ViewGroup) {
+        for (i in 0 until view.childCount) {
+            val child = view.getChildAt(i)
+            invalidateViews(child)
+        }
+    }
+}
+
+// We must separate the use of RenderNode so that it isn't referenced in any
+// way on platforms that don't have it. This extracts RenderNode use to a
+// potentially unloaded class, RenderNodeCapture.
+private interface DrawCapture {
+    fun beginRecording(width: Int, height: Int): Canvas
+    fun endRecording()
+}
+
+@TargetApi(Build.VERSION_CODES.Q)
+private class RenderNodeCapture : DrawCapture {
+    private val renderNode = RenderNode("Test")
+
+    override fun beginRecording(width: Int, height: Int): Canvas {
+        renderNode.setPosition(0, 0, width, height)
+        return renderNode.beginRecording()
+    }
+
+    override fun endRecording() {
+        renderNode.endRecording()
+    }
+}
+
+private class PictureCapture : DrawCapture {
+    private val picture = Picture()
+
+    override fun beginRecording(width: Int, height: Int): Canvas {
+        return picture.beginRecording(width, height)
+    }
+
+    override fun endRecording() {
+        picture.endRecording()
+    }
+}
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/BaseSimpleRadioButtonTestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/BaseSimpleRadioButtonTestCase.kt
index e854f7d1..f77c256 100644
--- a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/BaseSimpleRadioButtonTestCase.kt
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/BaseSimpleRadioButtonTestCase.kt
@@ -16,8 +16,6 @@
 
 package androidx.ui.test.cases
 
-import android.app.Activity
-import androidx.compose.FrameManager
 import androidx.compose.State
 import androidx.compose.state
 import androidx.compose.unaryPlus
@@ -26,9 +24,7 @@
 import androidx.ui.test.ComposeTestCase
 import androidx.ui.test.ToggleableTestCase
 
-abstract class BaseSimpleRadioButtonTestCase(
-    activity: Activity
-) : ComposeTestCase(activity), ToggleableTestCase {
+abstract class BaseSimpleRadioButtonTestCase : ComposeTestCase, ToggleableTestCase {
 
     private var state: State<Dp>? = null
 
@@ -46,6 +42,5 @@
                 10.dp
             }
         }
-        FrameManager.nextFrame()
     }
 }
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/CheckboxesInRowsTestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/CheckboxesInRowsTestCase.kt
index 05115b4..bf87f44 100644
--- a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/CheckboxesInRowsTestCase.kt
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/CheckboxesInRowsTestCase.kt
@@ -16,17 +16,12 @@
 
 package androidx.ui.test.cases
 
-import android.app.Activity
-import android.view.View
 import androidx.compose.Composable
-import androidx.compose.CompositionContext
-import androidx.compose.FrameManager
 import androidx.compose.State
 import androidx.compose.state
 import androidx.compose.unaryPlus
 import androidx.ui.core.Alignment
 import androidx.ui.core.Text
-import androidx.ui.core.setContent
 import androidx.ui.layout.Align
 import androidx.ui.layout.Column
 import androidx.ui.layout.FlexRow
@@ -35,20 +30,19 @@
 import androidx.ui.material.surface.Surface
 import androidx.ui.test.ComposeTestCase
 import androidx.ui.test.ToggleableTestCase
-import androidx.ui.test.findComposeView
 
 /**
  * Test case that puts the given amount of checkboxes into a column of rows and makes changes by
  * toggling the first checkbox.
  */
 class CheckboxesInRowsTestCase(
-    activity: Activity,
     private val amountOfCheckboxes: Int
-) : ComposeTestCase(activity), ToggleableTestCase {
+) : ComposeTestCase, ToggleableTestCase {
 
     private val states = mutableListOf<State<Boolean>>()
 
-    override fun setComposeContent(activity: Activity) = activity.setContent {
+    @Composable
+    override fun emitContent() {
         MaterialTheme {
             Surface {
                 Column {
@@ -67,12 +61,11 @@
                 }
             }
         }
-    }!!
+    }
 
     override fun toggleState() {
         val state = states.first()
         state.value = !state.value
-        FrameManager.nextFrame()
     }
 
     @Composable
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/NestedScrollerTestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/NestedScrollerTestCase.kt
index b9fc607..4ec539b 100644
--- a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/NestedScrollerTestCase.kt
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/NestedScrollerTestCase.kt
@@ -16,15 +16,12 @@
 
 package androidx.ui.test.cases
 
-import android.app.Activity
 import androidx.compose.Composable
-import androidx.compose.FrameManager
 import androidx.compose.memo
 import androidx.compose.unaryPlus
 import androidx.ui.core.Text
 import androidx.ui.core.WithDensity
 import androidx.ui.core.px
-import androidx.ui.core.setContent
 import androidx.ui.foundation.ColoredRect
 import androidx.ui.graphics.Color
 import androidx.ui.layout.Column
@@ -45,12 +42,11 @@
 /**
  * Test case that puts many horizontal scrollers in a vertical scroller
  */
-class NestedScrollerTestCase(
-    activity: Activity
-) : ComposeTestCase(activity), ToggleableTestCase {
+class NestedScrollerTestCase : ComposeTestCase, ToggleableTestCase {
     private val scrollerPosition = ScrollerPosition()
 
-    override fun setComposeContent(activity: Activity) = activity.setContent {
+    @Composable
+    override fun emitContent() {
         MaterialTheme {
             Surface {
                 VerticalScroller {
@@ -62,11 +58,10 @@
                 }
             }
         }
-    }!!
+    }
 
     override fun toggleState() {
         scrollerPosition.value = if (scrollerPosition.value == 0.px) 10.px else 0.px
-        FrameManager.nextFrame()
     }
 
     @Composable
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/RectsInColumnSharedModelTestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/RectsInColumnSharedModelTestCase.kt
index 0dcf66c..c7ddc466 100644
--- a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/RectsInColumnSharedModelTestCase.kt
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/RectsInColumnSharedModelTestCase.kt
@@ -16,11 +16,9 @@
 
 package androidx.ui.test.cases
 
-import android.app.Activity
-import androidx.compose.FrameManager
+import androidx.compose.Composable
 import androidx.compose.Model
 import androidx.ui.core.dp
-import androidx.ui.core.setContent
 import androidx.ui.foundation.ColoredRect
 import androidx.ui.graphics.Color
 import androidx.ui.layout.Column
@@ -39,13 +37,13 @@
  * that the whole loop has to be re-run when model changes.
  */
 class RectsInColumnSharedModelTestCase(
-    activity: Activity,
     private val amountOfRectangles: Int
-) : ComposeTestCase(activity), ToggleableTestCase {
+) : ComposeTestCase, ToggleableTestCase {
 
     private val model = RectanglesInColumnTestCaseColorModel(Color.Black)
 
-    override fun setComposeContent(activity: Activity) = activity.setContent {
+    @Composable
+    override fun emitContent() {
         MaterialTheme {
             Column {
                 repeat(amountOfRectangles) { i ->
@@ -57,7 +55,7 @@
                 }
             }
         }
-    }!!
+    }
 
     override fun toggleState() {
         if (model.color == Color.Magenta) {
@@ -65,6 +63,5 @@
         } else {
             model.color = Color.Magenta
         }
-        FrameManager.nextFrame()
     }
 }
\ No newline at end of file
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/RectsInColumnTestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/RectsInColumnTestCase.kt
index 95e34e6..186a69c 100644
--- a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/RectsInColumnTestCase.kt
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/RectsInColumnTestCase.kt
@@ -16,15 +16,12 @@
 
 package androidx.ui.test.cases
 
-import android.app.Activity
 import androidx.compose.Composable
-import androidx.compose.FrameManager
 import androidx.compose.State
 import androidx.compose.state
 import androidx.compose.unaryPlus
 import androidx.ui.foundation.ColoredRect
 import androidx.ui.core.dp
-import androidx.ui.core.setContent
 import androidx.ui.graphics.Color
 import androidx.ui.layout.Column
 import androidx.ui.material.MaterialTheme
@@ -39,13 +36,13 @@
  * Note: Each rectangle has its own model so changes should always affect only the first one.
  */
 class RectsInColumnTestCase(
-    activity: Activity,
     private val amountOfRectangles: Int
-) : ComposeTestCase(activity), ToggleableTestCase {
+) : ComposeTestCase, ToggleableTestCase {
 
     private val states = mutableListOf<State<Color>>()
 
-    override fun setComposeContent(activity: Activity) = activity.setContent {
+    @Composable
+    override fun emitContent() {
         MaterialTheme {
             Surface {
                 Column {
@@ -55,7 +52,7 @@
                 }
             }
         }
-    }!!
+    }
 
     override fun toggleState() {
         val state = states.first()
@@ -64,7 +61,6 @@
         } else {
             state.value = Color.Magenta
         }
-        FrameManager.nextFrame()
     }
 
     @Composable
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/ScrollerTestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/ScrollerTestCase.kt
index 8a342c1b..d13c17b8 100644
--- a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/ScrollerTestCase.kt
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/ScrollerTestCase.kt
@@ -16,16 +16,12 @@
 
 package androidx.ui.test.cases
 
-import android.app.Activity
 import androidx.compose.Composable
-import androidx.compose.CompositionContext
-import androidx.compose.FrameManager
 import androidx.compose.memo
 import androidx.compose.unaryPlus
 import androidx.ui.core.Draw
 import androidx.ui.core.dp
 import androidx.ui.core.px
-import androidx.ui.core.setContent
 import androidx.ui.core.toRect
 import androidx.ui.graphics.Color
 import androidx.ui.layout.Column
@@ -42,12 +38,11 @@
 /**
  * Test case that puts a large number of boxes in a column in a vertical scroller to force scrolling.
  */
-class ScrollerTestCase(
-    activity: Activity
-) : ComposeTestCase(activity), ToggleableTestCase {
+class ScrollerTestCase() : ComposeTestCase, ToggleableTestCase {
     private val scrollerPosition = ScrollerPosition()
 
-    override fun setComposeContent(activity: Activity): CompositionContext = activity.setContent {
+    @Composable
+    override fun emitContent() {
         VerticalScroller(
             scrollerPosition = scrollerPosition
         ) {
@@ -75,11 +70,10 @@
                 }
             }
         }
-    }!!
+    }
 
     override fun toggleState() {
         scrollerPosition.value = if (scrollerPosition.value == 0.px) 10.px else 0.px
-        FrameManager.nextFrame()
     }
 
     @Composable
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/SimpleRadioButton1TestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/SimpleRadioButton1TestCase.kt
index 1565ce0..a27e16a 100644
--- a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/SimpleRadioButton1TestCase.kt
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/SimpleRadioButton1TestCase.kt
@@ -16,9 +16,8 @@
 
 package androidx.ui.test.cases
 
-import android.app.Activity
+import androidx.compose.Composable
 import androidx.ui.core.dp
-import androidx.ui.core.setContent
 import androidx.ui.foundation.shape.DrawShape
 import androidx.ui.foundation.shape.border.Border
 import androidx.ui.foundation.shape.border.DrawBorder
@@ -26,11 +25,9 @@
 import androidx.ui.graphics.Color
 import androidx.ui.layout.Container
 
-class SimpleRadioButton1TestCase(
-    activity: Activity
-) : BaseSimpleRadioButtonTestCase(activity) {
-
-    override fun setComposeContent(activity: Activity) = activity.setContent {
+class SimpleRadioButton1TestCase : BaseSimpleRadioButtonTestCase() {
+    @Composable
+    override fun emitContent() {
         Container(width = 48.dp, height = 48.dp) {
             DrawBorder(CircleShape, Border(Color.Cyan, 1.dp))
             val innerSize = getInnerSize().value
@@ -38,5 +35,5 @@
                 DrawShape(CircleShape, Color.Cyan)
             }
         }
-    }!!
+    }
 }
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/SimpleRadioButton2TestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/SimpleRadioButton2TestCase.kt
index 268b133..9e9c622 100644
--- a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/SimpleRadioButton2TestCase.kt
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/SimpleRadioButton2TestCase.kt
@@ -16,13 +16,12 @@
 
 package androidx.ui.test.cases
 
-import android.app.Activity
+import androidx.compose.Composable
 import androidx.ui.core.Density
 import androidx.ui.core.Dp
 import androidx.ui.core.Px
 import androidx.ui.core.PxSize
 import androidx.ui.core.dp
-import androidx.ui.core.setContent
 import androidx.ui.core.withDensity
 import androidx.ui.engine.geometry.Offset
 import androidx.ui.engine.geometry.Outline
@@ -36,17 +35,15 @@
 import androidx.ui.graphics.Path
 import androidx.ui.layout.Container
 
-class SimpleRadioButton2TestCase(
-    activity: Activity
-) : BaseSimpleRadioButtonTestCase(activity) {
-
-    override fun setComposeContent(activity: Activity) = activity.setContent {
+class SimpleRadioButton2TestCase : BaseSimpleRadioButtonTestCase() {
+    @Composable
+    override fun emitContent() {
         Container(width = 48.dp, height = 48.dp) {
             DrawBorder(CircleShape, Border(Color.Cyan, 1.dp))
             val padding = (48.dp - getInnerSize().value) / 2
             DrawShape(PaddingShape(padding, CircleShape), Color.Cyan)
         }
-    }!!
+    }
 }
 
 private data class PaddingShape(val padding: Dp, val shape: Shape) : Shape {
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/SimpleRadioButton3TestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/SimpleRadioButton3TestCase.kt
index 522b303..a0c4360 100644
--- a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/SimpleRadioButton3TestCase.kt
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/SimpleRadioButton3TestCase.kt
@@ -16,25 +16,23 @@
 
 package androidx.ui.test.cases
 
-import android.app.Activity
+import androidx.compose.Composable
 import androidx.compose.memo
 import androidx.compose.unaryPlus
 import androidx.ui.core.Draw
 import androidx.ui.core.PxSize
 import androidx.ui.core.dp
 import androidx.ui.core.minDimension
-import androidx.ui.core.setContent
 import androidx.ui.engine.geometry.Offset
 import androidx.ui.graphics.Canvas
 import androidx.ui.graphics.Paint
 import androidx.ui.graphics.PaintingStyle
 import androidx.ui.layout.Container
 
-class SimpleRadioButton3TestCase(
-    activity: Activity
-) : BaseSimpleRadioButtonTestCase(activity) {
+class SimpleRadioButton3TestCase : BaseSimpleRadioButtonTestCase() {
 
-    override fun setComposeContent(activity: Activity) = activity.setContent {
+    @Composable
+    override fun emitContent() {
         Container(width = 48.dp, height = 48.dp) {
             val innerSize = getInnerSize()
             val borderPaint = +memo { Paint().apply { style = PaintingStyle.stroke } }
@@ -46,5 +44,5 @@
                 canvas.drawCircle(center, innerRadius, fillPaint)
             }
         }
-    }!!
+    }
 }
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/TableRecompositionTestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/TableRecompositionTestCase.kt
deleted file mode 100644
index 43870a5..0000000
--- a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/TableRecompositionTestCase.kt
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.ui.test.cases
-
-import android.app.Activity
-import androidx.compose.FrameManager
-import androidx.ui.foundation.ColoredRect
-import androidx.ui.core.dp
-import androidx.ui.core.setContent
-import androidx.ui.graphics.Color
-import androidx.ui.layout.Padding
-import androidx.ui.layout.Table
-import androidx.ui.material.MaterialTheme
-import androidx.ui.material.surface.Surface
-import androidx.ui.test.ComposeTestCase
-import androidx.ui.test.ToggleableTestCase
-
-/**
- * Test case that puts the given number of rectangles into a table layout and makes no changes.
- */
-class TableRecompositionTestCase(
-    activity: Activity,
-    private val numberOfCells: Int
-) : ComposeTestCase(activity), ToggleableTestCase {
-
-    override fun setComposeContent(activity: Activity) = activity.setContent {
-        MaterialTheme {
-            Surface {
-                Table(columns = numberOfCells) {
-                    tableDecoration(overlay = false) { }
-                    repeat(numberOfCells) {
-                        tableRow {
-                            Padding(2.dp) {
-                                ColoredRect(color = Color.Black, width = 100.dp, height = 50.dp)
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }!!
-
-    override fun toggleState() {
-        FrameManager.nextFrame()
-    }
-}
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/view/AndroidCheckboxesInLinearLayoutTestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/view/AndroidCheckboxesInLinearLayoutTestCase.kt
index 03bd230..1b80b5e 100644
--- a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/view/AndroidCheckboxesInLinearLayoutTestCase.kt
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/view/AndroidCheckboxesInLinearLayoutTestCase.kt
@@ -22,21 +22,20 @@
 import android.widget.CheckBox
 import android.widget.LinearLayout
 import android.widget.TextView
-import androidx.ui.test.AndroidTestCase
 import androidx.ui.test.R
+import androidx.ui.test.benchmark.android.AndroidTestCase
 import androidx.ui.test.cases.CheckboxesInRowsTestCase
 
 /**
  * Version of [CheckboxesInRowsTestCase] using Android views.
  */
 class AndroidCheckboxesInLinearLayoutTestCase(
-    activity: Activity,
     private val amountOfCheckboxes: Int
-) : AndroidTestCase(activity) {
+) : AndroidTestCase {
 
     private val checkboxes = mutableListOf<CheckBox>()
 
-    override fun createViewContent(activity: Activity): ViewGroup {
+    override fun getContent(activity: Activity): ViewGroup {
         val column = LinearLayout(activity)
         column.orientation = LinearLayout.VERTICAL
         column.layoutParams = ViewGroup.LayoutParams(
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/view/AndroidNestedScrollViewTestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/view/AndroidNestedScrollViewTestCase.kt
index 81c3554..bb22dc9 100644
--- a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/view/AndroidNestedScrollViewTestCase.kt
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/view/AndroidNestedScrollViewTestCase.kt
@@ -22,21 +22,19 @@
 import android.widget.HorizontalScrollView
 import androidx.ui.graphics.Color
 import androidx.ui.graphics.toArgb
-import androidx.ui.test.AndroidTestCase
 import androidx.ui.test.R
 import androidx.ui.test.ToggleableTestCase
+import androidx.ui.test.benchmark.android.AndroidTestCase
 import androidx.ui.test.cases.NestedScrollerTestCase
 import kotlin.random.Random
 
 /**
  * Version of [NestedScrollerTestCase] using Android views.
  */
-class AndroidNestedScrollViewTestCase(
-    activity: Activity
-) : AndroidTestCase(activity), ToggleableTestCase {
+class AndroidNestedScrollViewTestCase : AndroidTestCase, ToggleableTestCase {
     lateinit var firstScrollView: HorizontalScrollView
 
-    override fun createViewContent(activity: Activity): ViewGroup {
+    override fun getContent(activity: Activity): ViewGroup {
         val scrollView = activity.layoutInflater.inflate(R.layout.simple_store, null) as ViewGroup
         visitImages(scrollView) { view ->
             val color = Color(
diff --git a/ui/ui-foundation/src/androidTest/AndroidManifest.xml b/ui/ui-foundation/src/androidTest/AndroidManifest.xml
index bacf210..7dd676e 100644
--- a/ui/ui-foundation/src/androidTest/AndroidManifest.xml
+++ b/ui/ui-foundation/src/androidTest/AndroidManifest.xml
@@ -23,7 +23,7 @@
          tools:ignore="HardcodedDebugMode"
          tools:replace="android:debuggable">
         <activity
-            android:name="androidx.ui.test.android.DefaultTestActivity"
+            android:name="android.app.Activity"
             android:theme="@style/TestTheme"/>
     </application>
 </manifest>
diff --git a/ui/ui-framework/src/androidTest/AndroidManifest.xml b/ui/ui-framework/src/androidTest/AndroidManifest.xml
index 173fe35..65ac00625 100644
--- a/ui/ui-framework/src/androidTest/AndroidManifest.xml
+++ b/ui/ui-framework/src/androidTest/AndroidManifest.xml
@@ -22,7 +22,7 @@
             android:name="androidx.ui.framework.test.TestActivity"
             android:theme="@android:style/Theme.Material.NoActionBar.Fullscreen" />
         <activity
-            android:name="androidx.ui.test.android.DefaultTestActivity"
+            android:name="android.app.Activity"
             android:theme="@android:style/Theme.Material.NoActionBar.Fullscreen" />
     </application>
 </manifest>
diff --git a/ui/ui-material/src/androidTest/AndroidManifest.xml b/ui/ui-material/src/androidTest/AndroidManifest.xml
index e3bfeaf..cdce282 100644
--- a/ui/ui-material/src/androidTest/AndroidManifest.xml
+++ b/ui/ui-material/src/androidTest/AndroidManifest.xml
@@ -16,8 +16,6 @@
   -->
 <manifest package="androidx.ui.material" xmlns:android="http://schemas.android.com/apk/res/android">
      <application>
-        <activity
-            android:name="androidx.ui.test.android.DefaultTestActivity"
-            android:theme="@style/TestTheme"/>
+         <activity android:name="android.app.Activity" android:theme="@style/TestTheme"/>
     </application>
 </manifest>
diff --git a/ui/ui-test/api/0.1.0-dev01.txt b/ui/ui-test/api/0.1.0-dev01.txt
index ea248216..fb2dc68 100644
--- a/ui/ui-test/api/0.1.0-dev01.txt
+++ b/ui/ui-test/api/0.1.0-dev01.txt
@@ -36,7 +36,56 @@
     method public androidx.ui.test.CollectedSizes assertWidthEqualsTo(kotlin.jvm.functions.Function1<? super androidx.ui.core.DensityScope,androidx.ui.core.IntPx> expectedWidthPx);
   }
 
+  public interface ComposeBenchmarkScope extends androidx.ui.test.ComposeTestCaseScope {
+    method public void disposeContent();
+    method public void draw();
+    method public void drawFinish();
+    method public void drawPrepare();
+    method public void invalidateViews();
+    method public void measureWithSpec(int widthSpec, int heightSpec);
+    method public void requestLayout();
+    method public void setupContent();
+  }
+
+  public interface ComposeTestCase {
+    method public void getContent();
+  }
+
+  public interface ComposeTestCaseScope {
+    method public void capturePreviewPictureToActivity();
+    method public void doFrame();
+    method public void drawToBitmap();
+    method public boolean getDidLastRecomposeHaveChanges();
+    method public int getMeasuredHeight();
+    method public int getMeasuredWidth();
+    method public boolean hasPendingChanges();
+    method public void layout();
+    method public void measure();
+    method public void recompose();
+    property public abstract boolean didLastRecomposeHaveChanges;
+    property public abstract int measuredHeight;
+    property public abstract int measuredWidth;
+  }
+
+  public final class ComposeTestCaseScopeKt {
+    ctor public ComposeTestCaseScopeKt();
+    method public static void assertHasPendingChanges(androidx.ui.test.ComposeTestCaseScope);
+    method public static void assertLastRecomposeHadChanges(androidx.ui.test.ComposeTestCaseScope);
+    method public static void assertLastRecomposeHadNoChanges(androidx.ui.test.ComposeTestCaseScope);
+    method public static void assertMeasureSizeIsPositive(androidx.ui.test.ComposeTestCaseScope);
+    method public static void assertNoPendingChanges(androidx.ui.test.ComposeTestCaseScope);
+    method public static void doFramesAssertAllHadChangesExceptLastOne(androidx.ui.test.ComposeTestCaseScope, int numberOfFramesToBeStable);
+    method public static int doFramesUntilNoChangesPending(androidx.ui.test.ComposeTestCaseScope, int maxAmountOfFrames = 10);
+    method public static void recomposeAssertHadChanges(androidx.ui.test.ComposeTestCaseScope);
+  }
+
+  public interface ComposeTestCaseSetup {
+    method public void performTestWithEventsControl(kotlin.jvm.functions.Function1<? super androidx.ui.test.ComposeTestCaseScope,kotlin.Unit> block);
+  }
+
   public interface ComposeTestRule extends org.junit.rules.TestRule {
+    method public androidx.ui.test.ComposeTestCaseSetup forGivenContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
+    method public androidx.ui.test.ComposeTestCaseSetup forGivenTestCase(androidx.ui.test.ComposeTestCase testCase);
     method public androidx.ui.core.Density getDensity();
     method public android.util.DisplayMetrics getDisplayMetrics();
     method public void runOnUiThread(kotlin.jvm.functions.Function0<kotlin.Unit> action);
@@ -106,16 +155,52 @@
 
 package androidx.ui.test.android {
 
+  public final class AndroidComposeTestCaseRunner implements androidx.ui.test.ComposeBenchmarkScope androidx.ui.test.ComposeTestCaseScope {
+    ctor public AndroidComposeTestCaseRunner(androidx.ui.test.ComposeTestCase testCase, android.app.Activity activity);
+    method public void capturePreviewPictureToActivity();
+    method public void disposeContent();
+    method public void doFrame();
+    method public void draw();
+    method public void drawFinish();
+    method public void drawPrepare();
+    method public void drawToBitmap();
+    method public boolean getDidLastRecomposeHaveChanges();
+    method public int getMeasuredHeight();
+    method public int getMeasuredWidth();
+    method public boolean hasPendingChanges();
+    method public void invalidateViews();
+    method public void layout();
+    method public void measure();
+    method public void measureWithSpec(int widthSpec, int heightSpec);
+    method public void recompose();
+    method public void requestLayout();
+    method public void setupContent();
+    property public boolean didLastRecomposeHaveChanges;
+    property public int measuredHeight;
+    property public int measuredWidth;
+  }
+
+  public final class AndroidComposeTestCaseRunnerKt {
+    ctor public AndroidComposeTestCaseRunnerKt();
+  }
+
+  public final class AndroidComposeTestCaseSetup implements androidx.ui.test.ComposeTestCaseSetup {
+    ctor public AndroidComposeTestCaseSetup(androidx.ui.test.ComposeTestRule testRule, androidx.ui.test.ComposeTestCase testCase, android.app.Activity activity);
+    method public void performTestWithEventsControl(kotlin.jvm.functions.Function1<? super androidx.ui.test.ComposeTestCaseScope,kotlin.Unit> block);
+  }
+
   public final class AndroidComposeTestRule implements androidx.ui.test.ComposeTestRule {
     ctor public AndroidComposeTestRule(boolean disableTransitions, boolean shouldThrowOnRecomposeTimeout);
     ctor public AndroidComposeTestRule();
     method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description? description);
-    method public androidx.test.rule.ActivityTestRule<androidx.ui.test.android.DefaultTestActivity> getActivityTestRule();
+    method public androidx.ui.test.ComposeTestCaseSetup forGivenContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
+    method public androidx.ui.test.ComposeTestCaseSetup forGivenTestCase(androidx.ui.test.ComposeTestCase testCase);
+    method public androidx.test.rule.ActivityTestRule<android.app.Activity> getActivityTestRule();
     method public androidx.ui.core.Density getDensity();
     method public android.util.DisplayMetrics getDisplayMetrics();
     method public void runOnUiThread(kotlin.jvm.functions.Function0<kotlin.Unit> action);
     method public void setContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
-    property public final androidx.test.rule.ActivityTestRule<androidx.ui.test.android.DefaultTestActivity> activityTestRule;
+    property public final androidx.test.rule.ActivityTestRule<android.app.Activity> activityTestRule;
     property public androidx.ui.core.Density density;
     property public android.util.DisplayMetrics displayMetrics;
   }
@@ -125,13 +210,6 @@
     method public void evaluate();
   }
 
-  public final class DefaultTestActivity extends android.app.Activity {
-    ctor public DefaultTestActivity();
-    method public java.util.concurrent.CountDownLatch getHasFocusLatch();
-    method public void setHasFocusLatch(java.util.concurrent.CountDownLatch p);
-    property public final java.util.concurrent.CountDownLatch hasFocusLatch;
-  }
-
 }
 
 package androidx.ui.test.android.fake {
diff --git a/ui/ui-test/api/0.1.0-dev03.txt b/ui/ui-test/api/0.1.0-dev03.txt
index eb9c996..2d7dd82 100644
--- a/ui/ui-test/api/0.1.0-dev03.txt
+++ b/ui/ui-test/api/0.1.0-dev03.txt
@@ -36,7 +36,56 @@
     method public androidx.ui.test.CollectedSizes assertWidthEqualsTo(kotlin.jvm.functions.Function1<? super androidx.ui.core.DensityScope,androidx.ui.core.IntPx> expectedWidthPx);
   }
 
+  public interface ComposeBenchmarkScope extends androidx.ui.test.ComposeExecutionControl {
+    method public void disposeContent();
+    method public void draw();
+    method public void drawFinish();
+    method public void drawPrepare();
+    method public void invalidateViews();
+    method public void measureWithSpec(int widthSpec, int heightSpec);
+    method public void requestLayout();
+    method public void setupContent();
+  }
+
+  public interface ComposeExecutionControl {
+    method public void capturePreviewPictureToActivity();
+    method public void doFrame();
+    method public void drawToBitmap();
+    method public boolean getDidLastRecomposeHaveChanges();
+    method public int getMeasuredHeight();
+    method public int getMeasuredWidth();
+    method public boolean hasPendingChanges();
+    method public void layout();
+    method public void measure();
+    method public void recompose();
+    property public abstract boolean didLastRecomposeHaveChanges;
+    property public abstract int measuredHeight;
+    property public abstract int measuredWidth;
+  }
+
+  public final class ComposeExecutionControlKt {
+    ctor public ComposeExecutionControlKt();
+    method public static void assertHasPendingChanges(androidx.ui.test.ComposeExecutionControl);
+    method public static void assertLastRecomposeHadChanges(androidx.ui.test.ComposeExecutionControl);
+    method public static void assertLastRecomposeHadNoChanges(androidx.ui.test.ComposeExecutionControl);
+    method public static void assertMeasureSizeIsPositive(androidx.ui.test.ComposeExecutionControl);
+    method public static void assertNoPendingChanges(androidx.ui.test.ComposeExecutionControl);
+    method public static void doFramesAssertAllHadChangesExceptLastOne(androidx.ui.test.ComposeExecutionControl, int numberOfFramesToBeStable);
+    method public static int doFramesUntilNoChangesPending(androidx.ui.test.ComposeExecutionControl, int maxAmountOfFrames = 10);
+    method public static void recomposeAssertHadChanges(androidx.ui.test.ComposeExecutionControl);
+  }
+
+  public interface ComposeTestCase {
+    method public void emitContent();
+  }
+
+  public interface ComposeTestCaseSetup {
+    method public void performTestWithEventsControl(kotlin.jvm.functions.Function1<? super androidx.ui.test.ComposeExecutionControl,kotlin.Unit> block);
+  }
+
   public interface ComposeTestRule extends org.junit.rules.TestRule {
+    method public androidx.ui.test.ComposeTestCaseSetup forGivenContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
+    method public androidx.ui.test.ComposeTestCaseSetup forGivenTestCase(androidx.ui.test.ComposeTestCase testCase);
     method public androidx.ui.core.Density getDensity();
     method public android.util.DisplayMetrics getDisplayMetrics();
     method public void runOnUiThread(kotlin.jvm.functions.Function0<kotlin.Unit> action);
@@ -106,16 +155,28 @@
 
 package androidx.ui.test.android {
 
+  public final class AndroidComposeTestCaseRunnerKt {
+    ctor public AndroidComposeTestCaseRunnerKt();
+    method public static androidx.ui.test.ComposeBenchmarkScope createAndroidComposeBenchmarkRunner(androidx.ui.test.ComposeTestCase testCase, android.app.Activity activity);
+  }
+
+  public final class AndroidComposeTestCaseSetup implements androidx.ui.test.ComposeTestCaseSetup {
+    ctor public AndroidComposeTestCaseSetup(androidx.ui.test.ComposeTestRule testRule, androidx.ui.test.ComposeTestCase testCase, android.app.Activity activity);
+    method public void performTestWithEventsControl(kotlin.jvm.functions.Function1<? super androidx.ui.test.ComposeExecutionControl,kotlin.Unit> block);
+  }
+
   public final class AndroidComposeTestRule implements androidx.ui.test.ComposeTestRule {
     ctor public AndroidComposeTestRule(boolean disableTransitions, boolean shouldThrowOnRecomposeTimeout);
     ctor public AndroidComposeTestRule();
     method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description? description);
-    method public androidx.test.rule.ActivityTestRule<androidx.ui.test.android.DefaultTestActivity> getActivityTestRule();
+    method public androidx.ui.test.ComposeTestCaseSetup forGivenContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
+    method public androidx.ui.test.ComposeTestCaseSetup forGivenTestCase(androidx.ui.test.ComposeTestCase testCase);
+    method public androidx.test.rule.ActivityTestRule<android.app.Activity> getActivityTestRule();
     method public androidx.ui.core.Density getDensity();
     method public android.util.DisplayMetrics getDisplayMetrics();
     method public void runOnUiThread(kotlin.jvm.functions.Function0<kotlin.Unit> action);
     method public void setContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
-    property public final androidx.test.rule.ActivityTestRule<androidx.ui.test.android.DefaultTestActivity> activityTestRule;
+    property public final androidx.test.rule.ActivityTestRule<android.app.Activity> activityTestRule;
     property public androidx.ui.core.Density density;
     property public android.util.DisplayMetrics displayMetrics;
   }
@@ -125,13 +186,6 @@
     method public void evaluate();
   }
 
-  public final class DefaultTestActivity extends android.app.Activity {
-    ctor public DefaultTestActivity();
-    method public java.util.concurrent.CountDownLatch getHasFocusLatch();
-    method public void setHasFocusLatch(java.util.concurrent.CountDownLatch p);
-    property public final java.util.concurrent.CountDownLatch hasFocusLatch;
-  }
-
 }
 
 package androidx.ui.test.android.fake {
diff --git a/ui/ui-test/api/current.txt b/ui/ui-test/api/current.txt
index eb9c996..2d7dd82 100644
--- a/ui/ui-test/api/current.txt
+++ b/ui/ui-test/api/current.txt
@@ -36,7 +36,56 @@
     method public androidx.ui.test.CollectedSizes assertWidthEqualsTo(kotlin.jvm.functions.Function1<? super androidx.ui.core.DensityScope,androidx.ui.core.IntPx> expectedWidthPx);
   }
 
+  public interface ComposeBenchmarkScope extends androidx.ui.test.ComposeExecutionControl {
+    method public void disposeContent();
+    method public void draw();
+    method public void drawFinish();
+    method public void drawPrepare();
+    method public void invalidateViews();
+    method public void measureWithSpec(int widthSpec, int heightSpec);
+    method public void requestLayout();
+    method public void setupContent();
+  }
+
+  public interface ComposeExecutionControl {
+    method public void capturePreviewPictureToActivity();
+    method public void doFrame();
+    method public void drawToBitmap();
+    method public boolean getDidLastRecomposeHaveChanges();
+    method public int getMeasuredHeight();
+    method public int getMeasuredWidth();
+    method public boolean hasPendingChanges();
+    method public void layout();
+    method public void measure();
+    method public void recompose();
+    property public abstract boolean didLastRecomposeHaveChanges;
+    property public abstract int measuredHeight;
+    property public abstract int measuredWidth;
+  }
+
+  public final class ComposeExecutionControlKt {
+    ctor public ComposeExecutionControlKt();
+    method public static void assertHasPendingChanges(androidx.ui.test.ComposeExecutionControl);
+    method public static void assertLastRecomposeHadChanges(androidx.ui.test.ComposeExecutionControl);
+    method public static void assertLastRecomposeHadNoChanges(androidx.ui.test.ComposeExecutionControl);
+    method public static void assertMeasureSizeIsPositive(androidx.ui.test.ComposeExecutionControl);
+    method public static void assertNoPendingChanges(androidx.ui.test.ComposeExecutionControl);
+    method public static void doFramesAssertAllHadChangesExceptLastOne(androidx.ui.test.ComposeExecutionControl, int numberOfFramesToBeStable);
+    method public static int doFramesUntilNoChangesPending(androidx.ui.test.ComposeExecutionControl, int maxAmountOfFrames = 10);
+    method public static void recomposeAssertHadChanges(androidx.ui.test.ComposeExecutionControl);
+  }
+
+  public interface ComposeTestCase {
+    method public void emitContent();
+  }
+
+  public interface ComposeTestCaseSetup {
+    method public void performTestWithEventsControl(kotlin.jvm.functions.Function1<? super androidx.ui.test.ComposeExecutionControl,kotlin.Unit> block);
+  }
+
   public interface ComposeTestRule extends org.junit.rules.TestRule {
+    method public androidx.ui.test.ComposeTestCaseSetup forGivenContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
+    method public androidx.ui.test.ComposeTestCaseSetup forGivenTestCase(androidx.ui.test.ComposeTestCase testCase);
     method public androidx.ui.core.Density getDensity();
     method public android.util.DisplayMetrics getDisplayMetrics();
     method public void runOnUiThread(kotlin.jvm.functions.Function0<kotlin.Unit> action);
@@ -106,16 +155,28 @@
 
 package androidx.ui.test.android {
 
+  public final class AndroidComposeTestCaseRunnerKt {
+    ctor public AndroidComposeTestCaseRunnerKt();
+    method public static androidx.ui.test.ComposeBenchmarkScope createAndroidComposeBenchmarkRunner(androidx.ui.test.ComposeTestCase testCase, android.app.Activity activity);
+  }
+
+  public final class AndroidComposeTestCaseSetup implements androidx.ui.test.ComposeTestCaseSetup {
+    ctor public AndroidComposeTestCaseSetup(androidx.ui.test.ComposeTestRule testRule, androidx.ui.test.ComposeTestCase testCase, android.app.Activity activity);
+    method public void performTestWithEventsControl(kotlin.jvm.functions.Function1<? super androidx.ui.test.ComposeExecutionControl,kotlin.Unit> block);
+  }
+
   public final class AndroidComposeTestRule implements androidx.ui.test.ComposeTestRule {
     ctor public AndroidComposeTestRule(boolean disableTransitions, boolean shouldThrowOnRecomposeTimeout);
     ctor public AndroidComposeTestRule();
     method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description? description);
-    method public androidx.test.rule.ActivityTestRule<androidx.ui.test.android.DefaultTestActivity> getActivityTestRule();
+    method public androidx.ui.test.ComposeTestCaseSetup forGivenContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
+    method public androidx.ui.test.ComposeTestCaseSetup forGivenTestCase(androidx.ui.test.ComposeTestCase testCase);
+    method public androidx.test.rule.ActivityTestRule<android.app.Activity> getActivityTestRule();
     method public androidx.ui.core.Density getDensity();
     method public android.util.DisplayMetrics getDisplayMetrics();
     method public void runOnUiThread(kotlin.jvm.functions.Function0<kotlin.Unit> action);
     method public void setContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
-    property public final androidx.test.rule.ActivityTestRule<androidx.ui.test.android.DefaultTestActivity> activityTestRule;
+    property public final androidx.test.rule.ActivityTestRule<android.app.Activity> activityTestRule;
     property public androidx.ui.core.Density density;
     property public android.util.DisplayMetrics displayMetrics;
   }
@@ -125,13 +186,6 @@
     method public void evaluate();
   }
 
-  public final class DefaultTestActivity extends android.app.Activity {
-    ctor public DefaultTestActivity();
-    method public java.util.concurrent.CountDownLatch getHasFocusLatch();
-    method public void setHasFocusLatch(java.util.concurrent.CountDownLatch p);
-    property public final java.util.concurrent.CountDownLatch hasFocusLatch;
-  }
-
 }
 
 package androidx.ui.test.android.fake {
diff --git a/ui/ui-test/api/public_plus_experimental_0.1.0-dev01.txt b/ui/ui-test/api/public_plus_experimental_0.1.0-dev01.txt
index ea248216..fb2dc68 100644
--- a/ui/ui-test/api/public_plus_experimental_0.1.0-dev01.txt
+++ b/ui/ui-test/api/public_plus_experimental_0.1.0-dev01.txt
@@ -36,7 +36,56 @@
     method public androidx.ui.test.CollectedSizes assertWidthEqualsTo(kotlin.jvm.functions.Function1<? super androidx.ui.core.DensityScope,androidx.ui.core.IntPx> expectedWidthPx);
   }
 
+  public interface ComposeBenchmarkScope extends androidx.ui.test.ComposeTestCaseScope {
+    method public void disposeContent();
+    method public void draw();
+    method public void drawFinish();
+    method public void drawPrepare();
+    method public void invalidateViews();
+    method public void measureWithSpec(int widthSpec, int heightSpec);
+    method public void requestLayout();
+    method public void setupContent();
+  }
+
+  public interface ComposeTestCase {
+    method public void getContent();
+  }
+
+  public interface ComposeTestCaseScope {
+    method public void capturePreviewPictureToActivity();
+    method public void doFrame();
+    method public void drawToBitmap();
+    method public boolean getDidLastRecomposeHaveChanges();
+    method public int getMeasuredHeight();
+    method public int getMeasuredWidth();
+    method public boolean hasPendingChanges();
+    method public void layout();
+    method public void measure();
+    method public void recompose();
+    property public abstract boolean didLastRecomposeHaveChanges;
+    property public abstract int measuredHeight;
+    property public abstract int measuredWidth;
+  }
+
+  public final class ComposeTestCaseScopeKt {
+    ctor public ComposeTestCaseScopeKt();
+    method public static void assertHasPendingChanges(androidx.ui.test.ComposeTestCaseScope);
+    method public static void assertLastRecomposeHadChanges(androidx.ui.test.ComposeTestCaseScope);
+    method public static void assertLastRecomposeHadNoChanges(androidx.ui.test.ComposeTestCaseScope);
+    method public static void assertMeasureSizeIsPositive(androidx.ui.test.ComposeTestCaseScope);
+    method public static void assertNoPendingChanges(androidx.ui.test.ComposeTestCaseScope);
+    method public static void doFramesAssertAllHadChangesExceptLastOne(androidx.ui.test.ComposeTestCaseScope, int numberOfFramesToBeStable);
+    method public static int doFramesUntilNoChangesPending(androidx.ui.test.ComposeTestCaseScope, int maxAmountOfFrames = 10);
+    method public static void recomposeAssertHadChanges(androidx.ui.test.ComposeTestCaseScope);
+  }
+
+  public interface ComposeTestCaseSetup {
+    method public void performTestWithEventsControl(kotlin.jvm.functions.Function1<? super androidx.ui.test.ComposeTestCaseScope,kotlin.Unit> block);
+  }
+
   public interface ComposeTestRule extends org.junit.rules.TestRule {
+    method public androidx.ui.test.ComposeTestCaseSetup forGivenContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
+    method public androidx.ui.test.ComposeTestCaseSetup forGivenTestCase(androidx.ui.test.ComposeTestCase testCase);
     method public androidx.ui.core.Density getDensity();
     method public android.util.DisplayMetrics getDisplayMetrics();
     method public void runOnUiThread(kotlin.jvm.functions.Function0<kotlin.Unit> action);
@@ -106,16 +155,52 @@
 
 package androidx.ui.test.android {
 
+  public final class AndroidComposeTestCaseRunner implements androidx.ui.test.ComposeBenchmarkScope androidx.ui.test.ComposeTestCaseScope {
+    ctor public AndroidComposeTestCaseRunner(androidx.ui.test.ComposeTestCase testCase, android.app.Activity activity);
+    method public void capturePreviewPictureToActivity();
+    method public void disposeContent();
+    method public void doFrame();
+    method public void draw();
+    method public void drawFinish();
+    method public void drawPrepare();
+    method public void drawToBitmap();
+    method public boolean getDidLastRecomposeHaveChanges();
+    method public int getMeasuredHeight();
+    method public int getMeasuredWidth();
+    method public boolean hasPendingChanges();
+    method public void invalidateViews();
+    method public void layout();
+    method public void measure();
+    method public void measureWithSpec(int widthSpec, int heightSpec);
+    method public void recompose();
+    method public void requestLayout();
+    method public void setupContent();
+    property public boolean didLastRecomposeHaveChanges;
+    property public int measuredHeight;
+    property public int measuredWidth;
+  }
+
+  public final class AndroidComposeTestCaseRunnerKt {
+    ctor public AndroidComposeTestCaseRunnerKt();
+  }
+
+  public final class AndroidComposeTestCaseSetup implements androidx.ui.test.ComposeTestCaseSetup {
+    ctor public AndroidComposeTestCaseSetup(androidx.ui.test.ComposeTestRule testRule, androidx.ui.test.ComposeTestCase testCase, android.app.Activity activity);
+    method public void performTestWithEventsControl(kotlin.jvm.functions.Function1<? super androidx.ui.test.ComposeTestCaseScope,kotlin.Unit> block);
+  }
+
   public final class AndroidComposeTestRule implements androidx.ui.test.ComposeTestRule {
     ctor public AndroidComposeTestRule(boolean disableTransitions, boolean shouldThrowOnRecomposeTimeout);
     ctor public AndroidComposeTestRule();
     method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description? description);
-    method public androidx.test.rule.ActivityTestRule<androidx.ui.test.android.DefaultTestActivity> getActivityTestRule();
+    method public androidx.ui.test.ComposeTestCaseSetup forGivenContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
+    method public androidx.ui.test.ComposeTestCaseSetup forGivenTestCase(androidx.ui.test.ComposeTestCase testCase);
+    method public androidx.test.rule.ActivityTestRule<android.app.Activity> getActivityTestRule();
     method public androidx.ui.core.Density getDensity();
     method public android.util.DisplayMetrics getDisplayMetrics();
     method public void runOnUiThread(kotlin.jvm.functions.Function0<kotlin.Unit> action);
     method public void setContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
-    property public final androidx.test.rule.ActivityTestRule<androidx.ui.test.android.DefaultTestActivity> activityTestRule;
+    property public final androidx.test.rule.ActivityTestRule<android.app.Activity> activityTestRule;
     property public androidx.ui.core.Density density;
     property public android.util.DisplayMetrics displayMetrics;
   }
@@ -125,13 +210,6 @@
     method public void evaluate();
   }
 
-  public final class DefaultTestActivity extends android.app.Activity {
-    ctor public DefaultTestActivity();
-    method public java.util.concurrent.CountDownLatch getHasFocusLatch();
-    method public void setHasFocusLatch(java.util.concurrent.CountDownLatch p);
-    property public final java.util.concurrent.CountDownLatch hasFocusLatch;
-  }
-
 }
 
 package androidx.ui.test.android.fake {
diff --git a/ui/ui-test/api/public_plus_experimental_0.1.0-dev03.txt b/ui/ui-test/api/public_plus_experimental_0.1.0-dev03.txt
index eb9c996..2d7dd82 100644
--- a/ui/ui-test/api/public_plus_experimental_0.1.0-dev03.txt
+++ b/ui/ui-test/api/public_plus_experimental_0.1.0-dev03.txt
@@ -36,7 +36,56 @@
     method public androidx.ui.test.CollectedSizes assertWidthEqualsTo(kotlin.jvm.functions.Function1<? super androidx.ui.core.DensityScope,androidx.ui.core.IntPx> expectedWidthPx);
   }
 
+  public interface ComposeBenchmarkScope extends androidx.ui.test.ComposeExecutionControl {
+    method public void disposeContent();
+    method public void draw();
+    method public void drawFinish();
+    method public void drawPrepare();
+    method public void invalidateViews();
+    method public void measureWithSpec(int widthSpec, int heightSpec);
+    method public void requestLayout();
+    method public void setupContent();
+  }
+
+  public interface ComposeExecutionControl {
+    method public void capturePreviewPictureToActivity();
+    method public void doFrame();
+    method public void drawToBitmap();
+    method public boolean getDidLastRecomposeHaveChanges();
+    method public int getMeasuredHeight();
+    method public int getMeasuredWidth();
+    method public boolean hasPendingChanges();
+    method public void layout();
+    method public void measure();
+    method public void recompose();
+    property public abstract boolean didLastRecomposeHaveChanges;
+    property public abstract int measuredHeight;
+    property public abstract int measuredWidth;
+  }
+
+  public final class ComposeExecutionControlKt {
+    ctor public ComposeExecutionControlKt();
+    method public static void assertHasPendingChanges(androidx.ui.test.ComposeExecutionControl);
+    method public static void assertLastRecomposeHadChanges(androidx.ui.test.ComposeExecutionControl);
+    method public static void assertLastRecomposeHadNoChanges(androidx.ui.test.ComposeExecutionControl);
+    method public static void assertMeasureSizeIsPositive(androidx.ui.test.ComposeExecutionControl);
+    method public static void assertNoPendingChanges(androidx.ui.test.ComposeExecutionControl);
+    method public static void doFramesAssertAllHadChangesExceptLastOne(androidx.ui.test.ComposeExecutionControl, int numberOfFramesToBeStable);
+    method public static int doFramesUntilNoChangesPending(androidx.ui.test.ComposeExecutionControl, int maxAmountOfFrames = 10);
+    method public static void recomposeAssertHadChanges(androidx.ui.test.ComposeExecutionControl);
+  }
+
+  public interface ComposeTestCase {
+    method public void emitContent();
+  }
+
+  public interface ComposeTestCaseSetup {
+    method public void performTestWithEventsControl(kotlin.jvm.functions.Function1<? super androidx.ui.test.ComposeExecutionControl,kotlin.Unit> block);
+  }
+
   public interface ComposeTestRule extends org.junit.rules.TestRule {
+    method public androidx.ui.test.ComposeTestCaseSetup forGivenContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
+    method public androidx.ui.test.ComposeTestCaseSetup forGivenTestCase(androidx.ui.test.ComposeTestCase testCase);
     method public androidx.ui.core.Density getDensity();
     method public android.util.DisplayMetrics getDisplayMetrics();
     method public void runOnUiThread(kotlin.jvm.functions.Function0<kotlin.Unit> action);
@@ -106,16 +155,28 @@
 
 package androidx.ui.test.android {
 
+  public final class AndroidComposeTestCaseRunnerKt {
+    ctor public AndroidComposeTestCaseRunnerKt();
+    method public static androidx.ui.test.ComposeBenchmarkScope createAndroidComposeBenchmarkRunner(androidx.ui.test.ComposeTestCase testCase, android.app.Activity activity);
+  }
+
+  public final class AndroidComposeTestCaseSetup implements androidx.ui.test.ComposeTestCaseSetup {
+    ctor public AndroidComposeTestCaseSetup(androidx.ui.test.ComposeTestRule testRule, androidx.ui.test.ComposeTestCase testCase, android.app.Activity activity);
+    method public void performTestWithEventsControl(kotlin.jvm.functions.Function1<? super androidx.ui.test.ComposeExecutionControl,kotlin.Unit> block);
+  }
+
   public final class AndroidComposeTestRule implements androidx.ui.test.ComposeTestRule {
     ctor public AndroidComposeTestRule(boolean disableTransitions, boolean shouldThrowOnRecomposeTimeout);
     ctor public AndroidComposeTestRule();
     method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description? description);
-    method public androidx.test.rule.ActivityTestRule<androidx.ui.test.android.DefaultTestActivity> getActivityTestRule();
+    method public androidx.ui.test.ComposeTestCaseSetup forGivenContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
+    method public androidx.ui.test.ComposeTestCaseSetup forGivenTestCase(androidx.ui.test.ComposeTestCase testCase);
+    method public androidx.test.rule.ActivityTestRule<android.app.Activity> getActivityTestRule();
     method public androidx.ui.core.Density getDensity();
     method public android.util.DisplayMetrics getDisplayMetrics();
     method public void runOnUiThread(kotlin.jvm.functions.Function0<kotlin.Unit> action);
     method public void setContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
-    property public final androidx.test.rule.ActivityTestRule<androidx.ui.test.android.DefaultTestActivity> activityTestRule;
+    property public final androidx.test.rule.ActivityTestRule<android.app.Activity> activityTestRule;
     property public androidx.ui.core.Density density;
     property public android.util.DisplayMetrics displayMetrics;
   }
@@ -125,13 +186,6 @@
     method public void evaluate();
   }
 
-  public final class DefaultTestActivity extends android.app.Activity {
-    ctor public DefaultTestActivity();
-    method public java.util.concurrent.CountDownLatch getHasFocusLatch();
-    method public void setHasFocusLatch(java.util.concurrent.CountDownLatch p);
-    property public final java.util.concurrent.CountDownLatch hasFocusLatch;
-  }
-
 }
 
 package androidx.ui.test.android.fake {
diff --git a/ui/ui-test/api/public_plus_experimental_current.txt b/ui/ui-test/api/public_plus_experimental_current.txt
index eb9c996..2d7dd82 100644
--- a/ui/ui-test/api/public_plus_experimental_current.txt
+++ b/ui/ui-test/api/public_plus_experimental_current.txt
@@ -36,7 +36,56 @@
     method public androidx.ui.test.CollectedSizes assertWidthEqualsTo(kotlin.jvm.functions.Function1<? super androidx.ui.core.DensityScope,androidx.ui.core.IntPx> expectedWidthPx);
   }
 
+  public interface ComposeBenchmarkScope extends androidx.ui.test.ComposeExecutionControl {
+    method public void disposeContent();
+    method public void draw();
+    method public void drawFinish();
+    method public void drawPrepare();
+    method public void invalidateViews();
+    method public void measureWithSpec(int widthSpec, int heightSpec);
+    method public void requestLayout();
+    method public void setupContent();
+  }
+
+  public interface ComposeExecutionControl {
+    method public void capturePreviewPictureToActivity();
+    method public void doFrame();
+    method public void drawToBitmap();
+    method public boolean getDidLastRecomposeHaveChanges();
+    method public int getMeasuredHeight();
+    method public int getMeasuredWidth();
+    method public boolean hasPendingChanges();
+    method public void layout();
+    method public void measure();
+    method public void recompose();
+    property public abstract boolean didLastRecomposeHaveChanges;
+    property public abstract int measuredHeight;
+    property public abstract int measuredWidth;
+  }
+
+  public final class ComposeExecutionControlKt {
+    ctor public ComposeExecutionControlKt();
+    method public static void assertHasPendingChanges(androidx.ui.test.ComposeExecutionControl);
+    method public static void assertLastRecomposeHadChanges(androidx.ui.test.ComposeExecutionControl);
+    method public static void assertLastRecomposeHadNoChanges(androidx.ui.test.ComposeExecutionControl);
+    method public static void assertMeasureSizeIsPositive(androidx.ui.test.ComposeExecutionControl);
+    method public static void assertNoPendingChanges(androidx.ui.test.ComposeExecutionControl);
+    method public static void doFramesAssertAllHadChangesExceptLastOne(androidx.ui.test.ComposeExecutionControl, int numberOfFramesToBeStable);
+    method public static int doFramesUntilNoChangesPending(androidx.ui.test.ComposeExecutionControl, int maxAmountOfFrames = 10);
+    method public static void recomposeAssertHadChanges(androidx.ui.test.ComposeExecutionControl);
+  }
+
+  public interface ComposeTestCase {
+    method public void emitContent();
+  }
+
+  public interface ComposeTestCaseSetup {
+    method public void performTestWithEventsControl(kotlin.jvm.functions.Function1<? super androidx.ui.test.ComposeExecutionControl,kotlin.Unit> block);
+  }
+
   public interface ComposeTestRule extends org.junit.rules.TestRule {
+    method public androidx.ui.test.ComposeTestCaseSetup forGivenContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
+    method public androidx.ui.test.ComposeTestCaseSetup forGivenTestCase(androidx.ui.test.ComposeTestCase testCase);
     method public androidx.ui.core.Density getDensity();
     method public android.util.DisplayMetrics getDisplayMetrics();
     method public void runOnUiThread(kotlin.jvm.functions.Function0<kotlin.Unit> action);
@@ -106,16 +155,28 @@
 
 package androidx.ui.test.android {
 
+  public final class AndroidComposeTestCaseRunnerKt {
+    ctor public AndroidComposeTestCaseRunnerKt();
+    method public static androidx.ui.test.ComposeBenchmarkScope createAndroidComposeBenchmarkRunner(androidx.ui.test.ComposeTestCase testCase, android.app.Activity activity);
+  }
+
+  public final class AndroidComposeTestCaseSetup implements androidx.ui.test.ComposeTestCaseSetup {
+    ctor public AndroidComposeTestCaseSetup(androidx.ui.test.ComposeTestRule testRule, androidx.ui.test.ComposeTestCase testCase, android.app.Activity activity);
+    method public void performTestWithEventsControl(kotlin.jvm.functions.Function1<? super androidx.ui.test.ComposeExecutionControl,kotlin.Unit> block);
+  }
+
   public final class AndroidComposeTestRule implements androidx.ui.test.ComposeTestRule {
     ctor public AndroidComposeTestRule(boolean disableTransitions, boolean shouldThrowOnRecomposeTimeout);
     ctor public AndroidComposeTestRule();
     method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description? description);
-    method public androidx.test.rule.ActivityTestRule<androidx.ui.test.android.DefaultTestActivity> getActivityTestRule();
+    method public androidx.ui.test.ComposeTestCaseSetup forGivenContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
+    method public androidx.ui.test.ComposeTestCaseSetup forGivenTestCase(androidx.ui.test.ComposeTestCase testCase);
+    method public androidx.test.rule.ActivityTestRule<android.app.Activity> getActivityTestRule();
     method public androidx.ui.core.Density getDensity();
     method public android.util.DisplayMetrics getDisplayMetrics();
     method public void runOnUiThread(kotlin.jvm.functions.Function0<kotlin.Unit> action);
     method public void setContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
-    property public final androidx.test.rule.ActivityTestRule<androidx.ui.test.android.DefaultTestActivity> activityTestRule;
+    property public final androidx.test.rule.ActivityTestRule<android.app.Activity> activityTestRule;
     property public androidx.ui.core.Density density;
     property public android.util.DisplayMetrics displayMetrics;
   }
@@ -125,13 +186,6 @@
     method public void evaluate();
   }
 
-  public final class DefaultTestActivity extends android.app.Activity {
-    ctor public DefaultTestActivity();
-    method public java.util.concurrent.CountDownLatch getHasFocusLatch();
-    method public void setHasFocusLatch(java.util.concurrent.CountDownLatch p);
-    property public final java.util.concurrent.CountDownLatch hasFocusLatch;
-  }
-
 }
 
 package androidx.ui.test.android.fake {
diff --git a/ui/ui-test/api/restricted_0.1.0-dev01.txt b/ui/ui-test/api/restricted_0.1.0-dev01.txt
index ea248216..fb2dc68 100644
--- a/ui/ui-test/api/restricted_0.1.0-dev01.txt
+++ b/ui/ui-test/api/restricted_0.1.0-dev01.txt
@@ -36,7 +36,56 @@
     method public androidx.ui.test.CollectedSizes assertWidthEqualsTo(kotlin.jvm.functions.Function1<? super androidx.ui.core.DensityScope,androidx.ui.core.IntPx> expectedWidthPx);
   }
 
+  public interface ComposeBenchmarkScope extends androidx.ui.test.ComposeTestCaseScope {
+    method public void disposeContent();
+    method public void draw();
+    method public void drawFinish();
+    method public void drawPrepare();
+    method public void invalidateViews();
+    method public void measureWithSpec(int widthSpec, int heightSpec);
+    method public void requestLayout();
+    method public void setupContent();
+  }
+
+  public interface ComposeTestCase {
+    method public void getContent();
+  }
+
+  public interface ComposeTestCaseScope {
+    method public void capturePreviewPictureToActivity();
+    method public void doFrame();
+    method public void drawToBitmap();
+    method public boolean getDidLastRecomposeHaveChanges();
+    method public int getMeasuredHeight();
+    method public int getMeasuredWidth();
+    method public boolean hasPendingChanges();
+    method public void layout();
+    method public void measure();
+    method public void recompose();
+    property public abstract boolean didLastRecomposeHaveChanges;
+    property public abstract int measuredHeight;
+    property public abstract int measuredWidth;
+  }
+
+  public final class ComposeTestCaseScopeKt {
+    ctor public ComposeTestCaseScopeKt();
+    method public static void assertHasPendingChanges(androidx.ui.test.ComposeTestCaseScope);
+    method public static void assertLastRecomposeHadChanges(androidx.ui.test.ComposeTestCaseScope);
+    method public static void assertLastRecomposeHadNoChanges(androidx.ui.test.ComposeTestCaseScope);
+    method public static void assertMeasureSizeIsPositive(androidx.ui.test.ComposeTestCaseScope);
+    method public static void assertNoPendingChanges(androidx.ui.test.ComposeTestCaseScope);
+    method public static void doFramesAssertAllHadChangesExceptLastOne(androidx.ui.test.ComposeTestCaseScope, int numberOfFramesToBeStable);
+    method public static int doFramesUntilNoChangesPending(androidx.ui.test.ComposeTestCaseScope, int maxAmountOfFrames = 10);
+    method public static void recomposeAssertHadChanges(androidx.ui.test.ComposeTestCaseScope);
+  }
+
+  public interface ComposeTestCaseSetup {
+    method public void performTestWithEventsControl(kotlin.jvm.functions.Function1<? super androidx.ui.test.ComposeTestCaseScope,kotlin.Unit> block);
+  }
+
   public interface ComposeTestRule extends org.junit.rules.TestRule {
+    method public androidx.ui.test.ComposeTestCaseSetup forGivenContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
+    method public androidx.ui.test.ComposeTestCaseSetup forGivenTestCase(androidx.ui.test.ComposeTestCase testCase);
     method public androidx.ui.core.Density getDensity();
     method public android.util.DisplayMetrics getDisplayMetrics();
     method public void runOnUiThread(kotlin.jvm.functions.Function0<kotlin.Unit> action);
@@ -106,16 +155,52 @@
 
 package androidx.ui.test.android {
 
+  public final class AndroidComposeTestCaseRunner implements androidx.ui.test.ComposeBenchmarkScope androidx.ui.test.ComposeTestCaseScope {
+    ctor public AndroidComposeTestCaseRunner(androidx.ui.test.ComposeTestCase testCase, android.app.Activity activity);
+    method public void capturePreviewPictureToActivity();
+    method public void disposeContent();
+    method public void doFrame();
+    method public void draw();
+    method public void drawFinish();
+    method public void drawPrepare();
+    method public void drawToBitmap();
+    method public boolean getDidLastRecomposeHaveChanges();
+    method public int getMeasuredHeight();
+    method public int getMeasuredWidth();
+    method public boolean hasPendingChanges();
+    method public void invalidateViews();
+    method public void layout();
+    method public void measure();
+    method public void measureWithSpec(int widthSpec, int heightSpec);
+    method public void recompose();
+    method public void requestLayout();
+    method public void setupContent();
+    property public boolean didLastRecomposeHaveChanges;
+    property public int measuredHeight;
+    property public int measuredWidth;
+  }
+
+  public final class AndroidComposeTestCaseRunnerKt {
+    ctor public AndroidComposeTestCaseRunnerKt();
+  }
+
+  public final class AndroidComposeTestCaseSetup implements androidx.ui.test.ComposeTestCaseSetup {
+    ctor public AndroidComposeTestCaseSetup(androidx.ui.test.ComposeTestRule testRule, androidx.ui.test.ComposeTestCase testCase, android.app.Activity activity);
+    method public void performTestWithEventsControl(kotlin.jvm.functions.Function1<? super androidx.ui.test.ComposeTestCaseScope,kotlin.Unit> block);
+  }
+
   public final class AndroidComposeTestRule implements androidx.ui.test.ComposeTestRule {
     ctor public AndroidComposeTestRule(boolean disableTransitions, boolean shouldThrowOnRecomposeTimeout);
     ctor public AndroidComposeTestRule();
     method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description? description);
-    method public androidx.test.rule.ActivityTestRule<androidx.ui.test.android.DefaultTestActivity> getActivityTestRule();
+    method public androidx.ui.test.ComposeTestCaseSetup forGivenContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
+    method public androidx.ui.test.ComposeTestCaseSetup forGivenTestCase(androidx.ui.test.ComposeTestCase testCase);
+    method public androidx.test.rule.ActivityTestRule<android.app.Activity> getActivityTestRule();
     method public androidx.ui.core.Density getDensity();
     method public android.util.DisplayMetrics getDisplayMetrics();
     method public void runOnUiThread(kotlin.jvm.functions.Function0<kotlin.Unit> action);
     method public void setContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
-    property public final androidx.test.rule.ActivityTestRule<androidx.ui.test.android.DefaultTestActivity> activityTestRule;
+    property public final androidx.test.rule.ActivityTestRule<android.app.Activity> activityTestRule;
     property public androidx.ui.core.Density density;
     property public android.util.DisplayMetrics displayMetrics;
   }
@@ -125,13 +210,6 @@
     method public void evaluate();
   }
 
-  public final class DefaultTestActivity extends android.app.Activity {
-    ctor public DefaultTestActivity();
-    method public java.util.concurrent.CountDownLatch getHasFocusLatch();
-    method public void setHasFocusLatch(java.util.concurrent.CountDownLatch p);
-    property public final java.util.concurrent.CountDownLatch hasFocusLatch;
-  }
-
 }
 
 package androidx.ui.test.android.fake {
diff --git a/ui/ui-test/api/restricted_0.1.0-dev03.txt b/ui/ui-test/api/restricted_0.1.0-dev03.txt
index eb9c996..2d7dd82 100644
--- a/ui/ui-test/api/restricted_0.1.0-dev03.txt
+++ b/ui/ui-test/api/restricted_0.1.0-dev03.txt
@@ -36,7 +36,56 @@
     method public androidx.ui.test.CollectedSizes assertWidthEqualsTo(kotlin.jvm.functions.Function1<? super androidx.ui.core.DensityScope,androidx.ui.core.IntPx> expectedWidthPx);
   }
 
+  public interface ComposeBenchmarkScope extends androidx.ui.test.ComposeExecutionControl {
+    method public void disposeContent();
+    method public void draw();
+    method public void drawFinish();
+    method public void drawPrepare();
+    method public void invalidateViews();
+    method public void measureWithSpec(int widthSpec, int heightSpec);
+    method public void requestLayout();
+    method public void setupContent();
+  }
+
+  public interface ComposeExecutionControl {
+    method public void capturePreviewPictureToActivity();
+    method public void doFrame();
+    method public void drawToBitmap();
+    method public boolean getDidLastRecomposeHaveChanges();
+    method public int getMeasuredHeight();
+    method public int getMeasuredWidth();
+    method public boolean hasPendingChanges();
+    method public void layout();
+    method public void measure();
+    method public void recompose();
+    property public abstract boolean didLastRecomposeHaveChanges;
+    property public abstract int measuredHeight;
+    property public abstract int measuredWidth;
+  }
+
+  public final class ComposeExecutionControlKt {
+    ctor public ComposeExecutionControlKt();
+    method public static void assertHasPendingChanges(androidx.ui.test.ComposeExecutionControl);
+    method public static void assertLastRecomposeHadChanges(androidx.ui.test.ComposeExecutionControl);
+    method public static void assertLastRecomposeHadNoChanges(androidx.ui.test.ComposeExecutionControl);
+    method public static void assertMeasureSizeIsPositive(androidx.ui.test.ComposeExecutionControl);
+    method public static void assertNoPendingChanges(androidx.ui.test.ComposeExecutionControl);
+    method public static void doFramesAssertAllHadChangesExceptLastOne(androidx.ui.test.ComposeExecutionControl, int numberOfFramesToBeStable);
+    method public static int doFramesUntilNoChangesPending(androidx.ui.test.ComposeExecutionControl, int maxAmountOfFrames = 10);
+    method public static void recomposeAssertHadChanges(androidx.ui.test.ComposeExecutionControl);
+  }
+
+  public interface ComposeTestCase {
+    method public void emitContent();
+  }
+
+  public interface ComposeTestCaseSetup {
+    method public void performTestWithEventsControl(kotlin.jvm.functions.Function1<? super androidx.ui.test.ComposeExecutionControl,kotlin.Unit> block);
+  }
+
   public interface ComposeTestRule extends org.junit.rules.TestRule {
+    method public androidx.ui.test.ComposeTestCaseSetup forGivenContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
+    method public androidx.ui.test.ComposeTestCaseSetup forGivenTestCase(androidx.ui.test.ComposeTestCase testCase);
     method public androidx.ui.core.Density getDensity();
     method public android.util.DisplayMetrics getDisplayMetrics();
     method public void runOnUiThread(kotlin.jvm.functions.Function0<kotlin.Unit> action);
@@ -106,16 +155,28 @@
 
 package androidx.ui.test.android {
 
+  public final class AndroidComposeTestCaseRunnerKt {
+    ctor public AndroidComposeTestCaseRunnerKt();
+    method public static androidx.ui.test.ComposeBenchmarkScope createAndroidComposeBenchmarkRunner(androidx.ui.test.ComposeTestCase testCase, android.app.Activity activity);
+  }
+
+  public final class AndroidComposeTestCaseSetup implements androidx.ui.test.ComposeTestCaseSetup {
+    ctor public AndroidComposeTestCaseSetup(androidx.ui.test.ComposeTestRule testRule, androidx.ui.test.ComposeTestCase testCase, android.app.Activity activity);
+    method public void performTestWithEventsControl(kotlin.jvm.functions.Function1<? super androidx.ui.test.ComposeExecutionControl,kotlin.Unit> block);
+  }
+
   public final class AndroidComposeTestRule implements androidx.ui.test.ComposeTestRule {
     ctor public AndroidComposeTestRule(boolean disableTransitions, boolean shouldThrowOnRecomposeTimeout);
     ctor public AndroidComposeTestRule();
     method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description? description);
-    method public androidx.test.rule.ActivityTestRule<androidx.ui.test.android.DefaultTestActivity> getActivityTestRule();
+    method public androidx.ui.test.ComposeTestCaseSetup forGivenContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
+    method public androidx.ui.test.ComposeTestCaseSetup forGivenTestCase(androidx.ui.test.ComposeTestCase testCase);
+    method public androidx.test.rule.ActivityTestRule<android.app.Activity> getActivityTestRule();
     method public androidx.ui.core.Density getDensity();
     method public android.util.DisplayMetrics getDisplayMetrics();
     method public void runOnUiThread(kotlin.jvm.functions.Function0<kotlin.Unit> action);
     method public void setContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
-    property public final androidx.test.rule.ActivityTestRule<androidx.ui.test.android.DefaultTestActivity> activityTestRule;
+    property public final androidx.test.rule.ActivityTestRule<android.app.Activity> activityTestRule;
     property public androidx.ui.core.Density density;
     property public android.util.DisplayMetrics displayMetrics;
   }
@@ -125,13 +186,6 @@
     method public void evaluate();
   }
 
-  public final class DefaultTestActivity extends android.app.Activity {
-    ctor public DefaultTestActivity();
-    method public java.util.concurrent.CountDownLatch getHasFocusLatch();
-    method public void setHasFocusLatch(java.util.concurrent.CountDownLatch p);
-    property public final java.util.concurrent.CountDownLatch hasFocusLatch;
-  }
-
 }
 
 package androidx.ui.test.android.fake {
diff --git a/ui/ui-test/api/restricted_current.txt b/ui/ui-test/api/restricted_current.txt
index eb9c996..2d7dd82 100644
--- a/ui/ui-test/api/restricted_current.txt
+++ b/ui/ui-test/api/restricted_current.txt
@@ -36,7 +36,56 @@
     method public androidx.ui.test.CollectedSizes assertWidthEqualsTo(kotlin.jvm.functions.Function1<? super androidx.ui.core.DensityScope,androidx.ui.core.IntPx> expectedWidthPx);
   }
 
+  public interface ComposeBenchmarkScope extends androidx.ui.test.ComposeExecutionControl {
+    method public void disposeContent();
+    method public void draw();
+    method public void drawFinish();
+    method public void drawPrepare();
+    method public void invalidateViews();
+    method public void measureWithSpec(int widthSpec, int heightSpec);
+    method public void requestLayout();
+    method public void setupContent();
+  }
+
+  public interface ComposeExecutionControl {
+    method public void capturePreviewPictureToActivity();
+    method public void doFrame();
+    method public void drawToBitmap();
+    method public boolean getDidLastRecomposeHaveChanges();
+    method public int getMeasuredHeight();
+    method public int getMeasuredWidth();
+    method public boolean hasPendingChanges();
+    method public void layout();
+    method public void measure();
+    method public void recompose();
+    property public abstract boolean didLastRecomposeHaveChanges;
+    property public abstract int measuredHeight;
+    property public abstract int measuredWidth;
+  }
+
+  public final class ComposeExecutionControlKt {
+    ctor public ComposeExecutionControlKt();
+    method public static void assertHasPendingChanges(androidx.ui.test.ComposeExecutionControl);
+    method public static void assertLastRecomposeHadChanges(androidx.ui.test.ComposeExecutionControl);
+    method public static void assertLastRecomposeHadNoChanges(androidx.ui.test.ComposeExecutionControl);
+    method public static void assertMeasureSizeIsPositive(androidx.ui.test.ComposeExecutionControl);
+    method public static void assertNoPendingChanges(androidx.ui.test.ComposeExecutionControl);
+    method public static void doFramesAssertAllHadChangesExceptLastOne(androidx.ui.test.ComposeExecutionControl, int numberOfFramesToBeStable);
+    method public static int doFramesUntilNoChangesPending(androidx.ui.test.ComposeExecutionControl, int maxAmountOfFrames = 10);
+    method public static void recomposeAssertHadChanges(androidx.ui.test.ComposeExecutionControl);
+  }
+
+  public interface ComposeTestCase {
+    method public void emitContent();
+  }
+
+  public interface ComposeTestCaseSetup {
+    method public void performTestWithEventsControl(kotlin.jvm.functions.Function1<? super androidx.ui.test.ComposeExecutionControl,kotlin.Unit> block);
+  }
+
   public interface ComposeTestRule extends org.junit.rules.TestRule {
+    method public androidx.ui.test.ComposeTestCaseSetup forGivenContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
+    method public androidx.ui.test.ComposeTestCaseSetup forGivenTestCase(androidx.ui.test.ComposeTestCase testCase);
     method public androidx.ui.core.Density getDensity();
     method public android.util.DisplayMetrics getDisplayMetrics();
     method public void runOnUiThread(kotlin.jvm.functions.Function0<kotlin.Unit> action);
@@ -106,16 +155,28 @@
 
 package androidx.ui.test.android {
 
+  public final class AndroidComposeTestCaseRunnerKt {
+    ctor public AndroidComposeTestCaseRunnerKt();
+    method public static androidx.ui.test.ComposeBenchmarkScope createAndroidComposeBenchmarkRunner(androidx.ui.test.ComposeTestCase testCase, android.app.Activity activity);
+  }
+
+  public final class AndroidComposeTestCaseSetup implements androidx.ui.test.ComposeTestCaseSetup {
+    ctor public AndroidComposeTestCaseSetup(androidx.ui.test.ComposeTestRule testRule, androidx.ui.test.ComposeTestCase testCase, android.app.Activity activity);
+    method public void performTestWithEventsControl(kotlin.jvm.functions.Function1<? super androidx.ui.test.ComposeExecutionControl,kotlin.Unit> block);
+  }
+
   public final class AndroidComposeTestRule implements androidx.ui.test.ComposeTestRule {
     ctor public AndroidComposeTestRule(boolean disableTransitions, boolean shouldThrowOnRecomposeTimeout);
     ctor public AndroidComposeTestRule();
     method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description? description);
-    method public androidx.test.rule.ActivityTestRule<androidx.ui.test.android.DefaultTestActivity> getActivityTestRule();
+    method public androidx.ui.test.ComposeTestCaseSetup forGivenContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
+    method public androidx.ui.test.ComposeTestCaseSetup forGivenTestCase(androidx.ui.test.ComposeTestCase testCase);
+    method public androidx.test.rule.ActivityTestRule<android.app.Activity> getActivityTestRule();
     method public androidx.ui.core.Density getDensity();
     method public android.util.DisplayMetrics getDisplayMetrics();
     method public void runOnUiThread(kotlin.jvm.functions.Function0<kotlin.Unit> action);
     method public void setContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
-    property public final androidx.test.rule.ActivityTestRule<androidx.ui.test.android.DefaultTestActivity> activityTestRule;
+    property public final androidx.test.rule.ActivityTestRule<android.app.Activity> activityTestRule;
     property public androidx.ui.core.Density density;
     property public android.util.DisplayMetrics displayMetrics;
   }
@@ -125,13 +186,6 @@
     method public void evaluate();
   }
 
-  public final class DefaultTestActivity extends android.app.Activity {
-    ctor public DefaultTestActivity();
-    method public java.util.concurrent.CountDownLatch getHasFocusLatch();
-    method public void setHasFocusLatch(java.util.concurrent.CountDownLatch p);
-    property public final java.util.concurrent.CountDownLatch hasFocusLatch;
-  }
-
 }
 
 package androidx.ui.test.android.fake {
diff --git a/ui/ui-test/src/androidTest/AndroidManifest.xml b/ui/ui-test/src/androidTest/AndroidManifest.xml
index 761c512..3c799d0 100644
--- a/ui/ui-test/src/androidTest/AndroidManifest.xml
+++ b/ui/ui-test/src/androidTest/AndroidManifest.xml
@@ -16,7 +16,8 @@
   -->
 <manifest package="androidx.ui.test" xmlns:android="http://schemas.android.com/apk/res/android">
      <application>
-         <activity android:name="androidx.ui.test.android.DefaultTestActivity" android:theme="@android:style/Theme.Material.NoActionBar.Fullscreen"/>
+         <activity android:name="android.app.Activity"
+             android:theme="@android:style/Theme.Material.NoActionBar.Fullscreen"/>
     </application>
 </manifest>
 
diff --git a/ui/ui-test/src/androidTest/java/androidx/ui/test/AndroidComposeTestCaseRunnerTest.kt b/ui/ui-test/src/androidTest/java/androidx/ui/test/AndroidComposeTestCaseRunnerTest.kt
new file mode 100644
index 0000000..d7cf41b
--- /dev/null
+++ b/ui/ui-test/src/androidTest/java/androidx/ui/test/AndroidComposeTestCaseRunnerTest.kt
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.test
+
+import androidx.compose.Model
+import androidx.compose.onPreCommit
+import androidx.compose.state
+import androidx.compose.unaryPlus
+import androidx.test.filters.SmallTest
+import androidx.ui.core.Text
+import androidx.ui.layout.Container
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@Model
+private class TestModel {
+    var i = 0
+}
+
+@SmallTest
+@RunWith(JUnit4::class)
+class AndroidComposeTestCaseRunnerTest {
+
+    @get:Rule
+    val composeTestRule = createComposeRule(
+        disableTransitions = true
+    )
+
+    @Test
+    fun foreverRecomposing_viaModel_shouldFail() {
+        val model = TestModel()
+        composeTestRule.forGivenContent {
+            Text("Hello ${model.i}")
+            model.i++
+        }.performTestWithEventsControl {
+            assertFailsWith<AssertionError>(
+                "Changes are still pending after '10' frames.") {
+                doFramesAssertAllHadChangesExceptLastOne(10)
+            }
+        }
+    }
+
+    // @Test //- TODO: Does not work, performs only 1 frame until stable
+    fun foreverRecomposing_viaState_shouldFail() {
+        composeTestRule.forGivenContent {
+            val state = +state { 0 }
+            Text("Hello ${state.value}")
+            state.value++
+        }.performTestWithEventsControl {
+            assertFailsWith<AssertionError>(
+                "Changes are still pending after '10' frames.") {
+                doFramesAssertAllHadChangesExceptLastOne(10)
+            }
+        }
+    }
+
+    // @Test //- TODO: Does not work, performs only 1 frame until stable
+    fun foreverRecomposing_viaStatePreCommit_shouldFail() {
+        composeTestRule.forGivenContent {
+            val state = +state { 0 }
+            Text("Hello ${state.value}")
+            onPreCommit {
+                state.value++
+            }
+        }.performTestWithEventsControl {
+            assertFailsWith<AssertionError>(
+                "Changes are still pending after '10' frames.") {
+                doFramesAssertAllHadChangesExceptLastOne(10)
+            }
+        }
+    }
+
+    @Test
+    fun recomposeZeroTime() {
+        composeTestRule.forGivenContent {
+            // Just empty composable
+        }.performTestWithEventsControl {
+            doFrame()
+            assertNoPendingChanges()
+        }
+    }
+
+    @Test
+    fun recomposeZeroTime2() {
+        composeTestRule.forGivenContent {
+            Text("Hello")
+        }.performTestWithEventsControl {
+            doFrame()
+            assertNoPendingChanges()
+        }
+    }
+
+    @Test
+    fun recomposeOnce() {
+        composeTestRule.forGivenContent {
+            val state = +state { 0 }
+            if (state.value < 1) {
+                state.value++
+            }
+        }.performTestWithEventsControl {
+            doFrame()
+            assertNoPendingChanges()
+        }
+    }
+
+    // @Test //- TODO: Does not work, performs only 1 frame until stable
+    fun recomposeTwice() {
+        composeTestRule.forGivenContent {
+            val state = +state { 0 }
+            if (state.value < 2) {
+                state.value++
+            }
+        }.performTestWithEventsControl {
+            doFramesAssertAllHadChangesExceptLastOne(2)
+        }
+    }
+
+    @Test
+    fun recomposeTwice2() {
+        val model = TestModel()
+        composeTestRule.forGivenContent {
+            Text("Hello ${model.i}")
+            if (model.i < 2) {
+                model.i++
+            }
+        }.performTestWithEventsControl {
+            doFramesAssertAllHadChangesExceptLastOne(2)
+        }
+    }
+
+    @Test
+    fun measurePositiveOnEmptyShouldFail() {
+        composeTestRule.forGivenContent {
+            // Just empty composable
+        }.performTestWithEventsControl {
+            doFrame()
+            assertFailsWith<AssertionError> {
+                assertMeasureSizeIsPositive()
+            }
+        }
+    }
+
+    @Test
+    fun measurePositive() {
+        composeTestRule.forGivenContent {
+            Container {
+                Text("Hello")
+            }
+        }.performTestWithEventsControl {
+            doFrame()
+            assertMeasureSizeIsPositive()
+        }
+    }
+
+    private inline fun <reified T : Throwable> assertFailsWith(
+        expectedErrorMessage: String? = null,
+        noinline block: () -> Any
+    ) {
+        try {
+            block()
+        } catch (e: Throwable) {
+            if (e !is T) {
+                throw AssertionError("Expected exception not thrown, received: $e")
+            }
+            if (expectedErrorMessage != null && e.localizedMessage != expectedErrorMessage) {
+                throw AssertionError("Expected error message not found, received: '" +
+                        "${e.localizedMessage}'")
+            }
+            return
+        }
+
+        throw AssertionError("Expected exception not thrown")
+    }
+}
\ No newline at end of file
diff --git a/ui/ui-test/src/androidTest/java/androidx/ui/test/MultipleComposeRootsTest.kt b/ui/ui-test/src/androidTest/java/androidx/ui/test/MultipleComposeRootsTest.kt
index 9c38db3..85f2c4e 100644
--- a/ui/ui-test/src/androidTest/java/androidx/ui/test/MultipleComposeRootsTest.kt
+++ b/ui/ui-test/src/androidTest/java/androidx/ui/test/MultipleComposeRootsTest.kt
@@ -16,6 +16,7 @@
 
 package androidx.ui.test
 
+import android.app.Activity
 import android.widget.FrameLayout
 import android.widget.LinearLayout
 import android.widget.TextView
@@ -32,7 +33,6 @@
 import androidx.ui.material.MaterialTheme
 import androidx.ui.material.TriStateCheckbox
 import androidx.ui.material.surface.Surface
-import androidx.ui.test.android.DefaultTestActivity
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -60,9 +60,7 @@
 class MultipleComposeRootsTest {
 
     @get:Rule
-    val activityTestRule = ActivityTestRule<DefaultTestActivity>(
-        DefaultTestActivity::class.java
-    )
+    val activityTestRule = ActivityTestRule<Activity>(Activity::class.java)
 
     @get:Rule
     val disableTransitions = DisableTransitions()
diff --git a/ui/ui-test/src/main/java/androidx/ui/test/ComposeBenchmarkScope.kt b/ui/ui-test/src/main/java/androidx/ui/test/ComposeBenchmarkScope.kt
new file mode 100644
index 0000000..a26cdea
--- /dev/null
+++ b/ui/ui-test/src/main/java/androidx/ui/test/ComposeBenchmarkScope.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.test
+
+/**
+ * Test scope accessible from benchmarks. Provides extended set of hooks for compose benchmarking.
+ */
+interface ComposeBenchmarkScope : ComposeExecutionControl {
+    /**
+     * Sets up the content. This is by default called by first invocation of doFrame. However this
+     * is useful in case you need to benchmark first composition.
+     *
+     * If you want to call this multiple times, make sure you call [disposeContent] in between.
+     */
+    fun setupContent()
+
+    /**
+     * Request layout on the underlying view. This is should typically not be needed if your
+     * changes invalidate layout by default.
+     */
+    fun requestLayout()
+
+    /**
+     * Invalidates the view / compose hierarchy. This is should typically not be needed if your
+     * changes invalidate view by default.
+     */
+    fun invalidateViews()
+
+    /**
+     * Preparation for [draw]. Do not measure this in benchmark.
+     */
+    fun drawPrepare()
+
+    /**
+     * To be run in benchmark. Call [drawPrepare] before and [drawFinish] after.
+     */
+    fun draw()
+
+    /**
+     * Final step after [draw]. Do not measure this in benchmark.
+     */
+    fun drawFinish()
+
+    /**
+     * Calls measureWithSpec on the underlying view.
+     */
+    // TODO(b/143754545): Try to remove this.
+    fun measureWithSpec(widthSpec: Int, heightSpec: Int)
+
+    /**
+     * Disposes the content. This is typically needed when benchmarking the first content setup or
+     * composition.
+     */
+    fun disposeContent()
+}
diff --git a/ui/ui-test/src/main/java/androidx/ui/test/ComposeExecutionControl.kt b/ui/ui-test/src/main/java/androidx/ui/test/ComposeExecutionControl.kt
new file mode 100644
index 0000000..2ff8533
--- /dev/null
+++ b/ui/ui-test/src/main/java/androidx/ui/test/ComposeExecutionControl.kt
@@ -0,0 +1,216 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.test
+
+/**
+ * Test scope accessible from execution controlled tests to test compose.
+ */
+interface ComposeExecutionControl {
+    /**
+     * The measured width of the underlying view.
+     */
+    val measuredWidth: Int
+
+    /**
+     * The measured height of the underlying view.
+     */
+    val measuredHeight: Int
+
+    /**
+     * Performs measure.
+     *
+     * Note that this does not do any invalidation.
+     */
+    fun measure()
+
+    /**
+     * Performs layout.
+     *
+     * Note that this does not do any invalidation.
+     */
+    fun layout()
+
+    /**
+     * Performs full draw.
+     *
+     * Note that the performance is not close to real draw (unless running Q+).
+     */
+    fun drawToBitmap()
+
+    /**
+     * To be used for tests debugging.
+     *
+     * Draws the view under test into image view and places it in the current Activity. That will
+     * also replace the current content under test. This can be useful to verify / preview results
+     * of your time controlled tests.
+     */
+    fun capturePreviewPictureToActivity()
+
+    /**
+     * Whether the last frame / recompose had changes to recompose.
+     */
+    val didLastRecomposeHaveChanges: Boolean
+
+    /**
+     * Performs the full frame.
+     *
+     * This also sets up the content in case the content was not set up before.
+     *
+     * Following steps are performed
+     * 1) Recompose
+     * 2) Measure
+     * 3) Layout
+     * 4) Draw
+     */
+    fun doFrame()
+
+    /**
+     * Whether there are pending changes in the composition.
+     */
+    fun hasPendingChanges(): Boolean
+
+    /**
+     * Performs recomposition if needed.
+     *
+     * Note this is also called as part of [doFrame]
+     */
+    fun recompose()
+}
+
+// Assertions
+
+/**
+ * Assert that the underlying view under test has a positive size.
+ *
+ * Useful to assert that the test case has some content.
+ *
+ * @throws AssertionError if the underlying view has zero measured size.
+ */
+fun ComposeExecutionControl.assertMeasureSizeIsPositive() {
+    if (measuredWidth > 0 && measuredHeight > 0) {
+        return
+    }
+    throw AssertionError("Measured size is not positive!")
+}
+
+/**
+ * Asserts that last recomposition had some changes.
+ */
+fun ComposeExecutionControl.assertLastRecomposeHadChanges() {
+    assertLastRecomposeResult(expectingChanges = true)
+}
+
+/**
+ * Asserts that last recomposition had no some changes.
+ */
+fun ComposeExecutionControl.assertLastRecomposeHadNoChanges() {
+    assertLastRecomposeResult(expectingChanges = false)
+}
+
+/**
+ * Performs recomposition and asserts that there were or weren't pending changes based on
+ * [expectingChanges].
+ *
+ * @throws AssertionError if condition not satisfied.
+ */
+private fun ComposeExecutionControl.assertLastRecomposeResult(expectingChanges: Boolean) {
+    val message =
+        if (expectingChanges) {
+            "Expected pending changes on recomposition but there were none."
+        } else {
+            "Expected no pending changes on recomposition but there were some."
+        }
+    if (expectingChanges != didLastRecomposeHaveChanges) {
+        throw AssertionError(message)
+    }
+}
+
+/**
+ * Performs recomposition and asserts that there were some pending changes.
+ *
+ * @throws AssertionError if last recomposition had no changes.
+ */
+fun ComposeExecutionControl.recomposeAssertHadChanges() {
+    recompose()
+    assertLastRecomposeHadChanges()
+}
+
+/**
+ * Performs recomposition and asserts that there were some pending changes.
+ *
+ * @throws AssertionError if recomposition has pending changes.
+ */
+fun ComposeExecutionControl.assertNoPendingChanges() {
+    if (hasPendingChanges()) {
+        throw AssertionError("Expected no pending changes but there were some.")
+    }
+}
+
+/**
+ * Performs recomposition and asserts that there were some pending changes.
+ *
+ * @throws AssertionError if recomposition has no pending changes.
+ */
+fun ComposeExecutionControl.assertHasPendingChanges() {
+    if (!hasPendingChanges()) {
+        throw AssertionError("Expected pending changes but there were none.")
+    }
+}
+
+// Assertions runners
+
+/**
+ * Performs the given amount of frames and asserts that there are no changes pending afterwards.
+ * Also asserts that all the frames (except the last one) had changes to recompose.
+ *
+ * @throws AssertionError if any frame before [numberOfFramesToBeStable] frame had no pending
+ * changes or the last frame had pending changes.
+ */
+fun ComposeExecutionControl.doFramesAssertAllHadChangesExceptLastOne(
+    numberOfFramesToBeStable: Int
+) {
+    val framesDone = doFramesUntilNoChangesPending(numberOfFramesToBeStable)
+
+    if (framesDone < numberOfFramesToBeStable) {
+        throw AssertionError(
+            "Hierarchy got stable in frame '$framesDone', which is before expected!")
+    }
+}
+
+// Runners
+
+/**
+ * Runs frames until there are no changes pending.
+ *
+ * @param maxAmountOfFrames Max amount of frames to perform before giving up and throwing exception.
+ * @throws AssertionError if there are still pending changes after [maxAmountOfFrames] executed.
+ */
+fun ComposeExecutionControl.doFramesUntilNoChangesPending(maxAmountOfFrames: Int = 10): Int {
+    var framesDone = 0
+    while (framesDone < maxAmountOfFrames) {
+        doFrame()
+        framesDone++
+        if (!hasPendingChanges()) {
+            // We are stable!
+            return framesDone
+        }
+    }
+
+    // Still not stable
+    throw AssertionError("Changes are still pending after '$maxAmountOfFrames' " +
+            "frames.")
+}
diff --git a/ui/ui-test/src/main/java/androidx/ui/test/ComposeTestCase.kt b/ui/ui-test/src/main/java/androidx/ui/test/ComposeTestCase.kt
new file mode 100644
index 0000000..24fa926
--- /dev/null
+++ b/ui/ui-test/src/main/java/androidx/ui/test/ComposeTestCase.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.test
+
+import androidx.compose.Composable
+
+/**
+ * To be implemented to provide a test case that is then executed by [ComposeTestRule] or can be
+ * used in benchmarks.
+ */
+interface ComposeTestCase {
+    @Composable
+    fun emitContent()
+}
diff --git a/ui/ui-test/src/main/java/androidx/ui/test/ComposeTestRule.kt b/ui/ui-test/src/main/java/androidx/ui/test/ComposeTestRule.kt
index 87beabb..0e01792 100644
--- a/ui/ui-test/src/main/java/androidx/ui/test/ComposeTestRule.kt
+++ b/ui/ui-test/src/main/java/androidx/ui/test/ComposeTestRule.kt
@@ -18,7 +18,6 @@
 
 import android.util.DisplayMetrics
 import androidx.compose.Composable
-import androidx.test.rule.ActivityTestRule
 import androidx.ui.core.Density
 import androidx.ui.test.android.AndroidComposeTestRule
 import org.junit.rules.TestRule
@@ -32,7 +31,6 @@
  * you can still create [AndroidComposeTestRule] directly and access its underlying ActivityTestRule
  */
 interface ComposeTestRule : TestRule {
-
     /**
      * Current device screen's density.
      */
@@ -44,6 +42,18 @@
     fun setContent(composable: @Composable() () -> Unit)
 
     /**
+     * Takes the given content and prepares it for execution-controlled test via
+     * [ComposeTestCaseSetup].
+     */
+    fun forGivenContent(composable: @Composable() () -> Unit): ComposeTestCaseSetup
+
+    /**
+     * Takes the given test case and prepares it for execution-controlled test via
+     * [ComposeTestCaseSetup].
+     */
+    fun forGivenTestCase(testCase: ComposeTestCase): ComposeTestCaseSetup
+
+    /**
      * Runs action on UI thread with a guarantee that any operations modifying Compose data model
      * are safe to do in this block.
      */
@@ -54,6 +64,19 @@
 }
 
 /**
+ * Helper interface to run execution-controlled test via [ComposeTestRule].
+ */
+interface ComposeTestCaseSetup {
+    /**
+     * Takes the content provided via [ComposeTestRule#setContent] and runs the given test
+     * instruction. The test is executed on the main thread and prevents interference from Activity
+     * so the frames can be controlled manually. See [ComposeExecutionControl] for available
+     * methods.
+     */
+    fun performTestWithEventsControl(block: ComposeExecutionControl.() -> Unit)
+}
+
+/**
  * Factory method to provide implementation of [ComposeTestRule].
  */
 fun createComposeRule(disableTransitions: Boolean = false): ComposeTestRule {
diff --git a/ui/ui-test/src/main/java/androidx/ui/test/android/AndroidComposeTestCaseRunner.kt b/ui/ui-test/src/main/java/androidx/ui/test/android/AndroidComposeTestCaseRunner.kt
new file mode 100644
index 0000000..d84661b
--- /dev/null
+++ b/ui/ui-test/src/main/java/androidx/ui/test/android/AndroidComposeTestCaseRunner.kt
@@ -0,0 +1,330 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.test.android
+
+import android.R
+import android.annotation.TargetApi
+import android.app.Activity
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.graphics.Picture
+import android.graphics.RenderNode
+import android.os.Build
+import android.util.DisplayMetrics
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import androidx.compose.Compose
+import androidx.compose.CompositionContext
+import androidx.compose.FrameManager
+import androidx.compose.Recomposer
+import androidx.compose.frames.currentFrame
+import androidx.compose.frames.inFrame
+import androidx.ui.core.AndroidComposeView
+import androidx.ui.core.ComponentNode
+import androidx.ui.core.DrawNode
+import androidx.ui.core.setContent
+import androidx.ui.test.ComposeBenchmarkScope
+import androidx.ui.test.ComposeTestCase
+
+/**
+ * Factory method to provide implementation of [ComposeBenchmarkScope].
+ */
+fun createAndroidComposeBenchmarkRunner(
+    testCase: ComposeTestCase,
+    activity: Activity
+): ComposeBenchmarkScope {
+    return AndroidComposeTestCaseRunner(testCase, activity)
+}
+
+internal class AndroidComposeTestCaseRunner(
+    private val testCase: ComposeTestCase,
+    private val activity: Activity
+) : ComposeBenchmarkScope {
+
+    override val measuredWidth: Int
+        get() = view!!.measuredWidth
+    override val measuredHeight: Int
+        get() = view!!.measuredHeight
+
+    internal var view: ViewGroup? = null
+        private set
+
+    private var compositionContext: CompositionContext? = null
+
+    override var didLastRecomposeHaveChanges = false
+        private set
+
+    private val supportsRenderNode = Build.VERSION.SDK_INT >= 29
+
+    private val screenWithSpec: Int
+    private val screenHeightSpec: Int
+    private val capture = if (supportsRenderNode) RenderNodeCapture() else PictureCapture()
+    private var canvas: Canvas? = null
+
+    private var recomposer: Recomposer? = null
+
+    private var simulationState: SimulationState = SimulationState.Initialized
+
+    init {
+        val displayMetrics = DisplayMetrics()
+        activity.windowManager.defaultDisplay.getMetrics(displayMetrics)
+        val height = displayMetrics.heightPixels
+        val width = displayMetrics.widthPixels
+
+        screenWithSpec = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.AT_MOST)
+        screenHeightSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.AT_MOST)
+    }
+
+    override fun setupContent() {
+        require(view == null) { "Content was already set!" }
+
+        recomposer = Recomposer.current()
+        compositionContext = activity.setContent { testCase.emitContent() }!!
+        FrameManager.nextFrame()
+        view = findComposeView(activity)!!
+    }
+
+    override fun hasPendingChanges(): Boolean {
+        if (Recomposer.hasPendingChanges() || hasPendingChangesInFrame()) {
+            FrameManager.nextFrame()
+        }
+
+        return Recomposer.hasPendingChanges()
+    }
+
+    /**
+     * The reason we have this method is that if a model gets changed in the same frame as created
+     * it won'd trigger pending frame. So [Recompose#hasPendingChanges] stays false. Committing
+     * the current frame does not help either. So we need to check this in order to know if we
+     * need to recompose.
+     */
+    private fun hasPendingChangesInFrame(): Boolean {
+        return inFrame && currentFrame().hasPendingChanges()
+    }
+
+    override fun measure() {
+        getView().measure(screenWithSpec, screenHeightSpec)
+        simulationState = SimulationState.MeasureDone
+    }
+
+    override fun measureWithSpec(widthSpec: Int, heightSpec: Int) {
+        getView().measure(widthSpec, heightSpec)
+        simulationState = SimulationState.MeasureDone
+    }
+
+    override fun drawPrepare() {
+        require(simulationState == SimulationState.LayoutDone ||
+                simulationState == SimulationState.DrawDone) {
+            "Draw can be only executed after layout or draw, current state is '$simulationState'"
+        }
+        canvas = capture.beginRecording(getView().width, getView().height)
+        simulationState = SimulationState.DrawPrepared
+    }
+
+    override fun draw() {
+        require(simulationState == SimulationState.DrawPrepared) {
+            "You need to call 'drawPrepare' before calling 'draw'."
+        }
+        getView().draw(canvas)
+        simulationState = SimulationState.DrawInProgress
+    }
+
+    override fun drawFinish() {
+        require(simulationState == SimulationState.DrawInProgress) {
+            "You need to call 'draw' before calling 'drawFinish'."
+        }
+        capture.endRecording()
+        simulationState = SimulationState.DrawDone
+    }
+
+    override fun drawToBitmap() {
+        drawPrepare()
+        draw()
+        drawFinish()
+    }
+
+    override fun requestLayout() {
+        getView().requestLayout()
+    }
+
+    override fun layout() {
+        require(simulationState == SimulationState.MeasureDone) {
+            "Layout can be only executed after measure, current state is '$simulationState'"
+        }
+        val view = getView()
+        view.layout(view.left, view.top, view.right, view.bottom)
+        simulationState = SimulationState.LayoutDone
+    }
+
+    override fun recompose() {
+        if (hasPendingChanges()) {
+            didLastRecomposeHaveChanges = true
+            recomposer!!.recomposeSync()
+        } else {
+            didLastRecomposeHaveChanges = false
+        }
+        simulationState = SimulationState.RecomposeDone
+    }
+
+    override fun doFrame() {
+        if (view == null) {
+            setupContent()
+        }
+
+        recompose()
+
+        measure()
+        layout()
+        drawToBitmap()
+    }
+
+    override fun invalidateViews() {
+        invalidateViews(getView())
+    }
+
+    override fun disposeContent() {
+        if (view == null) {
+            // Already disposed or never created any content
+            return
+        }
+
+        // TODO(pavlis): replace with activity.disposeComposition() after it gets fixed.
+        Compose.disposeComposition((view as AndroidComposeView).root, activity, null)
+
+        // Clear the view
+        val rootView = activity.findViewById(R.id.content) as ViewGroup
+        rootView.removeAllViews()
+        // Important so we can set the content again.
+        view = null
+        simulationState = SimulationState.Initialized
+    }
+
+    override fun capturePreviewPictureToActivity() {
+        require(measuredWidth > 0 && measuredHeight > 0) {
+            "Preview can't be used on empty view. Did you run measure & layout before calling it?"
+        }
+
+        val picture = Picture()
+        val canvas = picture.beginRecording(getView().measuredWidth, getView().measuredHeight)
+        getView().draw(canvas)
+        picture.endRecording()
+        val imageView = ImageView(activity)
+        val bitmap: Bitmap
+        if (Build.VERSION.SDK_INT >= 28) {
+            bitmap = Bitmap.createBitmap(picture)
+        } else {
+            val width = picture.width.coerceAtLeast(1)
+            val height = picture.height.coerceAtLeast(1)
+            bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
+            Canvas(bitmap).drawPicture(picture)
+        }
+        imageView.setImageBitmap(bitmap)
+        activity.setContentView(imageView)
+    }
+
+    private fun getView(): ViewGroup {
+        require(view != null) { "View was not set! Call setupContent first!" }
+        return view!!
+    }
+}
+
+private enum class SimulationState {
+    Initialized,
+    MeasureDone,
+    LayoutDone,
+    DrawPrepared,
+    DrawInProgress,
+    DrawDone,
+    RecomposeDone
+}
+
+private fun findComposeView(activity: Activity): AndroidComposeView? {
+    return findComposeView(activity.findViewById(android.R.id.content) as ViewGroup)
+}
+
+private fun findComposeView(view: View): AndroidComposeView? {
+    if (view is AndroidComposeView) {
+        return view
+    }
+
+    if (view is ViewGroup) {
+        for (i in 0 until view.childCount) {
+            val composeView = findComposeView(view.getChildAt(i))
+            if (composeView != null) {
+                return composeView
+            }
+        }
+    }
+    return null
+}
+
+private fun invalidateViews(view: View) {
+    view.invalidate()
+    if (view is ViewGroup) {
+        for (i in 0 until view.childCount) {
+            val child = view.getChildAt(i)
+            invalidateViews(child)
+        }
+    }
+    if (view is AndroidComposeView) {
+        invalidateComponentNodes(view.root)
+    }
+}
+
+private fun invalidateComponentNodes(node: ComponentNode) {
+    if (node is DrawNode) {
+        node.invalidate()
+    }
+    node.visitChildren { child ->
+        invalidateComponentNodes(child)
+    }
+}
+
+// We must separate the use of RenderNode so that it isn't referenced in any
+// way on platforms that don't have it. This extracts RenderNode use to a
+// potentially unloaded class, RenderNodeCapture.
+private interface DrawCapture {
+    fun beginRecording(width: Int, height: Int): Canvas
+    fun endRecording()
+}
+
+@TargetApi(Build.VERSION_CODES.Q)
+private class RenderNodeCapture : DrawCapture {
+    private val renderNode = RenderNode("Test")
+
+    override fun beginRecording(width: Int, height: Int): Canvas {
+        renderNode.setPosition(0, 0, width, height)
+        return renderNode.beginRecording()
+    }
+
+    override fun endRecording() {
+        renderNode.endRecording()
+    }
+}
+
+private class PictureCapture : DrawCapture {
+    private val picture = Picture()
+
+    override fun beginRecording(width: Int, height: Int): Canvas {
+        return picture.beginRecording(width, height)
+    }
+
+    override fun endRecording() {
+        picture.endRecording()
+    }
+}
diff --git a/ui/ui-test/src/main/java/androidx/ui/test/android/AndroidComposeTestCaseSetup.kt b/ui/ui-test/src/main/java/androidx/ui/test/android/AndroidComposeTestCaseSetup.kt
new file mode 100644
index 0000000..80c945bb
--- /dev/null
+++ b/ui/ui-test/src/main/java/androidx/ui/test/android/AndroidComposeTestCaseSetup.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.test.android
+
+import android.app.Activity
+import androidx.ui.test.ComposeTestCase
+import androidx.ui.test.ComposeExecutionControl
+import androidx.ui.test.ComposeTestCaseSetup
+import androidx.ui.test.ComposeTestRule
+
+class AndroidComposeTestCaseSetup(
+    private val testRule: ComposeTestRule,
+    private val testCase: ComposeTestCase,
+    private val activity: Activity
+) : ComposeTestCaseSetup {
+    override fun performTestWithEventsControl(block: ComposeExecutionControl.() -> Unit) {
+        testRule.runOnUiThread {
+            // TODO: Ensure that no composition exists at this stage!
+            val runner = AndroidComposeTestCaseRunner(testCase, activity)
+            try {
+                runner.setupContent()
+                block.invoke(runner)
+            } finally {
+                runner.disposeContent()
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/ui/ui-test/src/main/java/androidx/ui/test/android/AndroidComposeTestRule.kt b/ui/ui-test/src/main/java/androidx/ui/test/android/AndroidComposeTestRule.kt
index e071dd9..3971e79 100644
--- a/ui/ui-test/src/main/java/androidx/ui/test/android/AndroidComposeTestRule.kt
+++ b/ui/ui-test/src/main/java/androidx/ui/test/android/AndroidComposeTestRule.kt
@@ -21,11 +21,12 @@
 import android.view.ViewGroup
 import android.view.ViewTreeObserver
 import androidx.compose.Composable
-import androidx.compose.Compose
 import androidx.test.rule.ActivityTestRule
 import androidx.ui.animation.transitionsEnabled
 import androidx.ui.core.Density
 import androidx.ui.core.setContent
+import androidx.ui.test.ComposeTestCase
+import androidx.ui.test.ComposeTestCaseSetup
 import androidx.ui.test.ComposeTestRule
 import androidx.ui.test.throwOnRecomposeTimeout
 import org.junit.runner.Description
@@ -41,7 +42,7 @@
     private val shouldThrowOnRecomposeTimeout: Boolean = false
 ) : ComposeTestRule {
 
-    val activityTestRule = ActivityTestRule<DefaultTestActivity>(DefaultTestActivity::class.java)
+    val activityTestRule = ActivityTestRule<Activity>(Activity::class.java)
 
     override val density: Density get() = Density(activityTestRule.activity)
 
@@ -64,9 +65,6 @@
     /**
      * Use this in your tests to setup the UI content to be tested. This should be called exactly
      * once per test.
-     * <p>
-     * Please note that you need to add the following activity
-     * [androidx.ui.test.android.DefaultTestActivity] to you tests manifest in order to use this.
      */
     @SuppressWarnings("SyntheticAccessor")
     override fun setContent(composable: @Composable() () -> Unit) {
@@ -91,6 +89,28 @@
         drawLatch.await(1, TimeUnit.SECONDS)
     }
 
+    override fun forGivenContent(composable: @Composable() () -> Unit): ComposeTestCaseSetup {
+        val testCase = object : ComposeTestCase {
+            @Composable
+            override fun emitContent() {
+                composable()
+            }
+        }
+        return AndroidComposeTestCaseSetup(
+            this,
+            testCase,
+            activityTestRule.activity
+        )
+    }
+
+    override fun forGivenTestCase(testCase: ComposeTestCase): ComposeTestCaseSetup {
+        return AndroidComposeTestCaseSetup(
+            this,
+            testCase,
+            activityTestRule.activity
+        )
+    }
+
     inner class AndroidComposeStatement(
         private val base: Statement
     ) : Statement() {
@@ -105,15 +125,4 @@
             }
         }
     }
-}
-
-class DefaultTestActivity : Activity() {
-    var hasFocusLatch = CountDownLatch(1)
-
-    override fun onWindowFocusChanged(hasFocus: Boolean) {
-        super.onWindowFocusChanged(hasFocus)
-        if (hasFocus) {
-            hasFocusLatch.countDown()
-        }
-    }
-}
+}
\ No newline at end of file