Merge "[DO NOT MERGE] Sync flicker from master to sc-v2" into sc-v2-dev

This commit is contained in:
TreeHugger Robot
2021-09-04 00:15:31 +00:00
committed by Android (Google) Code Review
52 changed files with 2636 additions and 430 deletions

View File

@@ -41,9 +41,9 @@ import java.io.PrintWriter;
*/
class ImeTracingServerImpl extends ImeTracing {
private static final String TRACE_DIRNAME = "/data/misc/wmtrace/";
private static final String TRACE_FILENAME_CLIENTS = "ime_trace_clients.pb";
private static final String TRACE_FILENAME_IMS = "ime_trace_service.pb";
private static final String TRACE_FILENAME_IMMS = "ime_trace_managerservice.pb";
private static final String TRACE_FILENAME_CLIENTS = "ime_trace_clients.winscope";
private static final String TRACE_FILENAME_IMS = "ime_trace_service.winscope";
private static final String TRACE_FILENAME_IMMS = "ime_trace_managerservice.winscope";
private static final int BUFFER_CAPACITY = 4096 * 1024;
// Needed for winscope to auto-detect the dump type. Explained further in

View File

@@ -28,7 +28,7 @@ import java.io.File;
*/
public class ProtoLogImpl extends BaseProtoLogImpl {
private static final int BUFFER_CAPACITY = 1024 * 1024;
private static final String LOG_FILENAME = "/data/misc/wmtrace/wm_log.pb";
private static final String LOG_FILENAME = "/data/misc/wmtrace/wm_log.winscope";
private static final String VIEWER_CONFIG_FILENAME = "/system/etc/protolog.conf.json.gz";
private static ProtoLogImpl sServiceInstance = null;

View File

@@ -17,11 +17,14 @@
package com.android.wm.shell.flicker.helpers
import android.app.Instrumentation
import android.graphics.Rect
import android.media.session.MediaController
import android.media.session.MediaSessionManager
import android.os.SystemClock
import androidx.test.uiautomator.By
import androidx.test.uiautomator.BySelector
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.helpers.FIND_TIMEOUT
import com.android.server.wm.flicker.helpers.SYSTEMUI_PACKAGE
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.pip.tv.closeTvPipWindow
@@ -85,6 +88,10 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
// Wait on WMHelper or simply wait for 3 seconds
wmHelper?.waitFor("hasPipWindow") { it.wmState.hasPipWindow() } ?: SystemClock.sleep(3_000)
// when entering pip, the dismiss button is visible at the start. to ensure the pip
// animation is complete, wait until the pip dismiss button is no longer visible.
// b/176822698: dismiss-only state will be removed in the future
uiDevice.wait(Until.gone(By.res(SYSTEMUI_PACKAGE, "dismiss")), FIND_TIMEOUT)
}
fun clickStartMediaSessionButton() {
@@ -113,25 +120,25 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
}
}
private fun getWindowRect(wmHelper: WindowManagerStateHelper): Rect {
val windowRegion = wmHelper.getWindowRegion(component)
require(!windowRegion.isEmpty) {
"Unable to find a PIP window in the current state"
}
return windowRegion.bounds
}
/**
* Expands the pip window and dismisses it by clicking on the X button.
*
* Note, currently the View coordinates reported by the accessibility are relative to
* the window, so the correct coordinates need to be calculated
*
* For example, in a PIP window located at Rect(508, 1444 - 1036, 1741), the
* dismiss button coordinates are shown as Rect(650, 0 - 782, 132), with center in
* Point(716, 66), instead of Point(970, 1403)
*
* See b/179337864
*/
fun closePipWindow(wmHelper: WindowManagerStateHelper) {
if (isTelevision) {
uiDevice.closeTvPipWindow()
} else {
expandPipWindow(wmHelper)
val windowRect = getWindowRect(wmHelper)
uiDevice.click(windowRect.centerX(), windowRect.centerY())
val exitPipObject = uiDevice.findObject(By.res(SYSTEMUI_PACKAGE, "dismiss"))
requireNotNull(exitPipObject) { "PIP window dismiss button not found" }
?: error("PIP window dismiss button not found")
val dismissButtonBounds = exitPipObject.visibleBounds
uiDevice.click(dismissButtonBounds.centerX(), dismissButtonBounds.centerY())
}
@@ -142,32 +149,29 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
}
/**
* Click once on the PIP window to expand it
* Close the pip window by pressing the expand button
*/
fun expandPipWindow(wmHelper: WindowManagerStateHelper) {
val windowRegion = wmHelper.getWindowRegion(component)
require(!windowRegion.isEmpty) {
"Unable to find a PIP window in the current state"
}
val windowRect = windowRegion.bounds
fun expandPipWindowToApp(wmHelper: WindowManagerStateHelper) {
val windowRect = getWindowRect(wmHelper)
uiDevice.click(windowRect.centerX(), windowRect.centerY())
// Ensure WindowManagerService wait until all animations have completed
// search and interact with the expand button
val expandSelector = By.res(SYSTEMUI_PACKAGE, "expand_button")
uiDevice.wait(Until.hasObject(expandSelector), FIND_TIMEOUT)
val expandPipObject = uiDevice.findObject(expandSelector)
?: error("PIP window expand button not found")
val expandButtonBounds = expandPipObject.visibleBounds
uiDevice.click(expandButtonBounds.centerX(), expandButtonBounds.centerY())
wmHelper.waitFor("!hasPipWindow") { !it.wmState.hasPipWindow() }
wmHelper.waitForAppTransitionIdle()
mInstrumentation.uiAutomation.syncInputTransactions()
}
/**
* Double click on the PIP window to reopen to app
* Double click on the PIP window to expand it
*/
fun expandPipWindowToApp(wmHelper: WindowManagerStateHelper) {
val windowRegion = wmHelper.getWindowRegion(component)
require(!windowRegion.isEmpty) {
"Unable to find a PIP window in the current state"
}
val windowRect = windowRegion.bounds
fun doubleClickPipWindow(wmHelper: WindowManagerStateHelper) {
val windowRect = getWindowRect(wmHelper)
uiDevice.click(windowRect.centerX(), windowRect.centerY())
uiDevice.click(windowRect.centerX(), windowRect.centerY())
wmHelper.waitFor("!hasPipWindow") { !it.wmState.hasPipWindow() }
wmHelper.waitForAppTransitionIdle()
}

View File

@@ -17,7 +17,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
import android.content.ComponentName
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.FlakyTest
@@ -113,7 +112,7 @@ class ExitPrimarySplitScreenShowSecondaryFullscreen(
override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
super.visibleLayersShownMoreThanOneConsecutiveEntry()
@Postsubmit
@Presubmit
@Test
override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
super.visibleWindowsShownMoreThanOneConsecutiveEntry()

View File

@@ -104,7 +104,7 @@ class LegacySplitScreenToLauncher(
@Presubmit
@Test
fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.endRotation)
fun entireScreenCovered() = testSpec.entireScreenCovered()
@Presubmit
@Test

View File

@@ -28,7 +28,6 @@ import com.android.server.wm.flicker.annotation.Group2
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.appPairsDividerBecomesVisible
@@ -77,7 +76,7 @@ class OpenAppToLegacySplitScreen(
@Presubmit
@Test
fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation)
fun entireScreenCovered() = testSpec.entireScreenCovered()
@Presubmit
@Test

View File

@@ -129,7 +129,7 @@ class ResizeLegacySplitScreen(
fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
@Test
fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.endRotation)
fun entireScreenCovered() = testSpec.entireScreenCovered()
@Test
fun navBarLayerRotatesAndScales() =

View File

@@ -23,8 +23,10 @@ import androidx.test.filters.RequiresDevice
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.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.traces.parser.toLayerName
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -32,8 +34,21 @@ import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
/**
* Test Pip launch.
* Test entering pip from an app by interacting with the app UI
*
* To run this test: `atest WMShellFlickerTests:EnterPipTest`
*
* Actions:
* Launch an app in full screen
* Press an "enter pip" button to put [pipApp] in pip mode
*
* Notes:
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
* are inherited [PipTransition]
* 2. Part of the test setup occurs automatically via
* [com.android.server.wm.flicker.TransitionRunnerWithRules],
* including configuring navigation mode, initial orientation and ensuring no
* apps are running before setup
*/
@RequiresDevice
@RunWith(Parameterized::class)
@@ -41,14 +56,19 @@ import org.junit.runners.Parameterized
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group3
class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
/**
* Defines the transition used to run the test
*/
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = buildTransition(eachRun = true, stringExtras = emptyMap()) {
transitions {
pipApp.clickEnterPipButton(wmHelper)
pipApp.expandPipWindow(wmHelper)
}
}
/**
* Checks [pipApp] window remains visible throughout the animation
*/
@Presubmit
@Test
fun pipAppWindowAlwaysVisible() {
@@ -57,6 +77,9 @@ class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
}
}
/**
* Checks [pipApp] layer remains visible throughout the animation
*/
@Presubmit
@Test
fun pipAppLayerAlwaysVisible() {
@@ -65,19 +88,89 @@ class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
}
}
@FlakyTest
/**
* Checks that the pip app window remains inside the display bounds throughout the whole
* animation
*/
@Presubmit
@Test
fun pipWindowBecomesVisible() {
fun pipWindowRemainInsideVisibleBounds() {
testSpec.assertWm {
invoke("pipWindowIsNotVisible") {
verify("Has no pip window").that(it.wmState.hasPipWindow()).isTrue()
}.then().invoke("pipWindowIsVisible") {
verify("Has pip window").that(it.wmState.hasPipWindow()).isTrue()
coversAtMost(displayBounds, pipApp.component)
}
}
/**
* Checks that the pip app layer remains inside the display bounds throughout the whole
* animation
*/
@Presubmit
@Test
fun pipLayerRemainInsideVisibleBounds() {
testSpec.assertLayers {
coversAtMost(displayBounds, pipApp.component)
}
}
/**
* Checks that the visible region of [pipApp] always reduces during the animation
*/
@Presubmit
@Test
fun pipLayerReduces() {
val layerName = pipApp.component.toLayerName()
testSpec.assertLayers {
val pipLayerList = this.layers { it.name.contains(layerName) && it.isVisible }
pipLayerList.zipWithNext { previous, current ->
current.visibleRegion.coversAtMost(previous.visibleRegion.region)
}
}
}
/**
* Checks that [pipApp] window becomes pinned
*/
@Presubmit
@Test
fun pipWindowBecomesPinned() {
testSpec.assertWm {
invoke("pipWindowIsNotPinned") { it.isNotPinned(pipApp.component) }
.then()
.invoke("pipWindowIsPinned") { it.isPinned(pipApp.component) }
}
}
/**
* Checks [LAUNCHER_COMPONENT] layer remains visible throughout the animation
*/
@Presubmit
@Test
fun launcherLayerBecomesVisible() {
testSpec.assertLayers {
isInvisible(LAUNCHER_COMPONENT)
.then()
.isVisible(LAUNCHER_COMPONENT)
}
}
/**
* Checks the focus doesn't change during the animation
*/
@FlakyTest
@Test
fun focusDoesNotChange() {
testSpec.assertEventLog {
this.focusDoesNotChange()
}
}
companion object {
/**
* Creates the test configurations.
*
* See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
* repetitions, screen orientation and navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<FlickerTestParameter> {

View File

@@ -25,7 +25,11 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.helpers.FixedAppHelper
import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_PORTRAIT
@@ -38,8 +42,22 @@ import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
/**
* Test Pip with orientation changes.
* To run this test: `atest WMShellFlickerTests:PipOrientationTest`
* Test entering pip while changing orientation (from app in landscape to pip window in portrait)
*
* To run this test: `atest EnterPipToOtherOrientationTest:EnterPipToOtherOrientationTest`
*
* Actions:
* Launch [testApp] on a fixed portrait orientation
* Launch [pipApp] on a fixed landscape orientation
* Broadcast action [ACTION_ENTER_PIP] to enter pip mode
*
* Notes:
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
* are inherited [PipTransition]
* 2. Part of the test setup occurs automatically via
* [com.android.server.wm.flicker.TransitionRunnerWithRules],
* including configuring navigation mode, initial orientation and ensuring no
* apps are running before setup
*/
@RequiresDevice
@RunWith(Parameterized::class)
@@ -53,6 +71,9 @@ class EnterPipToOtherOrientationTest(
private val startingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_90)
private val endingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_0)
/**
* Defines the transition used to run the test
*/
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = { configuration ->
setupAndTeardown(this, configuration)
@@ -79,21 +100,41 @@ class EnterPipToOtherOrientationTest(
broadcastActionTrigger.doAction(ACTION_ENTER_PIP)
wmHelper.waitFor { it.wmState.hasPipWindow() }
wmHelper.waitForAppTransitionIdle()
// during rotation the status bar becomes invisible and reappears at the end
wmHelper.waitForNavBarStatusBarVisible()
}
}
/**
* Checks that the [WindowManagerStateHelper.NAV_BAR_COMPONENT] has the correct position at
* the start and end of the transition
*/
@FlakyTest
@Test
override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
override fun navBarLayerRotatesAndScales() =
testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_90, Surface.ROTATION_0)
@FlakyTest
/**
* Checks that the [WindowManagerStateHelper.STATUS_BAR_COMPONENT] has the correct position at
* the start and end of the transition
*/
@Presubmit
@Test
override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
override fun statusBarLayerRotatesScales() =
testSpec.statusBarLayerRotatesScales(Surface.ROTATION_90, Surface.ROTATION_0)
@FlakyTest
/**
* Checks that all parts of the screen are covered at the start and end of the transition
*
* TODO b/197726599 Prevents all states from being checked
*/
@Presubmit
@Test
override fun entireScreenCovered() = super.entireScreenCovered()
override fun entireScreenCovered() = testSpec.entireScreenCovered(allStates = false)
/**
* Checks [pipApp] window remains visible and on top throughout the transition
*/
@Presubmit
@Test
fun pipAppWindowIsAlwaysOnTop() {
@@ -102,40 +143,84 @@ class EnterPipToOtherOrientationTest(
}
}
/**
* Checks that [testApp] window is not visible at the start
*/
@Presubmit
@Test
fun pipAppHidesTestApp() {
fun testAppWindowInvisibleOnStart() {
testSpec.assertWmStart {
isInvisible(testApp.component)
}
}
/**
* Checks that [testApp] window is visible at the end
*/
@Presubmit
@Test
fun testAppWindowIsVisible() {
fun testAppWindowVisibleOnEnd() {
testSpec.assertWmEnd {
isVisible(testApp.component)
}
}
/**
* Checks that [testApp] layer is not visible at the start
*/
@Presubmit
@Test
fun pipAppLayerHidesTestApp() {
fun testAppLayerInvisibleOnStart() {
testSpec.assertLayersStart {
visibleRegion(pipApp.component).coversExactly(startingBounds)
isInvisible(testApp.component)
}
}
/**
* Checks that [testApp] layer is visible at the end
*/
@Presubmit
@Test
fun testAppLayerCoversFullScreen() {
fun testAppLayerVisibleOnEnd() {
testSpec.assertLayersEnd {
visibleRegion(testApp.component).coversExactly(endingBounds)
isVisible(testApp.component)
}
}
/**
* Checks that the visible region of [pipApp] covers the full display area at the start of
* the transition
*/
@Presubmit
@Test
fun pipAppLayerCoversFullScreenOnStart() {
testSpec.assertLayersStart {
visibleRegion(pipApp.component).coversExactly(startingBounds)
}
}
/**
* Checks that the visible region of [testApp] plus the visible region of [pipApp]
* cover the full display area at the end of the transition
*/
@Presubmit
@Test
fun testAppPlusPipLayerCoversFullScreenOnEnd() {
testSpec.assertLayersEnd {
val pipRegion = visibleRegion(pipApp.component).region
visibleRegion(testApp.component)
.plus(pipRegion)
.coversExactly(endingBounds)
}
}
companion object {
/**
* Creates the test configurations.
*
* See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
* repetitions, screen orientation and navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {

View File

@@ -0,0 +1,131 @@
/*
* Copyright (C) 2020 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.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.traces.parser.toLayerName
import com.android.wm.shell.flicker.helpers.FixedAppHelper
import org.junit.Test
/**
* Base class for pip expand tests
*/
abstract class ExitPipToAppTransition(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
protected val testApp = FixedAppHelper(instrumentation)
/**
* Checks that the pip app window remains inside the display bounds throughout the whole
* animation
*/
@Presubmit
@Test
open fun pipAppWindowRemainInsideVisibleBounds() {
testSpec.assertWm {
coversAtMost(displayBounds, pipApp.component)
}
}
/**
* Checks that the pip app layer remains inside the display bounds throughout the whole
* animation
*/
@Presubmit
@Test
open fun pipAppLayerRemainInsideVisibleBounds() {
testSpec.assertLayers {
coversAtMost(displayBounds, pipApp.component)
}
}
/**
* Checks both app windows are visible at the start of the transition (with [pipApp] on top).
* Then, during the transition, [testApp] becomes invisible and [pipApp] remains visible
*/
@Presubmit
@Test
open fun showBothAppWindowsThenHidePip() {
testSpec.assertWm {
// when the activity is STOPPING, sometimes it becomes invisible in an entry before
// the window, sometimes in the same entry. This occurs because we log 1x per frame
// thus we ignore activity here
isAppWindowVisible(testApp.component, ignoreActivity = true)
.isAppWindowOnTop(pipApp.component)
.then()
.isAppWindowInvisible(testApp.component)
.isAppWindowVisible(pipApp.component)
}
}
/**
* Checks both app layers are visible at the start of the transition. Then, during the
* transition, [testApp] becomes invisible and [pipApp] remains visible
*/
@Presubmit
@Test
open fun showBothAppLayersThenHidePip() {
testSpec.assertLayers {
isVisible(testApp.component)
.isVisible(pipApp.component)
.then()
.isInvisible(testApp.component)
.isVisible(pipApp.component)
}
}
/**
* Checks that the visible region of [testApp] plus the visible region of [pipApp]
* cover the full display area at the start of the transition
*/
@Presubmit
@Test
open fun testPlusPipAppsCoverFullScreenAtStart() {
testSpec.assertLayersStart {
val pipRegion = visibleRegion(pipApp.component).region
visibleRegion(testApp.component)
.plus(pipRegion)
.coversExactly(displayBounds)
}
}
/**
* Checks that the visible region of [pipApp] covers the full display area at the end of
* the transition
*/
@Presubmit
@Test
open fun pipAppCoversFullScreenAtEnd() {
testSpec.assertLayersEnd {
visibleRegion(pipApp.component).coversExactly(displayBounds)
}
}
/**
* Checks that the visible region of [pipApp] always expands during the animation
*/
@Presubmit
@Test
open fun pipLayerExpands() {
val layerName = pipApp.component.toLayerName()
testSpec.assertLayers {
val pipLayerList = this.layers { it.name.contains(layerName) && it.isVisible }
pipLayerList.zipWithNext { previous, current ->
current.visibleRegion.coversAtLeast(previous.visibleRegion.region)
}
}
}
}

View File

@@ -20,15 +20,16 @@ import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.FlakyTest
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.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.startRotation
import org.junit.Test
import org.junit.runners.Parameterized
abstract class PipCloseTransition(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
/**
* Base class for exiting pip (closing pip window) without returning to the app
*/
abstract class ExitPipTransition(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = buildTransition(eachRun = true) { configuration ->
setup {
@@ -43,16 +44,28 @@ abstract class PipCloseTransition(testSpec: FlickerTestParameter) : PipTransitio
}
}
/**
* Checks that [pipApp] window is pinned and visible at the start and then becomes
* unpinned and invisible at the same moment, and remains unpinned and invisible
* until the end of the transition
*/
@Presubmit
@Test
open fun pipWindowBecomesInvisible() {
testSpec.assertWm {
this.invoke("hasPipWindow") { it.isPinned(pipApp.component) }
.then()
.isAppWindowInvisible(pipApp.component)
this.invoke("hasPipWindow") {
it.isPinned(pipApp.component).isVisible(pipApp.component)
}.then().invoke("!hasPipWindow") {
it.isNotPinned(pipApp.component).isInvisible(pipApp.component)
}
}
}
/**
* Checks that [pipApp] and [LAUNCHER_COMPONENT] layers are visible at the start
* of the transition. Then [pipApp] layer becomes invisible, and remains invisible
* until the end of the transition
*/
@Presubmit
@Test
open fun pipLayerBecomesInvisible() {
@@ -65,6 +78,10 @@ abstract class PipCloseTransition(testSpec: FlickerTestParameter) : PipTransitio
}
}
/**
* Checks that the focus changes between the [pipApp] window and the launcher when
* closing the pip window
*/
@FlakyTest(bugId = 151179149)
@Test
open fun focusChanges() {
@@ -72,14 +89,4 @@ abstract class PipCloseTransition(testSpec: FlickerTestParameter) : PipTransitio
this.focusChanges(pipApp.launcherName, "NexusLauncherActivity")
}
}
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
.getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0),
repetitions = 5)
}
}
}

View File

@@ -0,0 +1,93 @@
/*
* 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.wm.shell.flicker.pip
import android.view.Surface
import androidx.test.filters.RequiresDevice
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.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.traces.parser.toWindowName
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
/**
* Test expanding a pip window back to full screen via the expand button
*
* To run this test: `atest WMShellFlickerTests:ExitPipViaExpandButtonClickTest`
*
* Actions:
* Launch an app in pip mode [pipApp],
* Launch another full screen mode [testApp]
* Expand [pipApp] app to full screen by clicking on the pip window and
* then on the expand button
*
* Notes:
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
* are inherited [PipTransition]
* 2. Part of the test setup occurs automatically via
* [com.android.server.wm.flicker.TransitionRunnerWithRules],
* including configuring navigation mode, initial orientation and ensuring no
* apps are running before setup
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group3
class ExitPipViaExpandButtonClickTest(
testSpec: FlickerTestParameter
) : ExitPipToAppTransition(testSpec) {
/**
* Defines the transition used to run the test
*/
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = buildTransition(eachRun = true) {
setup {
eachRun {
// launch an app behind the pip one
testApp.launchViaIntent(wmHelper)
}
}
transitions {
// This will bring PipApp to fullscreen
pipApp.expandPipWindowToApp(wmHelper)
// Wait until the other app is no longer visible
wmHelper.waitForSurfaceAppeared(testApp.component.toWindowName())
}
}
companion object {
/**
* Creates the test configurations.
*
* See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
* repetitions, screen orientation and navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
supportedRotations = listOf(Surface.ROTATION_0), repetitions = 5)
}
}
}

View File

@@ -16,8 +16,6 @@
package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -25,91 +23,63 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.wm.shell.flicker.helpers.FixedAppHelper
import com.android.server.wm.traces.parser.toWindowName
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
/**
* Test Pip launch and exit.
* To run this test: `atest WMShellFlickerTests:EnterExitPipTest`
* Test expanding a pip window back to full screen via an intent
*
* To run this test: `atest WMShellFlickerTests:ExitPipViaIntentTest`
*
* Actions:
* Launch an app in pip mode [pipApp],
* Launch another full screen mode [testApp]
* Expand [pipApp] app to full screen via an intent
*
* Notes:
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
* are inherited from [PipTransition]
* 2. Part of the test setup occurs automatically via
* [com.android.server.wm.flicker.TransitionRunnerWithRules],
* including configuring navigation mode, initial orientation and ensuring no
* apps are running before setup
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group3
class EnterExitPipTest(
testSpec: FlickerTestParameter
) : PipTransition(testSpec) {
private val testApp = FixedAppHelper(instrumentation)
class ExitPipViaIntentTest(testSpec: FlickerTestParameter) : ExitPipToAppTransition(testSpec) {
/**
* Defines the transition used to run the test
*/
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = buildTransition(eachRun = true) {
setup {
eachRun {
// launch an app behind the pip one
testApp.launchViaIntent(wmHelper)
}
}
transitions {
// This will bring PipApp to fullscreen
pipApp.launchViaIntent(wmHelper)
// Wait until the other app is no longer visible
wmHelper.waitForSurfaceAppeared(testApp.component.toWindowName())
}
}
@Presubmit
@Test
fun pipAppRemainInsideVisibleBounds() {
testSpec.assertWm {
coversAtMost(displayBounds, pipApp.component)
}
}
@Postsubmit
@Test
fun showBothAppWindowsThenHidePip() {
testSpec.assertWm {
// when the activity is STOPPING, sometimes it becomes invisible in an entry before
// the window, sometimes in the same entry. This occurs because we log 1x per frame
// thus we ignore activity here
isAppWindowVisible(testApp.component, ignoreActivity = true)
.isAppWindowOnTop(pipApp.component)
.then()
.isAppWindowInvisible(testApp.component)
}
}
@Presubmit
@Test
fun showBothAppLayersThenHidePip() {
testSpec.assertLayers {
isVisible(testApp.component)
.isVisible(pipApp.component)
.then()
.isInvisible(testApp.component)
}
}
@Presubmit
@Test
fun testAppCoversFullScreenWithPipOnDisplay() {
testSpec.assertLayersStart {
visibleRegion(testApp.component).coversExactly(displayBounds)
visibleRegion(pipApp.component).coversAtMost(displayBounds)
}
}
@Presubmit
@Test
fun pipAppCoversFullScreen() {
testSpec.assertLayersEnd {
visibleRegion(pipApp.component).coversExactly(displayBounds)
}
}
companion object {
/**
* Creates the test configurations.
*
* See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
* repetitions, screen orientation and navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<FlickerTestParameter> {

View File

@@ -16,10 +16,12 @@
package com.android.wm.shell.flicker.pip
import androidx.test.filters.FlakyTest
import android.platform.test.annotations.Postsubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
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.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
import org.junit.FixMethodOrder
@@ -29,15 +31,29 @@ import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
/**
* Test Pip launch.
* To run this test: `atest WMShellFlickerTests:PipCloseWithDismissButton`
* Test closing a pip window via the dismiss button
*
* To run this test: `atest WMShellFlickerTests:ExitPipWithDismissButtonTest`
*
* Actions:
* Launch an app in pip mode [pipApp],
* Click on the pip window
* Click on dismiss button and wait window disappear
*
* Notes:
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
* are inherited [PipTransition]
* 2. Part of the test setup occurs automatically via
* [com.android.server.wm.flicker.TransitionRunnerWithRules],
* including configuring navigation mode, initial orientation and ensuring no
* apps are running before setup
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group3
class PipCloseWithDismissButtonTest(testSpec: FlickerTestParameter) : PipCloseTransition(testSpec) {
class ExitPipWithDismissButtonTest(testSpec: FlickerTestParameter) : ExitPipTransition(testSpec) {
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = {
super.transition(this, it)
@@ -46,11 +62,27 @@ class PipCloseWithDismissButtonTest(testSpec: FlickerTestParameter) : PipCloseTr
}
}
@FlakyTest
@Postsubmit
@Test
override fun pipLayerBecomesInvisible() = super.pipLayerBecomesInvisible()
@FlakyTest
@Postsubmit
@Test
override fun pipWindowBecomesInvisible() = super.pipWindowBecomesInvisible()
companion object {
/**
* Creates the test configurations.
*
* See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
* repetitions, screen orientation and navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
.getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0),
repetitions = 5)
}
}
}

View File

@@ -22,6 +22,7 @@ import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
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.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.startRotation
@@ -33,24 +34,40 @@ import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
/**
* Test Pip launch.
* To run this test: `atest WMShellFlickerTests:PipCloseWithSwipe`
* Test closing a pip window by swiping it to the bottom-center of the screen
*
* To run this test: `atest WMShellFlickerTests:ExitPipWithSwipeDownTest`
*
* Actions:
* Launch an app in pip mode [pipApp],
* Swipe the pip window to the bottom-center of the screen and wait it disappear
*
* Notes:
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
* are inherited [PipTransition]
* 2. Part of the test setup occurs automatically via
* [com.android.server.wm.flicker.TransitionRunnerWithRules],
* including configuring navigation mode, initial orientation and ensuring no
* apps are running before setup
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group3
class PipCloseWithSwipeTest(testSpec: FlickerTestParameter) : PipCloseTransition(testSpec) {
class ExitPipWithSwipeDownTest(testSpec: FlickerTestParameter) : ExitPipTransition(testSpec) {
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = {
super.transition(this, it)
get() = { args ->
super.transition(this, args)
transitions {
val pipRegion = wmHelper.getWindowRegion(pipApp.component).bounds
val pipCenterX = pipRegion.centerX()
val pipCenterY = pipRegion.centerY()
val displayCenterX = device.displayWidth / 2
device.swipe(pipCenterX, pipCenterY, displayCenterX, device.displayHeight, 5)
device.swipe(pipCenterX, pipCenterY, displayCenterX, device.displayHeight, 10)
wmHelper.waitFor("!hasPipWindow") { !it.wmState.hasPipWindow() }
wmHelper.waitForWindowSurfaceDisappeared(pipApp.component)
wmHelper.waitForAppTransitionIdle()
}
}
@@ -90,4 +107,20 @@ class PipCloseWithSwipeTest(testSpec: FlickerTestParameter) : PipCloseTransition
@Presubmit
@Test
override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
companion object {
/**
* Creates the test configurations.
*
* See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
* repetitions, screen orientation and navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
.getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0),
repetitions = 20)
}
}
}

View File

@@ -0,0 +1,176 @@
/*
* Copyright (C) 2020 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.wm.shell.flicker.pip
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
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.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.traces.parser.toLayerName
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
/**
* Test expanding a pip window by double clicking it
*
* To run this test: `atest WMShellFlickerTests:ExpandPipOnDoubleClickTest`
*
* Actions:
* Launch an app in pip mode [pipApp],
* Expand [pipApp] app to its maximum pip size by double clicking on it
*
* Notes:
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
* are inherited [PipTransition]
* 2. Part of the test setup occurs automatically via
* [com.android.server.wm.flicker.TransitionRunnerWithRules],
* including configuring navigation mode, initial orientation and ensuring no
* apps are running before setup
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group3
class ExpandPipOnDoubleClickTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = buildTransition(eachRun = true) {
transitions {
pipApp.doubleClickPipWindow(wmHelper)
}
}
/**
* Checks that the pip app window remains inside the display bounds throughout the whole
* animation
*/
@Presubmit
@Test
fun pipWindowRemainInsideVisibleBounds() {
testSpec.assertWm {
coversAtMost(displayBounds, pipApp.component)
}
}
/**
* Checks that the pip app layer remains inside the display bounds throughout the whole
* animation
*/
@Presubmit
@Test
fun pipLayerRemainInsideVisibleBounds() {
testSpec.assertLayers {
coversAtMost(displayBounds, pipApp.component)
}
}
/**
* Checks [pipApp] window remains visible throughout the animation
*/
@Postsubmit
@Test
fun pipWindowIsAlwaysVisible() {
testSpec.assertWm {
isAppWindowVisible(pipApp.component)
}
}
/**
* Checks [pipApp] layer remains visible throughout the animation
*/
@Presubmit
@Test
fun pipLayerIsAlwaysVisible() {
testSpec.assertLayers {
isVisible(pipApp.component)
}
}
/**
* Checks that the visible region of [pipApp] always expands during the animation
*/
@Presubmit
@Test
fun pipLayerExpands() {
val layerName = pipApp.component.toLayerName()
testSpec.assertLayers {
val pipLayerList = this.layers { it.name.contains(layerName) && it.isVisible }
pipLayerList.zipWithNext { previous, current ->
current.visibleRegion.coversAtLeast(previous.visibleRegion.region)
}
}
}
/**
* Checks [pipApp] window remains pinned throughout the animation
*/
@Presubmit
@Test
fun windowIsAlwaysPinned() {
testSpec.assertWm {
this.invoke("hasPipWindow") { it.isPinned(pipApp.component) }
}
}
/**
* Checks [pipApp] layer remains visible throughout the animation
*/
@Presubmit
@Test
fun launcherIsAlwaysVisible() {
testSpec.assertLayers {
isVisible(LAUNCHER_COMPONENT)
}
}
/**
* Checks that the focus doesn't change between windows during the transition
*/
@FlakyTest
@Test
fun focusDoesNotChange() {
testSpec.assertEventLog {
this.focusDoesNotChange()
}
}
companion object {
/**
* Creates the test configurations.
*
* See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
* repetitions, screen orientation and navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
.getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0),
repetitions = 5)
}
}
}

View File

@@ -0,0 +1,95 @@
/*
* 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.wm.shell.flicker.pip
import android.view.Surface
import androidx.test.filters.RequiresDevice
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.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.traces.RegionSubject
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
/**
* Test Pip movement with Launcher shelf height change (decrease).
*
* To run this test: `atest WMShellFlickerTests:MovePipDownShelfHeightChangeTest`
*
* Actions:
* Launch [pipApp] in pip mode
* Launch [testApp]
* Press home
* Check if pip window moves down (visually)
*
* Notes:
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
* are inherited [PipTransition]
* 2. Part of the test setup occurs automatically via
* [com.android.server.wm.flicker.TransitionRunnerWithRules],
* including configuring navigation mode, initial orientation and ensuring no
* apps are running before setup
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group3
class MovePipDownShelfHeightChangeTest(
testSpec: FlickerTestParameter
) : MovePipShelfHeightTransition(testSpec) {
/**
* Defines the transition used to run the test
*/
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = buildTransition(eachRun = false) {
teardown {
eachRun {
testApp.launchViaIntent(wmHelper)
}
test {
testApp.exit(wmHelper)
}
}
transitions {
taplInstrumentation.pressHome()
}
}
override fun assertRegionMovement(previous: RegionSubject, current: RegionSubject) {
current.isHigherOrEqual(previous.region)
}
companion object {
/**
* Creates the test configurations.
*
* See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
* repetitions, screen orientation and navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
supportedRotations = listOf(Surface.ROTATION_0), repetitions = 5)
}
}
}

View File

@@ -0,0 +1,118 @@
/*
* 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.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
import com.android.launcher3.tapl.LauncherInstrumentation
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.traces.RegionSubject
import com.android.server.wm.traces.parser.toLayerName
import com.android.server.wm.traces.parser.toWindowName
import com.android.wm.shell.flicker.helpers.FixedAppHelper
import org.junit.Test
/**
* Base class for pip tests with Launcher shelf height change
*/
abstract class MovePipShelfHeightTransition(
testSpec: FlickerTestParameter
) : PipTransition(testSpec) {
protected val taplInstrumentation = LauncherInstrumentation()
protected val testApp = FixedAppHelper(instrumentation)
/**
* Checks if the window movement direction is valid
*/
protected abstract fun assertRegionMovement(previous: RegionSubject, current: RegionSubject)
/**
* Checks [pipApp] window remains visible throughout the animation
*/
@Presubmit
@Test
open fun pipWindowIsAlwaysVisible() {
testSpec.assertWm {
isAppWindowVisible(pipApp.component)
}
}
/**
* Checks [pipApp] layer remains visible throughout the animation
*/
@Presubmit
@Test
open fun pipLayerIsAlwaysVisible() {
testSpec.assertLayers {
isVisible(pipApp.component)
}
}
/**
* Checks that the pip app window remains inside the display bounds throughout the whole
* animation
*/
@Presubmit
@Test
open fun pipWindowRemainInsideVisibleBounds() {
testSpec.assertWm {
coversAtMost(displayBounds, pipApp.component)
}
}
/**
* Checks that the pip app layer remains inside the display bounds throughout the whole
* animation
*/
@Presubmit
@Test
open fun pipLayerRemainInsideVisibleBounds() {
testSpec.assertLayers {
coversAtMost(displayBounds, pipApp.component)
}
}
/**
* Checks that the visible region of [pipApp] always moves in the correct direction
* during the animation.
*/
@Presubmit
@Test
open fun pipWindowMoves() {
val windowName = pipApp.component.toWindowName()
testSpec.assertWm {
val pipWindowList = this.windowStates { it.name.contains(windowName) && it.isVisible }
pipWindowList.zipWithNext { previous, current ->
assertRegionMovement(previous.frame, current.frame)
}
}
}
/**
* Checks that the visible region of [pipApp] always moves up during the animation
*/
@Presubmit
@Test
open fun pipLayerMoves() {
val layerName = pipApp.component.toLayerName()
testSpec.assertLayers {
val pipLayerList = this.layers { it.name.contains(layerName) && it.isVisible }
pipLayerList.zipWithNext { previous, current ->
assertRegionMovement(previous.visibleRegion, current.visibleRegion)
}
}
}
}

View File

@@ -16,36 +16,49 @@
package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.launcher3.tapl.LauncherInstrumentation
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.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.wm.shell.flicker.helpers.FixedAppHelper
import com.google.common.truth.Truth
import com.android.server.wm.flicker.traces.RegionSubject
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
/**
* Test Pip movement with Launcher shelf height change.
* To run this test: `atest WMShellFlickerTests:PipShelfHeightTest`
* Test Pip movement with Launcher shelf height change (increase).
*
* To run this test: `atest WMShellFlickerTests:MovePipUpShelfHeightChangeTest`
*
* Actions:
* Launch [pipApp] in pip mode
* Press home
* Launch [testApp]
* Check if pip window moves up (visually)
*
* Notes:
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
* are inherited [PipTransition]
* 2. Part of the test setup occurs automatically via
* [com.android.server.wm.flicker.TransitionRunnerWithRules],
* including configuring navigation mode, initial orientation and ensuring no
* apps are running before setup
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group3
class PipShelfHeightTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
private val taplInstrumentation = LauncherInstrumentation()
private val testApp = FixedAppHelper(instrumentation)
class MovePipUpShelfHeightChangeTest(
testSpec: FlickerTestParameter
) : MovePipShelfHeightTransition(testSpec) {
/**
* Defines the transition used to run the test
*/
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = buildTransition(eachRun = false) {
teardown {
@@ -61,33 +74,17 @@ class PipShelfHeightTest(testSpec: FlickerTestParameter) : PipTransition(testSpe
}
}
@Presubmit
@Test
fun pipAlwaysVisible() = testSpec.assertWm { this.isAppWindowVisible(pipApp.component) }
@Presubmit
@Test
fun pipLayerInsideDisplay() {
testSpec.assertLayersStart {
visibleRegion(pipApp.component).coversAtMost(displayBounds)
}
}
@Presubmit
@Test
fun pipWindowMovesUp() = testSpec.assertWmEnd {
val initialState = this.trace?.first()?.wmState
?: error("Trace should not be empty")
val startPos = initialState.pinnedWindows.first().frame
val currPos = this.wmState.pinnedWindows.first().frame
val subject = Truth.assertWithMessage("Pip should have moved up")
subject.that(currPos.top).isGreaterThan(startPos.top)
subject.that(currPos.bottom).isGreaterThan(startPos.bottom)
subject.that(currPos.left).isEqualTo(startPos.left)
subject.that(currPos.right).isEqualTo(startPos.right)
override fun assertRegionMovement(previous: RegionSubject, current: RegionSubject) {
current.isLowerOrEqual(previous.region)
}
companion object {
/**
* Creates the test configurations.
*
* See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
* repetitions, screen orientation and navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<FlickerTestParameter> {

View File

@@ -32,8 +32,6 @@ import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.traces.common.Region
import com.android.server.wm.traces.parser.minus
import com.android.wm.shell.flicker.helpers.FixedAppHelper
import org.junit.FixMethodOrder
import org.junit.Test
@@ -43,7 +41,22 @@ import org.junit.runners.Parameterized
/**
* Test Pip Stack in bounds after rotations.
*
* To run this test: `atest WMShellFlickerTests:PipRotationTest`
*
* Actions:
* Launch a [pipApp] in pip mode
* Launch another app [fixedApp] (appears below pip)
* Rotate the screen from [testSpec.config.startRotation] to [testSpec.config.endRotation]
* (usually, 0->90 and 90->0)
*
* Notes:
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
* are inherited from [PipTransition]
* 2. Part of the test setup occurs automatically via
* [com.android.server.wm.flicker.TransitionRunnerWithRules],
* including configuring navigation mode, initial orientation and ensuring no
* apps are running before setup
*/
@RequiresDevice
@RunWith(Parameterized::class)
@@ -52,8 +65,8 @@ import org.junit.runners.Parameterized
@Group3
class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
private val fixedApp = FixedAppHelper(instrumentation)
private val startingBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
private val endingBounds = WindowUtils.getDisplayBounds(testSpec.config.endRotation)
private val screenBoundsStart = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
private val screenBoundsEnd = WindowUtils.getDisplayBounds(testSpec.config.endRotation)
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = buildTransition(eachRun = false) { configuration ->
@@ -68,55 +81,108 @@ class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testSpec)
transitions {
setRotation(configuration.endRotation)
}
teardown {
eachRun {
setRotation(Surface.ROTATION_0)
}
}
}
/**
* Checks that all parts of the screen are covered at the start and end of the transition
*/
@Presubmit
@Test
override fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation,
testSpec.config.endRotation, allStates = false)
override fun entireScreenCovered() = testSpec.entireScreenCovered()
/**
* Checks the position of the navigation bar at the start and end of the transition
*/
@FlakyTest
@Test
override fun navBarLayerRotatesAndScales() =
testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation,
testSpec.config.endRotation)
/**
* Checks the position of the status bar at the start and end of the transition
*/
@Presubmit
@Test
override fun statusBarLayerRotatesScales() =
testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation,
testSpec.config.endRotation)
/**
* Checks that [fixedApp] layer is within [screenBoundsStart] at the start of the transition
*/
@Presubmit
@Test
fun appLayerRotates_StartingBounds() {
testSpec.assertLayersStart {
val pipRegion = visibleRegion(pipApp.component).region
val expectedWithoutPip = Region(startingBounds.bounds.left, startingBounds.bounds.top,
startingBounds.bounds.right, startingBounds.bounds.bottom).minus(pipRegion)
visibleRegion(fixedApp.component).coversExactly(expectedWithoutPip)
visibleRegion(pipApp.component).coversAtMost(startingBounds)
visibleRegion(fixedApp.component).coversExactly(screenBoundsStart)
}
}
/**
* Checks that [fixedApp] layer is within [screenBoundsEnd] at the end of the transition
*/
@Presubmit
@Test
fun appLayerRotates_EndingBounds() {
testSpec.assertLayersEnd {
val pipRegion = visibleRegion(pipApp.component).region
val expectedWithoutPip = Region(endingBounds.bounds.left, endingBounds.bounds.top,
endingBounds.bounds.right, endingBounds.bounds.bottom).minus(pipRegion)
visibleRegion(fixedApp.component).coversExactly(expectedWithoutPip)
visibleRegion(pipApp.component).coversAtMost(endingBounds)
visibleRegion(fixedApp.component).coversExactly(screenBoundsEnd)
}
}
/**
* Checks that [pipApp] layer is within [screenBoundsStart] at the start of the transition
*/
@Presubmit
@Test
fun pipLayerRotates_StartingBounds() {
testSpec.assertLayersStart {
visibleRegion(pipApp.component).coversAtMost(screenBoundsStart)
}
}
/**
* Checks that [pipApp] layer is within [screenBoundsEnd] at the end of the transition
*/
@Presubmit
@Test
fun pipLayerRotates_EndingBounds() {
testSpec.assertLayersEnd {
visibleRegion(pipApp.component).coversAtMost(screenBoundsEnd)
}
}
/**
* Ensure that the [pipApp] window does not obscure the [fixedApp] at the start of the
* transition
*/
@Presubmit
@Test
fun pipIsAboveFixedAppWindow_Start() {
testSpec.assertWmStart {
isAboveWindow(pipApp.component, fixedApp.component)
}
}
/**
* Ensure that the [pipApp] window does not obscure the [fixedApp] at the end of the
* transition
*/
@Presubmit
@Test
fun pipIsAboveFixedAppWindow_End() {
testSpec.assertWmEnd {
isAboveWindow(pipApp.component, fixedApp.component)
}
}
companion object {
/**
* Creates the test configurations.
*
* See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
* repetitions, screen orientation and navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {

View File

@@ -1,113 +0,0 @@
/*
* Copyright (C) 2020 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.wm.shell.flicker.pip
import android.view.Surface
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
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.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.startRotation
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
/**
* Test Pip launch.
* To run this test: `atest WMShellFlickerTests:PipToAppTest`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group3
class PipToAppTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = buildTransition(eachRun = true) { configuration ->
setup {
eachRun {
this.setRotation(configuration.startRotation)
}
}
teardown {
eachRun {
this.setRotation(Surface.ROTATION_0)
}
}
transitions {
pipApp.expandPipWindowToApp(wmHelper)
}
}
@FlakyTest
@Test
fun appReplacesPipWindow() {
testSpec.assertWm {
this.invoke("hasPipWindow") { it.isPinned(pipApp.component) }
.isAppWindowOnTop(pipApp.component)
.then()
.invoke("hasNotPipWindow") { it.isNotPinned(pipApp.component) }
.isAppWindowOnTop(pipApp.component)
}
}
@FlakyTest
@Test
fun appReplacesPipLayer() {
testSpec.assertLayers {
this.isVisible(pipApp.component)
.isVisible(LAUNCHER_COMPONENT)
.then()
.isVisible(pipApp.component)
.isInvisible(LAUNCHER_COMPONENT)
}
}
@FlakyTest
@Test
fun testAppCoversFullScreen() {
testSpec.assertLayersStart {
visibleRegion(pipApp.component).coversExactly(displayBounds)
}
}
@FlakyTest(bugId = 151179149)
@Test
fun focusChanges() {
testSpec.assertEventLog {
this.focusChanges("NexusLauncherActivity",
pipApp.launcherName, "NexusLauncherActivity")
}
}
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
.getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0),
repetitions = 5)
}
}
}

View File

@@ -187,6 +187,5 @@ abstract class PipTransition(protected val testSpec: FlickerTestParameter) {
@Presubmit
@Test
open fun entireScreenCovered() =
testSpec.entireScreenCovered(testSpec.config.startRotation, Surface.ROTATION_0)
open fun entireScreenCovered() = testSpec.entireScreenCovered()
}

View File

@@ -144,9 +144,7 @@ class SetRequestedOrientationWhilePinnedTest(
@FlakyTest
@Test
override fun entireScreenCovered() {
super.entireScreenCovered()
}
override fun entireScreenCovered() = super.entireScreenCovered()
companion object {
@Parameterized.Parameters(name = "{0}")

View File

@@ -43,6 +43,7 @@ import static com.android.server.accessibility.AccessibilityTraceProto.WHERE;
import static com.android.server.accessibility.AccessibilityTraceProto.WINDOW_MANAGER_SERVICE;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowTracing.WINSCOPE_EXT;
import static com.android.server.wm.utils.RegionUtils.forEachRect;
import android.accessibilityservice.AccessibilityTrace;
@@ -2081,7 +2082,7 @@ final class AccessibilityController {
}
private static final int BUFFER_CAPACITY = 1024 * 1024 * 12;
private static final String TRACE_FILENAME = "/data/misc/a11ytrace/a11y_trace.pb";
private static final String TRACE_FILENAME = "/data/misc/a11ytrace/a11y_trace" + WINSCOPE_EXT;
private static final String TAG = "AccessibilityTracing";
private static final long MAGIC_NUMBER_VALUE =
((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;

View File

@@ -54,7 +54,8 @@ class WindowTracing {
private static final int BUFFER_CAPACITY_CRITICAL = 512 * 1024;
private static final int BUFFER_CAPACITY_TRIM = 2048 * 1024;
private static final int BUFFER_CAPACITY_ALL = 4096 * 1024;
private static final String TRACE_FILENAME = "/data/misc/wmtrace/wm_trace.pb";
static final String WINSCOPE_EXT = ".winscope";
private static final String TRACE_FILENAME = "/data/misc/wmtrace/wm_trace" + WINSCOPE_EXT;
private static final String TAG = "WindowTracing";
private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;

View File

@@ -36,30 +36,35 @@ fun FlickerTestParameter.navBarWindowIsVisible() {
}
}
/**
* If [allStates] is true, checks if the stack space of all displays is fully covered
* by any visible layer, during the whole transitions
*
* Otherwise, checks if the stack space of all displays is fully covered
* by any visible layer, at the start and end of the transition
*
* @param allStates if all states should be checked, othersie, just initial and final
*/
@JvmOverloads
fun FlickerTestParameter.entireScreenCovered(
beginRotation: Int,
endRotation: Int = beginRotation,
allStates: Boolean = true
) {
val startingBounds = WindowUtils.getDisplayBounds(beginRotation)
val endingBounds = WindowUtils.getDisplayBounds(endRotation)
fun FlickerTestParameter.entireScreenCovered(allStates: Boolean = true) {
if (allStates) {
assertLayers {
if (startingBounds == endingBounds) {
this.coversAtLeast(startingBounds)
} else {
this.coversAtLeast(startingBounds)
.then()
.coversAtLeast(endingBounds)
this.invoke("entireScreenCovered") { entry ->
entry.entry.displays.forEach { display ->
entry.visibleRegion().coversAtLeast(display.layerStackSpace)
}
}
}
} else {
assertLayersStart {
this.visibleRegion().coversAtLeast(startingBounds)
this.entry.displays.forEach { display ->
this.visibleRegion().coversAtLeast(display.layerStackSpace)
}
}
assertLayersEnd {
this.visibleRegion().coversAtLeast(endingBounds)
this.entry.displays.forEach { display ->
this.visibleRegion().coversAtLeast(display.layerStackSpace)
}
}
}
}

View File

@@ -123,9 +123,7 @@ abstract class CloseAppTransition(protected val testSpec: FlickerTestParameter)
@Presubmit
@Test
open fun entireScreenCovered() {
testSpec.entireScreenCovered(testSpec.config.startRotation, Surface.ROTATION_0)
}
open fun entireScreenCovered() = testSpec.entireScreenCovered()
@Presubmit
@Test
@@ -152,4 +150,4 @@ abstract class CloseAppTransition(protected val testSpec: FlickerTestParameter)
open fun launcherLayerReplacesApp() {
testSpec.replacesLayer(testApp.component, LAUNCHER_COMPONENT)
}
}
}

View File

@@ -0,0 +1,32 @@
/*
* 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.helpers
import android.app.Instrumentation
import android.content.ComponentName
import android.support.test.launcherhelper.ILauncherStrategy
import android.support.test.launcherhelper.LauncherStrategyFactory
import com.android.server.wm.flicker.testapp.ActivityOptions
class NonResizeableAppHelper @JvmOverloads constructor(
instr: Instrumentation,
launcherName: String = ActivityOptions.NON_RESIZEABLE_ACTIVITY_LAUNCHER_NAME,
component: ComponentName = ActivityOptions.NON_RESIZEABLE_ACTIVITY_COMPONENT_NAME,
launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
.getInstance(instr)
.launcherStrategy
) : StandardAppHelper(instr, launcherName, component, launcherStrategy)

View File

@@ -0,0 +1,50 @@
/*
* 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.helpers
import android.app.Instrumentation
import android.content.ComponentName
import android.support.test.launcherhelper.ILauncherStrategy
import android.support.test.launcherhelper.LauncherStrategyFactory
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.testapp.ActivityOptions
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
class TwoActivitiesAppHelper @JvmOverloads constructor(
instr: Instrumentation,
launcherName: String = ActivityOptions.BUTTON_ACTIVITY_LAUNCHER_NAME,
component: ComponentName = ActivityOptions.BUTTON_ACTIVITY_COMPONENT_NAME,
launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
.getInstance(instr)
.launcherStrategy
) : StandardAppHelper(instr, launcherName, component, launcherStrategy) {
fun openSecondActivity(device: UiDevice, wmHelper: WindowManagerStateHelper) {
val button = device.wait(
Until.findObject(By.res(getPackage(), "launch_second_activity")),
FIND_TIMEOUT)
require(button != null) {
"Button not found, this usually happens when the device " +
"was left in an unknown state (e.g. in split screen)"
}
button.click()
wmHelper.waitForAppTransitionIdle()
wmHelper.waitForFullScreenApp(component)
}
}

View File

@@ -119,7 +119,7 @@ class CloseImeAutoOpenWindowToAppTest(private val testSpec: FlickerTestParameter
@Presubmit
@Test
fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation)
fun entireScreenCovered() = testSpec.entireScreenCovered()
@Presubmit
@Test

View File

@@ -116,8 +116,7 @@ class CloseImeAutoOpenWindowToHomeTest(private val testSpec: FlickerTestParamete
@Presubmit
@Test
fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation,
Surface.ROTATION_0)
fun entireScreenCovered() = testSpec.entireScreenCovered()
@Presubmit
@Test

View File

@@ -115,7 +115,7 @@ class CloseImeWindowToAppTest(private val testSpec: FlickerTestParameter) {
@Presubmit
@Test
fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation)
fun entireScreenCovered() = testSpec.entireScreenCovered()
@Presubmit
@Test
@@ -165,4 +165,4 @@ class CloseImeWindowToAppTest(private val testSpec: FlickerTestParameter) {
.getConfigNonRotationTests(repetitions = 5)
}
}
}
}

View File

@@ -125,8 +125,7 @@ class CloseImeWindowToHomeTest(private val testSpec: FlickerTestParameter) {
@Presubmit
@Test
fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation,
Surface.ROTATION_0)
fun entireScreenCovered() = testSpec.entireScreenCovered()
@Presubmit
@Test

View File

@@ -108,7 +108,7 @@ class OpenImeWindowTest(private val testSpec: FlickerTestParameter) {
@Presubmit
@Test
fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation)
fun entireScreenCovered() = testSpec.entireScreenCovered()
@Presubmit
@Test

View File

@@ -143,8 +143,7 @@ class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) {
@Presubmit
@Test
// During testing the launcher is always in portrait mode
fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation,
testSpec.config.endRotation)
fun entireScreenCovered() = testSpec.entireScreenCovered()
@Presubmit
@Test

View File

@@ -0,0 +1,116 @@
/*
* 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.launch
import android.app.Instrumentation
import android.platform.test.annotations.Presubmit
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.entireScreenCovered
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.repetitions
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.TwoActivitiesAppHelper
import com.android.server.wm.flicker.testapp.ActivityOptions
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
/**
* Test the back and forward transition between 2 activities.
* To run this test: `atest FlickerTests:ActivitiesTransitionTest`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group1
class ActivitiesTransitionTest(val testSpec: FlickerTestParameter) {
val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
private val testApp: TwoActivitiesAppHelper = TwoActivitiesAppHelper(instrumentation)
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
withTestName { testSpec.name }
repeat { testSpec.config.repetitions }
setup {
eachRun {
testApp.launchViaIntent(wmHelper)
wmHelper.waitForFullScreenApp(testApp.component)
}
}
teardown {
test {
testApp.exit()
}
}
transitions {
testApp.openSecondActivity(device, wmHelper)
device.pressBack()
wmHelper.waitForAppTransitionIdle()
wmHelper.waitForFullScreenApp(testApp.component)
}
}
}
@Presubmit
@Test
fun finishSubActivity() {
testSpec.assertWm {
this.isAppWindowOnTop(ActivityOptions.BUTTON_ACTIVITY_COMPONENT_NAME)
.then()
.isAppWindowOnTop(ActivityOptions.SIMPLE_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME)
.then()
.isAppWindowOnTop(ActivityOptions.BUTTON_ACTIVITY_COMPONENT_NAME)
}
}
@Presubmit
@Test
fun entireScreenCovered() = testSpec.entireScreenCovered()
@Presubmit
@Test
fun launcherWindowNotVisible() {
testSpec.assertWm {
this.isAppWindowInvisible(LAUNCHER_COMPONENT, ignoreActivity = true)
}
}
@Presubmit
@Test
fun launcherLayerNotVisible() {
testSpec.assertLayers { this.isInvisible(LAUNCHER_COMPONENT) }
}
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
.getConfigNonRotationTests(repetitions = 5)
}
}
}

View File

@@ -16,6 +16,8 @@
package com.android.server.wm.flicker.launch
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -33,8 +35,21 @@ import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
/**
* Test cold launch app from launcher.
* Test cold launching an app from launcher
*
* To run this test: `atest FlickerTests:OpenAppColdTest`
*
* Actions:
* Make sure no apps are running on the device
* Launch an app [testApp] and wait animation to complete
*
* Notes:
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
* are inherited [OpenAppTransition]
* 2. Part of the test setup occurs automatically via
* [com.android.server.wm.flicker.TransitionRunnerWithRules],
* including configuring navigation mode, initial orientation and ensuring no
* apps are running before setup
*/
@RequiresDevice
@RunWith(Parameterized::class)
@@ -42,6 +57,9 @@ import org.junit.runners.Parameterized
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group1
class OpenAppColdTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSpec) {
/**
* Defines the transition used to run the test
*/
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = {
super.transition(this, it)
@@ -62,25 +80,46 @@ class OpenAppColdTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSp
}
}
/** {@inheritDoc} */
@FlakyTest
@Test
override fun navBarLayerRotatesAndScales() {
super.navBarLayerRotatesAndScales()
}
@FlakyTest(bugId = 192721431)
/** {@inheritDoc} */
@Postsubmit
@Test
override fun appLayerReplacesLauncher() {
super.appLayerReplacesLauncher()
}
override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher()
@FlakyTest(bugId = 192721431)
/** {@inheritDoc} */
@Postsubmit
@Test
override fun appWindowReplacesLauncherAsTopWindow() {
override fun appWindowReplacesLauncherAsTopWindow() =
super.appWindowReplacesLauncherAsTopWindow()
}
/** {@inheritDoc} */
@Presubmit
@Test
override fun launcherWindowBecomesInvisible() = super.launcherWindowBecomesInvisible()
/** {@inheritDoc} */
@Presubmit
@Test
override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
/** {@inheritDoc} */
@Presubmit
@Test
override fun navBarWindowIsVisible() = super.navBarWindowIsVisible()
companion object {
/**
* Creates the test configurations.
*
* See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
* repetitions, screen orientation and navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {

View File

@@ -16,6 +16,7 @@
package com.android.server.wm.flicker.launch
import android.platform.test.annotations.Presubmit
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -33,8 +34,23 @@ import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
/**
* Launch an app from the recents app view (the overview)
* Test launching an app from the recents app view (the overview)
*
* To run this test: `atest FlickerTests:OpenAppFromOverviewTest`
*
* Actions:
* Launch [testApp]
* Press recents
* Relaunch an app [testApp] by selecting it in the overview screen, and wait animation to
* complete (only this action is traced)
*
* Notes:
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
* are inherited [OpenAppTransition]
* 2. Part of the test setup occurs automatically via
* [com.android.server.wm.flicker.TransitionRunnerWithRules],
* including configuring navigation mode, initial orientation and ensuring no
* apps are running before setup
*/
@RequiresDevice
@RunWith(Parameterized::class)
@@ -42,6 +58,9 @@ import org.junit.runners.Parameterized
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group1
class OpenAppFromOverviewTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSpec) {
/**
* Defines the transition used to run the test
*/
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = {
super.transition(this, it)
@@ -63,13 +82,38 @@ class OpenAppFromOverviewTest(testSpec: FlickerTestParameter) : OpenAppTransitio
}
}
/** {@inheritDoc} */
@FlakyTest
@Test
override fun navBarLayerRotatesAndScales() {
super.navBarLayerRotatesAndScales()
}
override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
/** {@inheritDoc} */
@Presubmit
@Test
override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher()
/** {@inheritDoc} */
@Presubmit
@Test
override fun launcherWindowBecomesInvisible() = super.launcherWindowBecomesInvisible()
/** {@inheritDoc} */
@Presubmit
@Test
override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
/** {@inheritDoc} */
@Presubmit
@Test
override fun navBarWindowIsVisible() = super.navBarWindowIsVisible()
companion object {
/**
* Creates the test configurations.
*
* See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
* repetitions, screen orientation and navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {

View File

@@ -0,0 +1,244 @@
/*
* 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.launch
import android.content.ComponentName
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.Surface
import android.view.WindowManagerPolicyConstants
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
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.annotation.Group1
import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.google.common.truth.Truth
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
/**
* Test launching an app while the device is locked
*
* To run this test: `atest FlickerTests:OpenAppNonResizeableTest`
*
* Actions:
* Lock the device.
* Launch an app on top of the lock screen [testApp] and wait animation to complete
*
* Notes:
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
* are inherited [OpenAppTransition]
* 2. Part of the test setup occurs automatically via
* [com.android.server.wm.flicker.TransitionRunnerWithRules],
* including configuring navigation mode, initial orientation and ensuring no
* apps are running before setup
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group1
class OpenAppNonResizeableTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSpec) {
override val testApp = NonResizeableAppHelper(instrumentation)
private val colorFadComponent = ComponentName("", "ColorFade BLAST#")
/**
* Defines the transition used to run the test
*/
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = { args ->
super.transition(this, args)
setup {
eachRun {
device.sleep()
wmHelper.waitFor("noAppWindowsOnTop") {
it.wmState.topVisibleAppWindow.isEmpty()
}
}
}
teardown {
eachRun {
testApp.exit(wmHelper)
}
}
transitions {
testApp.launchViaIntent(wmHelper)
wmHelper.waitForFullScreenApp(testApp.component)
}
}
/**
* Checks that the nav bar layer starts visible, becomes invisible during unlocking animation
* and becomes visible at the end
*/
@Presubmit
@Test
fun navBarLayerVisibilityChanges() {
testSpec.assertLayers {
this.isVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
.then()
.isInvisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
.then()
.isVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
}
}
/**
* Checks that the app layer doesn't exist at the start of the transition, that it is
* created (invisible) and becomes visible during the transition
*/
@Presubmit
@Test
fun appLayerBecomesVisible() {
testSpec.assertLayers {
this.notContains(testApp.component)
.then()
.isInvisible(testApp.component)
.then()
.isVisible(testApp.component)
}
}
/**
* Checks that the app window doesn't exist at the start of the transition, that it is
* created (invisible - optional) and becomes visible during the transition
*
* The `isAppWindowInvisible` step is optional because we log once per frame, upon logging,
* the window may be visible or not depending on what was processed until that moment.
*/
@Presubmit
@Test
fun appWindowBecomesVisible() {
testSpec.assertWm {
this.notContains(testApp.component)
.then()
.isAppWindowInvisible(testApp.component,
ignoreActivity = true, isOptional = true)
.then()
.isAppWindowVisible(testApp.component, ignoreActivity = true)
}
}
/**
* Checks if [testApp] is visible at the end of the transition
*/
@Presubmit
@Test
fun appWindowBecomesVisibleAtEnd() {
testSpec.assertWmEnd {
this.isVisible(testApp.component)
}
}
/**
* Checks that the nav bar starts the transition visible, then becomes invisible during
* then unlocking animation and becomes visible at the end of the transition
*/
@Postsubmit
@Test
fun navBarWindowsVisibilityChanges() {
testSpec.assertWm {
this.isAboveAppWindowVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
.then()
.isNonAppWindowInvisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
.then()
.isAboveAppWindowVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
}
}
/** {@inheritDoc} */
@FlakyTest
@Test
override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
super.visibleWindowsShownMoreThanOneConsecutiveEntry()
/** {@inheritDoc} */
@FlakyTest
@Test
override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
super.visibleLayersShownMoreThanOneConsecutiveEntry()
/** {@inheritDoc} */
@Postsubmit
@Test
override fun entireScreenCovered() = super.entireScreenCovered()
/**
* Checks that the focus changes from the launcher to [testApp]
*/
@FlakyTest
@Test
override fun focusChanges() = super.focusChanges()
/**
* Checks that the screen is locked at the start of the transition ([colorFadComponent])
* layer is visible
*/
@Postsubmit
@Test
fun screenLockedStart() {
testSpec.assertLayersStart {
isVisible(colorFadComponent)
}
}
/**
* This test checks if the launcher is visible at the start and the app at the end,
* it cannot use the regular assertion (check over time), because on lock screen neither
* the app not the launcher are visible, and there is no top visible window.
*/
@Postsubmit
@Test
override fun appWindowReplacesLauncherAsTopWindow() {
testSpec.assertWm {
this.invoke("noAppWindowsOnTop") {
Truth.assertWithMessage("Should not have any app window on top " +
"when the screen is locked")
.that(it.wmState.topVisibleAppWindow)
.isEmpty()
}.then()
.isAppWindowOnTop(testApp.component)
}
}
companion object {
/**
* Creates the test configurations.
*
* See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
* repetitions, screen orientation and navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
.getConfigNonRotationTests(
repetitions = 5,
supportedNavigationModes =
listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY),
supportedRotations = listOf(Surface.ROTATION_0)
)
}
}
}

View File

@@ -40,12 +40,16 @@ import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.SNAPSHOT_COMPONENT
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.SPLASH_SCREEN_COMPONENT
import org.junit.Test
abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) {
protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
protected val testApp: StandardAppHelper = SimpleAppHelper(instrumentation)
protected open val testApp: StandardAppHelper = SimpleAppHelper(instrumentation)
/**
* Defines the transition used to run the test
*/
protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit = {
withTestName { testSpec.name }
repeat { testSpec.config.repetitions }
@@ -62,6 +66,10 @@ abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) {
}
}
/**
* Entry point for the test runner. It will use this method to initialize and cache
* flicker executions
*/
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
@@ -69,42 +77,60 @@ abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) {
}
}
@Presubmit
@Test
/**
* Checks that the navigation bar window is visible during the whole transition
*/
open fun navBarWindowIsVisible() {
testSpec.navBarWindowIsVisible()
}
@Presubmit
@Test
/**
* Checks that the navigation bar layer is visible during the whole transition
*/
open fun navBarLayerIsVisible() {
testSpec.navBarLayerIsVisible()
}
/**
* Checks the position of the navigation bar at the start and end of the transition
*/
@Presubmit
@Test
open fun navBarLayerRotatesAndScales() {
testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0, testSpec.config.endRotation)
}
/**
* Checks that the status bar window is visible during the whole transition
*/
@Presubmit
@Test
open fun statusBarWindowIsVisible() {
testSpec.statusBarWindowIsVisible()
}
/**
* Checks that the status bar layer is visible during the whole transition
*/
@Presubmit
@Test
open fun statusBarLayerIsVisible() {
testSpec.statusBarLayerIsVisible()
}
/**
* Checks the position of the status bar at the start and end of the transition
*/
@Presubmit
@Test
open fun statusBarLayerRotatesScales() {
testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0, testSpec.config.endRotation)
}
/**
* Checks that all windows that are visible on the trace, are visible for at least 2
* consecutive entries.
*/
@Presubmit
@Test
open fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
@@ -113,6 +139,10 @@ abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) {
}
}
/**
* Checks that all layers that are visible on the trace, are visible for at least 2
* consecutive entries.
*/
@Presubmit
@Test
open fun visibleLayersShownMoreThanOneConsecutiveEntry() {
@@ -121,13 +151,16 @@ abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) {
}
}
/**
* Checks that all parts of the screen are covered during the transition
*/
@Presubmit
@Test
// During testing the launcher is always in portrait mode
open fun entireScreenCovered() {
testSpec.entireScreenCovered(Surface.ROTATION_0, testSpec.config.endRotation)
}
open fun entireScreenCovered() = testSpec.entireScreenCovered()
/**
* Checks that the focus changes from the launcher to [testApp]
*/
@Presubmit
@Test
open fun focusChanges() {
@@ -136,12 +169,19 @@ abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) {
}
}
@Presubmit
@Test
/**
* Checks that [LAUNCHER_COMPONENT] layer is visible at the start of the transition, and
* is replaced by [testApp], which remains visible until the end
*/
open fun appLayerReplacesLauncher() {
testSpec.replacesLayer(LAUNCHER_COMPONENT, testApp.component)
}
/**
* Checks that [LAUNCHER_COMPONENT] window is visible at the start of the transition, and
* is replaced by a snapshot or splash screen (optional), and finally, is replaced by
* [testApp], which remains visible until the end
*/
@Presubmit
@Test
open fun appWindowReplacesLauncherAsTopWindow() {
@@ -150,12 +190,16 @@ abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) {
.then()
.isAppWindowOnTop(SNAPSHOT_COMPONENT, isOptional = true)
.then()
.isAppWindowOnTop(SPLASH_SCREEN_COMPONENT, isOptional = true)
.then()
.isAppWindowOnTop(testApp.component)
}
}
@Presubmit
@Test
/**
* Checks that [LAUNCHER_COMPONENT] window is visible at the start, and
* becomes invisible during the transition
*/
open fun launcherWindowBecomesInvisible() {
testSpec.assertWm {
this.isAppWindowVisible(LAUNCHER_COMPONENT)

View File

@@ -16,6 +16,7 @@
package com.android.server.wm.flicker.launch
import android.platform.test.annotations.Presubmit
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -32,8 +33,22 @@ import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
/**
* Test warm launch app.
* Test warm launching an app from launcher
*
* To run this test: `atest FlickerTests:OpenAppWarmTest`
*
* Actions:
* Launch [testApp]
* Press home
* Relaunch an app [testApp] and wait animation to complete (only this action is traced)
*
* Notes:
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
* are inherited [OpenAppTransition]
* 2. Part of the test setup occurs automatically via
* [com.android.server.wm.flicker.TransitionRunnerWithRules],
* including configuring navigation mode, initial orientation and ensuring no
* apps are running before setup
*/
@RequiresDevice
@RunWith(Parameterized::class)
@@ -41,6 +56,9 @@ import org.junit.runners.Parameterized
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group1
class OpenAppWarmTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSpec) {
/**
* Defines the transition used to run the test
*/
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = {
super.transition(this, it)
@@ -65,11 +83,38 @@ class OpenAppWarmTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSp
}
}
/** {@inheritDoc} */
@FlakyTest
@Test
override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
/** {@inheritDoc} */
@Presubmit
@Test
override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher()
/** {@inheritDoc} */
@Presubmit
@Test
override fun launcherWindowBecomesInvisible() = super.launcherWindowBecomesInvisible()
/** {@inheritDoc} */
@Presubmit
@Test
override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
/** {@inheritDoc} */
@Presubmit
@Test
override fun navBarWindowIsVisible() = super.navBarWindowIsVisible()
companion object {
/**
* Creates the test configurations.
*
* See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
* repetitions, screen orientation and navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {

View File

@@ -0,0 +1,331 @@
/*
* 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.helpers.NonResizeableAppHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.isRotated
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 back to previous app from last opened app
*
* To run this test: `atest FlickerTests:QuickSwitchBetweenTwoAppsBackTest`
*
* Actions:
* Launch an app [testApp1]
* Launch another app [testApp2]
* Swipe right from the bottom of the screen to quick switch back to the first app [testApp1]
*
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group1
class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestParameter) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
private val testApp1 = SimpleAppHelper(instrumentation)
private val testApp2 = NonResizeableAppHelper(instrumentation)
private val startDisplayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
setup {
eachRun {
testApp1.launchViaIntent(wmHelper)
wmHelper.waitForFullScreenApp(testApp1.component)
testApp2.launchViaIntent(wmHelper)
wmHelper.waitForFullScreenApp(testApp2.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,
if (testSpec.config.startRotation.isRotated()) 75 else 30
)
wmHelper.waitForFullScreenApp(testApp1.component)
wmHelper.waitForAppTransitionIdle()
}
teardown {
test {
testApp1.exit()
testApp2.exit()
}
}
}
}
/**
* Checks that the transition starts with [testApp2]'s windows filling/covering exactly the
* entirety of the display.
*/
@Postsubmit
@Test
fun startsWithApp2WindowsCoverFullScreen() {
testSpec.assertWmStart {
this.frameRegion(testApp2.component).coversExactly(startDisplayBounds)
}
}
/**
* Checks that the transition starts with [testApp2]'s layers filling/covering exactly the
* entirety of the display.
*/
@Postsubmit
@Test
fun startsWithApp2LayersCoverFullScreen() {
testSpec.assertLayersStart {
this.visibleRegion(testApp2.component).coversExactly(startDisplayBounds)
}
}
/**
* Checks that the transition starts with [testApp2] being the top window.
*/
@Postsubmit
@Test
fun startsWithApp2WindowBeingOnTop() {
testSpec.assertWmStart {
this.isAppWindowOnTop(testApp2.component)
}
}
/**
* Checks that [testApp1] windows fill the entire screen (i.e. is "fullscreen") at the end of the
* transition once we have fully quick switched from [testApp2] back to the [testApp1].
*/
@Postsubmit
@Test
fun endsWithApp1WindowsCoveringFullScreen() {
testSpec.assertWmEnd {
this.frameRegion(testApp1.component).coversExactly(startDisplayBounds)
}
}
/**
* Checks that [testApp1] layers fill the entire screen (i.e. is "fullscreen") at the end of the
* transition once we have fully quick switched from [testApp2] back to the [testApp1].
*/
@Postsubmit
@Test
fun endsWithApp1LayersCoveringFullScreen() {
testSpec.assertLayersEnd {
this.visibleRegion(testApp1.component).coversExactly(startDisplayBounds)
}
}
/**
* Checks that [testApp1] is the top window at the end of the transition once we have fully quick
* switched from [testApp2] back to the [testApp1].
*/
@Postsubmit
@Test
fun endsWithApp1BeingOnTop() {
testSpec.assertWmEnd {
this.isAppWindowOnTop(testApp1.component)
}
}
/**
* Checks that [testApp1]'s window starts off invisible and becomes visible at some point before
* the end of the transition and then stays visible until the end of the transition.
*/
@Postsubmit
@Test
fun app1WindowBecomesAndStaysVisible() {
testSpec.assertWm {
this.isAppWindowInvisible(testApp1.component)
.then()
.isAppWindowVisible(SNAPSHOT_COMPONENT, isOptional = true)
.then()
.isAppWindowVisible(testApp1.component, ignoreActivity = true)
}
}
/**
* Checks that [testApp1]'s layer starts off invisible and becomes visible at some point before
* the end of the transition and then stays visible until the end of the transition.
*/
@Postsubmit
@Test
fun app1LayerBecomesAndStaysVisible() {
testSpec.assertLayers {
this.isInvisible(testApp1.component)
.then()
.isVisible(testApp1.component)
}
}
/**
* Checks that [testApp2]'s window starts off visible and becomes invisible at some point before
* the end of the transition and then stays invisible until the end of the transition.
*/
@Postsubmit
@Test
fun app2WindowBecomesAndStaysInvisible() {
testSpec.assertWm {
this.isAppWindowVisible(testApp2.component, ignoreActivity = true)
.then()
.isAppWindowInvisible(testApp2.component)
}
}
/**
* Checks that [testApp2]'s layer starts off visible and becomes invisible at some point before
* the end of the transition and then stays invisible until the end of the transition.
*/
@Postsubmit
@Test
fun app2LayerBecomesAndStaysInvisible() {
testSpec.assertLayers {
this.isVisible(testApp2.component)
.then()
.isInvisible(testApp2.component)
}
}
/**
* Checks that [testApp2]'s window is visible at least until [testApp1]'s window is visible.
* Ensures that at any point, either [testApp1] or [testApp2]'s windows are at least partially
* visible.
*/
@Postsubmit
@Test
fun app1WindowIsVisibleOnceApp2WindowIsInvisible() {
testSpec.assertWm {
this.isAppWindowVisible(testApp2.component)
.then()
// TODO: Do we actually want to test this? Seems too implementation specific...
.isAppWindowVisible(LAUNCHER_COMPONENT, isOptional = true)
.then()
.isAppWindowVisible(SNAPSHOT_COMPONENT, isOptional = true)
.then()
.isAppWindowVisible(testApp1.component)
}
}
/**
* Checks that [testApp2]'s layer is visible at least until [testApp1]'s window is visible.
* Ensures that at any point, either [testApp1] or [testApp2]'s windows are at least partially
* visible.
*/
@Postsubmit
@Test
fun app1LayerIsVisibleOnceApp2LayerIsInvisible() {
testSpec.assertLayers {
this.isVisible(testApp2.component)
.then()
.isVisible(LAUNCHER_COMPONENT, isOptional = true)
.then()
.isVisible(SNAPSHOT_COMPONENT, isOptional = true)
.then()
.isVisible(testApp1.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()
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
.getConfigNonRotationTests(
repetitions = 5,
supportedNavigationModes = listOf(
WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
),
supportedRotations = listOf(Surface.ROTATION_0, Surface.ROTATION_90)
)
}
}
}

View File

@@ -0,0 +1,347 @@
/*
* 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.Presubmit
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].
*/
@Presubmit
@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].
*/
@Presubmit
@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].
*/
@Presubmit
@Test
fun endsWithAppBeingOnTop() {
testSpec.assertWmEnd {
this.isAppWindowOnTop(testApp.component)
}
}
/**
* Checks that the transition starts with the home activity being tagged as visible.
*/
@Presubmit
@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.
*/
@Presubmit
@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.
*/
@Presubmit
@Test
fun startsWithLauncherLayersCoverFullScreen() {
testSpec.assertLayersStart {
this.visibleRegion(LAUNCHER_COMPONENT).coversExactly(startDisplayBounds)
}
}
/**
* Checks that the transition starts with the launcher being the top window.
*/
@Presubmit
@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].
*/
@Presubmit
@Test
fun endsWithHomeActivityFlaggedInvisible() {
testSpec.assertWmEnd {
this.isHomeActivityVisible(false)
}
}
/**
* Checks that [testApp]'s window starts off invisible and becomes visible at some point before
* the end of the transition and then stays visible until the end of the transition.
*/
@Presubmit
@Test
fun appWindowBecomesAndStaysVisible() {
testSpec.assertWm {
this.isAppWindowInvisible(testApp.component, ignoreActivity = true)
.then()
.isAppWindowVisible(testApp.component, ignoreActivity = true)
}
}
/**
* Checks that [testApp]'s layer starts off invisible and becomes visible at some point before
* the end of the transition and then stays visible until the end of the transition.
*/
@Presubmit
@Test
fun appLayerBecomesAndStaysVisible() {
testSpec.assertLayers {
this.isInvisible(testApp.component)
.then()
.isVisible(testApp.component)
}
}
/**
* Checks that the launcher window starts off visible and becomes invisible at some point before
* the end of the transition and then stays invisible until the end of the transition.
*/
@Presubmit
@Test
fun launcherWindowBecomesAndStaysInvisible() {
testSpec.assertWm {
this.isAppWindowVisible(LAUNCHER_COMPONENT)
.then()
.isAppWindowInvisible(LAUNCHER_COMPONENT)
}
}
/**
* Checks that the launcher layer starts off visible and becomes invisible at some point before
* the end of the transition and then stays invisible until the end of the transition.
*/
@Presubmit
@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.
*/
@Presubmit
@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.
*/
@Presubmit
@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.
*/
@Presubmit
@Test
fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsVisible()
/**
* Checks that the navbar layer is visible throughout the entire transition.
*/
@Presubmit
@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.
*/
@Presubmit
@Test
fun navbarIsAlwaysInRightPosition() =
testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation)
/**
* Checks that the status bar window is visible throughout the entire transition.
*/
@Presubmit
@Test
fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsVisible()
/**
* Checks that the status bar layer is visible throughout the entire transition.
*/
@Presubmit
@Test
fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsVisible()
/**
* Checks that the screen is always fully covered by visible layers throughout the transition.
*/
@Presubmit
@Test
fun screenIsAlwaysFilled() = testSpec.entireScreenCovered()
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
.getConfigNonRotationTests(
repetitions = 5,
supportedNavigationModes = listOf(
WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
),
// TODO: Test with 90 rotation
supportedRotations = listOf(Surface.ROTATION_0)
)
}
}
}

