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

Commit b67f386c authored by Matt Sziklay's avatar Matt Sziklay Committed by Automerger Merge Worker
Browse files

Merge "Move common TaskPositioner test logic into tests for utility class."...

Merge "Move common TaskPositioner test logic into tests for utility class." into udc-dev am: 2ca3fbb0 am: d0d1cdd1 am: f7db1240

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/22862736



Change-Id: I6d0f4dd14e42cda2f155b9f1fc8bcdddd295909c
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents 5604e6ac f7db1240
Loading
Loading
Loading
Loading
+232 −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.windowdecor

import android.app.ActivityManager
import android.graphics.PointF
import android.graphics.Rect
import android.os.IBinder
import android.testing.AndroidTestingRunner
import android.view.Display
import android.window.WindowContainerToken
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayLayout
import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_BOTTOM
import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_RIGHT
import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_TOP
import com.google.common.truth.Truth.assertThat
import junit.framework.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.`when` as whenever
import org.mockito.Mockito.any
import org.mockito.MockitoAnnotations

/**
 * Tests for [DragPositioningCallbackUtility].
 *
 * Build/Install/Run:
 * atest WMShellUnitTests:DragPositioningCallbackUtilityTest
 */
@RunWith(AndroidTestingRunner::class)
class DragPositioningCallbackUtilityTest {
    @Mock
    private lateinit var mockWindowDecoration: WindowDecoration<*>
    @Mock
    private lateinit var taskToken: WindowContainerToken
    @Mock
    private lateinit var taskBinder: IBinder
    @Mock
    private lateinit var mockDisplayController: DisplayController
    @Mock
    private lateinit var mockDisplayLayout: DisplayLayout
    @Mock
    private lateinit var mockDisplay: Display

    @Before
    fun setup() {
        MockitoAnnotations.initMocks(this)

        whenever(taskToken.asBinder()).thenReturn(taskBinder)
        whenever(mockDisplayController.getDisplayLayout(DISPLAY_ID)).thenReturn(mockDisplayLayout)
        whenever(mockDisplayLayout.densityDpi()).thenReturn(DENSITY_DPI)
        whenever(mockDisplayLayout.getStableBounds(any())).thenAnswer { i ->
            (i.arguments.first() as Rect).set(STABLE_BOUNDS)
        }

        mockWindowDecoration.mTaskInfo = ActivityManager.RunningTaskInfo().apply {
            taskId = TASK_ID
            token = taskToken
            minWidth = MIN_WIDTH
            minHeight = MIN_HEIGHT
            defaultMinSize = DEFAULT_MIN
            displayId = DISPLAY_ID
            configuration.windowConfiguration.bounds = STARTING_BOUNDS
        }
        mockWindowDecoration.mDisplay = mockDisplay
        whenever(mockDisplay.displayId).thenAnswer { DISPLAY_ID }
    }

    @Test
    fun testChangeBoundsDoesNotChangeHeightWhenLessThanMin() {
        val startingPoint = PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.top.toFloat())
        val repositionTaskBounds = Rect()

