[go: nahoru, domu]

Adding AndroidCraneViewTest with autofill tests

Bug: 139314168
Test: Ran the tests on emulators for API levels < 26 and > 26.
Change-Id: Ia388e448ffb1106e33a958a37f681e748dc67d5b
diff --git a/ui/ui-material/build.gradle b/ui/ui-material/build.gradle
index a6cacda..00174b8 100644
--- a/ui/ui-material/build.gradle
+++ b/ui/ui-material/build.gradle
@@ -55,6 +55,7 @@
 
     androidTestImplementation project(":ui:ui-material:integration-tests:samples")
     androidTestImplementation project(":ui:ui-test")
+    androidTestApi project(":ui:ui-platform")
 
     androidTestImplementation(ANDROIDX_TEST_RULES)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
diff --git a/ui/ui-platform/build.gradle b/ui/ui-platform/build.gradle
index 651fc72..f0f2b38 100644
--- a/ui/ui-platform/build.gradle
+++ b/ui/ui-platform/build.gradle
@@ -40,22 +40,27 @@
     testImplementation(ANDROIDX_TEST_RULES)
     testImplementation(ANDROIDX_TEST_RUNNER)
     testImplementation(JUNIT)
+    testImplementation(ROBOLECTRIC)
     testImplementation(MOCKITO_CORE)
     testImplementation(TRUTH)
     testImplementation MOCKITO_KOTLIN, {
         exclude group: 'org.mockito' // to keep control on the mockito version
     }
 
+    testImplementation project(":ui:ui-test")
+
     androidTestImplementation(ANDROIDX_TEST_RULES)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(JUNIT)
+    androidTestImplementation(TRUTH)
     androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation MOCKITO_KOTLIN, {
         exclude group: 'org.mockito' // to keep control on the mockito version
     }
 
-    implementation "androidx.core:core:1.0.0"
+    androidTestImplementation project(":ui:ui-test")
+
 }
 
 androidx {
@@ -66,3 +71,11 @@
     inceptionYear = "2019"
     description = "Contains internal implementation that allows separation of android implementation from host-side tests."
 }
+
+android {
+    testOptions {
+        unitTests {
+            includeAndroidResources = true
+        }
+    }
+}
diff --git a/ui/ui-platform/src/androidTest/AndroidManifest.xml b/ui/ui-platform/src/androidTest/AndroidManifest.xml
index f086f11..ce716e4 100644
--- a/ui/ui-platform/src/androidTest/AndroidManifest.xml
+++ b/ui/ui-platform/src/androidTest/AndroidManifest.xml
@@ -14,9 +14,9 @@
      limitations under the License.
 -->
 <manifest package="androidx.ui.platform" xmlns:android="http://schemas.android.com/apk/res/android">
-
     <application>
-        <activity android:name="androidx.ui.platform.test.TestActivity" android:theme="@android:style/Theme.Material.NoActionBar.Fullscreen"/>
+        <activity android:name="android.app.Activity"
+                  android:theme="@android:style/Theme.Material.NoActionBar.Fullscreen"/>
     </application>
 </manifest>
 
