Merge "Migrate model tests to multivalent" into main
This commit is contained in:
+11
-2
@@ -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: [
|
||||
|
||||
+4
-4
@@ -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;
|
||||
+4
@@ -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<ItemInfo>
|
||||
+3
@@ -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()
|
||||
+2
@@ -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<*>
|
||||
|
||||
+25
-1
@@ -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);
|
||||
+8
-3
@@ -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)
|
||||
+7
@@ -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<PackageManagerHelper>()
|
||||
private val expectedAppPackage = "appPackageExpected"
|
||||
+6
@@ -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() }
|
||||
+3
@@ -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;
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
+18
@@ -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";
|
||||
|
||||
+8
-143
@@ -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<WorkspaceItemInfo>
|
||||
@Mock private lateinit var mockWorkspaceInfo: WorkspaceItemInfo
|
||||
@Mock private lateinit var mockBgDataModel: BgDataModel
|
||||
@@ -122,6 +122,7 @@ class WorkspaceItemProcessorTest {
|
||||
mock<Context>().apply {
|
||||
whenever(packageManager).thenReturn(mock())
|
||||
whenever(packageManager.getUserBadgedLabel(any(), any())).thenReturn("")
|
||||
whenever(applicationContext).thenReturn(ApplicationProvider.getApplicationContext())
|
||||
}
|
||||
mockAppState =
|
||||
mock<LauncherAppState>().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<WidgetInflater>().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<PackageManagerHelper>().apply {
|
||||
whenever(isAppArchived(expectedPackage)).thenReturn(true)
|
||||
}
|
||||
mockCursor =
|
||||
mock<LoaderCursor>().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<WidgetInflater>().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
|
||||
+3
@@ -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
|
||||
+11
-10
@@ -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
|
||||
+5
@@ -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
|
||||
@@ -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() {
|
||||
|
||||
@@ -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<String> 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();
|
||||
}
|
||||
|
||||
|
||||
+16
-16
@@ -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<T> where T : View, T : PageIndicator {
|
||||
@@ -77,8 +77,8 @@ class ViewOnDrawExecutorTest<T> 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<T> where T : View, T : PageIndicator {
|
||||
|
||||
underTest.onViewAttachedToWindow(rootView)
|
||||
|
||||
verifyZeroInteractions(viewTreeObserver)
|
||||
verifyZeroInteractions(rootView)
|
||||
verifyNoMoreInteractions(viewTreeObserver)
|
||||
verifyNoMoreInteractions(rootView)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -117,10 +117,10 @@ class ViewOnDrawExecutorTest<T> 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<T> 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<T> where T : View, T : PageIndicator {
|
||||
|
||||
verify(runnable).run()
|
||||
verify(consumer).accept(underTest)
|
||||
verifyZeroInteractions(workspace)
|
||||
verifyNoMoreInteractions(workspace)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -179,8 +179,8 @@ class ViewOnDrawExecutorTest<T> where T : View, T : PageIndicator {
|
||||
fun cancel_notRun() {
|
||||
underTest.cancel()
|
||||
|
||||
verifyZeroInteractions(runnable)
|
||||
verifyNoMoreInteractions(runnable)
|
||||
verify(consumer).accept(underTest)
|
||||
verifyZeroInteractions(workspace)
|
||||
verifyNoMoreInteractions(workspace)
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
@@ -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<WorkspaceItemInfo>
|
||||
@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<IconRequestInfo<WorkspaceItemInfo>> = mutableListOf()
|
||||
private var mComponentName: ComponentName = ComponentName("package", "class")
|
||||
private var mUnlockedUsersArray: LongSparseArray<Boolean> = LongSparseArray()
|
||||
private var mKeyToPinnedShortcutsMap: MutableMap<ShortcutKey, ShortcutInfo> = mutableMapOf()
|
||||
private var mInstallingPkgs: HashMap<PackageUserKey, PackageInstaller.SessionInfo> = hashMapOf()
|
||||
private var mAllDeepShortcuts: MutableList<ShortcutInfo> = mutableListOf()
|
||||
private var mWidgetProvidersMap: MutableMap<ComponentKey, AppWidgetProviderInfo?> =
|
||||
mutableMapOf()
|
||||
private var mPendingPackages: MutableSet<PackageUserKey> = mutableSetOf()
|
||||
|
||||
private lateinit var itemProcessorUnderTest: WorkspaceItemProcessor
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
mUserHandle = UserHandle(0)
|
||||
mockIconRequestInfo = mock<IconRequestInfo<WorkspaceItemInfo>>()
|
||||
mockWorkspaceInfo = mock<WorkspaceItemInfo>()
|
||||
mockBgDataModel = mock<BgDataModel>()
|
||||
mComponentName = ComponentName("package", "class")
|
||||
mUnlockedUsersArray = LongSparseArray<Boolean>(1).apply { put(101, true) }
|
||||
intent =
|
||||
Intent().apply {
|
||||
component = mComponentName
|
||||
`package` = "pkg"
|
||||
putExtra(ShortcutKey.EXTRA_SHORTCUT_ID, "")
|
||||
}
|
||||
mockContext =
|
||||
mock<Context>().apply {
|
||||
whenever(packageManager).thenReturn(mock())
|
||||
whenever(packageManager.getUserBadgedLabel(any(), any())).thenReturn("")
|
||||
whenever(applicationContext).thenReturn(ApplicationProvider.getApplicationContext())
|
||||
}
|
||||
mockAppState =
|
||||
mock<LauncherAppState>().apply {
|
||||
whenever(context).thenReturn(mockContext)
|
||||
whenever(iconCache).thenReturn(mock())
|
||||
whenever(iconCache.getShortcutIcon(any(), any(), any())).then {}
|
||||
}
|
||||
mockPmHelper =
|
||||
mock<PackageManagerHelper>().apply {
|
||||
whenever(getAppLaunchIntent(mComponentName.packageName, mUserHandle))
|
||||
.thenReturn(intent)
|
||||
}
|
||||
mockLauncherApps =
|
||||
mock<LauncherApps>().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<UserCache>().apply {
|
||||
val userIconInfo =
|
||||
mock<UserIconInfo>().apply { whenever(isPrivate).thenReturn(false) }
|
||||
whenever(getUserInfo(any())).thenReturn(userIconInfo)
|
||||
}
|
||||
|
||||
mockUserManagerState = mock<UserManagerState>()
|
||||
mockWidgetInflater = mock<WidgetInflater>()
|
||||
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<WidgetInflater>().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<PackageManagerHelper>().apply {
|
||||
whenever(isAppArchived(expectedPackage)).thenReturn(true)
|
||||
}
|
||||
mockCursor =
|
||||
mock<LoaderCursor>().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<WidgetInflater>().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<ShortcutKey, ShortcutInfo> = mKeyToPinnedShortcutsMap,
|
||||
app: LauncherAppState = mockAppState,
|
||||
bgDataModel: BgDataModel = mockBgDataModel,
|
||||
widgetProvidersMap: MutableMap<ComponentKey, AppWidgetProviderInfo?> = mWidgetProvidersMap,
|
||||
widgetInflater: WidgetInflater = mockWidgetInflater,
|
||||
pmHelper: PackageManagerHelper = mockPmHelper,
|
||||
iconRequestInfos: MutableList<IconRequestInfo<WorkspaceItemInfo>> = mIconRequestInfos,
|
||||
isSdCardReady: Boolean = false,
|
||||
pendingPackages: MutableSet<PackageUserKey> = mPendingPackages,
|
||||
unlockedUsers: LongSparseArray<Boolean> = mUnlockedUsersArray,
|
||||
installingPkgs: HashMap<PackageUserKey, PackageInstaller.SessionInfo> = mInstallingPkgs,
|
||||
allDeepShortcuts: MutableList<ShortcutInfo> = 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
|
||||
)
|
||||
}
|
||||
@@ -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<InputStream>
|
||||
) {}
|
||||
|
||||
fun waitForLooperSync(looper: Looper) {}
|
||||
}
|
||||
@@ -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<InputStream>
|
||||
) {
|
||||
Shadows.shadowOf(contentResolver).registerInputStreamSupplier(uri, inputStreamSupplier)
|
||||
}
|
||||
|
||||
fun waitForLooperSync(looper: Looper) {
|
||||
Shadows.shadowOf(looper).runToEndOfTasks()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user