        // Resize to width of 95px and height of 5px with min width of 10px
        val newX = STARTING_BOUNDS.right.toFloat() - 5
        val newY = STARTING_BOUNDS.top.toFloat() + 95
        val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)

        DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_TOP,
            false /* hasMoved */, repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta,
            mockDisplayController, mockWindowDecoration)

        assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left)
        assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top)
        assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right - 5)
        assertThat(repositionTaskBounds.bottom).isEqualTo(STARTING_BOUNDS.bottom)
    }

    @Test
    fun testChangeBoundsDoesNotChangeWidthWhenLessThanMin() {
        val startingPoint = PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.top.toFloat())
        val repositionTaskBounds = Rect()

        // Resize to height of 95px and width of 5px with min width of 10px
        val newX = STARTING_BOUNDS.right.toFloat() - 95
        val newY = STARTING_BOUNDS.top.toFloat() + 5
        val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)

        DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_TOP,
            false /* hasMoved */, repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta,
            mockDisplayController, mockWindowDecoration)

        assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left)
        assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top + 5)
        assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right)
        assertThat(repositionTaskBounds.bottom).isEqualTo(STARTING_BOUNDS.bottom)
    }

    @Test
    fun testChangeBoundsDoesNotChangeHeightWhenNegative() {
        val startingPoint = PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.top.toFloat())
        val repositionTaskBounds = Rect()

        // Resize to width of 95px and width of -5px with minimum of 10px
        val newX = STARTING_BOUNDS.right.toFloat() - 5
        val newY = STARTING_BOUNDS.top.toFloat() + 105
        val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)

        DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_TOP,
            false /* hasMoved */, repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta,
            mockDisplayController, mockWindowDecoration)

        assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left)
        assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top)
        assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right - 5)
        assertThat(repositionTaskBounds.bottom).isEqualTo(STARTING_BOUNDS.bottom)
    }

    @Test
    fun testChangeBoundsRunsWhenResizeBoundsValid() {
        val startingPoint = PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.top.toFloat())
        val repositionTaskBounds = Rect()

        // Shrink to height 20px and width 20px with both min height/width equal to 10px
        val newX = STARTING_BOUNDS.right.toFloat() - 80
        val newY = STARTING_BOUNDS.top.toFloat() + 80
        val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)

        DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_TOP,
                false /* hasMoved */, repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta,
                mockDisplayController, mockWindowDecoration)
        assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left)
        assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top + 80)
        assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right - 80)
        assertThat(repositionTaskBounds.bottom).isEqualTo(STARTING_BOUNDS.bottom)
    }

    @Test
    fun testChangeBoundsDoesNotRunWithNegativeHeightAndWidth() {
        val startingPoint = PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.top.toFloat())
        val repositionTaskBounds = Rect()
        // Shrink to height -5px and width -5px with both min height/width equal to 10px
        val newX = STARTING_BOUNDS.right.toFloat() - 105
        val newY = STARTING_BOUNDS.top.toFloat() + 105

        val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)

        DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_TOP,
            false /* hasMoved */, repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta,
            mockDisplayController, mockWindowDecoration)
        assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left)
        assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top)
        assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right)
        assertThat(repositionTaskBounds.bottom).isEqualTo(STARTING_BOUNDS.bottom)
    }

    @Test
    fun testChangeBounds_toDisallowedBounds_freezesAtLimit() {
        var hasMoved = false
        val startingPoint = PointF(STARTING_BOUNDS.right.toFloat(),
            STARTING_BOUNDS.bottom.toFloat())
        val repositionTaskBounds = Rect()
        // Initial resize to width and height 110px.
        var newX = STARTING_BOUNDS.right.toFloat() + 10
        var newY = STARTING_BOUNDS.bottom.toFloat() + 10
        var delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)
        assertTrue(DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM,
            hasMoved, repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta,
            mockDisplayController, mockWindowDecoration))
        hasMoved = true
        // Resize width to 120px, height to disallowed area which should not result in a change.
        newX += 10
        newY = DISALLOWED_RESIZE_AREA.top.toFloat()
        delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)
        assertTrue(DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM,
            hasMoved, repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta,
            mockDisplayController, mockWindowDecoration))
        assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left)
        assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top)
        assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right + 20)
        assertThat(repositionTaskBounds.bottom).isEqualTo(STARTING_BOUNDS.bottom + 10)
    }

    companion object {
        private const val TASK_ID = 5
        private const val MIN_WIDTH = 10
        private const val MIN_HEIGHT = 10
        private const val DENSITY_DPI = 20
        private const val DEFAULT_MIN = 40
        private const val DISPLAY_ID = 1
        private const val NAVBAR_HEIGHT = 50
        private val DISPLAY_BOUNDS = Rect(0, 0, 2400, 1600)
        private val STARTING_BOUNDS = Rect(0, 0, 100, 100)
        private val DISALLOWED_RESIZE_AREA = Rect(
            DISPLAY_BOUNDS.left,
            DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT,
            DISPLAY_BOUNDS.right,
            DISPLAY_BOUNDS.bottom)
        private val STABLE_BOUNDS = Rect(
            DISPLAY_BOUNDS.left,
            DISPLAY_BOUNDS.top,
            DISPLAY_BOUNDS.right,
            DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT
        )
    }
}
 No newline at end of file