diff --git a/ui/ui-platform/src/androidTest/java/androidx/ui/core/AndroidCraneViewTest.kt b/ui/ui-platform/src/androidTest/java/androidx/ui/core/AndroidCraneViewTest.kt
new file mode 100644
index 0000000..09cec49
--- /dev/null
+++ b/ui/ui-platform/src/androidTest/java/androidx/ui/core/AndroidCraneViewTest.kt
@@ -0,0 +1,115 @@
+/*
+ * 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.core
+
+import android.app.Activity
+import android.graphics.Rect
+import android.util.SparseArray
+import android.view.View
+import android.view.ViewStructure
+import android.view.autofill.AutofillValue
+import androidx.test.filters.SdkSuppress
+import androidx.test.rule.ActivityTestRule
+import androidx.ui.autofill.AndroidAutofill
+import androidx.ui.autofill.AutofillNode
+import androidx.ui.autofill.AutofillType
+import androidx.ui.test.android.fake.FakeViewStructure
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+
+class AndroidCraneViewTest {
+    @get:Rule
+    val activityTestRule = ActivityTestRule<Activity>(Activity::class.java)
+
+    private val PACKAGE_NAME = "androidx.ui.platform.test"
+    private lateinit var craneView: AndroidCraneView
+
+    @Before
+    fun setup() {
+        craneView = AndroidCraneView(activityTestRule.activity)
+    }
+
+    @SdkSuppress(maxSdkVersion = 25)
+    @Test
+    fun autofillAmbient_belowApi26_isNull() {
+        assertThat(craneView.autofill).isNull()
+    }
+
+    @SdkSuppress(minSdkVersion = 26)
+    @Test
+    fun autofillAmbient_isNotNull() {
+        assertThat(craneView.autofill).isNotNull()
+    }
+
+    @SdkSuppress(minSdkVersion = 26)
+    @Test
+    fun autofillAmbient_returnsAnInstanceOfAndroidAutofill() {
+        assertThat(craneView.autofill).isInstanceOf(AndroidAutofill::class.java)
+    }
+
+    @SdkSuppress(minSdkVersion = 26)
+    @Test
+    fun onProvideAutofillVirtualStructure_populatesViewStructure() {
+        // Arrange.
+        val viewStructure: ViewStructure = FakeViewStructure()
+        val autofillNode = AutofillNode(
+            >
+            autofillTypes = listOf(AutofillType.Name),
+            boundingBox = Rect(0, 0, 0, 0)
+        )
+        craneView.autofillTree += autofillNode
+
+        // Act.
+        craneView.onProvideAutofillVirtualStructure(viewStructure, 0)
+
+        // Assert.
+        assertThat(viewStructure).isEqualTo(FakeViewStructure().apply {
+            children.add(FakeViewStructure().apply {
+                virtualId = autofillNode.id
+                packageName = PACKAGE_NAME
+                setAutofillType(View.AUTOFILL_TYPE_TEXT)
+                setAutofillHints(arrayOf(View.AUTOFILL_HINT_NAME))
+                setDimens(0, 0, 0, 0, 0, 0)
+            })
+        })
+    }
+
+    @SdkSuppress(minSdkVersion = 26)
+    @Test
+    fun autofill_triggersOnFill() {
+        // Arrange.
+        val expectedValue = "Name"
+        var autofilledValue = ""
+        val autofillNode = AutofillNode(
+             autofilledValue = it },
+            autofillTypes = listOf(AutofillType.Name),
+            boundingBox = Rect(0, 0, 0, 0)
+        )
+        val autofillValues = SparseArray<AutofillValue>().apply {
+            append(autofillNode.id, AutofillValue.forText(expectedValue))
+        }
+        craneView.autofillTree += autofillNode
+
+        // Act.
+        craneView.autofill(autofillValues)
+
+        // Assert.
+        assertThat(autofilledValue).isEqualTo(expectedValue)
+    }
+}
\ No newline at end of file
diff --git a/ui/ui-platform/src/main/java/androidx/ui/core/AndroidOwner.kt b/ui/ui-platform/src/main/java/androidx/ui/core/AndroidOwner.kt
index 456c958..fe51144 100644
--- a/ui/ui-platform/src/main/java/androidx/ui/core/AndroidOwner.kt
+++ b/ui/ui-platform/src/main/java/androidx/ui/core/AndroidOwner.kt
@@ -81,8 +81,8 @@
     // Map from model to LayoutNodes that *only* need layout and not measure
     private val relayoutOnly = ObserverMap<Any, LayoutNode>()
 
-    // TODO: Replace with SemanticsTree.
-    //  This is a temporary hack until we have a semantics tree implemented.
+    // Used by components that want to provide autofill semantic information.
+    // TODO: Replace with SemanticsTree: Temporary hack until we have a semantics tree implemented.
     val autofillTree = AutofillTree()
 
     // RepaintBoundaryNodes that have had their boundary changed. When using Views,
@@ -101,7 +101,7 @@
 
     var constraints = Constraints.tightConstraints(width = IntPx.Zero, height = IntPx.Zero)
     // TODO(mount): reinstate when coroutines are supported by IR compiler
-//    private val ownerScope = CoroutineScope(Dispatchers.Main.immediate + Job())
+    // private val ownerScope = CoroutineScope(Dispatchers.Main.immediate + Job())
 
     // Used for tracking which nodes a frame read is applied to
     internal var currentNode: ComponentNode? = null
@@ -112,6 +112,8 @@
     var configurationChangeObserver: () -> Unit = {}
 
     private val _autofill = if (autofillSupported()) AndroidAutofill(this, autofillTree) else null
+
+    // Used as an ambient for performing autofill.
     val autofill: Autofill? get() = _autofill
 
     override var measureIteration: Long = 1L
@@ -179,17 +181,17 @@
 
     override fun onInvalidate(drawNode: DrawNode) {
         // TODO(mount): use ownerScope. This isn't supported by IR compiler yet
-//        ownerScope.launch {
+        // ownerScope.launch {
         invalidateRepaintBoundary(drawNode)
-//        }
+        // }
     }
 
     override fun onSizeChange(layoutNode: LayoutNode) {
         // TODO(mount): use ownerScope. This isn't supported by IR compiler yet
-//        ownerScope.launch {
+        // ownerScope.launch {
         layoutNode.visitChildren(::collectChildrenRepaintBoundaries)
         invalidateRepaintBoundary(layoutNode)
-//        }
+        // }
     }
 
     /**
@@ -213,9 +215,9 @@
 
     override fun onPositionChange(layoutNode: LayoutNode) {
         // TODO(mount): use ownerScope. This isn't supported by IR compiler yet
-//        ownerScope.launch {
+        // ownerScope.launch {
         invalidateRepaintBoundary(layoutNode)
-//        }
+        // }
     }
 
     override fun onRepaintBoundaryParamsChange(repaintBoundaryNode: RepaintBoundaryNode) {
diff --git a/ui/ui-platform/src/test/java/androidx/ui/ComposeUiRobolectricTestRunner.kt b/ui/ui-platform/src/test/java/androidx/ui/ComposeUiRobolectricTestRunner.kt
new file mode 100644
index 0000000..32f660f
--- /dev/null
+++ b/ui/ui-platform/src/test/java/androidx/ui/ComposeUiRobolectricTestRunner.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui
+
+import org.junit.runners.model.FrameworkMethod
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.internal.bytecode.InstrumentationConfiguration
+
+/**
+ * A [RobolectricTestRunner] for [androidx.ui].
+ *
+ * It has instrumentation turned off for the [androidx.ui] package.
+ *
+ * Robolectric tries to instrument Kotlin classes, and it throws errors when it encounters
+ * companion objects and constructors with default values for parameters. We don't need
+ * shadowing of our classes because we want to use the actual objects in our tests.
+ *
+ * We can also make the argument that no external developer should shadow any of androidx.ui, as
+ * they shouldn't be testing library code. This concern is being tracked by b/139828620 which will
+ * make a change to Robolectric code to prevent instrumentation of all classes under [androidx.ui].
+ */
+class ComposeUiRobolectricTestRunner(testClass: Class<*>) : RobolectricTestRunner(testClass) {
+    override fun createClassLoaderConfig(method: FrameworkMethod?): InstrumentationConfiguration {
+        val builder = InstrumentationConfiguration.Builder(super.createClassLoaderConfig(method))
+        builder.doNotInstrumentPackage("androidx.ui")
+        return builder.build()
+    }
+}
\ No newline at end of file
diff --git a/ui/ui-platform/src/test/java/androidx/ui/autofill/AndroidAutofillTest.kt b/ui/ui-platform/src/test/java/androidx/ui/autofill/AndroidAutofillTest.kt
index a135080..553974f 100644
--- a/ui/ui-platform/src/test/java/androidx/ui/autofill/AndroidAutofillTest.kt
+++ b/ui/ui-platform/src/test/java/androidx/ui/autofill/AndroidAutofillTest.kt
@@ -16,65 +16,72 @@
 
 package androidx.ui.autofill
 
