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

Commit b84494ce authored by Mateusz Cicheński's avatar Mateusz Cicheński
Browse files

Add Maps entering PiP flicker test

Not all assertions will work initially, e.g. nav bar visibility changes
as the car mode overlay from Assistant is being rendered.

Also there are possible dialogs showing up when one opens Maps that
needs to be dismissed, asking for consulation in b/297893526.

Marking all tests that potentially fail as @Postsubmit too.

Bug: 282758103
Test: atest WMShellFlickerTestsPip:MapsEnterPipTest

Change-Id: I4d71264666eddc4318e06db6459b0caafc01e603
parent b0c3a9b2
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -57,6 +57,14 @@
        <option name="test-file-name" value="{MODULE}.apk"/>
        <option name="test-file-name" value="FlickerTestApp.apk"/>
    </target_preparer>
    <!-- Enable mocking GPS location by the test app -->
    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
        <option name="run-command"
                value="appops set com.android.wm.shell.flicker.pip android:mock_location allow"/>
        <option name="teardown-command"
                value="appops set com.android.wm.shell.flicker.pip android:mock_location deny"/>
    </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">
+3 −0
Original line number Diff line number Diff line
@@ -17,6 +17,9 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.android.wm.shell.flicker.pip">

    <!-- Enable mocking GPS location -->
    <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/>

    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
                     android:targetPackage="com.android.wm.shell.flicker.pip"
                     android:label="WindowManager Flicker Tests">
+233 −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.pip

import android.platform.test.annotations.Postsubmit
import android.tools.common.Rotation
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.apphelpers.StandardAppHelper
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import org.junit.Test
import org.junit.runners.Parameterized

abstract class AppsEnterPipTransition(flicker: LegacyFlickerTest) : EnterPipTransition(flicker) {
    protected abstract val standardAppHelper: StandardAppHelper

    /** Checks [standardAppHelper] window remains visible throughout the animation */
    @Postsubmit
    @Test
    override fun pipAppWindowAlwaysVisible() {
        flicker.assertWm { this.isAppWindowVisible(standardAppHelper) }
    }

    /** Checks [standardAppHelper] layer remains visible throughout the animation */
    @Postsubmit
    @Test
    override fun pipAppLayerAlwaysVisible() {
        flicker.assertLayers { this.isVisible(standardAppHelper) }
    }

    /** Checks the content overlay appears then disappears during the animation */
    @Postsubmit
    @Test
    override fun pipOverlayLayerAppearThenDisappear() {
        super.pipOverlayLayerAppearThenDisappear()
    }

    /**
     * Checks that [standardAppHelper] window remains inside the display bounds throughout the whole
     * animation
     */
    @Postsubmit
    @Test
    override fun pipWindowRemainInsideVisibleBounds() {
        flicker.assertWmVisibleRegion(standardAppHelper) { coversAtMost(displayBounds) }
    }

    /**
     * Checks that the [standardAppHelper] layer remains inside the display bounds throughout the
     * whole animation
     */
    @Postsubmit
    @Test
    override fun pipLayerOrOverlayRemainInsideVisibleBounds() {
        flicker.assertLayersVisibleRegion(
            standardAppHelper.or(ComponentNameMatcher.PIP_CONTENT_OVERLAY)
        ) {
            coversAtMost(displayBounds)
        }
    }

    /** Checks that the visible region of [standardAppHelper] always reduces during the animation */
    @Postsubmit
    @Test
    override fun pipLayerReduces() {
        flicker.assertLayers {
            val pipLayerList = this.layers {
                standardAppHelper.layerMatchesAnyOf(it) && it.isVisible
            }
            pipLayerList.zipWithNext { previous, current ->
                current.visibleRegion.notBiggerThan(previous.visibleRegion.region)
            }
        }
    }

    /** Checks that [standardAppHelper] window becomes pinned */
    @Postsubmit
    @Test
    override fun pipWindowBecomesPinned() {
        flicker.assertWm {
            invoke("pipWindowIsNotPinned") { it.isNotPinned(standardAppHelper) }
                .then()
                .invoke("pipWindowIsPinned") { it.isPinned(standardAppHelper) }
        }
    }

