mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-02 19:17:10 +00:00
test(sidecar): pin the tree-guarded keyboard dismissal
This commit is contained in:
1 parent
b4cd9d5cd1
commit
1e350f7fd3
1 file changed
+120
@@ -240,8 +240,128 @@ class InputTextTest {
|
|||||||
}
|
}
|
||||||
assertEquals(listOf("dumpsys input_method"), commands)
|
assertEquals(listOf("dumpsys input_method"), commands)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Typing is not the only thing that raises the keyboard: tapping a field
|
||||||
|
// raises it too, and nothing was closing that one. The snapshot the picker
|
||||||
|
// chooses from is missing every app node the keyboard covers, so the step
|
||||||
|
// spends its budget choosing between the few targets left. Closing it
|
||||||
|
// before the tree is read is what gives the step its targets back.
|
||||||
|
@Test fun aKeyboardInTheTreeIsClosedBeforeTheTreeIsReturned() {
|
||||||
|
var backs = 0
|
||||||
|
val reads = mutableListOf<String>()
|
||||||
|
val settled = treeWithoutKeyboard(
|
||||||
|
IME_TREE,
|
||||||
|
IME_PACKAGE,
|
||||||
|
dismiss = { backs++ },
|
||||||
|
reread = { APP_TREE.also { reads.add(it) } },
|
||||||
|
sleep = {},
|
||||||
|
)
|
||||||
|
assertEquals(APP_TREE, settled)
|
||||||
|
assertEquals(1, backs, "one BACK closes the keyboard")
|
||||||
|
assertEquals(1, reads.size, "the tree is re-read once it is gone")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The guard has to be the tree itself. BACK with no keyboard open
|
||||||
|
// navigates out of the screen, so a snapshot that pressed it on every read
|
||||||
|
// would walk the fuzzer backwards out of the app a step at a time.
|
||||||
|
@Test fun aTreeWithNoKeyboardIsReturnedUntouched() {
|
||||||
|
var backs = 0
|
||||||
|
var reads = 0
|
||||||
|
val settled = treeWithoutKeyboard(
|
||||||
|
APP_TREE,
|
||||||
|
IME_PACKAGE,
|
||||||
|
dismiss = { backs++ },
|
||||||
|
reread = {
|
||||||
|
reads++
|
||||||
|
APP_TREE
|
||||||
|
},
|
||||||
|
sleep = {},
|
||||||
|
)
|
||||||
|
assertEquals(APP_TREE, settled)
|
||||||
|
assertEquals(0, backs, "no keyboard in the tree means no BACK")
|
||||||
|
assertEquals(0, reads, "and no second hierarchy read to pay for")
|
||||||
|
}
|
||||||
|
|
||||||
|
// An unknown IME package is the honest "cannot tell", and the safe way to
|
||||||
|
// be wrong is to leave the keyboard up rather than press BACK blind.
|
||||||
|
@Test fun anUnknownImePackageSendsNoBack() {
|
||||||
|
var backs = 0
|
||||||
|
assertEquals(
|
||||||
|
IME_TREE,
|
||||||
|
treeWithoutKeyboard(
|
||||||
|
IME_TREE,
|
||||||
|
null,
|
||||||
|
dismiss = { backs++ },
|
||||||
|
reread = { APP_TREE },
|
||||||
|
sleep = {},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
assertEquals(0, backs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A keyboard the app puts straight back gets ONE back press, not one per
|
||||||
|
// re-read. The flag behind the older dismissal lags a BACK by up to 0.6s,
|
||||||
|
// and a burst of them inside that window is how a dismissal turns into
|
||||||
|
// navigation.
|
||||||
|
@Test fun aKeyboardThatStaysUpIsNotBackPressedRepeatedly() {
|
||||||
|
var backs = 0
|
||||||
|
var reads = 0
|
||||||
|
val settled = treeWithoutKeyboard(
|
||||||
|
IME_TREE,
|
||||||
|
IME_PACKAGE,
|
||||||
|
dismiss = { backs++ },
|
||||||
|
reread = {
|
||||||
|
reads++
|
||||||
|
IME_TREE
|
||||||
|
},
|
||||||
|
sleep = {},
|
||||||
|
)
|
||||||
|
assertEquals(IME_TREE, settled, "the caller still gets a tree")
|
||||||
|
assertEquals(1, backs)
|
||||||
|
assertTrue(
|
||||||
|
reads in 1..KEYBOARD_DISMISS_READS,
|
||||||
|
"bounded re-reads, got $reads",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test fun imePackageOfReadsTheComponentAndRejectsNonsense() {
|
||||||
|
assertEquals(
|
||||||
|
"com.google.android.inputmethod.latin",
|
||||||
|
imePackageOf(
|
||||||
|
"com.google.android.inputmethod.latin/.LatinIME\n",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
assertEquals(null, imePackageOf("null"))
|
||||||
|
assertEquals(null, imePackageOf(""))
|
||||||
|
assertEquals(null, imePackageOf(" \n"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test fun treeShowsImeMatchesTheImesOwnViewIdsOnly() {
|
||||||
|
assertTrue(treeShowsIme(IME_TREE, IME_PACKAGE))
|
||||||
|
assertTrue(!treeShowsIme(APP_TREE, IME_PACKAGE))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private const val IME_PACKAGE = "com.google.android.inputmethod.latin"
|
||||||
|
|
||||||
|
private val APP_TREE =
|
||||||
|
"""
|
||||||
|
{"attributes":{"resource-id":"AddAccountScreen"},"children":[
|
||||||
|
{"attributes":{"resource-id":"AccountNameField"},"children":[]},
|
||||||
|
{"attributes":{"resource-id":"AddAccountSubmit"},"children":[]}]}
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
// The submit control is gone: an open keyboard does not merely cover the node,
|
||||||
|
// it takes it out of the tree the picker enumerates.
|
||||||
|
private val IME_TREE =
|
||||||
|
"""
|
||||||
|
{"attributes":{"resource-id":"AddAccountScreen"},"children":[
|
||||||
|
{"attributes":{"resource-id":"AccountNameField"},"children":[]},
|
||||||
|
{"attributes":{
|
||||||
|
"resource-id":"com.google.android.inputmethod.latin:id/keyboard_holder"
|
||||||
|
},"children":[]}]}
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
private val IME_OPEN_DUMPSYS =
|
private val IME_OPEN_DUMPSYS =
|
||||||
"""
|
"""
|
||||||
mCurMethodId=com.google.android.inputmethod.latin/.LatinIME
|
mCurMethodId=com.google.android.inputmethod.latin/.LatinIME
|
||||||
|
|||||||
Reference in new issue
Block a user