-import android.content.Context
+import android.app.Activity
 import android.graphics.Rect
 import android.view.View
 import android.view.autofill.AutofillManager
 import androidx.test.filters.SmallTest
-import com.nhaarman.mockitokotlin2.eq
-import com.nhaarman.mockitokotlin2.mock
-import com.nhaarman.mockitokotlin2.whenever
-import com.nhaarman.mockitokotlin2.times
-import com.nhaarman.mockitokotlin2.verify
+import androidx.ui.ComposeUiRobolectricTestRunner
+import com.google.common.truth.Truth.assertThat
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 import org.junit.rules.ExpectedException
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
+import org.robolectric.Robolectric
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.Implementation
+import org.robolectric.annotation.Implements
+import org.robolectric.shadow.api.Shadow
 
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(ComposeUiRobolectricTestRunner::class)
+@Config(
+    manifest = Config.NONE,
+    shadows = [ShadowAutofillManager::class],
+    minSdk = 26
+)
 class AndroidAutofillTest {
 
     @get:Rule
     val expectedException = ExpectedException.none()!!
 
     private lateinit var androidAutofill: AndroidAutofill
-    private lateinit var autofillManager: AutofillManager
+    private lateinit var autofillManager: ShadowAutofillManager
     private lateinit var view: View
     private val autofillTree = AutofillTree()
 
     @Before
     fun setup() {
-        autofillManager = mock()
+        val activity = Robolectric.setupActivity(Activity::class.java)
+        view = View(activity)
+        activity.setContentView(view)
 
-        val context: Context = mock()
-        whenever(context.getSystemService(eq(AutofillManager::class.java)))
-            .thenReturn(autofillManager)
-
-        view = mock()
-        whenever(view.context).thenReturn(context)
+        autofillManager = Shadow.extract<ShadowAutofillManager>(
+            activity.getSystemService(AutofillManager::class.java)
+        )
 
         androidAutofill = AndroidAutofill(view, autofillTree)
     }
 
     @Test
     fun importantForAutofill_is_yes() {
-        verify(view).setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_YES)
+        assertThat(view.importantForAutofill).isEqualTo(View.IMPORTANT_FOR_AUTOFILL_YES)
     }
 
     @Test
     fun requestAutofillForNode_calls_notifyViewEntered() {
         // Arrange.
-        val autofillNode = AutofillNode( boundingBox = Rect(0, 0, 0, 0))
+        val bounds = Rect(0, 0, 0, 0)
+        val autofillNode = AutofillNode( boundingBox = bounds)
 
         // Act.
         androidAutofill.requestAutofillForNode(autofillNode)
 
         // Assert.
-        verify(autofillManager, times(1))
-            .notifyViewEntered(view, autofillNode.id, autofillNode.boundingBox!!)
+        assertThat(autofillManager.viewEnteredStats).containsExactly(
+            ShadowAutofillManager.NotifyViewEntered(view, autofillNode.id, bounds)
+        )
     }
 
     @Test
