Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 64bd2e2c authored by Nataniel Borges's avatar Nataniel Borges Committed by Android (Google) Code Review
Browse files

Merge "Add WM Flicker Tests for App Compat" into udc-dev

parents fd6a8541 73ad9b81
Loading
Loading
Loading
Loading
+116 −0
Original line number Diff line number Diff line
/*
 * 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.
 */

package com.android.wm.shell.flicker.appcompat

import android.content.Context
import android.system.helpers.CommandsHelper
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
import com.android.wm.shell.flicker.BaseTest
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.LetterboxAppHelper
import android.tools.device.flicker.legacy.FlickerTestFactory
import android.tools.device.flicker.legacy.IFlickerTestData
import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
import com.android.wm.shell.flicker.appWindowIsVisibleAtStart
import org.junit.Assume
import org.junit.Before
import org.junit.runners.Parameterized

abstract class BaseAppCompat(flicker: FlickerTest) : BaseTest(flicker) {
    protected val context: Context = instrumentation.context
    protected val letterboxApp = LetterboxAppHelper(instrumentation)
    lateinit var cmdHelper: CommandsHelper
    lateinit var letterboxStyle: HashMap<String, String>

    /** {@inheritDoc} */
    override val transition: FlickerBuilder.() -> Unit
        get() = {
            setup {
                setStartRotation()
                letterboxApp.launchViaIntent(wmHelper)
                setEndRotation()
            }
        }

    @Before
    fun before() {
        cmdHelper = CommandsHelper.getInstance(instrumentation)
        Assume.assumeTrue(tapl.isTablet && isIgnoreOrientationRequest())
    }

    private fun mapLetterboxStyle(): HashMap<String, String> {
        val res = cmdHelper.executeShellCommand("wm get-letterbox-style")
        val lines = res.lines()
        val map = HashMap<String, String>()
        for (line in lines) {
            val keyValuePair = line.split(":")
            if (keyValuePair.size == 2) {
                val key = keyValuePair[0].trim()
                map[key] = keyValuePair[1].trim()
            }
        }
        return map
    }

    private fun isIgnoreOrientationRequest(): Boolean {
        val res = cmdHelper.executeShellCommand("wm get-ignore-orientation-request")
        return res != null && res.contains("true")
    }

    fun IFlickerTestData.setStartRotation() = setRotation(flicker.scenario.startRotation)

    fun IFlickerTestData.setEndRotation() = setRotation(flicker.scenario.endRotation)

    /** Checks that app entering letterboxed state have rounded corners */
    fun assertLetterboxAppAtStartHasRoundedCorners() {
        assumeLetterboxRoundedCornersEnabled()
        flicker.assertLayersStart { this.hasRoundedCorners(letterboxApp) }
    }

    fun assertLetterboxAppAtEndHasRoundedCorners() {
        assumeLetterboxRoundedCornersEnabled()
        flicker.assertLayersEnd { this.hasRoundedCorners(letterboxApp) }
    }

    /** Only run on tests with config_letterboxActivityCornersRadius != 0 in devices */
    private fun assumeLetterboxRoundedCornersEnabled() {
        if (!::letterboxStyle.isInitialized) {
            letterboxStyle = mapLetterboxStyle()
        }
        Assume.assumeTrue(letterboxStyle.getValue("Corner radius") != "0")
    }

    fun assertLetterboxAppVisibleAtStartAndEnd() {
        flicker.appWindowIsVisibleAtStart(letterboxApp)
        flicker.appWindowIsVisibleAtEnd(letterboxApp)
    }

    companion object {
        /**
         * Creates the test configurations.
         *
         * See [FlickerTestFactory.rotationTests] for configuring screen orientation and
         * navigation modes.
         */
        @Parameterized.Parameters(name = "{0}")
        @JvmStatic
        fun getParams(): Collection<FlickerTest> {
            return FlickerTestFactory.rotationTests()
        }
    }
}
+89 −0
Original line number Diff line number Diff line
/*
 * 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.
 */

package com.android.wm.shell.flicker.appcompat

import android.platform.test.annotations.Postsubmit
import androidx.test.filters.RequiresDevice
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.common.datatypes.component.ComponentNameMatcher
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized

/**
 * Test launching app in size compat mode.
 *
 * To run this test: `atest WMShellFlickerTests:OpenAppInSizeCompatModeTest`
 *
 * Actions:
 * ```
 *     Rotate non resizable portrait only app to opposite orientation to trigger size compat mode
 * ```
 * Notes:
 * ```
 *     Some default assertions (e.g., nav bar, status bar and screen covered)
 *     are inherited [BaseTest]
 * ```
 */

@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
class OpenAppInSizeCompatModeTest(flicker: FlickerTest) : BaseAppCompat(flicker) {

    /** {@inheritDoc} */
    override val transition: FlickerBuilder.() -> Unit
        get() = {
            setup {
                setStartRotation()
                letterboxApp.launchViaIntent(wmHelper)
            }
            transitions { setEndRotation() }
            teardown { letterboxApp.exit(wmHelper) }
        }

    /**
     * Windows maybe recreated when rotated. Checks that the focus does not change or if it does,
     * focus returns to [letterboxApp]
     */
    @Postsubmit
    @Test
    fun letterboxAppFocusedAtEnd() = flicker.assertEventLog { focusChanges(letterboxApp.`package`) }

