[go: nahoru, domu]

Add more benchmarks.

A littbe bit of refactoring and better modularization.

Also adds Goerge's benchmarks and provides more abstractions so we can
reuse for future benchmarks.

This will get little simpler once Benchmark lib will allow to measure
sub-sections / slices so we can measure compose, measure, layout and
draw in one test case.

I have also added new benchmark where each ColoredRect has its own model
which does not invalidate the whole loop. Recomposition times there are
3x better.

Test: Added
Bug: 127479944
Change-Id: I2639aaefa3669009640023ea878f89d53f04f9cf
diff --git a/ui/integration-tests/benchmark/build.gradle b/ui/integration-tests/benchmark/build.gradle
index 3d0f974..4d8845b 100644
--- a/ui/integration-tests/benchmark/build.gradle
+++ b/ui/integration-tests/benchmark/build.gradle
@@ -28,8 +28,10 @@
 
 dependencies {
     kotlinPlugin project(path: ":compose:compose-compiler", configuration: "embeddablePlugin")
-    androidTestImplementation(project(":ui:integration-tests:test"))
-    androidTestImplementation(project(":benchmark"))
+    implementation(project(":ui:integration-tests:test"))
+    implementation(project(":benchmark"))
+    implementation(KOTLIN_COMPOSE_STDLIB)
+    implementation(JUNIT)
     androidTestImplementation(project(":ui:ui-core"))
     androidTestImplementation(project(":ui:ui-test"))
     androidTestImplementation(project(":ui:ui-material"))
@@ -37,9 +39,7 @@
     androidTestImplementation(project(":ui:ui-framework"))
     androidTestImplementation(project(":compose:compose-runtime"))
     androidTestImplementation 'androidx.test.ext:junit:1.1.0'
-    androidTestImplementation(KOTLIN_COMPOSE_STDLIB)
     androidTestImplementation(TRUTH)
-    androidTestImplementation(JUNIT)
     androidTestImplementation(ANDROIDX_TEST_RULES)
 }
 
@@ -52,6 +52,8 @@
     description = "UI Benchmarks"
 }
 
