[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/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(