summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Sergey Nikolaienkov <sergeynv@google.com> 2020-12-19 08:53:34 +0000
committer Sergey Nikolaienkov <sergeynv@google.com> 2020-12-28 06:22:41 +0000
commit7599ffc7b09a97757330f6dab9a7f89e15e9af1b (patch)
tree4596bb0ad12f42efb74ec36e3a8aa34286de54f6
parent19911209758f11f6c02b6880a1419f728ff6ba63 (diff)
Fix Tv Pip tests
Work around the eccentricities of the Accessibility framework on TV in Pip tests. Bug: 174818743 Test: atest WMShellFlickerTests:TvPipBasicTest Test: atest WMShellFlickerTests:TvPipMenuTests Test: atest WMShellFlickerTests:TvPipNotificationTests Change-Id: I00e12c5e88a7c4a6e9f3c18d7c978288d28ab43c
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt139
1 files changed, 89 insertions, 50 deletions
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt
index 4a38b0e94ba9..1b73920046dc 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt
@@ -33,71 +33,102 @@ private const val TV_PIP_MENU_FULLSCREEN_BUTTON_ID = "tv_pip_menu_fullscreen_but
private const val FOCUS_ATTEMPTS = 10
private const val WAIT_TIME_MS = 3_000L
+private val TV_PIP_MENU_SELECTOR =
+ By.res(SYSTEM_UI_PACKAGE_NAME, TV_PIP_MENU_ROOT_ID)
+private val TV_PIP_MENU_BUTTONS_CONTAINER_SELECTOR =
+ By.res(SYSTEM_UI_PACKAGE_NAME, TV_PIP_MENU_BUTTONS_CONTAINER_ID)
private val TV_PIP_MENU_CLOSE_BUTTON_SELECTOR =
By.res(SYSTEM_UI_PACKAGE_NAME, TV_PIP_MENU_CLOSE_BUTTON_ID)
private val TV_PIP_MENU_FULLSCREEN_BUTTON_SELECTOR =
By.res(SYSTEM_UI_PACKAGE_NAME, TV_PIP_MENU_FULLSCREEN_BUTTON_ID)
-private val tvPipMenuSelector = By.res(SYSTEM_UI_PACKAGE_NAME, TV_PIP_MENU_ROOT_ID)
-
-fun UiDevice.pressWindowKey() = pressKeyCode(KeyEvent.KEYCODE_WINDOW)
-
fun UiDevice.waitForTvPipMenu(): UiObject2? =
- wait(Until.findObject(tvPipMenuSelector), WAIT_TIME_MS)
+ wait(Until.findObject(TV_PIP_MENU_SELECTOR), WAIT_TIME_MS)
-fun UiDevice.waitForTvPipMenuToClose(): Boolean = wait(Until.gone(tvPipMenuSelector), WAIT_TIME_MS)
+fun UiDevice.waitForTvPipMenuToClose(): Boolean =
+ wait(Until.gone(TV_PIP_MENU_SELECTOR), WAIT_TIME_MS)
fun UiDevice.findTvPipMenuControls(): UiObject2? =
- findObject(tvPipMenuSelector)
- ?.findObject(By.res(SYSTEM_UI_PACKAGE_NAME, TV_PIP_MENU_BUTTONS_CONTAINER_ID))
+ findTvPipMenuElement(TV_PIP_MENU_BUTTONS_CONTAINER_SELECTOR)
fun UiDevice.findTvPipMenuCloseButton(): UiObject2? =
- findObject(tvPipMenuSelector)?.findObject(TV_PIP_MENU_CLOSE_BUTTON_SELECTOR)
+ findTvPipMenuElement(TV_PIP_MENU_CLOSE_BUTTON_SELECTOR)
+
+fun UiDevice.findTvPipMenuFullscreenButton(): UiObject2? =
+ findTvPipMenuElement(TV_PIP_MENU_FULLSCREEN_BUTTON_SELECTOR)
+
+fun UiDevice.findTvPipMenuElementWithDescription(desc: String): UiObject2? =
+ findTvPipMenuElement(By.desc(desc))
+
+private fun UiDevice.findTvPipMenuElement(selector: BySelector): UiObject2? =
+ findObject(TV_PIP_MENU_SELECTOR)?.findObject(selector)
+
+fun UiDevice.waitForTvPipMenuElementWithDescription(desc: String): UiObject2? {
+ // Ideally, we'd want to wait for an element with the given description that has the Pip Menu as
+ // its parent, but the API does not allow us to construct a query exactly that way.
+ // So instead we'll wait for a Pip Menu that has the element, which we are looking for, as a
+ // descendant and then retrieve the element from the menu and return to the caller of this
+ // method.
+ val elementSelector = By.desc(desc)
+ val menuContainingElementSelector = By.copy(TV_PIP_MENU_SELECTOR).hasDescendant(elementSelector)
+
+ return wait(Until.findObject(menuContainingElementSelector), WAIT_TIME_MS)
+ ?.findObject(elementSelector)
+}
+
+fun UiDevice.waitUntilTvPipMenuElementWithDescriptionIsGone(desc: String): Boolean? {
+ val elementSelector = By.desc(desc)
+ val menuContainingElementSelector = By.copy(TV_PIP_MENU_SELECTOR).hasDescendant(elementSelector)
+
+ return wait(Until.gone(menuContainingElementSelector), WAIT_TIME_MS)
+}
fun UiDevice.clickTvPipMenuCloseButton() {
- focusOnObjectInTvPipMenu(TV_PIP_MENU_CLOSE_BUTTON_SELECTOR) ||
+ focusOnAndClickTvPipMenuElement(TV_PIP_MENU_CLOSE_BUTTON_SELECTOR) ||
error("Could not focus on the Close button")
- pressDPadCenter()
}
-fun UiDevice.findTvPipMenuFullscreenButton(): UiObject2? =
- findObject(tvPipMenuSelector)?.findObject(TV_PIP_MENU_FULLSCREEN_BUTTON_SELECTOR)
-
fun UiDevice.clickTvPipMenuFullscreenButton() {
- focusOnObjectInTvPipMenu(TV_PIP_MENU_FULLSCREEN_BUTTON_SELECTOR) ||
+ focusOnAndClickTvPipMenuElement(TV_PIP_MENU_FULLSCREEN_BUTTON_SELECTOR) ||
error("Could not focus on the Fullscreen button")
- pressDPadCenter()
}
-fun UiDevice.findTvPipMenuElementWithDescription(desc: String): UiObject2? =
- findObject(tvPipMenuSelector)?.findObject(By.desc(desc).pkg(SYSTEM_UI_PACKAGE_NAME))
-
fun UiDevice.clickTvPipMenuElementWithDescription(desc: String) {
- focusOnObjectInTvPipMenu(By.desc(desc).pkg(SYSTEM_UI_PACKAGE_NAME)) ||
+ focusOnAndClickTvPipMenuElement(By.desc(desc).pkg(SYSTEM_UI_PACKAGE_NAME)) ||
error("Could not focus on the Pip menu object with \"$desc\" description")
- pressDPadCenter()
+ // So apparently Accessibility framework on TV is not very reliable and sometimes the state of
+ // the tree of accessibility nodes as seen by the accessibility clients kind of lags behind of
+ // the "real" state of the "UI tree". It seems, however, that moving focus around the tree
+ // forces the AccessibilityNodeInfo tree to get properly updated.
+ // So since we suspect that clicking on a Pip Menu element may cause some UI changes and we want
+ // those changes to be seen by the UiAutomator, which is using Accessibility framework under the
+ // hood for inspecting UI, we'll move the focus around a little.
+ moveFocus()
}
-fun UiDevice.waitForTvPipMenuElementWithDescription(desc: String): UiObject2? {
- val buttonSelector = By.desc(desc)
- val menuWithButtonSelector = By.copy(tvPipMenuSelector).hasDescendant(buttonSelector)
- return wait(Until.findObject(menuWithButtonSelector), WAIT_TIME_MS)
- ?.findObject(buttonSelector)
-}
-
-fun UiDevice.waitUntilTvPipMenuElementWithDescriptionIsGone(desc: String): Boolean? =
- wait(Until.gone(By.copy(tvPipMenuSelector).hasDescendant(By.desc(desc))), WAIT_TIME_MS)
+private fun UiDevice.focusOnAndClickTvPipMenuElement(selector: BySelector): Boolean {
+ repeat(FOCUS_ATTEMPTS) {
+ val element = findTvPipMenuElement(selector)
+ ?: error("The Pip Menu element we try to focus on is gone.")
+
+ if (element.isFocusedOrHasFocusedChild) {
+ pressDPadCenter()
+ return true
+ }
+
+ findTvPipMenuElement(By.focused(true))?.let { focused ->
+ if (element.visibleCenter.x < focused.visibleCenter.x)
+ pressDPadLeft() else pressDPadRight()
+ waitForIdle()
+ } ?: error("Pip menu does not contain a focused element")
+ }
-fun UiObject2.isFullscreen(uiDevice: UiDevice): Boolean = visibleBounds.run {
- height() == uiDevice.displayHeight && width() == uiDevice.displayWidth
+ return false
}
-val UiObject2.isFocusedOrHasFocusedChild: Boolean
- get() = isFocused || findObject(By.focused(true)) != null
-
fun UiDevice.closeTvPipWindow() {
// Check if Pip menu is Open. If it's not, open it.
- if (findObject(tvPipMenuSelector) == null) {
+ if (findObject(TV_PIP_MENU_SELECTOR) == null) {
pressWindowKey()
waitForTvPipMenu() ?: error("Could not open Pip menu")
}
@@ -106,17 +137,25 @@ fun UiDevice.closeTvPipWindow() {
waitForTvPipMenuToClose()
}
-private fun UiDevice.focusOnObjectInTvPipMenu(objectSelector: BySelector): Boolean {
- repeat(FOCUS_ATTEMPTS) {
- val menu = findObject(tvPipMenuSelector) ?: error("Pip Menu is now shown")
- val objectToFocus = menu.findObject(objectSelector)
- .apply { if (isFocusedOrHasFocusedChild) return true }
- ?: error("The object we try to focus on is gone.")
- val currentlyFocused = menu.findObject(By.focused(true))
- ?: error("Pip menu does not contain a focused element")
- if (objectToFocus.visibleCenter.x < currentlyFocused.visibleCenter.x)
- pressDPadLeft() else pressDPadRight()
- waitForIdle()
- }
- return false
-} \ No newline at end of file
+/**
+ * Simply presses the D-Pad Left and Right buttons once, which should move the focus on the screen,
+ * which should cause Accessibility events to be fired, which should, hopefully, properly update
+ * AccessibilityNodeInfo tree dispatched by the platform to the Accessibility services, one of which
+ * is the UiAutomator.
+ */
+private fun UiDevice.moveFocus() {
+ waitForIdle()
+ pressDPadLeft()
+ waitForIdle()
+ pressDPadRight()
+ waitForIdle()
+}
+
+fun UiDevice.pressWindowKey() = pressKeyCode(KeyEvent.KEYCODE_WINDOW)
+
+fun UiObject2.isFullscreen(uiDevice: UiDevice): Boolean = visibleBounds.run {
+ height() == uiDevice.displayHeight && width() == uiDevice.displayWidth
+}
+
+val UiObject2.isFocusedOrHasFocusedChild: Boolean
+ get() = isFocused || findObject(By.focused(true)) != null