@@ -98,6 +105,27 @@
         androidAutofill.cancelAutofillForNode(autofillNode)
 
         // Assert.
-        verify(autofillManager, times(1)).notifyViewExited(view, autofillNode.id)
+        assertThat(autofillManager.viewExitedStats).containsExactly(
+            ShadowAutofillManager.NotifyViewExited(view, autofillNode.id)
+        )
+    }
+}
+
+@Implements(value = AutofillManager::class, minSdk = 26)
+internal class ShadowAutofillManager {
+    data class NotifyViewEntered(val view: View, val virtualId: Int, val rect: Rect)
+    data class NotifyViewExited(val view: View, val virtualId: Int)
+
+    val viewEnteredStats = mutableListOf<NotifyViewEntered>()
+    val viewExitedStats = mutableListOf<NotifyViewExited>()
+
+    @Implementation
+    fun notifyViewEntered(view: View, virtualId: Int, rect: Rect) {
+        viewEnteredStats += NotifyViewEntered(view, virtualId, rect)
+    }
+
+    @Implementation
+    fun notifyViewExited(view: View, virtualId: Int) {
+        viewExitedStats += NotifyViewExited(view, virtualId)
     }
 }
\ No newline at end of file
diff --git a/ui/ui-platform/src/test/java/androidx/ui/autofill/AndroidPerformAutofillTest.kt b/ui/ui-platform/src/test/java/androidx/ui/autofill/AndroidPerformAutofillTest.kt
index 6acec9c..f559223 100644
--- a/ui/ui-platform/src/test/java/androidx/ui/autofill/AndroidPerformAutofillTest.kt
+++ b/ui/ui-platform/src/test/java/androidx/ui/autofill/AndroidPerformAutofillTest.kt
@@ -16,38 +16,34 @@
 
 package androidx.ui.autofill
 