    /** Checks [ComponentNameMatcher.LAUNCHER] layer remains visible throughout the animation */
    @Postsubmit
    @Test
    override fun launcherLayerBecomesVisible() {
        super.launcherLayerBecomesVisible()
    }

    /**
     * Checks that the focus changes between the [standardAppHelper] window and the launcher when
     * closing the pip window
     */
    @Postsubmit
    @Test
    override fun focusChanges() {
        flicker.assertEventLog {
            this.focusChanges(standardAppHelper.packageName, "NexusLauncherActivity")
        }
    }

    @Postsubmit
    @Test
    override fun hasAtMostOnePipDismissOverlayWindow() = super.hasAtMostOnePipDismissOverlayWindow()

    // ICommonAssertions.kt overrides due to Morris overlay

    /**
     * Checks that the [ComponentNameMatcher.NAV_BAR] layer is visible during the whole transition
     */
    @Postsubmit
    @Test
    override fun navBarLayerIsVisibleAtStartAndEnd() {
        // this fails due to Morris overlay
    }

    /**
     * Checks the position of the [ComponentNameMatcher.NAV_BAR] at the start and end of the
     * transition
     */
    @Postsubmit
    @Test
    override fun navBarLayerPositionAtStartAndEnd() {
        // this fails due to Morris overlay
    }

    /**
     * Checks that the [ComponentNameMatcher.NAV_BAR] window is visible during the whole transition
     *
     * Note: Phones only
     */
    @Postsubmit
    @Test
    override fun navBarWindowIsAlwaysVisible() {
        // this fails due to Morris overlay
    }

    /**
     * Checks that the [ComponentNameMatcher.TASK_BAR] layer is visible during the whole transition
     */
    @Postsubmit
    @Test
    override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()

    /**
     * Checks that the [ComponentNameMatcher.TASK_BAR] window is visible during the whole transition
     *
     * Note: Large screen only
     */
    @Postsubmit
    @Test
    override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible()

    /**
     * Checks that the [ComponentNameMatcher.STATUS_BAR] layer is visible during the whole
     * transition
     */
    @Postsubmit
    @Test
    override fun statusBarLayerIsVisibleAtStartAndEnd() =
        super.statusBarLayerIsVisibleAtStartAndEnd()

    /**
     * Checks the position of the [ComponentNameMatcher.STATUS_BAR] at the start and end of the
     * transition
     */
    @Postsubmit
    @Test
    override fun statusBarLayerPositionAtStartAndEnd() = super.statusBarLayerPositionAtStartAndEnd()

    /**
     * Checks that the [ComponentNameMatcher.STATUS_BAR] window is visible during the whole
     * transition
     */
    @Postsubmit
    @Test override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()

    /**
     * Checks that all layers that are visible on the trace, are visible for at least 2 consecutive
     * entries.
     */
    @Postsubmit
    @Test
    override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
        super.visibleLayersShownMoreThanOneConsecutiveEntry()

    /**
     * Checks that all windows that are visible on the trace, are visible for at least 2 consecutive
     * entries.
     */
    @Postsubmit
    @Test
    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
        super.visibleWindowsShownMoreThanOneConsecutiveEntry()

    /** Checks that all parts of the screen are covered during the transition */
    @Postsubmit
    @Test
    override fun entireScreenCovered() = super.entireScreenCovered()

