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

Commit 0e2e99ac authored by Charles Chen's avatar Charles Chen
Browse files

Verify if bubble app is touchable

Test: atest ScrollableBubbleAppTest
Bug: 396020056
Flag: TEST_ONLY
Change-Id: Ia86377c87618ff32cfed6094be22694ec926c4bd
parent de566aa6
Loading
Loading
Loading
Loading
+5 −65
Original line number Diff line number Diff line
@@ -17,14 +17,13 @@
package com.android.wm.shell.flicker.bubbles

import android.platform.test.annotations.Presubmit
import androidx.test.filters.RequiresDevice
import android.platform.test.annotations.RequiresFlagsEnabled
import android.tools.NavBar
import android.tools.traces.component.ComponentNameMatcher.Companion.BUBBLE
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.Flags
import com.android.wm.shell.Utils
import com.android.wm.shell.flicker.bubbles.testcase.BubbleAppBecomesNotExpandedTestCases
import com.android.wm.shell.flicker.bubbles.testcase.DismissExpandedBubbleTestCases
import com.android.wm.shell.flicker.bubbles.utils.ApplyPerParameterRule
import com.android.wm.shell.flicker.bubbles.utils.FlickerPropertyInitializer
import com.android.wm.shell.flicker.bubbles.utils.RecordTraceWithTransitionRule
@@ -55,18 +54,15 @@ import org.junit.runners.MethodSorters
 * ```
 * Verified tests:
 * - [BubbleFlickerTestBase]
 * - [BubbleAppBecomesNotExpandedTestCases]
 * - [BUBBLE] is visible and then disappear
 * - [DismissExpandedBubbleTestCases]
 */
@FlakyTest(bugId = 427850786)
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_CREATE_ANY_BUBBLE)
@RequiresDevice
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Presubmit
class DismissExpandedBubbleViaBubbleViewTest(navBar: NavBar) :
    BubbleFlickerTestBase(),
    BubbleAppBecomesNotExpandedTestCases
{
class DismissExpandedBubbleViaBubbleViewTest(navBar: NavBar) : BubbleFlickerTestBase(),
    DismissExpandedBubbleTestCases {
    companion object : FlickerPropertyInitializer() {
        private val recordTraceWithTransitionRule = RecordTraceWithTransitionRule(
            setUpBeforeTransition = {
@@ -94,62 +90,6 @@ class DismissExpandedBubbleViaBubbleViewTest(navBar: NavBar) :
        super.setUp()
    }

// region Bubble stack related tests

    /**
     * Verifies [BUBBLE] window is gone at the end of the transition.
     */
    @Test
    fun bubbleWindowIsGoneAtEnd() {
        wmStateSubjectAtEnd.notContains(BUBBLE)
    }

    /**
     * Verifies [BUBBLE] layer is gone at the end of the transition.
     */
    @Test
    fun bubbleLayerIsGoneAtEnd() {
        layerTraceEntrySubjectAtEnd.notContains(BUBBLE)
    }

    /**
     * Verifies [BUBBLE] window was visible then disappear.
     */
    @Test
    fun bubbleWindowWasVisibleThenDisappear() {
        wmTraceSubject
            .isAboveAppWindowVisible(BUBBLE)
            .then()
            // Use #isNonAppWindowInvisible here because the BUBBLE window may have been removed
            // from WM hierarchy.
            .isNonAppWindowInvisible(BUBBLE)
            .forAllEntries()
    }

    /**
     * Verifies [BUBBLE] layer was visible then disappear.
     */
    @Test
    fun bubbleLayerWasVisibleThenDisappear() {
        layersTraceSubject
            .isVisible(BUBBLE)
            .then()
            .isInvisible(BUBBLE)
            .forAllEntries()
    }

// endregion

// region bubble app related tests

    /**
     * Verifies bubble app window is gone at the end of the transition.
     */
    @Test
    fun appWindowIsGoneAtEnd() {
        wmStateSubjectAtEnd.notContains(testApp)
    }

    @FlakyTest(bugId = 396020056)
    @Test
    override fun appLayerBecomesInvisible() {
+96 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.bubbles

import android.platform.test.annotations.Presubmit
import android.platform.test.annotations.RequiresFlagsEnabled
import android.tools.NavBar
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.helpers.ScrollToFinishHelper
import com.android.wm.shell.Flags
import com.android.wm.shell.Utils
import com.android.wm.shell.flicker.bubbles.testcase.DismissExpandedBubbleTestCases
import com.android.wm.shell.flicker.bubbles.utils.ApplyPerParameterRule
import com.android.wm.shell.flicker.bubbles.utils.FlickerPropertyInitializer
import com.android.wm.shell.flicker.bubbles.utils.RecordTraceWithTransitionRule
import com.android.wm.shell.flicker.bubbles.utils.launchBubbleViaBubbleMenu
import com.android.wm.shell.flicker.bubbles.utils.waitAndVerifyBubbleGone
import org.junit.FixMethodOrder
import org.junit.Rule
import org.junit.runners.MethodSorters

/**
 * Verifies that the content within a bubble can be scrolled and that its UI elements can be clicked.
 *
 * This test validates user interaction by launching a bubble that displays an activity with a
 * [android.widget.ScrollView]. The scrollable content is intentionally long enough to not be
 * fully visible, with a "finish" button located at the very bottom.
 *
 * The test succeeds if it can programmatically scroll to the button and click it, which in
 * turn dismisses the bubble app. This confirms that both scrolling and touch events are
 * processed correctly within the bubble app.
 *
 * To run this test: `atest WMShellExplicitFlickerTestsBubbles:ScrollableBubbleAppTest`
 *
 * Pre-steps:
 * ```
 *     Launch [testApp] into bubble
 * ```
 *
 * Actions:
 * ```
 *     Scroll the [testApp] until find the finish button
 *     Click the finish button to finish the bubble app
 * ```
 * Verified tests:
 * - [BubbleFlickerTestBase]
 * - [DismissExpandedBubbleTestCases]
 */
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_CREATE_ANY_BUBBLE)
@RequiresDevice
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Presubmit
class ScrollableBubbleAppTest(navBar: NavBar) : BubbleFlickerTestBase(),
    DismissExpandedBubbleTestCases {

    companion object : FlickerPropertyInitializer() {
        private val recordTraceWithTransitionRule = RecordTraceWithTransitionRule(
            setUpBeforeTransition = { launchBubbleViaBubbleMenu(testApp, tapl, wmHelper) },
            transition = {
                testApp.scrollToFinish()
                waitAndVerifyBubbleGone(wmHelper)
            },
            tearDownAfterTransition = { testApp.exit() }
        )

        override val testApp
            get() = ScrollToFinishHelper(instrumentation)
    }

    @get:Rule
    val setUpRule = ApplyPerParameterRule(
        Utils.testSetupRule(navBar).around(recordTraceWithTransitionRule),
        params = arrayOf(navBar)
    )

    // This is necessary or the test will use the testApp from BubbleFlickerTestBase.
    override val testApp
        get() = ScrollableBubbleAppTest.testApp

    override val traceDataReader
        get() = recordTraceWithTransitionRule.reader
}
 No newline at end of file
+86 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.bubbles.testcase

import android.tools.traces.component.ComponentNameMatcher.Companion.BUBBLE
import org.junit.Test

/**
 * Verifies [testApp] is dismissed, and bubble window (represented as expanded bubble or bubble bar)
 * is also dismissed.
 *
 * - [BubbleAppBecomesNotExpandedTestCases]
 * - [BUBBLE] is visible and then disappear
 */
interface DismissExpandedBubbleTestCases : BubbleAppBecomesNotExpandedTestCases {

// region Bubble related tests

    /**
     * Verifies [BUBBLE] window is gone at the end of the transition.
     */
    @Test
    fun bubbleWindowIsGoneAtEnd() {
        wmStateSubjectAtEnd.notContains(BUBBLE)
    }

    /**
     * Verifies [BUBBLE] layer is gone at the end of the transition.
     */
    @Test
    fun bubbleLayerIsGoneAtEnd() {
        layerTraceEntrySubjectAtEnd.notContains(BUBBLE)
    }

    /**
     * Verifies [BUBBLE] window was visible then disappear.
     */
    @Test
    fun bubbleWindowWasVisibleThenDisappear() {
        wmTraceSubject
            .isAboveAppWindowVisible(BUBBLE)
            .then()
            // Use #isNonAppWindowInvisible here because the BUBBLE window may have been removed
            // from WM hierarchy.
            .isNonAppWindowInvisible(BUBBLE)
            .forAllEntries()
    }

    /**
     * Verifies [BUBBLE] layer was visible then disappear.
     */
    @Test
    fun bubbleLayerWasVisibleThenDisappear() {
        layersTraceSubject
            .isVisible(BUBBLE)
            .then()
            .isInvisible(BUBBLE)
            .forAllEntries()
    }

// endregion

// region bubble app related tests

    /**
     * Verifies bubble app window is gone at the end of the transition.
     */
    @Test
    fun appWindowIsGoneAtEnd() {
        wmStateSubjectAtEnd.notContains(testApp)
    }
}
 No newline at end of file
+5 −1
Original line number Diff line number Diff line
@@ -209,8 +209,12 @@ fun dismissBubbleAppViaBubbleView(uiDevice: UiDevice, wmHelper: WindowManagerSta
    uiDevice.bubbleIcon?.run {
        drag(Point(uiDevice.displayWidth / 2, uiDevice.displayHeight), 1000)
    }
}

    // Wait for bubble gone.
/**
 * Waits and verifies the bubble (represented as bubble icon or bubble bar) is gone.
 */
fun waitAndVerifyBubbleGone(wmHelper: WindowManagerStateHelper) {
    wmHelper
        .StateSyncBuilder()
        .add(ConditionsFactory.isWMStateComplete())
+59 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.platform.uiautomatorhelpers.scrollUntilFound
import android.tools.device.apphelpers.StandardAppHelper
import android.tools.helpers.FIND_TIMEOUT
import android.tools.traces.component.ComponentNameMatcher
import android.tools.traces.parsers.toFlickerComponent
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.testapp.ActivityOptions

/**
 * The helper to interact with `ScrollToFinishActivity`
 */
class ScrollToFinishHelper
@JvmOverloads
constructor(
    instr: Instrumentation,
    launcherName: String = ActivityOptions.ScrollToFinish.LABEL,
    component: ComponentNameMatcher = ActivityOptions.ScrollToFinish.COMPONENT.toFlickerComponent()
) : StandardAppHelper(instr, launcherName, component) {

    /**
     * Scrolls until the finish button is found and clicks the button.
     */
    fun scrollToFinish() {
        val rootActivityLayout = uiDevice.wait(
            Until.findObject(By.res(packageName, RES_ID_ROOT_ACTIVITY_LAYOUT)),
            FIND_TIMEOUT
        ) ?: error("Unable to find $RES_ID_ROOT_ACTIVITY_LAYOUT.")

        val finishButton = rootActivityLayout.scrollUntilFound(
            By.res(packageName, RES_ID_FINISH_BUTTON)
        ) ?: error("Unable to find $RES_ID_FINISH_BUTTON")
        finishButton.click()
    }

    companion object {
        private const val RES_ID_ROOT_ACTIVITY_LAYOUT = "root_activity_layout"
        private const val RES_ID_FINISH_BUTTON = "finish_button"
    }
}
 No newline at end of file
Loading