-import android.content.Context
+import android.app.Activity
 import android.graphics.Rect
+import android.util.SparseArray
 import android.view.View
-import android.view.autofill.AutofillManager
 import android.view.autofill.AutofillValue
 import androidx.test.filters.SmallTest
-import com.nhaarman.mockitokotlin2.eq
-import com.nhaarman.mockitokotlin2.mock
-import com.nhaarman.mockitokotlin2.times
-import com.nhaarman.mockitokotlin2.verify
-import com.nhaarman.mockitokotlin2.whenever
+import androidx.ui.ComposeUiRobolectricTestRunner
+import com.google.common.truth.Truth
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
+import org.robolectric.Robolectric
+import org.robolectric.annotation.Config
 
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(ComposeUiRobolectricTestRunner::class)
+@Config(
+    manifest = Config.NONE,
+    minSdk = 26)
 class AndroidPerformAutofillTest {
     private val autofillTree = AutofillTree()
     private lateinit var androidAutofill: AndroidAutofill
 
     @Before
     fun setup() {
-        val autofillManager: AutofillManager = mock()
-
-        val context: Context = mock()
-        whenever(context.getSystemService(eq(AutofillManager::class.java)))
-            .thenReturn(autofillManager)
-
-        val view: View = mock()
-        whenever(view.context).thenReturn(context)
+        val activity = Robolectric.setupActivity(Activity::class.java)
+        val view = View(activity)
+        activity.setContentView(view)
 
         androidAutofill = AndroidAutofill(view, autofillTree)
     }
@@ -55,48 +51,44 @@
     @Test
     fun performAutofill_name() {
         // Arrange.
-        val onFill: (String) -> Unit = mock()
+        val expectedValue = "First Name"
+        var autofilledValue = ""
         val autofillNode = AutofillNode(
-            >
+             autofilledValue = it },
             autofillTypes = listOf(AutofillType.Name),
             boundingBox = Rect(0, 0, 0, 0)
         )
         autofillTree += autofillNode
 
-        val autofillValue: AutofillValue = mock()
-        whenever(autofillValue.isText).thenReturn(true)
-        whenever(autofillValue.textValue).thenReturn("First Name")
-
-        val autofillValues = FakeSparseArray().apply { append(autofillNode.id, autofillValue) }
+        val autofillValues = SparseArray<AutofillValue>()
+            .apply { append(autofillNode.id, AutofillValue.forText(expectedValue)) }
 
         // Act.
         androidAutofill.performAutofill(autofillValues)
 
         // Assert.
-        verify(onFill, times(1)).invoke("First Name")
+        Truth.assertThat(autofilledValue).isEqualTo(expectedValue)
     }
 
     @Test
     fun performAutofill_email() {
         // Arrange.
-        val onFill: (String) -> Unit = mock()
+        val expectedValue = "email@google.com"
+        var autofilledValue = ""
         val autofillNode = AutofillNode(
-            >
+             autofilledValue = it },
             autofillTypes = listOf(AutofillType.EmailAddress),
             boundingBox = Rect(0, 0, 0, 0)
         )
         autofillTree += autofillNode
 
