1/ Migrate cts SplitScreenTests to wm shell flicker test

This is the initial version to achieve:
1. SplitScreenActivity in test-apps
2. Create SplitScreenTestBase and add a basic test cases to
   ensure SplitScreenActivity enter split screen mode
3. Assert StatusBar/NavBar/SplitScreenActivity showing when
   device enter split screen mode
4. Verify docked apps ending bounds

Bug: 169271943
Bug: 172811376
Test: atest WMShellFlickerTests:EnterSplitScreenTest
Change-Id: I26461b958c578dcbcc5ca2235f9d269559fd9ec5
This commit is contained in:
Bill Lin
2020-11-09 14:46:13 +08:00
parent 660f7cd8f1
commit d89e31bad2
14 changed files with 583 additions and 6 deletions

View File

@@ -71,6 +71,16 @@ fun LayersAssertion.noUncoveredRegions(
}
}
@JvmOverloads
fun LayersAssertion.statusBarLayerIsAlwaysVisible(
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
all("statusBarLayerIsAlwaysVisible", bugId, enabled) {
this.showsLayer(FlickerTestBase.STATUS_BAR_WINDOW_TITLE)
}
}
@JvmOverloads
fun LayersAssertion.navBarLayerIsAlwaysVisible(
bugId: Int = 0,
@@ -82,12 +92,22 @@ fun LayersAssertion.navBarLayerIsAlwaysVisible(
}
@JvmOverloads
fun LayersAssertion.statusBarLayerIsAlwaysVisible(
fun LayersAssertion.dockedStackDividerIsVisible(
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
all("statusBarLayerIsAlwaysVisible", bugId, enabled) {
this.showsLayer(FlickerTestBase.STATUS_BAR_WINDOW_TITLE)
end("dockedStackDividerIsVisible", bugId, enabled) {
this.showsLayer(FlickerTestBase.DOCKED_STACK_DIVIDER)
}
}
@JvmOverloads
fun LayersAssertion.dockedStackDividerIsInvisible(
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
end("dockedStackDividerIsInvisible", bugId, enabled) {
this.hasNotLayer(FlickerTestBase.DOCKED_STACK_DIVIDER)
}
}

View File

@@ -20,17 +20,27 @@ import android.content.ComponentName
const val IME_WINDOW_NAME = "InputMethod"
const val PIP_WINDOW_NAME = "PipMenuActivity"
const val SPLITSCREEN_PRIMARY_WINDOW_NAME = "SplitScreenActivity"
const val SPLITSCREEN_SECONDARY_WINDOW_NAME = "SplitScreenSecondaryActivity"
// Test App
const val SYSTEM_UI_PACKAGE_NAME = "com.android.systemui"
const val TEST_APP_PACKAGE_NAME = "com.android.wm.shell.flicker.testapp"
// Test App > Pip Activity
val TEST_APP_PIP_ACTIVITY_COMPONENT_NAME: ComponentName = ComponentName.createRelative(
TEST_APP_PACKAGE_NAME, ".PipActivity")
const val TEST_APP_PIP_ACTIVITY_LABEL = "PipApp"
const val TEST_APP_PIP_ACTIVITY_WINDOW_NAME = "PipActivity"
// Test App > Ime Activity
val TEST_APP_IME_ACTIVITY_COMPONENT_NAME: ComponentName = ComponentName.createRelative(
TEST_APP_PACKAGE_NAME, ".ImeActivity")
const val TEST_APP_IME_ACTIVITY_LABEL = "ImeApp"
const val SYSTEM_UI_PACKAGE_NAME = "com.android.systemui"
// Test App > SplitScreen Activity
val TEST_APP_SPLITSCREEN_PRIMARY_COMPONENT_NAME: ComponentName = ComponentName.createRelative(
TEST_APP_PACKAGE_NAME, ".$SPLITSCREEN_PRIMARY_WINDOW_NAME")
val TEST_APP_SPLITSCREEN_SECONDARY_COMPONENT_NAME: ComponentName = ComponentName.createRelative(
TEST_APP_PACKAGE_NAME, ".$SPLITSCREEN_SECONDARY_WINDOW_NAME")
const val TEST_APP_SPLITSCREEN_PRIMARY_LABEL = "SplitScreenPrimaryApp"
const val TEST_APP_SPLITSCREEN_SECONDARY_LABEL = "SplitScreenSecondaryApp"

View File

@@ -130,5 +130,6 @@ abstract class FlickerTestBase {
const val NAVIGATION_BAR_WINDOW_TITLE = "NavigationBar"
const val STATUS_BAR_WINDOW_TITLE = "StatusBar"
const val DOCKED_STACK_DIVIDER = "DockedStackDivider"
const val IMAGE_WALLPAPER = "ImageWallpaper"
}
}

View File

@@ -56,6 +56,9 @@ abstract class BaseAppHelper(
hasSystemFeature(FEATURE_LEANBACK) || hasSystemFeature(FEATURE_LEANBACK_ONLY)
}
val defaultWindowName: String
get() = launcherActivityComponent.className
val label: String
get() = context.packageManager.run {
getApplicationLabel(getApplicationInfo(packageName, 0)).toString()
@@ -74,7 +77,9 @@ abstract class BaseAppHelper(
return uiDevice.wait(Until.gone(appSelector), APP_CLOSE_WAIT_TIME_MS)
}
fun forceStop() = activityManager?.forceStopPackage(packageName)
fun forceStop() {
activityManager?.forceStopPackage(packageName)
}
override fun getOpenAppIntent(): Intent {
val intent = Intent()

View File

@@ -0,0 +1,64 @@
/*
* 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.helpers
import android.app.Instrumentation
import android.content.ComponentName
import android.graphics.Region
import android.os.SystemClock
import com.android.server.wm.flicker.helpers.WindowUtils
class SplitScreenHelper(
instrumentation: Instrumentation,
activityLabel: String,
componentName: ComponentName
) : BaseAppHelper(
instrumentation,
activityLabel,
componentName
) {
/**
* Reopens the first device window from the list of recent apps (overview)
*/
fun reopenAppFromOverview() {
val x = uiDevice.displayWidth / 2
val y = uiDevice.displayHeight / 2
uiDevice.click(x, y)
// Wait for animation to complete.
SystemClock.sleep(TIMEOUT_MS)
}
fun getPrimaryBounds(dividerBounds: Region): android.graphics.Region {
val primaryAppBounds = Region(0, 0, dividerBounds.bounds.right,
dividerBounds.bounds.bottom + WindowUtils.dockedStackDividerInset)
return primaryAppBounds
}
fun getSecondaryBounds(dividerBounds: Region): android.graphics.Region {
val displayBounds = WindowUtils.displayBounds
val secondaryAppBounds = Region(0,
dividerBounds.bounds.bottom - WindowUtils.dockedStackDividerInset,
displayBounds.right, displayBounds.bottom - WindowUtils.navigationBarHeight)
return secondaryAppBounds
}
companion object {
const val TEST_REPETITIONS = 1
const val TIMEOUT_MS = 3_000L
}
}

View File

@@ -0,0 +1,159 @@
/*
* 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.splitscreen
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.dsl.runWithFlicker
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.wm.shell.flicker.dockedStackDividerIsVisible
import com.android.wm.shell.flicker.helpers.SplitScreenHelper.Companion.TEST_REPETITIONS
import com.android.wm.shell.flicker.navBarLayerIsAlwaysVisible
import com.android.wm.shell.flicker.navBarWindowIsAlwaysVisible
import com.android.wm.shell.flicker.statusBarWindowIsAlwaysVisible
import com.android.wm.shell.flicker.statusBarLayerIsAlwaysVisible
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
/**
* Test SplitScreen launch.
* To run this test: `atest WMShellFlickerTests:EnterSplitScreenTest`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class EnterSplitScreenTest(
rotationName: String,
rotation: Int
) : SplitScreenTestBase(rotationName, rotation) {
private val splitScreenSetup: FlickerBuilder
get() = FlickerBuilder(instrumentation).apply {
val testLaunchActivity = "launch_splitScreen_test_activity"
withTestName {
testLaunchActivity
}
setup {
eachRun {
uiDevice.wakeUpAndGoToHomeScreen()
splitScreenApp.open()
uiDevice.pressHome()
}
}
teardown {
eachRun {
splitScreenApp.forceStop()
secondaryApp.forceStop()
}
}
assertions {
layersTrace {
navBarLayerIsAlwaysVisible()
statusBarLayerIsAlwaysVisible()
}
windowManagerTrace {
navBarWindowIsAlwaysVisible()
statusBarWindowIsAlwaysVisible()
}
}
}
@Test
fun testEnterSplitScreen_dockActivity() {
val testTag = "testEnterSplitScreen_dockActivity"
runWithFlicker(splitScreenSetup) {
withTestName { testTag }
repeat {
TEST_REPETITIONS
}
transitions {
uiDevice.launchSplitScreen()
}
assertions {
layersTrace {
dockedStackDividerIsVisible()
end("appsEndingBounds", enabled = false) {
val entry = this.trace.entries.firstOrNull()
?: throw IllegalStateException("Trace is empty")
this.hasVisibleRegion(splitScreenApp.defaultWindowName,
splitScreenApp.getPrimaryBounds(
entry.getVisibleBounds(DOCKED_STACK_DIVIDER)))
}
}
windowManagerTrace {
end {
showsAppWindow(splitScreenApp.defaultWindowName)
}
}
}
}
}
@Test
fun testEnterSplitScreen_launchToSide() {
val testTag = "testEnterSplitScreen_launchToSide"
runWithFlicker(splitScreenSetup) {
withTestName { testTag }
repeat {
TEST_REPETITIONS
}
transitions {
secondaryApp.open()
uiDevice.pressHome()
splitScreenApp.open()
uiDevice.pressHome()
uiDevice.launchSplitScreen()
splitScreenApp.reopenAppFromOverview()
}
assertions {
layersTrace {
dockedStackDividerIsVisible()
end("appsEndingBounds", enabled = false) {
val entry = this.trace.entries.firstOrNull()
?: throw IllegalStateException("Trace is empty")
this.hasVisibleRegion(splitScreenApp.defaultWindowName,
splitScreenApp.getPrimaryBounds(
entry.getVisibleBounds(DOCKED_STACK_DIVIDER)))
.and()
.hasVisibleRegion(secondaryApp.defaultWindowName,
splitScreenApp.getSecondaryBounds(
entry.getVisibleBounds(DOCKED_STACK_DIVIDER)))
}
}
windowManagerTrace {
end {
showsAppWindow(splitScreenApp.defaultWindowName)
.and().showsAppWindow(secondaryApp.defaultWindowName)
}
}
}
}
}
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<Array<Any>> {
val supportedRotations = intArrayOf(Surface.ROTATION_0)
return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
}
}
}

View File

@@ -0,0 +1,138 @@
/*
* 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.splitscreen
import android.util.Rational
import android.view.Surface
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.dsl.runWithFlicker
import com.android.server.wm.flicker.helpers.exitSplitScreen
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.resizeSplitScreen
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.wm.shell.flicker.dockedStackDividerIsInvisible
import com.android.wm.shell.flicker.helpers.SplitScreenHelper.Companion.TEST_REPETITIONS
import com.android.wm.shell.flicker.navBarWindowIsAlwaysVisible
import com.android.wm.shell.flicker.statusBarWindowIsAlwaysVisible
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
/**
* Test exit SplitScreen mode.
* To run this test: `atest WMShellFlickerTests:ExitSplitScreenTest`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class ExitSplitScreenTest(
rotationName: String,
rotation: Int
) : SplitScreenTestBase(rotationName, rotation) {
private val splitScreenSetup: FlickerBuilder
get() = FlickerBuilder(instrumentation).apply {
val testLaunchActivity = "launch_splitScreen_test_activity"
withTestName {
testLaunchActivity
}
setup {
eachRun {
uiDevice.wakeUpAndGoToHomeScreen()
secondaryApp.open()
uiDevice.pressHome()
splitScreenApp.open()
uiDevice.pressHome()
uiDevice.launchSplitScreen()
}
}
teardown {
eachRun {
splitScreenApp.forceStop()!!
secondaryApp.forceStop()!!
}
}
}
@Test
fun testEnterSplitScreen_exitPrimarySplitScreenMode() {
val testTag = "testEnterSplitScreen_exitPrimarySplitScreenMode"
runWithFlicker(splitScreenSetup) {
withTestName { testTag }
repeat {
TEST_REPETITIONS
}
transitions {
uiDevice.exitSplitScreen()
}
assertions {
layersTrace {
dockedStackDividerIsInvisible()
}
windowManagerTrace {
navBarWindowIsAlwaysVisible()
statusBarWindowIsAlwaysVisible()
end {
hidesAppWindow(splitScreenApp.defaultWindowName)
}
}
}
}
}
@Test
@FlakyTest(bugId = 172811376)
fun testEnterSplitScreen_exitPrimary_showSecondaryAppFullScreen() {
val testTag = "testEnterSplitScreen_exitPrimary_showSecondaryAppFullScreen"
runWithFlicker(splitScreenSetup) {
withTestName { testTag }
repeat {
TEST_REPETITIONS
}
transitions {
splitScreenApp.reopenAppFromOverview()
uiDevice.resizeSplitScreen(startRatio)
}
assertions {
layersTrace {
dockedStackDividerIsInvisible()
}
windowManagerTrace {
navBarWindowIsAlwaysVisible()
statusBarWindowIsAlwaysVisible()
end {
showsAppWindow(splitScreenApp.defaultWindowName)
}
}
}
}
}
companion object {
private val startRatio = Rational(1, 3)
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<Array<Any>> {
val supportedRotations = intArrayOf(Surface.ROTATION_0)
return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
}
}
}

View File

@@ -0,0 +1,36 @@
/*
* 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.splitscreen
import com.android.wm.shell.flicker.NonRotationTestBase
import com.android.wm.shell.flicker.TEST_APP_SPLITSCREEN_PRIMARY_COMPONENT_NAME
import com.android.wm.shell.flicker.TEST_APP_SPLITSCREEN_PRIMARY_LABEL
import com.android.wm.shell.flicker.TEST_APP_SPLITSCREEN_SECONDARY_COMPONENT_NAME
import com.android.wm.shell.flicker.TEST_APP_SPLITSCREEN_SECONDARY_LABEL
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
abstract class SplitScreenTestBase(
rotationName: String,
rotation: Int
) : NonRotationTestBase(rotationName, rotation) {
protected val splitScreenApp = SplitScreenHelper(instrumentation,
TEST_APP_SPLITSCREEN_PRIMARY_LABEL,
TEST_APP_SPLITSCREEN_PRIMARY_COMPONENT_NAME)
protected val secondaryApp = SplitScreenHelper(instrumentation,
TEST_APP_SPLITSCREEN_SECONDARY_LABEL,
TEST_APP_SPLITSCREEN_SECONDARY_COMPONENT_NAME)
}

View File

@@ -37,6 +37,7 @@
<category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".ImeActivity"
android:taskAffinity="com.android.wm.shell.flicker.testapp.ImeActivity"
android:label="ImeApp"
@@ -50,5 +51,27 @@
<category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".SplitScreenActivity"
android:resizeableActivity="true"
android:taskAffinity="com.android.wm.shell.flicker.testapp.SplitScreenActivity"
android:label="SplitScreenPrimaryApp"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".SplitScreenSecondaryActivity"
android:resizeableActivity="true"
android:taskAffinity="com.android.wm.shell.flicker.testapp.SplitScreenSecondaryActivity"
android:label="SplitScreenSecondaryApp"
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,32 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 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.
-->
<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_green_light">
<TextView
android:id="@+id/SplitScreenTest"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center_vertical|center_horizontal"
android:text="PrimaryActivity"
android:textAppearance="?android:attr/textAppearanceLarge"/>
</LinearLayout>

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 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.
-->
<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_blue_light">
<TextView
android:id="@+id/SplitScreenTest"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center_vertical|center_horizontal"
android:text="SecondaryActivity"
android:textAppearance="?android:attr/textAppearanceLarge"/>
</LinearLayout>

View File

@@ -0,0 +1,29 @@
/*
* 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.testapp;
import android.app.Activity;
import android.os.Bundle;
public class SplitScreenActivity extends Activity {
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_splitscreen);
}
}

View File

@@ -0,0 +1,28 @@
/*
* 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.testapp;
import android.app.Activity;
import android.os.Bundle;
public class SplitScreenSecondaryActivity extends Activity {
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_splitscreen_secondary);
}
}