    companion object {
        /**
         * Creates the test configurations.
         *
         * See [LegacyFlickerTestFactory.nonRotationTests] for configuring repetitions, screen
         * orientation and navigation modes.
         */
        @Parameterized.Parameters(name = "{0}")
        @JvmStatic
        fun getParams() =
            LegacyFlickerTestFactory.nonRotationTests(
                supportedRotations = listOf(Rotation.ROTATION_0)
            )
    }
}
+2 −2
Original line number Diff line number Diff line
@@ -91,7 +91,7 @@ abstract class EnterPipTransition(flicker: LegacyFlickerTest) : PipTransition(fl
    /** Checks that [pipApp] window becomes pinned */
    @Presubmit
    @Test
    fun pipWindowBecomesPinned() {
    open fun pipWindowBecomesPinned() {
        flicker.assertWm {
            invoke("pipWindowIsNotPinned") { it.isNotPinned(pipApp) }
                .then()
@@ -102,7 +102,7 @@ abstract class EnterPipTransition(flicker: LegacyFlickerTest) : PipTransition(fl
    /** Checks [ComponentNameMatcher.LAUNCHER] layer remains visible throughout the animation */
    @Presubmit
    @Test
    fun launcherLayerBecomesVisible() {
    open fun launcherLayerBecomesVisible() {
        flicker.assertLayers {
            isInvisible(ComponentNameMatcher.LAUNCHER)
                .then()
+132 −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.pip

import android.os.Handler
import android.os.Looper
import android.os.SystemClock
import android.content.Context
import android.location.Criteria
import android.location.Location
import android.location.LocationManager
import android.tools.device.apphelpers.MapsAppHelper
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import androidx.test.filters.RequiresDevice
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized

/**
 * Test entering pip from Maps app by interacting with the app UI
 *
 * To run this test: `atest WMShellFlickerTests:MapsEnterPipTest`
 *
 * Actions:
 * ```
 *     Launch Maps and start navigation mode
 *     Go home to enter PiP
 * ```
 *
 * 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
 *        [android.tools.device.flicker.legacy.runner.TransitionRunner],
 *        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)
open class MapsEnterPipTest(flicker: LegacyFlickerTest) : AppsEnterPipTransition(flicker) {
    override val standardAppHelper: MapsAppHelper = MapsAppHelper(instrumentation)

    val locationManager: LocationManager =
        instrumentation.context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
    val mainHandler = Handler(Looper.getMainLooper())
    var mockLocationEnabled = false

    val updateLocation = object : Runnable {
        override fun run() {
            // early bail out if mocking location is not enabled
            if (!mockLocationEnabled) return
            val location = Location("Googleplex")
            location.latitude = 37.42243438411294
            location.longitude = -122.08426281892311
            location.time = System.currentTimeMillis()
            location.elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos()
            location.accuracy = 100f
            locationManager.setTestProviderLocation(LocationManager.GPS_PROVIDER, location)
            mainHandler.postDelayed(this, 5)
        }
    }

    override val defaultEnterPip: FlickerBuilder.() -> Unit = {
        setup {
            locationManager.addTestProvider(
                LocationManager.GPS_PROVIDER,
                false,
                false,
                false,
                false,
                false,
                false,
                false,
                Criteria.POWER_LOW,
                Criteria.ACCURACY_FINE
            )
            locationManager.setTestProviderEnabled(LocationManager.GPS_PROVIDER, true)
            mockLocationEnabled = true
            mainHandler.post(updateLocation)

            // normal app open through the Launcher All Apps
            // var mapsAddressOption = "Golden Gate Bridge"
            // standardAppHelper.open()
            // standardAppHelper.doSearch(mapsAddressOption)
            // standardAppHelper.getDirections()
            // standardAppHelper.startNavigation();

            standardAppHelper.launchViaIntent(
                wmHelper,
                MapsAppHelper.getMapIntent(MapsAppHelper.INTENT_NAVIGATION)
            )

            standardAppHelper.waitForNavigationToStart()
        }
    }

    override val defaultTeardown: FlickerBuilder.() -> Unit = {
        teardown {
            standardAppHelper.exit(wmHelper)
            mainHandler.removeCallbacks(updateLocation)
            // the main looper callback might have tried to provide a new location after the
            // provider is no longer in test mode, causing a crash, this prevents it from happening
            mockLocationEnabled = false
            locationManager.removeTestProvider(LocationManager.GPS_PROVIDER)
        }
    }

    override val thisTransition: FlickerBuilder.() -> Unit = {
        transitions { tapl.goHome() }
    }
}