View File

@@ -16,7 +16,6 @@
package com.android.server.wm.flicker.rotation
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
@@ -85,7 +84,7 @@ class ChangeAppRotationTest(
testSpec.statusBarWindowIsVisible()
}
@Postsubmit
@Presubmit
@Test
fun statusBarLayerIsVisible() {
testSpec.statusBarLayerIsVisible()
@@ -98,30 +97,12 @@ class ChangeAppRotationTest(
testSpec.config.startRotation, testSpec.config.endRotation)
}
@Presubmit
@Test
override fun navBarWindowIsVisible() {
super.navBarWindowIsVisible()
}
@Postsubmit
@Test
override fun navBarLayerIsVisible() {
super.navBarLayerIsVisible()
}
@FlakyTest
@Test
override fun navBarLayerRotatesAndScales() {
super.navBarLayerRotatesAndScales()
}
@Postsubmit
@Test
override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
super.visibleLayersShownMoreThanOneConsecutiveEntry()
}
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic

View File

@@ -25,7 +25,6 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.helpers.StandardAppHelper
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.navBarLayerIsVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
@@ -39,8 +38,6 @@ abstract class RotationTransition(protected val testSpec: FlickerTestParameter)
protected abstract val testApp: StandardAppHelper
protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
protected val startingPos get() = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
protected val endingPos get() = WindowUtils.getDisplayBounds(testSpec.config.endRotation)
protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit = {
setup {
@@ -107,10 +104,7 @@ abstract class RotationTransition(protected val testSpec: FlickerTestParameter)
@Presubmit
@Test
open fun entireScreenCovered() {
testSpec.entireScreenCovered(testSpec.config.startRotation,
testSpec.config.endRotation, allStates = false)
}
open fun entireScreenCovered() = testSpec.entireScreenCovered()
@Presubmit
@Test
@@ -124,7 +118,9 @@ abstract class RotationTransition(protected val testSpec: FlickerTestParameter)
@Test
open fun appLayerRotates_StartingPos() {
testSpec.assertLayersStart {
this.visibleRegion(testApp.component).coversExactly(startingPos)
this.entry.displays.map { display ->
this.visibleRegion(testApp.component).coversExactly(display.layerStackSpace)
}
}
}
@@ -132,7 +128,9 @@ abstract class RotationTransition(protected val testSpec: FlickerTestParameter)
@Test
open fun appLayerRotates_EndingPos() {
testSpec.assertLayersEnd {
this.visibleRegion(testApp.component).coversExactly(endingPos)
this.entry.displays.map { display ->
this.visibleRegion(testApp.component).coversExactly(display.layerStackSpace)
}
}
}
}

View File

@@ -16,7 +16,6 @@
package com.android.server.wm.flicker.rotation
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.WindowManager
import androidx.test.filters.FlakyTest
@@ -99,13 +98,15 @@ class SeamlessAppRotationTest(
}
}
@Postsubmit
@Presubmit
@Test
fun appLayerRotates() {
testSpec.assertLayers {
this.coversExactly(startingPos, testApp.component)
.then()
.coversExactly(endingPos, testApp.component)
this.invoke("entireScreenCovered") { entry ->
entry.entry.displays.map { display ->
entry.visibleRegion(testApp.component).coversExactly(display.layerStackSpace)
}
}
}
}
@@ -125,24 +126,6 @@ class SeamlessAppRotationTest(
}
}
@Presubmit
@Test
override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
super.visibleLayersShownMoreThanOneConsecutiveEntry()
}
@Postsubmit
@Test
override fun navBarWindowIsVisible() {
super.navBarWindowIsVisible()
}
@Postsubmit
@Test
override fun navBarLayerIsVisible() {
super.navBarLayerIsVisible()
}
@FlakyTest
@Test
override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()

