Split flicker tests into completely independent folders, which can be transferred to the respective teams.
This also allows the tests to be better configured, so that app compat tests don't run on phones
Bug: 307885357
Fixes: 307359855
Test: atest FlickerTestsAppLaunch1 FlickerTestsAppLaunch2 FlickerTestsAppClose FlickerTestsIme FlickerTestsNotification FlickerTestsRotation FlickerTestsQuickswitch FlickerServiceTests
Change-Id: I2dfc90dd4c01614065ba4a1b39696eb7431c3dd5
diff --git a/tests/FlickerTests/Rotation/Android.bp b/tests/FlickerTests/Rotation/Android.bp
new file mode 100644
index 0000000..8e93b5b
--- /dev/null
+++ b/tests/FlickerTests/Rotation/Android.bp
@@ -0,0 +1,33 @@
+//
+// 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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "FlickerTestsRotation",
+ defaults: ["FlickerTestsDefault"],
+ manifest: "AndroidManifest.xml",
+ test_config_template: "AndroidTestTemplate.xml",
+ srcs: ["src/**/*"],
+ static_libs: ["FlickerTestsBase"],
+}
diff --git a/tests/FlickerTests/Rotation/AndroidManifest.xml b/tests/FlickerTests/Rotation/AndroidManifest.xml
new file mode 100644
index 0000000..6bbb1f6
--- /dev/null
+++ b/tests/FlickerTests/Rotation/AndroidManifest.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ package="com.android.server.wm.flicker.rotation">
+
+ <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/>
+ <!-- Read and write traces from external storage -->
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <!-- Allow the test to write directly to /sdcard/ -->
+ <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
+ <!-- Write secure settings -->
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+ <!-- Capture screen contents -->
+ <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
+ <!-- Enable / Disable tracing !-->
+ <uses-permission android:name="android.permission.DUMP" />
+ <!-- Force-stop test apps -->
+ <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES"/>
+ <!-- Run layers trace -->
+ <uses-permission android:name="android.permission.HARDWARE_TEST"/>
+ <!-- Capture screen recording -->
+ <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"/>
+ <!-- Workaround grant runtime permission exception from b/152733071 -->
+ <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
+ <uses-permission android:name="android.permission.READ_LOGS"/>
+ <!-- ATM.removeRootTasksWithActivityTypes() -->
+ <uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS" />
+ <!-- ActivityOptions.makeCustomTaskAnimation() -->
+ <uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS" />
+ <!-- Allow the test to connect to perfetto trace processor -->
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <!-- Allow the test to write directly to /sdcard/ and connect to trace processor -->
+ <application android:requestLegacyExternalStorage="true"
+ android:networkSecurityConfig="@xml/network_security_config"
+ android:largeHeap="true">
+ <uses-library android:name="android.test.runner"/>
+ <uses-library android:name="androidx.window.extensions" android:required="false"/>
+
+ <!-- (b/197936012) Remove startup provider due to test timeout issue -->
+ <provider
+ android:name="androidx.startup.InitializationProvider"
+ android:authorities="${applicationId}.androidx-startup"
+ tools:node="remove" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.server.wm.flicker.rotation"
+ android:label="WindowManager Flicker Tests">
+ </instrumentation>
+</manifest>
diff --git a/tests/FlickerTests/Rotation/AndroidTestTemplate.xml b/tests/FlickerTests/Rotation/AndroidTestTemplate.xml
new file mode 100644
index 0000000..b5ea739
--- /dev/null
+++ b/tests/FlickerTests/Rotation/AndroidTestTemplate.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright 2018 Google Inc. All Rights Reserved.
+ -->
+<configuration description="Runs WindowManager {MODULE}">
+ <option name="test-tag" value="FlickerTests"/>
+ <!-- Needed for storing the perfetto trace files in the sdcard/test_results-->
+ <option name="isolated-storage" value="false"/>
+
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <!-- keeps the screen on during tests -->
+ <option name="screen-always-on" value="on"/>
+ <!-- prevents the phone from restarting -->
+ <option name="force-skip-system-props" value="true"/>
+ <!-- set WM tracing verbose level to all -->
+ <option name="run-command" value="cmd window tracing level all"/>
+ <!-- set WM tracing to frame (avoid incomplete states) -->
+ <option name="run-command" value="cmd window tracing frame"/>
+ <!-- ensure lock screen mode is swipe -->
+ <option name="run-command" value="locksettings set-disabled false"/>
+ <!-- disable betterbug as it's log collection dialogues cause flakes in e2e tests -->
+ <option name="run-command" value="pm disable com.google.android.internal.betterbug"/>
+ <!-- restart launcher to activate TAPL -->
+ <option name="run-command"
+ value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher"/>
+ <!-- Increase trace size: 20mb for WM and 80mb for SF -->
+ <option name="run-command" value="cmd window tracing size 20480"/>
+ <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920"/>
+ <!-- b/307664397 - Ensure camera has the correct permissions and doesn't show a dialog -->
+ <option name="run-command"
+ value="pm grant com.google.android.GoogleCamera android.permission.CAMERA"/>
+ <option name="run-command"
+ value="pm grant com.google.android.GoogleCamera android.permission.RECORD_AUDIO"/>
+ <option name="run-command"
+ value="pm grant com.google.android.GoogleCamera android.permission.ACCESS_FINE_LOCATION"/>
+ <option name="run-command"
+ value="pm grant com.google.android.GoogleCamera android.permission.ACCESS_COARSE_LOCATION"/>
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="test-user-token" value="%TEST_USER%"/>
+ <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
+ <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
+ <option name="run-command" value="settings put system show_touches 1"/>
+ <option name="run-command" value="settings put system pointer_location 1"/>
+ <option name="teardown-command"
+ value="settings delete secure show_ime_with_hard_keyboard"/>
+ <option name="teardown-command" value="settings delete system show_touches"/>
+ <option name="teardown-command" value="settings delete system pointer_location"/>
+ <option name="teardown-command"
+ value="cmd overlay enable com.android.internal.systemui.navbar.gestural"/>
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true"/>
+ <option name="test-file-name" value="{MODULE}.apk"/>
+ <option name="test-file-name" value="FlickerTestApp.apk"/>
+ </target_preparer>
+ <!-- Needed for pushing the trace config file -->
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="push-file"
+ key="trace_config.textproto"
+ value="/data/misc/perfetto-traces/trace_config.textproto"
+ />
+ <!--Install the content provider automatically when we push some file in sdcard folder.-->
+ <!--Needed to avoid the installation during the test suite.-->
+ <option name="push-file" key="trace_config.textproto" value="/sdcard/sample.textproto"/>
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="{PACKAGE}"/>
+ <option name="shell-timeout" value="6600s"/>
+ <option name="test-timeout" value="6600s"/>
+ <option name="hidden-api-checks" value="false"/>
+ <option name="device-listeners" value="android.device.collectors.PerfettoListener"/>
+ <!-- PerfettoListener related arguments -->
+ <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/>
+ <option name="instrumentation-arg"
+ key="perfetto_config_file"
+ value="trace_config.textproto"
+ />
+ <option name="instrumentation-arg" key="per_run" value="true"/>
+ </test>
+ <!-- Needed for pulling the collected trace config on to the host -->
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="pull-pattern-keys" value="perfetto_file_path"/>
+ <option name="directory-keys"
+ value="/data/user/0/com.android.server.wm.flicker.rotation/files"/>
+ <option name="collect-on-run-ended-only" value="true"/>
+ <option name="clean-up" value="true"/>
+ </metrics_collector>
+</configuration>
diff --git a/tests/FlickerTests/Rotation/OWNERS b/tests/FlickerTests/Rotation/OWNERS
new file mode 100644
index 0000000..f7c0a87
--- /dev/null
+++ b/tests/FlickerTests/Rotation/OWNERS
@@ -0,0 +1,2 @@
+# window manager > animations/transitions
+# Bug component: 316275
diff --git a/tests/FlickerTests/Rotation/res/anim/show_hide_show_3000ms.xml b/tests/FlickerTests/Rotation/res/anim/show_hide_show_3000ms.xml
new file mode 100644
index 0000000..7b3f07e
--- /dev/null
+++ b/tests/FlickerTests/Rotation/res/anim/show_hide_show_3000ms.xml
@@ -0,0 +1,31 @@
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fillAfter="true">
+
+ <alpha
+ android:fromAlpha="1.0"
+ android:toAlpha="0.0"
+ android:duration="1000" />
+
+ <alpha
+ android:startOffset="2000"
+ android:fromAlpha="1.0"
+ android:toAlpha="1.0"
+ android:duration="1000" />
+</set>
\ No newline at end of file
diff --git a/tests/FlickerTests/Rotation/res/xml/network_security_config.xml b/tests/FlickerTests/Rotation/res/xml/network_security_config.xml
new file mode 100644
index 0000000..4bd9ca0
--- /dev/null
+++ b/tests/FlickerTests/Rotation/res/xml/network_security_config.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<network-security-config>
+ <domain-config cleartextTrafficPermitted="true">
+ <domain includeSubdomains="true">localhost</domain>
+ </domain-config>
+</network-security-config>
diff --git a/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
new file mode 100644
index 0000000..bdbf0d2
--- /dev/null
+++ b/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -0,0 +1,148 @@
+/*
+ * 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.server.wm.flicker.rotation
+
+import android.platform.test.annotations.PlatinumTest
+import android.platform.test.annotations.Presubmit
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test opening an app and cycling through app rotations
+ *
+ * Currently, runs:
+ * ```
+ * 0 -> 90 degrees
+ * 90 -> 0 degrees
+ * ```
+ *
+ * Actions:
+ * ```
+ * Launch an app (via intent)
+ * Set initial device orientation
+ * Start tracing
+ * Change device orientation
+ * Stop tracing
+ * ```
+ *
+ * To run this test: `atest FlickerTests:ChangeAppRotationTest`
+ *
+ * To run only the presubmit assertions add: `--
+ *
+ * ```
+ * --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
+ * --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Presubmit`
+ * ```
+ *
+ * To run only the postsubmit assertions add: `--
+ *
+ * ```
+ * --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
+ * --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Postsubmit`
+ * ```
+ *
+ * To run only the flaky assertions add: `--
+ *
+ * ```
+ * --module-arg FlickerTests:include-annotation:androidx.test.filters.FlakyTest`
+ * ```
+ *
+ * Notes:
+ * ```
+ * 1. Some default assertions (e.g., nav bar, status bar and screen covered)
+ * are inherited [RotationTransition]
+ * 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
+ * ```
+ */
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class ChangeAppRotationTest(flicker: LegacyFlickerTest) : RotationTransition(flicker) {
+ override val testApp = SimpleAppHelper(instrumentation)
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
+ setup { testApp.launchViaIntent(wmHelper) }
+ }
+
+ /**
+ * Windows maybe recreated when rotated. Checks that the focus does not change or if it does,
+ * focus returns to [testApp]
+ */
+ @Presubmit
+ @Test
+ fun focusChanges() {
+ flicker.assertEventLog { this.focusChanges(testApp.packageName) }
+ }
+
+ /**
+ * Checks that the [ComponentNameMatcher.ROTATION] layer appears during the transition, doesn't
+ * flicker, and disappears before the transition is complete
+ */
+ fun rotationLayerAppearsAndVanishesAssertion() {
+ flicker.assertLayers {
+ this.isVisible(testApp)
+ .then()
+ .isVisible(ComponentNameMatcher.ROTATION)
+ .then()
+ .isVisible(testApp)
+ .isInvisible(ComponentNameMatcher.ROTATION)
+ }
+ }
+
+ /**
+ * Checks that the [ComponentNameMatcher.ROTATION] layer appears during the transition, doesn't
+ * flicker, and disappears before the transition is complete
+ */
+ @Presubmit
+ @Test
+ fun rotationLayerAppearsAndVanishes() {
+ rotationLayerAppearsAndVanishesAssertion()
+ }
+
+ @Test
+ @PlatinumTest(focusArea = "framework")
+ override fun cujCompleted() {
+ super.cujCompleted()
+ focusChanges()
+ rotationLayerAppearsAndVanishes()
+ }
+
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [LegacyFlickerTestFactory.rotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams() = LegacyFlickerTestFactory.rotationTests()
+ }
+}
diff --git a/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/RotationTransition.kt b/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
new file mode 100644
index 0000000..b0ca4d2
--- /dev/null
+++ b/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
@@ -0,0 +1,82 @@
+/*
+ * 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.rotation
+
+import android.platform.test.annotations.Presubmit
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import com.android.server.wm.flicker.BaseTest
+import com.android.server.wm.flicker.helpers.setRotation
+import org.junit.Test
+
+/** Base class for app rotation tests */
+abstract class RotationTransition(flicker: LegacyFlickerTest) : BaseTest(flicker) {
+ protected abstract val testApp: StandardAppHelper
+
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit = {
+ setup { this.setRotation(flicker.scenario.startRotation) }
+ teardown { testApp.exit(wmHelper) }
+ transitions { this.setRotation(flicker.scenario.endRotation) }
+ }
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ flicker.assertLayers {
+ this.visibleLayersShownMoreThanOneConsecutiveEntry(
+ ignoreLayers =
+ listOf(
+ ComponentNameMatcher.SPLASH_SCREEN,
+ ComponentNameMatcher.SNAPSHOT,
+ ComponentNameMatcher("", "SecondaryHomeHandle")
+ )
+ )
+ }
+ }
+
+ /** Checks that [testApp] layer covers the entire screen at the start of the transition */
+ @Presubmit
+ @Test
+ open fun appLayerRotates_StartingPos() {
+ flicker.assertLayersStart {
+ this.entry.displays.map { display ->
+ this.visibleRegion(testApp).coversExactly(display.layerStackSpace)
+ }
+ }
+ }
+
+ /** Checks that [testApp] layer covers the entire screen at the end of the transition */
+ @Presubmit
+ @Test
+ open fun appLayerRotates_EndingPos() {
+ flicker.assertLayersEnd {
+ this.entry.displays.map { display ->
+ this.visibleRegion(testApp).coversExactly(display.layerStackSpace)
+ }
+ }
+ }
+
+ override fun cujCompleted() {
+ super.cujCompleted()
+ appLayerRotates_StartingPos()
+ appLayerRotates_EndingPos()
+ }
+}
diff --git a/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
new file mode 100644
index 0000000..6d3ae43
--- /dev/null
+++ b/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -0,0 +1,284 @@
+/*
+ * 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.server.wm.flicker.rotation
+
+import android.platform.test.annotations.PlatinumTest
+import android.platform.test.annotations.Presubmit
+import android.tools.common.ScenarioBuilder
+import android.tools.common.ScenarioImpl
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import android.view.WindowManager
+import com.android.server.wm.flicker.helpers.SeamlessRotationAppHelper
+import com.android.server.wm.flicker.testapp.ActivityOptions
+import org.junit.FixMethodOrder
+import org.junit.Ignore
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test opening an app and cycling through app rotations using seamless rotations
+ *
+ * Currently, runs:
+ * ```
+ * 0 -> 90 degrees
+ * 0 -> 90 degrees (with starved UI thread)
+ * 90 -> 0 degrees
+ * 90 -> 0 degrees (with starved UI thread)
+ * ```
+ *
+ * Actions:
+ * ```
+ * Launch an app in fullscreen and supporting seamless rotation (via intent)
+ * Set initial device orientation
+ * Start tracing
+ * Change device orientation
+ * Stop tracing
+ * ```
+ *
+ * To run this test: `atest FlickerTests:SeamlessAppRotationTest`
+ *
+ * To run only the presubmit assertions add: `--
+ *
+ * ```
+ * --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
+ * --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Presubmit`
+ * ```
+ *
+ * To run only the postsubmit assertions add: `--
+ *
+ * ```
+ * --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
+ * --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Postsubmit`
+ * ```
+ *
+ * To run only the flaky assertions add: `--
+ *
+ * ```
+ * --module-arg FlickerTests:include-annotation:androidx.test.filters.FlakyTest`
+ * ```
+ *
+ * Notes:
+ * ```
+ * 1. Some default assertions (e.g., nav bar, status bar and screen covered)
+ * are inherited [RotationTransition]
+ * 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
+ * ```
+ */
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class SeamlessAppRotationTest(flicker: LegacyFlickerTest) : RotationTransition(flicker) {
+ override val testApp = SeamlessRotationAppHelper(instrumentation)
+
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
+ setup {
+ testApp.launchViaIntent(
+ wmHelper,
+ stringExtras =
+ mapOf(
+ ActivityOptions.SeamlessRotation.EXTRA_STARVE_UI_THREAD to
+ flicker.starveUiThread.toString()
+ )
+ )
+ }
+ }
+
+ /** Checks that [testApp] window is always in full screen */
+ @Presubmit
+ @Test
+ fun appWindowFullScreen() {
+ flicker.assertWm {
+ this.invoke("isFullScreen") {
+ val appWindow =
+ it.windowState(testApp.packageName)
+ ?: error("App window for package ${testApp.packageName} not found")
+ val flags = appWindow.windowState.attributes.flags
+ appWindow
+ .check { "isFullScreen" }
+ .that(flags.and(WindowManager.LayoutParams.FLAG_FULLSCREEN))
+ .isGreater(0)
+ }
+ }
+ }
+
+ /** Checks that [testApp] window is always with seamless rotation */
+ @Presubmit
+ @Test
+ fun appWindowSeamlessRotation() {
+ flicker.assertWm {
+ this.invoke("isRotationSeamless") {
+ val appWindow =
+ it.windowState(testApp.packageName)
+ ?: error("App window for package ${testApp.packageName} not found")
+ val rotationAnimation = appWindow.windowState.attributes.rotationAnimation
+ appWindow
+ .check { "isRotationSeamless" }
+ .that(
+ rotationAnimation.and(
+ WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS
+ )
+ )
+ .isGreater(0)
+ }
+ }
+ }
+
+ /** Checks that [testApp] window is always visible */
+ @Presubmit
+ @Test
+ fun appLayerAlwaysVisible() {
+ flicker.assertLayers { isVisible(testApp) }
+ }
+
+ /** Checks that [testApp] layer covers the entire screen during the whole transition */
+ @Presubmit
+ @Test
+ fun appLayerRotates() {
+ flicker.assertLayers {
+ this.invoke("entireScreenCovered") { entry ->
+ entry.entry.displays.map { display ->
+ entry.visibleRegion(testApp).coversExactly(display.layerStackSpace)
+ }
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Not applicable to this CUJ. App is full screen")
+ override fun statusBarLayerPositionAtStartAndEnd() {}
+
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Not applicable to this CUJ. App is full screen")
+ override fun statusBarLayerIsVisibleAtStartAndEnd() {}
+
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Not applicable to this CUJ. App is full screen")
+ override fun statusBarWindowIsAlwaysVisible() {}
+
+ /**
+ * Checks that the [ComponentNameMatcher.STATUS_BAR] window is invisible during the whole
+ * transition
+ */
+ @Presubmit
+ @Test
+ fun statusBarWindowIsAlwaysInvisible() {
+ flicker.assertWm { this.isAboveAppWindowInvisible(ComponentNameMatcher.STATUS_BAR) }
+ }
+
+ /**
+ * Checks that the [ComponentNameMatcher.STATUS_BAR] layer is invisible during the whole
+ * transition
+ */
+ @Presubmit
+ @Test
+ fun statusBarLayerIsAlwaysInvisible() {
+ flicker.assertLayers { this.isInvisible(ComponentNameMatcher.STATUS_BAR) }
+ }
+
+ /** Checks that the focus doesn't change during animation */
+ @Presubmit
+ @Test
+ fun focusDoesNotChange() {
+ flicker.assertEventLog { this.focusDoesNotChange() }
+ }
+
+ @Test
+ @PlatinumTest(focusArea = "framework")
+ override fun cujCompleted() {
+ appWindowFullScreen()
+ appWindowSeamlessRotation()
+ focusDoesNotChange()
+ statusBarLayerIsAlwaysInvisible()
+ statusBarWindowIsAlwaysInvisible()
+ appLayerRotates_StartingPos()
+ appLayerRotates_EndingPos()
+ entireScreenCovered()
+ visibleLayersShownMoreThanOneConsecutiveEntry()
+ visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ runAndIgnoreAssumptionViolation { appLayerRotates() }
+ runAndIgnoreAssumptionViolation { appLayerAlwaysVisible() }
+ runAndIgnoreAssumptionViolation { navBarLayerIsVisibleAtStartAndEnd() }
+ runAndIgnoreAssumptionViolation { navBarWindowIsAlwaysVisible() }
+ runAndIgnoreAssumptionViolation { navBarLayerPositionAtStartAndEnd() }
+ runAndIgnoreAssumptionViolation { taskBarLayerIsVisibleAtStartAndEnd() }
+ runAndIgnoreAssumptionViolation { taskBarWindowIsAlwaysVisible() }
+ }
+
+ companion object {
+ private val LegacyFlickerTest.starveUiThread
+ get() =
+ scenario.getConfigValue<Boolean>(
+ ActivityOptions.SeamlessRotation.EXTRA_STARVE_UI_THREAD
+ )
+ ?: false
+
+ @JvmStatic
+ protected fun createConfig(
+ sourceConfig: LegacyFlickerTest,
+ starveUiThread: Boolean
+ ): LegacyFlickerTest {
+ val originalScenario = sourceConfig.initialize("createConfig") as ScenarioImpl
+ val nameExt = if (starveUiThread) "_BUSY_UI_THREAD" else ""
+ val newConfig =
+ ScenarioBuilder()
+ .forClass(originalScenario.testClass)
+ .withStartRotation(originalScenario.startRotation)
+ .withEndRotation(originalScenario.endRotation)
+ .withNavBarMode(originalScenario.navBarMode)
+ .withExtraConfigs(originalScenario.extraConfig)
+ .withDescriptionOverride(originalScenario.description)
+ .withExtraConfig(
+ ActivityOptions.SeamlessRotation.EXTRA_STARVE_UI_THREAD,
+ starveUiThread
+ )
+ .withDescriptionOverride("${originalScenario.description}$nameExt")
+ return LegacyFlickerTest(newConfig)
+ }
+
+ /**
+ * Creates the test configurations for seamless rotation based on the default rotation tests
+ * from [LegacyFlickerTestFactory.rotationTests], but adding a flag (
+ * [ActivityOptions.SeamlessRotation.EXTRA_STARVE_UI_THREAD]) to indicate if the app should
+ * starve the UI thread of not
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams() =
+ LegacyFlickerTestFactory.rotationTests().flatMap { sourceCfg ->
+ val legacyCfg = sourceCfg as LegacyFlickerTest
+ val defaultRun = createConfig(legacyCfg, starveUiThread = false)
+ val busyUiRun = createConfig(legacyCfg, starveUiThread = true)
+ listOf(defaultRun, busyUiRun)
+ }
+ }
+}
diff --git a/tests/FlickerTests/Rotation/trace_config/trace_config.textproto b/tests/FlickerTests/Rotation/trace_config/trace_config.textproto
new file mode 100644
index 0000000..c9a35ac
--- /dev/null
+++ b/tests/FlickerTests/Rotation/trace_config/trace_config.textproto
@@ -0,0 +1,77 @@
+# Copyright (C) 2023 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.
+
+# proto-message: TraceConfig
+
+# Enable periodic flushing of the trace buffer into the output file.
+write_into_file: true
+
+# Writes the userspace buffer into the file every 1s.
+file_write_period_ms: 2500
+
+# See b/126487238 - we need to guarantee ordering of events.
+flush_period_ms: 30000
+
+# The trace buffers needs to be big enough to hold |file_write_period_ms| of
+# trace data. The trace buffer sizing depends on the number of trace categories
+# enabled and the device activity.
+
+# RSS events
+buffers: {
+ size_kb: 63488
+ fill_policy: RING_BUFFER
+}
+
+data_sources {
+ config {
+ name: "linux.process_stats"
+ target_buffer: 0
+ # polled per-process memory counters and process/thread names.
+ # If you don't want the polled counters, remove the "process_stats_config"
+ # section, but keep the data source itself as it still provides on-demand
+ # thread/process naming for ftrace data below.
+ process_stats_config {
+ scan_all_processes_on_start: true
+ }
+ }
+}
+
+data_sources: {
+ config {
+ name: "linux.ftrace"
+ ftrace_config {
+ ftrace_events: "ftrace/print"
+ ftrace_events: "task/task_newtask"
+ ftrace_events: "task/task_rename"
+ atrace_categories: "ss"
+ atrace_categories: "wm"
+ atrace_categories: "am"
+ atrace_categories: "aidl"
+ atrace_categories: "input"
+ atrace_categories: "binder_driver"
+ atrace_categories: "sched_process_exit"
+ atrace_apps: "com.android.server.wm.flicker"
+ atrace_apps: "com.android.server.wm.flicker.other"
+ atrace_apps: "com.android.server.wm.flicker.close"
+ atrace_apps: "com.android.server.wm.flicker.ime"
+ atrace_apps: "com.android.server.wm.flicker.launch"
+ atrace_apps: "com.android.server.wm.flicker.quickswitch"
+ atrace_apps: "com.android.server.wm.flicker.rotation"
+ atrace_apps: "com.android.server.wm.flicker.testapp"
+ atrace_apps: "com.android.systemui"
+ atrace_apps: "com.google.android.apps.nexuslauncher"
+ }
+ }
+}
+