-        val autofillValue: AutofillValue = mock()
-        whenever(autofillValue.isText).thenReturn(true)
-        whenever(autofillValue.textValue).thenReturn("email@google.com")
-
-        val autofillValues = FakeSparseArray().apply { append(autofillNode.id, autofillValue) }
+        val autofillValues = SparseArray<AutofillValue>()
+            .apply { append(autofillNode.id, AutofillValue.forText(expectedValue)) }
 
         // Act.
         androidAutofill.performAutofill(autofillValues)
 
         // Assert.
-        verify(onFill, times(1)).invoke("email@google.com")
+        Truth.assertThat(autofilledValue).isEqualTo(expectedValue)
     }
 }
\ No newline at end of file
diff --git a/ui/ui-platform/src/test/java/androidx/ui/autofill/AndroidPopulateViewStructureTest.kt b/ui/ui-platform/src/test/java/androidx/ui/autofill/AndroidPopulateViewStructureTest.kt
index db8c641..9071352 100644
--- a/ui/ui-platform/src/test/java/androidx/ui/autofill/AndroidPopulateViewStructureTest.kt
+++ b/ui/ui-platform/src/test/java/androidx/ui/autofill/AndroidPopulateViewStructureTest.kt
@@ -16,38 +16,35 @@
 
 package androidx.ui.autofill
 
-import android.content.Context
+import android.app.Activity
 import android.graphics.Rect
 import android.view.View
 import android.view.ViewStructure
-import android.view.autofill.AutofillManager
 import androidx.test.filters.SmallTest
+import androidx.ui.ComposeUiRobolectricTestRunner
+import androidx.ui.test.android.fake.FakeViewStructure
 import com.google.common.truth.Truth.assertThat
