From 1a6970930bed0cd34195a3147bbc5da82a32f0bf Mon Sep 17 00:00:00 2001 From: Pablo Gamito Date: Mon, 16 Aug 2021 09:59:48 +0000 Subject: [PATCH] Add flicker test for quick switching back to app from launcher Bug: 178686893 Test: atest FlickerTests:QuickSwitchFromLauncherTest Change-Id: I78b3ee892b6893f3a59217b1078db58c6e4df94c --- .../QuickSwitchFromLauncherTest.kt | 359 ++++++++++++++++++ 1 file changed, 359 insertions(+) create mode 100644 tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt new file mode 100644 index 0000000000000..3c94be01a59b2 --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt @@ -0,0 +1,359 @@ +/* + * Copyright (C) 2021 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 com.android.server.wm.flicker.quickswitch + +import android.app.Instrumentation +import android.platform.test.annotations.Postsubmit +import android.platform.test.annotations.RequiresDevice +import android.view.Surface +import android.view.WindowManagerPolicyConstants +import androidx.test.platform.app.InstrumentationRegistry +import com.android.server.wm.flicker.FlickerBuilderProvider +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.LAUNCHER_COMPONENT +import com.android.server.wm.flicker.annotation.Group1 +import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.server.wm.flicker.entireScreenCovered +import com.android.server.wm.flicker.helpers.SimpleAppHelper +import com.android.server.wm.flicker.helpers.WindowUtils +import com.android.server.wm.flicker.navBarLayerIsVisible +import com.android.server.wm.flicker.navBarLayerRotatesAndScales +import com.android.server.wm.flicker.navBarWindowIsVisible +import com.android.server.wm.flicker.startRotation +import com.android.server.wm.flicker.statusBarLayerIsVisible +import com.android.server.wm.flicker.statusBarWindowIsVisible +import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.SNAPSHOT_COMPONENT +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test quick switching to last opened app from launcher + * + * To run this test: `atest FlickerTests:QuickSwitchFromLauncherTest` + * + * Actions: + * Launch an app + * Navigate home to show launcher + * Swipe right from the bottom of the screen to quick switch back to the app + * + */ +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@Group1 +class QuickSwitchFromLauncherTest(private val testSpec: FlickerTestParameter) { + private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + private val testApp = SimpleAppHelper(instrumentation) + private val startDisplayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation) + + @FlickerBuilderProvider + fun buildFlicker(): FlickerBuilder { + return FlickerBuilder(instrumentation).apply { + setup { + eachRun { + testApp.launchViaIntent(wmHelper) + device.pressHome() + wmHelper.waitForHomeActivityVisible() + wmHelper.waitForWindowSurfaceDisappeared(testApp.component) + } + } + transitions { + // Swipe right from bottom to quick switch back + // NOTE: We don't perform an edge-to-edge swipe but instead only swipe in the middle + // as to not accidentally trigger a swipe back or forward action which would result + // in the same behavior but not testing quick swap. + device.swipe( + startDisplayBounds.bounds.right / 3, + startDisplayBounds.bounds.bottom, + 2 * startDisplayBounds.bounds.right / 3, + startDisplayBounds.bounds.bottom, + 50 + ) + + wmHelper.waitForFullScreenApp(testApp.component) + wmHelper.waitForAppTransitionIdle() + } + + teardown { + eachRun { + testApp.exit() + } + } + } + } + + /** + * Checks that [testApp] windows fill the entire screen (i.e. is "fullscreen") at the end of the + * transition once we have fully quick switched from the launcher back to the [testApp]. + */ + @Postsubmit + @Test + fun endsWithAppWindowsCoveringFullScreen() { + testSpec.assertWmEnd { + this.frameRegion(testApp.component).coversExactly(startDisplayBounds) + } + } + + /** + * Checks that [testApp] layers fill the entire screen (i.e. is "fullscreen") at the end of the + * transition once we have fully quick switched from the launcher back to the [testApp]. + */ + @Postsubmit + @Test + fun endsWithAppLayersCoveringFullScreen() { + testSpec.assertLayersEnd { + this.visibleRegion(testApp.component).coversExactly(startDisplayBounds) + } + } + + /** + * Checks that [testApp] is the top window at the end of the transition once we have fully quick + * switched from the launcher back to the [testApp]. + */ + @Postsubmit + @Test + fun endsWithAppBeingOnTop() { + testSpec.assertWmEnd { + this.isAppWindowOnTop(testApp.component) + } + } + + /** + * Checks that the transition starts with the home activity being tagged as visible. + */ + @Postsubmit + @Test + fun startsWithHomeActivityFlaggedVisible() { + testSpec.assertWmStart { + this.isHomeActivityVisible(true) + } + } + + /** + * Checks that the transition starts with the launcher windows filling/covering exactly the + * entirety of the display. + */ + @Postsubmit + @Test + fun startsWithLauncherWindowsCoverFullScreen() { + testSpec.assertWmStart { + this.frameRegion(LAUNCHER_COMPONENT).coversExactly(startDisplayBounds) + } + } + + /** + * Checks that the transition starts with the launcher layers filling/covering exactly the + * entirety of the display. + */ + @Postsubmit + @Test + fun startsWithLauncherLayersCoverFullScreen() { + testSpec.assertLayersStart { + this.visibleRegion(LAUNCHER_COMPONENT).coversExactly(startDisplayBounds) + } + } + + /** + * Checks that the transition starts with the launcher being the top window. + */ + @Postsubmit + @Test + fun startsWithLauncherBeingOnTop() { + testSpec.assertWmStart { + this.isAppWindowOnTop(LAUNCHER_COMPONENT) + } + } + + /** + * Checks that the transition ends with the home activity being flagged as not visible. By this + * point we should have quick switched away from the launcher back to the [testApp]. + */ + @Postsubmit + @Test + fun endsWithHomeActivityFlaggedInvisible() { + testSpec.assertWmEnd { + this.isHomeActivityVisible(false) + } + } + + /** + * Checks that the [testApp] window starts off invisible and once it becomes visible, stays + * visible until the end of the transition. + * + * NOTE: It does not ensure that the app becomes visible at some point ([testApp] can stay + * invisible throughout and pass this check), that is what [endsWithAppBeingVisible] is for. + */ + @Postsubmit + @Test + fun appWindowBecomesAndStaysVisible() { + testSpec.assertWm { + this.isAppWindowInvisible(testApp.component, ignoreActivity = true) + .then() + .isAppWindowVisible(testApp.component, ignoreActivity = true) + } + } + + /** + * Checks that the [testApp] layer starts off invisible and once it becomes visible, stays + * visible until the end of the transition. + * + * NOTE: It does not ensure that the app becomes visible at some point ([testApp] can stay + * invisible throughout and pass this check). + */ + @Postsubmit + @Test + fun appLayerBecomesAndStaysVisible() { + testSpec.assertLayers { + this.isInvisible(testApp.component) + .then() + .isVisible(testApp.component) + } + } + + /** + * Checks that the launcher window starts off visible and once it becomes invisible, stays + * invisible until the end of the transition. + * + * NOTE: It does not ensure that the launcher becomes invisible at some point, (the launcher can + * stay invisible throughout and pass this check). + */ + @Postsubmit + @Test + fun launcherWindowBecomesAndStaysInvisible() { + testSpec.assertWm { + this.isAppWindowVisible(LAUNCHER_COMPONENT) + .then() + .isAppWindowInvisible(LAUNCHER_COMPONENT) + } + } + + /** + * Checks that the launcher layer starts off visible and once it becomes invisible, stays + * invisible until the end of the transition. + * + * NOTE: It does not ensure that the launcher becomes invisible at some point, (the launcher can + * stay invisible throughout and pass this check). + */ + @Postsubmit + @Test + fun launcherLayerBecomesAndStaysInvisible() { + testSpec.assertLayers { + this.isVisible(LAUNCHER_COMPONENT) + .then() + .isInvisible(LAUNCHER_COMPONENT) + } + } + + /** + * Checks that the launcher window is visible at least until the app window is visible. Ensures + * that at any point, either the launcher or [testApp] windows are at least partially visible. + */ + @Postsubmit + @Test + fun appWindowIsVisibleOnceLauncherWindowIsInvisible() { + testSpec.assertWm { + this.isAppWindowVisible(LAUNCHER_COMPONENT) + .then() + .isAppWindowVisible(SNAPSHOT_COMPONENT) + .then() + .isAppWindowVisible(testApp.component) + } + } + + /** + * Checks that the launcher layer is visible at least until the app layer is visible. Ensures + * that at any point, either the launcher or [testApp] layers are at least partially visible. + */ + @Postsubmit + @Test + fun appLayerIsVisibleOnceLauncherLayerIsInvisible() { + testSpec.assertLayers { + this.isVisible(LAUNCHER_COMPONENT) + .then() + .isVisible(SNAPSHOT_COMPONENT) + .then() + .isVisible(testApp.component) + } + } + + /** + * Checks that the navbar window is visible throughout the entire transition. + */ + @Postsubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsVisible() + + /** + * Checks that the navbar layer is visible throughout the entire transition. + */ + @Postsubmit + @Test + fun navBarLayerAlwaysIsVisible() = testSpec.navBarLayerIsVisible() + + /** + * Checks that the navbar is always in the right position and covers the expected region. + * + * NOTE: This doesn't check that the navbar is visible or not. + */ + @Postsubmit + @Test + fun navbarIsAlwaysInRightPosition() = + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation) + + /** + * Checks that the status bar window is visible throughout the entire transition. + */ + @Postsubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsVisible() + + /** + * Checks that the status bar layer is visible throughout the entire transition. + */ + @Postsubmit + @Test + fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsVisible() + + /** + * Checks that the screen is always fully covered by visible layers throughout the transition. + */ + @Postsubmit + @Test + fun screenIsAlwaysFilled() = testSpec.entireScreenCovered(testSpec.config.startRotation) + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection { + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests( + repetitions = 5, + supportedNavigationModes = listOf( + WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY + ), + // TODO: Test with 90 rotation + supportedRotations = listOf(Surface.ROTATION_0) + ) + } + } +} \ No newline at end of file