    @Postsubmit
    @Test
    fun letterboxedAppHasRoundedCorners() = assertLetterboxAppAtEndHasRoundedCorners()

    /**
     * Checks that the [ComponentNameMatcher.ROTATION] layer appears during the transition, doesn't
     * flicker, and disappears before the transition is complete
     */
    @Postsubmit
    @Test
    fun rotationLayerAppearsAndVanishes() {
        flicker.assertLayers {
            this.isVisible(letterboxApp)
                .then()
                .isVisible(ComponentNameMatcher.ROTATION)
                .then()
                .isVisible(letterboxApp)
                .isInvisible(ComponentNameMatcher.ROTATION)
        }
    }
}
 No newline at end of file
+86 −0
Original line number Diff line number Diff line
/*
 * 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.
 */

package com.android.wm.shell.flicker.appcompat

import android.platform.test.annotations.Postsubmit
import androidx.test.filters.RequiresDevice
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.helpers.WindowUtils
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized

/**
 * Test restarting app in size compat mode.
 *
 * To run this test: `atest WMShellFlickerTests:RestartAppInSizeCompatModeTest`
 *
 * Actions:
 * ```
 *     Rotate app to opposite orientation to trigger size compat mode
 *     Press restart button and wait for letterboxed app to resize
 * ```
 * Notes:
 * ```
 *     Some default assertions (e.g., nav bar, status bar and screen covered)
 *     are inherited [BaseTest]
 * ```
 */

@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
class RestartAppInSizeCompatModeTest(flicker: FlickerTest) : BaseAppCompat(flicker) {

    /** {@inheritDoc} */
    override val transition: FlickerBuilder.() -> Unit
        get() = {
            super.transition(this)
            transitions { letterboxApp.clickRestart(wmHelper) }
            teardown { letterboxApp.exit(wmHelper) }
        }

    @Postsubmit
    @Test
    fun appVisibleAtStartAndEnd() = assertLetterboxAppVisibleAtStartAndEnd()

    @Postsubmit
    @Test
    fun appLayerVisibilityChanges() {
        flicker.assertLayers {
            this.isVisible(letterboxApp)
                .then()
                .isInvisible(letterboxApp)
                .then()
                .isVisible(letterboxApp)
        }
    }

    @Postsubmit
    @Test
    fun letterboxedAppHasRoundedCorners() = assertLetterboxAppAtStartHasRoundedCorners()

    /** Checks that the visible region of [letterboxApp] is still within display bounds */
    @Postsubmit
    @Test
    fun appWindowRemainInsideVisibleBounds() {
        val displayBounds = WindowUtils.getDisplayBounds(flicker.scenario.endRotation)
        flicker.assertLayersEnd { visibleRegion(letterboxApp).coversAtMost(displayBounds) }
    }
}
 No newline at end of file
+51 −0
Original line number Diff line number Diff line
/*
 * 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.
 */

package com.android.server.wm.flicker.helpers

import android.app.Instrumentation
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.testapp.ActivityOptions
import android.tools.common.datatypes.component.ComponentNameMatcher
import android.tools.device.traces.parsers.toFlickerComponent
import android.tools.device.traces.parsers.WindowManagerStateHelper
import android.tools.device.apphelpers.StandardAppHelper
import android.tools.device.helpers.FIND_TIMEOUT
import android.tools.device.helpers.SYSTEMUI_PACKAGE

class LetterboxAppHelper
@JvmOverloads
constructor(
    instr: Instrumentation,
    launcherName: String = ActivityOptions.NonResizeablePortraitActivity.LABEL,
    component: ComponentNameMatcher =
        ActivityOptions.NonResizeablePortraitActivity.COMPONENT.toFlickerComponent()
) : StandardAppHelper(instr, launcherName, component) {

    fun clickRestart(wmHelper: WindowManagerStateHelper) {
        val restartButton = uiDevice.wait(Until.findObject(By.res(
            SYSTEMUI_PACKAGE, "size_compat_restart_button")), FIND_TIMEOUT)
        restartButton?.run { restartButton.click() } ?: error("Restart button not found")

        // size compat mode restart confirmation dialog button
        val restartDialogButton = uiDevice.wait(Until.findObject(By.res(
            SYSTEMUI_PACKAGE, "letterbox_restart_dialog_restart_button")), FIND_TIMEOUT)
        restartDialogButton?.run { restartDialogButton.click() }
            ?: error("Restart dialog button not found")
        wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
    }
}
+12 −0
Original line number Diff line number Diff line
@@ -88,6 +88,18 @@
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <activity android:name=".NonResizeablePortraitActivity"
                  android:theme="@style/CutoutNever"
                  android:resizeableActivity="false"
                  android:screenOrientation="portrait"
                  android:taskAffinity="com.android.server.wm.flicker.testapp.NonResizeablePortraitActivity"
                  android:label="NonResizeablePortraitActivity"
                  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=".LaunchNewActivity"
                  android:taskAffinity="com.android.server.wm.flicker.testapp.LaunchNewActivity"
                  android:theme="@style/CutoutShortEdges"
Loading