diff options
Diffstat (limited to 'quickstep/tests')
35 files changed, 2642 insertions, 502 deletions
diff --git a/quickstep/tests/OWNERS b/quickstep/tests/OWNERS index 02e8ebcaba..c271803f21 100644 --- a/quickstep/tests/OWNERS +++ b/quickstep/tests/OWNERS @@ -2,3 +2,4 @@ vadimt@google.com sunnygoyal@google.com winsonc@google.com hyunyoungs@google.com +mateuszc@google.com diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/FallbackTaskbarUIControllerTest.kt b/quickstep/tests/src/com/android/launcher3/taskbar/FallbackTaskbarUIControllerTest.kt index 8c13fe3f1e..f3115c6a52 100644 --- a/quickstep/tests/src/com/android/launcher3/taskbar/FallbackTaskbarUIControllerTest.kt +++ b/quickstep/tests/src/com/android/launcher3/taskbar/FallbackTaskbarUIControllerTest.kt @@ -24,11 +24,11 @@ import com.android.quickstep.fallback.RecentsState import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.ArgumentCaptor -import org.mockito.Mock -import org.mockito.Mockito.times -import org.mockito.Mockito.verify -import org.mockito.Mockito.`when` as whenever +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.mock +import org.mockito.kotlin.times +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever @RunWith(AndroidJUnit4::class) class FallbackTaskbarUIControllerTest : TaskbarBaseTestCase() { @@ -36,8 +36,8 @@ class FallbackTaskbarUIControllerTest : TaskbarBaseTestCase() { lateinit var fallbackTaskbarUIController: FallbackTaskbarUIController lateinit var stateListener: StateManager.StateListener<RecentsState> - @Mock lateinit var recentsActivity: RecentsActivity - @Mock lateinit var stateManager: StateManager<RecentsState> + private val recentsActivity: RecentsActivity = mock() + private val stateManager: StateManager<RecentsState> = mock() @Before override fun setup() { @@ -46,10 +46,10 @@ class FallbackTaskbarUIControllerTest : TaskbarBaseTestCase() { fallbackTaskbarUIController = FallbackTaskbarUIController(recentsActivity) // Capture registered state listener to send events to in our tests - val captor = ArgumentCaptor.forClass(StateManager.StateListener::class.java) + val captor = argumentCaptor<StateManager.StateListener<RecentsState>>() fallbackTaskbarUIController.init(taskbarControllers) verify(stateManager).addStateListener(captor.capture()) - stateListener = captor.value as StateManager.StateListener<RecentsState> + stateListener = captor.lastValue } @Test diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarBaseTestCase.kt b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarBaseTestCase.kt index 2c16c15caf..15b1e532bd 100644 --- a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarBaseTestCase.kt +++ b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarBaseTestCase.kt @@ -55,7 +55,7 @@ abstract class TaskbarBaseTestCase { @Mock lateinit var taskbarOverlayController: TaskbarOverlayController @Mock lateinit var taskbarEduTooltipController: TaskbarEduTooltipController @Mock lateinit var keyboardQuickSwitchController: KeyboardQuickSwitchController - @Mock lateinit var taskbarPinningController: TaskbarDividerPopupController + @Mock lateinit var taskbarPinningController: TaskbarPinningController @Mock lateinit var optionalBubbleControllers: Optional<BubbleControllers> lateinit var taskbarControllers: TaskbarControllers diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarHoverToolTipControllerTest.java b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarHoverToolTipControllerTest.java index 6c0d44da6c..9ed39066dd 100644 --- a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarHoverToolTipControllerTest.java +++ b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarHoverToolTipControllerTest.java @@ -18,7 +18,6 @@ package com.android.launcher3.taskbar; import static androidx.test.core.app.ApplicationProvider.getApplicationContext; import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS; -import static com.android.launcher3.taskbar.TaskbarHoverToolTipController.HOVER_TOOL_TIP_REVEAL_START_DELAY; import static com.google.common.truth.Truth.assertThat; @@ -27,7 +26,6 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -128,14 +126,11 @@ public class TaskbarHoverToolTipControllerTest extends TaskbarBaseTestCase { boolean hoverHandled = mTaskbarHoverToolTipController.onHover(mHoverBubbleTextView, mMotionEvent); - - // Verify fullscreen is not set until the delayed runnable to reveal the tooltip has run - verify(taskbarActivityContext, never()).setTaskbarWindowFullscreen(true); waitForIdleSync(); + assertThat(hoverHandled).isTrue(); verify(taskbarActivityContext).setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS, true); - verify(taskbarActivityContext).setTaskbarWindowFullscreen(true); } @Test @@ -159,14 +154,11 @@ public class TaskbarHoverToolTipControllerTest extends TaskbarBaseTestCase { boolean hoverHandled = mTaskbarHoverToolTipController.onHover(mHoverFolderIcon, mMotionEvent); - - // Verify fullscreen is not set until the delayed runnable to reveal the tooltip has run - verify(taskbarActivityContext, never()).setTaskbarWindowFullscreen(true); waitForIdleSync(); + assertThat(hoverHandled).isTrue(); verify(taskbarActivityContext).setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS, true); - verify(taskbarActivityContext).setTaskbarWindowFullscreen(true); } @Test @@ -222,7 +214,6 @@ public class TaskbarHoverToolTipControllerTest extends TaskbarBaseTestCase { } private void waitForIdleSync() { - mTestableLooper.moveTimeForward(HOVER_TOOL_TIP_REVEAL_START_DELAY + 1); mTestableLooper.processAllMessages(); } } diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarKeyguardControllerTest.kt b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarKeyguardControllerTest.kt index 148e36cde5..ed88c29b6a 100644 --- a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarKeyguardControllerTest.kt +++ b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarKeyguardControllerTest.kt @@ -23,17 +23,17 @@ import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_B import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED import org.junit.Before import org.junit.Test -import org.mockito.Mock -import org.mockito.Mockito.anyBoolean -import org.mockito.Mockito.never -import org.mockito.Mockito.times -import org.mockito.Mockito.verify -import org.mockito.Mockito.`when` as whenever +import org.mockito.kotlin.any +import org.mockito.kotlin.mock +import org.mockito.kotlin.never +import org.mockito.kotlin.times +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever class TaskbarKeyguardControllerTest : TaskbarBaseTestCase() { - @Mock lateinit var baseDragLayer: TaskbarDragLayer - @Mock lateinit var keyguardManager: KeyguardManager + private val baseDragLayer: TaskbarDragLayer = mock() + private val keyguardManager: KeyguardManager = mock() @Before override fun setup() { @@ -50,7 +50,7 @@ class TaskbarKeyguardControllerTest : TaskbarBaseTestCase() { @Test fun uninterestingFlags_noActions() { setFlags(0) - verify(navbarButtonsViewController, never()).setKeyguardVisible(anyBoolean(), anyBoolean()) + verify(navbarButtonsViewController, never()).setKeyguardVisible(any(), any()) } @Test diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactoryTest.kt b/quickstep/tests/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactoryTest.kt index 3920b08030..9c7f0144c1 100644 --- a/quickstep/tests/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactoryTest.kt +++ b/quickstep/tests/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactoryTest.kt @@ -12,35 +12,37 @@ import android.widget.LinearLayout import androidx.test.runner.AndroidJUnit4 import com.android.launcher3.DeviceProfile import com.android.launcher3.R +import com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NAVBAR_UNIFICATION import com.android.launcher3.taskbar.TaskbarManager +import com.android.systemui.shared.rotation.RotationButton import java.lang.IllegalStateException import org.junit.Assume.assumeTrue 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.MockitoAnnotations +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever @RunWith(AndroidJUnit4::class) class NavButtonLayoutFactoryTest { - @Mock lateinit var mockDeviceProfile: DeviceProfile - @Mock lateinit var mockParentButtonContainer: FrameLayout - @Mock lateinit var mockNavLayout: LinearLayout - @Mock lateinit var mockStartContextualLayout: ViewGroup - @Mock lateinit var mockEndContextualLayout: ViewGroup - @Mock lateinit var mockResources: Resources - @Mock lateinit var mockBackButton: ImageView - @Mock lateinit var mockRecentsButton: ImageView - @Mock lateinit var mockHomeButton: ImageView + private val mockDeviceProfile: DeviceProfile = mock() + private val mockParentButtonContainer: FrameLayout = mock() + private val mockNavLayout: LinearLayout = mock() + private val mockStartContextualLayout: ViewGroup = mock() + private val mockEndContextualLayout: ViewGroup = mock() + private val mockResources: Resources = mock() + private val mockBackButton: ImageView = mock() + private val mockRecentsButton: ImageView = mock() + private val mockHomeButton: ImageView = mock() + private val mockImeSwitcher: ImageView = mock() + private val mockRotationButton: RotationButton = mock() + private val mockA11yButton: ImageView = mock() private var surfaceRotation = Surface.ROTATION_0 @Before fun setup() { - MockitoAnnotations.initMocks(this) - // Init end nav buttons whenever(mockNavLayout.childCount).thenReturn(3) whenever(mockNavLayout.findViewById<View>(R.id.back)).thenReturn(mockBackButton) @@ -58,7 +60,7 @@ class NavButtonLayoutFactoryTest { @Test fun getKidsLayoutter() { - assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW) + assumeTrue(ENABLE_TASKBAR_NAVBAR_UNIFICATION) mockDeviceProfile.isTaskbarPresent = true val layoutter: NavButtonLayoutFactory.NavButtonLayoutter = getLayoutter( @@ -73,7 +75,7 @@ class NavButtonLayoutFactoryTest { @Test fun getSetupLayoutter() { - assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW) + assumeTrue(ENABLE_TASKBAR_NAVBAR_UNIFICATION) mockDeviceProfile.isTaskbarPresent = true val layoutter: NavButtonLayoutFactory.NavButtonLayoutter = getLayoutter( @@ -88,7 +90,7 @@ class NavButtonLayoutFactoryTest { @Test fun getTaskbarNavLayoutter() { - assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW) + assumeTrue(ENABLE_TASKBAR_NAVBAR_UNIFICATION) mockDeviceProfile.isTaskbarPresent = true val layoutter: NavButtonLayoutFactory.NavButtonLayoutter = getLayoutter( @@ -103,7 +105,7 @@ class NavButtonLayoutFactoryTest { @Test(expected = IllegalStateException::class) fun noValidLayoutForLargeScreenTaskbarNotPresent() { - assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW) + assumeTrue(ENABLE_TASKBAR_NAVBAR_UNIFICATION) mockDeviceProfile.isTaskbarPresent = false getLayoutter( isKidsMode = false, @@ -116,7 +118,7 @@ class NavButtonLayoutFactoryTest { @Test fun getTaskbarPortraitLayoutter() { - assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW) + assumeTrue(ENABLE_TASKBAR_NAVBAR_UNIFICATION) mockDeviceProfile.isTaskbarPresent = false val layoutter: NavButtonLayoutFactory.NavButtonLayoutter = getLayoutter( @@ -131,7 +133,7 @@ class NavButtonLayoutFactoryTest { @Test fun getTaskbarLandscapeLayoutter() { - assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW) + assumeTrue(ENABLE_TASKBAR_NAVBAR_UNIFICATION) mockDeviceProfile.isTaskbarPresent = false setDeviceProfileLandscape() val layoutter: NavButtonLayoutFactory.NavButtonLayoutter = @@ -147,23 +149,23 @@ class NavButtonLayoutFactoryTest { @Test fun getTaskbarSeascapeLayoutter() { - assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW) + assumeTrue(ENABLE_TASKBAR_NAVBAR_UNIFICATION) mockDeviceProfile.isTaskbarPresent = false setDeviceProfileLandscape() val layoutter: NavButtonLayoutFactory.NavButtonLayoutter = - getLayoutter( - isKidsMode = false, - isInSetup = false, - isThreeButtonNav = true, - phoneMode = true, - surfaceRotation = ROTATION_270 - ) + getLayoutter( + isKidsMode = false, + isInSetup = false, + isThreeButtonNav = true, + phoneMode = true, + surfaceRotation = ROTATION_270 + ) assert(layoutter is PhoneSeascapeNavLayoutter) } @Test(expected = IllegalStateException::class) fun noValidLayoutForPhoneGestureNav() { - assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW) + assumeTrue(ENABLE_TASKBAR_NAVBAR_UNIFICATION) mockDeviceProfile.isTaskbarPresent = false getLayoutter( isKidsMode = false, @@ -196,7 +198,10 @@ class NavButtonLayoutFactoryTest { isInSetup = isInSetup, isThreeButtonNav = isThreeButtonNav, phoneMode = phoneMode, - surfaceRotation = surfaceRotation + surfaceRotation = surfaceRotation, + imeSwitcher = mockImeSwitcher, + rotationButton = mockRotationButton, + a11yButton = mockA11yButton ) } } diff --git a/quickstep/tests/src/com/android/quickstep/AbstractTaplTestsTaskbar.java b/quickstep/tests/src/com/android/quickstep/AbstractTaplTestsTaskbar.java index 9c6c93d74a..fc757b44c9 100644 --- a/quickstep/tests/src/com/android/quickstep/AbstractTaplTestsTaskbar.java +++ b/quickstep/tests/src/com/android/quickstep/AbstractTaplTestsTaskbar.java @@ -25,7 +25,6 @@ import android.content.Intent; import com.android.launcher3.tapl.LauncherInstrumentation; import com.android.launcher3.tapl.Taskbar; import com.android.launcher3.ui.AbstractLauncherUiTest; -import com.android.launcher3.ui.TaplTestsLauncher3; import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.LauncherLayoutBuilder; import com.android.launcher3.util.TestUtil; @@ -37,7 +36,6 @@ import java.util.List; public class AbstractTaplTestsTaskbar extends AbstractQuickStepTest { - protected static final String TEST_APP_NAME = "LauncherTestApp"; protected static final String TEST_APP_PACKAGE = getInstrumentation().getContext().getPackageName(); protected static final String CALCULATOR_APP_PACKAGE = @@ -56,7 +54,7 @@ public class AbstractTaplTestsTaskbar extends AbstractQuickStepTest { "com.google.android.apps.nexuslauncher.tests", "com.android.launcher3.testcomponent.BaseTestingActivity"); mLauncherLayout = TestUtil.setLauncherDefaultLayout(mTargetContext, layoutBuilder); - TaplTestsLauncher3.initialize(this); + AbstractLauncherUiTest.initialize(this); startAppFast(CALCULATOR_APP_PACKAGE); mLauncher.enableBlockTimeout(true); mLauncher.showTaskbarIfHidden(); @@ -95,6 +93,6 @@ public class AbstractTaplTestsTaskbar extends AbstractQuickStepTest { launcher.enableTransientTaskbar(expectTransientTaskbar); launcher.recreateTaskbar(); launcher.checkForAnomaly(true, true); - AbstractLauncherUiTest.checkDetectedLeaks(launcher); + AbstractLauncherUiTest.checkDetectedLeaks(launcher, true); } } diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java index a67d787842..8d54dce8ef 100644 --- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java +++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java @@ -32,8 +32,6 @@ import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.rule.ShellCommandRule.disableHeadsUpNotification; import static com.android.launcher3.util.rule.ShellCommandRule.getLauncherCommand; -import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL; -import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -57,10 +55,12 @@ import com.android.launcher3.tapl.LauncherInstrumentation; import com.android.launcher3.tapl.OverviewTask; import com.android.launcher3.tapl.TestHelpers; import com.android.launcher3.testcomponent.TestCommandReceiver; +import com.android.launcher3.ui.AbstractLauncherUiTest; import com.android.launcher3.util.Wait; import com.android.launcher3.util.rule.FailureWatcher; import com.android.launcher3.util.rule.SamplerRule; import com.android.launcher3.util.rule.ScreenRecordRule; +import com.android.launcher3.util.rule.TestIsolationRule; import com.android.launcher3.util.rule.TestStabilityRule; import com.android.launcher3.util.rule.ViewCaptureRule; import com.android.quickstep.views.RecentsView; @@ -94,9 +94,6 @@ public class FallbackRecentsTest { public final TestRule mDisableHeadsUpNotification = disableHeadsUpNotification(); @Rule - public final TestRule mSetLauncherCommand; - - @Rule public final TestRule mOrderSensitiveRules; @Rule @@ -107,7 +104,7 @@ public class FallbackRecentsTest { Context context = instrumentation.getContext(); mDevice = UiDevice.getInstance(instrumentation); mDevice.setOrientationNatural(); - mLauncher = new LauncherInstrumentation(); + mLauncher = AbstractLauncherUiTest.createLauncherInstrumentation(); mLauncher.enableDebugTracing(); // b/143488140 //mLauncher.enableCheckEventsForSuccessfulGestures(); @@ -116,19 +113,7 @@ public class FallbackRecentsTest { Utilities.enableRunningInTestHarnessForTests(); } - final ViewCaptureRule viewCaptureRule = new ViewCaptureRule( - RecentsActivity.ACTIVITY_TRACKER::getCreatedActivity); - mOrderSensitiveRules = RuleChain - .outerRule(new SamplerRule()) - .around(new NavigationModeSwitchRule(mLauncher)) - .around(new FailureWatcher(mLauncher, viewCaptureRule::getViewCaptureData)) - .around(viewCaptureRule); - - mOtherLauncherActivity = context.getPackageManager().queryIntentActivities( - getHomeIntentInPackage(context), - MATCH_DISABLED_COMPONENTS).get(0).activityInfo; - - mSetLauncherCommand = (base, desc) -> new Statement() { + final TestRule setLauncherCommand = (base, desc) -> new Statement() { @Override public void evaluate() throws Throwable { TestCommandReceiver.callCommand(TestCommandReceiver.ENABLE_TEST_LAUNCHER); @@ -151,6 +136,22 @@ public class FallbackRecentsTest { } } }; + + final ViewCaptureRule viewCaptureRule = new ViewCaptureRule( + RecentsActivity.ACTIVITY_TRACKER::getCreatedActivity); + mOrderSensitiveRules = RuleChain + .outerRule(new SamplerRule()) + .around(new TestStabilityRule()) + .around(new NavigationModeSwitchRule(mLauncher)) + .around(new FailureWatcher(mLauncher, viewCaptureRule::getViewCaptureData)) + // .around(viewCaptureRule) b/315482167 + .around(new TestIsolationRule(mLauncher, false)) + .around(setLauncherCommand); + + mOtherLauncherActivity = context.getPackageManager().queryIntentActivities( + getHomeIntentInPackage(context), + MATCH_DISABLED_COMPONENTS).get(0).activityInfo; + if (TestHelpers.isInLauncherProcess()) { mLauncher.setSystemHealthSupplier(startTime -> TestCommandReceiver.callCommand( TestCommandReceiver.GET_SYSTEM_HEALTH_MESSAGE, startTime.toString()). @@ -161,13 +162,14 @@ public class FallbackRecentsTest { @Before public void setUp() { mLauncher.onTestStart(); + AbstractLauncherUiTest.onTestStart(); } @After public void tearDown() { try { // Limits UI tests affecting tests running after them. - AbstractQuickStepTest.checkDetectedLeaks(mLauncher); + AbstractQuickStepTest.checkDetectedLeaks(mLauncher, true); } finally { mLauncher.onTestFinish(); } @@ -175,7 +177,6 @@ public class FallbackRecentsTest { // b/143488140 //@NavigationModeSwitch - @TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/266606727 @Test public void goToOverviewFromHome() { mDevice.pressHome(); @@ -223,7 +224,6 @@ public class FallbackRecentsTest { // b/143488140 //@NavigationModeSwitch - @TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/266606727 @Test public void testOverview() { startAppFast(getAppPackageName()); diff --git a/quickstep/tests/src/com/android/quickstep/FullscreenDrawParamsTest.kt b/quickstep/tests/src/com/android/quickstep/FullscreenDrawParamsTest.kt index bb1afdf02d..db06b6bd7d 100644 --- a/quickstep/tests/src/com/android/quickstep/FullscreenDrawParamsTest.kt +++ b/quickstep/tests/src/com/android/quickstep/FullscreenDrawParamsTest.kt @@ -16,17 +16,13 @@ package com.android.quickstep import android.content.Context -import android.graphics.Rect import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.launcher3.FakeInvariantDeviceProfileTest import com.android.quickstep.util.TaskCornerRadius import com.android.quickstep.views.TaskView.FullscreenDrawParams -import com.android.systemui.shared.recents.model.ThumbnailData -import com.android.systemui.shared.recents.utilities.PreviewPositionHelper import com.android.systemui.shared.system.QuickStepContract import com.google.common.truth.Truth.assertThat -import kotlin.math.roundToInt import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -39,10 +35,6 @@ import org.mockito.Mockito.spy @RunWith(AndroidJUnit4::class) class FullscreenDrawParamsTest : FakeInvariantDeviceProfileTest() { - private val TASK_SCALE = 0.7f - private var mThumbnailData: ThumbnailData = mock(ThumbnailData::class.java) - - private val mPreviewPositionHelper = PreviewPositionHelper() private lateinit var params: FullscreenDrawParams @Before @@ -53,32 +45,11 @@ class FullscreenDrawParamsTest : FakeInvariantDeviceProfileTest() { @Test fun setStartProgress_correctCornerRadiusForTablet() { initializeVarsForTablet() - val dp = newDP() - val previewRect = Rect(0, 0, 100, 100) - val canvasWidth = (dp.widthPx * TASK_SCALE).roundToInt() - val canvasHeight = (dp.heightPx * TASK_SCALE).roundToInt() - val currentRotation = 0 - val isRtl = false - mPreviewPositionHelper.updateThumbnailMatrix( - previewRect, - mThumbnailData, - canvasWidth, - canvasHeight, - dp.widthPx, - dp.heightPx, - dp.taskbarHeight, - dp.isTablet, - currentRotation, - isRtl - ) params.setProgress( /* fullscreenProgress= */ 0f, /* parentScale= */ 1.0f, - /* taskViewScale= */ 1.0f, - /* previewWidth= */ 0, - dp, - mPreviewPositionHelper + /* taskViewScale= */ 1.0f ) val expectedRadius = TaskCornerRadius.get(context) @@ -88,32 +59,11 @@ class FullscreenDrawParamsTest : FakeInvariantDeviceProfileTest() { @Test fun setFullProgress_correctCornerRadiusForTablet() { initializeVarsForTablet() - val dp = newDP() - val previewRect = Rect(0, 0, 100, 100) - val canvasWidth = (dp.widthPx * TASK_SCALE).roundToInt() - val canvasHeight = (dp.heightPx * TASK_SCALE).roundToInt() - val currentRotation = 0 - val isRtl = false - mPreviewPositionHelper.updateThumbnailMatrix( - previewRect, - mThumbnailData, - canvasWidth, - canvasHeight, - dp.widthPx, - dp.heightPx, - dp.taskbarHeight, - dp.isTablet, - currentRotation, - isRtl - ) params.setProgress( /* fullscreenProgress= */ 1.0f, /* parentScale= */ 1.0f, - /* taskViewScale= */ 1.0f, - /* previewWidth= */ 0, - dp, - mPreviewPositionHelper + /* taskViewScale= */ 1.0f ) val expectedRadius = QuickStepContract.getWindowCornerRadius(context) @@ -123,32 +73,11 @@ class FullscreenDrawParamsTest : FakeInvariantDeviceProfileTest() { @Test fun setStartProgress_correctCornerRadiusForPhone() { initializeVarsForPhone() - val dp = newDP() - val previewRect = Rect(0, 0, 100, 100) - val canvasWidth = (dp.widthPx * TASK_SCALE).roundToInt() - val canvasHeight = (dp.heightPx * TASK_SCALE).roundToInt() - val currentRotation = 0 - val isRtl = false - mPreviewPositionHelper.updateThumbnailMatrix( - previewRect, - mThumbnailData, - canvasWidth, - canvasHeight, - dp.widthPx, - dp.heightPx, - dp.taskbarHeight, - dp.isTablet, - currentRotation, - isRtl - ) params.setProgress( /* fullscreenProgress= */ 0f, /* parentScale= */ 1.0f, - /* taskViewScale= */ 1.0f, - /* previewWidth= */ 0, - dp, - mPreviewPositionHelper + /* taskViewScale= */ 1.0f ) val expectedRadius = TaskCornerRadius.get(context) @@ -158,32 +87,11 @@ class FullscreenDrawParamsTest : FakeInvariantDeviceProfileTest() { @Test fun setFullProgress_correctCornerRadiusForPhone() { initializeVarsForPhone() - val dp = newDP() - val previewRect = Rect(0, 0, 100, 100) - val canvasWidth = (dp.widthPx * TASK_SCALE).roundToInt() - val canvasHeight = (dp.heightPx * TASK_SCALE).roundToInt() - val currentRotation = 0 - val isRtl = false - mPreviewPositionHelper.updateThumbnailMatrix( - previewRect, - mThumbnailData, - canvasWidth, - canvasHeight, - dp.widthPx, - dp.heightPx, - dp.taskbarHeight, - dp.isTablet, - currentRotation, - isRtl - ) params.setProgress( /* fullscreenProgress= */ 1.0f, /* parentScale= */ 1.0f, - /* taskViewScale= */ 1.0f, - /* previewWidth= */ 0, - dp, - mPreviewPositionHelper + /* taskViewScale= */ 1.0f ) val expectedRadius = QuickStepContract.getWindowCornerRadius(context) @@ -207,10 +115,7 @@ class FullscreenDrawParamsTest : FakeInvariantDeviceProfileTest() { spyParams.setProgress( /* fullscreenProgress= */ 0f, /* parentScale= */ 1.0f, - /* taskViewScale= */ 1.0f, - /* unused previewWidth= */ -1, - /* unusedDp= */ null, - /* unused previewPositionHelper= */ null + /* taskViewScale= */ 1.0f ) assertThat(spyParams.mCurrentDrawnCornerRadius).isEqualTo(display1TaskRadius) @@ -218,10 +123,7 @@ class FullscreenDrawParamsTest : FakeInvariantDeviceProfileTest() { spyParams.setProgress( /* fullscreenProgress= */ 0f, /* parentScale= */ 1.0f, - /* taskViewScale= */ 1.0f, - /* unused previewWidth= */ -1, - /* unusedDp= */ null, - /* unused previewPositionHelper= */ null + /* taskViewScale= */ 1.0f ) assertThat(spyParams.mCurrentDrawnCornerRadius).isEqualTo(display2TaskRadius) } @@ -243,10 +145,7 @@ class FullscreenDrawParamsTest : FakeInvariantDeviceProfileTest() { spyParams.setProgress( /* fullscreenProgress= */ 1.0f, /* parentScale= */ 1.0f, - /* taskViewScale= */ 1.0f, - /* unused previewWidth= */ -1, - /* unusedDp= */ null, - /* unused previewPositionHelper= */ null + /* taskViewScale= */ 1.0f ) assertThat(spyParams.mCurrentDrawnCornerRadius).isEqualTo(display1WindowRadius) @@ -255,9 +154,6 @@ class FullscreenDrawParamsTest : FakeInvariantDeviceProfileTest() { /* fullscreenProgress= */ 1.0f, /* parentScale= */ 1.0f, /* taskViewScale= */ 1.0f, - /* unused previewWidth= */ -1, - /* unusedDp= */ null, - /* unused previewPositionHelper= */ null ) assertThat(spyParams.mCurrentDrawnCornerRadius).isEqualTo(display2WindowRadius) } diff --git a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java index eded1c961f..df88726f68 100644 --- a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java +++ b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java @@ -185,7 +185,7 @@ public class NavigationModeSwitchRule implements TestRule { + launcher.getNavigationModeMismatchError(false), () -> launcher.getNavigationModeMismatchError(false) == null, WAIT_TIME_MS, launcher); - AbstractLauncherUiTest.checkDetectedLeaks(launcher); + AbstractLauncherUiTest.checkDetectedLeaks(launcher, false); return true; } diff --git a/quickstep/tests/src/com/android/quickstep/RecentsAnimationDeviceStateTest.kt b/quickstep/tests/src/com/android/quickstep/RecentsAnimationDeviceStateTest.kt new file mode 100644 index 0000000000..53bc2a23f2 --- /dev/null +++ b/quickstep/tests/src/com/android/quickstep/RecentsAnimationDeviceStateTest.kt @@ -0,0 +1,144 @@ +package com.android.quickstep + +import android.content.Context +import android.testing.AndroidTestingRunner +import android.view.Display +import android.view.IWindowManager +import androidx.test.core.app.ApplicationProvider +import androidx.test.filters.SmallTest +import com.android.launcher3.util.DisplayController.CHANGE_DENSITY +import com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE +import com.android.launcher3.util.DisplayController.CHANGE_ROTATION +import com.android.launcher3.util.DisplayController.Info +import com.android.launcher3.util.Executors +import com.android.launcher3.util.NavigationMode +import com.android.launcher3.util.window.WindowManagerProxy +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.reset +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations +import org.mockito.kotlin.verifyZeroInteractions +import org.mockito.kotlin.whenever + +/** Unit test for [RecentsAnimationDeviceState]. */ +@SmallTest +@RunWith(AndroidTestingRunner::class) +class RecentsAnimationDeviceStateTest { + + @Mock private lateinit var windowManager: IWindowManager + @Mock private lateinit var windowManagerProxy: WindowManagerProxy + @Mock private lateinit var info: Info + + private val context = ApplicationProvider.getApplicationContext() as Context + private lateinit var underTest: RecentsAnimationDeviceState + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + underTest = RecentsAnimationDeviceState(context, windowManager) + } + + @Test + fun registerExclusionListener_success() { + underTest.registerExclusionListener() + + awaitTasksCompleted() + verify(windowManager) + .registerSystemGestureExclusionListener( + underTest.mGestureExclusionListener, + Display.DEFAULT_DISPLAY + ) + } + + @Test + fun registerExclusionListener_again_fail() { + underTest.registerExclusionListener() + awaitTasksCompleted() + reset(windowManager) + + underTest.registerExclusionListener() + + awaitTasksCompleted() + verifyZeroInteractions(windowManager) + } + + @Test + fun unregisterExclusionListener_success() { + underTest.registerExclusionListener() + awaitTasksCompleted() + reset(windowManager) + + underTest.unregisterExclusionListener() + + awaitTasksCompleted() + verify(windowManager) + .unregisterSystemGestureExclusionListener( + underTest.mGestureExclusionListener, + Display.DEFAULT_DISPLAY + ) + } + + @Test + fun unregisterExclusionListener_again_fail() { + underTest.registerExclusionListener() + underTest.unregisterExclusionListener() + awaitTasksCompleted() + reset(windowManager) + + underTest.unregisterExclusionListener() + + awaitTasksCompleted() + verifyZeroInteractions(windowManager) + } + + @Test + fun onDisplayInfoChanged_noButton_registerExclusionListener() { + whenever(windowManagerProxy.getNavigationMode(context)).thenReturn(NavigationMode.NO_BUTTON) + + underTest.onDisplayInfoChanged(context, info, CHANGE_ROTATION or CHANGE_NAVIGATION_MODE) + + awaitTasksCompleted() + verify(windowManager) + .registerSystemGestureExclusionListener( + underTest.mGestureExclusionListener, + Display.DEFAULT_DISPLAY + ) + } + + @Test + fun onDisplayInfoChanged_twoButton_unregisterExclusionListener() { + underTest.registerExclusionListener() + awaitTasksCompleted() + whenever(info.getNavigationMode()).thenReturn(NavigationMode.TWO_BUTTONS) + reset(windowManager) + + underTest.onDisplayInfoChanged(context, info, CHANGE_ROTATION or CHANGE_NAVIGATION_MODE) + + awaitTasksCompleted() + verify(windowManager) + .unregisterSystemGestureExclusionListener( + underTest.mGestureExclusionListener, + Display.DEFAULT_DISPLAY + ) + } + + @Test + fun onDisplayInfoChanged_changeDensity_noOp() { + underTest.registerExclusionListener() + awaitTasksCompleted() + whenever(info.getNavigationMode()).thenReturn(NavigationMode.NO_BUTTON) + reset(windowManager) + + underTest.onDisplayInfoChanged(context, info, CHANGE_DENSITY) + + awaitTasksCompleted() + verifyZeroInteractions(windowManager) + } + + private fun awaitTasksCompleted() { + Executors.UI_HELPER_EXECUTOR.submit<Any> { null }.get() + } +} diff --git a/quickstep/tests/src/com/android/quickstep/RecentsModelTest.java b/quickstep/tests/src/com/android/quickstep/RecentsModelTest.java new file mode 100644 index 0000000000..648fa932a6 --- /dev/null +++ b/quickstep/tests/src/com/android/quickstep/RecentsModelTest.java @@ -0,0 +1,173 @@ +/* + * 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.quickstep; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.ActivityManager; +import android.content.Context; +import android.content.res.Resources; +import android.platform.test.flag.junit.SetFlagsRule; + +import androidx.test.annotation.UiThreadTest; +import androidx.test.filters.SmallTest; + +import com.android.launcher3.Flags; +import com.android.launcher3.R; +import com.android.launcher3.icons.IconProvider; +import com.android.quickstep.util.GroupTask; +import com.android.systemui.shared.recents.model.Task; +import com.android.systemui.shared.system.TaskStackChangeListeners; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; +import java.util.function.Consumer; + +@SmallTest +public class RecentsModelTest { + @Mock + private Context mContext; + + @Mock + private TaskThumbnailCache mThumbnailCache; + + @Mock + private RecentTasksList mTasksList; + + @Mock + private TaskThumbnailCache.HighResLoadingState mHighResLoadingState; + + private RecentsModel mRecentsModel; + + private RecentTasksList.TaskLoadResult mTaskResult; + + private Resources mResource; + + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + + @Before + public void setup() throws NoSuchFieldException { + MockitoAnnotations.initMocks(this); + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_GRID_ONLY_OVERVIEW); + mTaskResult = getTaskResult(); + doAnswer(invocation-> { + Consumer<ArrayList<GroupTask>> callback = invocation.getArgument(1); + callback.accept(mTaskResult); + return null; + }).when(mTasksList).getTaskKeys(anyInt(), any()); + + when(mHighResLoadingState.isEnabled()).thenReturn(true); + when(mThumbnailCache.getHighResLoadingState()).thenReturn(mHighResLoadingState); + when(mThumbnailCache.isPreloadingEnabled()).thenReturn(true); + + mRecentsModel = new RecentsModel(mContext, mTasksList, mock(TaskIconCache.class), + mThumbnailCache, mock(IconProvider.class), mock(TaskStackChangeListeners.class)); + + mResource = mock(Resources.class); + when(mResource.getInteger((R.integer.recentsThumbnailCacheSize))).thenReturn(3); + when(mContext.getResources()).thenReturn(mResource); + } + + @Test + @UiThreadTest + public void preloadOnHighResolutionEnabled() { + mRecentsModel.preloadCacheIfNeeded(); + + ArgumentCaptor<Task> taskArgs = ArgumentCaptor.forClass(Task.class); + verify(mRecentsModel.getThumbnailCache(), times(2)) + .updateThumbnailInCache(taskArgs.capture(), /* lowResolution= */ eq(false)); + + GroupTask expectedGroupTask = mTaskResult.get(0); + assertThat(taskArgs.getAllValues().get(0)).isEqualTo( + expectedGroupTask.task1); + assertThat(taskArgs.getAllValues().get(1)).isEqualTo( + expectedGroupTask.task2); + } + + @Test + public void notPreloadOnHighResolutionDisabled() { + when(mHighResLoadingState.isEnabled()).thenReturn(false); + when(mThumbnailCache.isPreloadingEnabled()).thenReturn(true); + mRecentsModel.preloadCacheIfNeeded(); + verify(mRecentsModel.getThumbnailCache(), never()) + .updateThumbnailInCache(any(), anyBoolean()); + } + + @Test + public void notPreloadOnPreloadDisabled() { + when(mThumbnailCache.isPreloadingEnabled()).thenReturn(false); + mRecentsModel.preloadCacheIfNeeded(); + verify(mRecentsModel.getThumbnailCache(), never()) + .updateThumbnailInCache(any(), anyBoolean()); + + } + + @Test + public void increaseCacheSizeAndPreload() { + // Mock to return preload is needed + when(mThumbnailCache.updateCacheSizeAndRemoveExcess()).thenReturn(true); + // Update cache size + mRecentsModel.updateCacheSizeAndPreloadIfNeeded(); + // Assert update cache is called + verify(mRecentsModel.getThumbnailCache(), times(2)) + .updateThumbnailInCache(any(), /* lowResolution= */ eq(false)); + } + + @Test + public void decreaseCacheSizeAndNotPreload() { + // Mock to return preload is not needed + when(mThumbnailCache.updateCacheSizeAndRemoveExcess()).thenReturn(false); + // Update cache size + mRecentsModel.updateCacheSizeAndPreloadIfNeeded(); + // Assert update cache is never called + verify(mRecentsModel.getThumbnailCache(), never()) + .updateThumbnailInCache(any(), anyBoolean()); + } + + private RecentTasksList.TaskLoadResult getTaskResult() { + RecentTasksList.TaskLoadResult allTasks = new RecentTasksList.TaskLoadResult(0, false, 1); + ActivityManager.RecentTaskInfo taskInfo1 = new ActivityManager.RecentTaskInfo(); + Task.TaskKey taskKey1 = new Task.TaskKey(taskInfo1); + Task task1 = Task.from(taskKey1, taskInfo1, false); + + ActivityManager.RecentTaskInfo taskInfo2 = new ActivityManager.RecentTaskInfo(); + Task.TaskKey taskKey2 = new Task.TaskKey(taskInfo2); + Task task2 = Task.from(taskKey2, taskInfo2, false); + + allTasks.add(new GroupTask(task1, task2, null)); + return allTasks; + } +} diff --git a/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java b/quickstep/tests/src/com/android/quickstep/TaplDigitalWellBeingToastTest.java index 1129a337e3..728fe6741d 100644 --- a/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java +++ b/quickstep/tests/src/com/android/quickstep/TaplDigitalWellBeingToastTest.java @@ -1,3 +1,18 @@ +/* + * 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.quickstep; import static androidx.test.InstrumentationRegistry.getInstrumentation; @@ -27,7 +42,7 @@ import java.time.Duration; @LargeTest @RunWith(AndroidJUnit4.class) -public class DigitalWellBeingToastTest extends AbstractQuickStepTest { +public class TaplDigitalWellBeingToastTest extends AbstractQuickStepTest { private static final String CALCULATOR_PACKAGE = resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR); diff --git a/quickstep/tests/src/com/android/quickstep/TaplOverviewIconTest.java b/quickstep/tests/src/com/android/quickstep/TaplOverviewIconTest.java new file mode 100644 index 0000000000..3f806d1764 --- /dev/null +++ b/quickstep/tests/src/com/android/quickstep/TaplOverviewIconTest.java @@ -0,0 +1,112 @@ +/* + * 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.quickstep; + +import static com.android.launcher3.ui.AbstractLauncherUiTest.initialize; +import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL; +import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import android.content.Intent; +import android.platform.test.annotations.PlatinumTest; + +import com.android.launcher3.tapl.OverviewTaskMenu; +import com.android.launcher3.ui.AbstractLauncherUiTest; +import com.android.launcher3.util.rule.TestStabilityRule; + +import org.junit.Before; +import org.junit.Test; + +/** + * This test run in both Out of process (Oop) and in-process (Ipc). + * Tests the app Icon in overview. + */ +public class TaplOverviewIconTest extends AbstractLauncherUiTest { + + private static final String CALCULATOR_APP_PACKAGE = + resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR); + + @Before + public void setUp() throws Exception { + super.setUp(); + initialize(this); + } + + @PlatinumTest(focusArea = "launcher") + @Test + public void testOverviewActionsMenu() { + startTestAppsWithCheck(); + + OverviewTaskMenu menu = mLauncher.goHome().switchToOverview().getCurrentTask().tapMenu(); + + assertNotNull("Tapping App info menu item returned null", menu.tapAppInfoMenuItem()); + executeOnLauncher(launcher -> assertTrue( + "Launcher activity is the top activity; expecting another activity to be the top", + isInLaunchedApp(launcher))); + } + + private void startTestAppsWithCheck() { + startTestApps(); + executeOnLauncher(launcher -> assertTrue( + "Launcher activity is the top activity; expecting another activity to be the top " + + "one", + isInLaunchedApp(launcher))); + } + + private void startTestApps() { + startAppFast(getAppPackageName()); + startAppFast(CALCULATOR_APP_PACKAGE); + startTestActivity(2); + } + + @Test + @TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/288939273 + public void testSplitTaskTapBothIconMenus() { + createAndLaunchASplitPair(); + + OverviewTaskMenu taskMenu = + mLauncher.goHome().switchToOverview().getCurrentTask().tapMenu(); + assertTrue("App info item not appearing in expanded task menu.", + taskMenu.hasMenuItem("App info")); + taskMenu.touchOutsideTaskMenuToDismiss(); + + OverviewTaskMenu splitMenu = + mLauncher.goHome().switchToOverview().getCurrentTask().tapSplitTaskMenu(); + assertTrue("App info item not appearing in expanded split task's menu.", + splitMenu.hasMenuItem("App info")); + splitMenu.touchOutsideTaskMenuToDismiss(); + } + + private void createAndLaunchASplitPair() { + startTestActivity(2); + startTestActivity(3); + + if (mLauncher.isTablet() && !mLauncher.isGridOnlyOverviewEnabled()) { + mLauncher.goHome().switchToOverview().getOverviewActions() + .clickSplit() + .getTestActivityTask(2) + .open(); + } else { + mLauncher.goHome().switchToOverview().getCurrentTask() + .tapMenu() + .tapSplitMenuItem() + .getCurrentTask() + .open(); + } + } +} diff --git a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java b/quickstep/tests/src/com/android/quickstep/TaplStartLauncherViaGestureTests.java index 20aa410f8f..7e274e8833 100644 --- a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java +++ b/quickstep/tests/src/com/android/quickstep/TaplStartLauncherViaGestureTests.java @@ -22,7 +22,7 @@ import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUB import androidx.test.filters.LargeTest; import androidx.test.runner.AndroidJUnit4; -import com.android.launcher3.ui.TaplTestsLauncher3; +import com.android.launcher3.ui.AbstractLauncherUiTest; import com.android.launcher3.util.rule.TestStabilityRule.Stability; import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch; @@ -32,7 +32,7 @@ import org.junit.runner.RunWith; @LargeTest @RunWith(AndroidJUnit4.class) -public class StartLauncherViaGestureTests extends AbstractQuickStepTest { +public class TaplStartLauncherViaGestureTests extends AbstractQuickStepTest { static final int STRESS_REPEAT_COUNT = 10; @@ -40,7 +40,7 @@ public class StartLauncherViaGestureTests extends AbstractQuickStepTest { @Before public void setUp() throws Exception { super.setUp(); - TaplTestsLauncher3.initialize(this); + AbstractLauncherUiTest.initialize(this); // b/143488140 mLauncher.goHome(); // Start an activity where the gestures start. @@ -49,7 +49,8 @@ public class StartLauncherViaGestureTests extends AbstractQuickStepTest { @Test @NavigationModeSwitch - @Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/187761685 + // Stress tests are long. We permanently demote them from presubmit to match the presubmit SLO. + @Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) public void testStressPressHome() { for (int i = 0; i < STRESS_REPEAT_COUNT; ++i) { // Destroy Launcher activity. @@ -62,7 +63,8 @@ public class StartLauncherViaGestureTests extends AbstractQuickStepTest { @Test @NavigationModeSwitch - @Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/187761685 + // Stress tests are long. We permanently demote them from presubmit to match the presubmit SLO. + @Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) public void testStressSwipeToOverview() { for (int i = 0; i < STRESS_REPEAT_COUNT; ++i) { // Destroy Launcher activity. diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsKeyboardQuickSwitch.java b/quickstep/tests/src/com/android/quickstep/TaplTestsKeyboardQuickSwitch.java new file mode 100644 index 0000000000..829e54bdff --- /dev/null +++ b/quickstep/tests/src/com/android/quickstep/TaplTestsKeyboardQuickSwitch.java @@ -0,0 +1,187 @@ +/* + * 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.quickstep; + +import android.content.Intent; + +import androidx.annotation.NonNull; +import androidx.test.filters.LargeTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.launcher3.tapl.KeyboardQuickSwitch; +import com.android.launcher3.ui.AbstractLauncherUiTest; + +import org.junit.Assume; +import org.junit.Test; +import org.junit.runner.RunWith; + +@LargeTest +@RunWith(AndroidJUnit4.class) +public class TaplTestsKeyboardQuickSwitch extends AbstractQuickStepTest { + + private enum TestSurface { + HOME, LAUNCHED_APP, HOME_ALL_APPS, WIDGETS, + } + + private enum TestCase { + DISMISS(0), + LAUNCH_LAST_APP(0), + LAUNCH_SELECTED_APP(1), + LAUNCH_OVERVIEW(5); + + private final int mNumAdditionalRunningTasks; + + TestCase(int numAdditionalRunningTasks) { + mNumAdditionalRunningTasks = numAdditionalRunningTasks; + } + } + + private static final String CALCULATOR_APP_PACKAGE = + resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR); + + @Override + public void setUp() throws Exception { + Assume.assumeTrue(mLauncher.isTablet()); + super.setUp(); + AbstractLauncherUiTest.initialize(this); + startAppFast(CALCULATOR_APP_PACKAGE); + startTestActivity(2); + } + + @Test + public void testDismiss_fromHome() { + runTest(TestSurface.HOME, TestCase.DISMISS); + } + + @Test + public void testDismiss_fromApp() { + runTest(TestSurface.LAUNCHED_APP, TestCase.DISMISS); + } + + @Test + public void testDismiss_fromHomeAllApps() { + runTest(TestSurface.HOME_ALL_APPS, TestCase.DISMISS); + } + + @Test + public void testDismiss_fromWidgets() { + runTest(TestSurface.WIDGETS, TestCase.DISMISS); + } + + @Test + public void testLaunchLastTask_fromHome() { + runTest(TestSurface.HOME, TestCase.LAUNCH_LAST_APP); + } + + @Test + public void testLaunchLastTask_fromApp() { + runTest(TestSurface.LAUNCHED_APP, TestCase.LAUNCH_LAST_APP); + } + + @Test + public void testLaunchLastTask_fromHomeAllApps() { + runTest(TestSurface.HOME_ALL_APPS, TestCase.LAUNCH_LAST_APP); + } + + @Test + public void testLaunchLastTask_fromWidgets() { + runTest(TestSurface.WIDGETS, TestCase.LAUNCH_LAST_APP); + } + + @Test + public void testLaunchSelectedTask_fromHome() { + runTest(TestSurface.HOME, TestCase.LAUNCH_SELECTED_APP); + } + + @Test + public void testLaunchSelectedTask_fromApp() { + runTest(TestSurface.LAUNCHED_APP, TestCase.LAUNCH_SELECTED_APP); + } + + @Test + public void testLaunchSelectedTask_fromHomeAllApps() { + runTest(TestSurface.HOME_ALL_APPS, TestCase.LAUNCH_SELECTED_APP); + } + + @Test + public void testLaunchSelectedTask_fromWidgets() { + runTest(TestSurface.WIDGETS, TestCase.LAUNCH_SELECTED_APP); + } + + @Test + public void testLaunchOverviewTask_fromHome() { + runTest(TestSurface.HOME, TestCase.LAUNCH_OVERVIEW); + } + + @Test + public void testLaunchOverviewTask_fromApp() { + runTest(TestSurface.LAUNCHED_APP, TestCase.LAUNCH_OVERVIEW); + } + + @Test + public void testLaunchOverviewTask_fromHomeAllApps() { + runTest(TestSurface.HOME_ALL_APPS, TestCase.LAUNCH_OVERVIEW); + } + + @Test + public void testLaunchOverviewTask_fromWidgets() { + runTest(TestSurface.WIDGETS, TestCase.LAUNCH_OVERVIEW); + } + + private void runTest(@NonNull TestSurface testSurface, @NonNull TestCase testCase) { + for (int i = 0; i < testCase.mNumAdditionalRunningTasks; i++) { + startTestActivity(3 + i); + } + + KeyboardQuickSwitch kqs; + switch (testSurface) { + case HOME: + kqs = mLauncher.goHome().showQuickSwitchView(); + break; + case LAUNCHED_APP: + mLauncher.setIgnoreTaskbarVisibility(true); + kqs = mLauncher.getLaunchedAppState().showQuickSwitchView(); + break; + case HOME_ALL_APPS: + kqs = mLauncher.goHome().switchToAllApps().showQuickSwitchView(); + break; + case WIDGETS: + kqs = mLauncher.goHome().openAllWidgets().showQuickSwitchView(); + break; + default: + throw new IllegalStateException( + "KeyboardQuickSwitch could not be initialized for test surface: " + + testSurface); + } + + switch (testCase) { + case DISMISS: + kqs.dismiss(); + break; + case LAUNCH_LAST_APP: + kqs.launchFocusedAppTask(CALCULATOR_APP_PACKAGE); + break; + case LAUNCH_SELECTED_APP: + kqs.moveFocusForward().launchFocusedAppTask(CALCULATOR_APP_PACKAGE); + break; + case LAUNCH_OVERVIEW: + kqs.moveFocusBackward().moveFocusBackward().launchFocusedOverviewTask(); + break; + default: + throw new IllegalStateException("Cannot run test case: " + testCase); + } + } +} diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsPersistentTaskbar.java b/quickstep/tests/src/com/android/quickstep/TaplTestsPersistentTaskbar.java index 1b5313bad0..c9e536a737 100644 --- a/quickstep/tests/src/com/android/quickstep/TaplTestsPersistentTaskbar.java +++ b/quickstep/tests/src/com/android/quickstep/TaplTestsPersistentTaskbar.java @@ -20,6 +20,8 @@ import static com.android.quickstep.TaskbarModeSwitchRule.Mode.PERSISTENT; import androidx.test.filters.LargeTest; import androidx.test.runner.AndroidJUnit4; +import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape; +import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch; import com.android.quickstep.TaskbarModeSwitchRule.TaskbarModeSwitch; import org.junit.Test; @@ -31,16 +33,10 @@ public class TaplTestsPersistentTaskbar extends AbstractTaplTestsTaskbar { @Test @TaskbarModeSwitch(mode = PERSISTENT) - public void testHideShowTaskbar() { - getTaskbar().hide(); - mLauncher.getLaunchedAppState().showTaskbar(); - } - - @Test - @TaskbarModeSwitch(mode = PERSISTENT) - public void testHideTaskbarPersistsOnRecreate() { - getTaskbar().hide(); - mLauncher.recreateTaskbar(); - mLauncher.getLaunchedAppState().assertTaskbarHidden(); + @PortraitLandscape + @NavigationModeSwitch + public void testTaskbarFillsWidth() { + // Width check is performed inside TAPL whenever getTaskbar() is called. + getTaskbar(); } } diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java index 25f90caa3e..6cbe1712e0 100644 --- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java +++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java @@ -16,11 +16,9 @@ package com.android.quickstep; -import static com.android.launcher3.testing.shared.TestProtocol.FLAKY_QUICK_SWITCH_TO_PREVIOUS_APP; -import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName; import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL; import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT; -import static com.android.quickstep.TaskbarModeSwitchRule.Mode.PERSISTENT; +import static com.android.quickstep.TaskbarModeSwitchRule.Mode.TRANSIENT; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -29,8 +27,8 @@ import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeTrue; import android.content.Intent; +import android.content.res.Configuration; import android.platform.test.annotations.PlatinumTest; -import android.util.Log; import androidx.test.filters.LargeTest; import androidx.test.platform.app.InstrumentationRegistry; @@ -46,10 +44,8 @@ import com.android.launcher3.tapl.LauncherInstrumentation.NavigationModel; import com.android.launcher3.tapl.Overview; import com.android.launcher3.tapl.OverviewActions; import com.android.launcher3.tapl.OverviewTask; -import com.android.launcher3.tapl.OverviewTaskMenu; +import com.android.launcher3.ui.AbstractLauncherUiTest; import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape; -import com.android.launcher3.ui.TaplTestsLauncher3; -import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.Wait; import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord; import com.android.launcher3.util.rule.TestStabilityRule; @@ -67,7 +63,6 @@ import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) public class TaplTestsQuickstep extends AbstractQuickStepTest { - private static final String APP_NAME = "LauncherTestApp"; private static final String CALCULATOR_APP_PACKAGE = resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR); private static final String READ_DEVICE_CONFIG_PERMISSION = @@ -76,7 +71,7 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { @Before public void setUp() throws Exception { super.setUp(); - TaplTestsLauncher3.initialize(this); + AbstractLauncherUiTest.initialize(this); executeOnLauncher(launcher -> { RecentsView recentsView = launcher.getOverviewPanel(); recentsView.getPagedViewOrientedState().forceAllowRotationForTesting(true); @@ -85,7 +80,7 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { @After public void tearDown() { - executeOnLauncher(launcher -> { + executeOnLauncherInTearDown(launcher -> { RecentsView recentsView = launcher.getOverviewPanel(); recentsView.getPagedViewOrientedState().forceAllowRotationForTesting(false); }); @@ -108,7 +103,6 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { @Test @NavigationModeSwitch @PortraitLandscape - @PlatinumTest(focusArea = "launcher") public void testWorkspaceSwitchToAllApps() { assertNotNull("switchToAllApps() returned null", mLauncher.getWorkspace().switchToAllApps()); @@ -185,8 +179,9 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { @Test @NavigationModeSwitch @PortraitLandscape - @PlatinumTest(focusArea = "launcher") public void testOverviewActions() throws Exception { + assumeFalse("Skipping Overview Actions tests for grid only overview", + mLauncher.isTablet() && mLauncher.isGridOnlyOverviewEnabled()); // Experimenting for b/165029151: final Overview overview = mLauncher.goHome().switchToOverview(); if (overview.hasTasks()) overview.dismissAllTasks(); @@ -199,19 +194,6 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { actionsView.clickAndDismissScreenshot(); } - - @Test - public void testOverviewActionsMenu() throws Exception { - startTestAppsWithCheck(); - - OverviewTaskMenu menu = mLauncher.goHome().switchToOverview().getCurrentTask().tapMenu(); - - assertNotNull("Tapping App info menu item returned null", menu.tapAppInfoMenuItem()); - executeOnLauncher(launcher -> assertTrue( - "Launcher activity is the top activity; expecting another activity to be the top", - isInLaunchedApp(launcher))); - } - private int getCurrentOverviewPage(Launcher launcher) { return launcher.<RecentsView>getOverviewPanel().getCurrentPage(); } @@ -233,7 +215,6 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { @NavigationModeSwitch @PortraitLandscape @ScreenRecord // b/238461765 - @PlatinumTest(focusArea = "launcher") public void testSwitchToOverview() throws Exception { startTestAppsWithCheck(); assertNotNull("Workspace.switchToOverview() returned null", @@ -242,11 +223,23 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { isInState(() -> LauncherState.OVERVIEW)); } + @Test + @TaskbarModeSwitch(mode = TRANSIENT) + public void testSwitchToOverviewWithStashedTaskbar() throws Exception { + try { + startTestAppsWithCheck(); + // Set ignoreTaskbarVisibility, as transient taskbar will be stashed after app launch. + mLauncher.setIgnoreTaskbarVisibility(true); + mLauncher.getLaunchedAppState().switchToOverview(); + } finally { + mLauncher.setIgnoreTaskbarVisibility(false); + } + } + @Ignore @Test @NavigationModeSwitch @PortraitLandscape - @PlatinumTest(focusArea = "launcher") public void testBackground() throws Exception { startAppFast(CALCULATOR_APP_PACKAGE); final LaunchedAppState launchedAppState = getAndAssertLaunchedApp(); @@ -271,27 +264,8 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { } @Test - @PortraitLandscape - public void testAllAppsFromHome() throws Exception { - // Test opening all apps - assertNotNull("switchToAllApps() returned null", - mLauncher.getWorkspace().switchToAllApps()); - - TaplTestsLauncher3.runAllAppsTest(this, mLauncher.getAllApps()); - - // Testing pressHome. - assertTrue("Launcher internal state is not All Apps", - isInState(() -> LauncherState.ALL_APPS)); - assertNotNull("pressHome returned null", mLauncher.goHome()); - assertTrue("Launcher internal state is not Home", - isInState(() -> LauncherState.NORMAL)); - assertNotNull("getHome returned null", mLauncher.getWorkspace()); - } - - @Test @NavigationModeSwitch @PortraitLandscape - @PlatinumTest(focusArea = "launcher") public void testQuickSwitchFromApp() throws Exception { startTestActivity(2); startTestActivity(3); @@ -320,9 +294,8 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { } @Test - @ScreenRecord // b/242163205 - @PlatinumTest(focusArea = "launcher") - @TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/286084688 + @TaskbarModeSwitch + @Ignore // b/314873201 public void testQuickSwitchToPreviousAppForTablet() throws Exception { assumeTrue(mLauncher.isTablet()); startTestActivity(2); @@ -331,33 +304,43 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { // Set ignoreTaskbarVisibility to true to verify the task bar visibility explicitly. mLauncher.setIgnoreTaskbarVisibility(true); - // Expect task bar invisible when the launched app was the IME activity. - LaunchedAppState launchedAppState = getAndAssertLaunchedApp(); - launchedAppState.assertTaskbarHidden(); - // Quick-switch to the test app with swiping to right. - quickSwitchToPreviousAppAndAssert(true /* toRight */); + try { + boolean isTransientTaskbar = mLauncher.isTransientTaskbar(); + // Expect task bar invisible when the launched app was the IME activity. + LaunchedAppState launchedAppState = getAndAssertLaunchedApp(); + if (!isTransientTaskbar && isHardwareKeyboard()) { + launchedAppState.assertTaskbarVisible(); + } else { + launchedAppState.assertTaskbarHidden(); + } + + // Quick-switch to the test app with swiping to right. + quickSwitchToPreviousAppAndAssert(true /* toRight */); - assertTestActivityIsRunning(2, - "The first app we should have quick switched to is not running"); - // Expect task bar visible when the launched app was the test activity. - launchedAppState = getAndAssertLaunchedApp(); - - Log.e(FLAKY_QUICK_SWITCH_TO_PREVIOUS_APP, - "is Taskbar Transient : " + DisplayController.isTransientTaskbar(mTargetContext)); - // TODO(b/286084688): Remove this branching check after test corruption is resolved. - // Branching this check because of test corruption. - if (DisplayController.isTransientTaskbar(mTargetContext)) { - launchedAppState.assertTaskbarHidden(); - } else { - launchedAppState.assertTaskbarVisible(); + assertTestActivityIsRunning(2, + "The first app we should have quick switched to is not running"); + launchedAppState = getAndAssertLaunchedApp(); + if (isTransientTaskbar) { + launchedAppState.assertTaskbarHidden(); + } else { + // Expect taskbar visible when the launched app was the test activity. + launchedAppState.assertTaskbarVisible(); + } + } finally { + // Reset ignoreTaskbarVisibility to ensure other tests still verify it. + mLauncher.setIgnoreTaskbarVisibility(false); } } + private boolean isHardwareKeyboard() { + return Configuration.KEYBOARD_QWERTY + == mTargetContext.getResources().getConfiguration().keyboard; + } + @Test @NavigationModeSwitch @PortraitLandscape - @PlatinumTest(focusArea = "launcher") public void testQuickSwitchFromHome() throws Exception { startTestActivity(2); mLauncher.goHome().quickSwitchToPreviousApp(); @@ -369,10 +352,11 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { @Test @PortraitLandscape @NavigationModeSwitch - @PlatinumTest(focusArea = "launcher") public void testPressBack() throws Exception { InstrumentationRegistry.getInstrumentation().getUiAutomation().adoptShellPermissionIdentity( READ_DEVICE_CONFIG_PERMISSION); + // Debug if we need to goHome to prevent wrong previous state b/315525621 + mLauncher.goHome(); assumeFalse(FeatureFlags.ENABLE_BACK_SWIPE_LAUNCHER_ANIMATION.get()); mLauncher.getWorkspace().switchToAllApps(); mLauncher.pressBack(); @@ -387,8 +371,10 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { @Test @PortraitLandscape - @TaskbarModeSwitch(mode = PERSISTENT) + @TaskbarModeSwitch() @PlatinumTest(focusArea = "launcher") + @TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/309820115 + @ScreenRecord // b/309820115 public void testOverviewForTablet() throws Exception { assumeTrue(mLauncher.isTablet()); @@ -411,7 +397,9 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { // Test opening the task. overview.getCurrentTask().open(); assertTrue("Test activity didn't open from Overview", - mDevice.wait(Until.hasObject(By.pkg(getAppPackageName()).text("TestActivity10")), + mDevice.wait(Until.hasObject(By.pkg(getAppPackageName()).text( + mLauncher.isGridOnlyOverviewEnabled() ? "TestActivity12" + : "TestActivity13")), DEFAULT_UI_TIMEOUT)); // Scroll the task offscreen as it is now first @@ -432,16 +420,17 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { (Math.abs(getTopRowTaskCountForTablet(launcher) - getBottomRowTaskCountForTablet( launcher)) <= 1))); - // Test dismissing more tasks. - assertTrue("Launcher internal state didn't remain in Overview", - isInState(() -> LauncherState.OVERVIEW)); - overview.getCurrentTask().dismiss(); - assertTrue("Launcher internal state didn't remain in Overview", - isInState(() -> LauncherState.OVERVIEW)); - overview.getCurrentTask().dismiss(); - executeOnLauncher(launcher -> assertTrue("Grid did not rebalance after multiple dismissals", - (Math.abs(getTopRowTaskCountForTablet(launcher) - getBottomRowTaskCountForTablet( - launcher)) <= 1))); + // TODO(b/308841019): Re-enable after fixing Overview jank when dismiss +// // Test dismissing more tasks. +// assertTrue("Launcher internal state didn't remain in Overview", +// isInState(() -> LauncherState.OVERVIEW)); +// overview.getCurrentTask().dismiss(); +// assertTrue("Launcher internal state didn't remain in Overview", +// isInState(() -> LauncherState.OVERVIEW)); +// overview.getCurrentTask().dismiss(); +// executeOnLauncher(launcher -> assertTrue("Grid did not rebalance after multiple dismissals", +// (Math.abs(getTopRowTaskCountForTablet(launcher) - getBottomRowTaskCountForTablet( +// launcher)) <= 1))); // Test dismissing all tasks. mLauncher.goHome().switchToOverview().dismissAllTasks(); @@ -478,6 +467,7 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { @Test @PortraitLandscape + @TaskbarModeSwitch public void testTaskbarDeadzonesForTablet() throws Exception { assumeTrue(mLauncher.isTablet()); @@ -490,19 +480,32 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { launcher -> assertTrue("Should have at least 3 tasks", getTaskCount(launcher) >= 3)); - // On persistent taskbar, it should not dismiss when tapping the taskbar - overview.touchTaskbarBottomCorner(/* tapRight= */ false); - assertTrue("Launcher internal state should be Overview", - isInState(() -> LauncherState.OVERVIEW)); + if (mLauncher.isTransientTaskbar()) { + // On transient taskbar, it should dismiss when tapping outside taskbar bounds. + overview.touchTaskbarBottomCorner(/* tapRight= */ false); + assertTrue("Launcher internal state should be Normal", + isInState(() -> LauncherState.NORMAL)); - // On persistent taskbar, it should not dismiss when tapping the taskbar - overview.touchTaskbarBottomCorner(/* tapRight= */ true); - assertTrue("Launcher internal state should be Overview", - isInState(() -> LauncherState.OVERVIEW)); + overview = mLauncher.getWorkspace().switchToOverview(); + + // On transient taskbar, it should dismiss when tapping outside taskbar bounds. + overview.touchTaskbarBottomCorner(/* tapRight= */ true); + assertTrue("Launcher internal state should be Normal", + isInState(() -> LauncherState.NORMAL)); + } else { + // On persistent taskbar, it should not dismiss when tapping the taskbar + overview.touchTaskbarBottomCorner(/* tapRight= */ false); + assertTrue("Launcher internal state should be Overview", + isInState(() -> LauncherState.OVERVIEW)); + + // On persistent taskbar, it should not dismiss when tapping the taskbar + overview.touchTaskbarBottomCorner(/* tapRight= */ true); + assertTrue("Launcher internal state should be Overview", + isInState(() -> LauncherState.OVERVIEW)); + } } @Test - @ScreenRecord // b/242163205 public void testDisableRotationCheckForPhone() throws Exception { assumeFalse(mLauncher.isTablet()); try { diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java b/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java index 92b598b626..1e33635a23 100644 --- a/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java +++ b/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java @@ -29,11 +29,14 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.LargeTest; import androidx.test.platform.app.InstrumentationRegistry; -import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.tapl.Overview; +import com.android.launcher3.tapl.Taskbar; +import com.android.launcher3.tapl.TaskbarAppIcon; +import com.android.launcher3.ui.AbstractLauncherUiTest; import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape; -import com.android.launcher3.ui.TaplTestsLauncher3; import com.android.launcher3.util.rule.TestStabilityRule; import com.android.quickstep.TaskbarModeSwitchRule.TaskbarModeSwitch; +import com.android.wm.shell.Flags; import org.junit.After; import org.junit.Before; @@ -54,7 +57,7 @@ public class TaplTestsSplitscreen extends AbstractQuickStepTest { @Before public void setUp() throws Exception { super.setUp(); - TaplTestsLauncher3.initialize(this); + AbstractLauncherUiTest.initialize(this); if (mLauncher.isTablet()) { mLauncher.enableBlockTimeout(true); @@ -108,7 +111,8 @@ public class TaplTestsSplitscreen extends AbstractQuickStepTest { @Test public void testSaveAppPairMenuItemExistsOnSplitPair() throws Exception { - assumeTrue(FeatureFlags.ENABLE_APP_PAIRS.get()); + assumeTrue("App pairs feature is currently not enabled, no test needed", + Flags.enableAppPairs()); createAndLaunchASplitPair(); @@ -122,7 +126,8 @@ public class TaplTestsSplitscreen extends AbstractQuickStepTest { @Test public void testSaveAppPairMenuItemDoesNotExistOnSingleTask() throws Exception { - assumeTrue(FeatureFlags.ENABLE_APP_PAIRS.get()); + assumeTrue("App pairs feature is currently not enabled, no test needed", + Flags.enableAppPairs()); startAppFast(CALCULATOR_APP_PACKAGE); @@ -134,11 +139,36 @@ public class TaplTestsSplitscreen extends AbstractQuickStepTest { .hasMenuItem("Save app pair")); } + @Test + public void testSplitSingleTaskFromTaskbar() { + // Currently only tablets have Taskbar in Overview, so test is only active on tablets + assumeTrue(mLauncher.isTablet()); + + if (!mLauncher.getRecentTasks().isEmpty()) { + // Clear all recent tasks + mLauncher.goHome().switchToOverview().dismissAllTasks(); + } + + startAppFast(getAppPackageName()); + + Overview overview = mLauncher.goHome().switchToOverview(); + if (mLauncher.isGridOnlyOverviewEnabled()) { + overview.getCurrentTask().tapMenu().tapSplitMenuItem(); + } else { + overview.getOverviewActions().clickSplit(); + } + + Taskbar taskbar = overview.getTaskbar(); + String firstAppName = taskbar.getIconNames().get(0); + TaskbarAppIcon firstApp = taskbar.getAppIcon(firstAppName); + firstApp.launchIntoSplitScreen(); + } + private void createAndLaunchASplitPair() { startTestActivity(2); startTestActivity(3); - if (mLauncher.isTablet()) { + if (mLauncher.isTablet() && !mLauncher.isGridOnlyOverviewEnabled()) { mLauncher.goHome().switchToOverview().getOverviewActions() .clickSplit() .getTestActivityTask(2) diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java b/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java index 4ff2f9c721..f0683f9082 100644 --- a/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java +++ b/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java @@ -15,13 +15,13 @@ */ package com.android.quickstep; +import static com.android.launcher3.util.TestConstants.AppNames.TEST_APP_NAME; import static com.android.quickstep.TaplTestsTaskbar.TaskbarMode.PERSISTENT; import static com.android.quickstep.TaplTestsTaskbar.TaskbarMode.TRANSIENT; import androidx.test.filters.LargeTest; import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape; -import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord; import org.junit.Test; import org.junit.runner.RunWith; @@ -89,7 +89,6 @@ public class TaplTestsTaskbar extends AbstractTaplTestsTaskbar { } @Test - @ScreenRecord // b/231615831 @PortraitLandscape public void testLaunchAppInSplitscreen() { getTaskbar().getAppIcon(TEST_APP_NAME).dragToSplitscreen( @@ -103,7 +102,6 @@ public class TaplTestsTaskbar extends AbstractTaplTestsTaskbar { } @Test - @ScreenRecord // b/231615831 @PortraitLandscape public void testLaunchShortcutInSplitscreen() { getTaskbar().getAppIcon(TEST_APP_NAME) @@ -132,7 +130,6 @@ public class TaplTestsTaskbar extends AbstractTaplTestsTaskbar { } @Test - @ScreenRecord // b/231615831 @PortraitLandscape public void testLaunchAppInSplitscreen_fromTaskbarAllApps() { getTaskbar().openAllApps() @@ -141,7 +138,6 @@ public class TaplTestsTaskbar extends AbstractTaplTestsTaskbar { } @Test - @ScreenRecord // b/231615831 @PortraitLandscape public void testLaunchShortcutInSplitscreen_fromTaskbarAllApps() { getTaskbar().openAllApps() @@ -151,6 +147,18 @@ public class TaplTestsTaskbar extends AbstractTaplTestsTaskbar { .dragToSplitscreen(TEST_APP_PACKAGE, CALCULATOR_APP_PACKAGE); } + @Test + @PortraitLandscape + public void testDismissAllAppsByTappingOutsideSheet() { + getTaskbar().openAllApps().dismissByTappingOutsideForTablet(/* tapRight= */ true); + getTaskbar().openAllApps().dismissByTappingOutsideForTablet(/* tapRight= */ false); + } + + @Test + public void testOpenMenuViaRightClick() { + getTaskbar().getAppIcon(TEST_APP_NAME).openDeepShortcutMenuWithRightClick(); + } + private boolean isTaskbarTestModeTransient() { return TRANSIENT == mTaskbarMode; } diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsTrackpad.java b/quickstep/tests/src/com/android/quickstep/TaplTestsTrackpad.java index 907dbccb91..3465f23476 100644 --- a/quickstep/tests/src/com/android/quickstep/TaplTestsTrackpad.java +++ b/quickstep/tests/src/com/android/quickstep/TaplTestsTrackpad.java @@ -19,7 +19,6 @@ package com.android.quickstep; import static com.android.quickstep.NavigationModeSwitchRule.Mode.ZERO_BUTTON; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeTrue; @@ -32,8 +31,8 @@ import androidx.test.runner.AndroidJUnit4; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.tapl.LauncherInstrumentation.TrackpadGestureType; import com.android.launcher3.tapl.Workspace; +import com.android.launcher3.ui.AbstractLauncherUiTest; import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape; -import com.android.launcher3.ui.TaplTestsLauncher3; import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch; import org.junit.After; @@ -51,7 +50,7 @@ public class TaplTestsTrackpad extends AbstractQuickStepTest { @Before public void setUp() throws Exception { super.setUp(); - TaplTestsLauncher3.initialize(this); + AbstractLauncherUiTest.initialize(this); } @After @@ -87,7 +86,7 @@ public class TaplTestsTrackpad extends AbstractQuickStepTest { mLauncher.setTrackpadGestureType(TrackpadGestureType.THREE_FINGER); startTestActivity(2); - mLauncher.pressBack(); + mLauncher.getLaunchedAppState().pressBackToWorkspace(); } finally { instrumentation.getUiAutomation().dropShellPermissionIdentity(); } diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsTransientTaskbar.java b/quickstep/tests/src/com/android/quickstep/TaplTestsTransientTaskbar.java index 3869bf7642..7109bbff54 100644 --- a/quickstep/tests/src/com/android/quickstep/TaplTestsTransientTaskbar.java +++ b/quickstep/tests/src/com/android/quickstep/TaplTestsTransientTaskbar.java @@ -15,13 +15,16 @@ */ package com.android.quickstep; -import static com.android.launcher3.config.FeatureFlags.ENABLE_CURSOR_HOVER_STATES; +import static com.android.launcher3.Flags.enableCursorHoverStates; +import static com.android.launcher3.util.TestConstants.AppNames.TEST_APP_NAME; import static com.android.quickstep.TaskbarModeSwitchRule.Mode.TRANSIENT; +import static org.junit.Assume.assumeTrue; + import androidx.test.filters.LargeTest; import androidx.test.runner.AndroidJUnit4; -import com.android.launcher3.util.TestUtil; +import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape; import com.android.quickstep.TaskbarModeSwitchRule.TaskbarModeSwitch; import org.junit.Test; @@ -34,44 +37,40 @@ public class TaplTestsTransientTaskbar extends AbstractTaplTestsTaskbar { @Test @TaskbarModeSwitch(mode = TRANSIENT) public void testShowTaskbarUnstashHintOnHover() { - try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_CURSOR_HOVER_STATES, true)) { - getTaskbar().getAppIcon(TEST_APP_NAME).launch(TEST_APP_PACKAGE); - mLauncher.getLaunchedAppState().hoverToShowTaskbarUnstashHint(); - } catch (Exception e) { - throw new RuntimeException(e); - } + assumeTrue(enableCursorHoverStates()); + getTaskbar().getAppIcon(TEST_APP_NAME).launch(TEST_APP_PACKAGE); + mLauncher.getLaunchedAppState().hoverToShowTaskbarUnstashHint(); } @Test @TaskbarModeSwitch(mode = TRANSIENT) public void testUnstashTaskbarOnScreenBottomEdgeHover() { - try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_CURSOR_HOVER_STATES, true)) { - getTaskbar().getAppIcon(TEST_APP_NAME).launch(TEST_APP_PACKAGE); - mLauncher.getLaunchedAppState().hoverScreenBottomEdgeToUnstashTaskbar(); - } catch (Exception e) { - throw new RuntimeException(e); - } + assumeTrue(enableCursorHoverStates()); + getTaskbar().getAppIcon(TEST_APP_NAME).launch(TEST_APP_PACKAGE); + mLauncher.getLaunchedAppState().hoverScreenBottomEdgeToUnstashTaskbar(); } @Test @TaskbarModeSwitch(mode = TRANSIENT) public void testHoverBelowHintedTaskbarToUnstash() { - try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_CURSOR_HOVER_STATES, true)) { - getTaskbar().getAppIcon(TEST_APP_NAME).launch(TEST_APP_PACKAGE); - mLauncher.getLaunchedAppState().hoverBelowHintedTaskbarToUnstash(); - } catch (Exception e) { - throw new RuntimeException(e); - } + assumeTrue(enableCursorHoverStates()); + getTaskbar().getAppIcon(TEST_APP_NAME).launch(TEST_APP_PACKAGE); + mLauncher.getLaunchedAppState().hoverBelowHintedTaskbarToUnstash(); + } + + @Test + @TaskbarModeSwitch(mode = TRANSIENT) + public void testClickHoveredTaskbarToGoHome() throws Exception { + assumeTrue(enableCursorHoverStates()); + getTaskbar().getAppIcon(TEST_APP_NAME).launch(TEST_APP_PACKAGE); + mLauncher.getLaunchedAppState().clickStashedTaskbarToGoHome(); } @Test @TaskbarModeSwitch(mode = TRANSIENT) - public void testClickHoveredTaskbarToGoHome() { - try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_CURSOR_HOVER_STATES, true)) { - getTaskbar().getAppIcon(TEST_APP_NAME).launch(TEST_APP_PACKAGE); - mLauncher.getLaunchedAppState().clickStashedTaskbarToGoHome(); - } catch (Exception e) { - throw new RuntimeException(e); - } + @PortraitLandscape + public void testSwipeToStashAndUnstash() { + getTaskbar().swipeDownToStash(); + mLauncher.getLaunchedAppState().swipeUpToUnstashTaskbar(); } } diff --git a/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java b/quickstep/tests/src/com/android/quickstep/TaplViewInflationDuringSwipeUp.java index 8cc84875e0..f76e17aa27 100644 --- a/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java +++ b/quickstep/tests/src/com/android/quickstep/TaplViewInflationDuringSwipeUp.java @@ -56,7 +56,7 @@ import com.android.launcher3.tapl.LaunchedAppState; import com.android.launcher3.testcomponent.ListViewService; import com.android.launcher3.testcomponent.ListViewService.SimpleViewsFactory; import com.android.launcher3.testcomponent.TestCommandReceiver; -import com.android.launcher3.ui.TaplTestsLauncher3; +import com.android.launcher3.ui.AbstractLauncherUiTest; import com.android.launcher3.ui.TestViewHelpers; import com.android.launcher3.util.Executors; import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; @@ -85,7 +85,7 @@ import java.util.function.IntConsumer; */ @LargeTest @RunWith(AndroidJUnit4.class) -public class ViewInflationDuringSwipeUp extends AbstractQuickStepTest { +public class TaplViewInflationDuringSwipeUp extends AbstractQuickStepTest { private SparseArray<ViewConfiguration> mConfigMap; private InitTracker mInitTracker; @@ -102,7 +102,7 @@ public class ViewInflationDuringSwipeUp extends AbstractQuickStepTest { // is started only after starting another app. startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR)); - TaplTestsLauncher3.initialize(this); + AbstractLauncherUiTest.initialize(this); mModel = LauncherAppState.getInstance(mTargetContext).getModel(); Executors.MODEL_EXECUTOR.submit(mModel.getModelDbController()::createEmptyDB).get(); diff --git a/quickstep/tests/src/com/android/quickstep/TaskThumbnailCacheTest.java b/quickstep/tests/src/com/android/quickstep/TaskThumbnailCacheTest.java new file mode 100644 index 0000000000..4e04261e5c --- /dev/null +++ b/quickstep/tests/src/com/android/quickstep/TaskThumbnailCacheTest.java @@ -0,0 +1,95 @@ +/* + * 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.quickstep; + +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.res.Resources; + +import androidx.test.filters.SmallTest; + +import com.android.launcher3.R; +import com.android.quickstep.util.TaskKeyCache; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.concurrent.Executor; + +@SmallTest +public class TaskThumbnailCacheTest { + @Mock + private Context mContext; + + @Mock + private Resources mResource; + + @Mock + private TaskKeyCache mTaskKeyCache; + + @Before + public void setup() throws NoSuchFieldException { + MockitoAnnotations.initMocks(this); + when(mContext.getResources()).thenReturn(mResource); + } + + @Test + public void increaseCacheSize() { + // Mock a cache size increase from 3 to 8 + when(mTaskKeyCache.getMaxSize()).thenReturn(3); + when(mResource.getInteger((R.integer.recentsThumbnailCacheSize))).thenReturn(8); + TaskThumbnailCache thumbnailCache = new TaskThumbnailCache(mContext, mock(Executor.class), + mTaskKeyCache); + + // Preload is needed when increasing size + assertTrue(thumbnailCache.updateCacheSizeAndRemoveExcess()); + verify(mTaskKeyCache, times(1)).updateCacheSizeAndRemoveExcess(8); + } + + @Test + public void decreaseCacheSize() { + // Mock a cache size decrease from 8 to 3 + when(mTaskKeyCache.getMaxSize()).thenReturn(8); + when(mResource.getInteger((R.integer.recentsThumbnailCacheSize))).thenReturn(3); + TaskThumbnailCache thumbnailCache = new TaskThumbnailCache(mContext, mock(Executor.class), + mTaskKeyCache); + // Preload is not needed when decreasing size + assertFalse(thumbnailCache.updateCacheSizeAndRemoveExcess()); + verify(mTaskKeyCache, times(1)).updateCacheSizeAndRemoveExcess(3); + } + + @Test + public void keepSameCacheSize() { + when(mTaskKeyCache.getMaxSize()).thenReturn(3); + when(mResource.getInteger((R.integer.recentsThumbnailCacheSize))).thenReturn(3); + TaskThumbnailCache thumbnailCache = new TaskThumbnailCache(mContext, mock(Executor.class), + mTaskKeyCache); + // Preload is not needed when it has the same cache size + assertFalse(thumbnailCache.updateCacheSizeAndRemoveExcess()); + verify(mTaskKeyCache, never()).updateCacheSizeAndRemoveExcess(anyInt()); + } +} diff --git a/quickstep/tests/src/com/android/quickstep/TaskViewTest.java b/quickstep/tests/src/com/android/quickstep/TaskViewTest.java new file mode 100644 index 0000000000..d744194d42 --- /dev/null +++ b/quickstep/tests/src/com/android/quickstep/TaskViewTest.java @@ -0,0 +1,118 @@ +/* + * 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.quickstep; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.pm.ApplicationInfo; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Rect; +import android.util.DisplayMetrics; +import android.view.MotionEvent; + +import androidx.test.filters.SmallTest; + +import com.android.launcher3.statemanager.StatefulActivity; +import com.android.quickstep.util.BorderAnimator; +import com.android.quickstep.views.TaskView; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +public class TaskViewTest { + + @Mock + private StatefulActivity mContext; + @Mock + private Resources mResource; + @Mock + private BorderAnimator mHoverAnimator; + @Mock + private BorderAnimator mFocusAnimator; + private TaskView mTaskView; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + when(mResource.getDisplayMetrics()).thenReturn(mock(DisplayMetrics.class)); + when(mResource.getConfiguration()).thenReturn(new Configuration()); + + when(mContext.getResources()).thenReturn(mResource); + when(mContext.getTheme()).thenReturn(mock(Resources.Theme.class)); + when(mContext.getApplicationInfo()).thenReturn(mock(ApplicationInfo.class)); + when(mContext.obtainStyledAttributes(any(), any(), anyInt(), anyInt())).thenReturn( + mock(TypedArray.class)); + + mTaskView = new TaskView(mContext, null, 0, 0, mFocusAnimator, mHoverAnimator); + } + + @Test + public void notShowBorderOnBorderDisabled() { + mTaskView.setBorderEnabled(/* enabled= */ false); + MotionEvent event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_HOVER_ENTER, 0.0f, 0.0f, 0); + mTaskView.onHoverEvent(MotionEvent.obtain(event)); + verify(mHoverAnimator, never()).setBorderVisibility(/* visible= */ true, /* animated= */ + true); + + mTaskView.onFocusChanged(false, 0, new Rect()); + verify(mFocusAnimator, never()).setBorderVisibility(/* visible= */ true, /* animated= */ + true); + } + + @Test + public void showBorderOnBorderEnabled() { + mTaskView.setBorderEnabled(/* enabled= */ true); + MotionEvent event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_HOVER_ENTER, 0.0f, 0.0f, 0); + mTaskView.onHoverEvent(MotionEvent.obtain(event)); + verify(mHoverAnimator, times(1)).setBorderVisibility(/* visible= */ true, /* animated= */ + true); + mTaskView.onFocusChanged(true, 0, new Rect()); + verify(mFocusAnimator, times(1)).setBorderVisibility(/* visible= */ true, /* animated= */ + true); + } + + @Test + public void hideBorderOnBorderDisabled() { + mTaskView.setBorderEnabled(/* enabled= */ false); + verify(mHoverAnimator, times(1)).setBorderVisibility(/* visible= */ false, /* animated= */ + true); + verify(mFocusAnimator, times(1)).setBorderVisibility(/* visible= */ false, /* animated= */ + true); + } + + @Test + public void notShowBorderByDefault() { + MotionEvent event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_HOVER_ENTER, 0.0f, 0.0f, 0); + mTaskView.onHoverEvent(MotionEvent.obtain(event)); + verify(mHoverAnimator, never()).setBorderVisibility(/* visible= */ false, /* animated= */ + true); + mTaskView.onFocusChanged(true, 0, new Rect()); + verify(mHoverAnimator, never()).setBorderVisibility(/* visible= */ false, /* animated= */ + true); + } +} diff --git a/quickstep/tests/src/com/android/quickstep/TaskbarModeSwitchRule.java b/quickstep/tests/src/com/android/quickstep/TaskbarModeSwitchRule.java index 9e41f74186..e5657fb644 100644 --- a/quickstep/tests/src/com/android/quickstep/TaskbarModeSwitchRule.java +++ b/quickstep/tests/src/com/android/quickstep/TaskbarModeSwitchRule.java @@ -123,7 +123,7 @@ public class TaskbarModeSwitchRule implements TestRule { assertTrue(launcher, "Couldn't set taskbar=" + expectTransientTaskbar, isTaskbarTransientMode(context) == expectTransientTaskbar, description); - AbstractLauncherUiTest.checkDetectedLeaks(launcher); + AbstractLauncherUiTest.checkDetectedLeaks(launcher, true); } private static void assertTrue(LauncherInstrumentation launcher, String message, diff --git a/quickstep/tests/src/com/android/quickstep/taskbar/controllers/TaskbarPinningControllerTest.kt b/quickstep/tests/src/com/android/quickstep/taskbar/controllers/TaskbarPinningControllerTest.kt new file mode 100644 index 0000000000..dbe4624e45 --- /dev/null +++ b/quickstep/tests/src/com/android/quickstep/taskbar/controllers/TaskbarPinningControllerTest.kt @@ -0,0 +1,202 @@ +/* + * 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.quickstep.taskbar.controllers + +import android.animation.AnimatorSet +import android.animation.ObjectAnimator +import android.view.View +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.launcher3.LauncherPrefs +import com.android.launcher3.LauncherPrefs.Companion.TASKBAR_PINNING +import com.android.launcher3.logging.StatsLogManager +import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_DIVIDER_MENU_CLOSE +import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_DIVIDER_MENU_OPEN +import com.android.launcher3.taskbar.TaskbarActivityContext +import com.android.launcher3.taskbar.TaskbarBaseTestCase +import com.android.launcher3.taskbar.TaskbarDividerPopupView +import com.android.launcher3.taskbar.TaskbarDragLayer +import com.android.launcher3.taskbar.TaskbarPinningController +import com.android.launcher3.taskbar.TaskbarPinningController.Companion.PINNING_PERSISTENT +import com.android.launcher3.taskbar.TaskbarPinningController.Companion.PINNING_TRANSIENT +import com.android.launcher3.taskbar.TaskbarSharedState +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.any +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.doNothing +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.never +import org.mockito.kotlin.spy +import org.mockito.kotlin.times +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever + +@SmallTest +@RunWith(AndroidJUnit4::class) +class TaskbarPinningControllerTest : TaskbarBaseTestCase() { + private val taskbarDragLayer = mock<TaskbarDragLayer>() + private val taskbarSharedState = mock<TaskbarSharedState>() + private val launcherPrefs = mock<LauncherPrefs> { on { get(TASKBAR_PINNING) } doReturn false } + private val statsLogger = mock<StatsLogManager.StatsLogger>() + private val statsLogManager = mock<StatsLogManager> { on { logger() } doReturn statsLogger } + private lateinit var pinningController: TaskbarPinningController + + @Before + override fun setup() { + super.setup() + whenever(taskbarActivityContext.launcherPrefs).thenReturn(launcherPrefs) + whenever(taskbarActivityContext.dragLayer).thenReturn(taskbarDragLayer) + whenever(taskbarActivityContext.statsLogManager).thenReturn(statsLogManager) + pinningController = spy(TaskbarPinningController(taskbarActivityContext)) + pinningController.init(taskbarControllers, taskbarSharedState) + } + + @Test + fun testOnCloseCallback_whenClosingPopupView_shouldLogStatsForClosingPopupMenu() { + pinningController.onCloseCallback(false) + verify(statsLogger, times(1)).log(LAUNCHER_TASKBAR_DIVIDER_MENU_CLOSE) + } + + @Test + fun testOnCloseCallback_whenClosingPopupView_shouldPostVisibilityChangedToDragLayer() { + val argumentCaptor = argumentCaptor<Runnable>() + pinningController.onCloseCallback(false) + verify(taskbarDragLayer, times(1)).post(argumentCaptor.capture()) + + val runnable = argumentCaptor.lastValue + assertThat(runnable).isNotNull() + + runnable.run() + verify(taskbarActivityContext, times(1)).onPopupVisibilityChanged(false) + } + + @Test + fun testOnCloseCallback_whenPreferenceUnchanged_shouldNotAnimateTaskbarPinning() { + pinningController.onCloseCallback(false) + verify(taskbarSharedState, never()).taskbarWasPinned = true + verify(pinningController, never()).animateTaskbarPinning(any()) + } + + @Test + fun testOnCloseCallback_whenPreferenceChanged_shouldAnimateToPinnedTaskbar() { + whenever(launcherPrefs.get(TASKBAR_PINNING)).thenReturn(false) + doNothing().whenever(pinningController).animateTaskbarPinning(any()) + + pinningController.onCloseCallback(true) + + verify(taskbarSharedState, times(1)).taskbarWasPinned = false + verify(pinningController, times(1)).animateTaskbarPinning(PINNING_PERSISTENT) + } + + @Test + fun testOnCloseCallback_whenPreferenceChanged_shouldAnimateToTransientTaskbar() { + whenever(launcherPrefs.get(TASKBAR_PINNING)).thenReturn(true) + doNothing().whenever(pinningController).animateTaskbarPinning(any()) + + pinningController.onCloseCallback(true) + + verify(taskbarSharedState, times(1)).taskbarWasPinned = true + verify(pinningController, times(1)).animateTaskbarPinning(PINNING_TRANSIENT) + } + + @Test + fun testShowPinningView_whenShowingPinningView_shouldSetTaskbarWindowFullscreenAndPostRunnableToView() { + val popupView = + mock<TaskbarDividerPopupView<TaskbarActivityContext>> { + on { requestFocus() } doReturn true + } + val view = mock<View>() + val argumentCaptor = argumentCaptor<Runnable>() + doReturn(popupView).whenever(pinningController).getPopupView(view) + + pinningController.showPinningView(view) + + verify(view, times(1)).post(argumentCaptor.capture()) + + val runnable = argumentCaptor.lastValue + assertThat(runnable).isNotNull() + runnable.run() + + verify(pinningController, times(1)).getPopupView(view) + verify(popupView, times(1)).requestFocus() + verify(popupView, times(1)).onCloseCallback = any() + verify(taskbarActivityContext, times(1)).onPopupVisibilityChanged(true) + verify(popupView, times(1)).show() + verify(statsLogger, times(1)).log(LAUNCHER_TASKBAR_DIVIDER_MENU_OPEN) + } + + @Test + fun testAnimateTaskbarPinning_whenAnimationEnds_shouldInvokeCallbackDoOnEnd() { + val animatorSet = spy(AnimatorSet()) + doReturn(animatorSet) + .whenever(pinningController) + .getAnimatorSetForTaskbarPinningAnimation(PINNING_PERSISTENT) + doNothing().whenever(animatorSet).start() + pinningController.animateTaskbarPinning(PINNING_PERSISTENT) + animatorSet.listeners[0].onAnimationEnd(ObjectAnimator()) + verify(pinningController, times(1)).recreateTaskbarAndUpdatePinningValue() + } + + @Test + fun testAnimateTaskbarPinning_whenAnimatingToPersistentTaskbar_shouldAnimateToPinnedTaskbar() { + val animatorSet = spy(AnimatorSet()) + doReturn(animatorSet) + .whenever(pinningController) + .getAnimatorSetForTaskbarPinningAnimation(PINNING_PERSISTENT) + doNothing().whenever(animatorSet).start() + pinningController.animateTaskbarPinning(PINNING_PERSISTENT) + + verify(taskbarOverlayController, times(1)).hideWindow() + verify(pinningController, times(1)) + .getAnimatorSetForTaskbarPinningAnimation(PINNING_PERSISTENT) + verify(taskbarViewController, times(1)) + .animateAwayNotificationDotsDuringTaskbarPinningAnimation() + verify(taskbarDragLayer, times(1)).setAnimatingTaskbarPinning(true) + assertThat(pinningController.isAnimatingTaskbarPinning).isTrue() + assertThat(animatorSet.listeners).isNotNull() + } + + @Test + fun testAnimateTaskbarPinning_whenAnimatingToTransientTaskbar_shouldAnimateToTransientTaskbar() { + val animatorSet = spy(AnimatorSet()) + doReturn(animatorSet) + .whenever(pinningController) + .getAnimatorSetForTaskbarPinningAnimation(PINNING_TRANSIENT) + doNothing().whenever(animatorSet).start() + pinningController.animateTaskbarPinning(PINNING_TRANSIENT) + + verify(taskbarOverlayController, times(1)).hideWindow() + verify(pinningController, times(1)) + .getAnimatorSetForTaskbarPinningAnimation(PINNING_TRANSIENT) + verify(taskbarDragLayer, times(1)).setAnimatingTaskbarPinning(true) + assertThat(pinningController.isAnimatingTaskbarPinning).isTrue() + verify(taskbarViewController, times(1)) + .animateAwayNotificationDotsDuringTaskbarPinningAnimation() + assertThat(animatorSet.listeners).isNotNull() + } + + @Test + fun testRecreateTaskbarAndUpdatePinningValue_whenAnimationEnds_shouldUpdateTaskbarPinningLauncherPref() { + pinningController.recreateTaskbarAndUpdatePinningValue() + verify(taskbarDragLayer, times(1)).setAnimatingTaskbarPinning(false) + assertThat(pinningController.isAnimatingTaskbarPinning).isFalse() + verify(launcherPrefs, times(1)).put(TASKBAR_PINNING, true) + } +} diff --git a/quickstep/tests/src/com/android/quickstep/util/AppPairsControllerTest.kt b/quickstep/tests/src/com/android/quickstep/util/AppPairsControllerTest.kt new file mode 100644 index 0000000000..1723844843 --- /dev/null +++ b/quickstep/tests/src/com/android/quickstep/util/AppPairsControllerTest.kt @@ -0,0 +1,147 @@ +/* + * 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.quickstep.util + +import android.content.Context +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.launcher3.logging.StatsLogManager +import com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT +import com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT +import com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_30_70 +import com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50 +import com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_70_30 +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.MockitoAnnotations + +@RunWith(AndroidJUnit4::class) +class AppPairsControllerTest { + @Mock lateinit var context: Context + @Mock lateinit var splitSelectStateController: SplitSelectStateController + @Mock lateinit var statsLogManager: StatsLogManager + + private lateinit var appPairsController: AppPairsController + + private val left30: Int by lazy { + appPairsController.encodeRank(STAGE_POSITION_TOP_OR_LEFT, SNAP_TO_30_70) + } + private val left50: Int by lazy { + appPairsController.encodeRank(STAGE_POSITION_TOP_OR_LEFT, SNAP_TO_50_50) + } + private val left70: Int by lazy { + appPairsController.encodeRank(STAGE_POSITION_TOP_OR_LEFT, SNAP_TO_70_30) + } + private val right30: Int by lazy { + appPairsController.encodeRank(STAGE_POSITION_BOTTOM_OR_RIGHT, SNAP_TO_30_70) + } + private val right50: Int by lazy { + appPairsController.encodeRank(STAGE_POSITION_BOTTOM_OR_RIGHT, SNAP_TO_50_50) + } + private val right70: Int by lazy { + appPairsController.encodeRank(STAGE_POSITION_BOTTOM_OR_RIGHT, SNAP_TO_70_30) + } + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + appPairsController = + AppPairsController(context, splitSelectStateController, statsLogManager) + } + + @Test + fun shouldEncodeRankCorrectly() { + assertEquals("left + 30-70 should encode as 0 (0b0)", 0, left30) + assertEquals("left + 50-50 should encode as 1 (0b1)", 1, left50) + assertEquals("left + 70-30 should encode as 2 (0b10)", 2, left70) + // See AppPairsController#BITMASK_SIZE and BITMASK_FOR_SNAP_POSITION for context + assertEquals("right + 30-70 should encode as 1 followed by 16 0s", 1 shl 16, right30) + assertEquals("right + 50-50 should encode as the above value + 1", (1 shl 16) + 1, right50) + assertEquals("right + 70-30 should encode as the above value + 2", (1 shl 16) + 2, right70) + } + + @Test + fun shouldDecodeRankCorrectly() { + assertEquals( + "left + 30-70 should decode to left", + STAGE_POSITION_TOP_OR_LEFT, + AppPairsController.convertRankToStagePosition(left30), + ) + assertEquals( + "left + 30-70 should decode to 30-70", + SNAP_TO_30_70, + AppPairsController.convertRankToSnapPosition(left30), + ) + + assertEquals( + "left + 50-50 should decode to left", + STAGE_POSITION_TOP_OR_LEFT, + AppPairsController.convertRankToStagePosition(left50), + ) + assertEquals( + "left + 50-50 should decode to 50-50", + SNAP_TO_50_50, + AppPairsController.convertRankToSnapPosition(left50), + ) + + assertEquals( + "left + 70-30 should decode to left", + STAGE_POSITION_TOP_OR_LEFT, + AppPairsController.convertRankToStagePosition(left70), + ) + assertEquals( + "left + 70-30 should decode to 70-30", + SNAP_TO_70_30, + AppPairsController.convertRankToSnapPosition(left70), + ) + + assertEquals( + "right + 30-70 should decode to right", + STAGE_POSITION_BOTTOM_OR_RIGHT, + AppPairsController.convertRankToStagePosition(right30), + ) + assertEquals( + "right + 30-70 should decode to 30-70", + SNAP_TO_30_70, + AppPairsController.convertRankToSnapPosition(right30), + ) + + assertEquals( + "right + 50-50 should decode to right", + STAGE_POSITION_BOTTOM_OR_RIGHT, + AppPairsController.convertRankToStagePosition(right50), + ) + assertEquals( + "right + 50-50 should decode to 50-50", + SNAP_TO_50_50, + AppPairsController.convertRankToSnapPosition(right50), + ) + + assertEquals( + "right + 70-30 should decode to right", + STAGE_POSITION_BOTTOM_OR_RIGHT, + AppPairsController.convertRankToStagePosition(right70), + ) + assertEquals( + "right + 70-30 should decode to 70-30", + SNAP_TO_70_30, + AppPairsController.convertRankToSnapPosition(right70), + ) + } +} diff --git a/quickstep/tests/src/com/android/quickstep/util/SplitAnimationControllerTest.kt b/quickstep/tests/src/com/android/quickstep/util/SplitAnimationControllerTest.kt index 7e07b813a7..86018b1783 100644 --- a/quickstep/tests/src/com/android/quickstep/util/SplitAnimationControllerTest.kt +++ b/quickstep/tests/src/com/android/quickstep/util/SplitAnimationControllerTest.kt @@ -19,8 +19,13 @@ package com.android.quickstep.util import android.graphics.Bitmap import android.graphics.drawable.Drawable +import android.view.SurfaceControl.Transaction import android.view.View +import android.window.TransitionInfo import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.launcher3.apppairs.AppPairIcon +import com.android.launcher3.statehandlers.DepthController +import com.android.launcher3.statemanager.StateManager import com.android.launcher3.util.SplitConfigurationOptions import com.android.quickstep.views.GroupedTaskView import com.android.quickstep.views.IconView @@ -32,39 +37,48 @@ import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mock -import org.mockito.MockitoAnnotations -import org.mockito.Mockito.`when` as whenever +import org.mockito.kotlin.any +import org.mockito.kotlin.doNothing +import org.mockito.kotlin.mock +import org.mockito.kotlin.spy +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever @RunWith(AndroidJUnit4::class) class SplitAnimationControllerTest { private val taskId = 9 + private val taskId2 = 10 - @Mock lateinit var mockSplitSelectStateController: SplitSelectStateController + private val mockSplitSelectStateController: SplitSelectStateController = mock() // TaskView - @Mock lateinit var mockTaskView: TaskView - @Mock lateinit var mockThumbnailView: TaskThumbnailView - @Mock lateinit var mockBitmap: Bitmap - @Mock lateinit var mockIconView: IconView - @Mock lateinit var mockTaskViewDrawable: Drawable + private val mockTaskView: TaskView = mock() + private val mockThumbnailView: TaskThumbnailView = mock() + private val mockBitmap: Bitmap = mock() + private val mockIconView: IconView = mock() + private val mockTaskViewDrawable: Drawable = mock() // GroupedTaskView - @Mock lateinit var mockGroupedTaskView: GroupedTaskView - @Mock lateinit var mockTask: Task - @Mock lateinit var mockTaskKey: Task.TaskKey - @Mock lateinit var mockTaskIdAttributeContainer: TaskIdAttributeContainer + private val mockGroupedTaskView: GroupedTaskView = mock() + private val mockTask: Task = mock() + private val mockTaskKey: Task.TaskKey = mock() + private val mockTaskIdAttributeContainer: TaskIdAttributeContainer = mock() + // AppPairIcon + private val mockAppPairIcon: AppPairIcon = mock() // SplitSelectSource - @Mock lateinit var splitSelectSource: SplitConfigurationOptions.SplitSelectSource - @Mock lateinit var mockSplitSourceDrawable: Drawable - @Mock lateinit var mockSplitSourceView: View + private val splitSelectSource: SplitConfigurationOptions.SplitSelectSource = mock() + private val mockSplitSourceDrawable: Drawable = mock() + private val mockSplitSourceView: View = mock() + + private val stateManager: StateManager<*> = mock() + private val depthController: DepthController = mock() + private val transitionInfo: TransitionInfo = mock() + private val transaction: Transaction = mock() lateinit var splitAnimationController: SplitAnimationController @Before fun setup() { - MockitoAnnotations.initMocks(this) - whenever(mockTaskView.thumbnail).thenReturn(mockThumbnailView) whenever(mockThumbnailView.thumbnail).thenReturn(mockBitmap) whenever(mockTaskView.iconView).thenReturn(mockIconView) @@ -85,12 +99,14 @@ class SplitAnimationControllerTest { // Missing taskView icon whenever(mockIconView.drawable).thenReturn(null) - val splitAnimInitProps : SplitAnimationController.Companion.SplitAnimInitProps = - splitAnimationController.getFirstAnimInitViews( - { mockTaskView }, { splitSelectSource }) + val splitAnimInitProps: SplitAnimationController.Companion.SplitAnimInitProps = + splitAnimationController.getFirstAnimInitViews({ mockTaskView }, { splitSelectSource }) - assertEquals("Did not fallback to use splitSource icon drawable", - mockSplitSourceDrawable, splitAnimInitProps.iconDrawable) + assertEquals( + "Did not fallback to use splitSource icon drawable", + mockSplitSourceDrawable, + splitAnimInitProps.iconDrawable + ) } @Test @@ -99,12 +115,14 @@ class SplitAnimationControllerTest { whenever(mockSplitSelectStateController.isAnimateCurrentTaskDismissal).thenReturn(true) whenever(mockSplitSelectStateController.isDismissingFromSplitPair).thenReturn(false) - val splitAnimInitProps : SplitAnimationController.Companion.SplitAnimInitProps = - splitAnimationController.getFirstAnimInitViews( - { mockTaskView }, { splitSelectSource }) + val splitAnimInitProps: SplitAnimationController.Companion.SplitAnimInitProps = + splitAnimationController.getFirstAnimInitViews({ mockTaskView }, { splitSelectSource }) - assertEquals("Did not use taskView icon drawable", mockTaskViewDrawable, - splitAnimInitProps.iconDrawable) + assertEquals( + "Did not use taskView icon drawable", + mockTaskViewDrawable, + splitAnimInitProps.iconDrawable + ) } @Test @@ -116,12 +134,14 @@ class SplitAnimationControllerTest { // Set split source to null whenever(splitSelectSource.drawable).thenReturn(null) - val splitAnimInitProps : SplitAnimationController.Companion.SplitAnimInitProps = - splitAnimationController.getFirstAnimInitViews( - { mockTaskView }, { splitSelectSource }) + val splitAnimInitProps: SplitAnimationController.Companion.SplitAnimInitProps = + splitAnimationController.getFirstAnimInitViews({ mockTaskView }, { splitSelectSource }) - assertEquals("Did not use taskView icon drawable", mockTaskViewDrawable, - splitAnimInitProps.iconDrawable) + assertEquals( + "Did not use taskView icon drawable", + mockTaskViewDrawable, + splitAnimInitProps.iconDrawable + ) } @Test @@ -130,12 +150,14 @@ class SplitAnimationControllerTest { whenever(mockSplitSelectStateController.isAnimateCurrentTaskDismissal).thenReturn(false) whenever(mockSplitSelectStateController.isDismissingFromSplitPair).thenReturn(false) - val splitAnimInitProps : SplitAnimationController.Companion.SplitAnimInitProps = - splitAnimationController.getFirstAnimInitViews( - { mockTaskView }, { splitSelectSource }) + val splitAnimInitProps: SplitAnimationController.Companion.SplitAnimInitProps = + splitAnimationController.getFirstAnimInitViews({ mockTaskView }, { splitSelectSource }) - assertEquals("Did not use splitSource icon drawable", mockSplitSourceDrawable, - splitAnimInitProps.iconDrawable) + assertEquals( + "Did not use splitSource icon drawable", + mockSplitSourceDrawable, + splitAnimInitProps.iconDrawable + ) } @Test @@ -154,12 +176,123 @@ class SplitAnimationControllerTest { whenever(mockTaskKey.getId()).thenReturn(taskId) whenever(mockSplitSelectStateController.initialTaskId).thenReturn(taskId) whenever(mockGroupedTaskView.taskIdAttributeContainers) - .thenReturn(Array(1) { mockTaskIdAttributeContainer }) - val splitAnimInitProps : SplitAnimationController.Companion.SplitAnimInitProps = - splitAnimationController.getFirstAnimInitViews( - { mockGroupedTaskView }, { splitSelectSource }) + .thenReturn(Array(1) { mockTaskIdAttributeContainer }) + val splitAnimInitProps: SplitAnimationController.Companion.SplitAnimInitProps = + splitAnimationController.getFirstAnimInitViews( + { mockGroupedTaskView }, + { splitSelectSource } + ) + + assertEquals( + "Did not use splitSource icon drawable", + mockSplitSourceDrawable, + splitAnimInitProps.iconDrawable + ) + } + + @Test + fun playsAppropriateSplitLaunchAnimation_playsLegacyLaunchCorrectly() { + val spySplitAnimationController = spy(splitAnimationController) + doNothing() + .whenever(spySplitAnimationController) + .composeRecentsSplitLaunchAnimatorLegacy( + any(), any(), any(), any(), any(), any(), any(), any(), any()) + + spySplitAnimationController.playSplitLaunchAnimation( + mockGroupedTaskView, + null /* launchingIconView */, + taskId, + taskId2, + arrayOf() /* apps */, + arrayOf() /* wallpapers */, + arrayOf() /* nonApps */, + stateManager, + depthController, + null /* info */, + null /* t */, + {} /* finishCallback */ + ) + + verify(spySplitAnimationController) + .composeRecentsSplitLaunchAnimatorLegacy( + any(), any(), any(), any(), any(), any(), any(), any(), any()) + } + + @Test + fun playsAppropriateSplitLaunchAnimation_playsRecentsLaunchCorrectly() { + val spySplitAnimationController = spy(splitAnimationController) + doNothing() + .whenever(spySplitAnimationController) + .composeRecentsSplitLaunchAnimator(any(), any(), any(), any(), any(), any()) + + spySplitAnimationController.playSplitLaunchAnimation( + mockGroupedTaskView, + null /* launchingIconView */, + taskId, + taskId2, + null /* apps */, + null /* wallpapers */, + null /* nonApps */, + stateManager, + depthController, + transitionInfo, + transaction, + {} /* finishCallback */ + ) + + verify(spySplitAnimationController) + .composeRecentsSplitLaunchAnimator(any(), any(), any(), any(), any(), any()) + } + + @Test + fun playsAppropriateSplitLaunchAnimation_playsIconLaunchCorrectly() { + val spySplitAnimationController = spy(splitAnimationController) + doNothing() + .whenever(spySplitAnimationController) + .composeIconSplitLaunchAnimator(any(), any(), any(), any(), any(), any()) + + spySplitAnimationController.playSplitLaunchAnimation( + null /* launchingTaskView */, + mockAppPairIcon, + taskId, + taskId2, + null /* apps */, + null /* wallpapers */, + null /* nonApps */, + stateManager, + depthController, + transitionInfo, + transaction, + {} /* finishCallback */ + ) + + verify(spySplitAnimationController) + .composeIconSplitLaunchAnimator(any(), any(), any(), any(), any(), any()) + } + + @Test + fun playsAppropriateSplitLaunchAnimation_playsFadeInLaunchCorrectly() { + val spySplitAnimationController = spy(splitAnimationController) + doNothing() + .whenever(spySplitAnimationController) + .composeFadeInSplitLaunchAnimator(any(), any(), any(), any(), any()) + + spySplitAnimationController.playSplitLaunchAnimation( + null /* launchingTaskView */, + null /* launchingIconView */, + taskId, + taskId2, + null /* apps */, + null /* wallpapers */, + null /* nonApps */, + stateManager, + depthController, + transitionInfo, + transaction, + {} /* finishCallback */ + ) - assertEquals("Did not use splitSource icon drawable", mockSplitSourceDrawable, - splitAnimInitProps.iconDrawable) + verify(spySplitAnimationController) + .composeFadeInSplitLaunchAnimator(any(), any(), any(), any(), any()) } -}
\ No newline at end of file +} diff --git a/quickstep/tests/src/com/android/quickstep/util/SplitSelectDataHolderTest.kt b/quickstep/tests/src/com/android/quickstep/util/SplitSelectDataHolderTest.kt index fc767fae72..c6c5be4c60 100644 --- a/quickstep/tests/src/com/android/quickstep/util/SplitSelectDataHolderTest.kt +++ b/quickstep/tests/src/com/android/quickstep/util/SplitSelectDataHolderTest.kt @@ -381,7 +381,7 @@ class SplitSelectDataHolderTest { } @Test - fun clearState() { + fun clearState_task() { splitSelectDataHolder.setInitialTaskSelect( sampleTaskInfo, STAGE_POSITION_TOP_OR_LEFT, @@ -392,4 +392,18 @@ class SplitSelectDataHolderTest { splitSelectDataHolder.resetState() assertFalse(splitSelectDataHolder.isSplitSelectActive()) } + + @Test + fun clearState_intent() { + splitSelectDataHolder.setInitialTaskSelect( + sampleIntent, + STAGE_POSITION_TOP_OR_LEFT, + sampleItemInfo, + null, + INVALID_TASK_ID + ) + splitSelectDataHolder.setSecondTask(sampleIntent, sampleUser) + splitSelectDataHolder.resetState() + assertFalse(splitSelectDataHolder.isSplitSelectActive()) + } } diff --git a/quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt b/quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt index 69109c2c45..f41ac4809b 100644 --- a/quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt +++ b/quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt @@ -20,7 +20,6 @@ package com.android.quickstep.util import android.app.ActivityManager import android.app.PendingIntent import android.content.ComponentName -import android.content.Context import android.content.Intent import android.graphics.Rect import android.os.Handler @@ -31,12 +30,13 @@ import com.android.launcher3.logging.StatsLogManager import com.android.launcher3.model.data.ItemInfo import com.android.launcher3.statehandlers.DepthController import com.android.launcher3.statemanager.StateManager +import com.android.launcher3.statemanager.StatefulActivity import com.android.launcher3.util.ComponentKey import com.android.launcher3.util.SplitConfigurationOptions -import com.android.launcher3.util.withArgCaptor import com.android.quickstep.RecentsModel import com.android.quickstep.SystemUiProxy import com.android.systemui.shared.recents.model.Task +import com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50 import java.util.function.Consumer import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse @@ -45,22 +45,22 @@ import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mock -import org.mockito.Mockito.verify -import org.mockito.Mockito.`when` -import org.mockito.MockitoAnnotations +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever @RunWith(AndroidJUnit4::class) class SplitSelectStateControllerTest { - @Mock lateinit var systemUiProxy: SystemUiProxy - @Mock lateinit var depthController: DepthController - @Mock lateinit var statsLogManager: StatsLogManager - @Mock lateinit var stateManager: StateManager<LauncherState> - @Mock lateinit var handler: Handler - @Mock lateinit var context: Context - @Mock lateinit var recentsModel: RecentsModel - @Mock lateinit var pendingIntent: PendingIntent + private val systemUiProxy: SystemUiProxy = mock() + private val depthController: DepthController = mock() + private val statsLogManager: StatsLogManager = mock() + private val stateManager: StateManager<LauncherState> = mock() + private val handler: Handler = mock() + private val context: StatefulActivity<*> = mock() + private val recentsModel: RecentsModel = mock() + private val pendingIntent: PendingIntent = mock() lateinit var splitSelectStateController: SplitSelectStateController @@ -68,11 +68,12 @@ class SplitSelectStateControllerTest { private val nonPrimaryUserHandle = UserHandle(ActivityManager.RunningTaskInfo().userId + 10) private var taskIdCounter = 0 - private fun getUniqueId(): Int { return ++taskIdCounter } + private fun getUniqueId(): Int { + return ++taskIdCounter + } @Before fun setup() { - MockitoAnnotations.initMocks(this) splitSelectStateController = SplitSelectStateController( context, @@ -81,7 +82,8 @@ class SplitSelectStateControllerTest { depthController, statsLogManager, systemUiProxy, - recentsModel + recentsModel, + null /*activityBackCallback*/ ) } @@ -109,13 +111,15 @@ class SplitSelectStateControllerTest { // Capture callback from recentsModel#getTasks() val consumer = - withArgCaptor<Consumer<ArrayList<GroupTask>>> { - splitSelectStateController.findLastActiveTasksAndRunCallback( - listOf(nonMatchingComponent), - taskConsumer - ) - verify(recentsModel).getTasks(capture()) - } + argumentCaptor<Consumer<ArrayList<GroupTask>>> { + splitSelectStateController.findLastActiveTasksAndRunCallback( + listOf(nonMatchingComponent), + false /* findExactPairMatch */, + taskConsumer + ) + verify(recentsModel).getTasks(capture()) + } + .lastValue // Send our mocked tasks consumer.accept(tasks) @@ -160,13 +164,15 @@ class SplitSelectStateControllerTest { // Capture callback from recentsModel#getTasks() val consumer = - withArgCaptor<Consumer<ArrayList<GroupTask>>> { - splitSelectStateController.findLastActiveTasksAndRunCallback( - listOf(matchingComponent), - taskConsumer - ) - verify(recentsModel).getTasks(capture()) - } + argumentCaptor<Consumer<ArrayList<GroupTask>>> { + splitSelectStateController.findLastActiveTasksAndRunCallback( + listOf(matchingComponent), + false /* findExactPairMatch */, + taskConsumer + ) + verify(recentsModel).getTasks(capture()) + } + .lastValue // Send our mocked tasks consumer.accept(tasks) @@ -199,13 +205,15 @@ class SplitSelectStateControllerTest { // Capture callback from recentsModel#getTasks() val consumer = - withArgCaptor<Consumer<ArrayList<GroupTask>>> { - splitSelectStateController.findLastActiveTasksAndRunCallback( - listOf(nonPrimaryUserComponent), - taskConsumer - ) - verify(recentsModel).getTasks(capture()) - } + argumentCaptor<Consumer<ArrayList<GroupTask>>> { + splitSelectStateController.findLastActiveTasksAndRunCallback( + listOf(nonPrimaryUserComponent), + false /* findExactPairMatch */, + taskConsumer + ) + verify(recentsModel).getTasks(capture()) + } + .lastValue // Send our mocked tasks consumer.accept(tasks) @@ -253,13 +261,15 @@ class SplitSelectStateControllerTest { // Capture callback from recentsModel#getTasks() val consumer = - withArgCaptor<Consumer<ArrayList<GroupTask>>> { - splitSelectStateController.findLastActiveTasksAndRunCallback( - listOf(nonPrimaryUserComponent), - taskConsumer - ) - verify(recentsModel).getTasks(capture()) - } + argumentCaptor<Consumer<ArrayList<GroupTask>>> { + splitSelectStateController.findLastActiveTasksAndRunCallback( + listOf(nonPrimaryUserComponent), + false /* findExactPairMatch */, + taskConsumer + ) + verify(recentsModel).getTasks(capture()) + } + .lastValue // Send our mocked tasks consumer.accept(tasks) @@ -304,13 +314,15 @@ class SplitSelectStateControllerTest { // Capture callback from recentsModel#getTasks() val consumer = - withArgCaptor<Consumer<ArrayList<GroupTask>>> { - splitSelectStateController.findLastActiveTasksAndRunCallback( - listOf(matchingComponent), - taskConsumer - ) - verify(recentsModel).getTasks(capture()) - } + argumentCaptor<Consumer<ArrayList<GroupTask>>> { + splitSelectStateController.findLastActiveTasksAndRunCallback( + listOf(matchingComponent), + false /* findExactPairMatch */, + taskConsumer + ) + verify(recentsModel).getTasks(capture()) + } + .lastValue // Send our mocked tasks consumer.accept(tasks) @@ -325,10 +337,7 @@ class SplitSelectStateControllerTest { ComponentKey(ComponentName(matchingPackage, matchingClass), primaryUserHandle) val groupTask1 = - generateGroupTask( - ComponentName("hotdog", "pie"), - ComponentName("pumpkin", "pie") - ) + generateGroupTask(ComponentName("hotdog", "pie"), ComponentName("pumpkin", "pie")) val groupTask2 = generateGroupTask( ComponentName("pomegranate", "juice"), @@ -359,13 +368,15 @@ class SplitSelectStateControllerTest { // Capture callback from recentsModel#getTasks() val consumer = - withArgCaptor<Consumer<ArrayList<GroupTask>>> { - splitSelectStateController.findLastActiveTasksAndRunCallback( - listOf(nonMatchingComponent, matchingComponent), - taskConsumer - ) - verify(recentsModel).getTasks(capture()) - } + argumentCaptor<Consumer<ArrayList<GroupTask>>> { + splitSelectStateController.findLastActiveTasksAndRunCallback( + listOf(nonMatchingComponent, matchingComponent), + false /* findExactPairMatch */, + taskConsumer + ) + verify(recentsModel).getTasks(capture()) + } + .lastValue // Send our mocked tasks consumer.accept(tasks) @@ -379,10 +390,7 @@ class SplitSelectStateControllerTest { ComponentKey(ComponentName(matchingPackage, matchingClass), primaryUserHandle) val groupTask1 = - generateGroupTask( - ComponentName("hotdog", "pie"), - ComponentName("pumpkin", "pie") - ) + generateGroupTask(ComponentName("hotdog", "pie"), ComponentName("pumpkin", "pie")) val groupTask2 = generateGroupTask( ComponentName("pomegranate", "juice"), @@ -413,13 +421,15 @@ class SplitSelectStateControllerTest { // Capture callback from recentsModel#getTasks() val consumer = - withArgCaptor<Consumer<ArrayList<GroupTask>>> { - splitSelectStateController.findLastActiveTasksAndRunCallback( - listOf(matchingComponent, matchingComponent), - taskConsumer - ) - verify(recentsModel).getTasks(capture()) - } + argumentCaptor<Consumer<ArrayList<GroupTask>>> { + splitSelectStateController.findLastActiveTasksAndRunCallback( + listOf(matchingComponent, matchingComponent), + false /* findExactPairMatch */, + taskConsumer + ) + verify(recentsModel).getTasks(capture()) + } + .lastValue // Send our mocked tasks consumer.accept(tasks) @@ -477,14 +487,68 @@ class SplitSelectStateControllerTest { // Capture callback from recentsModel#getTasks() val consumer = - withArgCaptor<Consumer<ArrayList<GroupTask>>> { - splitSelectStateController.findLastActiveTasksAndRunCallback( - listOf(matchingComponent, matchingComponent), - taskConsumer - ) - verify(recentsModel).getTasks(capture()) + argumentCaptor<Consumer<ArrayList<GroupTask>>> { + splitSelectStateController.findLastActiveTasksAndRunCallback( + listOf(matchingComponent, matchingComponent), + false /* findExactPairMatch */, + taskConsumer + ) + verify(recentsModel).getTasks(capture()) + } + .lastValue + + // Send our mocked tasks + consumer.accept(tasks) + } + + @Test + fun activeTasks_multipleSearchShouldFindExactPairMatch() { + val matchingPackage = "hotdog" + val matchingClass = "juice" + val matchingComponent = + ComponentKey(ComponentName(matchingPackage, matchingClass), primaryUserHandle) + val matchingPackage2 = "pomegranate" + val matchingClass2 = "juice" + val matchingComponent2 = + ComponentKey(ComponentName(matchingPackage2, matchingClass2), primaryUserHandle) + + val groupTask1 = + generateGroupTask(ComponentName("hotdog", "pie"), ComponentName("pumpkin", "pie")) + val groupTask2 = + generateGroupTask( + ComponentName(matchingPackage2, matchingClass2), + ComponentName(matchingPackage, matchingClass) + ) + val groupTask3 = + generateGroupTask( + ComponentName("hotdog", "pie"), + ComponentName(matchingPackage, matchingClass) + ) + val tasks: ArrayList<GroupTask> = ArrayList() + tasks.add(groupTask3) + tasks.add(groupTask2) + tasks.add(groupTask1) + + // Assertions happen in the callback we get from what we pass into + // #findLastActiveTasksAndRunCallback + val taskConsumer = + Consumer<List<Task>> { + assertEquals("Expected array length 1", 1, it.size) + assertEquals("Found wrong task", it[0], groupTask2.task1) } + // Capture callback from recentsModel#getTasks() + val consumer = + argumentCaptor<Consumer<ArrayList<GroupTask>>> { + splitSelectStateController.findLastActiveTasksAndRunCallback( + listOf(matchingComponent2, matchingComponent), + true /* findExactPairMatch */, + taskConsumer + ) + verify(recentsModel).getTasks(capture()) + } + .lastValue + // Send our mocked tasks consumer.accept(tasks) } @@ -529,7 +593,7 @@ class SplitSelectStateControllerTest { @Test fun secondPendingIntentSet() { val itemInfo = ItemInfo() - `when`(pendingIntent.creatorUserHandle).thenReturn(primaryUserHandle) + whenever(pendingIntent.creatorUserHandle).thenReturn(primaryUserHandle) splitSelectStateController.setInitialTaskSelect(null, 0, itemInfo, null, 1) splitSelectStateController.setSecondTask(pendingIntent) assertTrue(splitSelectStateController.isBothSplitAppsConfirmed) @@ -558,7 +622,7 @@ class SplitSelectStateControllerTest { return GroupTask( task1, task2, - SplitConfigurationOptions.SplitBounds(Rect(), Rect(), -1, -1) + SplitConfigurationOptions.SplitBounds(Rect(), Rect(), -1, -1, SNAP_TO_50_50) ) } @@ -590,7 +654,7 @@ class SplitSelectStateControllerTest { return GroupTask( task1, task2, - SplitConfigurationOptions.SplitBounds(Rect(), Rect(), -1, -1) + SplitConfigurationOptions.SplitBounds(Rect(), Rect(), -1, -1, SNAP_TO_50_50) ) } } diff --git a/quickstep/tests/src/com/android/quickstep/util/TaskGridNavHelperTest.java b/quickstep/tests/src/com/android/quickstep/util/TaskGridNavHelperTest.java new file mode 100644 index 0000000000..7ef4910ce5 --- /dev/null +++ b/quickstep/tests/src/com/android/quickstep/util/TaskGridNavHelperTest.java @@ -0,0 +1,510 @@ +/* + * 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.quickstep.util; + +import static com.android.quickstep.util.TaskGridNavHelper.CLEAR_ALL_PLACEHOLDER_ID; +import static com.android.quickstep.util.TaskGridNavHelper.INVALID_FOCUSED_TASK_ID; + +import static org.junit.Assert.assertEquals; + +import com.android.launcher3.util.IntArray; + +import org.junit.Test; + +public class TaskGridNavHelperTest { + + @Test + public void equalLengthRows_noFocused_onTop_pressDown_goesToBottom() { + IntArray topIds = IntArray.wrap(1, 3, 5); + IntArray bottomIds = IntArray.wrap(2, 4, 6); + int currentPageTaskViewId = 1; + int delta = 1; + @TaskGridNavHelper.TASK_NAV_DIRECTION int direction = TaskGridNavHelper.DIRECTION_DOWN; + boolean cycle = true; + TaskGridNavHelper taskGridNavHelper = + new TaskGridNavHelper(topIds, bottomIds, INVALID_FOCUSED_TASK_ID); + + int nextGridPage = + taskGridNavHelper.getNextGridPage(currentPageTaskViewId, delta, direction, cycle); + + assertEquals("Wrong next page returned.", 2, nextGridPage); + } + + @Test + public void equalLengthRows_noFocused_onTop_pressUp_goesToBottom() { + IntArray topIds = IntArray.wrap(1, 3, 5); + IntArray bottomIds = IntArray.wrap(2, 4, 6); + int currentPageTaskViewId = 1; + int delta = 1; + @TaskGridNavHelper.TASK_NAV_DIRECTION int direction = TaskGridNavHelper.DIRECTION_UP; + boolean cycle = true; + TaskGridNavHelper taskGridNavHelper = + new TaskGridNavHelper(topIds, bottomIds, INVALID_FOCUSED_TASK_ID); + + int nextGridPage = + taskGridNavHelper.getNextGridPage(currentPageTaskViewId, delta, direction, cycle); + + assertEquals("Wrong next page returned.", 2, nextGridPage); + } + + @Test + public void equalLengthRows_noFocused_onBottom_pressDown_goesToTop() { + IntArray topIds = IntArray.wrap(1, 3, 5); + IntArray bottomIds = IntArray.wrap(2, 4, 6); + int currentPageTaskViewId = 2; + int delta = 1; + @TaskGridNavHelper.TASK_NAV_DIRECTION int direction = TaskGridNavHelper.DIRECTION_DOWN; + boolean cycle = true; + TaskGridNavHelper taskGridNavHelper = + new TaskGridNavHelper(topIds, bottomIds, INVALID_FOCUSED_TASK_ID); + + int nextGridPage = + taskGridNavHelper.getNextGridPage(currentPageTaskViewId, delta, direction, cycle); + + assertEquals("Wrong next page returned.", 1, nextGridPage); + } + + @Test + public void equalLengthRows_noFocused_onBottom_pressUp_goesToTop() { + IntArray topIds = IntArray.wrap(1, 3, 5); + IntArray bottomIds = IntArray.wrap(2, 4, 6); + int currentPageTaskViewId = 2; + int delta = 1; + @TaskGridNavHelper.TASK_NAV_DIRECTION int direction = TaskGridNavHelper.DIRECTION_UP; + boolean cycle = true; + TaskGridNavHelper taskGridNavHelper = + new TaskGridNavHelper(topIds, bottomIds, INVALID_FOCUSED_TASK_ID); + + int nextGridPage = + taskGridNavHelper.getNextGridPage(currentPageTaskViewId, delta, direction, cycle); + + assertEquals("Wrong next page returned.", 1, nextGridPage); + } + + @Test + public void equalLengthRows_noFocused_onTop_pressLeft_goesLeft() { + IntArray topIds = IntArray.wrap(1, 3, 5); + IntArray bottomIds = IntArray.wrap(2, 4, 6); + int currentPageTaskViewId = 1; + int delta = 1; + @TaskGridNavHelper.TASK_NAV_DIRECTION int direction = TaskGridNavHelper.DIRECTION_LEFT; + boolean cycle = true; + TaskGridNavHelper taskGridNavHelper = + new TaskGridNavHelper(topIds, bottomIds, INVALID_FOCUSED_TASK_ID); + + int nextGridPage = + taskGridNavHelper.getNextGridPage(currentPageTaskViewId, delta, direction, cycle); + + assertEquals("Wrong next page returned.", 3, nextGridPage); + } + + @Test + public void equalLengthRows_noFocused_onBottom_pressLeft_goesLeft() { + IntArray topIds = IntArray.wrap(1, 3, 5); + IntArray bottomIds = IntArray.wrap(2, 4, 6); + int currentPageTaskViewId = 2; + int delta = 1; + @TaskGridNavHelper.TASK_NAV_DIRECTION int direction = TaskGridNavHelper.DIRECTION_LEFT; + boolean cycle = true; + TaskGridNavHelper taskGridNavHelper = + new TaskGridNavHelper(topIds, bottomIds, INVALID_FOCUSED_TASK_ID); + + int nextGridPage = + taskGridNavHelper.getNextGridPage(currentPageTaskViewId, delta, direction, cycle); + + assertEquals("Wrong next page returned.", 4, nextGridPage); + } + + @Test + public void equalLengthRows_noFocused_onTop_secondItem_pressRight_goesRight() { + IntArray topIds = IntArray.wrap(1, 3, 5); + IntArray bottomIds = IntArray.wrap(2, 4, 6); + int currentPageTaskViewId = 3; + int delta = -1; + @TaskGridNavHelper.TASK_NAV_DIRECTION int direction = TaskGridNavHelper.DIRECTION_RIGHT; + boolean cycle = true; + TaskGridNavHelper taskGridNavHelper = + new TaskGridNavHelper(topIds, bottomIds, INVALID_FOCUSED_TASK_ID); + + int nextGridPage = + taskGridNavHelper.getNextGridPage(currentPageTaskViewId, delta, direction, cycle); + + assertEquals("Wrong next page returned.", 1, nextGridPage); + } + + @Test + public void equalLengthRows_noFocused_onBottom_secondItem_pressRight_goesRight() { + IntArray topIds = IntArray.wrap(1, 3, 5); + IntArray bottomIds = IntArray.wrap(2, 4, 6); + int currentPageTaskViewId = 4; + int delta = -1; + @TaskGridNavHelper.TASK_NAV_DIRECTION int direction = TaskGridNavHelper.DIRECTION_RIGHT; + boolean cycle = true; + TaskGridNavHelper taskGridNavHelper = + new TaskGridNavHelper(topIds, bottomIds, INVALID_FOCUSED_TASK_ID); + + int nextGridPage = + taskGridNavHelper.getNextGridPage(currentPageTaskViewId, delta, direction, cycle); + + assertEquals("Wrong next page returned.", 2, nextGridPage); + } + + @Test + public void equalLengthRows_noFocused_onTop_pressRight_cycleToClearAll() { + IntArray topIds = IntArray.wrap(1, 3, 5); + IntArray bottomIds = IntArray.wrap(2, 4, 6); + int currentPageTaskViewId = 1; + int delta = -1; + @TaskGridNavHelper.TASK_NAV_DIRECTION int direction = TaskGridNavHelper.DIRECTION_RIGHT; + boolean cycle = true; + TaskGridNavHelper taskGridNavHelper = + new TaskGridNavHelper(topIds, bottomIds, INVALID_FOCUSED_TASK_ID); + + int nextGridPage = + taskGridNavHelper.getNextGridPage(currentPageTaskViewId, delta, direction, cycle); + + assertEquals("Wrong next page returned.", CLEAR_ALL_PLACEHOLDER_ID, nextGridPage); + } + + @Test + public void equalLengthRows_noFocused_onBottom_pressRight_cycleToClearAll() { + IntArray topIds = IntArray.wrap(1, 3, 5); + IntArray bottomIds = IntArray.wrap(2, 4, 6); + int currentPageTaskViewId = 2; + int delta = -1; + @TaskGridNavHelper.TASK_NAV_DIRECTION int direction = TaskGridNavHelper.DIRECTION_RIGHT; + boolean cycle = true; + TaskGridNavHelper taskGridNavHelper = + new TaskGridNavHelper(topIds, bottomIds, INVALID_FOCUSED_TASK_ID); + + int nextGridPage = + taskGridNavHelper.getNextGridPage(currentPageTaskViewId, delta, direction, cycle); + + assertEquals("Wrong next page returned.", CLEAR_ALL_PLACEHOLDER_ID, nextGridPage); + } + + @Test + public void equalLengthRows_noFocused_onTop_lastItem_pressLeft_toClearAll() { + IntArray topIds = IntArray.wrap(1, 3, 5); + IntArray bottomIds = IntArray.wrap(2, 4, 6); + int currentPageTaskViewId = 5; + int delta = 1; + @TaskGridNavHelper.TASK_NAV_DIRECTION int direction = TaskGridNavHelper.DIRECTION_LEFT; + boolean cycle = true; + TaskGridNavHelper taskGridNavHelper = + new TaskGridNavHelper(topIds, bottomIds, INVALID_FOCUSED_TASK_ID); + + int nextGridPage = + taskGridNavHelper.getNextGridPage(currentPageTaskViewId, delta, direction, cycle); + + assertEquals("Wrong next page returned.", CLEAR_ALL_PLACEHOLDER_ID, nextGridPage); + } + + @Test + public void equalLengthRows_noFocused_onBottom_lastItem_pressLeft_toClearAll() { + IntArray topIds = IntArray.wrap(1, 3, 5); + IntArray bottomIds = IntArray.wrap(2, 4, 6); + int currentPageTaskViewId = 6; + int delta = 1; + @TaskGridNavHelper.TASK_NAV_DIRECTION int direction = TaskGridNavHelper.DIRECTION_LEFT; + boolean cycle = true; + TaskGridNavHelper taskGridNavHelper = + new TaskGridNavHelper(topIds, bottomIds, INVALID_FOCUSED_TASK_ID); + + int nextGridPage = + taskGridNavHelper.getNextGridPage(currentPageTaskViewId, delta, direction, cycle); + + assertEquals("Wrong next page returned.", CLEAR_ALL_PLACEHOLDER_ID, nextGridPage); + } + + @Test + public void equalLengthRows_noFocused_onClearAll_pressLeft_cycleToFirst() { + IntArray topIds = IntArray.wrap(1, 3, 5); + IntArray bottomIds = IntArray.wrap(2, 4, 6); + int currentPageTaskViewId = CLEAR_ALL_PLACEHOLDER_ID; + int delta = 1; + @TaskGridNavHelper.TASK_NAV_DIRECTION int direction = TaskGridNavHelper.DIRECTION_LEFT; + boolean cycle = true; + TaskGridNavHelper taskGridNavHelper = + new TaskGridNavHelper(topIds, bottomIds, INVALID_FOCUSED_TASK_ID); + + int nextGridPage = + taskGridNavHelper.getNextGridPage(currentPageTaskViewId, delta, direction, cycle); + + assertEquals("Wrong next page returned.", 1, nextGridPage); + } + + @Test + public void equalLengthRows_noFocused_onClearAll_pressRight_toLastInBottom() { + IntArray topIds = IntArray.wrap(1, 3, 5); + IntArray bottomIds = IntArray.wrap(2, 4, 6); + int currentPageTaskViewId = CLEAR_ALL_PLACEHOLDER_ID; + int delta = -1; + @TaskGridNavHelper.TASK_NAV_DIRECTION int direction = TaskGridNavHelper.DIRECTION_RIGHT; + boolean cycle = true; + TaskGridNavHelper taskGridNavHelper = + new TaskGridNavHelper(topIds, bottomIds, INVALID_FOCUSED_TASK_ID); + + int nextGridPage = + taskGridNavHelper.getNextGridPage(currentPageTaskViewId, delta, direction, cycle); + + assertEquals("Wrong next page returned.", 6, nextGridPage); + } + + @Test + public void equalLengthRows_withFocused_onFocused_pressLeft_toTop() { + IntArray topIds = IntArray.wrap(1, 3, 5); + IntArray bottomIds = IntArray.wrap(2, 4, 6); + int focusedTaskId = 99; + int currentPageTaskViewId = focusedTaskId; + int delta = 1; + @TaskGridNavHelper.TASK_NAV_DIRECTION int direction = TaskGridNavHelper.DIRECTION_LEFT; + boolean cycle = true; + TaskGridNavHelper taskGridNavHelper = + new TaskGridNavHelper(topIds, bottomIds, focusedTaskId); + + int nextGridPage = + taskGridNavHelper.getNextGridPage(currentPageTaskViewId, delta, direction, cycle); + + assertEquals("Wrong next page returned.", 1, nextGridPage); + } + + @Test + public void equalLengthRows_withFocused_onFocused_pressUp_stayOnFocused() { + IntArray topIds = IntArray.wrap(1, 3, 5); + IntArray bottomIds = IntArray.wrap(2, 4, 6); + int focusedTaskId = 99; + int currentPageTaskViewId = focusedTaskId; + int delta = 1; + @TaskGridNavHelper.TASK_NAV_DIRECTION int direction = TaskGridNavHelper.DIRECTION_UP; + boolean cycle = true; + TaskGridNavHelper taskGridNavHelper = + new TaskGridNavHelper(topIds, bottomIds, focusedTaskId); + + int nextGridPage = + taskGridNavHelper.getNextGridPage(currentPageTaskViewId, delta, direction, cycle); + + assertEquals("Wrong next page returned.", focusedTaskId, nextGridPage); + } + + @Test + public void equalLengthRows_withFocused_onFocused_pressDown_stayOnFocused() { + IntArray topIds = IntArray.wrap(1, 3, 5); + IntArray bottomIds = IntArray.wrap(2, 4, 6); + int focusedTaskId = 99; + int currentPageTaskViewId = focusedTaskId; + int delta = 1; + @TaskGridNavHelper.TASK_NAV_DIRECTION int direction = TaskGridNavHelper.DIRECTION_DOWN; + boolean cycle = true; + TaskGridNavHelper taskGridNavHelper = + new TaskGridNavHelper(topIds, bottomIds, focusedTaskId); + + int nextGridPage = + taskGridNavHelper.getNextGridPage(currentPageTaskViewId, delta, direction, cycle); + + assertEquals("Wrong next page returned.", focusedTaskId, nextGridPage); + } + + @Test + public void equalLengthRows_withFocused_onFocused_pressRight_cycleToClearAll() { + IntArray topIds = IntArray.wrap(1, 3, 5); + IntArray bottomIds = IntArray.wrap(2, 4, 6); + int focusedTaskId = 99; + int currentPageTaskViewId = focusedTaskId; + int delta = -1; + @TaskGridNavHelper.TASK_NAV_DIRECTION int direction = TaskGridNavHelper.DIRECTION_RIGHT; + boolean cycle = true; + TaskGridNavHelper taskGridNavHelper = + new TaskGridNavHelper(topIds, bottomIds, focusedTaskId); + + int nextGridPage = + taskGridNavHelper.getNextGridPage(currentPageTaskViewId, delta, direction, cycle); + + assertEquals("Wrong next page returned.", CLEAR_ALL_PLACEHOLDER_ID, nextGridPage); + } + + @Test + public void equalLengthRows_withFocused_onClearAll_pressLeft_cycleToFocusedTask() { + IntArray topIds = IntArray.wrap(1, 3, 5); + IntArray bottomIds = IntArray.wrap(2, 4, 6); + int focusedTaskId = 99; + int currentPageTaskViewId = CLEAR_ALL_PLACEHOLDER_ID; + int delta = 1; + @TaskGridNavHelper.TASK_NAV_DIRECTION int direction = TaskGridNavHelper.DIRECTION_LEFT; + boolean cycle = true; + TaskGridNavHelper taskGridNavHelper = + new TaskGridNavHelper(topIds, bottomIds, focusedTaskId); + + int nextGridPage = + taskGridNavHelper.getNextGridPage(currentPageTaskViewId, delta, direction, cycle); + + assertEquals("Wrong next page returned.", focusedTaskId, nextGridPage); + } + + @Test + public void longerTopRow_noFocused_atEndTopBeyondBottom_pressDown_stayTop() { + IntArray topIds = IntArray.wrap(1, 3, 5, 7); + IntArray bottomIds = IntArray.wrap(2, 4, 6); + int currentPageTaskViewId = 7; + int delta = 1; + @TaskGridNavHelper.TASK_NAV_DIRECTION int direction = TaskGridNavHelper.DIRECTION_DOWN; + boolean cycle = true; + TaskGridNavHelper taskGridNavHelper = + new TaskGridNavHelper(topIds, bottomIds, INVALID_FOCUSED_TASK_ID); + + int nextGridPage = + taskGridNavHelper.getNextGridPage(currentPageTaskViewId, delta, direction, cycle); + + assertEquals("Wrong next page returned.", 7, nextGridPage); + } + + @Test + public void longerTopRow_noFocused_atEndTopBeyondBottom_pressUp_stayTop() { + IntArray topIds = IntArray.wrap(1, 3, 5, 7); + IntArray bottomIds = IntArray.wrap(2, 4, 6); + int currentPageTaskViewId = 7; + int delta = 1; + @TaskGridNavHelper.TASK_NAV_DIRECTION int direction = TaskGridNavHelper.DIRECTION_UP; + boolean cycle = true; + TaskGridNavHelper taskGridNavHelper = + new TaskGridNavHelper(topIds, bottomIds, INVALID_FOCUSED_TASK_ID); + + int nextGridPage = + taskGridNavHelper.getNextGridPage(currentPageTaskViewId, delta, direction, cycle); + + assertEquals("Wrong next page returned.", 7, nextGridPage); + } + + @Test + public void longerTopRow_noFocused_atEndBottom_pressLeft_goToTop() { + IntArray topIds = IntArray.wrap(1, 3, 5, 7); + IntArray bottomIds = IntArray.wrap(2, 4, 6); + int currentPageTaskViewId = 6; + int delta = 1; + @TaskGridNavHelper.TASK_NAV_DIRECTION int direction = TaskGridNavHelper.DIRECTION_LEFT; + boolean cycle = true; + TaskGridNavHelper taskGridNavHelper = + new TaskGridNavHelper(topIds, bottomIds, INVALID_FOCUSED_TASK_ID); + + int nextGridPage = + taskGridNavHelper.getNextGridPage(currentPageTaskViewId, delta, direction, cycle); + + assertEquals("Wrong next page returned.", 7, nextGridPage); + } + + @Test + public void longerTopRow_noFocused_atClearAll_pressRight_goToLonger() { + IntArray topIds = IntArray.wrap(1, 3, 5, 7); + IntArray bottomIds = IntArray.wrap(2, 4, 6); + int currentPageTaskViewId = CLEAR_ALL_PLACEHOLDER_ID; + int delta = -1; + @TaskGridNavHelper.TASK_NAV_DIRECTION int direction = TaskGridNavHelper.DIRECTION_RIGHT; + boolean cycle = true; + TaskGridNavHelper taskGridNavHelper = + new TaskGridNavHelper(topIds, bottomIds, INVALID_FOCUSED_TASK_ID); + + int nextGridPage = + taskGridNavHelper.getNextGridPage(currentPageTaskViewId, delta, direction, cycle); + + assertEquals("Wrong next page returned.", 7, nextGridPage); + } + + @Test + public void longerBottomRow_noFocused_atClearAll_pressRight_goToLonger() { + IntArray topIds = IntArray.wrap(1, 3, 5); + IntArray bottomIds = IntArray.wrap(2, 4, 6, 7); + int currentPageTaskViewId = CLEAR_ALL_PLACEHOLDER_ID; + int delta = -1; + @TaskGridNavHelper.TASK_NAV_DIRECTION int direction = TaskGridNavHelper.DIRECTION_RIGHT; + boolean cycle = true; + TaskGridNavHelper taskGridNavHelper = + new TaskGridNavHelper(topIds, bottomIds, INVALID_FOCUSED_TASK_ID); + + int nextGridPage = + taskGridNavHelper.getNextGridPage(currentPageTaskViewId, delta, direction, cycle); + + assertEquals("Wrong next page returned.", 7, nextGridPage); + } + + @Test + public void equalLengthRows_noFocused_onTop_pressTab_goesToBottom() { + IntArray topIds = IntArray.wrap(1, 3, 5); + IntArray bottomIds = IntArray.wrap(2, 4, 6); + int currentPageTaskViewId = 1; + int delta = 1; + @TaskGridNavHelper.TASK_NAV_DIRECTION int direction = TaskGridNavHelper.DIRECTION_TAB; + boolean cycle = true; + TaskGridNavHelper taskGridNavHelper = + new TaskGridNavHelper(topIds, bottomIds, INVALID_FOCUSED_TASK_ID); + + int nextGridPage = + taskGridNavHelper.getNextGridPage(currentPageTaskViewId, delta, direction, cycle); + + assertEquals("Wrong next page returned.", 2, nextGridPage); + } + + @Test + public void equalLengthRows_noFocused_onBottom_pressTab_goesToNextTop() { + IntArray topIds = IntArray.wrap(1, 3, 5); + IntArray bottomIds = IntArray.wrap(2, 4, 6); + int currentPageTaskViewId = 2; + int delta = 1; + @TaskGridNavHelper.TASK_NAV_DIRECTION int direction = TaskGridNavHelper.DIRECTION_TAB; + boolean cycle = true; + TaskGridNavHelper taskGridNavHelper = + new TaskGridNavHelper(topIds, bottomIds, INVALID_FOCUSED_TASK_ID); + + int nextGridPage = + taskGridNavHelper.getNextGridPage(currentPageTaskViewId, delta, direction, cycle); + + assertEquals("Wrong next page returned.", 3, nextGridPage); + } + + @Test + public void equalLengthRows_noFocused_onTop_pressTabWithShift_goesToPreviousBottom() { + IntArray topIds = IntArray.wrap(1, 3, 5); + IntArray bottomIds = IntArray.wrap(2, 4, 6); + int currentPageTaskViewId = 3; + int delta = -1; + @TaskGridNavHelper.TASK_NAV_DIRECTION int direction = TaskGridNavHelper.DIRECTION_TAB; + boolean cycle = true; + TaskGridNavHelper taskGridNavHelper = + new TaskGridNavHelper(topIds, bottomIds, INVALID_FOCUSED_TASK_ID); + + int nextGridPage = + taskGridNavHelper.getNextGridPage(currentPageTaskViewId, delta, direction, cycle); + + assertEquals("Wrong next page returned.", 2, nextGridPage); + } + + @Test + public void equalLengthRows_noFocused_onBottom_pressTabWithShift_goesToTop() { + IntArray topIds = IntArray.wrap(1, 3, 5); + IntArray bottomIds = IntArray.wrap(2, 4, 6); + int currentPageTaskViewId = 2; + int delta = -1; + @TaskGridNavHelper.TASK_NAV_DIRECTION int direction = TaskGridNavHelper.DIRECTION_TAB; + boolean cycle = true; + TaskGridNavHelper taskGridNavHelper = + new TaskGridNavHelper(topIds, bottomIds, INVALID_FOCUSED_TASK_ID); + + int nextGridPage = + taskGridNavHelper.getNextGridPage(currentPageTaskViewId, delta, direction, cycle); + + assertEquals("Wrong next page returned.", 1, nextGridPage); + } +} diff --git a/quickstep/tests/src/com/android/quickstep/util/TaskKeyByLastActiveTimeCacheTest.java b/quickstep/tests/src/com/android/quickstep/util/TaskKeyByLastActiveTimeCacheTest.java new file mode 100644 index 0000000000..ea2688a0fc --- /dev/null +++ b/quickstep/tests/src/com/android/quickstep/util/TaskKeyByLastActiveTimeCacheTest.java @@ -0,0 +1,281 @@ +/* + * 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.quickstep.util; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertNull; + +import android.content.ComponentName; +import android.content.Intent; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.shared.recents.model.Task; +import com.android.systemui.shared.recents.model.ThumbnailData; + +import org.junit.Test; + +@SmallTest +public class TaskKeyByLastActiveTimeCacheTest { + @Test + public void add() { + TaskKeyByLastActiveTimeCache<ThumbnailData> cache = new TaskKeyByLastActiveTimeCache<>(3); + Task.TaskKey key1 = new Task.TaskKey(1, 0, new Intent(), + new ComponentName("", ""), 0, 1); + ThumbnailData data1 = new ThumbnailData(); + cache.put(key1, data1); + + Task.TaskKey key2 = new Task.TaskKey(2, 0, new Intent(), + new ComponentName("", ""), 0, 2); + ThumbnailData data2 = new ThumbnailData(); + cache.put(key2, data2); + + assertEquals(2, cache.getSize()); + assertEquals(data1, cache.getAndInvalidateIfModified(key1)); + assertEquals(data2, cache.getAndInvalidateIfModified(key2)); + + assertEquals(2, cache.getQueue().size()); + assertEquals(key1, cache.getQueue().poll()); + assertEquals(key2, cache.getQueue().poll()); + } + + @Test + public void addSameTasksWithSameLastActiveTimeTwice() { + // Add 2 tasks with same id and last active time, it should only have 1 entry in cache + TaskKeyByLastActiveTimeCache<ThumbnailData> cache = new TaskKeyByLastActiveTimeCache<>(3); + Task.TaskKey key1 = new Task.TaskKey(1, 0, new Intent(), + new ComponentName("", ""), 0, 1000); + ThumbnailData data1 = new ThumbnailData(); + cache.put(key1, data1); + + Task.TaskKey key2 = new Task.TaskKey(1, 0, new Intent(), + new ComponentName("", ""), 0, 1000); + ThumbnailData data2 = new ThumbnailData(); + cache.put(key2, data2); + + assertEquals(1, cache.getSize()); + assertEquals(data2, cache.getAndInvalidateIfModified(key2)); + + assertEquals(1, cache.getQueue().size()); + assertEquals(key2, cache.getQueue().poll()); + } + + @Test + public void addSameTasksWithDifferentLastActiveTime() { + // Add 2 tasks with same id and different last active time, it should only have the + // higher last active time entry + TaskKeyByLastActiveTimeCache<ThumbnailData> cache = new TaskKeyByLastActiveTimeCache<>(3); + Task.TaskKey key1 = new Task.TaskKey(1, 0, new Intent(), + new ComponentName("", ""), 0, 1000); + ThumbnailData data1 = new ThumbnailData(); + cache.put(key1, data1); + + Task.TaskKey key2 = new Task.TaskKey(1, 0, new Intent(), + new ComponentName("", ""), 0, 2000); + ThumbnailData data2 = new ThumbnailData(); + cache.put(key2, data2); + + assertEquals(1, cache.getSize()); + assertEquals(data2, cache.getAndInvalidateIfModified(key2)); + + assertEquals(1, cache.getQueue().size()); + Task.TaskKey queueKey = cache.getQueue().poll(); + assertEquals(key2, queueKey); + // TaskKey's equal method does not check last active time, so we check here + assertEquals(2000, queueKey.lastActiveTime); + } + + @Test + public void remove() { + TaskKeyByLastActiveTimeCache<ThumbnailData> cache = new TaskKeyByLastActiveTimeCache<>(3); + Task.TaskKey key1 = new Task.TaskKey(1, 0, new Intent(), + new ComponentName("", ""), 0, 0); + cache.put(key1, new ThumbnailData()); + + cache.remove(key1); + + assertEquals(0, cache.getSize()); + assertEquals(0, cache.getQueue().size()); + } + + @Test + public void removeByStubKey() { + TaskKeyByLastActiveTimeCache<ThumbnailData> cache = new TaskKeyByLastActiveTimeCache<>(3); + Task.TaskKey key1 = new Task.TaskKey(1, 1, new Intent(), + new ComponentName("", ""), 1, 100); + cache.put(key1, new ThumbnailData()); + + Task.TaskKey stubKey = new Task.TaskKey(1, 0, new Intent(), + new ComponentName("", ""), 0, 0); + cache.remove(stubKey); + + assertEquals(0, cache.getSize()); + assertEquals(0, cache.getQueue().size()); + } + + @Test + public void evictAll() { + TaskKeyByLastActiveTimeCache<ThumbnailData> cache = new TaskKeyByLastActiveTimeCache<>(3); + Task.TaskKey key1 = new Task.TaskKey(1, 0, new Intent(), + new ComponentName("", ""), 0, 0); + cache.put(key1, new ThumbnailData()); + Task.TaskKey key2 = new Task.TaskKey(2, 0, new Intent(), + new ComponentName("", ""), 0, 0); + cache.put(key2, new ThumbnailData()); + + cache.evictAll(); + + assertEquals(0, cache.getSize()); + assertEquals(0, cache.getQueue().size()); + } + + @Test + public void removeAllByPredicate() { + TaskKeyByLastActiveTimeCache<ThumbnailData> cache = new TaskKeyByLastActiveTimeCache<>(3); + // Add user 1's tasks + Task.TaskKey user1Key1 = new Task.TaskKey(1, 0, new Intent(), + new ComponentName("", ""), 1, 0); + cache.put(user1Key1, new ThumbnailData()); + Task.TaskKey user1Key2 = new Task.TaskKey(2, 0, new Intent(), + new ComponentName("", ""), 1, 0); + cache.put(user1Key2, new ThumbnailData()); + // Add user 2's task + Task.TaskKey user2Key = new Task.TaskKey(3, 0, new Intent(), + new ComponentName("", ""), 2, 0); + ThumbnailData user2Data = new ThumbnailData(); + cache.put(user2Key, user2Data); + + cache.removeAll(key -> key.userId == 1); + + // Only user 2's task remains + assertEquals(1, cache.getSize()); + assertEquals(user2Data, cache.getAndInvalidateIfModified(user2Key)); + + assertEquals(1, cache.getQueue().size()); + assertEquals(user2Key, cache.getQueue().poll()); + } + + @Test + public void getAndInvalidateIfModified() { + TaskKeyByLastActiveTimeCache<ThumbnailData> cache = new TaskKeyByLastActiveTimeCache<>(3); + // Add user 1's tasks + Task.TaskKey key1 = new Task.TaskKey(1, 0, new Intent(), + new ComponentName("", ""), 1, 0); + ThumbnailData data1 = new ThumbnailData(); + cache.put(key1, data1); + + // Get result with task key of same last active time + Task.TaskKey keyWithSameActiveTime = new Task.TaskKey(1, 0, new Intent(), + new ComponentName("", ""), 1, 0); + ThumbnailData result1 = cache.getAndInvalidateIfModified(keyWithSameActiveTime); + assertEquals(data1, result1); + assertEquals(1, cache.getQueue().size()); + + // Invalidate result with task key of new last active time + Task.TaskKey keyWithNewActiveTime = new Task.TaskKey(1, 0, new Intent(), + new ComponentName("", ""), 1, 1); + ThumbnailData result2 = cache.getAndInvalidateIfModified(keyWithNewActiveTime); + // No entry is retrieved because the key has higher last active time + assertNull(result2); + assertEquals(0, cache.getSize()); + assertEquals(0, cache.getQueue().size()); + } + + @Test + public void removeByLastActiveTimeWhenOverMaxSize() { + TaskKeyByLastActiveTimeCache<ThumbnailData> cache = new TaskKeyByLastActiveTimeCache<>(2); + Task.TaskKey key1 = new Task.TaskKey(1, 0, new Intent(), + new ComponentName("", ""), 0, 200); + ThumbnailData task1 = new ThumbnailData(); + cache.put(key1, task1); + Task.TaskKey key2 = new Task.TaskKey(2, 0, new Intent(), + new ComponentName("", ""), 0, 100); + ThumbnailData task2 = new ThumbnailData(); + cache.put(key2, task2); + + // Add the 3rd entry which will exceed the max cache size + Task.TaskKey key3 = new Task.TaskKey(3, 0, new Intent(), + new ComponentName("", ""), 0, 300); + ThumbnailData task3 = new ThumbnailData(); + cache.put(key3, task3); + + // Assert map size and check the remaining entries have higher active time + assertEquals(2, cache.getSize()); + assertEquals(task1, cache.getAndInvalidateIfModified(key1)); + assertEquals(task3, cache.getAndInvalidateIfModified(key3)); + assertNull(cache.getAndInvalidateIfModified(key2)); + + // Assert queue size and check the remaining entries have higher active time + assertEquals(2, cache.getQueue().size()); + Task.TaskKey queueKey1 = cache.getQueue().poll(); + assertEquals(key1, queueKey1); + assertEquals(200, queueKey1.lastActiveTime); + Task.TaskKey queueKey2 = cache.getQueue().poll(); + assertEquals(key3, queueKey2); + assertEquals(300, queueKey2.lastActiveTime); + } + + @Test + public void updateIfAlreadyInCache() { + TaskKeyByLastActiveTimeCache<ThumbnailData> cache = new TaskKeyByLastActiveTimeCache<>(2); + Task.TaskKey key1 = new Task.TaskKey(1, 0, new Intent(), + new ComponentName("", ""), 0, 200); + cache.put(key1, new ThumbnailData()); + + // Update original data to new data + ThumbnailData newData = new ThumbnailData(); + cache.updateIfAlreadyInCache(key1.id, newData); + + // Data is updated to newData successfully + ThumbnailData result = cache.getAndInvalidateIfModified(key1); + assertEquals(newData, result); + } + + @Test + public void updateCacheSizeAndInvalidateExcess() { + // Last active time are not in-sync with insertion order to simulate the real async case + TaskKeyByLastActiveTimeCache<ThumbnailData> cache = new TaskKeyByLastActiveTimeCache<>(4); + Task.TaskKey key1 = new Task.TaskKey(1, 0, new Intent(), + new ComponentName("", ""), 0, 200); + cache.put(key1, new ThumbnailData()); + + Task.TaskKey key2 = new Task.TaskKey(2, 0, new Intent(), + new ComponentName("", ""), 0, 100); + cache.put(key2, new ThumbnailData()); + + Task.TaskKey key3 = new Task.TaskKey(3, 0, new Intent(), + new ComponentName("", ""), 0, 400); + cache.put(key3, new ThumbnailData()); + + Task.TaskKey key4 = new Task.TaskKey(4, 0, new Intent(), + new ComponentName("", ""), 0, 300); + cache.put(key4, new ThumbnailData()); + + // Check that it has 4 entries before cache size changes + assertEquals(4, cache.getSize()); + assertEquals(4, cache.getQueue().size()); + + // Update size to 2 + cache.updateCacheSizeAndRemoveExcess(2); + + // Number of entries becomes 2, only key3 and key4 remain + assertEquals(2, cache.getSize()); + assertEquals(2, cache.getQueue().size()); + assertNotNull(cache.getAndInvalidateIfModified(key3)); + assertNotNull(cache.getAndInvalidateIfModified(key4)); + } +} diff --git a/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java b/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java index a54dc2da18..b365173663 100644 --- a/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java +++ b/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java @@ -18,11 +18,15 @@ package com.android.quickstep.util; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import android.content.Context; +import android.content.res.Configuration; import android.graphics.Point; import android.graphics.Rect; import android.graphics.RectF; import android.util.ArrayMap; +import android.util.DisplayMetrics; import android.view.RemoteAnimationTarget; import android.view.Surface; @@ -35,7 +39,6 @@ import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.DisplayController.Info; import com.android.launcher3.util.LauncherModelHelper; import com.android.launcher3.util.NavigationMode; -import com.android.launcher3.util.ReflectionHelpers; import com.android.launcher3.util.RotationUtils; import com.android.launcher3.util.WindowBounds; import com.android.launcher3.util.window.CachedDisplayInfo; @@ -61,6 +64,7 @@ public class TaskViewSimulatorTest { public void taskProperlyScaled_portrait_noRotation_sameInsets1() { new TaskMatrixVerifier() .withLauncherSize(1200, 2450) + .withDensityDpi(420) .withInsets(new Rect(0, 80, 0, 120)) .verifyNoTransforms(); } @@ -69,6 +73,7 @@ public class TaskViewSimulatorTest { public void taskProperlyScaled_portrait_noRotation_sameInsets2() { new TaskMatrixVerifier() .withLauncherSize(1200, 2450) + .withDensityDpi(420) .withInsets(new Rect(55, 80, 55, 120)) .verifyNoTransforms(); } @@ -77,6 +82,7 @@ public class TaskViewSimulatorTest { public void taskProperlyScaled_landscape_noRotation_sameInsets1() { new TaskMatrixVerifier() .withLauncherSize(2450, 1250) + .withDensityDpi(420) .withInsets(new Rect(0, 80, 0, 40)) .verifyNoTransforms(); } @@ -85,6 +91,7 @@ public class TaskViewSimulatorTest { public void taskProperlyScaled_landscape_noRotation_sameInsets2() { new TaskMatrixVerifier() .withLauncherSize(2450, 1250) + .withDensityDpi(420) .withInsets(new Rect(0, 80, 120, 0)) .verifyNoTransforms(); } @@ -93,6 +100,7 @@ public class TaskViewSimulatorTest { public void taskProperlyScaled_landscape_noRotation_sameInsets3() { new TaskMatrixVerifier() .withLauncherSize(2450, 1250) + .withDensityDpi(420) .withInsets(new Rect(55, 80, 55, 120)) .verifyNoTransforms(); } @@ -101,6 +109,7 @@ public class TaskViewSimulatorTest { public void taskProperlyScaled_landscape_rotated() { new TaskMatrixVerifier() .withLauncherSize(1200, 2450) + .withDensityDpi(420) .withInsets(new Rect(0, 80, 0, 120)) .withAppBounds( new Rect(0, 0, 2450, 1200), @@ -112,6 +121,7 @@ public class TaskViewSimulatorTest { private static class TaskMatrixVerifier extends TransformParams { private Point mDisplaySize = new Point(); + private int mDensityDpi = DisplayMetrics.DENSITY_DEFAULT; private Rect mDisplayInsets = new Rect(); private Rect mAppBounds = new Rect(); private Rect mLauncherInsets = new Rect(); @@ -129,6 +139,11 @@ public class TaskViewSimulatorTest { return this; } + TaskMatrixVerifier withDensityDpi(int densityDpi) { + mDensityDpi = densityDpi; + return this; + } + TaskMatrixVerifier withInsets(Rect insets) { mDisplayInsets.set(insets); mLauncherInsets.set(insets); @@ -173,13 +188,17 @@ public class TaskViewSimulatorTest { new ArrayMap<>(); perDisplayBoundsCache.put(cdi.normalize(), allBounds); - DisplayController.Info mockInfo = new Info( - helper.sandboxContext, wmProxy, perDisplayBoundsCache); + Configuration configuration = new Configuration(); + configuration.densityDpi = mDensityDpi; + Context configurationContext = helper.sandboxContext.createConfigurationContext( + configuration); + + DisplayController.Info info = new Info( + configurationContext, wmProxy, perDisplayBoundsCache); - DisplayController controller = - DisplayController.INSTANCE.get(helper.sandboxContext); - controller.close(); - ReflectionHelpers.setField(controller, "mInfo", mockInfo); + DisplayController mockController = mock(DisplayController.class); + when(mockController.getInfo()).thenReturn(info); + helper.sandboxContext.putObject(DisplayController.INSTANCE, mockController); mDeviceProfile = InvariantDeviceProfile.INSTANCE.get(helper.sandboxContext) .getBestMatch(mAppBounds.width(), mAppBounds.height(), rotation); @@ -189,7 +208,7 @@ public class TaskViewSimulatorTest { FallbackActivityInterface.INSTANCE); tvs.setDp(mDeviceProfile); - int launcherRotation = mockInfo.rotation; + int launcherRotation = info.rotation; if (mAppRotation < 0) { mAppRotation = launcherRotation; } diff --git a/quickstep/tests/src/com/android/quickstep/util/unfold/PreemptiveUnfoldTransitionProgressProviderTest.kt b/quickstep/tests/src/com/android/quickstep/util/unfold/PreemptiveUnfoldTransitionProgressProviderTest.kt index f73be7269d..6a418a4d8b 100644 --- a/quickstep/tests/src/com/android/quickstep/util/unfold/PreemptiveUnfoldTransitionProgressProviderTest.kt +++ b/quickstep/tests/src/com/android/quickstep/util/unfold/PreemptiveUnfoldTransitionProgressProviderTest.kt @@ -21,19 +21,17 @@ import android.testing.TestableLooper import android.testing.TestableLooper.RunWithLooper import android.util.Log import androidx.test.filters.SmallTest -import com.android.launcher3.util.any -import com.android.launcher3.util.mock import com.android.systemui.unfold.UnfoldTransitionProgressProvider import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mockito.anyBoolean -import org.mockito.Mockito.anyFloat -import org.mockito.Mockito.inOrder -import org.mockito.Mockito.never -import org.mockito.Mockito.verify +import org.mockito.kotlin.any +import org.mockito.kotlin.inOrder +import org.mockito.kotlin.mock +import org.mockito.kotlin.never +import org.mockito.kotlin.verify @SmallTest @RunWith(AndroidTestingRunner::class) @@ -74,7 +72,7 @@ class PreemptiveUnfoldTransitionProgressProviderTest { provider.preemptivelyStartTransition(initialProgress = null) verify(listener).onTransitionStarted() - verify(listener, never()).onTransitionProgress(anyFloat()) + verify(listener, never()).onTransitionProgress(any()) } @Test @@ -90,7 +88,7 @@ class PreemptiveUnfoldTransitionProgressProviderTest { provider.preemptivelyStartTransition() provider.cancelPreemptiveStart() - with(inOrder(listener)) { + inOrder(listener) { verify(listener).onTransitionStarted() verify(listener).onTransitionFinished() } @@ -111,7 +109,7 @@ class PreemptiveUnfoldTransitionProgressProviderTest { source.onTransitionStarted() source.onTransitionFinished() - with(inOrder(listener)) { + inOrder(listener) { verify(listener).onTransitionStarted() verify(listener).onTransitionFinished() } @@ -152,7 +150,7 @@ class PreemptiveUnfoldTransitionProgressProviderTest { provider.preemptivelyStartTransition() source.onTransitionFinished() - with(inOrder(listener)) { + inOrder(listener) { verify(listener).onTransitionStarted() verify(listener).onTransitionFinished() } @@ -165,7 +163,7 @@ class PreemptiveUnfoldTransitionProgressProviderTest { testableLooper.moveTimeForward(PREEMPTIVE_UNFOLD_TIMEOUT_MS + 1) testableLooper.processAllMessages() - with(inOrder(listener)) { + inOrder(listener) { verify(listener).onTransitionStarted() verify(listener).onTransitionFinished() } @@ -178,7 +176,7 @@ class PreemptiveUnfoldTransitionProgressProviderTest { testableLooper.moveTimeForward(PREEMPTIVE_UNFOLD_TIMEOUT_MS + 1) testableLooper.processAllMessages() - verify(testWtfHandler).onTerribleFailure(any(), any(), anyBoolean()) + verify(testWtfHandler).onTerribleFailure(any(), any(), any()) } @Test @@ -225,7 +223,7 @@ class PreemptiveUnfoldTransitionProgressProviderTest { source.onTransitionFinished() - with(inOrder(listener)) { + inOrder(listener) { verify(listener).onTransitionStarted() verify(listener).onTransitionFinished() } |