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

Commit 3cd8a7bb authored by Sergey Pinkevich's avatar Sergey Pinkevich
Browse files

E2E scenario for tab tearing in Desktop Windowing mode

Bug: 376389593
Flag: EXEMPT flag is not needed for E2E test
Test: atest PlatformScenarioTests:TabTearingDragAndDropTest

Change-Id: Id0d1fa5d7d9a830c55c053d4541646cdef7c74e0
parent a9ee1862
Loading
Loading
Loading
Loading
+29 −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.functional

import android.platform.test.annotations.Postsubmit
import android.platform.test.rule.ScreenRecordRule
import com.android.wm.shell.scenarios.TabTearingDragAndDrop
import org.junit.runner.RunWith
import org.junit.runners.BlockJUnit4ClassRunner

/* Functional test for [TabTearingDragAndDrop]. */
@RunWith(BlockJUnit4ClassRunner::class)
@Postsubmit
@ScreenRecordRule.ScreenRecord
class TabTearingDragAndDropTest : TabTearingDragAndDrop()
 No newline at end of file
+84 −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.scenarios

import android.app.Instrumentation
import android.tools.NavBar
import android.tools.Rotation
import android.tools.device.apphelpers.BrowserAppHelper
import android.tools.flicker.rules.ChangeDisplayOrientationRule
import android.tools.traces.parsers.WindowManagerStateHelper
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
import com.android.window.flags.Flags
import com.android.wm.shell.Utils
import org.junit.After
import org.junit.Assume
import org.junit.Before
import org.junit.Ignore
import org.junit.Rule
import org.junit.Test

@Ignore("Test Base Class")
abstract class TabTearingDragAndDrop(val rotation: Rotation = Rotation.ROTATION_0) :
    TestScenarioBase() {

    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
    private val tapl = LauncherInstrumentation()
    private val wmHelper = WindowManagerStateHelper(instrumentation)
    private val device = UiDevice.getInstance(instrumentation)
    private val browserAppHelper = BrowserAppHelper(instrumentation)
    private val browserDesktopAppHelper = DesktopModeAppHelper(browserAppHelper)

    @Rule
    @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)

    @Before
    fun setup() {
        Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
        tapl.apply {
            setEnableRotation(true)
            setExpectedRotation(rotation.value)
            enableTransientTaskbar(false)
        }
        ChangeDisplayOrientationRule.setRotation(rotation)
        browserDesktopAppHelper.enterDesktopMode(wmHelper, device)
    }

    @Test
    open fun tearTab() {
        browserAppHelper.openThreeDotsMenu()
        browserAppHelper.clickNewTabInMenu()
        browserDesktopAppHelper.dragWindowTopLeftCorner(
            device,
            wmHelper,
            DesktopModeAppHelper.WindowDraggingDirection.CENTER
        )
        browserAppHelper.performTabTearing(
            wmHelper,
            BrowserAppHelper.Companion.TabDraggingDirection.TOP_LEFT
        )
        wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
    }

    @After
    fun teardown() {
        browserDesktopAppHelper.exit(wmHelper)
    }
}
 No newline at end of file
+49 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.server.wm.flicker.helpers
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.content.Context
import android.graphics.Insets
import android.graphics.Point
import android.graphics.Rect
import android.graphics.Region
import android.os.SystemClock
@@ -417,6 +418,50 @@ open class DesktopModeAppHelper(private val innerHelper: IStandardAppHelper) :
            .waitForAndVerify()
    }

    /**
     * Simulates dragging the top-left corner of the current window to the center of the device
     * screen.
     *
     * This function calculates a starting point slightly offset from the actual top-left corner (60
     * pixels down and right to prevent app resizing) and drags it to the center of the default
     * display.
     *
     * @param device The UiDevice instance used to perform the drag interaction on the device.
     * @param wmHelper A helper class instance used to retrieve window and display dimension
     *   information.
     * @param direction A direction where the window should be dragged to.
     * @throws IllegalStateException if the default display information cannot be obtained from the
     *   wmHelper.
     */
    fun dragWindowTopLeftCorner(
        device: UiDevice,
        wmHelper: WindowManagerStateHelper,
        direction: WindowDraggingDirection = WindowDraggingDirection.CENTER,
    ) {
        val windowBounds = wmHelper.getWindowRegion(this).bounds
        val displayBounds = getDisplayRect(wmHelper)
        // We take start dragging point with some offset to use app moving instead of resizing.
        val startDraggingPoint =
            Point(/* x= */ windowBounds.left + 60, /* y= */ windowBounds.top + 60)
        val endDraggingPoint =
            when (direction) {
                WindowDraggingDirection.CENTER ->
                    Point(/* x= */ displayBounds.centerX(), /* y= */ displayBounds.centerY())
            }

        device.drag(
            startDraggingPoint.x,
            startDraggingPoint.y,
            endDraggingPoint.x,
            endDraggingPoint.y,
            /* steps= */ 100,
        )
        wmHelper
            .StateSyncBuilder()
            .withAppTransitionIdle()
            .waitForAndVerify()
    }

    /** Drag a window to a snap resize region, found at the left and right edges of the screen. */
    fun dragToSnapResizeRegion(
        wmHelper: WindowManagerStateHelper,
@@ -606,6 +651,10 @@ open class DesktopModeAppHelper(private val innerHelper: IStandardAppHelper) :
        MAXIMIZE_BUTTON_IN_MENU
    }

    enum class WindowDraggingDirection {
        CENTER
    }

    private companion object {
        val TIMEOUT: Duration = Duration.ofSeconds(3)
        const val SNAP_RESIZE_DRAG_INSET: Int = 5 // inset to avoid dragging to display edge