+android.defaultConfig.minSdkVersion 29 // RenderNode requires 29+
+
 tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
     kotlinOptions {
         useIR = true
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
new file mode 100644
index 0000000..dba8ad2
--- /dev/null
+++ b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/CheckboxesInRowsBenchmark.kt
@@ -0,0 +1,103 @@
+/*
+ * 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.test
+
+import android.app.Activity
+import androidx.benchmark.BenchmarkRule
+import androidx.test.filters.LargeTest
+import androidx.test.rule.ActivityTestRule
+import androidx.ui.benchmark.measureDrawPerf
+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.cases.CheckboxesInRowsTestCase
+import androidx.ui.test.DisableTransitions
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+/**
+ * Benchmark that runs [CheckboxesInRowsTestCase].
+ */
+@LargeTest
+@RunWith(Parameterized::class)
+class CheckboxesInRowsBenchmark(private val numberOfCheckboxes: Int) {
+
+    companion object {
+        @JvmStatic
+        @Parameterized.Parameters
+        fun initParameters(): Array<Any> = arrayOf(1, 10)
+    }
+
+    @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
+
+    @Test
+    fun toggleCheckbox_recompose() {
+        val testCase = CheckboxesInRowsTestCase(activity, numberOfCheckboxes)
+        benchmarkRule.toggleStateMeasureRecompose(activity, testCase) {
+            testCase.toggleState()
+        }
+    }
+
+    @Test
+    fun toggleCheckbox_measure() {
+        val testCase = CheckboxesInRowsTestCase(activity, numberOfCheckboxes)
+        benchmarkRule.toggleStateMeasureMeasure(activity, testCase) {
+            testCase.toggleState()
+        }
+    }
+
+    @Test
+    fun toggleCheckbox_layout() {
+        val testCase = CheckboxesInRowsTestCase(activity, numberOfCheckboxes)
+        benchmarkRule.toggleStateMeasureLayout(activity, testCase) {
+            testCase.toggleState()
+        }
+    }
+
+    @Test
+    fun toggleCheckbox_draw() {
+        val testCase = CheckboxesInRowsTestCase(activity, numberOfCheckboxes)
+        benchmarkRule.toggleStateMeasureDraw(activity, testCase) {
+            testCase.toggleState()
+        }
+    }
+
+    @Test
+    fun layout() {
+        val testCase = CheckboxesInRowsTestCase(activity, numberOfCheckboxes)
+        benchmarkRule.measureLayoutPerf(activity, testCase)
+    }
+
+    @Test
+    fun draw() {
+        val testCase = CheckboxesInRowsTestCase(activity, numberOfCheckboxes)
+        benchmarkRule.measureDrawPerf(activity, testCase)
+    }
+}
\ No newline at end of file
diff --git a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/ColoredRectBenchmark.kt b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/ColoredRectBenchmark.kt
deleted file mode 100644
index ff19b8a..0000000
--- a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/ColoredRectBenchmark.kt
+++ /dev/null
@@ -1,72 +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.benchmark.test
-
-import android.app.Activity
-import androidx.benchmark.BenchmarkRule
-import androidx.benchmark.measureRepeated
-import androidx.test.filters.LargeTest
-import androidx.test.rule.ActivityTestRule
-import androidx.ui.test.DisableTransitions
-import androidx.ui.test.RectanglesInColumnTestCase
-import androidx.ui.test.recomposeSyncAssertHadChanges
-import androidx.ui.test.recomposeSyncAssertNoChanges
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
-
-/**
- * Benchmark that runs [RectanglesInColumnTestCase]. Currently we test re-composition time.
- */
-@LargeTest
-@RunWith(Parameterized::class)
-class ColoredRectBenchmark(private val numberOfRectangles: Int) {
-
-    companion object {
-        @JvmStatic
-        @Parameterized.Parameters
-        fun initParameters(): Array<Any> = arrayOf(1, 10)
-    }
-
-    @get:Rule
-    val benchmarkRule = BenchmarkRule()
-
-    @get:Rule
-    val activityRule = ActivityTestRule(Activity::class.java)
-
-    @get:Rule
-    val disableAnimationRule = DisableTransitions()
-
-    @Test
-    fun toggleRectangleColor_recompose() {
-        activityRule.runOnUiThread(object : Runnable {
-            override fun run() {
-                val testCase = RectanglesInColumnTestCase(activityRule.activity, numberOfRectangles)
-                    .apply { runSetup() }
-                testCase.compositionContext.recomposeSyncAssertNoChanges()
-
-                benchmarkRule.measureRepeated {
-                    runWithTimingDisabled {
-                        testCase.toggleState()
-                    }
-                    testCase.compositionContext.recomposeSyncAssertHadChanges()
-                }
-            }
-        })
-    }
-}
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
new file mode 100644
index 0000000..b91315b
--- /dev/null
+++ b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/RectsInColumnBenchmark.kt
@@ -0,0 +1,103 @@
+/*
+ * 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.test
+
+import android.app.Activity
+import androidx.benchmark.BenchmarkRule
+import androidx.test.filters.LargeTest
+import androidx.test.rule.ActivityTestRule
+import androidx.ui.benchmark.measureDrawPerf
+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.test.cases.RectsInColumnTestCase
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+/**
+ * Benchmark that runs [RectsInColumnTestCase].
+ */
+@LargeTest
+@RunWith(Parameterized::class)
+class RectsInColumnBenchmark(private val numberOfRectangles: Int) {
+
+    companion object {
+        @JvmStatic
+        @Parameterized.Parameters
+        fun initParameters(): Array<Any> = arrayOf(1, 10)
+    }
+
+    @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
+
+    @Test
+    fun toggleRectangleColor_recompose() {
+        val testCase = RectsInColumnTestCase(activity, numberOfRectangles)
+        benchmarkRule.toggleStateMeasureRecompose(activity, testCase) {
+            testCase.toggleState()
+        }
+    }
+
+    @Test
+    fun toggleRectangleColor_measure() {
+        val testCase = RectsInColumnTestCase(activity, numberOfRectangles)
+        benchmarkRule.toggleStateMeasureMeasure(activity, testCase) {
+            testCase.toggleState()
+        }
+    }
+
+    @Test
+    fun toggleRectangleColor_layout() {
+        val testCase = RectsInColumnTestCase(activity, numberOfRectangles)
+        benchmarkRule.toggleStateMeasureLayout(activity, testCase) {
+            testCase.toggleState()
+        }
+    }
+
+    @Test
+    fun toggleRectangleColor_draw() {
+        val testCase = RectsInColumnTestCase(activity, numberOfRectangles)
+        benchmarkRule.toggleStateMeasureDraw(activity, testCase) {
+            testCase.toggleState()
+        }
+    }
+
+    @Test
+    fun layout() {
+        val testCase = RectsInColumnTestCase(activity, numberOfRectangles)
+        benchmarkRule.measureLayoutPerf(activity, testCase)
+    }
+
+    @Test
+    fun draw() {
+        val testCase = RectsInColumnTestCase(activity, numberOfRectangles)
+        benchmarkRule.measureDrawPerf(activity, testCase)
+    }
+}
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
new file mode 100644
index 0000000..28fc60a
--- /dev/null
+++ b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/RectsInColumnSharedModelBenchmark.kt
@@ -0,0 +1,103 @@
+/*
+ * 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.test
+
+import android.app.Activity
+import androidx.benchmark.BenchmarkRule
+import androidx.test.filters.LargeTest
+import androidx.test.rule.ActivityTestRule
+import androidx.ui.benchmark.measureDrawPerf
+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.test.cases.RectsInColumnSharedModelTestCase
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+/**
+ * Benchmark that runs [RectsInColumnSharedModelTestCase].
+ */
+@LargeTest
+@RunWith(Parameterized::class)
+class RectsInColumnSharedModelBenchmark(private val numberOfRectangles: Int) {
+
+    companion object {
+        @JvmStatic
+        @Parameterized.Parameters
+        fun initParameters(): Array<Any> = arrayOf(1, 10)
+    }
+
+    @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
+
+    @Test
+    fun toggleRectangleColor_recompose() {
+        val testCase = RectsInColumnSharedModelTestCase(activity, numberOfRectangles)
+        benchmarkRule.toggleStateMeasureRecompose(activity, testCase) {
+            testCase.toggleState()
+        }
+    }
+
+    @Test
+    fun toggleRectangleColor_measure() {
+        val testCase = RectsInColumnSharedModelTestCase(activity, numberOfRectangles)
+        benchmarkRule.toggleStateMeasureMeasure(activity, testCase) {
+            testCase.toggleState()
+        }
+    }
+
+    @Test
+    fun toggleRectangleColor_layout() {
+        val testCase = RectsInColumnSharedModelTestCase(activity, numberOfRectangles)
+        benchmarkRule.toggleStateMeasureLayout(activity, testCase) {
+            testCase.toggleState()
+        }
+    }
+
+    @Test
+    fun toggleRectangleColor_draw() {
+        val testCase = RectsInColumnSharedModelTestCase(activity, numberOfRectangles)
+        benchmarkRule.toggleStateMeasureDraw(activity, testCase) {
+            testCase.toggleState()
+        }
+    }
+
+    @Test
+    fun layout() {
+        val testCase = RectsInColumnSharedModelTestCase(activity, numberOfRectangles)
+        benchmarkRule.measureLayoutPerf(activity, testCase)
+    }
+
+    @Test
+    fun draw() {
+        val testCase = RectsInColumnSharedModelTestCase(activity, numberOfRectangles)
+        benchmarkRule.measureDrawPerf(activity, testCase)
+    }
+}
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
new file mode 100644
index 0000000..4c5674e
--- /dev/null
+++ b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/view/AndroidCheckboxesInLinearLayoutBenchmark.kt
@@ -0,0 +1,67 @@
+/*
+ * 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.test.view
+
+import android.app.Activity
+import androidx.benchmark.BenchmarkRule
+import androidx.test.filters.LargeTest
+import androidx.test.rule.ActivityTestRule
+import androidx.ui.benchmark.measureDrawPerf
+import androidx.ui.benchmark.measureLayoutPerf
+import androidx.ui.test.DisableTransitions
+import androidx.ui.test.cases.view.AndroidCheckboxesInLinearLayoutTestCase
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+/**
+ * Benchmark that runs [AndroidCheckboxesInLinearLayoutTestCase].
+ */
+@LargeTest
+@RunWith(Parameterized::class)
+class AndroidCheckboxesInLinearLayoutBenchmark(private val numberOfCheckboxes: Int) {
+
+    companion object {
+        @JvmStatic
+        @Parameterized.Parameters
+        fun initParameters(): Array<Any> = arrayOf(1, 10)
+    }
+
+    @get:Rule
+    val benchmarkRule = BenchmarkRule()
+
+    @get:Rule
+    val activityRule = ActivityTestRule(Activity::class.java)
+
+    @get:Rule
+    val disableAnimationRule = DisableTransitions()
+
+    @Test
+    fun layout() {
+        val testCase = AndroidCheckboxesInLinearLayoutTestCase(activityRule.activity,
+            numberOfCheckboxes)
+        benchmarkRule.measureLayoutPerf(activityRule.activity, testCase)
+    }
+
+    @Test
+    fun draw() {
+        val testCase = AndroidCheckboxesInLinearLayoutTestCase(activityRule.activity,
+            numberOfCheckboxes)
+        benchmarkRule.measureDrawPerf(activityRule.activity, testCase)
+    }
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..2fd0360
--- /dev/null
+++ b/ui/integration-tests/benchmark/src/main/java/androidx/ui/benchmark/BenchmarksExtensions.kt
@@ -0,0 +1,180 @@
+/*
+ * 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 android.view.View
+import androidx.benchmark.BenchmarkRule
+import androidx.benchmark.measureRepeated
+import androidx.ui.test.ComposeTestCase
+import androidx.ui.test.TestCase
+import androidx.ui.test.invalidateViews
+import androidx.ui.test.recomposeSyncAssertHadChanges
+import androidx.ui.test.recomposeSyncAssertNoChanges
+import androidx.ui.test.runOnUiThreadSync
+
+/**
+ * Measures measure and layout performance of the given testCase by toggling measure constraints.
+ */
+fun BenchmarkRule.measureLayoutPerf(activity: Activity, testCase: TestCase) {
+    activity.runOnUiThreadSync {
+        testCase.runSetup()
+
+        val width = testCase.view.measuredWidth
+        val height = testCase.view.measuredHeight
+        var widthSpec = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY)
+        var heightSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY)
+
+        testCase.measureWithSpec(widthSpec, heightSpec)
+        testCase.layout()
+
+        var lastWidth = testCase.view.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)
+            }
+            testCase.measureWithSpec(widthSpec, heightSpec)
+            testCase.layout()
+        }
+    }
+}
+
+/**
+ * Measures draw performance of the given testCase by invalidating the view hierarchy.
+ */
+fun BenchmarkRule.measureDrawPerf(activity: Activity, testCase: TestCase) {
+    activity.runOnUiThreadSync {
+        testCase.runSetup()
+
+        measureRepeated {
+            runWithTimingDisabled {
+                testCase.invalidateViews()
+                testCase.prepareDraw()
+            }
+            testCase.draw()
+            runWithTimingDisabled {
+                testCase.finishDraw()
+            }
+        }
+    }
+}
+
+/**
+ *  Measures recomposition time of the hierarchy after changing a state.
+ */
+fun BenchmarkRule.toggleStateMeasureRecompose(
+    activity: Activity,
+    testCase: ComposeTestCase,
+    toggleState: () -> Unit
+) {
+    activity.runOnUiThreadSync {
+        testCase.runSetup()
+        testCase.recomposeSyncAssertNoChanges()
+
+        measureRepeated {
+            runWithTimingDisabled {
+                toggleState()
+            }
+            testCase.recomposeSyncAssertHadChanges()
+        }
+    }
+}
+
+/**
+ *  Measures measure time of the hierarchy after changing a state.
+ */
+fun BenchmarkRule.toggleStateMeasureMeasure(
+    activity: Activity,
+    testCase: ComposeTestCase,
+    toggleState: () -> Unit
+) {
+    activity.runOnUiThreadSync {
+        testCase.runSetup()
+        testCase.recomposeSyncAssertNoChanges()
+
+        measureRepeated {
+            runWithTimingDisabled {
+                toggleState()
+                testCase.recomposeSyncAssertHadChanges()
+            }
+            testCase.measure()
+        }
+    }
+}
+
+/**
+ *  Measures layout time of the hierarchy after changing a state.
+ */
+fun BenchmarkRule.toggleStateMeasureLayout(
+    activity: Activity,
+    testCase: ComposeTestCase,
+    toggleState: () -> Unit
+) {
+    activity.runOnUiThreadSync {
+        testCase.runSetup()
+        testCase.recomposeSyncAssertNoChanges()
+
+        measureRepeated {
+            runWithTimingDisabled {
+                toggleState()
+                testCase.recomposeSyncAssertHadChanges()
+                testCase.measure()
+            }
+            testCase.layout()
+        }
+    }
+}
+
+/**
+ *  Measures draw time of the hierarchy after changing a state.
+ */
+fun BenchmarkRule.toggleStateMeasureDraw(
+    activity: Activity,
+    testCase: ComposeTestCase,
+    toggleState: () -> Unit
+) {
+    activity.runOnUiThreadSync {
+        testCase.runSetup()
+        testCase.recomposeSyncAssertNoChanges()
+
+        measureRepeated {
+            runWithTimingDisabled {
+                toggleState()
+                testCase.recomposeSyncAssertHadChanges()
+                testCase.measure()
+                testCase.layout()
+                testCase.prepareDraw()
+            }
+            testCase.draw()
+            runWithTimingDisabled {
+                testCase.finishDraw()
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/ui/integration-tests/test/build.gradle b/ui/integration-tests/test/build.gradle
index cff4d04..2853179 100644
--- a/ui/integration-tests/test/build.gradle
+++ b/ui/integration-tests/test/build.gradle
@@ -36,6 +36,7 @@
 
     implementation(JUNIT)
     implementation(TRUTH)
+    implementation(ANDROIDX_TEST_RULES)
 
     implementation project(":compose:compose-runtime")
     implementation project(":ui:ui-core")
@@ -43,9 +44,8 @@
     implementation project(":ui:ui-framework")
     implementation project(":ui:ui-layout")
     implementation project(":ui:ui-material")
+    implementation project(":ui:ui-platform")
     implementation project(":ui:ui-test")
-
-    androidTestImplementation(ANDROIDX_TEST_RULES)
 }
 
 androidx {
@@ -57,9 +57,10 @@
     description = "UI Integration Tests"
 }
 
+android.defaultConfig.minSdkVersion 29 // RenderNode requires 29+
+
 tasks.withType(KotlinCompile).all {
     kotlinOptions {
         useIR = true
     }
 }
-
diff --git a/ui/integration-tests/test/src/androidTest/java/androidx/ui/test/ColoredRectTest.kt b/ui/integration-tests/test/src/androidTest/java/androidx/ui/test/CheckboxesInRowsTest.kt
similarity index 63%
copy from ui/integration-tests/test/src/androidTest/java/androidx/ui/test/ColoredRectTest.kt
copy to ui/integration-tests/test/src/androidTest/java/androidx/ui/test/CheckboxesInRowsTest.kt
index f45d6ad..9d7f8a8 100644
--- a/ui/integration-tests/test/src/androidTest/java/androidx/ui/test/ColoredRectTest.kt
+++ b/ui/integration-tests/test/src/androidTest/java/androidx/ui/test/CheckboxesInRowsTest.kt
@@ -17,20 +17,22 @@
 package androidx.ui.test
 
 import android.app.Activity
-import androidx.compose.composer
 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
 import org.junit.runners.Parameterized
 
 /**
- * Ensure correctness of [RectanglesInColumnTestCase].
+ * Ensure correctness of [CheckboxesInRowsTestCase].
  */
 @MediumTest
 @RunWith(Parameterized::class)
-class ColoredRectTest(private val numberOfRectangles: Int) {
+class CheckboxesInRowsTest(private val numberOfCheckboxes: Int) {
 
     companion object {
         @JvmStatic
@@ -46,23 +48,12 @@
 
     @Test
     fun toggleRectangleColor_compose() {
-        activityRule.runOnUiThread(object : Runnable {
-            override fun run() {
-                val testCase = RectanglesInColumnTestCase(activityRule.activity, numberOfRectangles)
-                    .apply { runSetup() }
-
-                testCase.compositionContext.recomposeSyncAssertNoChanges()
-
-                // Change state
+        activityRule.runOnUiThreadSync {
+            val testCase = CheckboxesInRowsTestCase(activityRule.activity,
+                numberOfCheckboxes)
+            runComposeTestWithStateToggleAndAssertRecompositions(testCase) {
                 testCase.toggleState()
-
-                // Recompose our changes
-                testCase.compositionContext.recomposeSyncAssertHadChanges()
-
-                // No other compositions should be pending
-                testCase.compositionContext.recomposeSyncAssertNoChanges()
             }
-        })
+        }
     }
-
-}
+}
\ No newline at end of file
diff --git a/ui/integration-tests/test/src/androidTest/java/androidx/ui/test/ColoredRectTest.kt b/ui/integration-tests/test/src/androidTest/java/androidx/ui/test/RectsInColumnSharedModelTest.kt
similarity index 63%
copy from ui/integration-tests/test/src/androidTest/java/androidx/ui/test/ColoredRectTest.kt
copy to ui/integration-tests/test/src/androidTest/java/androidx/ui/test/RectsInColumnSharedModelTest.kt
index f45d6ad..cf724d8 100644
--- a/ui/integration-tests/test/src/androidTest/java/androidx/ui/test/ColoredRectTest.kt
+++ b/ui/integration-tests/test/src/androidTest/java/androidx/ui/test/RectsInColumnSharedModelTest.kt
@@ -17,20 +17,20 @@
 package androidx.ui.test
 
 import android.app.Activity
-import androidx.compose.composer
 import androidx.test.filters.MediumTest
 import androidx.test.rule.ActivityTestRule
+import androidx.ui.test.cases.RectsInColumnSharedModelTestCase
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
 
 /**
- * Ensure correctness of [RectanglesInColumnTestCase].
+ * Ensure correctness of [RectsInColumnSharedModelTestCase].
  */
 @MediumTest
 @RunWith(Parameterized::class)
-class ColoredRectTest(private val numberOfRectangles: Int) {
+class RectsInColumnSharedModelTest(private val numberOfRectangles: Int) {
 
     companion object {
         @JvmStatic
@@ -46,23 +46,12 @@
 
     @Test
     fun toggleRectangleColor_compose() {
-        activityRule.runOnUiThread(object : Runnable {
-            override fun run() {
-                val testCase = RectanglesInColumnTestCase(activityRule.activity, numberOfRectangles)
-                    .apply { runSetup() }
-
-                testCase.compositionContext.recomposeSyncAssertNoChanges()
-
-                // Change state
+        activityRule.runOnUiThreadSync {
+            val testCase = RectsInColumnSharedModelTestCase(activityRule.activity,
+                numberOfRectangles)
+            runComposeTestWithStateToggleAndAssertRecompositions(testCase) {
                 testCase.toggleState()
-
-                // Recompose our changes
-                testCase.compositionContext.recomposeSyncAssertHadChanges()
-
-                // No other compositions should be pending
-                testCase.compositionContext.recomposeSyncAssertNoChanges()
             }
-        })
+        }
     }
-
-}
+}
\ No newline at end of file
diff --git a/ui/integration-tests/test/src/androidTest/java/androidx/ui/test/ColoredRectTest.kt b/ui/integration-tests/test/src/androidTest/java/androidx/ui/test/RectsInColumnTest.kt
similarity index 63%
rename from ui/integration-tests/test/src/androidTest/java/androidx/ui/test/ColoredRectTest.kt
rename to ui/integration-tests/test/src/androidTest/java/androidx/ui/test/RectsInColumnTest.kt
index f45d6ad..ba8c6e6 100644
--- a/ui/integration-tests/test/src/androidTest/java/androidx/ui/test/ColoredRectTest.kt
+++ b/ui/integration-tests/test/src/androidTest/java/androidx/ui/test/RectsInColumnTest.kt
@@ -17,20 +17,20 @@
 package androidx.ui.test
 
 import android.app.Activity
-import androidx.compose.composer
 import androidx.test.filters.MediumTest
 import androidx.test.rule.ActivityTestRule
+import androidx.ui.test.cases.RectsInColumnTestCase
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
 
 /**
- * Ensure correctness of [RectanglesInColumnTestCase].
+ * Ensure correctness of [RectsInColumnTestCase].
  */
 @MediumTest
 @RunWith(Parameterized::class)
-class ColoredRectTest(private val numberOfRectangles: Int) {
+class RectsInColumnTest(private val numberOfRectangles: Int) {
 
     companion object {
         @JvmStatic
@@ -46,23 +46,11 @@
 
     @Test
     fun toggleRectangleColor_compose() {
-        activityRule.runOnUiThread(object : Runnable {
-            override fun run() {
-                val testCase = RectanglesInColumnTestCase(activityRule.activity, numberOfRectangles)
-                    .apply { runSetup() }
-
-                testCase.compositionContext.recomposeSyncAssertNoChanges()
-
-                // Change state
+        activityRule.runOnUiThreadSync {
+            val testCase = RectsInColumnTestCase(activityRule.activity, numberOfRectangles)
+            runComposeTestWithStateToggleAndAssertRecompositions(testCase) {
                 testCase.toggleState()
-
-                // Recompose our changes
-                testCase.compositionContext.recomposeSyncAssertHadChanges()
-
-                // No other compositions should be pending
-                testCase.compositionContext.recomposeSyncAssertNoChanges()
             }
-        })
+        }
     }
-
-}
+}
\ 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
new file mode 100644
index 0000000..ec9d692
--- /dev/null
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/TestCase.kt
@@ -0,0 +1,186 @@
+/*
+ * 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 android.graphics.Bitmap
+import android.graphics.Canvas
+import android.graphics.Picture
+import android.graphics.RenderNode
+import android.util.DisplayMetrics
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import androidx.compose.CompositionContext
+import androidx.ui.core.AndroidCraneView
+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 renderNode = RenderNode("test")
+    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)
+    }
+
+    lateinit var view: ViewGroup
+
+    abstract fun runSetup()
+
+    /**
+     * 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() {
+        renderNode.setPosition(0, 0, view.width, view.height)
+        canvas = renderNode.beginRecording()
+    }
+
+    /**
+     * To be run in benchmark. Call [prepareDraw] before and [finishDraw] after.
+     */
+    fun draw() {
+        view.draw(canvas)
+    }
+
+    /**
+     * Do not measure this in benchmark.
+     */
+    fun finishDraw() {
+        renderNode.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)
+    }
+}
+
+abstract class ComposeTestCase(
+    activity: Activity
+) : TestCase(activity) {
+
+    lateinit var compositionContext: CompositionContext
+}
+
+fun TestCase.assertMeasureSizeIsPositive() {
+    Truth.assertThat(view.measuredWidth).isAtLeast(1)
+    Truth.assertThat(view.measuredHeight).isAtLeast(1)
+}
+
+fun TestCase.invalidateViews() {
+    invalidateViews(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)
+        }
+    }
+    if (view is AndroidCraneView) {
+        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() {
+    val hadChanges = compositionContext.recomposeSync()
+    Assert.assertTrue(
+        "Expected pending changes on recomposition but there were none. Did " +
+                "you forget to call FrameManager.next()?", hadChanges
+    )
+}
+
+/**
+ * Performs recomposition and asserts that there were no pending changes.
+ */
+fun ComposeTestCase.recomposeSyncAssertNoChanges() {
+    val hadChanges = compositionContext.recomposeSync()
+    Assert.assertFalse(
+        "Expected no pending changes on recomposition but there were some.",
+        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)
+    imageView.setImageBitmap(Bitmap.createBitmap(picture))
+    activity.setContentView(imageView)
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..55cb4d1
--- /dev/null
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/TestExecutors.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.runSetup()
+
+    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
index 1ead593..9bfae0d7 100644
--- 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
@@ -16,15 +16,14 @@
 
 package androidx.ui.test
 
-import androidx.compose.composer
 import android.app.Activity
+import androidx.compose.composer
 import androidx.compose.Composable
 import androidx.compose.CompositionContext
 import androidx.ui.core.composeIntoActivity
+import androidx.test.rule.ActivityTestRule
 import androidx.ui.material.MaterialTheme
 import androidx.ui.material.surface.Surface
-import org.junit.Assert.assertFalse
-import org.junit.Assert.assertTrue
 
 fun ComposeMaterialIntoActivity(
     activity: Activity,
@@ -39,20 +38,20 @@
     }
 }
 
-/**
- * Performs recomposition and asserts that there were some pending changes.
- */
-fun CompositionContext.recomposeSyncAssertHadChanges() {
-    val hadChanges = recomposeSync()
-    assertTrue("Expected pending changes on recomposition but there were none. Did " +
-            "you forget to call FrameManager.next()?", hadChanges)
+fun <T : Activity> ActivityTestRule<T>.runOnUiThreadSync(action: () -> Unit) {
+    // Workaround for lambda bug in IR
+    runOnUiThread(object : Runnable {
+        override fun run() {
+            action.invoke()
+        }
+    })
 }
 
-/**
- * Performs recomposition and asserts that there were no pending changes.
- */
-fun CompositionContext.recomposeSyncAssertNoChanges() {
-    val hadChanges = recomposeSync()
-    assertFalse("Expected no pending changes on recomposition but there were some.",
-        hadChanges)
+fun Activity.runOnUiThreadSync(action: () -> Unit) {
+    // Workaround for lambda bug in IR
+    runOnUiThread(object : Runnable {
+        override fun run() {
+            action.invoke()
+        }
+    })
 }
\ No newline at end of file
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
new file mode 100644
index 0000000..368b2a8
--- /dev/null
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/CheckboxesInRowsTestCase.kt
@@ -0,0 +1,87 @@
+/*
+ * 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.composer
+import androidx.compose.Composable
+import androidx.compose.FrameManager
+import androidx.compose.State
+import androidx.compose.state
+import androidx.compose.unaryPlus
+import androidx.ui.core.Text
+import androidx.ui.layout.Align
+import androidx.ui.layout.Alignment
+import androidx.ui.layout.Column
+import androidx.ui.layout.FlexRow
+import androidx.ui.material.Checkbox
+import androidx.ui.test.ComposeMaterialIntoActivity
+import androidx.ui.test.ComposeTestCase
+
+/**
+ * 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) {
+
+    private val states = mutableListOf<State<Boolean>>()
+
+    override fun runSetup() {
+        compositionContext = ComposeMaterialIntoActivity(activity) {
+            Column {
+                repeat(amountOfCheckboxes) {
+                    FlexRow {
+                        inflexible {
+                            Text(text = "Check Me!")
+                        }
+                        expanded(1f) {
+                            Align(alignment = Alignment.CenterRight) {
+                                CheckboxWithState()
+                            }
+                        }
+                    }
+                }
+            }
+        }!!
+        FrameManager.nextFrame()
+
+        view = activity.findViewById(android.R.id.content)
+
+        measure()
+        layout()
+        drawSlow()
+    }
+
+    fun toggleState() {
+        val state = states.first()
+        state.value = !state.value
+        FrameManager.nextFrame()
+    }
+
+    @Composable
+    fun CheckboxWithState() {
+        val state = +state { false }
+        states.add(state)
+        Checkbox(
+            checked = state.value,
+             state.value = !state.value }
+        )
+    }
+}
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/RectanglesInColumnTestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/RectsInColumnSharedModelTestCase.kt
similarity index 75%
rename from ui/integration-tests/test/src/main/java/androidx/ui/test/RectanglesInColumnTestCase.kt
rename to ui/integration-tests/test/src/main/java/androidx/ui/test/cases/RectsInColumnSharedModelTestCase.kt
index 9b9047e..434fa7e 100644
--- a/ui/integration-tests/test/src/main/java/androidx/ui/test/RectanglesInColumnTestCase.kt
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/RectsInColumnSharedModelTestCase.kt
@@ -14,34 +14,37 @@
  * limitations under the License.
  */
 
-package androidx.ui.test
+package androidx.ui.test.cases
 
-import androidx.compose.composer
 import android.app.Activity
-import androidx.compose.CompositionContext
+import androidx.compose.composer
 import androidx.compose.FrameManager
 import androidx.compose.Model
 import androidx.ui.core.dp
 import androidx.ui.foundation.ColoredRect
 import androidx.ui.graphics.Color
 import androidx.ui.layout.Column
+import androidx.ui.test.ComposeMaterialIntoActivity
+import androidx.ui.test.ComposeTestCase
 
 @Model
-class RectanglesInColumnTestCaseColorModel(var color: Color)
+private class RectanglesInColumnTestCaseColorModel(var color: Color)
 
 /**
  * Test case that puts the given amount of rectangles into a column layout and makes changes by
  * modifying the color used in the model.
+ *
+ * Note: Rectangle are created in for loop that reference a single model. Currently it will happen
+ * that the whole loop has to be re-run when model changes.
  */
-class RectanglesInColumnTestCase(
-    private val activity: Activity,
+class RectsInColumnSharedModelTestCase(
+    activity: Activity,
     private val amountOfRectangles: Int
-) {
+) : ComposeTestCase(activity) {
 
     private val model = RectanglesInColumnTestCaseColorModel(Color.Black)
-    lateinit var compositionContext: CompositionContext
 
-    fun runSetup() {
+    override fun runSetup() {
         compositionContext = ComposeMaterialIntoActivity(activity) {
             Column {
                 repeat(amountOfRectangles) { i ->
@@ -54,6 +57,12 @@
             }
         }!!
         FrameManager.nextFrame()
+
+        view = activity.findViewById(android.R.id.content)
+
+        measure()
+        layout()
+        drawSlow()
     }
 
     fun toggleState() {
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
new file mode 100644
index 0000000..2686f8b
--- /dev/null
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/RectsInColumnTestCase.kt
@@ -0,0 +1,79 @@
+/*
+ * 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.composer
+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.graphics.Color
+import androidx.ui.layout.Column
+import androidx.ui.test.ComposeMaterialIntoActivity
+import androidx.ui.test.ComposeTestCase
+
+/**
+ * Test case that puts the given amount of rectangles into a column layout and makes changes by
+ * modifying the color used in the model.
+ *
+ * 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) {
+
+    private val states = mutableListOf<State<Color>>()
+
+    override fun runSetup() {
+        compositionContext = ComposeMaterialIntoActivity(activity) {
+            Column {
+                repeat(amountOfRectangles) {
+                    ColoredRectWithModel()
+                }
+            }
+        }!!
+        FrameManager.nextFrame()
+
+        view = activity.findViewById(android.R.id.content)
+
+        measure()
+        layout()
+        drawSlow()
+    }
+
+    fun toggleState() {
+        val state = states.first()
+        if (state.value == Color.Purple) {
+            state.value = Color.Blue
+        } else {
+            state.value = Color.Purple
+        }
+        FrameManager.nextFrame()
+    }
+
+    @Composable
+    fun ColoredRectWithModel() {
+        val state = +state { Color.Black }
+        states.add(state)
+        ColoredRect(color = state.value, width = 100.dp, height = 50.dp)
+    }
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..e12d6fc
--- /dev/null
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/view/AndroidCheckboxesInLinearLayoutTestCase.kt
@@ -0,0 +1,88 @@
+/*
+ * 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.view
+
+import android.app.Activity
+import android.view.View
+import android.view.ViewGroup
+import android.widget.CheckBox
+import android.widget.LinearLayout
+import android.widget.TextView
+import androidx.ui.test.R
+import androidx.ui.test.TestCase
+
+/**
+ * Version of [CheckboxesInRowsTestCase] using Android views.
+ */
+class AndroidCheckboxesInLinearLayoutTestCase(
+    activity: Activity,
+    private val amountOfCheckboxes: Int
+) : TestCase(activity) {
+
+    private val checkboxes = mutableListOf<CheckBox>()
+
+    override fun runSetup() {
+        val column = LinearLayout(activity)
+        column.orientation = LinearLayout.VERTICAL
+        column.layoutParams = ViewGroup.LayoutParams(
+            ViewGroup.LayoutParams.MATCH_PARENT,
+            ViewGroup.LayoutParams.WRAP_CONTENT
+        )
+        column.id = R.id.normal
+        repeat(amountOfCheckboxes) {
+            val row = LinearLayout(activity)
+            row.orientation = LinearLayout.HORIZONTAL
+            row.layoutParams = LinearLayout.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT,
+                ViewGroup.LayoutParams.WRAP_CONTENT
+            )
+            val text = TextView(activity)
+            text.text = "Check Me!"
+            text.layoutParams = LinearLayout.LayoutParams(
+                ViewGroup.LayoutParams.WRAP_CONTENT,
+                ViewGroup.LayoutParams.WRAP_CONTENT
+            )
+            val checkbox = CheckBox(activity)
+            checkbox.isChecked = false
+            checkbox.layoutParams = LinearLayout.LayoutParams(
+                ViewGroup.LayoutParams.WRAP_CONTENT,
+                ViewGroup.LayoutParams.WRAP_CONTENT
+            )
+
+            val space = View(activity)
+            val layoutParams = LinearLayout.LayoutParams(0, 1)
+            layoutParams.weight = 1f
+            space.layoutParams = layoutParams
+            row.addView(text)
+            row.addView(space)
+            row.addView(checkbox)
+            column.addView(row)
+        }
+
+        view = column
+        activity.setContentView(column)
+
+        measure()
+        layout()
+        drawSlow()
+    }
+
+    fun toggleState() {
+        val checkbox = checkboxes.first()
+        checkbox.isChecked = !checkbox.isChecked
+    }
+}