diff options
| author | 2020-12-19 08:53:34 +0000 | |
|---|---|---|
| committer | 2020-12-28 06:22:41 +0000 | |
| commit | 7599ffc7b09a97757330f6dab9a7f89e15e9af1b (patch) | |
| tree | 4596bb0ad12f42efb74ec36e3a8aa34286de54f6 | |
| parent | 19911209758f11f6c02b6880a1419f728ff6ba63 (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.kt | 139 |
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 |