+6 −294
Original line number Diff line number Diff line
@@ -14,7 +14,6 @@ import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayLayout
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_BOTTOM
import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_RIGHT
import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_TOP
import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_UNDEFINED
@@ -22,7 +21,7 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.Mockito.`when` as whenever
import org.mockito.Mockito.any
import org.mockito.Mockito.argThat
import org.mockito.Mockito.never
@@ -72,10 +71,10 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
                mockDragStartListener
            )

        `when`(taskToken.asBinder()).thenReturn(taskBinder)
        `when`(mockDisplayController.getDisplayLayout(DISPLAY_ID)).thenReturn(mockDisplayLayout)
        `when`(mockDisplayLayout.densityDpi()).thenReturn(DENSITY_DPI)
        `when`(mockDisplayLayout.getStableBounds(any())).thenAnswer { i ->
        whenever(taskToken.asBinder()).thenReturn(taskBinder)
        whenever(mockDisplayController.getDisplayLayout(DISPLAY_ID)).thenReturn(mockDisplayLayout)
        whenever(mockDisplayLayout.densityDpi()).thenReturn(DENSITY_DPI)
        whenever(mockDisplayLayout.getStableBounds(any())).thenAnswer { i ->
            (i.arguments.first() as Rect).set(STABLE_BOUNDS)
        }

@@ -89,7 +88,7 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
            configuration.windowConfiguration.bounds = STARTING_BOUNDS
        }
        mockWindowDecoration.mDisplay = mockDisplay
        `when`(mockDisplay.displayId).thenAnswer { DISPLAY_ID }
        whenever(mockDisplay.displayId).thenAnswer { DISPLAY_ID }
    }

    @Test
