diff --git a/tests/Android.bp b/tests/Android.bp index c99f656de4..99455702cf 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -160,7 +160,7 @@ android_library { } filegroup { - name: "launcher-testing-helpers", + name: "launcher-testing-helpers-multivalent", srcs: [ "src/**/*.java", "src/**/*.kt", @@ -174,11 +174,20 @@ filegroup { // Test classes "src/**/*Test.java", "src/**/*Test.kt", + "src/**/RoboApiWrapper.kt", "multivalentTests/src/**/*Test.java", "multivalentTests/src/**/*Test.kt", ], } +filegroup { + name: "launcher-testing-helpers", + srcs: [ + ":launcher-testing-helpers-multivalent", + "src/**/RoboApiWrapper.kt", + ], +} + android_robolectric_test { enabled: true, name: "Launcher3RoboTests", @@ -186,7 +195,7 @@ android_robolectric_test { ":launcher3-robo-src", // Test util classes - ":launcher-testing-helpers", + ":launcher-testing-helpers-multivalent", ":launcher-testing-shared", ], exclude_srcs: [ diff --git a/tests/src/com/android/launcher3/folder/FolderNameInfosTest.kt b/tests/multivalentTests/src/com/android/launcher3/folder/FolderNameInfosTest.kt similarity index 100% rename from tests/src/com/android/launcher3/folder/FolderNameInfosTest.kt rename to tests/multivalentTests/src/com/android/launcher3/folder/FolderNameInfosTest.kt diff --git a/tests/src/com/android/launcher3/folder/FolderPagedViewTest.kt b/tests/multivalentTests/src/com/android/launcher3/folder/FolderPagedViewTest.kt similarity index 100% rename from tests/src/com/android/launcher3/folder/FolderPagedViewTest.kt rename to tests/multivalentTests/src/com/android/launcher3/folder/FolderPagedViewTest.kt diff --git a/tests/src/com/android/launcher3/folder/FolderTest.kt b/tests/multivalentTests/src/com/android/launcher3/folder/FolderTest.kt similarity index 100% rename from tests/src/com/android/launcher3/folder/FolderTest.kt rename to tests/multivalentTests/src/com/android/launcher3/folder/FolderTest.kt diff --git a/tests/src/com/android/launcher3/folder/PreviewBackgroundTest.java b/tests/multivalentTests/src/com/android/launcher3/folder/PreviewBackgroundTest.java similarity index 98% rename from tests/src/com/android/launcher3/folder/PreviewBackgroundTest.java rename to tests/multivalentTests/src/com/android/launcher3/folder/PreviewBackgroundTest.java index 7242e9c77a..b9b7d6a9d7 100644 --- a/tests/src/com/android/launcher3/folder/PreviewBackgroundTest.java +++ b/tests/multivalentTests/src/com/android/launcher3/folder/PreviewBackgroundTest.java @@ -24,15 +24,15 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper; import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.PathInterpolator; +import androidx.test.annotation.UiThreadTest; import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; import com.android.launcher3.CellLayout; +import com.android.launcher3.util.LauncherMultivalentJUnit; import org.junit.Before; import org.junit.Test; @@ -41,8 +41,8 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper(setAsMainLooper = true) +@UiThreadTest +@RunWith(LauncherMultivalentJUnit.class) public class PreviewBackgroundTest { private static final float REST_SCALE = 1f; diff --git a/tests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt b/tests/multivalentTests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt similarity index 98% rename from tests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt rename to tests/multivalentTests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt index 7c9f99aa56..d236551ca6 100644 --- a/tests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt +++ b/tests/multivalentTests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt @@ -29,6 +29,7 @@ import com.android.launcher3.graphics.PreloadIconDrawable import com.android.launcher3.icons.BaseIconFactory import com.android.launcher3.icons.FastBitmapDrawable import com.android.launcher3.icons.UserBadgeDrawable +import com.android.launcher3.model.ModelTestRule import com.android.launcher3.model.data.FolderInfo import com.android.launcher3.model.data.ItemInfo import com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_ARCHIVED @@ -44,6 +45,7 @@ import com.android.launcher3.util.UserIconInfo import com.google.common.truth.Truth.assertThat import org.junit.After import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -52,6 +54,8 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class PreviewItemManagerTest { + @get:Rule val modelTestRule = ModelTestRule() + private lateinit var previewItemManager: PreviewItemManager private lateinit var context: Context private lateinit var folderItems: ArrayList diff --git a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt b/tests/multivalentTests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt similarity index 99% rename from tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt rename to tests/multivalentTests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt index 370af0cea5..43dc36ba0c 100644 --- a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt +++ b/tests/multivalentTests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt @@ -27,6 +27,7 @@ import com.android.launcher3.util.TestUtil.runOnExecutorSync import com.google.common.truth.Truth.assertThat import org.junit.After import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.times @@ -43,6 +44,8 @@ import org.mockito.kotlin.whenever @RunWith(AndroidJUnit4::class) class AddWorkspaceItemsTaskTest : AbstractWorkspaceModelTest() { + @get:Rule val modelTestRule = ModelTestRule() + private lateinit var mDataModelCallbacks: MyCallbacks private val mWorkspaceItemSpaceFinder: WorkspaceItemSpaceFinder = mock() diff --git a/tests/src/com/android/launcher3/model/AsyncBindingTest.kt b/tests/multivalentTests/src/com/android/launcher3/model/AsyncBindingTest.kt similarity index 99% rename from tests/src/com/android/launcher3/model/AsyncBindingTest.kt rename to tests/multivalentTests/src/com/android/launcher3/model/AsyncBindingTest.kt index af367a814a..dce75b92df 100644 --- a/tests/src/com/android/launcher3/model/AsyncBindingTest.kt +++ b/tests/multivalentTests/src/com/android/launcher3/model/AsyncBindingTest.kt @@ -64,6 +64,8 @@ class AsyncBindingTest { @get:Rule val setFlagsRule = SetFlagsRule() + @get:Rule val modelTestRule = ModelTestRule() + @Spy private var callbacks = MyCallbacks() @Mock private lateinit var itemInflater: ItemInflater<*> diff --git a/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java b/tests/multivalentTests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java similarity index 84% rename from tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java rename to tests/multivalentTests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java index 328558d1a7..535080ae93 100644 --- a/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java +++ b/tests/multivalentTests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2024 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.launcher3.model; import static android.os.Process.myUserHandle; @@ -11,10 +26,13 @@ import static com.android.launcher3.util.TestUtil.runOnExecutorSync; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import android.content.Context; +import android.content.pm.PackageInstaller; +import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; @@ -49,6 +67,9 @@ public class CacheDataUpdatedTaskTest { @Rule(order = 0) public TestRule testStabilityRule = new TestStabilityRule(); + @Rule(order = 1) + public ModelTestRule mModelTestRule = new ModelTestRule(); + private static final String PENDING_APP_1 = TEST_PACKAGE + ".pending1"; private static final String PENDING_APP_2 = TEST_PACKAGE + ".pending2"; @@ -128,10 +149,13 @@ public class CacheDataUpdatedTaskTest { @Test public void testSessionUpdate_updates_pending_apps() { // Run on model executor so that no other task runs in the middle. + PackageInstaller.SessionInfo sessionInfo = ApplicationProvider.getApplicationContext() + .getPackageManager().getPackageInstaller().getSessionInfo(mSession1); + assertNotNull(sessionInfo); runOnExecutorSync(MODEL_EXECUTOR, () -> { LauncherAppState.getInstance(mContext).getIconCache().updateSessionCache( new PackageUserKey(PENDING_APP_1, myUserHandle()), - mContext.getPackageManager().getPackageInstaller().getSessionInfo(mSession1)); + sessionInfo); // Clear all icons from apps list so that its easy to check what was updated allItems().forEach(wi -> wi.bitmap = BitmapInfo.LOW_RES_INFO); diff --git a/tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java b/tests/multivalentTests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java similarity index 93% rename from tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java rename to tests/multivalentTests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java index 10785f7406..e14e145614 100644 --- a/tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java +++ b/tests/multivalentTests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java @@ -22,11 +22,11 @@ import static com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE; import static org.junit.Assert.assertEquals; import static org.junit.Assume.assumeTrue; -import android.content.Context; import android.content.pm.LauncherApps; import android.content.pm.PackageInstaller; import android.content.pm.PackageInstaller.SessionParams; +import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; @@ -39,6 +39,7 @@ import com.android.launcher3.util.LauncherModelHelper; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -49,8 +50,10 @@ import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) public class DefaultLayoutProviderTest { + @Rule public ModelTestRule rule = new ModelTestRule(); + private LauncherModelHelper mModelHelper; - private Context mTargetContext; + private LauncherModelHelper.SandboxModelContext mTargetContext; @Before public void setUp() { @@ -114,8 +117,10 @@ public class DefaultLayoutProviderTest { SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL); params.setAppPackageName(pendingAppPkg); params.setAppIcon(BitmapInfo.LOW_RES_ICON); + params.installerPackageName = ApplicationProvider.getApplicationContext().getPackageName(); - PackageInstaller installer = mTargetContext.getPackageManager().getPackageInstaller(); + PackageInstaller installer = ApplicationProvider.getApplicationContext().getPackageManager() + .getPackageInstaller(); installer.createSession(params); writeLayoutAndLoad(new LauncherLayoutBuilder().atWorkspace(0, 1, 0) diff --git a/tests/src/com/android/launcher3/model/FirstScreenBroadcastHelperTest.kt b/tests/multivalentTests/src/com/android/launcher3/model/FirstScreenBroadcastHelperTest.kt similarity index 98% rename from tests/src/com/android/launcher3/model/FirstScreenBroadcastHelperTest.kt rename to tests/multivalentTests/src/com/android/launcher3/model/FirstScreenBroadcastHelperTest.kt index aadf72e801..d2d951217d 100644 --- a/tests/src/com/android/launcher3/model/FirstScreenBroadcastHelperTest.kt +++ b/tests/multivalentTests/src/com/android/launcher3/model/FirstScreenBroadcastHelperTest.kt @@ -21,6 +21,7 @@ import android.content.ComponentName import android.content.Intent import android.content.pm.PackageInstaller.SessionInfo import android.os.UserHandle +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT @@ -33,14 +34,20 @@ import com.android.launcher3.model.data.WorkspaceItemInfo import com.android.launcher3.util.PackageManagerHelper import com.android.launcher3.util.PackageUserKey import junit.framework.Assert.assertEquals +import org.junit.Rule import org.junit.Test +import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.kotlin.mock import org.mockito.kotlin.spy import org.mockito.kotlin.verify import org.mockito.kotlin.whenever +@RunWith(AndroidJUnit4::class) class FirstScreenBroadcastHelperTest { + + @get:Rule val modelTestRule = ModelTestRule() + private val context = spy(InstrumentationRegistry.getInstrumentation().targetContext) private val mockPmHelper = mock() private val expectedAppPackage = "appPackageExpected" diff --git a/tests/src/com/android/launcher3/model/FolderIconLoadTest.kt b/tests/multivalentTests/src/com/android/launcher3/model/FolderIconLoadTest.kt similarity index 96% rename from tests/src/com/android/launcher3/model/FolderIconLoadTest.kt rename to tests/multivalentTests/src/com/android/launcher3/model/FolderIconLoadTest.kt index c4a4c9b7b6..d002493390 100644 --- a/tests/src/com/android/launcher3/model/FolderIconLoadTest.kt +++ b/tests/multivalentTests/src/com/android/launcher3/model/FolderIconLoadTest.kt @@ -23,12 +23,14 @@ import com.android.launcher3.util.Executors import com.android.launcher3.util.LauncherLayoutBuilder import com.android.launcher3.util.LauncherModelHelper import com.android.launcher3.util.LauncherModelHelper.* +import com.android.launcher3.util.RoboApiWrapper import com.android.launcher3.util.TestUtil import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertWithMessage import java.util.concurrent.CountDownLatch import org.junit.After import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -36,6 +38,9 @@ import org.junit.runner.RunWith @SmallTest @RunWith(AndroidJUnit4::class) class FolderIconLoadTest { + + @get:Rule(order = 0) val modelTestRule = ModelTestRule() + private lateinit var modelHelper: LauncherModelHelper private val uniqueActivities = @@ -145,6 +150,7 @@ class FolderIconLoadTest { while (cache.isIconUpdateInProgress) { val wait = CountDownLatch(1) Executors.MODEL_EXECUTOR.handler.postDelayed({ wait.countDown() }, 10) + RoboApiWrapper.waitForLooperSync(Executors.MODEL_EXECUTOR.handler.looper) wait.await() } TestUtil.runOnExecutorSync(Executors.MODEL_EXECUTOR) { cache.clearMemoryCache() } diff --git a/tests/src/com/android/launcher3/model/LoaderCursorTest.java b/tests/multivalentTests/src/com/android/launcher3/model/LoaderCursorTest.java similarity index 99% rename from tests/src/com/android/launcher3/model/LoaderCursorTest.java rename to tests/multivalentTests/src/com/android/launcher3/model/LoaderCursorTest.java index b4945d7129..ac911b3984 100644 --- a/tests/src/com/android/launcher3/model/LoaderCursorTest.java +++ b/tests/multivalentTests/src/com/android/launcher3/model/LoaderCursorTest.java @@ -67,6 +67,7 @@ import com.android.launcher3.util.PackageManagerHelper; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -77,6 +78,8 @@ import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) public class LoaderCursorTest { + @Rule public ModelTestRule rule = new ModelTestRule(); + private LauncherModelHelper mModelHelper; private LauncherAppState mApp; private PackageManagerHelper mPmHelper; diff --git a/tests/multivalentTests/src/com/android/launcher3/model/ModelTestRule.kt b/tests/multivalentTests/src/com/android/launcher3/model/ModelTestRule.kt new file mode 100644 index 0000000000..ad2c2a49f0 --- /dev/null +++ b/tests/multivalentTests/src/com/android/launcher3/model/ModelTestRule.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 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.launcher3.model + +import com.android.launcher3.util.RoboApiWrapper +import org.junit.rules.TestWatcher +import org.junit.runner.Description + +class ModelTestRule : TestWatcher() { + override fun starting(description: Description?) { + RoboApiWrapper.initialize() + } +} diff --git a/tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java b/tests/multivalentTests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java similarity index 87% rename from tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java rename to tests/multivalentTests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java index 4ba61ac315..a0d9da94b3 100644 --- a/tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java +++ b/tests/multivalentTests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2024 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.launcher3.model; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; @@ -22,6 +37,7 @@ import com.android.launcher3.util.LauncherModelHelper; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -32,6 +48,8 @@ import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) public class PackageInstallStateChangedTaskTest { + @Rule public ModelTestRule mModelTestRule = new ModelTestRule(); + private static final String PENDING_APP_1 = TEST_PACKAGE + ".pending1"; private static final String PENDING_APP_2 = TEST_PACKAGE + ".pending2"; diff --git a/tests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt b/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt similarity index 81% rename from tests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt rename to tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt index 6cf3b198bf..1d9c161e93 100644 --- a/tests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt +++ b/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt @@ -25,10 +25,9 @@ import android.content.pm.PackageInstaller import android.content.pm.ShortcutInfo import android.os.Process import android.os.UserHandle -import android.platform.test.annotations.EnableFlags import android.util.LongSparseArray -import com.android.dx.mockito.inline.extended.ExtendedMockito -import com.android.launcher3.Flags.FLAG_ENABLE_SUPPORT_FOR_ARCHIVING +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.launcher3.LauncherAppState import com.android.launcher3.LauncherSettings.Favorites import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP @@ -36,7 +35,6 @@ import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_FOLDER -import com.android.launcher3.Utilities import com.android.launcher3.Utilities.EMPTY_PERSON_ARRAY import com.android.launcher3.backuprestore.LauncherRestoreEventLogger import com.android.launcher3.backuprestore.LauncherRestoreEventLogger.RestoreError.Companion.MISSING_INFO @@ -46,7 +44,6 @@ import com.android.launcher3.model.data.FolderInfo import com.android.launcher3.model.data.IconRequestInfo import com.android.launcher3.model.data.ItemInfo import com.android.launcher3.model.data.LauncherAppWidgetInfo -import com.android.launcher3.model.data.LauncherAppWidgetInfo.FLAG_RESTORE_STARTED import com.android.launcher3.model.data.LauncherAppWidgetInfo.FLAG_UI_NOT_READY import com.android.launcher3.model.data.WorkspaceItemInfo import com.android.launcher3.pm.UserCache @@ -57,11 +54,12 @@ import com.android.launcher3.util.PackageUserKey import com.android.launcher3.util.UserIconInfo import com.android.launcher3.widget.LauncherAppWidgetProviderInfo import com.android.launcher3.widget.WidgetInflater -import com.android.launcher3.widget.WidgetSections import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertWithMessage import org.junit.Before +import org.junit.Rule import org.junit.Test +import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.Mock import org.mockito.Mockito.RETURNS_DEEP_STUBS @@ -74,10 +72,12 @@ import org.mockito.kotlin.doAnswer import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.whenever -import org.mockito.quality.Strictness +@RunWith(AndroidJUnit4::class) class WorkspaceItemProcessorTest { + @get:Rule val modelTestRule = ModelTestRule() + @Mock private lateinit var mockIconRequestInfo: IconRequestInfo @Mock private lateinit var mockWorkspaceInfo: WorkspaceItemInfo @Mock private lateinit var mockBgDataModel: BgDataModel @@ -122,6 +122,7 @@ class WorkspaceItemProcessorTest { mock().apply { whenever(packageManager).thenReturn(mock()) whenever(packageManager.getUserBadgedLabel(any(), any())).thenReturn("") + whenever(applicationContext).thenReturn(ApplicationProvider.getApplicationContext()) } mockAppState = mock().apply { @@ -665,142 +666,6 @@ class WorkspaceItemProcessorTest { ) } - @Test - fun `When Pending App Widget has not started restore then update db and add item`() { - - val mockitoSession = - ExtendedMockito.mockitoSession() - .strictness(Strictness.LENIENT) - .mockStatic(WidgetSections::class.java) - .startMocking() - try { - // Given - val expectedProvider = "com.google.android.testApp/com.android.testApp.testAppProvider" - val expectedComponentName = - ComponentName.unflattenFromString(expectedProvider)!!.flattenToString() - val expectedRestoreStatus = FLAG_UI_NOT_READY or FLAG_RESTORE_STARTED - val expectedAppWidgetId = 0 - mockCursor.apply { - itemType = ITEM_TYPE_APPWIDGET - user = mUserHandle - restoreFlag = FLAG_UI_NOT_READY - container = CONTAINER_DESKTOP - whenever(isOnWorkspaceOrHotseat).thenCallRealMethod() - whenever(appWidgetProvider).thenReturn(expectedProvider) - whenever(appWidgetId).thenReturn(expectedAppWidgetId) - whenever(spanX).thenReturn(2) - whenever(spanY).thenReturn(1) - whenever(options).thenReturn(0) - whenever(appWidgetSource).thenReturn(20) - whenever(applyCommonProperties(any())).thenCallRealMethod() - whenever( - updater() - .put(Favorites.APPWIDGET_PROVIDER, expectedComponentName) - .put(Favorites.APPWIDGET_ID, expectedAppWidgetId) - .put(Favorites.RESTORED, expectedRestoreStatus) - .commit() - ) - .thenReturn(1) - } - val inflationResult = - WidgetInflater.InflationResult( - type = WidgetInflater.TYPE_PENDING, - widgetInfo = null - ) - mockWidgetInflater = - mock().apply { - whenever(inflateAppWidget(any())).thenReturn(inflationResult) - } - val packageUserKey = PackageUserKey("com.google.android.testApp", mUserHandle) - mInstallingPkgs[packageUserKey] = PackageInstaller.SessionInfo() - - // When - itemProcessorUnderTest = - createWorkspaceItemProcessorUnderTest(widgetProvidersMap = mWidgetProvidersMap) - itemProcessorUnderTest.processItem() - - // Then - val expectedWidgetInfo = - LauncherAppWidgetInfo().apply { - appWidgetId = expectedAppWidgetId - providerName = ComponentName.unflattenFromString(expectedProvider) - restoreStatus = expectedRestoreStatus - } - verify( - mockCursor - .updater() - .put(Favorites.APPWIDGET_PROVIDER, expectedProvider) - .put(Favorites.APPWIDGET_ID, expectedAppWidgetId) - .put(Favorites.RESTORED, expectedRestoreStatus) - ) - .commit() - val widgetInfoCaptor = ArgumentCaptor.forClass(LauncherAppWidgetInfo::class.java) - verify(mockCursor).checkAndAddItem(widgetInfoCaptor.capture(), eq(mockBgDataModel)) - val actualWidgetInfo = widgetInfoCaptor.value - with(actualWidgetInfo) { - assertThat(providerName).isEqualTo(expectedWidgetInfo.providerName) - assertThat(restoreStatus).isEqualTo(expectedWidgetInfo.restoreStatus) - assertThat(targetComponent).isEqualTo(expectedWidgetInfo.targetComponent) - assertThat(appWidgetId).isEqualTo(expectedWidgetInfo.appWidgetId) - } - } finally { - mockitoSession.finishMocking() - } - } - - @Test - @EnableFlags(FLAG_ENABLE_SUPPORT_FOR_ARCHIVING) - fun `When Archived Pending App Widget then checkAndAddItem`() { - val mockitoSession = - ExtendedMockito.mockitoSession().mockStatic(Utilities::class.java).startMocking() - try { - // Given - val expectedProvider = "com.google.android.testApp/com.android.testApp.testAppProvider" - val expectedComponentName = ComponentName.unflattenFromString(expectedProvider) - val expectedPackage = expectedComponentName!!.packageName - mockPmHelper = - mock().apply { - whenever(isAppArchived(expectedPackage)).thenReturn(true) - } - mockCursor = - mock().apply { - itemType = ITEM_TYPE_APPWIDGET - id = 1 - user = UserHandle(1) - restoreFlag = FLAG_UI_NOT_READY - container = CONTAINER_DESKTOP - whenever(isOnWorkspaceOrHotseat).thenCallRealMethod() - whenever(appWidgetProvider).thenReturn(expectedProvider) - whenever(appWidgetId).thenReturn(0) - whenever(spanX).thenReturn(2) - whenever(spanY).thenReturn(1) - whenever(options).thenReturn(0) - whenever(appWidgetSource).thenReturn(20) - whenever(applyCommonProperties(any())).thenCallRealMethod() - } - mInstallingPkgs = hashMapOf() - val inflationResult = - WidgetInflater.InflationResult( - type = WidgetInflater.TYPE_PENDING, - widgetInfo = null - ) - mockWidgetInflater = - mock().apply { - whenever(inflateAppWidget(any())).thenReturn(inflationResult) - } - itemProcessorUnderTest = - createWorkspaceItemProcessorUnderTest(widgetProvidersMap = mWidgetProvidersMap) - - // When - itemProcessorUnderTest.processItem() - - // Then - verify(mockCursor).checkAndAddItem(any(), any()) - } finally { - mockitoSession.finishMocking() - } - } - @Test fun `When widget inflation result is TYPE_DELETE then mark deleted`() { // Given diff --git a/tests/src/com/android/launcher3/model/WorkspaceItemSpaceFinderTest.kt b/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemSpaceFinderTest.kt similarity index 98% rename from tests/src/com/android/launcher3/model/WorkspaceItemSpaceFinderTest.kt rename to tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemSpaceFinderTest.kt index b3d02be460..ae8e96612b 100644 --- a/tests/src/com/android/launcher3/model/WorkspaceItemSpaceFinderTest.kt +++ b/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemSpaceFinderTest.kt @@ -21,6 +21,7 @@ import androidx.test.filters.SmallTest import com.google.common.truth.Truth.assertThat import org.junit.After import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -29,6 +30,8 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class WorkspaceItemSpaceFinderTest : AbstractWorkspaceModelTest() { + @get:Rule val modelTestRule = ModelTestRule() + private val mItemSpaceFinder = WorkspaceItemSpaceFinder() @Before diff --git a/tests/src/com/android/launcher3/pm/InstallSessionTrackerTest.kt b/tests/multivalentTests/src/com/android/launcher3/pm/InstallSessionTrackerTest.kt similarity index 94% rename from tests/src/com/android/launcher3/pm/InstallSessionTrackerTest.kt rename to tests/multivalentTests/src/com/android/launcher3/pm/InstallSessionTrackerTest.kt index b531adb7af..d860710a34 100644 --- a/tests/src/com/android/launcher3/pm/InstallSessionTrackerTest.kt +++ b/tests/multivalentTests/src/com/android/launcher3/pm/InstallSessionTrackerTest.kt @@ -26,6 +26,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SdkSuppress import androidx.test.filters.SmallTest import com.android.launcher3.Flags.FLAG_ENABLE_SUPPORT_FOR_ARCHIVING +import com.android.launcher3.model.ModelTestRule import com.android.launcher3.util.Executors.MODEL_EXECUTOR import com.android.launcher3.util.LauncherModelHelper import com.android.launcher3.util.PackageUserKey @@ -35,6 +36,7 @@ import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith 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 @@ -43,7 +45,9 @@ import org.mockito.kotlin.whenever @SmallTest @RunWith(AndroidJUnit4::class) class InstallSessionTrackerTest { - @get:Rule val setFlagsRule = SetFlagsRule() + @get:Rule(order = 0) val setFlagsRule = SetFlagsRule() + + @get:Rule(order = 1) val modelTestRule = ModelTestRule() private val mockInstallSessionHelper: InstallSessionHelper = mock() private val mockCallback: InstallSessionTracker.Callback = mock() @@ -200,13 +204,9 @@ class InstallSessionTrackerTest { @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q) fun `register triggers registerPackageInstallerSessionCallback for versions from Q`() { // Given - whenever( - launcherApps.registerPackageInstallerSessionCallback( - MODEL_EXECUTOR, - installSessionTracker - ) - ) - .then { /* no-op */ } + doNothing() + .whenever(launcherApps) + .registerPackageInstallerSessionCallback(MODEL_EXECUTOR, installSessionTracker) // When installSessionTracker.register() // Then @@ -218,8 +218,9 @@ class InstallSessionTrackerTest { @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q) fun `unregister triggers unregisterPackageInstallerSessionCallback for versions from Q`() { // Given - whenever(launcherApps.unregisterPackageInstallerSessionCallback(installSessionTracker)) - .then { /* no-op */ } + doNothing() + .whenever(launcherApps) + .unregisterPackageInstallerSessionCallback(installSessionTracker) // When installSessionTracker.unregister() // Then diff --git a/tests/src/com/android/launcher3/pm/UserCacheTest.kt b/tests/multivalentTests/src/com/android/launcher3/pm/UserCacheTest.kt similarity index 97% rename from tests/src/com/android/launcher3/pm/UserCacheTest.kt rename to tests/multivalentTests/src/com/android/launcher3/pm/UserCacheTest.kt index b21219e6b8..482dcedb0e 100644 --- a/tests/src/com/android/launcher3/pm/UserCacheTest.kt +++ b/tests/multivalentTests/src/com/android/launcher3/pm/UserCacheTest.kt @@ -20,6 +20,7 @@ import android.os.Process.myUserHandle import android.os.UserHandle import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry +import com.android.launcher3.model.ModelTestRule import com.android.launcher3.util.Executors.MODEL_EXECUTOR import com.android.launcher3.util.LauncherModelHelper import com.android.launcher3.util.TestUtil @@ -27,11 +28,15 @@ import com.android.launcher3.util.UserIconInfo import com.google.common.truth.Truth.assertThat import org.junit.After import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class UserCacheTest { + + @get:Rule val modelTestRule = ModelTestRule() + private val launcherModelHelper = LauncherModelHelper() private val sandboxContext = launcherModelHelper.sandboxContext private lateinit var userCache: UserCache diff --git a/tests/multivalentTests/src/com/android/launcher3/ui/BubbleTextViewTest.java b/tests/multivalentTests/src/com/android/launcher3/ui/BubbleTextViewTest.java index aa7f3887e1..b933ed2cdf 100644 --- a/tests/multivalentTests/src/com/android/launcher3/ui/BubbleTextViewTest.java +++ b/tests/multivalentTests/src/com/android/launcher3/ui/BubbleTextViewTest.java @@ -26,6 +26,7 @@ import static com.android.launcher3.BubbleTextView.DISPLAY_ALL_APPS; import static com.android.launcher3.BubbleTextView.DISPLAY_PREDICTION_ROW; import static com.android.launcher3.BubbleTextView.DISPLAY_SEARCH_RESULT; import static com.android.launcher3.BubbleTextView.DISPLAY_SEARCH_RESULT_SMALL; +import static com.android.launcher3.Flags.FLAG_ENABLE_SUPPORT_FOR_ARCHIVING; import static com.android.launcher3.Flags.FLAG_USE_NEW_ICON_FOR_ARCHIVED_APPS; import static com.android.launcher3.LauncherPrefs.ENABLE_TWOLINE_ALLAPPS_TOGGLE; import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_ARCHIVED; @@ -416,7 +417,7 @@ public class BubbleTextViewTest { assertThat(mBubbleTextView.getIcon().hasBadge()).isEqualTo(false); } - @EnableFlags(FLAG_USE_NEW_ICON_FOR_ARCHIVED_APPS) + @EnableFlags({FLAG_ENABLE_SUPPORT_FOR_ARCHIVING, FLAG_USE_NEW_ICON_FOR_ARCHIVED_APPS}) @Test public void applyIconAndLabel_setsImageSpan_whenInactiveArchivedApp() { // Given @@ -452,7 +453,7 @@ public class BubbleTextViewTest { assertThat(actualSpan.getVerticalAlignment()).isEqualTo(ALIGN_CENTER); } - @EnableFlags(FLAG_USE_NEW_ICON_FOR_ARCHIVED_APPS) + @EnableFlags({FLAG_ENABLE_SUPPORT_FOR_ARCHIVING, FLAG_USE_NEW_ICON_FOR_ARCHIVED_APPS}) @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S) @Test public void applyIconAndLabel_setsBoldDrawable_whenBoldedTextForArchivedApp() { diff --git a/tests/multivalentTests/src/com/android/launcher3/util/LauncherModelHelper.java b/tests/multivalentTests/src/com/android/launcher3/util/LauncherModelHelper.java index f18c02b372..2d53e29bd6 100644 --- a/tests/multivalentTests/src/com/android/launcher3/util/LauncherModelHelper.java +++ b/tests/multivalentTests/src/com/android/launcher3/util/LauncherModelHelper.java @@ -45,21 +45,24 @@ import android.test.mock.MockContentResolver; import android.util.ArrayMap; import androidx.test.core.app.ApplicationProvider; -import androidx.test.uiautomator.UiDevice; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherModel; import com.android.launcher3.model.BgDataModel; import com.android.launcher3.model.BgDataModel.Callbacks; +import com.android.launcher3.model.ModelDbController; import com.android.launcher3.testing.TestInformationProvider; import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext; +import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.OutputStreamWriter; +import java.util.Arrays; +import java.util.List; import java.util.UUID; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; @@ -85,6 +88,23 @@ public class LauncherModelHelper { public static final String TEST_ACTIVITY13 = "com.android.launcher3.tests.Activity14"; public static final String TEST_ACTIVITY14 = "com.android.launcher3.tests.Activity15"; + public static final List ACTIVITY_LIST = Arrays.asList( + TEST_ACTIVITY, + TEST_ACTIVITY2, + TEST_ACTIVITY3, + TEST_ACTIVITY4, + TEST_ACTIVITY5, + TEST_ACTIVITY6, + TEST_ACTIVITY7, + TEST_ACTIVITY8, + TEST_ACTIVITY9, + TEST_ACTIVITY10, + TEST_ACTIVITY11, + TEST_ACTIVITY12, + TEST_ACTIVITY13, + TEST_ACTIVITY14 + ); + // Authority for providing a test default-workspace-layout data. private static final String TEST_PROVIDER_AUTHORITY = LauncherModelHelper.class.getName().toLowerCase(); @@ -128,7 +148,9 @@ public class LauncherModelHelper { icon.eraseColor(Color.RED); sp.setAppIcon(icon); sp.setAppLabel(pkg); - PackageInstaller pi = sandboxContext.getPackageManager().getPackageInstaller(); + sp.setInstallerPackageName(ApplicationProvider.getApplicationContext().getPackageName()); + PackageInstaller pi = ApplicationProvider.getApplicationContext().getPackageManager() + .getPackageInstaller(); int sessionId = pi.createSession(sp); mDestroyTask.add(() -> pi.abandonSession(sessionId)); return sessionId; @@ -164,11 +186,19 @@ public class LauncherModelHelper { public LauncherModelHelper setupDefaultLayoutProvider(LauncherLayoutBuilder builder) throws Exception { InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(sandboxContext); - idp.numRows = idp.numColumns = idp.numDatabaseHotseatIcons = DEFAULT_GRID_SIZE; - idp.iconBitmapSize = DEFAULT_BITMAP_SIZE; + if (idp.numRows == 0 && idp.numColumns == 0) { + idp.numRows = idp.numColumns = idp.numDatabaseHotseatIcons = DEFAULT_GRID_SIZE; + } + if (idp.iconBitmapSize == 0) { + idp.iconBitmapSize = DEFAULT_BITMAP_SIZE; + } - UiDevice.getInstance(getInstrumentation()).executeShellCommand( - "settings put secure launcher3.layout.provider " + TEST_PROVIDER_AUTHORITY); + Settings.Secure.putString(sandboxContext.getContentResolver(), "launcher3.layout.provider", + TEST_PROVIDER_AUTHORITY); + + // TODO: use a wrapper class to differentiate the behavior + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + builder.build(new OutputStreamWriter(bos)); ContentProvider cp = new TestInformationProvider() { @Override @@ -177,8 +207,6 @@ public class LauncherModelHelper { try { ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe(); AutoCloseOutputStream outputStream = new AutoCloseOutputStream(pipe[1]); - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - builder.build(new OutputStreamWriter(bos)); outputStream.write(bos.toByteArray()); outputStream.flush(); outputStream.close(); @@ -189,9 +217,13 @@ public class LauncherModelHelper { } }; setupProvider(TEST_PROVIDER_AUTHORITY, cp); + RoboApiWrapper.INSTANCE.registerInputStream(sandboxContext.getContentResolver(), + ModelDbController.getLayoutUri(TEST_PROVIDER_AUTHORITY, sandboxContext), + ()-> new ByteArrayInputStream(bos.toByteArray())); + mDestroyTask.add(() -> runOnExecutorSync(MODEL_EXECUTOR, () -> - UiDevice.getInstance(getInstrumentation()).executeShellCommand( - "settings delete secure launcher3.layout.provider"))); + Settings.Secure.putString(sandboxContext.getContentResolver(), + "launcher3.layout.provider", ""))); return this; } @@ -203,7 +235,7 @@ public class LauncherModelHelper { MAIN_EXECUTOR.submit(() -> getModel().addCallbacksAndLoad(mockCb)).get(); Executors.MODEL_EXECUTOR.submit(() -> { }).get(); - MAIN_EXECUTOR.submit(() -> { }).get(); + getInstrumentation().waitForIdleSync(); MAIN_EXECUTOR.submit(() -> getModel().removeCallbacks(mockCb)).get(); } diff --git a/tests/src/com/android/launcher3/util/ViewOnDrawExecutorTest.kt b/tests/multivalentTests/src/com/android/launcher3/util/ViewOnDrawExecutorTest.kt similarity index 86% rename from tests/src/com/android/launcher3/util/ViewOnDrawExecutorTest.kt rename to tests/multivalentTests/src/com/android/launcher3/util/ViewOnDrawExecutorTest.kt index 011d138ebc..d26c4d4a3b 100644 --- a/tests/src/com/android/launcher3/util/ViewOnDrawExecutorTest.kt +++ b/tests/multivalentTests/src/com/android/launcher3/util/ViewOnDrawExecutorTest.kt @@ -32,7 +32,7 @@ import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations import org.mockito.kotlin.reset import org.mockito.kotlin.same -import org.mockito.kotlin.verifyZeroInteractions +import org.mockito.kotlin.verifyNoMoreInteractions @RunWith(AndroidJUnit4::class) class ViewOnDrawExecutorTest where T : View, T : PageIndicator { @@ -77,8 +77,8 @@ class ViewOnDrawExecutorTest where T : View, T : PageIndicator { underTest.attachTo(launcher) verify(workspace).addOnAttachStateChangeListener(same(underTest)) - verifyZeroInteractions(viewTreeObserver) - verifyZeroInteractions(rootView) + verifyNoMoreInteractions(viewTreeObserver) + verifyNoMoreInteractions(rootView) } @Test @@ -100,8 +100,8 @@ class ViewOnDrawExecutorTest where T : View, T : PageIndicator { underTest.onViewAttachedToWindow(rootView) - verifyZeroInteractions(viewTreeObserver) - verifyZeroInteractions(rootView) + verifyNoMoreInteractions(viewTreeObserver) + verifyNoMoreInteractions(rootView) } @Test @@ -117,10 +117,10 @@ class ViewOnDrawExecutorTest where T : View, T : PageIndicator { fun run_before_onDraw_noOp() { underTest.run() - verifyZeroInteractions(runnable) - verifyZeroInteractions(viewTreeObserver) - verifyZeroInteractions(workspace) - verifyZeroInteractions(consumer) + verifyNoMoreInteractions(runnable) + verifyNoMoreInteractions(viewTreeObserver) + verifyNoMoreInteractions(workspace) + verifyNoMoreInteractions(consumer) } @Test @@ -148,10 +148,10 @@ class ViewOnDrawExecutorTest where T : View, T : PageIndicator { underTest.run() - verifyZeroInteractions(runnable) - verifyZeroInteractions(viewTreeObserver) - verifyZeroInteractions(workspace) - verifyZeroInteractions(consumer) + verifyNoMoreInteractions(runnable) + verifyNoMoreInteractions(viewTreeObserver) + verifyNoMoreInteractions(workspace) + verifyNoMoreInteractions(consumer) } @Test @@ -160,7 +160,7 @@ class ViewOnDrawExecutorTest where T : View, T : PageIndicator { verify(runnable).run() verify(consumer).accept(underTest) - verifyZeroInteractions(workspace) + verifyNoMoreInteractions(workspace) } @Test @@ -179,8 +179,8 @@ class ViewOnDrawExecutorTest where T : View, T : PageIndicator { fun cancel_notRun() { underTest.cancel() - verifyZeroInteractions(runnable) + verifyNoMoreInteractions(runnable) verify(consumer).accept(underTest) - verifyZeroInteractions(workspace) + verifyNoMoreInteractions(workspace) } } diff --git a/tests/src/com/android/launcher3/model/PackageUpdatedTaskTest.kt b/tests/src/com/android/launcher3/model/PackageUpdatedTaskTest.kt index d9af07a196..05f626d207 100644 --- a/tests/src/com/android/launcher3/model/PackageUpdatedTaskTest.kt +++ b/tests/src/com/android/launcher3/model/PackageUpdatedTaskTest.kt @@ -58,7 +58,8 @@ import org.mockito.kotlin.whenever @RunWith(AndroidJUnit4::class) class PackageUpdatedTaskTest { - @get:Rule val setFlagsRule = SetFlagsRule() + @get:Rule(order = 0) val setFlagsRule = SetFlagsRule() + @get:Rule(order = 1) val modelTestRule = ModelTestRule() private val mUser = UserHandle(0) private val mDataModel: BgDataModel = BgDataModel() diff --git a/tests/src/com/android/launcher3/model/WorkspaceItemProcessorExtraTest.kt b/tests/src/com/android/launcher3/model/WorkspaceItemProcessorExtraTest.kt new file mode 100644 index 0000000000..b93c3053e2 --- /dev/null +++ b/tests/src/com/android/launcher3/model/WorkspaceItemProcessorExtraTest.kt @@ -0,0 +1,338 @@ +/* + * Copyright (C) 2024 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.launcher3.model + +import android.appwidget.AppWidgetProviderInfo +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.content.pm.LauncherApps +import android.content.pm.PackageInstaller +import android.content.pm.ShortcutInfo +import android.os.UserHandle +import android.platform.test.annotations.EnableFlags +import android.util.LongSparseArray +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.dx.mockito.inline.extended.ExtendedMockito +import com.android.launcher3.Flags.FLAG_ENABLE_SUPPORT_FOR_ARCHIVING +import com.android.launcher3.LauncherAppState +import com.android.launcher3.LauncherSettings.Favorites +import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP +import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION +import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET +import com.android.launcher3.Utilities +import com.android.launcher3.model.data.IconRequestInfo +import com.android.launcher3.model.data.LauncherAppWidgetInfo +import com.android.launcher3.model.data.LauncherAppWidgetInfo.FLAG_RESTORE_STARTED +import com.android.launcher3.model.data.LauncherAppWidgetInfo.FLAG_UI_NOT_READY +import com.android.launcher3.model.data.WorkspaceItemInfo +import com.android.launcher3.pm.UserCache +import com.android.launcher3.shortcuts.ShortcutKey +import com.android.launcher3.util.ComponentKey +import com.android.launcher3.util.PackageManagerHelper +import com.android.launcher3.util.PackageUserKey +import com.android.launcher3.util.UserIconInfo +import com.android.launcher3.widget.WidgetInflater +import com.android.launcher3.widget.WidgetSections +import com.google.common.truth.Truth.assertThat +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 +import org.mockito.Mockito.RETURNS_DEEP_STUBS +import org.mockito.Mockito.verify +import org.mockito.kotlin.any +import org.mockito.kotlin.doAnswer +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever +import org.mockito.quality.Strictness + +@RunWith(AndroidJUnit4::class) +class WorkspaceItemProcessorExtraTest { + + @Mock private lateinit var mockIconRequestInfo: IconRequestInfo + @Mock private lateinit var mockWorkspaceInfo: WorkspaceItemInfo + @Mock private lateinit var mockBgDataModel: BgDataModel + @Mock private lateinit var mockContext: Context + @Mock private lateinit var mockAppState: LauncherAppState + @Mock private lateinit var mockPmHelper: PackageManagerHelper + @Mock private lateinit var mockLauncherApps: LauncherApps + @Mock private lateinit var mockCursor: LoaderCursor + @Mock private lateinit var mockUserCache: UserCache + @Mock private lateinit var mockUserManagerState: UserManagerState + @Mock private lateinit var mockWidgetInflater: WidgetInflater + + private var intent: Intent = Intent() + private var mUserHandle: UserHandle = UserHandle(0) + private var mIconRequestInfos: MutableList> = mutableListOf() + private var mComponentName: ComponentName = ComponentName("package", "class") + private var mUnlockedUsersArray: LongSparseArray = LongSparseArray() + private var mKeyToPinnedShortcutsMap: MutableMap = mutableMapOf() + private var mInstallingPkgs: HashMap = hashMapOf() + private var mAllDeepShortcuts: MutableList = mutableListOf() + private var mWidgetProvidersMap: MutableMap = + mutableMapOf() + private var mPendingPackages: MutableSet = mutableSetOf() + + private lateinit var itemProcessorUnderTest: WorkspaceItemProcessor + + @Before + fun setup() { + mUserHandle = UserHandle(0) + mockIconRequestInfo = mock>() + mockWorkspaceInfo = mock() + mockBgDataModel = mock() + mComponentName = ComponentName("package", "class") + mUnlockedUsersArray = LongSparseArray(1).apply { put(101, true) } + intent = + Intent().apply { + component = mComponentName + `package` = "pkg" + putExtra(ShortcutKey.EXTRA_SHORTCUT_ID, "") + } + mockContext = + mock().apply { + whenever(packageManager).thenReturn(mock()) + whenever(packageManager.getUserBadgedLabel(any(), any())).thenReturn("") + whenever(applicationContext).thenReturn(ApplicationProvider.getApplicationContext()) + } + mockAppState = + mock().apply { + whenever(context).thenReturn(mockContext) + whenever(iconCache).thenReturn(mock()) + whenever(iconCache.getShortcutIcon(any(), any(), any())).then {} + } + mockPmHelper = + mock().apply { + whenever(getAppLaunchIntent(mComponentName.packageName, mUserHandle)) + .thenReturn(intent) + } + mockLauncherApps = + mock().apply { + whenever(isPackageEnabled("package", mUserHandle)).thenReturn(true) + whenever(isActivityEnabled(mComponentName, mUserHandle)).thenReturn(true) + } + mockCursor = + Mockito.mock(LoaderCursor::class.java, RETURNS_DEEP_STUBS).apply { + user = mUserHandle + itemType = ITEM_TYPE_APPLICATION + id = 1 + restoreFlag = 1 + serialNumber = 101 + whenever(parseIntent()).thenReturn(intent) + whenever(markRestored()).doAnswer { restoreFlag = 0 } + whenever(updater().put(Favorites.INTENT, intent.toUri(0)).commit()).thenReturn(1) + whenever(getAppShortcutInfo(any(), any(), any(), any())) + .thenReturn(mockWorkspaceInfo) + whenever(createIconRequestInfo(any(), any())).thenReturn(mockIconRequestInfo) + } + mockUserCache = + mock().apply { + val userIconInfo = + mock().apply { whenever(isPrivate).thenReturn(false) } + whenever(getUserInfo(any())).thenReturn(userIconInfo) + } + + mockUserManagerState = mock() + mockWidgetInflater = mock() + mKeyToPinnedShortcutsMap = mutableMapOf() + mInstallingPkgs = hashMapOf() + mAllDeepShortcuts = mutableListOf() + mWidgetProvidersMap = mutableMapOf() + mIconRequestInfos = mutableListOf() + mPendingPackages = mutableSetOf() + } + + @Test + fun `When Pending App Widget has not started restore then update db and add item`() { + + val mockitoSession = + ExtendedMockito.mockitoSession() + .strictness(Strictness.LENIENT) + .mockStatic(WidgetSections::class.java) + .startMocking() + try { + // Given + val expectedProvider = "com.google.android.testApp/com.android.testApp.testAppProvider" + val expectedComponentName = + ComponentName.unflattenFromString(expectedProvider)!!.flattenToString() + val expectedRestoreStatus = FLAG_UI_NOT_READY or FLAG_RESTORE_STARTED + val expectedAppWidgetId = 0 + mockCursor.apply { + itemType = ITEM_TYPE_APPWIDGET + user = mUserHandle + restoreFlag = FLAG_UI_NOT_READY + container = CONTAINER_DESKTOP + whenever(isOnWorkspaceOrHotseat).thenCallRealMethod() + whenever(appWidgetProvider).thenReturn(expectedProvider) + whenever(appWidgetId).thenReturn(expectedAppWidgetId) + whenever(spanX).thenReturn(2) + whenever(spanY).thenReturn(1) + whenever(options).thenReturn(0) + whenever(appWidgetSource).thenReturn(20) + whenever(applyCommonProperties(any())).thenCallRealMethod() + whenever( + updater() + .put(Favorites.APPWIDGET_PROVIDER, expectedComponentName) + .put(Favorites.APPWIDGET_ID, expectedAppWidgetId) + .put(Favorites.RESTORED, expectedRestoreStatus) + .commit() + ) + .thenReturn(1) + } + val inflationResult = + WidgetInflater.InflationResult( + type = WidgetInflater.TYPE_PENDING, + widgetInfo = null + ) + mockWidgetInflater = + mock().apply { + whenever(inflateAppWidget(any())).thenReturn(inflationResult) + } + val packageUserKey = PackageUserKey("com.google.android.testApp", mUserHandle) + mInstallingPkgs[packageUserKey] = PackageInstaller.SessionInfo() + + // When + itemProcessorUnderTest = + createWorkspaceItemProcessorUnderTest(widgetProvidersMap = mWidgetProvidersMap) + itemProcessorUnderTest.processItem() + + // Then + val expectedWidgetInfo = + LauncherAppWidgetInfo().apply { + appWidgetId = expectedAppWidgetId + providerName = ComponentName.unflattenFromString(expectedProvider) + restoreStatus = expectedRestoreStatus + } + verify( + mockCursor + .updater() + .put(Favorites.APPWIDGET_PROVIDER, expectedProvider) + .put(Favorites.APPWIDGET_ID, expectedAppWidgetId) + .put(Favorites.RESTORED, expectedRestoreStatus) + ) + .commit() + val widgetInfoCaptor = ArgumentCaptor.forClass(LauncherAppWidgetInfo::class.java) + verify(mockCursor).checkAndAddItem(widgetInfoCaptor.capture(), eq(mockBgDataModel)) + val actualWidgetInfo = widgetInfoCaptor.value + with(actualWidgetInfo) { + assertThat(providerName).isEqualTo(expectedWidgetInfo.providerName) + assertThat(restoreStatus).isEqualTo(expectedWidgetInfo.restoreStatus) + assertThat(targetComponent).isEqualTo(expectedWidgetInfo.targetComponent) + assertThat(appWidgetId).isEqualTo(expectedWidgetInfo.appWidgetId) + } + } finally { + mockitoSession.finishMocking() + } + } + + @Test + @EnableFlags(FLAG_ENABLE_SUPPORT_FOR_ARCHIVING) + fun `When Archived Pending App Widget then checkAndAddItem`() { + val mockitoSession = + ExtendedMockito.mockitoSession().mockStatic(Utilities::class.java).startMocking() + try { + // Given + val expectedProvider = "com.google.android.testApp/com.android.testApp.testAppProvider" + val expectedComponentName = ComponentName.unflattenFromString(expectedProvider) + val expectedPackage = expectedComponentName!!.packageName + mockPmHelper = + mock().apply { + whenever(isAppArchived(expectedPackage)).thenReturn(true) + } + mockCursor = + mock().apply { + itemType = ITEM_TYPE_APPWIDGET + id = 1 + user = UserHandle(1) + restoreFlag = FLAG_UI_NOT_READY + container = CONTAINER_DESKTOP + whenever(isOnWorkspaceOrHotseat).thenCallRealMethod() + whenever(appWidgetProvider).thenReturn(expectedProvider) + whenever(appWidgetId).thenReturn(0) + whenever(spanX).thenReturn(2) + whenever(spanY).thenReturn(1) + whenever(options).thenReturn(0) + whenever(appWidgetSource).thenReturn(20) + whenever(applyCommonProperties(any())).thenCallRealMethod() + } + mInstallingPkgs = hashMapOf() + val inflationResult = + WidgetInflater.InflationResult( + type = WidgetInflater.TYPE_PENDING, + widgetInfo = null + ) + mockWidgetInflater = + mock().apply { + whenever(inflateAppWidget(any())).thenReturn(inflationResult) + } + itemProcessorUnderTest = + createWorkspaceItemProcessorUnderTest(widgetProvidersMap = mWidgetProvidersMap) + + // When + itemProcessorUnderTest.processItem() + + // Then + verify(mockCursor).checkAndAddItem(any(), any()) + } finally { + mockitoSession.finishMocking() + } + } + + private fun createWorkspaceItemProcessorUnderTest( + cursor: LoaderCursor = mockCursor, + memoryLogger: LoaderMemoryLogger? = null, + userCache: UserCache = mockUserCache, + userManagerState: UserManagerState = mockUserManagerState, + launcherApps: LauncherApps = mockLauncherApps, + shortcutKeyToPinnedShortcuts: Map = mKeyToPinnedShortcutsMap, + app: LauncherAppState = mockAppState, + bgDataModel: BgDataModel = mockBgDataModel, + widgetProvidersMap: MutableMap = mWidgetProvidersMap, + widgetInflater: WidgetInflater = mockWidgetInflater, + pmHelper: PackageManagerHelper = mockPmHelper, + iconRequestInfos: MutableList> = mIconRequestInfos, + isSdCardReady: Boolean = false, + pendingPackages: MutableSet = mPendingPackages, + unlockedUsers: LongSparseArray = mUnlockedUsersArray, + installingPkgs: HashMap = mInstallingPkgs, + allDeepShortcuts: MutableList = mAllDeepShortcuts + ) = + WorkspaceItemProcessor( + c = cursor, + memoryLogger = memoryLogger, + userCache = userCache, + userManagerState = userManagerState, + launcherApps = launcherApps, + app = app, + bgDataModel = bgDataModel, + widgetProvidersMap = widgetProvidersMap, + widgetInflater = widgetInflater, + pmHelper = pmHelper, + unlockedUsers = unlockedUsers, + iconRequestInfos = iconRequestInfos, + pendingPackages = pendingPackages, + isSdCardReady = isSdCardReady, + shortcutKeyToPinnedShortcuts = shortcutKeyToPinnedShortcuts, + installingPkgs = installingPkgs, + allDeepShortcuts = allDeepShortcuts + ) +} diff --git a/tests/multivalentTests/src/com/android/launcher3/pm/InstallSessionHelperTest.kt b/tests/src/com/android/launcher3/pm/InstallSessionHelperTest.kt similarity index 100% rename from tests/multivalentTests/src/com/android/launcher3/pm/InstallSessionHelperTest.kt rename to tests/src/com/android/launcher3/pm/InstallSessionHelperTest.kt diff --git a/tests/src/com/android/launcher3/util/RoboApiWrapper.kt b/tests/src/com/android/launcher3/util/RoboApiWrapper.kt new file mode 100644 index 0000000000..583652dc49 --- /dev/null +++ b/tests/src/com/android/launcher3/util/RoboApiWrapper.kt @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2024 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.launcher3.util + +import android.content.ContentResolver +import android.net.Uri +import android.os.Looper +import java.io.InputStream +import java.util.function.Supplier + +object RoboApiWrapper { + + fun initialize() {} + + fun registerInputStream( + contentResolver: ContentResolver, + uri: Uri, + inputStreamSupplier: Supplier + ) {} + + fun waitForLooperSync(looper: Looper) {} +} diff --git a/tests/src_deviceless/com/android/launcher3/util/RoboApiWrapper.kt b/tests/src_deviceless/com/android/launcher3/util/RoboApiWrapper.kt new file mode 100644 index 0000000000..92322686d9 --- /dev/null +++ b/tests/src_deviceless/com/android/launcher3/util/RoboApiWrapper.kt @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2024 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.launcher3.util + +import android.content.ComponentName +import android.content.ContentResolver +import android.content.Intent +import android.content.IntentFilter +import android.content.pm.ApplicationInfo +import android.content.pm.LauncherActivityInfo +import android.content.pm.LauncherApps +import android.net.Uri +import android.os.Looper +import android.os.Process +import androidx.test.platform.app.InstrumentationRegistry +import java.io.InputStream +import java.util.function.Supplier +import org.mockito.Mockito +import org.mockito.kotlin.whenever +import org.robolectric.RuntimeEnvironment +import org.robolectric.Shadows + +object RoboApiWrapper { + + fun initialize() { + Shadows.shadowOf( + RuntimeEnvironment.getApplication().getSystemService(LauncherApps::class.java) + ) + .addEnabledPackage( + Process.myUserHandle(), + InstrumentationRegistry.getInstrumentation().context.packageName + ) + LauncherModelHelper.ACTIVITY_LIST.forEach { + installApp(ComponentName(InstrumentationRegistry.getInstrumentation().context, it)) + } + } + + private fun installApp(componentName: ComponentName) { + val app = RuntimeEnvironment.getApplication() + val user = Process.myUserHandle() + + val pm = Shadows.shadowOf(app.packageManager) + val ai = pm.addActivityIfNotPresent(componentName) + pm.addIntentFilterForActivity( + componentName, + IntentFilter(Intent.ACTION_MAIN).apply { addCategory(Intent.CATEGORY_LAUNCHER) } + ) + + val li = Mockito.mock(LauncherActivityInfo::class.java) + val appInfo = ApplicationInfo().apply { flags = 0 } + Mockito.doReturn(ai).whenever(li).activityInfo + Mockito.doReturn(appInfo).whenever(li).applicationInfo + Mockito.doReturn(user).whenever(li).user + Mockito.doReturn(1f).whenever(li).loadingProgress + Mockito.doReturn(componentName).whenever(li).componentName + + Shadows.shadowOf(app.getSystemService(LauncherApps::class.java)).apply { + addActivity(user, li) + addEnabledPackage(user, componentName.packageName) + setActivityEnabled(user, componentName) + addApplicationInfo(user, componentName.packageName, ai.applicationInfo) + } + } + + fun registerInputStream( + contentResolver: ContentResolver, + uri: Uri, + inputStreamSupplier: Supplier + ) { + Shadows.shadowOf(contentResolver).registerInputStreamSupplier(uri, inputStreamSupplier) + } + + fun waitForLooperSync(looper: Looper) { + Shadows.shadowOf(looper).runToEndOfTasks() + } +}