View File

@@ -59,5 +59,26 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".NonResizeableActivity"
android:resizeableActivity="false"
android:taskAffinity="com.android.server.wm.flicker.testapp.NonResizeableActivity"
android:label="NonResizeableApp"
android:exported="true"
android:showOnLockScreen="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".ButtonActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.ButtonActivity"
android:configChanges="orientation|screenSize"
android:label="ButtonActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2018 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.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_orange_light">
<Button
android:id="@+id/launch_second_activity"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Second activity" />
</LinearLayout>

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 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.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@android:color/holo_orange_light">
<TextView
android:id="@+id/NonResizeableTest"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center_vertical|center_horizontal"
android:text="NonResizeableActivity"
android:textAppearance="?android:attr/textAppearanceLarge"/>
</LinearLayout>

View File

@@ -41,4 +41,14 @@ public class ActivityOptions {
public static final ComponentName SIMPLE_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME =
new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".SimpleActivity");
public static final String NON_RESIZEABLE_ACTIVITY_LAUNCHER_NAME = "NonResizeableApp";
public static final ComponentName NON_RESIZEABLE_ACTIVITY_COMPONENT_NAME =
new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".NonResizeableActivity");
public static final String BUTTON_ACTIVITY_LAUNCHER_NAME = "ButtonApp";
public static final ComponentName BUTTON_ACTIVITY_COMPONENT_NAME =
new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".ButtonActivity");
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright (C) 2018 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.testapp;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.WindowManager;
import android.widget.Button;
public class ButtonActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
WindowManager.LayoutParams p = getWindow().getAttributes();
p.layoutInDisplayCutoutMode = WindowManager.LayoutParams
.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
getWindow().setAttributes(p);
setContentView(R.layout.activity_button);
Button button = findViewById(R.id.launch_second_activity);
button.setOnClickListener(v -> {
Intent intent = new Intent(ButtonActivity.this, SimpleActivity.class);
startActivity(intent);
});
}
}

View File

@@ -0,0 +1,37 @@
/*
* 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.testapp;
import android.app.Activity;
import android.app.KeyguardManager;
import android.os.Bundle;
public class NonResizeableActivity extends Activity {
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_non_resizeable);
setShowWhenLocked(true);
setTurnScreenOn(true);
KeyguardManager keyguardManager = getSystemService(KeyguardManager.class);
if (keyguardManager != null) {
keyguardManager.requestDismissKeyguard(this, null);
}
}
}