@@ -237,293 +236,6 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
        })
    }

    @Test
    fun testDragResize_resize_setBoundsDoesNotChangeHeightWhenLessThanMin() {
        taskPositioner.onDragPositioningStart(
                CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top
                STARTING_BOUNDS.right.toFloat(),
                STARTING_BOUNDS.top.toFloat()
        )

        // Resize to width of 95px and height of 5px with min width of 10px
        val newX = STARTING_BOUNDS.right.toFloat() - 5
        val newY = STARTING_BOUNDS.top.toFloat() + 95
        taskPositioner.onDragPositioningMove(
                newX,
                newY
        )

        taskPositioner.onDragPositioningEnd(newX, newY)

        verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
            return@argThat wct.changes.any { (token, change) ->
                token == taskBinder &&
                        ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS)
                                != 0) && change.configuration.windowConfiguration.bounds.top ==
                        STARTING_BOUNDS.top &&
                        change.configuration.windowConfiguration.bounds.bottom ==
                        STARTING_BOUNDS.bottom
            }
        })
    }

    @Test
    fun testDragResize_resize_setBoundsDoesNotChangeWidthWhenLessThanMin() {
        taskPositioner.onDragPositioningStart(
                CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top
                STARTING_BOUNDS.right.toFloat(),
                STARTING_BOUNDS.top.toFloat()
        )

        // Resize to height of 95px and width of 5px with min width of 10px
        val newX = STARTING_BOUNDS.right.toFloat() - 95
        val newY = STARTING_BOUNDS.top.toFloat() + 5
        taskPositioner.onDragPositioningMove(
                newX,
                newY
        )

        taskPositioner.onDragPositioningEnd(newX, newY)

        verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
            return@argThat wct.changes.any { (token, change) ->
                token == taskBinder &&
                        ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS)
                                != 0) && change.configuration.windowConfiguration.bounds.right ==
                        STARTING_BOUNDS.right &&
                        change.configuration.windowConfiguration.bounds.left ==
                        STARTING_BOUNDS.left
            }
        })
    }

    @Test
    fun testDragResize_resize_setBoundsDoesNotChangeHeightWhenNegative() {
        taskPositioner.onDragPositioningStart(
                CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top
                STARTING_BOUNDS.right.toFloat(),
                STARTING_BOUNDS.top.toFloat()
        )

        // Resize to height of -5px and width of 95px
        val newX = STARTING_BOUNDS.right.toFloat() - 5
        val newY = STARTING_BOUNDS.top.toFloat() + 105
        taskPositioner.onDragPositioningMove(
                newX,
                newY
        )

        taskPositioner.onDragPositioningEnd(newX, newY)

        verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
            return@argThat wct.changes.any { (token, change) ->
                token == taskBinder &&
                        ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS)
                                != 0) && change.configuration.windowConfiguration.bounds.top ==
                        STARTING_BOUNDS.top &&
                        change.configuration.windowConfiguration.bounds.bottom ==
                        STARTING_BOUNDS.bottom
            }
        })
    }

    @Test
    fun testDragResize_resize_setBoundsDoesNotChangeWidthWhenNegative() {
        taskPositioner.onDragPositioningStart(
                CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top
                STARTING_BOUNDS.right.toFloat(),
                STARTING_BOUNDS.top.toFloat()
        )

        // Resize to width of -5px and height of 95px
        val newX = STARTING_BOUNDS.right.toFloat() - 105
        val newY = STARTING_BOUNDS.top.toFloat() + 5
        taskPositioner.onDragPositioningMove(
                newX,
                newY
        )

        taskPositioner.onDragPositioningEnd(newX, newY)

        verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
            return@argThat wct.changes.any { (token, change) ->
                token == taskBinder &&
                        ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS)
                                != 0) && change.configuration.windowConfiguration.bounds.right ==
                        STARTING_BOUNDS.right &&
                        change.configuration.windowConfiguration.bounds.left ==
                        STARTING_BOUNDS.left
            }
        })
    }

    @Test
    fun testDragResize_resize_setBoundsRunsWhenResizeBoundsValid() {
        taskPositioner.onDragPositioningStart(
                CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top
                STARTING_BOUNDS.right.toFloat(),
                STARTING_BOUNDS.top.toFloat()
        )

        // Shrink to height 20px and width 20px with both min height/width equal to 10px
        val newX = STARTING_BOUNDS.right.toFloat() - 80
        val newY = STARTING_BOUNDS.top.toFloat() + 80
        taskPositioner.onDragPositioningMove(
                newX,
                newY
        )

        taskPositioner.onDragPositioningEnd(newX, newY)

        verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
            return@argThat wct.changes.any { (token, change) ->
                token == taskBinder &&
                        ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0)
            }
        })
    }

    @Test
    fun testDragResize_resize_setBoundsDoesNotRunWithNegativeHeightAndWidth() {
        taskPositioner.onDragPositioningStart(
                CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top
                STARTING_BOUNDS.right.toFloat(),
                STARTING_BOUNDS.top.toFloat()
        )

        // Shrink to height 5px and width 5px with both min height/width equal to 10px
        val newX = STARTING_BOUNDS.right.toFloat() - 95
        val newY = STARTING_BOUNDS.top.toFloat() + 95
        taskPositioner.onDragPositioningMove(
                newX,
                newY
        )

        taskPositioner.onDragPositioningEnd(newX, newY)

        verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct ->
            return@argThat wct.changes.any { (token, change) ->
                token == taskBinder &&
                        ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0)
            }
        })
    }

    @Test
    fun testDragResize_resize_useDefaultMinWhenMinWidthInvalid() {
        mockWindowDecoration.mTaskInfo.minWidth = -1

        taskPositioner.onDragPositioningStart(
                CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top
                STARTING_BOUNDS.right.toFloat(),
                STARTING_BOUNDS.top.toFloat()
        )

        // Shrink to width and height of 3px with invalid minWidth = -1 and defaultMinSize = 5px
        val newX = STARTING_BOUNDS.right.toFloat() - 97
        val newY = STARTING_BOUNDS.top.toFloat() + 97
        taskPositioner.onDragPositioningMove(
                newX,
                newY
        )

        taskPositioner.onDragPositioningEnd(newX, newY)

        verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct ->
            return@argThat wct.changes.any { (token, change) ->
                token == taskBinder &&
                        ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0)
            }
        })
    }

    @Test
    fun testDragResize_resize_useMinWidthWhenValid() {
        taskPositioner.onDragPositioningStart(
                CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top
                STARTING_BOUNDS.right.toFloat(),
                STARTING_BOUNDS.top.toFloat()
        )

        // Shrink to width and height of 7px with valid minWidth = 10px and defaultMinSize = 5px
        val newX = STARTING_BOUNDS.right.toFloat() - 93
        val newY = STARTING_BOUNDS.top.toFloat() + 93
        taskPositioner.onDragPositioningMove(
                newX,
                newY
        )

        taskPositioner.onDragPositioningEnd(newX, newY)

        verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct ->
            return@argThat wct.changes.any { (token, change) ->
                token == taskBinder &&
                        ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0)
            }
        })
    }

    fun testDragResize_toDisallowedBounds_freezesAtLimit() {
        taskPositioner.onDragPositioningStart(
                CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM, // Resize right-bottom corner
                STARTING_BOUNDS.right.toFloat(),
                STARTING_BOUNDS.bottom.toFloat()
        )

        // Resize the task by 10px to the right and bottom, a valid destination
        val newBounds = Rect(
                STARTING_BOUNDS.left,
                STARTING_BOUNDS.top,
                STARTING_BOUNDS.right + 10,
                STARTING_BOUNDS.bottom + 10)
        taskPositioner.onDragPositioningMove(
                newBounds.right.toFloat(),
                newBounds.bottom.toFloat()
        )

        // Resize the task by another 10px to the right (allowed) and to just in the disallowed
        // area of the Y coordinate.
        val newBounds2 = Rect(
                newBounds.left,
                newBounds.top,
                newBounds.right + 10,
                DISALLOWED_RESIZE_AREA.top
        )
        taskPositioner.onDragPositioningMove(
                newBounds2.right.toFloat(),
                newBounds2.bottom.toFloat()
        )

        taskPositioner.onDragPositioningEnd(newBounds2.right.toFloat(), newBounds2.bottom.toFloat())

        // The first resize falls in the allowed area, verify there's a change for it.
        verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
            return@argThat wct.changes.any { (token, change) ->
                token == taskBinder && change.ofBounds(newBounds)
            }
        })
        // The second resize falls in the disallowed area, verify there's no change for it.
        verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct ->
            return@argThat wct.changes.any { (token, change) ->
                token == taskBinder && change.ofBounds(newBounds2)
            }
        })
        // Instead, there should be a change for its allowed portion (the X movement) with the Y
        // staying frozen in the last valid resize position.
        verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
            return@argThat wct.changes.any { (token, change) ->
                token == taskBinder && change.ofBounds(
                        Rect(
                                newBounds2.left,
                                newBounds2.top,
                                newBounds2.right,
                                newBounds.bottom // Stayed at the first resize destination.
                        )
                )
            }
        })
    }

    private fun WindowContainerTransaction.Change.ofBounds(bounds: Rect): Boolean {
        return ((windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0) &&
                bounds == configuration.windowConfiguration.bounds
+6 −6

File changed.

Preview size limit exceeded, changes collapsed.