-import com.nhaarman.mockitokotlin2.eq
-import com.nhaarman.mockitokotlin2.mock
-import com.nhaarman.mockitokotlin2.whenever
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
+import org.robolectric.Robolectric
+import org.robolectric.annotation.Config
 
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(ComposeUiRobolectricTestRunner::class)
+@Config(
+    manifest = Config.NONE,
+    minSdk = 26)
 class AndroidPopulateViewStructureTest {
+    private val PACKAGE_NAME = "androidx.ui.platform.test"
     private val autofillTree = AutofillTree()
     private lateinit var androidAutofill: AndroidAutofill
 
     @Before
     fun setup() {
-        val autofillManager: AutofillManager = mock()
-
-        val context: Context = mock()
-        whenever(context.getSystemService(eq(AutofillManager::class.java)))
-            .thenReturn(autofillManager)
-        whenever(context.packageName).thenReturn("com.google.testpackage")
-
-        val view: View = mock()
-        whenever(view.context).thenReturn(context)
+        val activity = Robolectric.setupActivity(Activity::class.java)
+        val view = View(activity)
+        activity.setContentView(view)
 
         androidAutofill = AndroidAutofill(view, autofillTree)
     }
@@ -82,7 +79,7 @@
         assertThat(viewStructure).isEqualTo(FakeViewStructure().apply {
             children.add(FakeViewStructure().apply {
                 virtualId = autofillNode.id
-                packageName = "com.google.testpackage"
+                packageName = PACKAGE_NAME
                 setAutofillType(View.AUTOFILL_TYPE_TEXT)
                 setAutofillHints(arrayOf(View.AUTOFILL_HINT_NAME))
                 setDimens(0, 0, 0, 0, 0, 0)
@@ -115,14 +112,14 @@
         assertThat(viewStructure).isEqualTo(FakeViewStructure().apply {
             children.add(FakeViewStructure().apply {
                 virtualId = nameAutofillNode.id
-                packageName = "com.google.testpackage"
+                packageName = PACKAGE_NAME
                 setAutofillType(View.AUTOFILL_TYPE_TEXT)
                 setAutofillHints(arrayOf(View.AUTOFILL_HINT_NAME))
                 setDimens(0, 0, 0, 0, 0, 0)
             })
             children.add(FakeViewStructure().apply {
                 virtualId = emailAutofillNode.id
-                packageName = "com.google.testpackage"
+                packageName = PACKAGE_NAME
                 setAutofillType(View.AUTOFILL_TYPE_TEXT)
                 setAutofillHints(arrayOf(View.AUTOFILL_HINT_EMAIL_ADDRESS))
                 setDimens(0, 0, 0, 0, 0, 0)
diff --git a/ui/ui-test/api/1.0.0-alpha01.txt b/ui/ui-test/api/1.0.0-alpha01.txt
index 3d54f9f..0cc2bc2 100644
--- a/ui/ui-test/api/1.0.0-alpha01.txt
+++ b/ui/ui-test/api/1.0.0-alpha01.txt
@@ -133,3 +133,11 @@
 
 }
 
+package androidx.ui.test.android.fake {
+
+  public final class FakeViewStructureKt {
+    ctor public FakeViewStructureKt();
+  }
+
+}
+
diff --git a/ui/ui-test/api/current.txt b/ui/ui-test/api/current.txt
index 3d54f9f..0cc2bc2 100644
--- a/ui/ui-test/api/current.txt
+++ b/ui/ui-test/api/current.txt
@@ -133,3 +133,11 @@
 
 }
 
+package androidx.ui.test.android.fake {
+
+  public final class FakeViewStructureKt {
+    ctor public FakeViewStructureKt();
+  }
+
+}
+
diff --git a/ui/ui-test/api/restricted_1.0.0-alpha01.txt b/ui/ui-test/api/restricted_1.0.0-alpha01.txt
index 3d54f9f..0cc2bc2 100644
--- a/ui/ui-test/api/restricted_1.0.0-alpha01.txt
+++ b/ui/ui-test/api/restricted_1.0.0-alpha01.txt
@@ -133,3 +133,11 @@
 
 }
 
+package androidx.ui.test.android.fake {
+
+  public final class FakeViewStructureKt {
+    ctor public FakeViewStructureKt();
+  }
+
+}
+
diff --git a/ui/ui-test/api/restricted_current.txt b/ui/ui-test/api/restricted_current.txt
index 3d54f9f..0cc2bc2 100644
--- a/ui/ui-test/api/restricted_current.txt
+++ b/ui/ui-test/api/restricted_current.txt
@@ -133,3 +133,11 @@
 
 }
 
+package androidx.ui.test.android.fake {
+
+  public final class FakeViewStructureKt {
+    ctor public FakeViewStructureKt();
+  }
+
+}
+
diff --git a/ui/ui-test/build.gradle b/ui/ui-test/build.gradle
index c936fdc..a9fafbe5 100644
--- a/ui/ui-test/build.gradle
+++ b/ui/ui-test/build.gradle
@@ -32,20 +32,18 @@
     kotlinPlugin project(path: ":compose:compose-compiler", configuration: "embeddablePlugin")
 
     implementation(KOTLIN_COMPOSE_STDLIB)
-
     implementation(ANDROIDX_TEST_RULES)
     implementation(ANDROIDX_TEST_RUNNER)
     implementation(ESPRESSO_CORE)
     implementation(JUNIT)
 
-    api project(":ui:ui-platform")
-
     implementation project(":compose:compose-runtime")
     implementation project(":ui:ui-animation")
     implementation project(":ui:ui-core")
     implementation project(":ui:ui-foundation")
     implementation project(":ui:ui-framework")
     implementation project(":ui:ui-layout")
+    implementation project(":ui:ui-platform")
 
     testImplementation(TRUTH)
 
diff --git a/ui/ui-platform/src/test/java/androidx/ui/autofill/AndroidFakes.kt b/ui/ui-test/src/main/java/androidx/ui/test/android/fake/FakeViewStructure.kt
similarity index 82%
rename from ui/ui-platform/src/test/java/androidx/ui/autofill/AndroidFakes.kt
rename to ui/ui-test/src/main/java/androidx/ui/test/android/fake/FakeViewStructure.kt
index 0d539e3..24fe108 100644
--- a/ui/ui-platform/src/test/java/androidx/ui/autofill/AndroidFakes.kt
+++ b/ui/ui-test/src/main/java/androidx/ui/test/android/fake/FakeViewStructure.kt
@@ -14,56 +14,60 @@
  * limitations under the License.
  */
 
-package androidx.ui.autofill
+package androidx.ui.test.android.fake
 
 import android.graphics.Matrix
 import android.graphics.Rect
+import android.os.Build
 import android.os.Bundle
 import android.os.LocaleList
-import android.util.SparseArray
+import android.os.Parcel
 import android.view.View
 import android.view.ViewStructure
 import android.view.autofill.AutofillId
 import android.view.autofill.AutofillValue
-import com.nhaarman.mockitokotlin2.mock
+import androidx.annotation.GuardedBy
+import androidx.annotation.RequiresApi
 
 /**
- * A fake implementation of { SparseArray } to use in tests.
+ * A fake implementation of [ViewStructure] to use in tests.
+ *
+ * @hide
  */
-internal class FakeSparseArray : SparseArray<AutofillValue>() {
-
-    private val map = mutableMapOf<Int, AutofillValue>()
-
-    override fun append(key: Int, value: AutofillValue?) {
-        value?.let { map.putIfAbsent(key, it) }
-    }
-
-    override fun size() = map.count()
-
-    override fun keyAt(index: Int) = map.asSequence().elementAt(index).component1()
-
-    override fun get(key: Int) = map[key] ?: error("no element for the specified key")
-}
-
-/**
- * A fake implementation of { ViewStructure } to use in tests.
- */
-internal data class FakeViewStructure(
+@RequiresApi(Build.VERSION_CODES.O)
+data class FakeViewStructure(
     var virtualId: Int = 0,
     var packageName: String? = null,
     var typeName: String? = null,
     var entryName: String? = null,
     var children: MutableList<FakeViewStructure> = mutableListOf(),
     var bounds: Rect? = null,
-    private val autofillId: AutofillId? = mock(),
+    private val autofillId: AutofillId? = generateAutofillId(),
     private var autofillType: Int = View.AUTOFILL_TYPE_NONE,
     private var autofillHints: Array<out String> = arrayOf()
 ) : ViewStructure() {
 
+    internal companion object {
+        @GuardedBy("this")
+        private var previousId = 0
+        private val NO_SESSION = 0
+
+        @Synchronized
+        private fun generateAutofillId(): AutofillId {
+            var autofillId: AutofillId? = null
+            useParcel { parcel ->
+                parcel.writeInt(++previousId) // View Id.
+                parcel.writeInt(NO_SESSION) // Flag.
+                autofillId = AutofillId.CREATOR.createFromParcel(parcel)
+            }
+            return autofillId ?: error("Could not generate autofill id")
+        }
+    }
+
     override fun getChildCount() = children.count()
 
     override fun addChildCount(childCount: Int): Int {
-        repeat(childCount) { children.add(FakeViewStructure()) }
+        repeat(childCount) { children.add(FakeViewStructure(autofillId = autofillId)) }
         return children.count() - childCount
     }
 
@@ -206,4 +210,15 @@
             other.right == right &&
             other.bottom == bottom &&
             other.top == top
+}
+
+/** Obtains a parcel and then recycles it correctly whether an exception is thrown or not. */
+private fun useParcel(block: (Parcel) -> Unit) {
+    var parcel: Parcel? = null
+    try {
+        parcel = Parcel.obtain()
+        block(parcel)
+    } finally {
+        parcel?.recycle()
+    }
 }
\ No newline at end of file