diff options
Diffstat (limited to 'tests/src/com/android')
90 files changed, 5356 insertions, 3452 deletions
diff --git a/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt b/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt index dd79ca8b02..e46726dc50 100644 --- a/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt +++ b/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt @@ -24,8 +24,10 @@ import android.view.Surface import androidx.test.core.app.ApplicationProvider import com.android.launcher3.testing.shared.ResourceUtils import com.android.launcher3.util.DisplayController +import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext import com.android.launcher3.util.NavigationMode import com.android.launcher3.util.WindowBounds +import com.android.launcher3.util.rule.TestStabilityRule import com.android.launcher3.util.window.CachedDisplayInfo import com.android.launcher3.util.window.WindowManagerProxy import java.io.BufferedReader @@ -34,11 +36,11 @@ import java.io.PrintWriter import java.io.StringWriter import kotlin.math.max import kotlin.math.min -import org.junit.After -import org.junit.Before -import org.mockito.ArgumentMatchers -import org.mockito.Mockito.mock -import org.mockito.Mockito.`when` as whenever +import org.junit.Rule +import org.mockito.kotlin.any +import org.mockito.kotlin.mock +import org.mockito.kotlin.spy +import org.mockito.kotlin.whenever /** * This is an abstract class for DeviceProfile tests that create an InvariantDeviceProfile based on @@ -47,27 +49,13 @@ import org.mockito.Mockito.`when` as whenever * For an implementation that mocks InvariantDeviceProfile, use [FakeInvariantDeviceProfileTest] */ abstract class AbstractDeviceProfileTest { - protected var context: Context? = null + protected lateinit var context: SandboxContext protected open val runningContext: Context = ApplicationProvider.getApplicationContext() - private var displayController: DisplayController = mock(DisplayController::class.java) - private var windowManagerProxy: WindowManagerProxy = mock(WindowManagerProxy::class.java) - private lateinit var originalDisplayController: DisplayController - private lateinit var originalWindowManagerProxy: WindowManagerProxy + private val displayController: DisplayController = mock() + private val windowManagerProxy: WindowManagerProxy = mock() + private val launcherPrefs: LauncherPrefs = mock() - @Before - open fun setUp() { - val appContext: Context = ApplicationProvider.getApplicationContext() - originalWindowManagerProxy = WindowManagerProxy.INSTANCE.get(appContext) - originalDisplayController = DisplayController.INSTANCE.get(appContext) - WindowManagerProxy.INSTANCE.initializeForTesting(windowManagerProxy) - DisplayController.INSTANCE.initializeForTesting(displayController) - } - - @After - open fun tearDown() { - WindowManagerProxy.INSTANCE.initializeForTesting(originalWindowManagerProxy) - DisplayController.INSTANCE.initializeForTesting(originalDisplayController) - } + @Rule @JvmField val testStabilityRule = TestStabilityRule() class DeviceSpec( val naturalSize: Pair<Int, Int>, @@ -283,11 +271,11 @@ abstract class AbstractDeviceProfileTest { ) { val windowsBounds = perDisplayBoundsCache[displayInfo]!! val realBounds = windowsBounds[rotation] - whenever(windowManagerProxy.getDisplayInfo(ArgumentMatchers.any())).thenReturn(displayInfo) - whenever(windowManagerProxy.getRealBounds(ArgumentMatchers.any(), ArgumentMatchers.any())) - .thenReturn(realBounds) - whenever(windowManagerProxy.getRotation(ArgumentMatchers.any())).thenReturn(rotation) - whenever(windowManagerProxy.getNavigationMode(ArgumentMatchers.any())) + whenever(windowManagerProxy.getDisplayInfo(any())).thenReturn(displayInfo) + whenever(windowManagerProxy.getRealBounds(any(), any())).thenReturn(realBounds) + whenever(windowManagerProxy.getCurrentBounds(any())).thenReturn(realBounds.bounds) + whenever(windowManagerProxy.getRotation(any())).thenReturn(rotation) + whenever(windowManagerProxy.getNavigationMode(any())) .thenReturn( if (isGestureMode) NavigationMode.NO_BUTTON else NavigationMode.THREE_BUTTONS ) @@ -300,11 +288,22 @@ abstract class AbstractDeviceProfileTest { screenHeightDp = (realBounds.bounds.height() / density).toInt() smallestScreenWidthDp = min(screenWidthDp, screenHeightDp) } - context = runningContext.createConfigurationContext(config) + val configurationContext = runningContext.createConfigurationContext(config) + context = + SandboxContext( + configurationContext, + DisplayController.INSTANCE, + WindowManagerProxy.INSTANCE, + LauncherPrefs.INSTANCE + ) + context.putObject(DisplayController.INSTANCE, displayController) + context.putObject(WindowManagerProxy.INSTANCE, windowManagerProxy) + context.putObject(LauncherPrefs.INSTANCE, launcherPrefs) - val info = DisplayController.Info(context, windowManagerProxy, perDisplayBoundsCache) + whenever(launcherPrefs.get(LauncherPrefs.TASKBAR_PINNING)).thenReturn(false) + val info = spy(DisplayController.Info(context, windowManagerProxy, perDisplayBoundsCache)) whenever(displayController.info).thenReturn(info) - whenever(displayController.isTransientTaskbar).thenReturn(isGestureMode) + whenever(info.isTransientTaskbar).thenReturn(isGestureMode) } /** Create a new dump of DeviceProfile, saves to a file in the device and returns it */ diff --git a/tests/src/com/android/launcher3/AppWidgetsRestoredReceiverTest.kt b/tests/src/com/android/launcher3/AppWidgetsRestoredReceiverTest.kt new file mode 100644 index 0000000000..21abab4d49 --- /dev/null +++ b/tests/src/com/android/launcher3/AppWidgetsRestoredReceiverTest.kt @@ -0,0 +1,171 @@ +package com.android.launcher3 + +import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_DELETED +import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_HOST_RESTORED +import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_IDS +import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_OLD_IDS +import android.appwidget.AppWidgetManager.EXTRA_HOST_ID +import android.content.Intent +import android.platform.uiautomator_helpers.DeviceHelpers +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.launcher3.LauncherPrefs.Companion.APP_WIDGET_IDS +import com.android.launcher3.LauncherPrefs.Companion.OLD_APP_WIDGET_IDS +import com.android.launcher3.util.IntArray +import com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE +import com.android.launcher3.widget.LauncherWidgetHolder.APPWIDGET_HOST_ID +import com.google.common.truth.Truth.assertThat +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +/** Tests for [AppWidgetsRestoredReceiver] */ +@SmallTest +@RunWith(AndroidJUnit4::class) +class AppWidgetsRestoredReceiverTest { + private lateinit var launcherPrefs: LauncherPrefs + private lateinit var receiverUnderTest: AppWidgetsRestoredReceiver + + @Before + fun setup() { + launcherPrefs = LauncherPrefs(DeviceHelpers.context) + receiverUnderTest = AppWidgetsRestoredReceiver() + } + + @After + fun tearDown() { + launcherPrefs.remove(OLD_APP_WIDGET_IDS, APP_WIDGET_IDS) + } + + @Test + fun `When AppWidgetsRestoredReceiver gets valid broadcast it sets old and new app widget ids`() { + // Given + val oldIds = intArrayOf(1, 2, 10) + val newIds = intArrayOf(10, 11, 12) + val expectedOldIds = IntArray.wrap(*oldIds).toConcatString() + val expectedNewIds = IntArray.wrap(*newIds).toConcatString() + val intent = + Intent().apply { + component = null + `package` = TEST_PACKAGE + action = ACTION_APPWIDGET_HOST_RESTORED + putExtra(EXTRA_APPWIDGET_OLD_IDS, oldIds) + putExtra(EXTRA_APPWIDGET_IDS, newIds) + putExtra(EXTRA_HOST_ID, APPWIDGET_HOST_ID) + } + + // When + receiverUnderTest.onReceive(DeviceHelpers.context, intent) + + // Then + assertThat(launcherPrefs.get(OLD_APP_WIDGET_IDS)).isEqualTo(expectedOldIds) + assertThat(launcherPrefs.get(APP_WIDGET_IDS)).isEqualTo(expectedNewIds) + } + + @Test + fun `AppWidgetsRestoredReceiver does not set widget ids when Intent action is invalid`() { + // Given + val oldIds = intArrayOf(1, 2, 10) + val newIds = intArrayOf(10, 11, 12) + val intent = + Intent().apply { + component = null + `package` = TEST_PACKAGE + action = ACTION_APPWIDGET_DELETED + putExtra(EXTRA_APPWIDGET_OLD_IDS, oldIds) + putExtra(EXTRA_APPWIDGET_IDS, newIds) + putExtra(EXTRA_HOST_ID, APPWIDGET_HOST_ID) + } + + // When + receiverUnderTest.onReceive(DeviceHelpers.context, intent) + + // Then + assertThat(launcherPrefs.has(OLD_APP_WIDGET_IDS, APP_WIDGET_IDS)).isFalse() + } + + @Test + fun `AppWidgetsRestoredReceiver does not set widget ids when Intent host id is not Launcher`() { + // Given + val oldIds = intArrayOf(1, 2, 10) + val newIds = intArrayOf(10, 11, 12) + val intent = + Intent().apply { + component = null + `package` = TEST_PACKAGE + action = ACTION_APPWIDGET_HOST_RESTORED + putExtra(EXTRA_APPWIDGET_OLD_IDS, oldIds) + putExtra(EXTRA_APPWIDGET_IDS, newIds) + putExtra(EXTRA_HOST_ID, 999999999) + } + + // When + receiverUnderTest.onReceive(DeviceHelpers.context, intent) + + // Then + assertThat(launcherPrefs.has(OLD_APP_WIDGET_IDS, APP_WIDGET_IDS)).isFalse() + } + + @Test + fun `AppWidgetsRestoredReceiver does not set ids when new and old ids differ in length`() { + // Given + val oldIds = intArrayOf(10) + val newIds = intArrayOf(10, 11, 12) + val intent = + Intent().apply { + component = null + `package` = TEST_PACKAGE + action = ACTION_APPWIDGET_HOST_RESTORED + putExtra(EXTRA_APPWIDGET_OLD_IDS, oldIds) + putExtra(EXTRA_APPWIDGET_IDS, newIds) + putExtra(EXTRA_HOST_ID, APPWIDGET_HOST_ID) + } + + // When + receiverUnderTest.onReceive(DeviceHelpers.context, intent) + + // Then + assertThat(launcherPrefs.has(OLD_APP_WIDGET_IDS, APP_WIDGET_IDS)).isFalse() + } + + @Test + fun `AppWidgetsRestoredReceiver does not set widget ids when old ids not set`() { + // Given + val newIds = intArrayOf(10, 11, 12) + val intent = + Intent().apply { + component = null + `package` = TEST_PACKAGE + action = ACTION_APPWIDGET_HOST_RESTORED + putExtra(EXTRA_APPWIDGET_IDS, newIds) + putExtra(EXTRA_HOST_ID, APPWIDGET_HOST_ID) + } + + // When + receiverUnderTest.onReceive(DeviceHelpers.context, intent) + + // Then + assertThat(launcherPrefs.has(OLD_APP_WIDGET_IDS, APP_WIDGET_IDS)).isFalse() + } + + @Test + fun `AppWidgetsRestoredReceiver does not set widget ids when new ids not set`() { + // Given + val oldIds = intArrayOf(10, 11, 12) + val intent = + Intent().apply { + component = null + `package` = TEST_PACKAGE + action = ACTION_APPWIDGET_HOST_RESTORED + putExtra(EXTRA_APPWIDGET_OLD_IDS, oldIds) + putExtra(EXTRA_HOST_ID, APPWIDGET_HOST_ID) + } + + // When + receiverUnderTest.onReceive(DeviceHelpers.context, intent) + + // Then + assertThat(launcherPrefs.has(OLD_APP_WIDGET_IDS, APP_WIDGET_IDS)).isFalse() + } +} diff --git a/tests/src/com/android/launcher3/DeleteDropTargetTest.kt b/tests/src/com/android/launcher3/DeleteDropTargetTest.kt index bcfb90b4ec..46e66e404d 100644 --- a/tests/src/com/android/launcher3/DeleteDropTargetTest.kt +++ b/tests/src/com/android/launcher3/DeleteDropTargetTest.kt @@ -32,9 +32,9 @@ class DeleteDropTargetTest { buttonDropTarget.setTextMultiLine(false) // No space for text - assertThat(buttonDropTarget.isTextClippedVertically(30)).isTrue() + assertThat(buttonDropTarget.isTextClippedVertically(1)).isTrue() // A lot of space for text so the text should not be clipped - assertThat(buttonDropTarget.isTextClippedVertically(100)).isFalse() + assertThat(buttonDropTarget.isTextClippedVertically(1000)).isFalse() } } diff --git a/tests/src/com/android/launcher3/FakeInvariantDeviceProfileTest.kt b/tests/src/com/android/launcher3/FakeInvariantDeviceProfileTest.kt index c22cf40de6..30b5663a1e 100644 --- a/tests/src/com/android/launcher3/FakeInvariantDeviceProfileTest.kt +++ b/tests/src/com/android/launcher3/FakeInvariantDeviceProfileTest.kt @@ -27,9 +27,9 @@ import com.android.launcher3.util.WindowBounds import java.io.PrintWriter import java.io.StringWriter import org.junit.Before -import org.mockito.ArgumentMatchers.any -import org.mockito.Mockito.mock -import org.mockito.Mockito.`when` as whenever +import org.mockito.kotlin.any +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever /** * This is an abstract class for DeviceProfile tests that don't need the real Context and mock an @@ -41,12 +41,13 @@ abstract class FakeInvariantDeviceProfileTest { protected var context: Context? = null protected var inv: InvariantDeviceProfile? = null - protected var info: Info = mock(Info::class.java) + protected val info: Info = mock() protected var windowBounds: WindowBounds? = null protected var isMultiWindowMode: Boolean = false protected var transposeLayoutWithOrientation: Boolean = false protected var useTwoPanels: Boolean = false protected var isGestureMode: Boolean = true + protected var isTransientTaskbar: Boolean = true @Before fun setUp() { @@ -68,7 +69,8 @@ abstract class FakeInvariantDeviceProfileTest { useTwoPanels, isGestureMode, DEFAULT_PROVIDER, - DEFAULT_DIMENSION_PROVIDER + DEFAULT_DIMENSION_PROVIDER, + isTransientTaskbar, ) protected fun initializeVarsForPhone( @@ -93,6 +95,7 @@ abstract class FakeInvariantDeviceProfileTest { whenever(info.smallestSizeDp(any())).thenReturn(411f) this.isGestureMode = isGestureMode + this.isTransientTaskbar = false transposeLayoutWithOrientation = true inv = @@ -118,8 +121,8 @@ abstract class FakeInvariantDeviceProfileTest { listOf(PointF(16f, 16f), PointF(16f, 16f), PointF(16f, 16f), PointF(16f, 16f)) .toTypedArray() - numFolderRows = 3 - numFolderColumns = 3 + numFolderRows = intArrayOf(3, 3, 3, 3) + numFolderColumns = intArrayOf(3, 3, 3, 3) folderStyle = R.style.FolderStyleDefault inlineNavButtonsEndSpacing = R.dimen.taskbar_button_margin_split @@ -175,6 +178,7 @@ abstract class FakeInvariantDeviceProfileTest { whenever(info.smallestSizeDp(any())).thenReturn(800f) this.isGestureMode = isGestureMode + this.isTransientTaskbar = true useTwoPanels = false inv = @@ -200,8 +204,8 @@ abstract class FakeInvariantDeviceProfileTest { listOf(PointF(16f, 64f), PointF(64f, 16f), PointF(16f, 64f), PointF(16f, 64f)) .toTypedArray() - numFolderRows = 3 - numFolderColumns = 3 + numFolderRows = intArrayOf(3, 3, 3, 3) + numFolderColumns = intArrayOf(3, 3, 3, 3) folderStyle = R.style.FolderStyleDefault inlineNavButtonsEndSpacing = R.dimen.taskbar_button_margin_6_5 @@ -258,6 +262,7 @@ abstract class FakeInvariantDeviceProfileTest { whenever(info.smallestSizeDp(any())).thenReturn(700f) this.isGestureMode = isGestureMode + this.isTransientTaskbar = true useTwoPanels = true inv = @@ -283,8 +288,8 @@ abstract class FakeInvariantDeviceProfileTest { listOf(PointF(16f, 16f), PointF(16f, 16f), PointF(16f, 20f), PointF(20f, 20f)) .toTypedArray() - numFolderRows = 3 - numFolderColumns = 3 + numFolderRows = intArrayOf(3, 3, 3, 3) + numFolderColumns = intArrayOf(3, 3, 3, 3) folderStyle = R.style.FolderStyleDefault inlineNavButtonsEndSpacing = R.dimen.taskbar_button_margin_split diff --git a/tests/src/com/android/launcher3/LauncherPrefsTest.kt b/tests/src/com/android/launcher3/LauncherPrefsTest.kt index d59e02a767..88a430bd32 100644 --- a/tests/src/com/android/launcher3/LauncherPrefsTest.kt +++ b/tests/src/com/android/launcher3/LauncherPrefsTest.kt @@ -33,7 +33,8 @@ import org.junit.runner.RunWith private val TEST_BOOLEAN_ITEM = LauncherPrefs.nonRestorableItem("1", false) private val TEST_STRING_ITEM = LauncherPrefs.nonRestorableItem("2", "( ͡❛ ͜ʖ ͡❛)") private val TEST_INT_ITEM = LauncherPrefs.nonRestorableItem("3", -1) -private val TEST_CONTEXTUAL_ITEM = ContextualItem("4", true, { true }, false, Boolean::class.java) +private val TEST_CONTEXTUAL_ITEM = + ContextualItem("4", true, { true }, EncryptionType.ENCRYPTED, Boolean::class.java) private const val TEST_DEFAULT_VALUE = "default" private const val TEST_PREF_KEY = "test_pref_key" @@ -51,13 +52,13 @@ class LauncherPrefsTest { @BeforeClass @JvmStatic fun setup() { - isBootAwareStartupDataEnabled = true + moveStartupDataToDeviceProtectedStorageIsEnabled = true } @AfterClass @JvmStatic fun teardown() { - isBootAwareStartupDataEnabled = false + moveStartupDataToDeviceProtectedStorageIsEnabled = false } } @@ -203,7 +204,11 @@ class LauncherPrefsTest { @Test fun put_bootAwareItem_updatesDeviceProtectedStorage() { val bootAwareItem = - LauncherPrefs.backedUpItem(TEST_PREF_KEY, TEST_DEFAULT_VALUE, isBootAware = true) + LauncherPrefs.backedUpItem( + TEST_PREF_KEY, + TEST_DEFAULT_VALUE, + EncryptionType.DEVICE_PROTECTED + ) val bootAwarePrefs: SharedPreferences = context @@ -220,7 +225,11 @@ class LauncherPrefsTest { @Test fun put_bootAwareItem_updatesEncryptedStorage() { val bootAwareItem = - LauncherPrefs.backedUpItem(TEST_PREF_KEY, TEST_DEFAULT_VALUE, isBootAware = true) + LauncherPrefs.backedUpItem( + TEST_PREF_KEY, + TEST_DEFAULT_VALUE, + EncryptionType.MOVE_TO_DEVICE_PROTECTED + ) val encryptedPrefs: SharedPreferences = context.getSharedPreferences(bootAwareItem.sharedPrefFile, Context.MODE_PRIVATE) @@ -235,7 +244,11 @@ class LauncherPrefsTest { @Test fun remove_bootAwareItem_removesFromDeviceProtectedStorage() { val bootAwareItem = - LauncherPrefs.backedUpItem(TEST_PREF_KEY, TEST_DEFAULT_VALUE, isBootAware = true) + LauncherPrefs.backedUpItem( + TEST_PREF_KEY, + TEST_DEFAULT_VALUE, + EncryptionType.MOVE_TO_DEVICE_PROTECTED + ) val bootAwarePrefs: SharedPreferences = context @@ -254,7 +267,11 @@ class LauncherPrefsTest { @Test fun remove_bootAwareItem_removesFromEncryptedStorage() { val bootAwareItem = - LauncherPrefs.backedUpItem(TEST_PREF_KEY, TEST_DEFAULT_VALUE, isBootAware = true) + LauncherPrefs.backedUpItem( + TEST_PREF_KEY, + TEST_DEFAULT_VALUE, + EncryptionType.MOVE_TO_DEVICE_PROTECTED + ) val encryptedPrefs: SharedPreferences = context.getSharedPreferences(bootAwareItem.sharedPrefFile, Context.MODE_PRIVATE) @@ -271,7 +288,11 @@ class LauncherPrefsTest { @Test fun migrate_bootAwareItemsToDeviceProtectedStorage_worksAsIntended() { val bootAwareItem = - LauncherPrefs.backedUpItem(TEST_PREF_KEY, TEST_DEFAULT_VALUE, isBootAware = true) + LauncherPrefs.backedUpItem( + TEST_PREF_KEY, + TEST_DEFAULT_VALUE, + EncryptionType.MOVE_TO_DEVICE_PROTECTED + ) launcherPrefs.removeSync(bootAwareItem) val bootAwarePrefs: SharedPreferences = @@ -303,7 +324,7 @@ class LauncherPrefsTest { LauncherPrefs.backedUpItem( TEST_PREF_KEY + "_", TEST_DEFAULT_VALUE + "_", - isBootAware = false + EncryptionType.ENCRYPTED ) val bootAwarePrefs: SharedPreferences = diff --git a/tests/src/com/android/launcher3/allapps/AlphabeticalAppsListTest.java b/tests/src/com/android/launcher3/allapps/AlphabeticalAppsListTest.java new file mode 100644 index 0000000000..259f5199db --- /dev/null +++ b/tests/src/com/android/launcher3/allapps/AlphabeticalAppsListTest.java @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.allapps; + +import static androidx.test.core.app.ApplicationProvider.getApplicationContext; + +import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_PRIVATE_SPACE_HEADER; +import static com.android.launcher3.allapps.UserProfileManager.STATE_DISABLED; +import static com.android.launcher3.allapps.UserProfileManager.STATE_ENABLED; +import static com.android.launcher3.allapps.UserProfileManager.STATE_TRANSITION; + +import static org.junit.Assert.assertEquals; +import static org.mockito.AdditionalAnswers.answer; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.os.Process; +import android.os.UserHandle; +import android.platform.test.flag.junit.SetFlagsRule; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.launcher3.Flags; +import com.android.launcher3.model.data.AppInfo; +import com.android.launcher3.util.ActivityContextWrapper; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +@RunWith(AndroidJUnit4.class) +public class AlphabeticalAppsListTest { + + private static final UserHandle MAIN_HANDLE = Process.myUserHandle(); + private static final UserHandle PRIVATE_HANDLE = new UserHandle(11); + + private static final int PRIVATE_SPACE_HEADER_ITEM_COUNT = 1; + private static final int MAIN_USER_APP_COUNT = 2; + private static final int PRIVATE_USER_APP_COUNT = 1; + + private AlphabeticalAppsList<?> mAlphabeticalAppsList; + @Mock + private AllAppsStore<?> mAllAppsStore; + @Mock + private PrivateProfileManager mPrivateProfileManager; + private Context mContext; + + @Rule + public final SetFlagsRule mSetFlagsRule = + new SetFlagsRule(SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT); + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = new ActivityContextWrapper(getApplicationContext()); + when(mPrivateProfileManager.getItemInfoMatcher()).thenReturn(info -> + info != null && info.user.equals(PRIVATE_HANDLE)); + mAlphabeticalAppsList = new AlphabeticalAppsList<>(mContext, mAllAppsStore, + null, mPrivateProfileManager); + } + + @Test + public void privateProfileEnabled_allPrivateProfileViewsArePresent() { + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_PRIVATE_SPACE); + when(mAllAppsStore.getApps()).thenReturn(createAppInfoListForMainAndPrivateUser()); + when(mPrivateProfileManager.addPrivateSpaceHeader(any())) + .thenAnswer(answer(this::addPrivateSpaceHeader)); + when(mPrivateProfileManager.getCurrentState()).thenReturn(STATE_ENABLED); + + mAlphabeticalAppsList.updateItemFilter(info -> info != null + && info.user.equals(MAIN_HANDLE)); + + assertEquals(MAIN_USER_APP_COUNT + PRIVATE_SPACE_HEADER_ITEM_COUNT + + PRIVATE_USER_APP_COUNT, mAlphabeticalAppsList.getAdapterItems().size()); + assertEquals(PRIVATE_SPACE_HEADER_ITEM_COUNT, + mAlphabeticalAppsList.getAdapterItems().stream().filter(item -> + item.viewType == VIEW_TYPE_PRIVATE_SPACE_HEADER).toList().size()); + assertEquals(PRIVATE_USER_APP_COUNT, + mAlphabeticalAppsList.getAdapterItems().stream().filter(item -> + item.itemInfo != null + && item.itemInfo.user.equals(PRIVATE_HANDLE)).toList().size()); + } + + @Test + public void privateProfileDisabled_onlyPrivateProfileHeaderViewIsPresent() { + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_PRIVATE_SPACE); + when(mAllAppsStore.getApps()).thenReturn(createAppInfoListForMainAndPrivateUser()); + when(mPrivateProfileManager.addPrivateSpaceHeader(any())) + .thenAnswer(answer(this::addPrivateSpaceHeader)); + when(mPrivateProfileManager.getCurrentState()).thenReturn(STATE_DISABLED); + + mAlphabeticalAppsList.updateItemFilter(info -> info != null + && info.user.equals(MAIN_HANDLE)); + + assertEquals(MAIN_USER_APP_COUNT + PRIVATE_SPACE_HEADER_ITEM_COUNT, + mAlphabeticalAppsList.getAdapterItems().size()); + assertEquals(PRIVATE_SPACE_HEADER_ITEM_COUNT, mAlphabeticalAppsList + .getAdapterItems().stream().filter(item -> + item.viewType == VIEW_TYPE_PRIVATE_SPACE_HEADER).toList().size()); + assertEquals(0, mAlphabeticalAppsList.getAdapterItems().stream().filter(item -> + item.itemInfo != null + && item.itemInfo.user.equals(PRIVATE_HANDLE)).toList().size()); + } + + @Test + public void privateProfileTransitioning_onlyPrivateProfileHeaderViewIsPresent() { + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_PRIVATE_SPACE); + when(mAllAppsStore.getApps()).thenReturn(createAppInfoListForMainAndPrivateUser()); + when(mPrivateProfileManager.addPrivateSpaceHeader(any())) + .thenAnswer(answer(this::addPrivateSpaceHeader)); + when(mPrivateProfileManager.getCurrentState()).thenReturn(STATE_TRANSITION); + + mAlphabeticalAppsList.updateItemFilter(info -> info != null + && info.user.equals(MAIN_HANDLE)); + + assertEquals(MAIN_USER_APP_COUNT + PRIVATE_SPACE_HEADER_ITEM_COUNT, + mAlphabeticalAppsList.getAdapterItems().size()); + assertEquals(PRIVATE_SPACE_HEADER_ITEM_COUNT, mAlphabeticalAppsList + .getAdapterItems().stream().filter(item -> + item.viewType == VIEW_TYPE_PRIVATE_SPACE_HEADER).toList().size()); + assertEquals(0, mAlphabeticalAppsList.getAdapterItems().stream().filter(item -> + item.itemInfo != null + && item.itemInfo.user.equals(PRIVATE_HANDLE)).toList().size()); + } + + @Test + public void privateProfileHidden_noPrivateProfileViewIsPresent() { + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_PRIVATE_SPACE); + when(mAllAppsStore.getApps()).thenReturn(createAppInfoListForMainAndPrivateUser()); + when(mPrivateProfileManager.isPrivateSpaceHidden()).thenReturn(true); + + mAlphabeticalAppsList.updateItemFilter(info -> info != null + && info.user.equals(MAIN_HANDLE)); + + assertEquals(MAIN_USER_APP_COUNT, mAlphabeticalAppsList.getAdapterItems().size()); + assertEquals(0, mAlphabeticalAppsList.getAdapterItems().stream().filter(item -> + item.viewType == VIEW_TYPE_PRIVATE_SPACE_HEADER).toList().size()); + assertEquals(0, mAlphabeticalAppsList.getAdapterItems().stream().filter(item -> + item.itemInfo != null + && item.itemInfo.user.equals(PRIVATE_HANDLE)).toList().size()); + } + + @Test + public void privateProfileNotPresent_onlyMainUserViewsArePresent() { + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_PRIVATE_SPACE); + when(mAllAppsStore.getApps()).thenReturn(createAppInfoListForMainUser()); + + mAlphabeticalAppsList.updateItemFilter(info -> info != null + && info.user.equals(MAIN_HANDLE)); + + assertEquals(2, mAlphabeticalAppsList.getAdapterItems().size()); + assertEquals(0, mAlphabeticalAppsList.getAdapterItems().stream().filter(item -> + item.itemInfo != null + && item.itemInfo.itemType == VIEW_TYPE_PRIVATE_SPACE_HEADER) + .toList().size()); + assertEquals(0, mAlphabeticalAppsList.getAdapterItems().stream().filter(item -> + item.itemInfo != null && item.itemInfo.user.equals(PRIVATE_HANDLE)) + .toList().size()); + } + + private int addPrivateSpaceHeader(List<BaseAllAppsAdapter.AdapterItem> adapterItemList) { + adapterItemList.add(new BaseAllAppsAdapter.AdapterItem(VIEW_TYPE_PRIVATE_SPACE_HEADER)); + return adapterItemList.size(); + } + + private AppInfo[] createAppInfoListForMainUser() { + ComponentName gmailComponentName = new ComponentName(mContext, + "com.android.launcher3.tests.Activity" + "Gmail"); + AppInfo gmailAppInfo = new + AppInfo(gmailComponentName, "Gmail", MAIN_HANDLE, new Intent()); + ComponentName driveComponentName = new ComponentName(mContext, + "com.android.launcher3.tests.Activity" + "Drive"); + AppInfo driveAppInfo = new + AppInfo(driveComponentName, "Drive", MAIN_HANDLE, new Intent()); + return new AppInfo[]{gmailAppInfo, driveAppInfo}; + } + + private AppInfo[] createAppInfoListForPrivateUser() { + ComponentName privateMessengercomponentName = new ComponentName(mContext, + "com.android.launcher3.tests.Activity" + "PrivateMessenger"); + AppInfo privateMessengerAppInfo = new AppInfo(privateMessengercomponentName, + "Private Messenger", PRIVATE_HANDLE, new Intent()); + return new AppInfo[]{privateMessengerAppInfo}; + } + + private AppInfo[] createAppInfoListForMainAndPrivateUser() { + return Stream.concat(Arrays.stream(createAppInfoListForMainUser()), + Arrays.stream(createAppInfoListForPrivateUser())).toArray(AppInfo[]::new); + } + +} diff --git a/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java b/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java new file mode 100644 index 0000000000..79d00c9f2f --- /dev/null +++ b/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.allapps; + +import static com.android.launcher3.allapps.UserProfileManager.STATE_DISABLED; +import static com.android.launcher3.allapps.UserProfileManager.STATE_ENABLED; +import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED; +import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.os.Process; +import android.os.UserHandle; +import android.os.UserManager; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.launcher3.logging.StatsLogManager; +import com.android.launcher3.pm.UserCache; +import com.android.launcher3.util.UserIconInfo; + +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.MockitoAnnotations; + +import java.util.Arrays; + +@RunWith(AndroidJUnit4.class) +public class PrivateProfileManagerTest { + + private static final UserHandle MAIN_HANDLE = Process.myUserHandle(); + private static final UserHandle PRIVATE_HANDLE = new UserHandle(11); + private static final UserIconInfo MAIN_ICON_INFO = + new UserIconInfo(MAIN_HANDLE, UserIconInfo.TYPE_MAIN); + private static final UserIconInfo PRIVATE_ICON_INFO = + new UserIconInfo(PRIVATE_HANDLE, UserIconInfo.TYPE_PRIVATE); + private static final String SAFETY_CENTER_INTENT = Intent.ACTION_SAFETY_CENTER; + private static final String PS_SETTINGS_FRAGMENT_KEY = ":settings:fragment_args_key"; + private static final String PS_SETTINGS_FRAGMENT_VALUE = "AndroidPrivateSpace_personal"; + + private PrivateProfileManager mPrivateProfileManager; + @Mock + private ActivityAllAppsContainerView mActivityAllAppsContainerView; + @Mock + private StatsLogManager mStatsLogManager; + @Mock + private UserCache mUserCache; + @Mock + private UserManager mUserManager; + @Mock + private Context mContext; + @Mock + private AllAppsStore mAllAppsStore; + @Mock + private PackageManager mPackageManager; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + when(mUserCache.getUserProfiles()) + .thenReturn(Arrays.asList(MAIN_HANDLE, PRIVATE_HANDLE)); + when(mUserCache.getUserInfo(Process.myUserHandle())).thenReturn(MAIN_ICON_INFO); + when(mUserCache.getUserInfo(PRIVATE_HANDLE)).thenReturn(PRIVATE_ICON_INFO); + when(mActivityAllAppsContainerView.getContext()).thenReturn(mContext); + when(mActivityAllAppsContainerView.getAppsStore()).thenReturn(mAllAppsStore); + when(mContext.getPackageManager()).thenReturn(mPackageManager); + when(mPackageManager.resolveActivity(any(), any())).thenReturn(new ResolveInfo()); + mPrivateProfileManager = new PrivateProfileManager(mUserManager, + mActivityAllAppsContainerView, mStatsLogManager, mUserCache); + } + + @Test + public void lockPrivateProfile_requestsQuietModeAsTrue() throws Exception { + when(mAllAppsStore.hasModelFlag(FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED)).thenReturn(false); + + mPrivateProfileManager.lockPrivateProfile(); + + awaitTasksCompleted(); + Mockito.verify(mUserManager).requestQuietModeEnabled(true, PRIVATE_HANDLE); + } + + @Test + public void unlockPrivateProfile_requestsQuietModeAsFalse() throws Exception { + when(mAllAppsStore.hasModelFlag(FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED)).thenReturn(true); + + mPrivateProfileManager.unlockPrivateProfile(); + + awaitTasksCompleted(); + Mockito.verify(mUserManager).requestQuietModeEnabled(false, PRIVATE_HANDLE); + } + + @Test + public void quietModeFlagPresent_privateSpaceIsResetToDisabled() { + PrivateProfileManager privateProfileManager = spy(mPrivateProfileManager); + doNothing().when(privateProfileManager).resetPrivateSpaceDecorator(anyInt()); + when(mAllAppsStore.hasModelFlag(FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED)) + .thenReturn(false, true); + + // In first call the state should be disabled. + privateProfileManager.reset(); + assertEquals(STATE_ENABLED, privateProfileManager.getCurrentState()); + + // In the next call the state should be disabled. + privateProfileManager.reset(); + assertEquals(STATE_DISABLED, privateProfileManager.getCurrentState()); + } + + @Test + public void openPrivateSpaceSettings_triggersSecurityAndPrivacyIntent() { + Intent expectedIntent = new Intent(SAFETY_CENTER_INTENT); + expectedIntent.putExtra(PS_SETTINGS_FRAGMENT_KEY, PS_SETTINGS_FRAGMENT_VALUE); + ArgumentCaptor<Intent> acIntent = ArgumentCaptor.forClass(Intent.class); + + mPrivateProfileManager.openPrivateSpaceSettings(); + + Mockito.verify(mContext).startActivity(acIntent.capture()); + Intent actualIntent = acIntent.getValue(); + assertEquals(expectedIntent.getAction(), actualIntent.getAction()); + assertEquals(expectedIntent.getStringExtra(PS_SETTINGS_FRAGMENT_KEY), + actualIntent.getStringExtra(PS_SETTINGS_FRAGMENT_KEY)); + } + + private static void awaitTasksCompleted() throws Exception { + UI_HELPER_EXECUTOR.submit(() -> null).get(); + } +} diff --git a/tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewControllerTest.java b/tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewControllerTest.java new file mode 100644 index 0000000000..bc09cddba9 --- /dev/null +++ b/tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewControllerTest.java @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.allapps; + +import static androidx.test.core.app.ApplicationProvider.getApplicationContext; + +import static com.android.launcher3.allapps.UserProfileManager.STATE_DISABLED; +import static com.android.launcher3.allapps.UserProfileManager.STATE_ENABLED; +import static com.android.launcher3.allapps.UserProfileManager.STATE_TRANSITION; +import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.ImageView; +import android.widget.RelativeLayout; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.launcher3.R; +import com.android.launcher3.util.ActivityContextWrapper; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class PrivateSpaceHeaderViewControllerTest { + + private static final int CONTAINER_HEADER_ELEMENT_COUNT = 1; + private static final int LOCK_UNLOCK_BUTTON_COUNT = 1; + private static final int PS_SETTINGS_BUTTON_COUNT_VISIBLE = 1; + private static final int PS_SETTINGS_BUTTON_COUNT_INVISIBLE = 0; + private static final int PS_TRANSITION_IMAGE_COUNT = 1; + + private Context mContext; + private LayoutInflater mLayoutInflater; + private PrivateSpaceHeaderViewController mPsHeaderViewController; + private RelativeLayout mPsHeaderLayout; + @Mock + private PrivateProfileManager mPrivateProfileManager; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = new ActivityContextWrapper(getApplicationContext()); + mLayoutInflater = LayoutInflater.from(getApplicationContext()); + mPsHeaderViewController = new PrivateSpaceHeaderViewController(mPrivateProfileManager); + mPsHeaderLayout = (RelativeLayout) mLayoutInflater.inflate(R.layout.private_space_header, + null); + } + + @Test + public void privateProfileDisabled_psHeaderContainsLockedView() throws Exception { + Bitmap unlockButton = getBitmap(mContext.getDrawable(R.drawable.bg_ps_unlock_button)); + when(mPrivateProfileManager.getCurrentState()).thenReturn(STATE_DISABLED); + + mPsHeaderViewController.addPrivateSpaceHeaderViewElements(mPsHeaderLayout); + awaitTasksCompleted(); + + int totalContainerHeaderView = 0; + int totalLockUnlockButtonView = 0; + for (int i = 0; i < mPsHeaderLayout.getChildCount(); i++) { + View view = mPsHeaderLayout.getChildAt(i); + if (view.getId() == R.id.ps_container_header) { + totalContainerHeaderView += 1; + assertEquals(View.VISIBLE, view.getVisibility()); + } else if (view.getId() == R.id.ps_lock_unlock_button + && view instanceof ImageView imageView) { + totalLockUnlockButtonView += 1; + assertEquals(View.VISIBLE, view.getVisibility()); + getBitmap(imageView.getDrawable()).sameAs(unlockButton); + } else { + assertEquals(View.GONE, view.getVisibility()); + } + } + assertEquals(CONTAINER_HEADER_ELEMENT_COUNT, totalContainerHeaderView); + assertEquals(LOCK_UNLOCK_BUTTON_COUNT, totalLockUnlockButtonView); + } + + @Test + public void privateProfileEnabled_psHeaderContainsUnlockedView() throws Exception { + Bitmap lockImage = getBitmap(mContext.getDrawable(R.drawable.bg_ps_lock_button)); + Bitmap settingsImage = getBitmap(mContext.getDrawable(R.drawable.bg_ps_settings_button)); + when(mPrivateProfileManager.getCurrentState()).thenReturn(STATE_ENABLED); + when(mPrivateProfileManager.isPrivateSpaceSettingsAvailable()).thenReturn(true); + + mPsHeaderViewController.addPrivateSpaceHeaderViewElements(mPsHeaderLayout); + awaitTasksCompleted(); + + int totalContainerHeaderView = 0; + int totalLockUnlockButtonView = 0; + int totalSettingsImageView = 0; + for (int i = 0; i < mPsHeaderLayout.getChildCount(); i++) { + View view = mPsHeaderLayout.getChildAt(i); + if (view.getId() == R.id.ps_container_header) { + totalContainerHeaderView += 1; + assertEquals(View.VISIBLE, view.getVisibility()); + } else if (view.getId() == R.id.ps_lock_unlock_button + && view instanceof ImageView imageView) { + totalLockUnlockButtonView += 1; + assertEquals(View.VISIBLE, view.getVisibility()); + getBitmap(imageView.getDrawable()).sameAs(lockImage); + } else if (view.getId() == R.id.ps_settings_button + && view instanceof ImageView imageView) { + totalSettingsImageView += 1; + assertEquals(View.VISIBLE, view.getVisibility()); + getBitmap(imageView.getDrawable()).sameAs(settingsImage); + } else { + assertEquals(View.GONE, view.getVisibility()); + } + } + assertEquals(CONTAINER_HEADER_ELEMENT_COUNT, totalContainerHeaderView); + assertEquals(LOCK_UNLOCK_BUTTON_COUNT, totalLockUnlockButtonView); + assertEquals(PS_SETTINGS_BUTTON_COUNT_VISIBLE, totalSettingsImageView); + } + + @Test + public void privateProfileEnabledAndNoSettingsIntent_psHeaderContainsUnlockedView() + throws Exception { + Bitmap lockImage = getBitmap(mContext.getDrawable(R.drawable.bg_ps_lock_button)); + when(mPrivateProfileManager.getCurrentState()).thenReturn(STATE_ENABLED); + when(mPrivateProfileManager.isPrivateSpaceSettingsAvailable()).thenReturn(false); + + mPsHeaderViewController.addPrivateSpaceHeaderViewElements(mPsHeaderLayout); + awaitTasksCompleted(); + + int totalContainerHeaderView = 0; + int totalLockUnlockButtonView = 0; + int totalSettingsImageView = 0; + for (int i = 0; i < mPsHeaderLayout.getChildCount(); i++) { + View view = mPsHeaderLayout.getChildAt(i); + if (view.getId() == R.id.ps_container_header) { + totalContainerHeaderView += 1; + assertEquals(View.VISIBLE, view.getVisibility()); + } else if (view.getId() == R.id.ps_lock_unlock_button + && view instanceof ImageView imageView) { + totalLockUnlockButtonView += 1; + assertEquals(View.VISIBLE, view.getVisibility()); + getBitmap(imageView.getDrawable()).sameAs(lockImage); + } else { + assertEquals(View.GONE, view.getVisibility()); + } + } + assertEquals(CONTAINER_HEADER_ELEMENT_COUNT, totalContainerHeaderView); + assertEquals(LOCK_UNLOCK_BUTTON_COUNT, totalLockUnlockButtonView); + assertEquals(PS_SETTINGS_BUTTON_COUNT_INVISIBLE, totalSettingsImageView); + } + + @Test + public void privateProfileTransitioning_psHeaderContainsTransitionView() throws Exception { + Bitmap transitionImage = getBitmap(mContext.getDrawable(R.drawable.bg_ps_transition_image)); + when(mPrivateProfileManager.getCurrentState()).thenReturn(STATE_TRANSITION); + + mPsHeaderViewController.addPrivateSpaceHeaderViewElements(mPsHeaderLayout); + awaitTasksCompleted(); + + int totalContainerHeaderView = 0; + int totalLockUnlockButtonView = 0; + for (int i = 0; i < mPsHeaderLayout.getChildCount(); i++) { + View view = mPsHeaderLayout.getChildAt(i); + if (view.getId() == R.id.ps_container_header) { + totalContainerHeaderView += 1; + assertEquals(View.VISIBLE, view.getVisibility()); + } else if (view.getId() == R.id.ps_transition_image + && view instanceof ImageView imageView) { + totalLockUnlockButtonView += 1; + assertEquals(View.VISIBLE, view.getVisibility()); + getBitmap(imageView.getDrawable()).sameAs(transitionImage); + } else { + assertEquals(View.GONE, view.getVisibility()); + } + } + assertEquals(CONTAINER_HEADER_ELEMENT_COUNT, totalContainerHeaderView); + assertEquals(PS_TRANSITION_IMAGE_COUNT, totalLockUnlockButtonView); + } + + private Bitmap getBitmap(Drawable drawable) { + Bitmap result; + if (drawable instanceof BitmapDrawable) { + result = ((BitmapDrawable) drawable).getBitmap(); + } else { + int width = drawable.getIntrinsicWidth(); + int height = drawable.getIntrinsicHeight(); + // Some drawables have no intrinsic width - e.g. solid colours. + if (width <= 0) { + width = 1; + } + if (height <= 0) { + height = 1; + } + + result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(result); + drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); + drawable.draw(canvas); + } + return result; + } + + private static void awaitTasksCompleted() throws Exception { + UI_HELPER_EXECUTOR.submit(() -> null).get(); + } +} diff --git a/tests/src/com/android/launcher3/allapps/TaplKeyboardFocusTest.java b/tests/src/com/android/launcher3/allapps/TaplKeyboardFocusTest.java new file mode 100644 index 0000000000..ee32e97744 --- /dev/null +++ b/tests/src/com/android/launcher3/allapps/TaplKeyboardFocusTest.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.allapps; + +import static com.android.launcher3.ui.AbstractLauncherUiTest.initialize; +import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL; +import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import android.view.KeyEvent; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import com.android.launcher3.LauncherState; +import com.android.launcher3.tapl.HomeAllApps; +import com.android.launcher3.ui.AbstractLauncherUiTest; +import com.android.launcher3.util.rule.TestStabilityRule; +import com.android.launcher3.views.ActivityContext; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class TaplKeyboardFocusTest extends AbstractLauncherUiTest { + + @Before + public void setUp() throws Exception { + super.setUp(); + initialize(this); + } + + @Test + public void testAllAppsFocusApp() { + final HomeAllApps allApps = mLauncher.goHome().switchToAllApps(); + assertTrue("Launcher internal state is not All Apps", + isInState(() -> LauncherState.ALL_APPS)); + allApps.freeze(); + try { + mLauncher.pressAndHoldKeyCode(KeyEvent.KEYCODE_DPAD_DOWN, 0); + executeOnLauncher(launcher -> assertNotNull("No focused child.", + launcher.getAppsView().getActiveRecyclerView().getApps().getFocusedChild())); + } finally { + allApps.unfreeze(); + } + } + + @Test + public void testAllAppsExitSearchAndFocusApp() { + final HomeAllApps allApps = mLauncher.goHome().switchToAllApps(); + assertTrue("Launcher internal state is not All Apps", + isInState(() -> LauncherState.ALL_APPS)); + allApps.freeze(); + try { + executeOnLauncher(launcher -> launcher.getAppsView().getSearchView().requestFocus()); + waitForLauncherCondition("Search view does not have focus.", + launcher -> launcher.getAppsView().getSearchView().hasFocus()); + + mLauncher.pressAndHoldKeyCode(KeyEvent.KEYCODE_DPAD_DOWN, 0); + executeOnLauncher(launcher -> assertNotNull("No focused child.", + launcher.getAppsView().getActiveRecyclerView().getApps().getFocusedChild())); + } finally { + allApps.unfreeze(); + } + } + + @Test + @TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/311410127 + public void testAllAppsExitSearchAndFocusSearchResults() { + final HomeAllApps allApps = mLauncher.goHome().switchToAllApps(); + assertTrue("Launcher internal state is not All Apps", + isInState(() -> LauncherState.ALL_APPS)); + allApps.freeze(); + try { + executeOnLauncher(launcher -> launcher.getAppsView().getSearchView().requestFocus()); + waitForLauncherCondition("Search view does not have focus.", + launcher -> launcher.getAppsView().getSearchView().hasFocus()); + + mLauncher.pressAndHoldKeyCode(KeyEvent.KEYCODE_C, 0); + waitForLauncherCondition("Search view not active.", + launcher -> launcher.getAppsView().getActiveRecyclerView() + instanceof SearchRecyclerView); + mLauncher.unpressKeyCode(KeyEvent.KEYCODE_C, 0); + + executeOnLauncher(launcher -> launcher.getAppsView().getSearchUiManager().getEditText() + .hideKeyboard(/* clearFocus= */ false)); + waitForLauncherCondition("Keyboard still visible.", + ActivityContext::isSoftwareKeyboardHidden); + + mLauncher.pressAndHoldKeyCode(KeyEvent.KEYCODE_DPAD_DOWN, 0); + mLauncher.unpressKeyCode(KeyEvent.KEYCODE_DPAD_DOWN, 0); + waitForLauncherCondition("No focused child", launcher -> + launcher.getAppsView().getActiveRecyclerView().getApps().getFocusedChild() + != null); + } finally { + allApps.unfreeze(); + } + } +} diff --git a/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllApps.java b/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllApps.java new file mode 100644 index 0000000000..b4a5169c30 --- /dev/null +++ b/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllApps.java @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.allapps; + +import static com.android.launcher3.util.TestUtil.expectFail; +import static com.android.launcher3.ui.AbstractLauncherUiTest.initialize; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeFalse; +import static org.junit.Assume.assumeTrue; + +import android.content.Intent; +import android.platform.test.annotations.PlatinumTest; + +import androidx.test.filters.FlakyTest; +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.launcher3.LauncherState; +import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.tapl.AllApps; +import com.android.launcher3.ui.AbstractLauncherUiTest; +import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape; + +import org.junit.Before; +import org.junit.Test; + +/** + * Test that we can open and close the all apps in multiple situations. + * The test runs in Out of process (Oop) and in process. + */ +public class TaplOpenCloseAllApps extends AbstractLauncherUiTest { + + public static final String READ_DEVICE_CONFIG_PERMISSION = + "android.permission.READ_DEVICE_CONFIG"; + + /** + * Calls static method initialize + */ + @Before + public void setUp() throws Exception { + super.setUp(); + initialize(this); + } + + /** + * Make sure we can go home after pressing the context menu on an Icon on the AllApps. + */ + @Test + public void testPressHomeOnAllAppsContextMenu() { + final AllApps allApps = mLauncher.getWorkspace().switchToAllApps(); + allApps.freeze(); + try { + allApps.getAppIcon("TestActivity7").openMenu(); + } finally { + allApps.unfreeze(); + } + mLauncher.goHome(); + } + + /** + * Make sure we can open AllApps from the Workspace. + */ + @Test + @PortraitLandscape + public void testWorkspaceSwitchToAllApps() { + assertNotNull("switchToAllApps() returned null", + mLauncher.getWorkspace().switchToAllApps()); + assertTrue("Launcher internal state is not All Apps", + isInState(() -> LauncherState.ALL_APPS)); + } + + /** + * Make sure we can go to Workspace from AllApps + */ + @Test + @PortraitLandscape + public void testAllAppsSwitchToWorkspace() { + assertNotNull("switchToWorkspace() returned null", + mLauncher.getWorkspace().switchToAllApps() + .switchToWorkspace(/* swipeDown= */ true)); + assertTrue("Launcher internal state is not Workspace", + isInState(() -> LauncherState.NORMAL)); + } + + /** + * Make sure the swipe up gesture can take us back to the workspace from AllApps + */ + @PlatinumTest(focusArea = "launcher") + @Test + @PortraitLandscape + public void testAllAppsSwipeUpToWorkspace() { + assertNotNull("testAllAppsSwipeUpToWorkspace() returned null", + mLauncher.getWorkspace().switchToAllApps() + .switchToWorkspace(/* swipeDown= */ false)); + assertTrue("Launcher internal state is not Workspace", + isInState(() -> LauncherState.NORMAL)); + } + + /** + * Make sure we can go to the Workspace from AllApps on tablets by tapping on the background. + */ + @Test + @PortraitLandscape + public void testAllAppsDeadzoneForTablet() { + assumeTrue(mLauncher.isTablet()); + + mLauncher.getWorkspace().switchToAllApps().dismissByTappingOutsideForTablet( + true /* tapRight */); + mLauncher.getWorkspace().switchToAllApps().dismissByTappingOutsideForTablet( + false /* tapRight */); + } + + /** + * Make sure that AllApps closes when pressing the home button + */ + @Test + @PortraitLandscape + public void testAllAppsFromHome() { + // Test opening all apps + assertNotNull("switchToAllApps() returned null", + mLauncher.getWorkspace().switchToAllApps()); + + runAllAppsTest(mLauncher.getAllApps()); + + // Testing pressHome. + assertTrue("Launcher internal state is not All Apps", + isInState(() -> LauncherState.ALL_APPS)); + assertNotNull("pressHome returned null", mLauncher.goHome()); + assertTrue("Launcher internal state is not Home", + isInState(() -> LauncherState.NORMAL)); + assertNotNull("getHome returned null", mLauncher.getWorkspace()); + } + + /** + * Makes sure the state of AllApps is correct. + */ + public void runAllAppsTest(AllApps allApps) { + allApps.freeze(); + try { + assertNotNull("allApps parameter is null", allApps); + + assertTrue( + "Launcher internal state is not All Apps", + isInState(() -> LauncherState.ALL_APPS)); + + // Test flinging forward and backward. + executeOnLauncher(launcher -> assertEquals( + "All Apps started in already scrolled state", 0, + getAllAppsScroll(launcher))); + + allApps.flingForward(); + assertTrue("Launcher internal state is not All Apps", + isInState(() -> LauncherState.ALL_APPS)); + final Integer flingForwardY = getFromLauncher( + launcher -> getAllAppsScroll(launcher)); + executeOnLauncher( + launcher -> assertTrue("flingForward() didn't scroll App Apps", + flingForwardY > 0)); + + allApps.flingBackward(); + assertTrue( + "Launcher internal state is not All Apps", + isInState(() -> LauncherState.ALL_APPS)); + final Integer flingBackwardY = getFromLauncher( + launcher -> getAllAppsScroll(launcher)); + executeOnLauncher(launcher -> assertTrue("flingBackward() didn't scroll App Apps", + flingBackwardY < flingForwardY)); + + // Test scrolling down to YouTube. + assertNotNull("All apps: can't find YouTube", allApps.getAppIcon("YouTube")); + // Test scrolling up to Camera. + assertNotNull("All apps: can't find Camera", allApps.getAppIcon("Camera")); + // Test failing to find a non-existing app. + final AllApps allAppsFinal = allApps; + expectFail("All apps: could find a non-existing app", + () -> allAppsFinal.getAppIcon("NO APP")); + + assertTrue( + "Launcher internal state is not All Apps", + isInState(() -> LauncherState.ALL_APPS)); + } finally { + allApps.unfreeze(); + } + } + + /** + * Makes sure that when pressing back when AllApps is open we go back to the Home screen. + */ + @FlakyTest(bugId = 256615483) + @Test + @PortraitLandscape + public void testPressBackFromAllAppsToHome() { + InstrumentationRegistry.getInstrumentation().getUiAutomation().adoptShellPermissionIdentity( + READ_DEVICE_CONFIG_PERMISSION); + assumeFalse(FeatureFlags.ENABLE_BACK_SWIPE_LAUNCHER_ANIMATION.get()); + mLauncher + .getWorkspace() + .switchToAllApps() + .pressBackToWorkspace(); + waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL); + startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR)); + mLauncher.pressBack(); + mLauncher.getWorkspace(); + waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL); + } + + @Test + public void testDismissAllAppsWithEscKey() { + mLauncher.goHome().switchToAllApps().dismissByEscKey(); + waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL); + } +} diff --git a/tests/src/com/android/launcher3/allapps/TaplTestsAllAppsIconsWorking.java b/tests/src/com/android/launcher3/allapps/TaplTestsAllAppsIconsWorking.java new file mode 100644 index 0000000000..9f6bbdfbca --- /dev/null +++ b/tests/src/com/android/launcher3/allapps/TaplTestsAllAppsIconsWorking.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.allapps; + +import static com.android.launcher3.ui.AbstractLauncherUiTest.initialize; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import com.android.launcher3.LauncherState; +import com.android.launcher3.tapl.AppIcon; +import com.android.launcher3.tapl.HomeAllApps; +import com.android.launcher3.ui.AbstractLauncherUiTest; +import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape; + +import org.junit.Before; +import org.junit.Test; + +/** + * The test runs in Out of process (Oop) and in process. + * Makes sure the basic behaviors of Icons on AllApps are working. + */ +public class TaplTestsAllAppsIconsWorking extends AbstractLauncherUiTest { + + @Before + public void setUp() throws Exception { + super.setUp(); + initialize(this); + } + + /** + * Makes sure we can launch an icon from All apps + */ + @Test + @PortraitLandscape + public void testAppIconLaunchFromAllAppsFromHome() { + final HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps(); + assertTrue("Launcher internal state is not All Apps", + isInState(() -> LauncherState.ALL_APPS)); + + allApps.freeze(); + try { + final AppIcon app = allApps.getAppIcon("TestActivity7"); + assertNotNull("AppIcon.launch returned null", app.launch(getAppPackageName())); + executeOnLauncher(launcher -> assertTrue( + "Launcher activity is the top activity; expecting another activity to be the " + + "top one", + isInLaunchedApp(launcher))); + } finally { + allApps.unfreeze(); + } + } +} diff --git a/tests/src/com/android/launcher3/appiconmenu/TaplAppIconMenuTest.java b/tests/src/com/android/launcher3/appiconmenu/TaplAppIconMenuTest.java new file mode 100644 index 0000000000..a1f2cefb4c --- /dev/null +++ b/tests/src/com/android/launcher3/appiconmenu/TaplAppIconMenuTest.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.appiconmenu; + +import static com.android.launcher3.util.TestConstants.AppNames.TEST_APP_NAME; +import static com.android.launcher3.ui.AbstractLauncherUiTest.initialize; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import android.platform.test.annotations.PlatinumTest; + +import com.android.launcher3.Launcher; +import com.android.launcher3.popup.ArrowPopup; +import com.android.launcher3.tapl.AllApps; +import com.android.launcher3.tapl.AppIconMenu; +import com.android.launcher3.tapl.AppIconMenuItem; +import com.android.launcher3.tapl.HomeAllApps; +import com.android.launcher3.ui.AbstractLauncherUiTest; +import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape; + +import org.junit.Before; +import org.junit.Test; + +/** + * This test run in both Out of process (Oop) and in-process (Ipc). + * Tests the AppIconMenu (the menu that appears when you long press an app icon) and also make sure + * we can launch a shortcut from it. + */ +public class TaplAppIconMenuTest extends AbstractLauncherUiTest { + + @Before + public void setUp() throws Exception { + super.setUp(); + initialize(this); + } + + private boolean isOptionsPopupVisible(Launcher launcher) { + final ArrowPopup<?> popup = launcher.getOptionsPopup(); + return popup != null && popup.isShown(); + } + + /** + * Open All apps then open the AppIconMenu then launch a shortcut from the menu and make sure it + * launches. + */ + @Test + @PortraitLandscape + @PlatinumTest(focusArea = "launcher") + public void testLaunchMenuItem() { + final AllApps allApps = mLauncher.getWorkspace().switchToAllApps(); + allApps.freeze(); + try { + final AppIconMenu menu = allApps.getAppIcon(TEST_APP_NAME).openDeepShortcutMenu(); + + executeOnLauncher( + launcher -> assertTrue("Launcher internal state didn't switch to Showing Menu", + isOptionsPopupVisible(launcher))); + + final AppIconMenuItem menuItem = menu.getMenuItem(1); + assertEquals("Wrong menu item", "Shortcut 2", menuItem.getText()); + menuItem.launch(getAppPackageName()); + } finally { + allApps.unfreeze(); + } + } + + /** + * Drag icon from AllApps to the workspace and then open the AppIconMenu and launch a shortcut + * from it. + */ + @PlatinumTest(focusArea = "launcher") + @Test + public void testLaunchHomeScreenMenuItem() { + // Drag the test app icon to home screen and open short cut menu from the icon + final HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps(); + allApps.freeze(); + try { + allApps.getAppIcon(TEST_APP_NAME).dragToWorkspace(false, false); + final AppIconMenu menu = mLauncher.getWorkspace().getWorkspaceAppIcon( + TEST_APP_NAME).openDeepShortcutMenu(); + + executeOnLauncher( + launcher -> assertTrue("Launcher internal state didn't switch to Showing Menu", + isOptionsPopupVisible(launcher))); + + final AppIconMenuItem menuItem = menu.getMenuItem(1); + assertEquals("Wrong menu item", "Shortcut 2", menuItem.getText()); + menuItem.launch(getAppPackageName()); + } finally { + allApps.unfreeze(); + } + } +} diff --git a/tests/src/com/android/launcher3/celllayout/CellLayoutTestUtils.java b/tests/src/com/android/launcher3/celllayout/CellLayoutTestUtils.java index 86a7bd3df0..d8ae74bf3c 100644 --- a/tests/src/com/android/launcher3/celllayout/CellLayoutTestUtils.java +++ b/tests/src/com/android/launcher3/celllayout/CellLayoutTestUtils.java @@ -21,6 +21,7 @@ import android.view.View; import com.android.launcher3.CellLayout; import com.android.launcher3.Launcher; +import com.android.launcher3.celllayout.board.CellLayoutBoard; import com.android.launcher3.views.DoubleShadowBubbleTextView; import java.util.ArrayList; @@ -60,9 +61,7 @@ public class CellLayoutTestUtils { } public static CellLayoutBoard viewsToBoard(List<View> views, int width, int height) { - CellLayoutBoard board = new CellLayoutBoard(); - board.mWidth = width; - board.mHeight = height; + CellLayoutBoard board = new CellLayoutBoard(width, height); for (View callView : views) { CellLayoutLayoutParams params = (CellLayoutLayoutParams) callView.getLayoutParams(); diff --git a/tests/src/com/android/launcher3/celllayout/CellPosMapperTest.java b/tests/src/com/android/launcher3/celllayout/CellPosMapperTest.java index 29efb188bb..b8bf362740 100644 --- a/tests/src/com/android/launcher3/celllayout/CellPosMapperTest.java +++ b/tests/src/com/android/launcher3/celllayout/CellPosMapperTest.java @@ -39,44 +39,46 @@ public class CellPosMapperTest { @Test public void testMapModelToPresenter_default() { - assertThat(CellPosMapper.DEFAULT.mapModelToPresenter( + CellPosMapper mapper = CellPosMapper.DEFAULT; + assertThat(mapper.mapModelToPresenter( createInfo(0, 0, 0, CONTAINER_DESKTOP))).isEqualTo(new CellPos(0, 0, 0)); - assertThat(CellPosMapper.DEFAULT.mapModelToPresenter( + assertThat(mapper.mapModelToPresenter( createInfo(0, 0, 1, CONTAINER_DESKTOP))).isEqualTo(new CellPos(0, 0, 1)); - assertThat(CellPosMapper.DEFAULT.mapModelToPresenter( + assertThat(mapper.mapModelToPresenter( createInfo(5, 0, 1, CONTAINER_DESKTOP))).isEqualTo(new CellPos(5, 0, 1)); - assertThat(CellPosMapper.DEFAULT.mapModelToPresenter( + assertThat(mapper.mapModelToPresenter( createInfo(5, 0, 0, CONTAINER_DESKTOP))).isEqualTo(new CellPos(5, 0, 0)); - assertThat(CellPosMapper.DEFAULT.mapModelToPresenter( + assertThat(mapper.mapModelToPresenter( createInfo(0, 0, 0, CONTAINER_HOTSEAT))).isEqualTo(new CellPos(0, 0, 0)); - assertThat(CellPosMapper.DEFAULT.mapModelToPresenter( + assertThat(mapper.mapModelToPresenter( createInfo(0, 0, 1, CONTAINER_HOTSEAT))).isEqualTo(new CellPos(0, 0, 1)); - assertThat(CellPosMapper.DEFAULT.mapModelToPresenter( + assertThat(mapper.mapModelToPresenter( createInfo(5, 0, 1, CONTAINER_HOTSEAT))).isEqualTo(new CellPos(5, 0, 1)); - assertThat(CellPosMapper.DEFAULT.mapModelToPresenter( + assertThat(mapper.mapModelToPresenter( createInfo(5, 0, 0, CONTAINER_HOTSEAT))).isEqualTo(new CellPos(5, 0, 0)); } @Test public void testMapPresenterToModel_default() { - assertThat(CellPosMapper.DEFAULT.mapPresenterToModel( + CellPosMapper mapper = CellPosMapper.DEFAULT; + assertThat(mapper.mapPresenterToModel( 0, 0, 0, CONTAINER_DESKTOP)).isEqualTo(new CellPos(0, 0, 0)); - assertThat(CellPosMapper.DEFAULT.mapPresenterToModel( + assertThat(mapper.mapPresenterToModel( 0, 0, 1, CONTAINER_DESKTOP)).isEqualTo(new CellPos(0, 0, 1)); - assertThat(CellPosMapper.DEFAULT.mapPresenterToModel( + assertThat(mapper.mapPresenterToModel( 5, 0, 1, CONTAINER_DESKTOP)).isEqualTo(new CellPos(5, 0, 1)); - assertThat(CellPosMapper.DEFAULT.mapPresenterToModel( + assertThat(mapper.mapPresenterToModel( 5, 0, 0, CONTAINER_DESKTOP)).isEqualTo(new CellPos(5, 0, 0)); - assertThat(CellPosMapper.DEFAULT.mapPresenterToModel( + assertThat(mapper.mapPresenterToModel( 0, 0, 0, CONTAINER_HOTSEAT)).isEqualTo(new CellPos(0, 0, 0)); - assertThat(CellPosMapper.DEFAULT.mapPresenterToModel( - 0, 0, 1, CONTAINER_HOTSEAT)).isEqualTo(new CellPos(0, 0, 1)); - assertThat(CellPosMapper.DEFAULT.mapPresenterToModel( - 5, 0, 1, CONTAINER_HOTSEAT)).isEqualTo(new CellPos(5, 0, 1)); - assertThat(CellPosMapper.DEFAULT.mapPresenterToModel( - 5, 0, 0, CONTAINER_HOTSEAT)).isEqualTo(new CellPos(5, 0, 0)); + assertThat(mapper.mapPresenterToModel( + 0, 0, 1, CONTAINER_HOTSEAT)).isEqualTo(new CellPos(0, 0, 0)); + assertThat(mapper.mapPresenterToModel( + 5, 0, 1, CONTAINER_HOTSEAT)).isEqualTo(new CellPos(5, 0, 5)); + assertThat(mapper.mapPresenterToModel( + 5, 0, 0, CONTAINER_HOTSEAT)).isEqualTo(new CellPos(5, 0, 5)); } @Test @@ -116,11 +118,33 @@ public class CellPosMapperTest { assertThat(mapper.mapPresenterToModel( 0, 0, 0, CONTAINER_HOTSEAT)).isEqualTo(new CellPos(0, 0, 0)); assertThat(mapper.mapPresenterToModel( - 0, 0, 1, CONTAINER_HOTSEAT)).isEqualTo(new CellPos(0, 0, 1)); + 0, 0, 1, CONTAINER_HOTSEAT)).isEqualTo(new CellPos(0, 0, 0)); + assertThat(mapper.mapPresenterToModel( + 5, 0, 1, CONTAINER_HOTSEAT)).isEqualTo(new CellPos(5, 0, 5)); + assertThat(mapper.mapPresenterToModel( + 5, 0, 0, CONTAINER_HOTSEAT)).isEqualTo(new CellPos(5, 0, 5)); + } + + @Test + public void testMapPresenterToModel_VerticalHotseat() { + CellPosMapper mapper = new CellPosMapper(true, 6); + assertThat(mapper.mapPresenterToModel( + 0, 0, 0, CONTAINER_DESKTOP)).isEqualTo(new CellPos(0, 0, 0)); + assertThat(mapper.mapPresenterToModel( + 0, 0, 1, CONTAINER_DESKTOP)).isEqualTo(new CellPos(0, 0, 1)); + assertThat(mapper.mapPresenterToModel( + 5, 0, 1, CONTAINER_DESKTOP)).isEqualTo(new CellPos(5, 0, 1)); + assertThat(mapper.mapPresenterToModel( + 5, 0, 0, CONTAINER_DESKTOP)).isEqualTo(new CellPos(5, 0, 0)); + + assertThat(mapper.mapPresenterToModel( + 0, 0, 0, CONTAINER_HOTSEAT)).isEqualTo(new CellPos(0, 0, 5)); + assertThat(mapper.mapPresenterToModel( + 0, 0, 1, CONTAINER_HOTSEAT)).isEqualTo(new CellPos(0, 0, 5)); assertThat(mapper.mapPresenterToModel( - 5, 0, 1, CONTAINER_HOTSEAT)).isEqualTo(new CellPos(5, 0, 1)); + 0, 5, 1, CONTAINER_HOTSEAT)).isEqualTo(new CellPos(0, 5, 0)); assertThat(mapper.mapPresenterToModel( - 5, 0, 0, CONTAINER_HOTSEAT)).isEqualTo(new CellPos(5, 0, 0)); + 0, 5, 0, CONTAINER_HOTSEAT)).isEqualTo(new CellPos(0, 5, 0)); } private ItemInfo createInfo(int cellX, int cellY, int screen, int container) { diff --git a/tests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java b/tests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java index bf7a21c32d..fb364ad44d 100644 --- a/tests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java +++ b/tests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java @@ -29,13 +29,13 @@ import androidx.test.uiautomator.UiDevice; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherModel; import com.android.launcher3.LauncherSettings; -import com.android.launcher3.model.BgDataModel.Callbacks; import com.android.launcher3.model.ModelDbController; import com.android.launcher3.model.data.FolderInfo; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction; import com.android.launcher3.tapl.LauncherInstrumentation; import com.android.launcher3.util.ContentWriter; +import com.android.launcher3.util.ModelTestExtensions; import java.util.ArrayList; import java.util.function.Supplier; @@ -60,8 +60,7 @@ public class FavoriteItemsTransaction { public void commit() { LauncherModel model = LauncherAppState.getInstance(mContext).getModel(); // Load the model once so that there is no pending migration: - loadModelSync(model); - + ModelTestExtensions.INSTANCE.loadModelSync(model); runOnExecutorSync(MODEL_EXECUTOR, () -> { ModelDbController controller = model.getModelDbController(); // Migrate any previous data so that the DB state is correct @@ -105,16 +104,7 @@ public class FavoriteItemsTransaction { // Reload model runOnExecutorSync(MAIN_EXECUTOR, model::forceReload); - loadModelSync(model); - } - - private void loadModelSync(LauncherModel model) { - Callbacks mockCb = new Callbacks() { }; - runOnExecutorSync(MAIN_EXECUTOR, () -> model.addCallbacksAndLoad(mockCb)); - runOnExecutorSync(MODEL_EXECUTOR, () -> { }); - - runOnExecutorSync(MAIN_EXECUTOR, () -> { }); - runOnExecutorSync(MAIN_EXECUTOR, () -> model.removeCallbacks(mockCb)); + ModelTestExtensions.INSTANCE.loadModelSync(model); } /** diff --git a/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java b/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java index 91a0634f3d..e1af7746fc 100644 --- a/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java +++ b/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java @@ -22,7 +22,7 @@ import static org.junit.Assert.assertTrue; import android.content.Context; import android.graphics.Point; -import android.graphics.Rect; +import android.util.Log; import android.view.View; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -31,6 +31,13 @@ import androidx.test.filters.SmallTest; import com.android.launcher3.CellLayout; import com.android.launcher3.DeviceProfile; import com.android.launcher3.InvariantDeviceProfile; +import com.android.launcher3.MultipageCellLayout; +import com.android.launcher3.celllayout.board.CellLayoutBoard; +import com.android.launcher3.celllayout.board.IconPoint; +import com.android.launcher3.celllayout.board.PermutedBoardComparator; +import com.android.launcher3.celllayout.board.WidgetRect; +import com.android.launcher3.celllayout.testgenerator.RandomBoardGenerator; +import com.android.launcher3.celllayout.testgenerator.RandomMultiBoardGenerator; import com.android.launcher3.util.ActivityContextWrapper; import com.android.launcher3.views.DoubleShadowBubbleTextView; @@ -50,10 +57,25 @@ import java.util.Random; @SmallTest @RunWith(AndroidJUnit4.class) public class ReorderAlgorithmUnitTest { + + private static final String TAG = "ReorderAlgorithmUnitTest"; + private static final char MAIN_WIDGET_TYPE = 'z'; + + // There is nothing special about this numbers, the random seed is just to be able to reproduce + // the test cases and the height and width is a random number similar to what users expect on + // their devices + private static final int SEED = 897; + private static final int MAX_BOARD_SIZE = 13; + + private static final int TOTAL_OF_CASES_GENERATED = 300; private Context mApplicationContext; private int mPrevNumColumns, mPrevNumRows; + /** + * This test reads existing test cases and makes sure the CellLayout produces the same + * output for each of them for a given input. + */ @Test public void testAllCases() throws IOException { List<ReorderAlgorithmUnitTestCase> testCases = getTestCases( @@ -62,7 +84,7 @@ public class ReorderAlgorithmUnitTest { List<Integer> failingCases = new ArrayList<>(); for (int i = 0; i < testCases.size(); i++) { try { - evaluateTestCase(testCases.get(i)); + evaluateTestCase(testCases.get(i), false); } catch (AssertionError e) { e.printStackTrace(); failingCases.add(i); @@ -72,6 +94,47 @@ public class ReorderAlgorithmUnitTest { failingCases.size()); } + /** + * This test generates random CellLayout configurations and then try to reorder it and makes + * sure the result is a valid board meaning it didn't remove any widget or icon. + */ + @Test + public void generateValidTests() { + Random generator = new Random(SEED); + mApplicationContext = new ActivityContextWrapper(getApplicationContext()); + for (int i = 0; i < TOTAL_OF_CASES_GENERATED; i++) { + // Using a new seed so that we can replicate the same test cases. + int seed = generator.nextInt(); + Log.d(TAG, "Seed = " + seed); + ReorderAlgorithmUnitTestCase testCase = generateRandomTestCase( + new RandomBoardGenerator(new Random(seed)) + ); + Log.d(TAG, "testCase = " + testCase); + assertTrue("invalid case " + i, + validateIntegrity(testCase.startBoard, testCase.endBoard, testCase)); + } + } + + /** + * Same as above but testing the Multipage CellLayout. + */ + @Test + public void generateValidTests_Multi() { + Random generator = new Random(SEED); + mApplicationContext = new ActivityContextWrapper(getApplicationContext()); + for (int i = 0; i < TOTAL_OF_CASES_GENERATED; i++) { + // Using a new seed so that we can replicate the same test cases. + int seed = generator.nextInt(); + Log.d(TAG, "Seed = " + seed); + ReorderAlgorithmUnitTestCase testCase = generateRandomTestCase( + new RandomMultiBoardGenerator(new Random(seed)) + ); + Log.d(TAG, "testCase = " + testCase); + assertTrue("invalid case " + i, + validateIntegrity(testCase.startBoard, testCase.endBoard, testCase)); + } + } + private void addViewInCellLayout(CellLayout cellLayout, int cellX, int cellY, int spanX, int spanY, boolean isWidget) { View cell = isWidget ? new View(mApplicationContext) : new DoubleShadowBubbleTextView( @@ -81,15 +144,16 @@ public class ReorderAlgorithmUnitTest { (CellLayoutLayoutParams) cell.getLayoutParams(), true); } - public CellLayout createCellLayout(int width, int height) { + public CellLayout createCellLayout(int width, int height, boolean isMulti) { Context c = mApplicationContext; DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(c).getDeviceProfile(c).copy(c); // modify the device profile. - dp.inv.numColumns = width; + dp.inv.numColumns = isMulti ? width / 2 : width; dp.inv.numRows = height; dp.cellLayoutBorderSpacePx = new Point(0, 0); - CellLayout cl = new CellLayout(getWrappedContext(c, dp)); + CellLayout cl = isMulti ? new MultipageCellLayout(getWrappedContext(c, dp)) + : new CellLayout(getWrappedContext(c, dp)); // I put a very large number for width and height so that all the items can fit, it doesn't // need to be exact, just bigger than the sum of cell border cl.measure(View.MeasureSpec.makeMeasureSpec(10000, View.MeasureSpec.EXACTLY), @@ -105,52 +169,78 @@ public class ReorderAlgorithmUnitTest { }; } - public CellLayout.ItemConfiguration solve(CellLayoutBoard board, int x, int y, int spanX, - int spanY, int minSpanX, int minSpanY) { - CellLayout cl = createCellLayout(board.getWidth(), board.getHeight()); + public ItemConfiguration solve(CellLayoutBoard board, int x, int y, int spanX, + int spanY, int minSpanX, int minSpanY, boolean isMulti) { + CellLayout cl = createCellLayout(board.getWidth(), board.getHeight(), isMulti); // The views have to be sorted or the result can vary board.getIcons() .stream() - .map(CellLayoutBoard.IconPoint::getCoord) + .map(IconPoint::getCoord) .sorted(Comparator.comparing(p -> ((Point) p).x).thenComparing(p -> ((Point) p).y)) .forEach(p -> addViewInCellLayout(cl, p.x, p.y, 1, 1, false)); - board.getWidgets().stream() - .sorted(Comparator.comparing(CellLayoutBoard.WidgetRect::getCellX) - .thenComparing(CellLayoutBoard.WidgetRect::getCellY)) - .forEach(widget -> addViewInCellLayout(cl, widget.getCellX(), widget.getCellY(), - widget.getSpanX(), widget.getSpanY(), true)); + board.getWidgets() + .stream() + .sorted(Comparator + .comparing(WidgetRect::getCellX) + .thenComparing(WidgetRect::getCellY) + ).forEach( + widget -> addViewInCellLayout(cl, widget.getCellX(), widget.getCellY(), + widget.getSpanX(), widget.getSpanY(), true) + ); int[] testCaseXYinPixels = new int[2]; cl.regionToCenterPoint(x, y, spanX, spanY, testCaseXYinPixels); - CellLayout.ItemConfiguration solution = cl.createReorderAlgorithm().calculateReorder( - testCaseXYinPixels[0], testCaseXYinPixels[1], minSpanX, minSpanY, spanX, spanY, - null); + ItemConfiguration configuration = new ItemConfiguration(); + cl.copyCurrentStateToSolution(configuration); + ItemConfiguration solution = cl.createReorderAlgorithm() + .calculateReorder( + new ReorderParameters( + testCaseXYinPixels[0], + testCaseXYinPixels[1], + spanX, + spanY, + minSpanX, + minSpanY, + null, + configuration + ) + ); if (solution == null) { - solution = new CellLayout.ItemConfiguration(); + solution = new ItemConfiguration(); + solution.isSolution = false; + } + if (!solution.isSolution) { + cl.copyCurrentStateToSolution(solution); + if (cl instanceof MultipageCellLayout) { + solution = + ((MultipageCellLayout) cl).createReorderAlgorithm().removeSeamFromSolution( + solution); + } solution.isSolution = false; } return solution; } - public CellLayoutBoard boardFromSolution(CellLayout.ItemConfiguration solution, int width, + public CellLayoutBoard boardFromSolution(ItemConfiguration solution, int width, int height) { // Update the views with solution value solution.map.forEach((key, val) -> key.setLayoutParams( new CellLayoutLayoutParams(val.cellX, val.cellY, val.spanX, val.spanY))); CellLayoutBoard board = CellLayoutTestUtils.viewsToBoard( new ArrayList<>(solution.map.keySet()), width, height); - board.addWidget(solution.cellX, solution.cellY, solution.spanX, solution.spanY, - 'z'); + if (solution.isSolution) { + board.addWidget(solution.cellX, solution.cellY, solution.spanX, solution.spanY, + MAIN_WIDGET_TYPE); + } return board; } - public void evaluateTestCase(ReorderAlgorithmUnitTestCase testCase) { - CellLayout.ItemConfiguration solution = solve(testCase.startBoard, testCase.x, - testCase.y, testCase.spanX, testCase.spanY, testCase.minSpanX, - testCase.minSpanY); - assertEquals("should be a valid solution", solution.isSolution, - testCase.isValidSolution); + public void evaluateTestCase(ReorderAlgorithmUnitTestCase testCase, boolean isMultiCellLayout) { + ItemConfiguration solution = solve(testCase.startBoard, testCase.x, testCase.y, + testCase.spanX, testCase.spanY, testCase.minSpanX, testCase.minSpanY, + isMultiCellLayout); + assertEquals("should be a valid solution", solution.isSolution, testCase.isValidSolution); if (testCase.isValidSolution) { CellLayoutBoard finishBoard = boardFromSolution(solution, testCase.startBoard.getWidth(), testCase.startBoard.getHeight()); @@ -175,35 +265,34 @@ public class ReorderAlgorithmUnitTest { dp.inv.numRows = mPrevNumRows; } - @SuppressWarnings("UnusedMethod") - /** - * Utility function used to generate all the test cases - */ - private ReorderAlgorithmUnitTestCase generateRandomTestCase() { + private ReorderAlgorithmUnitTestCase generateRandomTestCase( + RandomBoardGenerator boardGenerator) { ReorderAlgorithmUnitTestCase testCase = new ReorderAlgorithmUnitTestCase(); - int width = getRandom(3, 8); - int height = getRandom(3, 8); + boolean isMultiCellLayout = boardGenerator instanceof RandomMultiBoardGenerator; - int targetWidth = getRandom(1, width - 2); - int targetHeight = getRandom(1, height - 2); + int width = isMultiCellLayout + ? boardGenerator.getRandom(3, MAX_BOARD_SIZE / 2) * 2 + : boardGenerator.getRandom(3, MAX_BOARD_SIZE); + int height = boardGenerator.getRandom(3, MAX_BOARD_SIZE); - int minTargetWidth = getRandom(1, targetWidth); - int minTargetHeight = getRandom(1, targetHeight); + int targetWidth = boardGenerator.getRandom(1, width - 2); + int targetHeight = boardGenerator.getRandom(1, height - 2); - int x = getRandom(0, width - targetWidth); - int y = getRandom(0, height - targetHeight); + int minTargetWidth = boardGenerator.getRandom(1, targetWidth); + int minTargetHeight = boardGenerator.getRandom(1, targetHeight); - CellLayoutBoard board = generateBoard(new CellLayoutBoard(width, height), - new Rect(0, 0, width, height), targetWidth * targetHeight); + int x = boardGenerator.getRandom(0, width - targetWidth); + int y = boardGenerator.getRandom(0, height - targetHeight); - CellLayout.ItemConfiguration solution = solve(board, x, y, targetWidth, targetHeight, - minTargetWidth, minTargetHeight); + CellLayoutBoard board = boardGenerator.generateBoard(width, height, + targetWidth * targetHeight); - CellLayoutBoard finishBoard = solution.isSolution ? boardFromSolution(solution, - board.getWidth(), board.getHeight()) : new CellLayoutBoard(board.getWidth(), - board.getHeight()); + ItemConfiguration solution = solve(board, x, y, targetWidth, targetHeight, + minTargetWidth, minTargetHeight, isMultiCellLayout); + CellLayoutBoard finishBoard = boardFromSolution(solution, board.getWidth(), + board.getHeight()); testCase.startBoard = board; testCase.endBoard = finishBoard; @@ -219,39 +308,24 @@ public class ReorderAlgorithmUnitTest { return testCase; } - private int getRandom(int start, int end) { - int random = end == 0 ? 0 : new Random().nextInt(end); - return start + random; - } - - private CellLayoutBoard generateBoard(CellLayoutBoard board, Rect area, - int emptySpaces) { - if (area.height() * area.width() <= 0) return board; - - int width = getRandom(1, area.width() - 1); - int height = getRandom(1, area.height() - 1); - - int x = area.left + getRandom(0, area.width() - width); - int y = area.top + getRandom(0, area.height() - height); - - if (emptySpaces > 0) { - emptySpaces -= width * height; - } else if (width * height > 1) { - board.addWidget(x, y, width, height); - } else { - board.addIcon(x, y); + /** + * Makes sure the final solution has valid integrity meaning that the number and sizes of + * widgets is the expect and there are no missing widgets. + */ + public boolean validateIntegrity(CellLayoutBoard startBoard, CellLayoutBoard finishBoard, + ReorderAlgorithmUnitTestCase testCase) { + if (!testCase.isValidSolution) { + // if we couldn't place the widget then the solution should be identical to the board + return startBoard.compareTo(finishBoard) == 0; } - - generateBoard(board, - new Rect(area.left, area.top, area.right, y), emptySpaces); - generateBoard(board, - new Rect(area.left, y, x, area.bottom), emptySpaces); - generateBoard(board, - new Rect(x, y + height, area.right, area.bottom), emptySpaces); - generateBoard(board, - new Rect(x + width, y, area.right, y + height), emptySpaces); - - return board; + WidgetRect addedWidget = finishBoard.getWidgetOfType(MAIN_WIDGET_TYPE); + finishBoard.removeItem(MAIN_WIDGET_TYPE); + Comparator<CellLayoutBoard> comparator = new PermutedBoardComparator(); + if (comparator.compare(startBoard, finishBoard) != 0) { + return false; + } + return addedWidget.getSpanX() >= testCase.minSpanX + && addedWidget.getSpanY() >= testCase.minSpanY; } private static List<ReorderAlgorithmUnitTestCase> getTestCases(String testPath) diff --git a/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTestCase.java b/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTestCase.java index 4274130702..3dc503019d 100644 --- a/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTestCase.java +++ b/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTestCase.java @@ -15,6 +15,8 @@ */ package com.android.launcher3.celllayout; +import com.android.launcher3.celllayout.board.CellLayoutBoard; + import java.util.Iterator; /** diff --git a/tests/src/com/android/launcher3/celllayout/ReorderTestCase.java b/tests/src/com/android/launcher3/celllayout/ReorderTestCase.java index ed84a60fae..0c1403ff42 100644 --- a/tests/src/com/android/launcher3/celllayout/ReorderTestCase.java +++ b/tests/src/com/android/launcher3/celllayout/ReorderTestCase.java @@ -17,6 +17,8 @@ package com.android.launcher3.celllayout; import android.graphics.Point; +import com.android.launcher3.celllayout.board.CellLayoutBoard; + import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; diff --git a/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java b/tests/src/com/android/launcher3/celllayout/TaplReorderWidgetsTest.java index 00d7ce65e3..8bdcd033f7 100644 --- a/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java +++ b/tests/src/com/android/launcher3/celllayout/TaplReorderWidgetsTest.java @@ -15,6 +15,8 @@ */ package com.android.launcher3.celllayout; +import static android.platform.uiautomator_helpers.DeviceHelpers.getContext; + import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -28,13 +30,18 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.launcher3.InvariantDeviceProfile; +import com.android.launcher3.LauncherAppState; import com.android.launcher3.MultipageCellLayout; +import com.android.launcher3.celllayout.board.CellLayoutBoard; +import com.android.launcher3.celllayout.board.TestWorkspaceBuilder; +import com.android.launcher3.celllayout.board.WidgetRect; import com.android.launcher3.tapl.Widget; import com.android.launcher3.tapl.WidgetResizeFrame; import com.android.launcher3.ui.AbstractLauncherUiTest; -import com.android.launcher3.ui.TaplTestsLauncher3; +import com.android.launcher3.util.ModelTestExtensions; import com.android.launcher3.util.rule.ShellCommandRule; +import org.junit.After; import org.junit.Assert; import org.junit.Assume; import org.junit.Before; @@ -51,12 +58,12 @@ import java.util.Map; @SmallTest @RunWith(AndroidJUnit4.class) -public class ReorderWidgets extends AbstractLauncherUiTest { +public class TaplReorderWidgetsTest extends AbstractLauncherUiTest { @Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind(); - private static final String TAG = ReorderWidgets.class.getSimpleName(); + private static final String TAG = TaplReorderWidgetsTest.class.getSimpleName(); private static final List<String> FOLDABLE_GRIDS = List.of("normal", "practical", "reasonable"); @@ -65,7 +72,14 @@ public class ReorderWidgets extends AbstractLauncherUiTest { @Before public void setup() throws Throwable { mWorkspaceBuilder = new TestWorkspaceBuilder(mTargetContext); - TaplTestsLauncher3.initialize(this); + AbstractLauncherUiTest.initialize(this); + } + + @After + public void tearDown() { + ModelTestExtensions.INSTANCE.clearModelDb( + LauncherAppState.getInstance(getContext()).getModel() + ); } /** @@ -106,12 +120,12 @@ public class ReorderWidgets extends AbstractLauncherUiTest { return getFromLauncher(CellLayoutTestUtils::workspaceToBoards); } - private CellLayoutBoard.WidgetRect getWidgetClosestTo(Point point) { + private WidgetRect getWidgetClosestTo(Point point) { ArrayList<CellLayoutBoard> workspaceBoards = workspaceToBoards(); int maxDistance = 9999; - CellLayoutBoard.WidgetRect bestRect = null; + WidgetRect bestRect = null; for (int i = 0; i < workspaceBoards.get(0).getWidgets().size(); i++) { - CellLayoutBoard.WidgetRect widget = workspaceBoards.get(0).getWidgets().get(i); + WidgetRect widget = workspaceBoards.get(0).getWidgets().get(i); if (widget.getCellX() == 0 && widget.getCellY() == 0) { continue; } @@ -134,7 +148,7 @@ public class ReorderWidgets extends AbstractLauncherUiTest { * underlying code does different things in that case */ private void triggerWidgetResize(ReorderTestCase testCase) { - CellLayoutBoard.WidgetRect widgetRect = getWidgetClosestTo(testCase.moveMainTo); + WidgetRect widgetRect = getWidgetClosestTo(testCase.moveMainTo); if (widgetRect == null) { // Some test doesn't have a widget in the final position, in those cases we will ignore // them @@ -148,7 +162,7 @@ public class ReorderWidgets extends AbstractLauncherUiTest { } private void runTestCase(ReorderTestCase testCase) { - CellLayoutBoard.WidgetRect mainWidgetCellPos = CellLayoutBoard.getMainFromList( + WidgetRect mainWidgetCellPos = CellLayoutBoard.getMainFromList( testCase.mStart); FavoriteItemsTransaction transaction = diff --git a/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java b/tests/src/com/android/launcher3/celllayout/board/CellLayoutBoard.java index ff667e60bf..dbbdcf519a 100644 --- a/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java +++ b/tests/src/com/android/launcher3/celllayout/board/CellLayoutBoard.java @@ -13,13 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.launcher3.celllayout; +package com.android.launcher3.celllayout.board; import android.graphics.Point; import android.graphics.Rect; +import androidx.annotation.NonNull; + import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -31,172 +34,13 @@ import java.util.stream.Collectors; public class CellLayoutBoard implements Comparable<CellLayoutBoard> { - private boolean intersects(Rect r1, Rect r2) { - // If one rectangle is on left side of other - if (r1.left > r2.right || r2.left > r1.right) { - return false; - } - - // If one rectangle is above other - if (r1.bottom > r2.top || r2.bottom > r1.top) { - return false; - } - - return true; - } - - private boolean overlapsWithIgnored(Set<Rect> ignoredRectangles, Rect rect) { - for (Rect ignoredRect : ignoredRectangles) { - // Using the built in intersects doesn't work because it doesn't account for area 0 - if (intersects(ignoredRect, rect)) { - return true; - } - } - return false; - } + public static final Comparator<CellLayoutBoard> COMPARATOR = new IdenticalBoardComparator(); @Override - public int compareTo(CellLayoutBoard cellLayoutBoard) { - // to be equal they need to have the same number of widgets and the same dimensions - // their order can be different - Set<Rect> widgetsSet = new HashSet<>(); - Set<Rect> ignoredRectangles = new HashSet<>(); - for (WidgetRect rect : mWidgetsRects) { - if (rect.shouldIgnore()) { - ignoredRectangles.add(rect.mBounds); - } else { - widgetsSet.add(rect.mBounds); - } - } - for (WidgetRect rect : cellLayoutBoard.mWidgetsRects) { - // ignore rectangles overlapping with the area marked by x - if (overlapsWithIgnored(ignoredRectangles, rect.mBounds)) { - continue; - } - if (!widgetsSet.contains(rect.mBounds)) { - return -1; - } - widgetsSet.remove(rect.mBounds); - } - if (!widgetsSet.isEmpty()) { - return 1; - } - - // to be equal they need to have the same number of icons their order can be different - Set<Point> iconsSet = new HashSet<>(); - mIconPoints.forEach(icon -> iconsSet.add(icon.getCoord())); - for (IconPoint icon : cellLayoutBoard.mIconPoints) { - if (!iconsSet.contains(icon.getCoord())) { - return -1; - } - iconsSet.remove(icon.getCoord()); - } - if (!iconsSet.isEmpty()) { - return 1; - } - return 0; - } - - public static class CellType { - // The cells marked by this will be filled by 1x1 widgets and will be ignored when - // validating - public static final char IGNORE = 'x'; - // The cells marked by this will be filled by app icons - public static final char ICON = 'i'; - // The cells marked by FOLDER will be filled by folders with 27 app icons inside - public static final char FOLDER = 'Z'; - // Empty space - public static final char EMPTY = '-'; - // Widget that will be saved as "main widget" for easier retrieval - public static final char MAIN_WIDGET = 'm'; - // Everything else will be consider a widget - } - - public static class WidgetRect { - public char mType; - public Rect mBounds; - - WidgetRect(char type, Rect bounds) { - this.mType = type; - this.mBounds = bounds; - } - - int getSpanX() { - return mBounds.right - mBounds.left + 1; - } - - int getSpanY() { - return mBounds.top - mBounds.bottom + 1; - } - - int getCellX() { - return mBounds.left; - } - - int getCellY() { - return mBounds.bottom; - } - - boolean shouldIgnore() { - return this.mType == CellType.IGNORE; - } - - boolean contains(int x, int y) { - return mBounds.contains(x, y); - } - - @Override - public String toString() { - return "WidgetRect type = " + mType + " x = " + getCellX() + " | y " + getCellY() - + " xs = " + getSpanX() + " ys = " + getSpanY(); - } - } - - public static class IconPoint { - public Point coord; - public char mType; - - public IconPoint(Point coord, char type) { - this.coord = coord; - mType = type; - } - - public char getType() { - return mType; - } - - public void setType(char type) { - mType = type; - } - - public Point getCoord() { - return coord; - } - - public void setCoord(Point coord) { - this.coord = coord; - } + public int compareTo(@NonNull CellLayoutBoard cellLayoutBoard) { + return COMPARATOR.compare(this, cellLayoutBoard); } - public static class FolderPoint { - public Point coord; - public char mType; - - public FolderPoint(Point coord, char type) { - this.coord = coord; - mType = type; - } - - /** - * [A-Z]: Represents a folder and number of icons in the folder is represented by - * the order of letter in the alphabet, A=2, B=3, C=4 ... etc. - */ - public int getNumberIconsInside() { - return (mType - 'A') + 2; - } - } - - private HashSet<Character> mUsedWidgetTypes = new HashSet<>(); static final int INFINITE = 99999; @@ -213,7 +57,7 @@ public class CellLayoutBoard implements Comparable<CellLayoutBoard> { int mWidth, mHeight; - CellLayoutBoard() { + public CellLayoutBoard() { for (int x = 0; x < mWidget.length; x++) { for (int y = 0; y < mWidget[0].length; y++) { mWidget[x][y] = CellType.EMPTY; @@ -221,8 +65,8 @@ public class CellLayoutBoard implements Comparable<CellLayoutBoard> { } } - CellLayoutBoard(int width, int height) { - mWidget = new char[width][height]; + public CellLayoutBoard(int width, int height) { + mWidget = new char[width + 1][height + 1]; this.mWidth = width; this.mHeight = height; for (int x = 0; x < mWidget.length; x++) { @@ -238,6 +82,15 @@ public class CellLayoutBoard implements Comparable<CellLayoutBoard> { return isXInRect && isYInRect; } + public WidgetRect getWidgetAt(Point p) { + return getWidgetAt(p.x, p.y); + } + + public WidgetRect getWidgetOfType(char type) { + return mWidgetsRects.stream() + .filter(widgetRect -> widgetRect.mType == type).findFirst().orElse(null); + } + public WidgetRect getWidgetAt(int x, int y) { return mWidgetsRects.stream() .filter(widgetRect -> pointInsideRect(x, y, widgetRect)).findFirst().orElse(null); @@ -264,8 +117,8 @@ public class CellLayoutBoard implements Comparable<CellLayoutBoard> { } private void removeWidgetFromBoard(WidgetRect widget) { - for (int xi = widget.mBounds.left; xi < widget.mBounds.right; xi++) { - for (int yi = widget.mBounds.top; yi < widget.mBounds.bottom; yi++) { + for (int xi = widget.mBounds.left; xi <= widget.mBounds.right; xi++) { + for (int yi = widget.mBounds.bottom; yi <= widget.mBounds.top; yi++) { mWidget[xi][yi] = '-'; } } @@ -306,7 +159,7 @@ public class CellLayoutBoard implements Comparable<CellLayoutBoard> { private void removeOverlappingItems(Point p) { // Remove overlapping widgets and remove them from the board mWidgetsRects = mWidgetsRects.stream().filter(widget -> { - if (widget.mBounds.contains(p.x, p.y)) { + if (IdenticalBoardComparator.Companion.touchesPoint(widget.mBounds, p)) { removeWidgetFromBoard(widget); return false; } @@ -336,8 +189,9 @@ public class CellLayoutBoard implements Comparable<CellLayoutBoard> { } private char getNextWidgetType() { - for (char type = 'a'; type <= 'z'; type++) { - if (type == 'i') continue; + for (char type = 'a'; type < 'z'; type++) { + if (type == CellType.ICON) continue; + if (type == CellType.IGNORE) continue; if (mUsedWidgetTypes.contains(type)) continue; mUsedWidgetTypes.add(type); return type; @@ -357,6 +211,17 @@ public class CellLayoutBoard implements Comparable<CellLayoutBoard> { } } + public void removeItem(char type) { + mWidgetsRects.stream() + .filter(widgetRect -> widgetRect.mType == type) + .forEach(widgetRect -> removeOverlappingItems( + new Point(widgetRect.getCellX(), widgetRect.getCellY()))); + } + + public void removeItem(Point p) { + removeOverlappingItems(p); + } + public void addWidget(int x, int y, int spanX, int spanY) { addWidget(x, y, spanX, spanY, getNextWidgetType()); } @@ -506,8 +371,8 @@ public class CellLayoutBoard implements Comparable<CellLayoutBoard> { s.append("\n"); maxX = Math.min(maxX, mWidget.length); maxY = Math.min(maxY, mWidget[0].length); - for (int y = 0; y < maxY; y++) { - for (int x = 0; x < maxX; x++) { + for (int y = 0; y <= maxY; y++) { + for (int x = 0; x <= maxX; x++) { s.append(mWidget[x][y]); } s.append('\n'); diff --git a/tests/src/com/android/launcher3/celllayout/board/CellType.java b/tests/src/com/android/launcher3/celllayout/board/CellType.java new file mode 100644 index 0000000000..49c146b32a --- /dev/null +++ b/tests/src/com/android/launcher3/celllayout/board/CellType.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.celllayout.board; + +public class CellType { + // The cells marked by this will be filled by 1x1 widgets and will be ignored when + // validating + public static final char IGNORE = 'x'; + // The cells marked by this will be filled by app icons + public static final char ICON = 'i'; + // The cells marked by FOLDER will be filled by folders with 27 app icons inside + public static final char FOLDER = 'Z'; + // Empty space + public static final char EMPTY = '-'; + // Widget that will be saved as "main widget" for easier retrieval + public static final char MAIN_WIDGET = 'm'; + // Everything else will be consider a widget +} diff --git a/tests/src/com/android/launcher3/celllayout/board/FolderPoint.java b/tests/src/com/android/launcher3/celllayout/board/FolderPoint.java new file mode 100644 index 0000000000..39ba434dc0 --- /dev/null +++ b/tests/src/com/android/launcher3/celllayout/board/FolderPoint.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.celllayout.board; + +import android.graphics.Point; + +public class FolderPoint { + public Point coord; + public char mType; + + public FolderPoint(Point coord, char type) { + this.coord = coord; + mType = type; + } + + /** + * [A-Z]: Represents a folder and number of icons in the folder is represented by + * the order of letter in the alphabet, A=2, B=3, C=4 ... etc. + */ + public int getNumberIconsInside() { + return (mType - 'A') + 2; + } +} diff --git a/tests/src/com/android/launcher3/celllayout/TestBoardAppIcon.java b/tests/src/com/android/launcher3/celllayout/board/IconPoint.java index 04604d7735..d3d297003d 100644 --- a/tests/src/com/android/launcher3/celllayout/TestBoardAppIcon.java +++ b/tests/src/com/android/launcher3/celllayout/board/IconPoint.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 The Android Open Source Project + * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,15 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.launcher3.celllayout; + +package com.android.launcher3.celllayout.board; import android.graphics.Point; -public class TestBoardAppIcon { +public class IconPoint { public Point coord; public char mType; - public TestBoardAppIcon(Point coord, char type) { + public IconPoint(Point coord, char type) { this.coord = coord; mType = type; } diff --git a/tests/src/com/android/launcher3/celllayout/board/IdenticalBoardComparator.kt b/tests/src/com/android/launcher3/celllayout/board/IdenticalBoardComparator.kt new file mode 100644 index 0000000000..a4a420cf59 --- /dev/null +++ b/tests/src/com/android/launcher3/celllayout/board/IdenticalBoardComparator.kt @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.celllayout.board + +import android.graphics.Point +import android.graphics.Rect + +/** + * Compares two [CellLayoutBoard] and returns 0 if they are identical, meaning they have the same + * widget and icons in the same place, they can be different letters tough. + */ +class IdenticalBoardComparator : Comparator<CellLayoutBoard> { + + /** Converts a list of WidgetRect into a map of the count of different widget.bounds */ + private fun widgetsToBoundsMap(widgets: List<WidgetRect>) = + widgets.groupingBy { it.mBounds }.eachCount() + + /** Converts a list of IconPoint into a map of the count of different icon.coord */ + private fun iconsToPosCountMap(widgets: List<IconPoint>) = + widgets.groupingBy { it.getCoord() }.eachCount() + + override fun compare( + cellLayoutBoard: CellLayoutBoard, + otherCellLayoutBoard: CellLayoutBoard + ): Int { + // to be equal they need to have the same number of widgets and the same dimensions + // their order can be different + val widgetsMap: Map<Rect, Int> = + widgetsToBoundsMap(cellLayoutBoard.widgets.filter { !it.shouldIgnore() }) + val ignoredRectangles: Map<Rect, Int> = + widgetsToBoundsMap(cellLayoutBoard.widgets.filter { it.shouldIgnore() }) + + val otherWidgetMap: Map<Rect, Int> = + widgetsToBoundsMap( + otherCellLayoutBoard.widgets + .filter { !it.shouldIgnore() } + .filter { !overlapsWithIgnored(ignoredRectangles, it.mBounds) } + ) + + if (widgetsMap != otherWidgetMap) { + return -1 + } + + // to be equal they need to have the same number of icons their order can be different + return if ( + iconsToPosCountMap(cellLayoutBoard.icons) == + iconsToPosCountMap(otherCellLayoutBoard.icons) + ) { + 0 + } else { + 1 + } + } + + private fun overlapsWithIgnored(ignoredRectangles: Map<Rect, Int>, rect: Rect): Boolean { + for (ignoredRect in ignoredRectangles.keys) { + // Using the built in intersects doesn't work because it doesn't account for area 0 + if (touches(ignoredRect, rect)) { + return true + } + } + return false + } + + companion object { + /** + * Similar function to {@link Rect#intersects} but this one returns true if the rectangles + * are intersecting or touching whereas {@link Rect#intersects} doesn't return true when + * they are touching. + */ + fun touches(r1: Rect, r2: Rect): Boolean { + // If one rectangle is on left side of other + return if (r1.left > r2.right || r2.left > r1.right) { + false + } else r1.bottom <= r2.top && r2.bottom <= r1.top + + // If one rectangle is above other + } + + /** + * Similar function to {@link Rect#contains} but this one returns true if {link @Point} is + * intersecting or touching the {@link Rect}. Similar to {@link touches}. + */ + fun touchesPoint(r1: Rect, p: Point): Boolean { + return r1.left <= p.x && p.x <= r1.right && r1.bottom <= p.y && p.y <= r1.top + } + } +} diff --git a/tests/src/com/android/launcher3/celllayout/board/PermutedBoardComparator.kt b/tests/src/com/android/launcher3/celllayout/board/PermutedBoardComparator.kt new file mode 100644 index 0000000000..c3d13a5701 --- /dev/null +++ b/tests/src/com/android/launcher3/celllayout/board/PermutedBoardComparator.kt @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.celllayout.board + +import android.graphics.Point + +/** + * Compares two [CellLayoutBoard] and returns 0 if they contain the same widgets and icons even if + * they are in different positions i.e. in a different permutation. + */ +class PermutedBoardComparator : Comparator<CellLayoutBoard> { + + /** + * The key for the set is the span since the widgets could change location but shouldn't change + * size + */ + private fun boardToSpanCountMap(widgets: List<WidgetRect>) = + widgets.groupingBy { Point(it.spanX, it.spanY) }.eachCount() + override fun compare( + cellLayoutBoard: CellLayoutBoard, + otherCellLayoutBoard: CellLayoutBoard + ): Int { + return if ( + boardToSpanCountMap(cellLayoutBoard.widgets) != + boardToSpanCountMap(otherCellLayoutBoard.widgets) + ) { + 1 + } else cellLayoutBoard.icons.size.compareTo(otherCellLayoutBoard.icons.size) + } +} diff --git a/tests/src/com/android/launcher3/celllayout/TestWorkspaceBuilder.java b/tests/src/com/android/launcher3/celllayout/board/TestWorkspaceBuilder.java index 6489bc1cfc..06a7db2ae0 100644 --- a/tests/src/com/android/launcher3/celllayout/TestWorkspaceBuilder.java +++ b/tests/src/com/android/launcher3/celllayout/board/TestWorkspaceBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 The Android Open Source Project + * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.launcher3.celllayout; +package com.android.launcher3.celllayout.board; import static androidx.test.core.app.ApplicationProvider.getApplicationContext; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; @@ -31,6 +31,12 @@ import android.util.Log; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.LauncherSettings; +import com.android.launcher3.celllayout.FavoriteItemsTransaction; +import com.android.launcher3.celllayout.board.CellLayoutBoard; +import com.android.launcher3.celllayout.board.CellType; +import com.android.launcher3.celllayout.board.FolderPoint; +import com.android.launcher3.celllayout.board.IconPoint; +import com.android.launcher3.celllayout.board.WidgetRect; import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.model.data.FolderInfo; import com.android.launcher3.model.data.ItemInfo; @@ -61,7 +67,7 @@ public class TestWorkspaceBuilder { /** * Fills the given rect in WidgetRect with 1x1 widgets. This is useful to equalize cases. */ - private FavoriteItemsTransaction fillWithWidgets(CellLayoutBoard.WidgetRect widgetRect, + private FavoriteItemsTransaction fillWithWidgets(WidgetRect widgetRect, FavoriteItemsTransaction transaction, int screenId) { int initX = widgetRect.getCellX(); int initY = widgetRect.getCellY(); @@ -70,7 +76,7 @@ public class TestWorkspaceBuilder { try { // this widgets are filling, we don't care if we can't place them transaction.addItem(createWidgetInCell( - new CellLayoutBoard.WidgetRect(CellLayoutBoard.CellType.IGNORE, + new WidgetRect(CellType.IGNORE, new Rect(x, y, x, y)), screenId)); } catch (Exception e) { Log.d(TAG, "Unable to place filling widget at " + x + "," + y); @@ -97,7 +103,7 @@ public class TestWorkspaceBuilder { ); } - private void addCorrespondingWidgetRect(CellLayoutBoard.WidgetRect widgetRect, + private void addCorrespondingWidgetRect(WidgetRect widgetRect, FavoriteItemsTransaction transaction, int screenId) { if (widgetRect.mType == 'x') { fillWithWidgets(widgetRect, transaction, screenId); @@ -133,7 +139,7 @@ public class TestWorkspaceBuilder { } private Supplier<ItemInfo> createWidgetInCell( - CellLayoutBoard.WidgetRect widgetRect, int screenId) { + WidgetRect widgetRect, int screenId) { // Create the widget lazily since the appWidgetId can get lost during setup return () -> { LauncherAppWidgetProviderInfo info = findWidgetProvider(false); @@ -147,7 +153,7 @@ public class TestWorkspaceBuilder { }; } - public FolderInfo createFolderInCell(CellLayoutBoard.FolderPoint folderPoint, int screenId) { + public FolderInfo createFolderInCell(FolderPoint folderPoint, int screenId) { FolderInfo folderInfo = new FolderInfo(); folderInfo.screenId = screenId; folderInfo.container = LauncherSettings.Favorites.CONTAINER_DESKTOP; @@ -171,7 +177,7 @@ public class TestWorkspaceBuilder { return item; } - private ItemInfo createIconInCell(CellLayoutBoard.IconPoint iconPoint, int screenId) { + private ItemInfo createIconInCell(IconPoint iconPoint, int screenId) { WorkspaceItemInfo item = new WorkspaceItemInfo(getApp()); item.screenId = screenId; item.cellX = iconPoint.getCoord().x; diff --git a/tests/src/com/android/launcher3/celllayout/TestBoardWidget.java b/tests/src/com/android/launcher3/celllayout/board/WidgetRect.java index 7f9aa9516c..c90ce8504f 100644 --- a/tests/src/com/android/launcher3/celllayout/TestBoardWidget.java +++ b/tests/src/com/android/launcher3/celllayout/board/WidgetRect.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 The Android Open Source Project + * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,36 +13,47 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.launcher3.celllayout; + +package com.android.launcher3.celllayout.board; import android.graphics.Rect; -public class TestBoardWidget { +public class WidgetRect { public char mType; public Rect mBounds; - TestBoardWidget(char type, Rect bounds) { + public WidgetRect(char type, Rect bounds) { this.mType = type; this.mBounds = bounds; } - int getSpanX() { + public int getSpanX() { return mBounds.right - mBounds.left + 1; } - int getSpanY() { + public int getSpanY() { return mBounds.top - mBounds.bottom + 1; } - int getCellX() { + public int getCellX() { return mBounds.left; } - int getCellY() { + public int getCellY() { return mBounds.bottom; } boolean shouldIgnore() { - return this.mType == 'x'; + return this.mType == CellType.IGNORE; + } + + boolean contains(int x, int y) { + return mBounds.contains(x, y); + } + + @Override + public String toString() { + return "WidgetRect type = " + mType + " x = " + getCellX() + " | y " + getCellY() + + " xs = " + getSpanX() + " ys = " + getSpanY(); } } diff --git a/tests/src/com/android/launcher3/celllayout/testgenerator/DeterministicRandomGenerator.kt b/tests/src/com/android/launcher3/celllayout/testgenerator/DeterministicRandomGenerator.kt new file mode 100644 index 0000000000..e5829730c0 --- /dev/null +++ b/tests/src/com/android/launcher3/celllayout/testgenerator/DeterministicRandomGenerator.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.celllayout.testgenerator + +import java.util.Random + +abstract class DeterministicRandomGenerator(private val generator: Random) { + fun getRandom(start: Int, end: Int): Int = start + (if (end == 0) 0 else generator.nextInt(end)) +} diff --git a/tests/src/com/android/launcher3/celllayout/testgenerator/RandomBoardGenerator.kt b/tests/src/com/android/launcher3/celllayout/testgenerator/RandomBoardGenerator.kt new file mode 100644 index 0000000000..770024fb5d --- /dev/null +++ b/tests/src/com/android/launcher3/celllayout/testgenerator/RandomBoardGenerator.kt @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.celllayout.testgenerator + +import android.graphics.Rect +import com.android.launcher3.celllayout.board.CellLayoutBoard +import java.util.Random + +/** Generates a random CellLayoutBoard. */ +open class RandomBoardGenerator(generator: Random) : DeterministicRandomGenerator(generator) { + /** + * @param remainingEmptySpaces the maximum number of spaces we will fill with icons and widgets + * meaning that if the number is 100 we will try to fill the board with at most 100 spaces + * usually less than 100. + * @return a randomly generated board filled with icons and widgets. + */ + open fun generateBoard(width: Int, height: Int, remainingEmptySpaces: Int): CellLayoutBoard? { + val cellLayoutBoard = CellLayoutBoard(width, height) + return fillBoard(cellLayoutBoard, Rect(0, 0, width, height), remainingEmptySpaces) + } + + protected fun fillBoard( + board: CellLayoutBoard, + area: Rect, + remainingEmptySpacesArg: Int + ): CellLayoutBoard { + var remainingEmptySpaces = remainingEmptySpacesArg + if (area.height() * area.width() <= 0) return board + val width = getRandom(1, area.width() - 1) + val height = getRandom(1, area.height() - 1) + val x = area.left + getRandom(0, area.width() - width) + val y = area.top + getRandom(0, area.height() - height) + if (remainingEmptySpaces > 0) { + remainingEmptySpaces -= width * height + } else if (board.widgets.size <= 22 && width * height > 1) { + board.addWidget(x, y, width, height) + } else { + board.addIcon(x, y) + } + fillBoard(board, Rect(area.left, area.top, area.right, y), remainingEmptySpaces) + fillBoard(board, Rect(area.left, y, x, area.bottom), remainingEmptySpaces) + fillBoard(board, Rect(x, y + height, area.right, area.bottom), remainingEmptySpaces) + fillBoard(board, Rect(x + width, y, area.right, y + height), remainingEmptySpaces) + return board + } +} diff --git a/tests/src/com/android/launcher3/celllayout/testgenerator/RandomMultiBoardGenerator.kt b/tests/src/com/android/launcher3/celllayout/testgenerator/RandomMultiBoardGenerator.kt new file mode 100644 index 0000000000..da4de7ddf2 --- /dev/null +++ b/tests/src/com/android/launcher3/celllayout/testgenerator/RandomMultiBoardGenerator.kt @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.celllayout.testgenerator + +import android.graphics.Rect +import com.android.launcher3.celllayout.board.CellLayoutBoard +import java.util.Random + +class RandomMultiBoardGenerator(generator: Random) : RandomBoardGenerator(generator) { + override fun generateBoard( + width: Int, + height: Int, + remainingEmptySpaces: Int + ): CellLayoutBoard { + val cellLayoutBoard = CellLayoutBoard(width, height) + fillBoard(cellLayoutBoard, Rect(0, 0, width / 2, height), remainingEmptySpaces / 2) + return fillBoard( + cellLayoutBoard, + Rect(width / 2, 0, width, height), + remainingEmptySpaces / 2 + ) + } +} diff --git a/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java b/tests/src/com/android/launcher3/compat/TaplPromiseIconUiTest.java index f34a29e9f8..8200c9443c 100644 --- a/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java +++ b/tests/src/com/android/launcher3/compat/TaplPromiseIconUiTest.java @@ -27,7 +27,7 @@ import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.ui.AbstractLauncherUiTest; import com.android.launcher3.util.LauncherBindableItemsContainer.ItemOperator; -import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord; +import com.android.launcher3.util.rule.ViewCaptureRule; import org.junit.After; import org.junit.Test; @@ -41,7 +41,7 @@ import java.util.UUID; */ @LargeTest @RunWith(AndroidJUnit4.class) -public class PromiseIconUiTest extends AbstractLauncherUiTest { +public class TaplPromiseIconUiTest extends AbstractLauncherUiTest { private int mSessionId = -1; @@ -74,7 +74,6 @@ public class PromiseIconUiTest extends AbstractLauncherUiTest { } @Test - @ScreenRecord // b/202985412 public void testPromiseIcon_addedFromEligibleSession() throws Throwable { final String appLabel = "Test Promise App " + UUID.randomUUID().toString(); final ItemOperator findPromiseApp = (info, view) -> @@ -97,7 +96,7 @@ public class PromiseIconUiTest extends AbstractLauncherUiTest { } @Test - @ScreenRecord // b/202985412 + @ViewCaptureRule.MayProduceNoFrames public void testPromiseIcon_notAddedFromIneligibleSession() throws Throwable { final String appLabel = "Test Promise App " + UUID.randomUUID().toString(); final ItemOperator findPromiseApp = (info, view) -> diff --git a/tests/src/com/android/launcher3/dragging/TaplDragTest.java b/tests/src/com/android/launcher3/dragging/TaplDragTest.java new file mode 100644 index 0000000000..e0403678ba --- /dev/null +++ b/tests/src/com/android/launcher3/dragging/TaplDragTest.java @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.dragging; + +import static com.android.launcher3.util.TestConstants.AppNames.TEST_APP_NAME; +import static com.android.launcher3.util.TestConstants.AppNames.GMAIL_APP_NAME; +import static com.android.launcher3.util.TestConstants.AppNames.MAPS_APP_NAME; +import static com.android.launcher3.util.TestConstants.AppNames.STORE_APP_NAME; +import static com.android.launcher3.ui.AbstractLauncherUiTest.initialize; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import android.graphics.Point; +import android.os.SystemClock; +import android.platform.test.annotations.PlatinumTest; +import android.util.Log; + +import com.android.launcher3.tapl.Folder; +import com.android.launcher3.tapl.FolderIcon; +import com.android.launcher3.tapl.HomeAllApps; +import com.android.launcher3.tapl.HomeAppIcon; +import com.android.launcher3.tapl.HomeAppIconMenuItem; +import com.android.launcher3.tapl.Workspace; +import com.android.launcher3.ui.AbstractLauncherUiTest; +import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape; +import com.android.launcher3.util.TestUtil; +import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord; + +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; + +/** + * This test run in both Out of process (Oop) and in-process (Ipc). + * Tests multiple facets of the drag interaction in the Launcher: + * * Can create a folder by dragging items. + * * Can create a shortcut by dragging it to Workspace. + * * Can create shortcuts in multiple spaces in the Workspace. + * * Can cancel a drag icon to workspace by dragging outside of the Workspace. + * * Can drag an icon from AllApps into the workspace + * * Can drag an icon on the Workspace to other positions of the Workspace. + */ +public class TaplDragTest extends AbstractLauncherUiTest { + + @Before + public void setUp() throws Exception { + super.setUp(); + initialize(this); + } + + /** + * Adds two icons to the Workspace and combines them into a folder, then makes sure the icons + * are no longer in the Workspace then adds a third one to test adding an icon to an existing + * folder instead of creating one and drags it to the folder. + */ + @Test + @PortraitLandscape + @ScreenRecord + @Ignore // b/233075289 + @PlatinumTest(focusArea = "launcher") + public void testDragToFolder() { + // TODO: add the use case to drag an icon to an existing folder. Currently it either fails + // on tablets or phones due to difference in resolution. + final HomeAppIcon playStoreIcon = createShortcutIfNotExist(STORE_APP_NAME, 0, 1); + final HomeAppIcon gmailIcon = createShortcutInCenterIfNotExist(GMAIL_APP_NAME); + + FolderIcon folderIcon = gmailIcon.dragToIcon(playStoreIcon); + Folder folder = folderIcon.open(); + folder.getAppIcon(STORE_APP_NAME); + folder.getAppIcon(GMAIL_APP_NAME); + Workspace workspace = folder.close(); + + workspace.verifyWorkspaceAppIconIsGone(STORE_APP_NAME + " should be moved to a folder.", + STORE_APP_NAME); + workspace.verifyWorkspaceAppIconIsGone(GMAIL_APP_NAME + " should be moved to a folder.", + GMAIL_APP_NAME); + + final HomeAppIcon mapIcon = createShortcutInCenterIfNotExist(MAPS_APP_NAME); + folderIcon = mapIcon.dragToIcon(folderIcon); + folder = folderIcon.open(); + folder.getAppIcon(MAPS_APP_NAME); + workspace = folder.close(); + + workspace.verifyWorkspaceAppIconIsGone(MAPS_APP_NAME + " should be moved to a folder.", + MAPS_APP_NAME); + } + + + /** Drags a shortcut from a long press menu into the workspace. + * 1. Open all apps and wait for load complete. + * 2. Find the app and long press it to show shortcuts. + * 3. Press icon center until shortcuts appear + * 4. Drags shortcut to any free space in the Workspace. + */ + @Test + @PortraitLandscape + @PlatinumTest(focusArea = "launcher") + public void testDragShortcut() { + + final HomeAllApps allApps = mLauncher + .getWorkspace() + .switchToAllApps(); + allApps.freeze(); + try { + final HomeAppIconMenuItem menuItem = allApps + .getAppIcon(TEST_APP_NAME) + .openDeepShortcutMenu() + .getMenuItem(0); + final String actualShortcutName = menuItem.getText(); + final String expectedShortcutName = "Shortcut 1"; + + assertEquals(expectedShortcutName, actualShortcutName); + menuItem.dragToWorkspace(false, false); + mLauncher.getWorkspace().getWorkspaceAppIcon(expectedShortcutName) + .launch(getAppPackageName()); + } finally { + allApps.unfreeze(); + } + } + + /** + * Similar to testDragShortcut but it adds shortcuts to multiple positions of the Workspace + * namely the corners and the center. + */ + @Test + @PortraitLandscape + @PlatinumTest(focusArea = "launcher") + public void testDragShortcutToMultipleWorkspaceCells() { + Point[] targets = TestUtil.getCornersAndCenterPositions(mLauncher); + + for (Point target : targets) { + final HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps(); + allApps.freeze(); + try { + allApps.getAppIcon(TEST_APP_NAME) + .openDeepShortcutMenu() + .getMenuItem(0) + .dragToWorkspace(target.x, target.y); + } finally { + allApps.unfreeze(); + } + } + } + + /** + * Drags an icon to the workspace but instead of submitting it, it gets dragged outside of the + * Workspace to cancel it. + */ + + @Test + @PortraitLandscape + @PlatinumTest(focusArea = "launcher") + public void testDragAndCancelAppIcon() { + final HomeAppIcon homeAppIcon = createShortcutInCenterIfNotExist(GMAIL_APP_NAME); + Point positionBeforeDrag = + mLauncher.getWorkspace().getWorkspaceIconsPositions().get(GMAIL_APP_NAME); + assertNotNull("App not found in Workspace before dragging.", positionBeforeDrag); + + mLauncher.getWorkspace().dragAndCancelAppIcon(homeAppIcon); + + Point positionAfterDrag = + mLauncher.getWorkspace().getWorkspaceIconsPositions().get(GMAIL_APP_NAME); + assertNotNull("App not found in Workspace after dragging.", positionAfterDrag); + assertEquals("App not returned to same position in Workspace after drag & cancel", + positionBeforeDrag, positionAfterDrag); + } + + /** + * Drags app icon from AllApps into the Workspace. + * 1. Open all apps and wait for load complete. + * 2. Drag icon to homescreen. + * 3. Verify that the icon works on homescreen. + */ + @PlatinumTest(focusArea = "launcher") + @Test + @PortraitLandscape + public void testDragAppIcon() { + + final HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps(); + allApps.freeze(); + try { + allApps.getAppIcon(TEST_APP_NAME).dragToWorkspace(false, false); + mLauncher.getWorkspace().getWorkspaceAppIcon(TEST_APP_NAME).launch(getAppPackageName()); + } finally { + allApps.unfreeze(); + } + executeOnLauncher(launcher -> assertTrue( + "Launcher activity is the top activity; expecting another activity to be the top " + + "one", + isInLaunchedApp(launcher))); + } + + /** + * Similar start to testDragAppIcon but after dragging the icon to the workspace, it drags the + * icon inside of the workspace to different positions. + * @throws Exception + */ + @Test + @PortraitLandscape + @PlatinumTest(focusArea = "launcher") + public void testDragAppIconToMultipleWorkspaceCells() throws Exception { + long startTime, endTime, elapsedTime; + Point[] targets = TestUtil.getCornersAndCenterPositions(mLauncher); + + for (Point target : targets) { + startTime = SystemClock.uptimeMillis(); + final HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps(); + allApps.freeze(); + try { + allApps.getAppIcon(TEST_APP_NAME).dragToWorkspace(target.x, target.y); + } finally { + allApps.unfreeze(); + } + // Reset the workspace for the next shortcut creation. + initialize(this, true); + endTime = SystemClock.uptimeMillis(); + elapsedTime = endTime - startTime; + Log.d("testDragAppIconToWorkspaceCellTime", + "Milliseconds taken to drag app icon to workspace cell: " + elapsedTime); + } + + // test to move a shortcut to other cell. + final HomeAppIcon launcherTestAppIcon = createShortcutInCenterIfNotExist(TEST_APP_NAME); + for (Point target : targets) { + startTime = SystemClock.uptimeMillis(); + launcherTestAppIcon.dragToWorkspace(target.x, target.y); + endTime = SystemClock.uptimeMillis(); + elapsedTime = endTime - startTime; + Log.d("testDragAppIconToWorkspaceCellTime", + "Milliseconds taken to move shortcut to other cell: " + elapsedTime); + } + } +} diff --git a/tests/src/com/android/launcher3/dragging/TaplUninstallRemove.java b/tests/src/com/android/launcher3/dragging/TaplUninstallRemove.java new file mode 100644 index 0000000000..568fc9f9c3 --- /dev/null +++ b/tests/src/com/android/launcher3/dragging/TaplUninstallRemove.java @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.dragging; + +import static com.android.launcher3.testing.shared.TestProtocol.ICON_MISSING; +import static com.android.launcher3.ui.AbstractLauncherUiTest.initialize; +import static com.android.launcher3.util.TestConstants.AppNames.DUMMY_APP_NAME; +import static com.android.launcher3.util.TestConstants.AppNames.GMAIL_APP_NAME; +import static com.android.launcher3.util.TestConstants.AppNames.MAPS_APP_NAME; +import static com.android.launcher3.util.TestConstants.AppNames.STORE_APP_NAME; +import static com.android.launcher3.util.TestConstants.AppNames.TEST_APP_NAME; +import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL; +import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT; + +import static com.google.common.truth.Truth.assertThat; + +import android.graphics.Point; +import android.platform.test.annotations.PlatinumTest; +import android.util.Log; + +import com.android.launcher3.tapl.HomeAllApps; +import com.android.launcher3.tapl.HomeAppIcon; +import com.android.launcher3.tapl.Workspace; +import com.android.launcher3.ui.AbstractLauncherUiTest; +import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape; +import com.android.launcher3.util.TestUtil; +import com.android.launcher3.util.Wait; +import com.android.launcher3.util.rule.TestStabilityRule; + +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Map; + +/** + * Test runs in Out of process (Oop) and In process (Ipc) + * Test the behaviour of uninstalling and removing apps both from AllApps, Workspace and Hotseat. + */ +public class TaplUninstallRemove extends AbstractLauncherUiTest { + + @Before + public void setUp() throws Exception { + super.setUp(); + initialize(this); + } + + /** + * Deletes app both built-in and user-installed from the Workspace and makes sure it's no longer + * in the Workspace. + */ + @Test + @PortraitLandscape + public void testDeleteFromWorkspace() { + for (String appName : new String[]{GMAIL_APP_NAME, STORE_APP_NAME, TEST_APP_NAME}) { + final HomeAppIcon homeAppIcon = createShortcutInCenterIfNotExist(appName); + Workspace workspace = mLauncher.getWorkspace().deleteAppIcon(homeAppIcon); + workspace.verifyWorkspaceAppIconIsGone( + appName + " app was found after being deleted from workspace", + appName); + } + } + + private void verifyAppUninstalledFromAllApps(Workspace workspace, String appName) { + final HomeAllApps allApps = workspace.switchToAllApps(); + Wait.atMost(appName + " app was found on all apps after being uninstalled", + () -> allApps.tryGetAppIcon(appName) == null, + DEFAULT_UI_TIMEOUT, mLauncher); + } + + private void installDummyAppAndWaitForUIUpdate() throws IOException { + TestUtil.installDummyApp(); + waitForLauncherUIUpdate(); + } + + private void waitForLauncherUIUpdate() { + // Wait for model thread completion as it may be processing + // the install event from the SystemService + mLauncher.waitForModelQueueCleared(); + // Wait for Launcher UI thread completion, as it may be processing updating the UI in + // response to the model update. Not that `waitForLauncherInitialized` is just a proxy + // method, we can use any method which touches Launcher UI thread, + mLauncher.waitForLauncherInitialized(); + } + + /** + * Makes sure you can uninstall an app from the Workspace. + */ + @Test + @PortraitLandscape + @PlatinumTest(focusArea = "launcher") + @TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/311099513 + public void testUninstallFromWorkspace() throws Exception { + installDummyAppAndWaitForUIUpdate(); + try { + verifyAppUninstalledFromAllApps( + createShortcutInCenterIfNotExist(DUMMY_APP_NAME).uninstall(), DUMMY_APP_NAME); + } finally { + TestUtil.uninstallDummyApp(); + } + } + + /** + * Makes sure you can uninstall an app from AllApps. + */ + @Test + @PortraitLandscape + @PlatinumTest(focusArea = "launcher") + public void testUninstallFromAllApps() throws Exception { + installDummyAppAndWaitForUIUpdate(); + try { + Workspace workspace = mLauncher.getWorkspace(); + final HomeAllApps allApps = workspace.switchToAllApps(); + workspace = allApps.getAppIcon(DUMMY_APP_NAME).uninstall(); + verifyAppUninstalledFromAllApps(workspace, DUMMY_APP_NAME); + } finally { + TestUtil.uninstallDummyApp(); + } + } + + /** + * Adds three icons to the workspace and removes one of them by dragging to uninstall. + */ + @Test + @PlatinumTest(focusArea = "launcher") + public void uninstallWorkspaceIcon() throws IOException { + Point[] gridPositions = TestUtil.getCornersAndCenterPositions(mLauncher); + StringBuilder sb = new StringBuilder(); + for (Point p : gridPositions) { + sb.append(p).append(", "); + } + Log.d(ICON_MISSING, "allGridPositions: " + sb); + try { + installDummyAppAndWaitForUIUpdate(); + + final String[] appNameCandidates = {DUMMY_APP_NAME, MAPS_APP_NAME, STORE_APP_NAME}; + + // List of test apps trimmed down to the length of grid positions. + final String[] appNames = Arrays.copyOfRange( + appNameCandidates, + 0, Math.min(gridPositions.length, appNameCandidates.length)); + + for (int i = 0; i < appNames.length; ++i) { + createShortcutIfNotExist(appNames[i], gridPositions[i]); + } + + Map<String, Point> initialPositions = + mLauncher.getWorkspace().getWorkspaceIconsPositions(); + assertThat(initialPositions.keySet()).containsAtLeastElementsIn(appNames); + + mLauncher.getWorkspace().getWorkspaceAppIcon(DUMMY_APP_NAME).uninstall(); + mLauncher.getWorkspace().verifyWorkspaceAppIconIsGone( + DUMMY_APP_NAME + " was expected to disappear after uninstall.", DUMMY_APP_NAME); + + if (!TestStabilityRule.isPresubmit()) { // b/315847371 + Map<String, Point> finalPositions = + mLauncher.getWorkspace().getWorkspaceIconsPositions(); + assertThat(finalPositions).doesNotContainKey(DUMMY_APP_NAME); + } + } finally { + TestUtil.uninstallDummyApp(); + } + } + + /** + * Drag icon from the Hotseat to the delete drop target + */ + @Test + @PortraitLandscape + public void testAddDeleteShortcutOnHotseat() { + mLauncher.getWorkspace() + .deleteAppIcon(mLauncher.getWorkspace().getHotseatAppIcon(0)) + .switchToAllApps() + .getAppIcon(TEST_APP_NAME) + .dragToHotseat(0); + mLauncher.getWorkspace().deleteAppIcon( + mLauncher.getWorkspace().getHotseatAppIcon(TEST_APP_NAME)); + } +} diff --git a/tests/src/com/android/launcher3/logging/StartupLatencyLoggerTest.kt b/tests/src/com/android/launcher3/logging/StartupLatencyLoggerTest.kt index fffa6d7b08..130dfad2ac 100644 --- a/tests/src/com/android/launcher3/logging/StartupLatencyLoggerTest.kt +++ b/tests/src/com/android/launcher3/logging/StartupLatencyLoggerTest.kt @@ -9,13 +9,12 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -/** Unit test for [StartupLatencyLogger]. */ +/** Unit test for [ColdRebootStartupLatencyLogger]. */ @SmallTest @RunWith(AndroidJUnit4::class) class StartupLatencyLoggerTest { - private val underTest: StartupLatencyLogger = - StartupLatencyLogger(StatsLogManager.StatsLatencyLogger.LatencyType.COLD) + private val underTest = ColdRebootStartupLatencyLogger() @Before fun setup() { @@ -155,105 +154,6 @@ class StartupLatencyLoggerTest { @Test @UiThreadTest - fun loadStartOfWorkspace_thenEndWithSync_logSyncStart() { - underTest - .logStart( - StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION, - 100 - ) - .logWorkspaceLoadStartTime(111) - - underTest.logEnd( - StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_SYNC, - 120 - ) - - assertThat(underTest.startTimeByEvent.size()).isEqualTo(2) - assertThat( - underTest.startTimeByEvent.get( - StatsLogManager.LauncherLatencyEvent - .LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_SYNC - .id - ) - ) - .isEqualTo(111) - } - - @Test - @UiThreadTest - fun loadStartOfWorkspaceLoadSync_thenAsync_asyncNotLogged() { - underTest - .logStart( - StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION, - 100 - ) - .logStart( - StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_SYNC, - 110 - ) - - underTest.logStart( - StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_ASYNC, - 111 - ) - - assertThat(underTest.startTimeByEvent.size()).isEqualTo(2) - assertThat( - underTest.startTimeByEvent.get( - StatsLogManager.LauncherLatencyEvent - .LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_SYNC - .id - ) - ) - .isEqualTo(110) - assertThat( - underTest.startTimeByEvent.get( - StatsLogManager.LauncherLatencyEvent - .LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_ASYNC - .id - ) - ) - .isEqualTo(0) - } - - @Test - @UiThreadTest - fun loadStartOfWorkspaceLoadAsync_thenSync_syncNotLogged() { - underTest.logStart( - StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION, - 100 - ) - underTest.logStart( - StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_ASYNC, - 111 - ) - - underTest.logStart( - StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_SYNC, - 112 - ) - - assertThat(underTest.startTimeByEvent.size()).isEqualTo(2) - assertThat( - underTest.startTimeByEvent.get( - StatsLogManager.LauncherLatencyEvent - .LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_ASYNC - .id - ) - ) - .isEqualTo(111) - assertThat( - underTest.startTimeByEvent.get( - StatsLogManager.LauncherLatencyEvent - .LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_SYNC - .id - ) - ) - .isEqualTo(0) - } - - @Test - @UiThreadTest fun logEndOfEvent_withoutStartEvent_notLogged() { underTest.logStart( StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION, @@ -261,7 +161,7 @@ class StartupLatencyLoggerTest { ) underTest.logEnd( - StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_SYNC, + StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_ASYNC, 120 ) @@ -269,7 +169,7 @@ class StartupLatencyLoggerTest { assertThat( underTest.endTimeByEvent.get( StatsLogManager.LauncherLatencyEvent - .LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_SYNC + .LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_ASYNC .id ) ) @@ -290,7 +190,7 @@ class StartupLatencyLoggerTest { ) underTest.logEnd( - StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_SYNC, + StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_ASYNC, 121 ) @@ -298,7 +198,7 @@ class StartupLatencyLoggerTest { assertThat( underTest.endTimeByEvent.get( StatsLogManager.LauncherLatencyEvent - .LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_SYNC + .LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_ASYNC .id ) ) @@ -357,12 +257,35 @@ class StartupLatencyLoggerTest { assertThat(underTest.startTimeByEvent.size()).isEqualTo(4) assertThat(underTest.endTimeByEvent.size()).isEqualTo(4) assertThat(underTest.cardinality).isEqualTo(235) + assertThat(underTest.isTornDown).isFalse() + + underTest.reset() + + assertThat(underTest.startTimeByEvent.isEmpty()).isTrue() + assertThat(underTest.endTimeByEvent.isEmpty()).isTrue() + assertThat(underTest.cardinality).isEqualTo(ColdRebootStartupLatencyLogger.UNSET_INT) + assertThat(underTest.workspaceLoadStartTime) + .isEqualTo(ColdRebootStartupLatencyLogger.UNSET_LONG) + assertThat(underTest.isTornDown).isTrue() + } + @Test + @UiThreadTest + fun tornDown_rejectLogs() { underTest.reset() + underTest + .logStart( + StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION, + 100 + ) + .logEnd( + StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION, + 200 + ) + .logCardinality(123) assertThat(underTest.startTimeByEvent.isEmpty()).isTrue() assertThat(underTest.endTimeByEvent.isEmpty()).isTrue() - assertThat(underTest.cardinality).isEqualTo(StartupLatencyLogger.UNSET_INT) - assertThat(underTest.workspaceLoadStartTime).isEqualTo(StartupLatencyLogger.UNSET_LONG) + assertThat(underTest.cardinality).isEqualTo(ColdRebootStartupLatencyLogger.UNSET_INT) } } diff --git a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt index 115522721a..78c61d5c2b 100644 --- a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt +++ b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt @@ -24,20 +24,19 @@ import com.android.launcher3.model.data.WorkspaceItemInfo import com.android.launcher3.util.Executors import com.android.launcher3.util.IntArray import com.android.launcher3.util.TestUtil.runOnExecutorSync -import com.android.launcher3.util.any -import com.android.launcher3.util.eq -import com.android.launcher3.util.same import com.google.common.truth.Truth.assertThat import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mock import org.mockito.Mockito.times -import org.mockito.Mockito.verify -import org.mockito.Mockito.verifyZeroInteractions -import org.mockito.Mockito.`when` as whenever -import org.mockito.MockitoAnnotations +import org.mockito.kotlin.any +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.same +import org.mockito.kotlin.verify +import org.mockito.kotlin.verifyZeroInteractions +import org.mockito.kotlin.whenever /** Tests for [AddWorkspaceItemsTask] */ @SmallTest @@ -46,12 +45,11 @@ class AddWorkspaceItemsTaskTest : AbstractWorkspaceModelTest() { private lateinit var mDataModelCallbacks: MyCallbacks - @Mock private lateinit var mWorkspaceItemSpaceFinder: WorkspaceItemSpaceFinder + private val mWorkspaceItemSpaceFinder: WorkspaceItemSpaceFinder = mock() @Before override fun setup() { super.setup() - MockitoAnnotations.initMocks(this) mDataModelCallbacks = MyCallbacks() Executors.MAIN_EXECUTOR.submit { mModelHelper.model.addCallbacks(mDataModelCallbacks) } .get() diff --git a/tests/src/com/android/launcher3/model/DatabaseHelperTest.kt b/tests/src/com/android/launcher3/model/DatabaseHelperTest.kt new file mode 100644 index 0000000000..c9ea421039 --- /dev/null +++ b/tests/src/com/android/launcher3/model/DatabaseHelperTest.kt @@ -0,0 +1,79 @@ +package com.android.launcher3.model + +import android.database.sqlite.SQLiteDatabase +import android.os.UserHandle +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import androidx.test.platform.app.InstrumentationRegistry +import com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME +import com.android.launcher3.LauncherSettings.Favorites.TMP_TABLE +import com.android.launcher3.LauncherSettings.Favorites.addTableToDb +import com.android.launcher3.pm.UserCache +import com.android.launcher3.provider.LauncherDbUtils +import java.util.function.ToLongFunction +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Test +import org.junit.runner.RunWith + +private const val INSERTION_SQL = "databases/v30_workspace_items.sql" + +private const val ICON_PACKAGE = "iconPackage" +private const val ICON_RESOURCE = "iconResource" + +@SmallTest +@RunWith(AndroidJUnit4::class) +class DatabaseHelperTest { + + /** + * b/304687723 occurred when a return was accidentally added to a case statement in + * DatabaseHelper.onUpgrade, which stopped the final data migration from successfully occurring. + * This test loads an in-memory db from a text file containing SQL statements, and then performs + * the migration on the db, and verifies that the correct columns have been deleted. + */ + @Test + fun onUpgrade_to_version_32_from_30() { + val context = InstrumentationRegistry.getInstrumentation().targetContext + val userSerialProvider = + ToLongFunction<UserHandle> { + UserCache.INSTANCE.get(context).getSerialNumberForUser(it) + } + val dbHelper = DatabaseHelper(context, null, userSerialProvider) {} + val db = FactitiousDbController(context, INSERTION_SQL).inMemoryDb + + dbHelper.onUpgrade(db, 30, 32) + + assertFalse(hasFavoritesColumn(db, ICON_PACKAGE)) + assertFalse(hasFavoritesColumn(db, ICON_RESOURCE)) + } + + /** + * b/304687723 causes a crash due to copying a table with 21 columns to a table with 19 columns. + * This test loads an in-memory db from a text file containing SQL statements, and then copies + * data from the created table into a temporary one, and verifies that no exception is thrown. + */ + @Test + fun after_migrating_from_db_v30_to_v32_copy_table() { + val context = InstrumentationRegistry.getInstrumentation().targetContext + val db = FactitiousDbController(context, INSERTION_SQL).inMemoryDb // v30 - 21 columns + + addTableToDb(db, 1, true, TMP_TABLE) + LauncherDbUtils.copyTable(db, TABLE_NAME, db, TMP_TABLE, context) + + val c1 = db.query(TABLE_NAME, null, null, null, null, null, null) + val c2 = db.query(TMP_TABLE, null, null, null, null, null, null) + + assertEquals(21, c1.columnCount) + assertEquals(19, c2.columnCount) + assertEquals(c1.count, c2.count) + + c1.close() + c2.close() + } + + private fun hasFavoritesColumn(db: SQLiteDatabase, columnName: String): Boolean { + db.query(TABLE_NAME, null, null, null, null, null, null).use { c -> + return c.getColumnIndex(columnName) >= 0 + } + } +} diff --git a/tests/src/com/android/launcher3/model/FactitiousDbController.kt b/tests/src/com/android/launcher3/model/FactitiousDbController.kt new file mode 100644 index 0000000000..711e1d2d65 --- /dev/null +++ b/tests/src/com/android/launcher3/model/FactitiousDbController.kt @@ -0,0 +1,60 @@ +package com.android.launcher3.model + +import android.content.Context +import android.database.Cursor +import android.database.sqlite.SQLiteDatabase +import androidx.test.platform.app.InstrumentationRegistry +import java.io.BufferedReader +import java.io.InputStreamReader + +private val All_COLUMNS = + arrayOf( + "_id", + "title", + "intent", + "container", + "screen", + "cellX", + "cellY", + "spanX", + "spanY", + "itemType", + "appWidgetId", + "icon", + "appWidgetProvider", + "modified", + "restored", + "profileId", + "rank", + "options", + "appWidgetSource" + ) + +class FactitiousDbController(context: Context, insertFile: String) : ModelDbController(context) { + + val inMemoryDb: SQLiteDatabase by lazy { + SQLiteDatabase.createInMemory(SQLiteDatabase.OpenParams.Builder().build()).also { db -> + BufferedReader( + InputStreamReader( + InstrumentationRegistry.getInstrumentation().context.assets.open(insertFile) + ) + ) + .lines() + .forEach { sqlStatement -> db.execSQL(sqlStatement) } + } + } + + override fun query( + table: String, + projection: Array<out String>?, + selection: String?, + selectionArgs: Array<out String>?, + sortOrder: String? + ): Cursor { + return inMemoryDb.query(table, All_COLUMNS, selection, selectionArgs, null, null, sortOrder) + } + + override fun loadDefaultFavoritesIfNecessary() { + // No-Op + } +} diff --git a/tests/src/com/android/launcher3/model/FolderIconLoadTest.kt b/tests/src/com/android/launcher3/model/FolderIconLoadTest.kt new file mode 100644 index 0000000000..60a4d2d667 --- /dev/null +++ b/tests/src/com/android/launcher3/model/FolderIconLoadTest.kt @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.model + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.launcher3.LauncherAppState +import com.android.launcher3.model.data.WorkspaceItemInfo +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.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.Test +import org.junit.runner.RunWith + +/** Tests to verify that folder icons are loaded with appropriate resolution */ +@SmallTest +@RunWith(AndroidJUnit4::class) +class FolderIconLoadTest { + private lateinit var modelHelper: LauncherModelHelper + + private val uniqueActivities = + listOf( + 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 + ) + + @Before + fun setUp() { + modelHelper = LauncherModelHelper() + } + + @After + @Throws(Exception::class) + fun tearDown() { + modelHelper.destroy() + TestUtil.uninstallDummyApp() + } + + @Test + @Throws(Exception::class) + fun folderLoadedWithHighRes_2x2() { + val items = setupAndLoadFolder(4) + assertThat(items.size).isEqualTo(4) + verifyHighRes(items, 0, 1, 2, 3) + } + + @Test + @Throws(Exception::class) + fun folderLoadedWithHighRes_3x2() { + val items = setupAndLoadFolder(6) + assertThat(items.size).isEqualTo(6) + verifyHighRes(items, 0, 1, 3, 4) + verifyLowRes(items, 2, 5) + } + + @Test + @Throws(Exception::class) + fun folderLoadedWithHighRes_max_3x3() { + val idp = LauncherAppState.getIDP(modelHelper.sandboxContext) + idp.numFolderColumns = intArrayOf(3, 3, 3, 3) + idp.numFolderRows = intArrayOf(3, 3, 3, 3) + recreateSupportedDeviceProfiles() + + val items = setupAndLoadFolder(14) + verifyHighRes(items, 0, 1, 3, 4) + verifyLowRes(items, 2, 5, 6, 7, 8, 9, 10, 11, 12, 13) + } + + @Test + @Throws(Exception::class) + fun folderLoadedWithHighRes_max_4x4() { + val idp = LauncherAppState.getIDP(modelHelper.sandboxContext) + idp.numFolderColumns = intArrayOf(4, 4, 4, 4) + idp.numFolderRows = intArrayOf(4, 4, 4, 4) + recreateSupportedDeviceProfiles() + + val items = setupAndLoadFolder(14) + verifyHighRes(items, 0, 1, 4, 5) + verifyLowRes(items, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13) + } + + @Test + @Throws(Exception::class) + fun folderLoadedWithHighRes_differentFolderConfigurations() { + val idp = LauncherAppState.getIDP(modelHelper.sandboxContext) + idp.numFolderColumns = intArrayOf(4, 3, 4, 4) + idp.numFolderRows = intArrayOf(4, 3, 4, 4) + recreateSupportedDeviceProfiles() + + val items = setupAndLoadFolder(14) + verifyHighRes(items, 0, 1, 3, 4, 5) + verifyLowRes(items, 2, 6, 7, 8, 9, 10, 11, 12, 13) + } + + @Throws(Exception::class) + private fun setupAndLoadFolder(itemCount: Int): ArrayList<WorkspaceItemInfo> { + val builder = + LauncherLayoutBuilder() + .atWorkspace(0, 0, 1) + .putFolder("Sample") + .apply { + for (i in 0..itemCount - 1) this.addApp(TEST_PACKAGE, uniqueActivities[i]) + } + .build() + + modelHelper.setupDefaultLayoutProvider(builder) + modelHelper.loadModelSync() + + // The first load initializes the DB, load again so that icons are now used from the DB + // Wait for the icon cache to be updated and then reload + val app = LauncherAppState.getInstance(modelHelper.sandboxContext) + val cache = app.iconCache + while (cache.isIconUpdateInProgress) { + val wait = CountDownLatch(1) + Executors.MODEL_EXECUTOR.handler.postDelayed({ wait.countDown() }, 10) + wait.await() + } + TestUtil.runOnExecutorSync(Executors.MODEL_EXECUTOR) { cache.clearMemoryCache() } + // Reload again with correct icon state + app.model.forceReload() + modelHelper.loadModelSync() + val folders = modelHelper.getBgDataModel().folders + + assertThat(folders.size()).isEqualTo(1) + assertThat(folders.valueAt(0).contents.size).isEqualTo(itemCount) + return folders.valueAt(0).contents + } + + private fun verifyHighRes(items: ArrayList<WorkspaceItemInfo>, vararg indices: Int) { + for (index in indices) { + assertWithMessage("Index $index was not highRes") + .that(items[index].bitmap.isNullOrLowRes) + .isFalse() + } + } + + private fun verifyLowRes(items: ArrayList<WorkspaceItemInfo>, vararg indices: Int) { + for (index in indices) { + assertWithMessage("Index $index was not lowRes") + .that(items[index].bitmap.isNullOrLowRes) + .isTrue() + } + } + + /** Recreate DeviceProfiles after changing InvariantDeviceProfile */ + private fun recreateSupportedDeviceProfiles() { + LauncherAppState.getIDP(modelHelper.sandboxContext).supportedProfiles = + LauncherAppState.getIDP(modelHelper.sandboxContext).supportedProfiles.map { + it.copy(modelHelper.sandboxContext) + } + } +} diff --git a/tests/src/com/android/launcher3/model/LoaderCursorTest.java b/tests/src/com/android/launcher3/model/LoaderCursorTest.java index 544ed6b01d..389ec5c09c 100644 --- a/tests/src/com/android/launcher3/model/LoaderCursorTest.java +++ b/tests/src/com/android/launcher3/model/LoaderCursorTest.java @@ -175,7 +175,7 @@ public class LoaderCursorTest { // Item outside screen bounds are not placed assertFalse(mLoaderCursor.checkItemPlacement( - newItemInfo(4, 4, 1, 1, CONTAINER_DESKTOP, 1))); + newItemInfo(4, 4, 1, 1, CONTAINER_DESKTOP, 1), true)); } @Test @@ -186,22 +186,22 @@ public class LoaderCursorTest { // Overlapping mItems are not placed assertTrue(mLoaderCursor.checkItemPlacement( - newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 1))); + newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 1), true)); assertFalse(mLoaderCursor.checkItemPlacement( - newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 1))); + newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 1), true)); assertTrue(mLoaderCursor.checkItemPlacement( - newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 2))); + newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 2), true)); assertFalse(mLoaderCursor.checkItemPlacement( - newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 2))); + newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 2), true)); assertTrue(mLoaderCursor.checkItemPlacement( - newItemInfo(1, 1, 1, 1, CONTAINER_DESKTOP, 1))); + newItemInfo(1, 1, 1, 1, CONTAINER_DESKTOP, 1), true)); assertTrue(mLoaderCursor.checkItemPlacement( - newItemInfo(2, 2, 2, 2, CONTAINER_DESKTOP, 1))); + newItemInfo(2, 2, 2, 2, CONTAINER_DESKTOP, 1), true)); assertFalse(mLoaderCursor.checkItemPlacement( - newItemInfo(3, 2, 1, 2, CONTAINER_DESKTOP, 1))); + newItemInfo(3, 2, 1, 2, CONTAINER_DESKTOP, 1), true)); } @Test @@ -212,12 +212,12 @@ public class LoaderCursorTest { // Hotseat mItems are only placed based on screenId assertTrue(mLoaderCursor.checkItemPlacement( - newItemInfo(3, 3, 1, 1, CONTAINER_HOTSEAT, 1))); + newItemInfo(3, 3, 1, 1, CONTAINER_HOTSEAT, 1), true)); assertTrue(mLoaderCursor.checkItemPlacement( - newItemInfo(3, 3, 1, 1, CONTAINER_HOTSEAT, 2))); + newItemInfo(3, 3, 1, 1, CONTAINER_HOTSEAT, 2), true)); assertFalse(mLoaderCursor.checkItemPlacement( - newItemInfo(3, 3, 1, 1, CONTAINER_HOTSEAT, 3))); + newItemInfo(3, 3, 1, 1, CONTAINER_HOTSEAT, 3), true)); } private ItemInfo newItemInfo(int cellX, int cellY, int spanX, int spanY, diff --git a/tests/src/com/android/launcher3/model/LoaderTaskTest.kt b/tests/src/com/android/launcher3/model/LoaderTaskTest.kt new file mode 100644 index 0000000000..def27b8897 --- /dev/null +++ b/tests/src/com/android/launcher3/model/LoaderTaskTest.kt @@ -0,0 +1,162 @@ +package com.android.launcher3.model + +import android.content.Context +import android.os.UserHandle +import android.platform.test.flag.junit.SetFlagsRule +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import androidx.test.platform.app.InstrumentationRegistry +import com.android.launcher3.Flags +import com.android.launcher3.InvariantDeviceProfile +import com.android.launcher3.LauncherAppState +import com.android.launcher3.LauncherModel +import com.android.launcher3.LauncherModel.LoaderTransaction +import com.android.launcher3.icons.IconCache +import com.android.launcher3.icons.cache.CachingLogic +import com.android.launcher3.icons.cache.IconCacheUpdateHandler +import com.android.launcher3.pm.UserCache +import com.android.launcher3.util.Executors.MODEL_EXECUTOR +import com.android.launcher3.util.LooperIdleLock +import com.android.launcher3.util.UserIconInfo +import com.android.launcher3.util.rule.StaticMockitoRule +import com.google.common.truth.Truth +import java.util.concurrent.CountDownLatch +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.any +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.Mockito.times +import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` +import org.mockito.MockitoAnnotations +import org.mockito.Spy + +private const val INSERTION_STATEMENT_FILE = "databases/workspace_items.sql" + +@SmallTest +@RunWith(AndroidJUnit4::class) +class LoaderTaskTest { + @Mock private lateinit var app: LauncherAppState + @Mock private lateinit var bgAllAppsList: AllAppsList + @Mock private lateinit var modelDelegate: ModelDelegate + @Mock private lateinit var launcherBinder: LauncherBinder + @Mock private lateinit var launcherModel: LauncherModel + @Mock private lateinit var transaction: LoaderTransaction + @Mock private lateinit var iconCache: IconCache + @Mock private lateinit var idleLock: LooperIdleLock + @Mock private lateinit var iconCacheUpdateHandler: IconCacheUpdateHandler + @Mock private lateinit var userCache: UserCache + + @Spy private var userManagerState: UserManagerState? = UserManagerState() + + @get:Rule(order = 0) val staticMockitoRule = StaticMockitoRule(UserCache::class.java) + @get:Rule(order = 1) + val setFlagsRule = SetFlagsRule().apply { initAllFlagsToReleaseConfigDefault() } + + @Before + fun setup() { + val context = InstrumentationRegistry.getInstrumentation().targetContext + val idp = + InvariantDeviceProfile.INSTANCE[context].apply { + numRows = 5 + numColumns = 6 + numDatabaseHotseatIcons = 5 + } + + MockitoAnnotations.initMocks(this) + `when`(app.context).thenReturn(context) + `when`(app.model).thenReturn(launcherModel) + `when`(launcherModel.beginLoader(any(LoaderTask::class.java))).thenReturn(transaction) + `when`(app.iconCache).thenReturn(iconCache) + `when`(launcherModel.modelDbController) + .thenReturn(FactitiousDbController(context, INSERTION_STATEMENT_FILE)) + `when`(app.invariantDeviceProfile).thenReturn(idp) + `when`(launcherBinder.newIdleLock(any(LoaderTask::class.java))).thenReturn(idleLock) + `when`(idleLock.awaitLocked(1000)).thenReturn(false) + `when`(iconCache.updateHandler).thenReturn(iconCacheUpdateHandler) + `when`(UserCache.getInstance(any(Context::class.java))).thenReturn(userCache) + } + + @Test + fun loadsDataProperly() = + with(BgDataModel()) { + LoaderTask(app, bgAllAppsList, this, modelDelegate, launcherBinder) + .runSyncOnBackgroundThread() + Truth.assertThat(workspaceItems.size).isAtLeast(25) + Truth.assertThat(appWidgets.size).isAtLeast(7) + Truth.assertThat(folders.size()).isAtLeast(8) + Truth.assertThat(itemsIdMap.size()).isAtLeast(40) + } + + @Test + fun bindsLoadedDataCorrectly() { + LoaderTask(app, bgAllAppsList, BgDataModel(), modelDelegate, launcherBinder) + .runSyncOnBackgroundThread() + + verify(launcherBinder).bindWorkspace(true, false) + verify(modelDelegate).workspaceLoadComplete() + verify(modelDelegate).loadAndBindAllAppsItems(any(), any(), any()) + verify(launcherBinder).bindAllApps() + verify(iconCacheUpdateHandler, times(4)).updateIcons(any(), any<CachingLogic<Any>>(), any()) + verify(launcherBinder).bindDeepShortcuts() + verify(launcherBinder).bindWidgets() + verify(modelDelegate).loadAndBindOtherItems(any()) + verify(iconCacheUpdateHandler).finish() + verify(modelDelegate).modelLoadComplete() + verify(transaction).commit() + } + + @Test + fun setsQuietModeFlagCorrectlyForWorkProfile() = + with(BgDataModel()) { + setFlagsRule.enableFlags(Flags.FLAG_ENABLE_PRIVATE_SPACE) + val MAIN_HANDLE = UserHandle.of(0) + val mockUserHandles = arrayListOf<UserHandle>(MAIN_HANDLE) + `when`(userCache.userProfiles).thenReturn(mockUserHandles) + `when`(userManagerState?.isUserQuiet(MAIN_HANDLE)).thenReturn(true) + `when`(userCache.getUserInfo(MAIN_HANDLE)).thenReturn(UserIconInfo(MAIN_HANDLE, 1)) + + LoaderTask(app, bgAllAppsList, this, modelDelegate, launcherBinder, userManagerState) + .runSyncOnBackgroundThread() + + verify(bgAllAppsList) + .setFlags(BgDataModel.Callbacks.FLAG_WORK_PROFILE_QUIET_MODE_ENABLED, true) + verify(bgAllAppsList) + .setFlags(BgDataModel.Callbacks.FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED, false) + verify(bgAllAppsList, Mockito.never()) + .setFlags(BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED, true) + } + + @Test + fun setsQuietModeFlagCorrectlyForPrivateProfile() = + with(BgDataModel()) { + setFlagsRule.enableFlags(Flags.FLAG_ENABLE_PRIVATE_SPACE) + val MAIN_HANDLE = UserHandle.of(0) + val mockUserHandles = arrayListOf<UserHandle>(MAIN_HANDLE) + `when`(userCache.userProfiles).thenReturn(mockUserHandles) + `when`(userManagerState?.isUserQuiet(MAIN_HANDLE)).thenReturn(true) + `when`(userCache.getUserInfo(MAIN_HANDLE)).thenReturn(UserIconInfo(MAIN_HANDLE, 3)) + + LoaderTask(app, bgAllAppsList, this, modelDelegate, launcherBinder, userManagerState) + .runSyncOnBackgroundThread() + + verify(bgAllAppsList) + .setFlags(BgDataModel.Callbacks.FLAG_WORK_PROFILE_QUIET_MODE_ENABLED, false) + verify(bgAllAppsList) + .setFlags(BgDataModel.Callbacks.FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED, true) + verify(bgAllAppsList, Mockito.never()) + .setFlags(BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED, true) + } +} + +private fun LoaderTask.runSyncOnBackgroundThread() { + val latch = CountDownLatch(1) + MODEL_EXECUTOR.execute { + run() + latch.countDown() + } + latch.await() +} diff --git a/tests/src/com/android/launcher3/nonquickstep/DeviceProfileDumpTest.kt b/tests/src/com/android/launcher3/nonquickstep/DeviceProfileDumpTest.kt index 270672f209..9b67310314 100644 --- a/tests/src/com/android/launcher3/nonquickstep/DeviceProfileDumpTest.kt +++ b/tests/src/com/android/launcher3/nonquickstep/DeviceProfileDumpTest.kt @@ -15,14 +15,14 @@ */ package com.android.launcher3.nonquickstep +import android.content.Context import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import androidx.test.platform.app.InstrumentationRegistry import com.android.launcher3.AbstractDeviceProfileTest import com.android.launcher3.DeviceProfile import com.android.launcher3.InvariantDeviceProfile import com.google.common.truth.Truth.assertThat -import java.io.PrintWriter -import java.io.StringWriter import org.junit.Test import org.junit.runner.RunWith @@ -30,142 +30,14 @@ import org.junit.runner.RunWith @SmallTest @RunWith(AndroidJUnit4::class) class DeviceProfileDumpTest : AbstractDeviceProfileTest() { - + private val testContext: Context = InstrumentationRegistry.getInstrumentation().context + private val folderName: String = "DeviceProfileDumpTest" @Test fun phonePortrait3Button() { initializeVarsForPhone(deviceSpecs["phone"]!!, isGestureMode = false) val dp = getDeviceProfileForGrid("5_by_5") - assertThat(dump(dp)) - .isEqualTo( - "DeviceProfile:\n" + - "\t1 dp = 2.625 px\n" + - "\tisTablet:false\n" + - "\tisPhone:true\n" + - "\ttransposeLayoutWithOrientation:true\n" + - "\tisGestureMode:false\n" + - "\tisLandscape:false\n" + - "\tisMultiWindowMode:false\n" + - "\tisTwoPanels:false\n" + - "\twindowX: 0.0px (0.0dp)\n" + - "\twindowY: 0.0px (0.0dp)\n" + - "\twidthPx: 1080.0px (411.42856dp)\n" + - "\theightPx: 2400.0px (914.2857dp)\n" + - "\tavailableWidthPx: 1080.0px (411.42856dp)\n" + - "\tavailableHeightPx: 2156.0px (821.3333dp)\n" + - "\tmInsets.left: 0.0px (0.0dp)\n" + - "\tmInsets.top: 118.0px (44.95238dp)\n" + - "\tmInsets.right: 0.0px (0.0dp)\n" + - "\tmInsets.bottom: 126.0px (48.0dp)\n" + - "\taspectRatio:2.2222223\n" + - "\tisResponsiveGrid:false\n" + - "\tisScalableGrid:false\n" + - "\tinv.numRows: 5\n" + - "\tinv.numColumns: 5\n" + - "\tinv.numSearchContainerColumns: 5\n" + - "\tminCellSize: PointF(0.0, 0.0)dp\n" + - "\tcellWidthPx: 159.0px (60.57143dp)\n" + - "\tcellHeightPx: 229.0px (87.2381dp)\n" + - "\tgetCellSize().x: 207.0px (78.85714dp)\n" + - "\tgetCellSize().y: 379.0px (144.38095dp)\n" + - "\tcellLayoutBorderSpacePx Horizontal: 0.0px (0.0dp)\n" + - "\tcellLayoutBorderSpacePx Vertical: 0.0px (0.0dp)\n" + - "\tcellLayoutPaddingPx.left: 21.0px (8.0dp)\n" + - "\tcellLayoutPaddingPx.top: 28.0px (10.666667dp)\n" + - "\tcellLayoutPaddingPx.right: 21.0px (8.0dp)\n" + - "\tcellLayoutPaddingPx.bottom: 28.0px (10.666667dp)\n" + - "\ticonSizePx: 147.0px (56.0dp)\n" + - "\ticonTextSizePx: 38.0px (14.476191dp)\n" + - "\ticonDrawablePaddingPx: 12.0px (4.571429dp)\n" + - "\tinv.numFolderRows: 4\n" + - "\tinv.numFolderColumns: 4\n" + - "\tfolderCellWidthPx: 195.0px (74.28571dp)\n" + - "\tfolderCellHeightPx: 230.0px (87.61905dp)\n" + - "\tfolderChildIconSizePx: 147.0px (56.0dp)\n" + - "\tfolderChildTextSizePx: 38.0px (14.476191dp)\n" + - "\tfolderChildDrawablePaddingPx: 4.0px (1.5238096dp)\n" + - "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" + - "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" + - "\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" + - "\tfolderTopPadding: 63.0px (24.0dp)\n" + - "\tfolderFooterHeight: 147.0px (56.0dp)\n" + - "\tbottomSheetTopPadding: 146.0px (55.61905dp)\n" + - "\tbottomSheetOpenDuration: 267\n" + - "\tbottomSheetCloseDuration: 267\n" + - "\tbottomSheetWorkspaceScale: 1.0\n" + - "\tbottomSheetDepth: 0.0\n" + - "\tallAppsShiftRange: 788.0px (300.1905dp)\n" + - "\tallAppsTopPadding: 0.0px (0.0dp)\n" + - "\tallAppsOpenDuration: 600\n" + - "\tallAppsCloseDuration: 300\n" + - "\tallAppsIconSizePx: 147.0px (56.0dp)\n" + - "\tallAppsIconTextSizePx: 38.0px (14.476191dp)\n" + - "\tallAppsIconDrawablePaddingPx: 21.0px (8.0dp)\n" + - "\tallAppsCellHeightPx: 315.0px (120.0dp)\n" + - "\tallAppsCellWidthPx: 189.0px (72.0dp)\n" + - "\tallAppsBorderSpacePxX: 42.0px (16.0dp)\n" + - "\tallAppsBorderSpacePxY: 42.0px (16.0dp)\n" + - "\tnumShownAllAppsColumns: 5\n" + - "\tallAppsLeftRightPadding: 0.0px (0.0dp)\n" + - "\tallAppsLeftRightMargin: 0.0px (0.0dp)\n" + - "\thotseatBarSizePx: 294.0px (112.0dp)\n" + - "\tinv.hotseatColumnSpan: 5\n" + - "\thotseatCellHeightPx: 166.0px (63.238094dp)\n" + - "\thotseatBarBottomSpacePx: 147.0px (56.0dp)\n" + - "\thotseatBarSidePaddingStartPx: 0.0px (0.0dp)\n" + - "\thotseatBarSidePaddingEndPx: 0.0px (0.0dp)\n" + - "\thotseatBarEndOffset: 0.0px (0.0dp)\n" + - "\thotseatQsbSpace: 0.0px (0.0dp)\n" + - "\thotseatQsbHeight: 0.0px (0.0dp)\n" + - "\tspringLoadedHotseatBarTopMarginPx: 200.0px (76.190475dp)\n" + - "\tgetHotseatLayoutPadding(context).top: 0.0px (0.0dp)\n" + - "\tgetHotseatLayoutPadding(context).bottom: 128.0px (48.761906dp)\n" + - "\tgetHotseatLayoutPadding(context).left: 21.0px (8.0dp)\n" + - "\tgetHotseatLayoutPadding(context).right: 21.0px (8.0dp)\n" + - "\tnumShownHotseatIcons: 5\n" + - "\thotseatBorderSpace: 0.0px (0.0dp)\n" + - "\tisQsbInline: false\n" + - "\thotseatQsbWidth: 0.0px (0.0dp)\n" + - "\tisTaskbarPresent:false\n" + - "\tisTaskbarPresentInApps:false\n" + - "\ttaskbarHeight: 0.0px (0.0dp)\n" + - "\tstashedTaskbarHeight: 0.0px (0.0dp)\n" + - "\ttaskbarBottomMargin: 0.0px (0.0dp)\n" + - "\ttaskbarIconSize: 0.0px (0.0dp)\n" + - "\tdesiredWorkspaceHorizontalMarginPx: 21.0px (8.0dp)\n" + - "\tworkspacePadding.left: 0.0px (0.0dp)\n" + - "\tworkspacePadding.top: 0.0px (0.0dp)\n" + - "\tworkspacePadding.right: 0.0px (0.0dp)\n" + - "\tworkspacePadding.bottom: 203.0px (77.333336dp)\n" + - "\ticonScale: 1.0px (0.3809524dp)\n" + - "\tcellScaleToFit : 1.0px (0.3809524dp)\n" + - "\textraSpace: 752.0px (286.4762dp)\n" + - "\tunscaled extraSpace: 752.0px (286.4762dp)\n" + - "\tmaxEmptySpace: 0.0px (0.0dp)\n" + - "\tworkspaceTopPadding: 0.0px (0.0dp)\n" + - "\tworkspaceBottomPadding: 0.0px (0.0dp)\n" + - "\toverviewTaskMarginPx: 0.0px (0.0dp)\n" + - "\toverviewTaskIconSizePx: 0.0px (0.0dp)\n" + - "\toverviewTaskIconDrawableSizePx: 0.0px (0.0dp)\n" + - "\toverviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)\n" + - "\toverviewTaskThumbnailTopMarginPx: 0.0px (0.0dp)\n" + - "\toverviewActionsTopMarginPx: 0.0px (0.0dp)\n" + - "\toverviewActionsHeight: 0.0px (0.0dp)\n" + - "\toverviewActionsClaimedSpaceBelow: 126.0px (48.0dp)\n" + - "\toverviewActionsButtonSpacing: 0.0px (0.0dp)\n" + - "\toverviewPageSpacing: 0.0px (0.0dp)\n" + - "\toverviewRowSpacing: 0.0px (0.0dp)\n" + - "\toverviewGridSideMargin: 0.0px (0.0dp)\n" + - "\tdropTargetBarTopMarginPx: 84.0px (32.0dp)\n" + - "\tdropTargetBarSizePx: 147.0px (56.0dp)\n" + - "\tdropTargetBarBottomMarginPx: 42.0px (16.0dp)\n" + - "\tgetCellLayoutSpringLoadShrunkTop(): 391.0px (148.95238dp)\n" + - "\tgetCellLayoutSpringLoadShrunkBottom(): 1906.0px (726.0952dp)\n" + - "\tworkspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)\n" + - "\tgetWorkspaceSpringLoadScale(): 0.77572966px (0.29551607dp)\n" + - "\tgetCellLayoutHeight(): 1953.0px (744.0dp)\n" + - "\tgetCellLayoutWidth(): 1080.0px (411.42856dp)\n" - ) + assertDump(dp, "phonePortrait3Button") } @Test @@ -173,136 +45,7 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { initializeVarsForPhone(deviceSpecs["phone"]!!) val dp = getDeviceProfileForGrid("5_by_5") - assertThat(dump(dp)) - .isEqualTo( - "DeviceProfile:\n" + - "\t1 dp = 2.625 px\n" + - "\tisTablet:false\n" + - "\tisPhone:true\n" + - "\ttransposeLayoutWithOrientation:true\n" + - "\tisGestureMode:true\n" + - "\tisLandscape:false\n" + - "\tisMultiWindowMode:false\n" + - "\tisTwoPanels:false\n" + - "\twindowX: 0.0px (0.0dp)\n" + - "\twindowY: 0.0px (0.0dp)\n" + - "\twidthPx: 1080.0px (411.42856dp)\n" + - "\theightPx: 2400.0px (914.2857dp)\n" + - "\tavailableWidthPx: 1080.0px (411.42856dp)\n" + - "\tavailableHeightPx: 2219.0px (845.3333dp)\n" + - "\tmInsets.left: 0.0px (0.0dp)\n" + - "\tmInsets.top: 118.0px (44.95238dp)\n" + - "\tmInsets.right: 0.0px (0.0dp)\n" + - "\tmInsets.bottom: 63.0px (24.0dp)\n" + - "\taspectRatio:2.2222223\n" + - "\tisResponsiveGrid:false\n" + - "\tisScalableGrid:false\n" + - "\tinv.numRows: 5\n" + - "\tinv.numColumns: 5\n" + - "\tinv.numSearchContainerColumns: 5\n" + - "\tminCellSize: PointF(0.0, 0.0)dp\n" + - "\tcellWidthPx: 159.0px (60.57143dp)\n" + - "\tcellHeightPx: 229.0px (87.2381dp)\n" + - "\tgetCellSize().x: 207.0px (78.85714dp)\n" + - "\tgetCellSize().y: 383.0px (145.90475dp)\n" + - "\tcellLayoutBorderSpacePx Horizontal: 0.0px (0.0dp)\n" + - "\tcellLayoutBorderSpacePx Vertical: 0.0px (0.0dp)\n" + - "\tcellLayoutPaddingPx.left: 21.0px (8.0dp)\n" + - "\tcellLayoutPaddingPx.top: 28.0px (10.666667dp)\n" + - "\tcellLayoutPaddingPx.right: 21.0px (8.0dp)\n" + - "\tcellLayoutPaddingPx.bottom: 28.0px (10.666667dp)\n" + - "\ticonSizePx: 147.0px (56.0dp)\n" + - "\ticonTextSizePx: 38.0px (14.476191dp)\n" + - "\ticonDrawablePaddingPx: 12.0px (4.571429dp)\n" + - "\tinv.numFolderRows: 4\n" + - "\tinv.numFolderColumns: 4\n" + - "\tfolderCellWidthPx: 195.0px (74.28571dp)\n" + - "\tfolderCellHeightPx: 230.0px (87.61905dp)\n" + - "\tfolderChildIconSizePx: 147.0px (56.0dp)\n" + - "\tfolderChildTextSizePx: 38.0px (14.476191dp)\n" + - "\tfolderChildDrawablePaddingPx: 4.0px (1.5238096dp)\n" + - "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" + - "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" + - "\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" + - "\tfolderTopPadding: 63.0px (24.0dp)\n" + - "\tfolderFooterHeight: 147.0px (56.0dp)\n" + - "\tbottomSheetTopPadding: 146.0px (55.61905dp)\n" + - "\tbottomSheetOpenDuration: 267\n" + - "\tbottomSheetCloseDuration: 267\n" + - "\tbottomSheetWorkspaceScale: 1.0\n" + - "\tbottomSheetDepth: 0.0\n" + - "\tallAppsShiftRange: 788.0px (300.1905dp)\n" + - "\tallAppsTopPadding: 0.0px (0.0dp)\n" + - "\tallAppsOpenDuration: 600\n" + - "\tallAppsCloseDuration: 300\n" + - "\tallAppsIconSizePx: 147.0px (56.0dp)\n" + - "\tallAppsIconTextSizePx: 38.0px (14.476191dp)\n" + - "\tallAppsIconDrawablePaddingPx: 21.0px (8.0dp)\n" + - "\tallAppsCellHeightPx: 315.0px (120.0dp)\n" + - "\tallAppsCellWidthPx: 189.0px (72.0dp)\n" + - "\tallAppsBorderSpacePxX: 42.0px (16.0dp)\n" + - "\tallAppsBorderSpacePxY: 42.0px (16.0dp)\n" + - "\tnumShownAllAppsColumns: 5\n" + - "\tallAppsLeftRightPadding: 0.0px (0.0dp)\n" + - "\tallAppsLeftRightMargin: 0.0px (0.0dp)\n" + - "\thotseatBarSizePx: 273.0px (104.0dp)\n" + - "\tinv.hotseatColumnSpan: 5\n" + - "\thotseatCellHeightPx: 166.0px (63.238094dp)\n" + - "\thotseatBarBottomSpacePx: 126.0px (48.0dp)\n" + - "\thotseatBarSidePaddingStartPx: 0.0px (0.0dp)\n" + - "\thotseatBarSidePaddingEndPx: 0.0px (0.0dp)\n" + - "\thotseatBarEndOffset: 0.0px (0.0dp)\n" + - "\thotseatQsbSpace: 0.0px (0.0dp)\n" + - "\thotseatQsbHeight: 0.0px (0.0dp)\n" + - "\tspringLoadedHotseatBarTopMarginPx: 200.0px (76.190475dp)\n" + - "\tgetHotseatLayoutPadding(context).top: 0.0px (0.0dp)\n" + - "\tgetHotseatLayoutPadding(context).bottom: 107.0px (40.761906dp)\n" + - "\tgetHotseatLayoutPadding(context).left: 21.0px (8.0dp)\n" + - "\tgetHotseatLayoutPadding(context).right: 21.0px (8.0dp)\n" + - "\tnumShownHotseatIcons: 5\n" + - "\thotseatBorderSpace: 0.0px (0.0dp)\n" + - "\tisQsbInline: false\n" + - "\thotseatQsbWidth: 0.0px (0.0dp)\n" + - "\tisTaskbarPresent:false\n" + - "\tisTaskbarPresentInApps:false\n" + - "\ttaskbarHeight: 0.0px (0.0dp)\n" + - "\tstashedTaskbarHeight: 0.0px (0.0dp)\n" + - "\ttaskbarBottomMargin: 0.0px (0.0dp)\n" + - "\ttaskbarIconSize: 0.0px (0.0dp)\n" + - "\tdesiredWorkspaceHorizontalMarginPx: 21.0px (8.0dp)\n" + - "\tworkspacePadding.left: 0.0px (0.0dp)\n" + - "\tworkspacePadding.top: 0.0px (0.0dp)\n" + - "\tworkspacePadding.right: 0.0px (0.0dp)\n" + - "\tworkspacePadding.bottom: 245.0px (93.333336dp)\n" + - "\ticonScale: 1.0px (0.3809524dp)\n" + - "\tcellScaleToFit : 1.0px (0.3809524dp)\n" + - "\textraSpace: 773.0px (294.4762dp)\n" + - "\tunscaled extraSpace: 773.0px (294.4762dp)\n" + - "\tmaxEmptySpace: 0.0px (0.0dp)\n" + - "\tworkspaceTopPadding: 0.0px (0.0dp)\n" + - "\tworkspaceBottomPadding: 0.0px (0.0dp)\n" + - "\toverviewTaskMarginPx: 0.0px (0.0dp)\n" + - "\toverviewTaskIconSizePx: 0.0px (0.0dp)\n" + - "\toverviewTaskIconDrawableSizePx: 0.0px (0.0dp)\n" + - "\toverviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)\n" + - "\toverviewTaskThumbnailTopMarginPx: 0.0px (0.0dp)\n" + - "\toverviewActionsTopMarginPx: 0.0px (0.0dp)\n" + - "\toverviewActionsHeight: 0.0px (0.0dp)\n" + - "\toverviewActionsClaimedSpaceBelow: 63.0px (24.0dp)\n" + - "\toverviewActionsButtonSpacing: 0.0px (0.0dp)\n" + - "\toverviewPageSpacing: 0.0px (0.0dp)\n" + - "\toverviewRowSpacing: 0.0px (0.0dp)\n" + - "\toverviewGridSideMargin: 0.0px (0.0dp)\n" + - "\tdropTargetBarTopMarginPx: 84.0px (32.0dp)\n" + - "\tdropTargetBarSizePx: 147.0px (56.0dp)\n" + - "\tdropTargetBarBottomMarginPx: 42.0px (16.0dp)\n" + - "\tgetCellLayoutSpringLoadShrunkTop(): 391.0px (148.95238dp)\n" + - "\tgetCellLayoutSpringLoadShrunkBottom(): 1927.0px (734.0952dp)\n" + - "\tworkspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)\n" + - "\tgetWorkspaceSpringLoadScale(): 0.7781155px (0.29642496dp)\n" + - "\tgetCellLayoutHeight(): 1974.0px (752.0dp)\n" + - "\tgetCellLayoutWidth(): 1080.0px (411.42856dp)\n" - ) + assertDump(dp, "phonePortrait") } @Test @@ -310,136 +53,7 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { initializeVarsForPhone(deviceSpecs["phone"]!!, isVerticalBar = true, isGestureMode = false) val dp = getDeviceProfileForGrid("5_by_5") - assertThat(dump(dp)) - .isEqualTo( - "DeviceProfile:\n" + - "\t1 dp = 2.625 px\n" + - "\tisTablet:false\n" + - "\tisPhone:true\n" + - "\ttransposeLayoutWithOrientation:true\n" + - "\tisGestureMode:false\n" + - "\tisLandscape:true\n" + - "\tisMultiWindowMode:false\n" + - "\tisTwoPanels:false\n" + - "\twindowX: 0.0px (0.0dp)\n" + - "\twindowY: 0.0px (0.0dp)\n" + - "\twidthPx: 2400.0px (914.2857dp)\n" + - "\theightPx: 1080.0px (411.42856dp)\n" + - "\tavailableWidthPx: 2156.0px (821.3333dp)\n" + - "\tavailableHeightPx: 1006.0px (383.2381dp)\n" + - "\tmInsets.left: 118.0px (44.95238dp)\n" + - "\tmInsets.top: 74.0px (28.190475dp)\n" + - "\tmInsets.right: 126.0px (48.0dp)\n" + - "\tmInsets.bottom: 0.0px (0.0dp)\n" + - "\taspectRatio:2.2222223\n" + - "\tisResponsiveGrid:false\n" + - "\tisScalableGrid:false\n" + - "\tinv.numRows: 5\n" + - "\tinv.numColumns: 5\n" + - "\tinv.numSearchContainerColumns: 5\n" + - "\tminCellSize: PointF(0.0, 0.0)dp\n" + - "\tcellWidthPx: 152.0px (57.904762dp)\n" + - "\tcellHeightPx: 166.0px (63.238094dp)\n" + - "\tgetCellSize().x: 368.0px (140.19048dp)\n" + - "\tgetCellSize().y: 193.0px (73.52381dp)\n" + - "\tcellLayoutBorderSpacePx Horizontal: 0.0px (0.0dp)\n" + - "\tcellLayoutBorderSpacePx Vertical: 0.0px (0.0dp)\n" + - "\tcellLayoutPaddingPx.left: 53.0px (20.190475dp)\n" + - "\tcellLayoutPaddingPx.top: 0.0px (0.0dp)\n" + - "\tcellLayoutPaddingPx.right: 53.0px (20.190475dp)\n" + - "\tcellLayoutPaddingPx.bottom: 40.0px (15.238095dp)\n" + - "\ticonSizePx: 147.0px (56.0dp)\n" + - "\ticonTextSizePx: 0.0px (0.0dp)\n" + - "\ticonDrawablePaddingPx: 0.0px (0.0dp)\n" + - "\tinv.numFolderRows: 4\n" + - "\tinv.numFolderColumns: 4\n" + - "\tfolderCellWidthPx: 173.0px (65.90476dp)\n" + - "\tfolderCellHeightPx: 205.0px (78.09524dp)\n" + - "\tfolderChildIconSizePx: 131.0px (49.904762dp)\n" + - "\tfolderChildTextSizePx: 34.0px (12.952381dp)\n" + - "\tfolderChildDrawablePaddingPx: 4.0px (1.5238096dp)\n" + - "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" + - "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" + - "\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" + - "\tfolderTopPadding: 56.0px (21.333334dp)\n" + - "\tfolderFooterHeight: 131.0px (49.904762dp)\n" + - "\tbottomSheetTopPadding: 114.0px (43.42857dp)\n" + - "\tbottomSheetOpenDuration: 267\n" + - "\tbottomSheetCloseDuration: 267\n" + - "\tbottomSheetWorkspaceScale: 1.0\n" + - "\tbottomSheetDepth: 0.0\n" + - "\tallAppsShiftRange: 788.0px (300.1905dp)\n" + - "\tallAppsTopPadding: 0.0px (0.0dp)\n" + - "\tallAppsOpenDuration: 600\n" + - "\tallAppsCloseDuration: 300\n" + - "\tallAppsIconSizePx: 147.0px (56.0dp)\n" + - "\tallAppsIconTextSizePx: 38.0px (14.476191dp)\n" + - "\tallAppsIconDrawablePaddingPx: 21.0px (8.0dp)\n" + - "\tallAppsCellHeightPx: 321.0px (122.28571dp)\n" + - "\tallAppsCellWidthPx: 189.0px (72.0dp)\n" + - "\tallAppsBorderSpacePxX: 42.0px (16.0dp)\n" + - "\tallAppsBorderSpacePxY: 42.0px (16.0dp)\n" + - "\tnumShownAllAppsColumns: 5\n" + - "\tallAppsLeftRightPadding: 0.0px (0.0dp)\n" + - "\tallAppsLeftRightMargin: 0.0px (0.0dp)\n" + - "\thotseatBarSizePx: 252.0px (96.0dp)\n" + - "\tinv.hotseatColumnSpan: 5\n" + - "\thotseatCellHeightPx: 166.0px (63.238094dp)\n" + - "\thotseatBarBottomSpacePx: 126.0px (48.0dp)\n" + - "\thotseatBarSidePaddingStartPx: 63.0px (24.0dp)\n" + - "\thotseatBarSidePaddingEndPx: 42.0px (16.0dp)\n" + - "\thotseatBarEndOffset: 0.0px (0.0dp)\n" + - "\thotseatQsbSpace: 0.0px (0.0dp)\n" + - "\thotseatQsbHeight: 0.0px (0.0dp)\n" + - "\tspringLoadedHotseatBarTopMarginPx: 118.0px (44.95238dp)\n" + - "\tgetHotseatLayoutPadding(context).top: 64.0px (24.380953dp)\n" + - "\tgetHotseatLayoutPadding(context).bottom: 49.0px (18.666666dp)\n" + - "\tgetHotseatLayoutPadding(context).left: 42.0px (16.0dp)\n" + - "\tgetHotseatLayoutPadding(context).right: 189.0px (72.0dp)\n" + - "\tnumShownHotseatIcons: 5\n" + - "\thotseatBorderSpace: 0.0px (0.0dp)\n" + - "\tisQsbInline: false\n" + - "\thotseatQsbWidth: 0.0px (0.0dp)\n" + - "\tisTaskbarPresent:false\n" + - "\tisTaskbarPresentInApps:false\n" + - "\ttaskbarHeight: 0.0px (0.0dp)\n" + - "\tstashedTaskbarHeight: 0.0px (0.0dp)\n" + - "\ttaskbarBottomMargin: 0.0px (0.0dp)\n" + - "\ttaskbarIconSize: 0.0px (0.0dp)\n" + - "\tdesiredWorkspaceHorizontalMarginPx: 0.0px (0.0dp)\n" + - "\tworkspacePadding.left: 10.0px (3.8095238dp)\n" + - "\tworkspacePadding.top: 0.0px (0.0dp)\n" + - "\tworkspacePadding.right: 199.0px (75.809525dp)\n" + - "\tworkspacePadding.bottom: 0.0px (0.0dp)\n" + - "\ticonScale: 1.0px (0.3809524dp)\n" + - "\tcellScaleToFit : 1.0px (0.3809524dp)\n" + - "\textraSpace: 136.0px (51.809525dp)\n" + - "\tunscaled extraSpace: 136.0px (51.809525dp)\n" + - "\tmaxEmptySpace: 0.0px (0.0dp)\n" + - "\tworkspaceTopPadding: 0.0px (0.0dp)\n" + - "\tworkspaceBottomPadding: 0.0px (0.0dp)\n" + - "\toverviewTaskMarginPx: 0.0px (0.0dp)\n" + - "\toverviewTaskIconSizePx: 0.0px (0.0dp)\n" + - "\toverviewTaskIconDrawableSizePx: 0.0px (0.0dp)\n" + - "\toverviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)\n" + - "\toverviewTaskThumbnailTopMarginPx: 0.0px (0.0dp)\n" + - "\toverviewActionsTopMarginPx: 0.0px (0.0dp)\n" + - "\toverviewActionsHeight: 0.0px (0.0dp)\n" + - "\toverviewActionsClaimedSpaceBelow: 0.0px (0.0dp)\n" + - "\toverviewActionsButtonSpacing: 0.0px (0.0dp)\n" + - "\toverviewPageSpacing: 0.0px (0.0dp)\n" + - "\toverviewRowSpacing: 0.0px (0.0dp)\n" + - "\toverviewGridSideMargin: 0.0px (0.0dp)\n" + - "\tdropTargetBarTopMarginPx: 16.0px (6.095238dp)\n" + - "\tdropTargetBarSizePx: 95.0px (36.190475dp)\n" + - "\tdropTargetBarBottomMarginPx: 16.0px (6.095238dp)\n" + - "\tgetCellLayoutSpringLoadShrunkTop(): 201.0px (76.57143dp)\n" + - "\tgetCellLayoutSpringLoadShrunkBottom(): 1008.0px (384.0dp)\n" + - "\tworkspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)\n" + - "\tgetWorkspaceSpringLoadScale(): 0.8021869px (0.305595dp)\n" + - "\tgetCellLayoutHeight(): 1006.0px (383.2381dp)\n" + - "\tgetCellLayoutWidth(): 1947.0px (741.7143dp)\n" - ) + assertDump(dp, "phoneVerticalBar3Button") } @Test @@ -447,136 +61,7 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { initializeVarsForPhone(deviceSpecs["phone"]!!, isVerticalBar = true) val dp = getDeviceProfileForGrid("5_by_5") - assertThat(dump(dp)) - .isEqualTo( - "DeviceProfile:\n" + - "\t1 dp = 2.625 px\n" + - "\tisTablet:false\n" + - "\tisPhone:true\n" + - "\ttransposeLayoutWithOrientation:true\n" + - "\tisGestureMode:true\n" + - "\tisLandscape:true\n" + - "\tisMultiWindowMode:false\n" + - "\tisTwoPanels:false\n" + - "\twindowX: 0.0px (0.0dp)\n" + - "\twindowY: 0.0px (0.0dp)\n" + - "\twidthPx: 2400.0px (914.2857dp)\n" + - "\theightPx: 1080.0px (411.42856dp)\n" + - "\tavailableWidthPx: 2282.0px (869.3333dp)\n" + - "\tavailableHeightPx: 943.0px (359.2381dp)\n" + - "\tmInsets.left: 118.0px (44.95238dp)\n" + - "\tmInsets.top: 74.0px (28.190475dp)\n" + - "\tmInsets.right: 0.0px (0.0dp)\n" + - "\tmInsets.bottom: 63.0px (24.0dp)\n" + - "\taspectRatio:2.2222223\n" + - "\tisResponsiveGrid:false\n" + - "\tisScalableGrid:false\n" + - "\tinv.numRows: 5\n" + - "\tinv.numColumns: 5\n" + - "\tinv.numSearchContainerColumns: 5\n" + - "\tminCellSize: PointF(0.0, 0.0)dp\n" + - "\tcellWidthPx: 152.0px (57.904762dp)\n" + - "\tcellHeightPx: 166.0px (63.238094dp)\n" + - "\tgetCellSize().x: 393.0px (149.71428dp)\n" + - "\tgetCellSize().y: 180.0px (68.57143dp)\n" + - "\tcellLayoutBorderSpacePx Horizontal: 0.0px (0.0dp)\n" + - "\tcellLayoutBorderSpacePx Vertical: 0.0px (0.0dp)\n" + - "\tcellLayoutPaddingPx.left: 53.0px (20.190475dp)\n" + - "\tcellLayoutPaddingPx.top: 0.0px (0.0dp)\n" + - "\tcellLayoutPaddingPx.right: 53.0px (20.190475dp)\n" + - "\tcellLayoutPaddingPx.bottom: 40.0px (15.238095dp)\n" + - "\ticonSizePx: 147.0px (56.0dp)\n" + - "\ticonTextSizePx: 0.0px (0.0dp)\n" + - "\ticonDrawablePaddingPx: 0.0px (0.0dp)\n" + - "\tinv.numFolderRows: 4\n" + - "\tinv.numFolderColumns: 4\n" + - "\tfolderCellWidthPx: 163.0px (62.095238dp)\n" + - "\tfolderCellHeightPx: 192.0px (73.14286dp)\n" + - "\tfolderChildIconSizePx: 123.0px (46.857143dp)\n" + - "\tfolderChildTextSizePx: 32.0px (12.190476dp)\n" + - "\tfolderChildDrawablePaddingPx: 3.0px (1.1428572dp)\n" + - "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" + - "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" + - "\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" + - "\tfolderTopPadding: 53.0px (20.190475dp)\n" + - "\tfolderFooterHeight: 123.0px (46.857143dp)\n" + - "\tbottomSheetTopPadding: 114.0px (43.42857dp)\n" + - "\tbottomSheetOpenDuration: 267\n" + - "\tbottomSheetCloseDuration: 267\n" + - "\tbottomSheetWorkspaceScale: 1.0\n" + - "\tbottomSheetDepth: 0.0\n" + - "\tallAppsShiftRange: 788.0px (300.1905dp)\n" + - "\tallAppsTopPadding: 0.0px (0.0dp)\n" + - "\tallAppsOpenDuration: 600\n" + - "\tallAppsCloseDuration: 300\n" + - "\tallAppsIconSizePx: 147.0px (56.0dp)\n" + - "\tallAppsIconTextSizePx: 38.0px (14.476191dp)\n" + - "\tallAppsIconDrawablePaddingPx: 21.0px (8.0dp)\n" + - "\tallAppsCellHeightPx: 321.0px (122.28571dp)\n" + - "\tallAppsCellWidthPx: 189.0px (72.0dp)\n" + - "\tallAppsBorderSpacePxX: 42.0px (16.0dp)\n" + - "\tallAppsBorderSpacePxY: 42.0px (16.0dp)\n" + - "\tnumShownAllAppsColumns: 5\n" + - "\tallAppsLeftRightPadding: 0.0px (0.0dp)\n" + - "\tallAppsLeftRightMargin: 0.0px (0.0dp)\n" + - "\thotseatBarSizePx: 252.0px (96.0dp)\n" + - "\tinv.hotseatColumnSpan: 5\n" + - "\thotseatCellHeightPx: 166.0px (63.238094dp)\n" + - "\thotseatBarBottomSpacePx: 126.0px (48.0dp)\n" + - "\thotseatBarSidePaddingStartPx: 63.0px (24.0dp)\n" + - "\thotseatBarSidePaddingEndPx: 42.0px (16.0dp)\n" + - "\thotseatBarEndOffset: 0.0px (0.0dp)\n" + - "\thotseatQsbSpace: 0.0px (0.0dp)\n" + - "\thotseatQsbHeight: 0.0px (0.0dp)\n" + - "\tspringLoadedHotseatBarTopMarginPx: 118.0px (44.95238dp)\n" + - "\tgetHotseatLayoutPadding(context).top: 64.0px (24.380953dp)\n" + - "\tgetHotseatLayoutPadding(context).bottom: 112.0px (42.666668dp)\n" + - "\tgetHotseatLayoutPadding(context).left: 42.0px (16.0dp)\n" + - "\tgetHotseatLayoutPadding(context).right: 63.0px (24.0dp)\n" + - "\tnumShownHotseatIcons: 5\n" + - "\thotseatBorderSpace: 0.0px (0.0dp)\n" + - "\tisQsbInline: false\n" + - "\thotseatQsbWidth: 0.0px (0.0dp)\n" + - "\tisTaskbarPresent:false\n" + - "\tisTaskbarPresentInApps:false\n" + - "\ttaskbarHeight: 0.0px (0.0dp)\n" + - "\tstashedTaskbarHeight: 0.0px (0.0dp)\n" + - "\ttaskbarBottomMargin: 0.0px (0.0dp)\n" + - "\ttaskbarIconSize: 0.0px (0.0dp)\n" + - "\tdesiredWorkspaceHorizontalMarginPx: 0.0px (0.0dp)\n" + - "\tworkspacePadding.left: 10.0px (3.8095238dp)\n" + - "\tworkspacePadding.top: 0.0px (0.0dp)\n" + - "\tworkspacePadding.right: 199.0px (75.809525dp)\n" + - "\tworkspacePadding.bottom: 0.0px (0.0dp)\n" + - "\ticonScale: 1.0px (0.3809524dp)\n" + - "\tcellScaleToFit : 1.0px (0.3809524dp)\n" + - "\textraSpace: 73.0px (27.809525dp)\n" + - "\tunscaled extraSpace: 73.0px (27.809525dp)\n" + - "\tmaxEmptySpace: 0.0px (0.0dp)\n" + - "\tworkspaceTopPadding: 0.0px (0.0dp)\n" + - "\tworkspaceBottomPadding: 0.0px (0.0dp)\n" + - "\toverviewTaskMarginPx: 0.0px (0.0dp)\n" + - "\toverviewTaskIconSizePx: 0.0px (0.0dp)\n" + - "\toverviewTaskIconDrawableSizePx: 0.0px (0.0dp)\n" + - "\toverviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)\n" + - "\toverviewTaskThumbnailTopMarginPx: 0.0px (0.0dp)\n" + - "\toverviewActionsTopMarginPx: 0.0px (0.0dp)\n" + - "\toverviewActionsHeight: 0.0px (0.0dp)\n" + - "\toverviewActionsClaimedSpaceBelow: 63.0px (24.0dp)\n" + - "\toverviewActionsButtonSpacing: 0.0px (0.0dp)\n" + - "\toverviewPageSpacing: 0.0px (0.0dp)\n" + - "\toverviewRowSpacing: 0.0px (0.0dp)\n" + - "\toverviewGridSideMargin: 0.0px (0.0dp)\n" + - "\tdropTargetBarTopMarginPx: 16.0px (6.095238dp)\n" + - "\tdropTargetBarSizePx: 95.0px (36.190475dp)\n" + - "\tdropTargetBarBottomMarginPx: 16.0px (6.095238dp)\n" + - "\tgetCellLayoutSpringLoadShrunkTop(): 201.0px (76.57143dp)\n" + - "\tgetCellLayoutSpringLoadShrunkBottom(): 952.0px (362.66666dp)\n" + - "\tworkspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)\n" + - "\tgetWorkspaceSpringLoadScale(): 0.79639447px (0.30338836dp)\n" + - "\tgetCellLayoutHeight(): 943.0px (359.2381dp)\n" + - "\tgetCellLayoutWidth(): 2073.0px (789.7143dp)\n" - ) + assertDump(dp, "phoneVerticalBar") } @Test @@ -585,136 +70,7 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { val dp = getDeviceProfileForGrid("6_by_5") dp.isTaskbarPresentInApps = true - assertThat(dump(dp)) - .isEqualTo( - "DeviceProfile:\n" + - "\t1 dp = 2.0 px\n" + - "\tisTablet:true\n" + - "\tisPhone:false\n" + - "\ttransposeLayoutWithOrientation:false\n" + - "\tisGestureMode:false\n" + - "\tisLandscape:true\n" + - "\tisMultiWindowMode:false\n" + - "\tisTwoPanels:false\n" + - "\twindowX: 0.0px (0.0dp)\n" + - "\twindowY: 0.0px (0.0dp)\n" + - "\twidthPx: 2560.0px (1280.0dp)\n" + - "\theightPx: 1600.0px (800.0dp)\n" + - "\tavailableWidthPx: 2560.0px (1280.0dp)\n" + - "\tavailableHeightPx: 1496.0px (748.0dp)\n" + - "\tmInsets.left: 0.0px (0.0dp)\n" + - "\tmInsets.top: 104.0px (52.0dp)\n" + - "\tmInsets.right: 0.0px (0.0dp)\n" + - "\tmInsets.bottom: 0.0px (0.0dp)\n" + - "\taspectRatio:1.6\n" + - "\tisResponsiveGrid:false\n" + - "\tisScalableGrid:true\n" + - "\tinv.numRows: 5\n" + - "\tinv.numColumns: 6\n" + - "\tinv.numSearchContainerColumns: 3\n" + - "\tminCellSize: PointF(120.0, 104.0)dp\n" + - "\tcellWidthPx: 240.0px (120.0dp)\n" + - "\tcellHeightPx: 208.0px (104.0dp)\n" + - "\tgetCellSize().x: 240.0px (120.0dp)\n" + - "\tgetCellSize().y: 208.0px (104.0dp)\n" + - "\tcellLayoutBorderSpacePx Horizontal: 128.0px (64.0dp)\n" + - "\tcellLayoutBorderSpacePx Vertical: 32.0px (16.0dp)\n" + - "\tcellLayoutPaddingPx.left: 59.0px (29.5dp)\n" + - "\tcellLayoutPaddingPx.top: 25.0px (12.5dp)\n" + - "\tcellLayoutPaddingPx.right: 59.0px (29.5dp)\n" + - "\tcellLayoutPaddingPx.bottom: 59.0px (29.5dp)\n" + - "\ticonSizePx: 120.0px (60.0dp)\n" + - "\ticonTextSizePx: 28.0px (14.0dp)\n" + - "\ticonDrawablePaddingPx: 9.0px (4.5dp)\n" + - "\tinv.numFolderRows: 3\n" + - "\tinv.numFolderColumns: 3\n" + - "\tfolderCellWidthPx: 240.0px (120.0dp)\n" + - "\tfolderCellHeightPx: 208.0px (104.0dp)\n" + - "\tfolderChildIconSizePx: 120.0px (60.0dp)\n" + - "\tfolderChildTextSizePx: 28.0px (14.0dp)\n" + - "\tfolderChildDrawablePaddingPx: 11.0px (5.5dp)\n" + - "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" + - "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" + - "\tfolderContentPaddingLeftRight: 0.0px (0.0dp)\n" + - "\tfolderTopPadding: 48.0px (24.0dp)\n" + - "\tfolderFooterHeight: 112.0px (56.0dp)\n" + - "\tbottomSheetTopPadding: 104.0px (52.0dp)\n" + - "\tbottomSheetOpenDuration: 500\n" + - "\tbottomSheetCloseDuration: 500\n" + - "\tbottomSheetWorkspaceScale: 0.97\n" + - "\tbottomSheetDepth: 0.0\n" + - "\tallAppsShiftRange: 1496.0px (748.0dp)\n" + - "\tallAppsTopPadding: 104.0px (52.0dp)\n" + - "\tallAppsOpenDuration: 500\n" + - "\tallAppsCloseDuration: 500\n" + - "\tallAppsIconSizePx: 120.0px (60.0dp)\n" + - "\tallAppsIconTextSizePx: 28.0px (14.0dp)\n" + - "\tallAppsIconDrawablePaddingPx: 9.0px (4.5dp)\n" + - "\tallAppsCellHeightPx: 284.0px (142.0dp)\n" + - "\tallAppsCellWidthPx: 252.0px (126.0dp)\n" + - "\tallAppsBorderSpacePxX: 32.0px (16.0dp)\n" + - "\tallAppsBorderSpacePxY: 32.0px (16.0dp)\n" + - "\tnumShownAllAppsColumns: 6\n" + - "\tallAppsLeftRightPadding: 32.0px (16.0dp)\n" + - "\tallAppsLeftRightMargin: 412.0px (206.0dp)\n" + - "\thotseatBarSizePx: 200.0px (100.0dp)\n" + - "\tinv.hotseatColumnSpan: 4\n" + - "\thotseatCellHeightPx: 135.0px (67.5dp)\n" + - "\thotseatBarBottomSpacePx: 80.0px (40.0dp)\n" + - "\thotseatBarSidePaddingStartPx: 0.0px (0.0dp)\n" + - "\thotseatBarSidePaddingEndPx: 0.0px (0.0dp)\n" + - "\thotseatBarEndOffset: 0.0px (0.0dp)\n" + - "\thotseatQsbSpace: 0.0px (0.0dp)\n" + - "\thotseatQsbHeight: 0.0px (0.0dp)\n" + - "\tspringLoadedHotseatBarTopMarginPx: 128.0px (64.0dp)\n" + - "\tgetHotseatLayoutPadding(context).top: 0.0px (0.0dp)\n" + - "\tgetHotseatLayoutPadding(context).bottom: 65.0px (32.5dp)\n" + - "\tgetHotseatLayoutPadding(context).left: 668.0px (334.0dp)\n" + - "\tgetHotseatLayoutPadding(context).right: 668.0px (334.0dp)\n" + - "\tnumShownHotseatIcons: 6\n" + - "\thotseatBorderSpace: 100.0px (50.0dp)\n" + - "\tisQsbInline: false\n" + - "\thotseatQsbWidth: 1224.0px (612.0dp)\n" + - "\tisTaskbarPresent:false\n" + - "\tisTaskbarPresentInApps:true\n" + - "\ttaskbarHeight: 0.0px (0.0dp)\n" + - "\tstashedTaskbarHeight: 0.0px (0.0dp)\n" + - "\ttaskbarBottomMargin: 0.0px (0.0dp)\n" + - "\ttaskbarIconSize: 0.0px (0.0dp)\n" + - "\tdesiredWorkspaceHorizontalMarginPx: 240.0px (120.0dp)\n" + - "\tworkspacePadding.left: 181.0px (90.5dp)\n" + - "\tworkspacePadding.top: 0.0px (0.0dp)\n" + - "\tworkspacePadding.right: 181.0px (90.5dp)\n" + - "\tworkspacePadding.bottom: 244.0px (122.0dp)\n" + - "\ticonScale: 1.0px (0.5dp)\n" + - "\tcellScaleToFit : 1.0px (0.5dp)\n" + - "\textraSpace: 80.0px (40.0dp)\n" + - "\tunscaled extraSpace: 80.0px (40.0dp)\n" + - "\tmaxEmptySpace: 200.0px (100.0dp)\n" + - "\tworkspaceTopPadding: 25.0px (12.5dp)\n" + - "\tworkspaceBottomPadding: 55.0px (27.5dp)\n" + - "\toverviewTaskMarginPx: 0.0px (0.0dp)\n" + - "\toverviewTaskIconSizePx: 0.0px (0.0dp)\n" + - "\toverviewTaskIconDrawableSizePx: 0.0px (0.0dp)\n" + - "\toverviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)\n" + - "\toverviewTaskThumbnailTopMarginPx: 0.0px (0.0dp)\n" + - "\toverviewActionsTopMarginPx: 0.0px (0.0dp)\n" + - "\toverviewActionsHeight: 0.0px (0.0dp)\n" + - "\toverviewActionsClaimedSpaceBelow: 0.0px (0.0dp)\n" + - "\toverviewActionsButtonSpacing: 0.0px (0.0dp)\n" + - "\toverviewPageSpacing: 0.0px (0.0dp)\n" + - "\toverviewRowSpacing: 0.0px (0.0dp)\n" + - "\toverviewGridSideMargin: 0.0px (0.0dp)\n" + - "\tdropTargetBarTopMarginPx: 0.0px (0.0dp)\n" + - "\tdropTargetBarSizePx: 144.0px (72.0dp)\n" + - "\tdropTargetBarBottomMarginPx: 64.0px (32.0dp)\n" + - "\tgetCellLayoutSpringLoadShrunkTop(): 312.0px (156.0dp)\n" + - "\tgetCellLayoutSpringLoadShrunkBottom(): 1272.0px (636.0dp)\n" + - "\tworkspaceSpringLoadedMinNextPageVisiblePx: 48.0px (24.0dp)\n" + - "\tgetWorkspaceSpringLoadScale(): 0.76677316px (0.38338658dp)\n" + - "\tgetCellLayoutHeight(): 1252.0px (626.0dp)\n" + - "\tgetCellLayoutWidth(): 2198.0px (1099.0dp)\n" - ) + assertDump(dp, "tabletLandscape3Button") } @Test @@ -723,136 +79,7 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { val dp = getDeviceProfileForGrid("6_by_5") dp.isTaskbarPresentInApps = true - assertThat(dump(dp)) - .isEqualTo( - "DeviceProfile:\n" + - "\t1 dp = 2.0 px\n" + - "\tisTablet:true\n" + - "\tisPhone:false\n" + - "\ttransposeLayoutWithOrientation:false\n" + - "\tisGestureMode:true\n" + - "\tisLandscape:true\n" + - "\tisMultiWindowMode:false\n" + - "\tisTwoPanels:false\n" + - "\twindowX: 0.0px (0.0dp)\n" + - "\twindowY: 0.0px (0.0dp)\n" + - "\twidthPx: 2560.0px (1280.0dp)\n" + - "\theightPx: 1600.0px (800.0dp)\n" + - "\tavailableWidthPx: 2560.0px (1280.0dp)\n" + - "\tavailableHeightPx: 1496.0px (748.0dp)\n" + - "\tmInsets.left: 0.0px (0.0dp)\n" + - "\tmInsets.top: 104.0px (52.0dp)\n" + - "\tmInsets.right: 0.0px (0.0dp)\n" + - "\tmInsets.bottom: 0.0px (0.0dp)\n" + - "\taspectRatio:1.6\n" + - "\tisResponsiveGrid:false\n" + - "\tisScalableGrid:true\n" + - "\tinv.numRows: 5\n" + - "\tinv.numColumns: 6\n" + - "\tinv.numSearchContainerColumns: 3\n" + - "\tminCellSize: PointF(120.0, 104.0)dp\n" + - "\tcellWidthPx: 240.0px (120.0dp)\n" + - "\tcellHeightPx: 208.0px (104.0dp)\n" + - "\tgetCellSize().x: 240.0px (120.0dp)\n" + - "\tgetCellSize().y: 208.0px (104.0dp)\n" + - "\tcellLayoutBorderSpacePx Horizontal: 128.0px (64.0dp)\n" + - "\tcellLayoutBorderSpacePx Vertical: 32.0px (16.0dp)\n" + - "\tcellLayoutPaddingPx.left: 59.0px (29.5dp)\n" + - "\tcellLayoutPaddingPx.top: 25.0px (12.5dp)\n" + - "\tcellLayoutPaddingPx.right: 59.0px (29.5dp)\n" + - "\tcellLayoutPaddingPx.bottom: 59.0px (29.5dp)\n" + - "\ticonSizePx: 120.0px (60.0dp)\n" + - "\ticonTextSizePx: 28.0px (14.0dp)\n" + - "\ticonDrawablePaddingPx: 9.0px (4.5dp)\n" + - "\tinv.numFolderRows: 3\n" + - "\tinv.numFolderColumns: 3\n" + - "\tfolderCellWidthPx: 240.0px (120.0dp)\n" + - "\tfolderCellHeightPx: 208.0px (104.0dp)\n" + - "\tfolderChildIconSizePx: 120.0px (60.0dp)\n" + - "\tfolderChildTextSizePx: 28.0px (14.0dp)\n" + - "\tfolderChildDrawablePaddingPx: 11.0px (5.5dp)\n" + - "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" + - "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" + - "\tfolderContentPaddingLeftRight: 0.0px (0.0dp)\n" + - "\tfolderTopPadding: 48.0px (24.0dp)\n" + - "\tfolderFooterHeight: 112.0px (56.0dp)\n" + - "\tbottomSheetTopPadding: 104.0px (52.0dp)\n" + - "\tbottomSheetOpenDuration: 500\n" + - "\tbottomSheetCloseDuration: 500\n" + - "\tbottomSheetWorkspaceScale: 0.97\n" + - "\tbottomSheetDepth: 0.0\n" + - "\tallAppsShiftRange: 1496.0px (748.0dp)\n" + - "\tallAppsTopPadding: 104.0px (52.0dp)\n" + - "\tallAppsOpenDuration: 500\n" + - "\tallAppsCloseDuration: 500\n" + - "\tallAppsIconSizePx: 120.0px (60.0dp)\n" + - "\tallAppsIconTextSizePx: 28.0px (14.0dp)\n" + - "\tallAppsIconDrawablePaddingPx: 9.0px (4.5dp)\n" + - "\tallAppsCellHeightPx: 284.0px (142.0dp)\n" + - "\tallAppsCellWidthPx: 252.0px (126.0dp)\n" + - "\tallAppsBorderSpacePxX: 32.0px (16.0dp)\n" + - "\tallAppsBorderSpacePxY: 32.0px (16.0dp)\n" + - "\tnumShownAllAppsColumns: 6\n" + - "\tallAppsLeftRightPadding: 32.0px (16.0dp)\n" + - "\tallAppsLeftRightMargin: 412.0px (206.0dp)\n" + - "\thotseatBarSizePx: 200.0px (100.0dp)\n" + - "\tinv.hotseatColumnSpan: 4\n" + - "\thotseatCellHeightPx: 135.0px (67.5dp)\n" + - "\thotseatBarBottomSpacePx: 80.0px (40.0dp)\n" + - "\thotseatBarSidePaddingStartPx: 0.0px (0.0dp)\n" + - "\thotseatBarSidePaddingEndPx: 0.0px (0.0dp)\n" + - "\thotseatBarEndOffset: 0.0px (0.0dp)\n" + - "\thotseatQsbSpace: 0.0px (0.0dp)\n" + - "\thotseatQsbHeight: 0.0px (0.0dp)\n" + - "\tspringLoadedHotseatBarTopMarginPx: 128.0px (64.0dp)\n" + - "\tgetHotseatLayoutPadding(context).top: 0.0px (0.0dp)\n" + - "\tgetHotseatLayoutPadding(context).bottom: 65.0px (32.5dp)\n" + - "\tgetHotseatLayoutPadding(context).left: 668.0px (334.0dp)\n" + - "\tgetHotseatLayoutPadding(context).right: 668.0px (334.0dp)\n" + - "\tnumShownHotseatIcons: 6\n" + - "\thotseatBorderSpace: 100.0px (50.0dp)\n" + - "\tisQsbInline: false\n" + - "\thotseatQsbWidth: 1224.0px (612.0dp)\n" + - "\tisTaskbarPresent:false\n" + - "\tisTaskbarPresentInApps:true\n" + - "\ttaskbarHeight: 0.0px (0.0dp)\n" + - "\tstashedTaskbarHeight: 0.0px (0.0dp)\n" + - "\ttaskbarBottomMargin: 0.0px (0.0dp)\n" + - "\ttaskbarIconSize: 0.0px (0.0dp)\n" + - "\tdesiredWorkspaceHorizontalMarginPx: 240.0px (120.0dp)\n" + - "\tworkspacePadding.left: 181.0px (90.5dp)\n" + - "\tworkspacePadding.top: 0.0px (0.0dp)\n" + - "\tworkspacePadding.right: 181.0px (90.5dp)\n" + - "\tworkspacePadding.bottom: 244.0px (122.0dp)\n" + - "\ticonScale: 1.0px (0.5dp)\n" + - "\tcellScaleToFit : 1.0px (0.5dp)\n" + - "\textraSpace: 80.0px (40.0dp)\n" + - "\tunscaled extraSpace: 80.0px (40.0dp)\n" + - "\tmaxEmptySpace: 200.0px (100.0dp)\n" + - "\tworkspaceTopPadding: 25.0px (12.5dp)\n" + - "\tworkspaceBottomPadding: 55.0px (27.5dp)\n" + - "\toverviewTaskMarginPx: 0.0px (0.0dp)\n" + - "\toverviewTaskIconSizePx: 0.0px (0.0dp)\n" + - "\toverviewTaskIconDrawableSizePx: 0.0px (0.0dp)\n" + - "\toverviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)\n" + - "\toverviewTaskThumbnailTopMarginPx: 0.0px (0.0dp)\n" + - "\toverviewActionsTopMarginPx: 0.0px (0.0dp)\n" + - "\toverviewActionsHeight: 0.0px (0.0dp)\n" + - "\toverviewActionsClaimedSpaceBelow: 0.0px (0.0dp)\n" + - "\toverviewActionsButtonSpacing: 0.0px (0.0dp)\n" + - "\toverviewPageSpacing: 0.0px (0.0dp)\n" + - "\toverviewRowSpacing: 0.0px (0.0dp)\n" + - "\toverviewGridSideMargin: 0.0px (0.0dp)\n" + - "\tdropTargetBarTopMarginPx: 0.0px (0.0dp)\n" + - "\tdropTargetBarSizePx: 144.0px (72.0dp)\n" + - "\tdropTargetBarBottomMarginPx: 64.0px (32.0dp)\n" + - "\tgetCellLayoutSpringLoadShrunkTop(): 312.0px (156.0dp)\n" + - "\tgetCellLayoutSpringLoadShrunkBottom(): 1272.0px (636.0dp)\n" + - "\tworkspaceSpringLoadedMinNextPageVisiblePx: 48.0px (24.0dp)\n" + - "\tgetWorkspaceSpringLoadScale(): 0.76677316px (0.38338658dp)\n" + - "\tgetCellLayoutHeight(): 1252.0px (626.0dp)\n" + - "\tgetCellLayoutWidth(): 2198.0px (1099.0dp)\n" - ) + assertDump(dp, "tabletLandscape") } @Test @@ -861,136 +88,7 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { val dp = getDeviceProfileForGrid("6_by_5") dp.isTaskbarPresentInApps = true - assertThat(dump(dp)) - .isEqualTo( - "DeviceProfile:\n" + - "\t1 dp = 2.0 px\n" + - "\tisTablet:true\n" + - "\tisPhone:false\n" + - "\ttransposeLayoutWithOrientation:false\n" + - "\tisGestureMode:false\n" + - "\tisLandscape:false\n" + - "\tisMultiWindowMode:false\n" + - "\tisTwoPanels:false\n" + - "\twindowX: 0.0px (0.0dp)\n" + - "\twindowY: 0.0px (0.0dp)\n" + - "\twidthPx: 1600.0px (800.0dp)\n" + - "\theightPx: 2560.0px (1280.0dp)\n" + - "\tavailableWidthPx: 1600.0px (800.0dp)\n" + - "\tavailableHeightPx: 2456.0px (1228.0dp)\n" + - "\tmInsets.left: 0.0px (0.0dp)\n" + - "\tmInsets.top: 104.0px (52.0dp)\n" + - "\tmInsets.right: 0.0px (0.0dp)\n" + - "\tmInsets.bottom: 0.0px (0.0dp)\n" + - "\taspectRatio:1.6\n" + - "\tisResponsiveGrid:false\n" + - "\tisScalableGrid:true\n" + - "\tinv.numRows: 5\n" + - "\tinv.numColumns: 6\n" + - "\tinv.numSearchContainerColumns: 3\n" + - "\tminCellSize: PointF(102.0, 120.0)dp\n" + - "\tcellWidthPx: 204.0px (102.0dp)\n" + - "\tcellHeightPx: 240.0px (120.0dp)\n" + - "\tgetCellSize().x: 204.0px (102.0dp)\n" + - "\tgetCellSize().y: 240.0px (120.0dp)\n" + - "\tcellLayoutBorderSpacePx Horizontal: 32.0px (16.0dp)\n" + - "\tcellLayoutBorderSpacePx Vertical: 128.0px (64.0dp)\n" + - "\tcellLayoutPaddingPx.left: 72.0px (36.0dp)\n" + - "\tcellLayoutPaddingPx.top: 72.0px (36.0dp)\n" + - "\tcellLayoutPaddingPx.right: 72.0px (36.0dp)\n" + - "\tcellLayoutPaddingPx.bottom: 72.0px (36.0dp)\n" + - "\ticonSizePx: 120.0px (60.0dp)\n" + - "\ticonTextSizePx: 28.0px (14.0dp)\n" + - "\ticonDrawablePaddingPx: 9.0px (4.5dp)\n" + - "\tinv.numFolderRows: 3\n" + - "\tinv.numFolderColumns: 3\n" + - "\tfolderCellWidthPx: 204.0px (102.0dp)\n" + - "\tfolderCellHeightPx: 240.0px (120.0dp)\n" + - "\tfolderChildIconSizePx: 120.0px (60.0dp)\n" + - "\tfolderChildTextSizePx: 28.0px (14.0dp)\n" + - "\tfolderChildDrawablePaddingPx: 22.0px (11.0dp)\n" + - "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" + - "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" + - "\tfolderContentPaddingLeftRight: 0.0px (0.0dp)\n" + - "\tfolderTopPadding: 48.0px (24.0dp)\n" + - "\tfolderFooterHeight: 112.0px (56.0dp)\n" + - "\tbottomSheetTopPadding: 704.0px (352.0dp)\n" + - "\tbottomSheetOpenDuration: 500\n" + - "\tbottomSheetCloseDuration: 500\n" + - "\tbottomSheetWorkspaceScale: 0.97\n" + - "\tbottomSheetDepth: 0.0\n" + - "\tallAppsShiftRange: 1810.0px (905.0dp)\n" + - "\tallAppsTopPadding: 750.0px (375.0dp)\n" + - "\tallAppsOpenDuration: 500\n" + - "\tallAppsCloseDuration: 500\n" + - "\tallAppsIconSizePx: 120.0px (60.0dp)\n" + - "\tallAppsIconTextSizePx: 28.0px (14.0dp)\n" + - "\tallAppsIconDrawablePaddingPx: 9.0px (4.5dp)\n" + - "\tallAppsCellHeightPx: 316.0px (158.0dp)\n" + - "\tallAppsCellWidthPx: 192.0px (96.0dp)\n" + - "\tallAppsBorderSpacePxX: 16.0px (8.0dp)\n" + - "\tallAppsBorderSpacePxY: 32.0px (16.0dp)\n" + - "\tnumShownAllAppsColumns: 6\n" + - "\tallAppsLeftRightPadding: 32.0px (16.0dp)\n" + - "\tallAppsLeftRightMargin: 152.0px (76.0dp)\n" + - "\thotseatBarSizePx: 272.0px (136.0dp)\n" + - "\tinv.hotseatColumnSpan: 6\n" + - "\thotseatCellHeightPx: 135.0px (67.5dp)\n" + - "\thotseatBarBottomSpacePx: 152.0px (76.0dp)\n" + - "\thotseatBarSidePaddingStartPx: 0.0px (0.0dp)\n" + - "\thotseatBarSidePaddingEndPx: 0.0px (0.0dp)\n" + - "\thotseatBarEndOffset: 0.0px (0.0dp)\n" + - "\thotseatQsbSpace: 0.0px (0.0dp)\n" + - "\thotseatQsbHeight: 0.0px (0.0dp)\n" + - "\tspringLoadedHotseatBarTopMarginPx: 216.0px (108.0dp)\n" + - "\tgetHotseatLayoutPadding(context).top: 0.0px (0.0dp)\n" + - "\tgetHotseatLayoutPadding(context).bottom: 137.0px (68.5dp)\n" + - "\tgetHotseatLayoutPadding(context).left: 150.0px (75.0dp)\n" + - "\tgetHotseatLayoutPadding(context).right: 150.0px (75.0dp)\n" + - "\tnumShownHotseatIcons: 6\n" + - "\thotseatBorderSpace: 116.0px (58.0dp)\n" + - "\tisQsbInline: false\n" + - "\thotseatQsbWidth: 1300.0px (650.0dp)\n" + - "\tisTaskbarPresent:false\n" + - "\tisTaskbarPresentInApps:true\n" + - "\ttaskbarHeight: 0.0px (0.0dp)\n" + - "\tstashedTaskbarHeight: 0.0px (0.0dp)\n" + - "\ttaskbarBottomMargin: 0.0px (0.0dp)\n" + - "\ttaskbarIconSize: 0.0px (0.0dp)\n" + - "\tdesiredWorkspaceHorizontalMarginPx: 108.0px (54.0dp)\n" + - "\tworkspacePadding.left: 36.0px (18.0dp)\n" + - "\tworkspacePadding.top: 132.0px (66.0dp)\n" + - "\tworkspacePadding.right: 36.0px (18.0dp)\n" + - "\tworkspacePadding.bottom: 468.0px (234.0dp)\n" + - "\ticonScale: 1.0px (0.5dp)\n" + - "\tcellScaleToFit : 1.0px (0.5dp)\n" + - "\textraSpace: 424.0px (212.0dp)\n" + - "\tunscaled extraSpace: 424.0px (212.0dp)\n" + - "\tmaxEmptySpace: 19998.0px (9999.0dp)\n" + - "\tworkspaceTopPadding: 204.0px (102.0dp)\n" + - "\tworkspaceBottomPadding: 220.0px (110.0dp)\n" + - "\toverviewTaskMarginPx: 0.0px (0.0dp)\n" + - "\toverviewTaskIconSizePx: 0.0px (0.0dp)\n" + - "\toverviewTaskIconDrawableSizePx: 0.0px (0.0dp)\n" + - "\toverviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)\n" + - "\toverviewTaskThumbnailTopMarginPx: 0.0px (0.0dp)\n" + - "\toverviewActionsTopMarginPx: 0.0px (0.0dp)\n" + - "\toverviewActionsHeight: 0.0px (0.0dp)\n" + - "\toverviewActionsClaimedSpaceBelow: 0.0px (0.0dp)\n" + - "\toverviewActionsButtonSpacing: 0.0px (0.0dp)\n" + - "\toverviewPageSpacing: 0.0px (0.0dp)\n" + - "\toverviewRowSpacing: 0.0px (0.0dp)\n" + - "\toverviewGridSideMargin: 0.0px (0.0dp)\n" + - "\tdropTargetBarTopMarginPx: 220.0px (110.0dp)\n" + - "\tdropTargetBarSizePx: 144.0px (72.0dp)\n" + - "\tdropTargetBarBottomMarginPx: 96.0px (48.0dp)\n" + - "\tgetCellLayoutSpringLoadShrunkTop(): 564.0px (282.0dp)\n" + - "\tgetCellLayoutSpringLoadShrunkBottom(): 2072.0px (1036.0dp)\n" + - "\tworkspaceSpringLoadedMinNextPageVisiblePx: 48.0px (24.0dp)\n" + - "\tgetWorkspaceSpringLoadScale(): 0.8125px (0.40625dp)\n" + - "\tgetCellLayoutHeight(): 1856.0px (928.0dp)\n" + - "\tgetCellLayoutWidth(): 1528.0px (764.0dp)\n" - ) + assertDump(dp, "tabletPortrait3Button") } @Test @@ -999,136 +97,7 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { val dp = getDeviceProfileForGrid("6_by_5") dp.isTaskbarPresentInApps = true - assertThat(dump(dp)) - .isEqualTo( - "DeviceProfile:\n" + - "\t1 dp = 2.0 px\n" + - "\tisTablet:true\n" + - "\tisPhone:false\n" + - "\ttransposeLayoutWithOrientation:false\n" + - "\tisGestureMode:true\n" + - "\tisLandscape:false\n" + - "\tisMultiWindowMode:false\n" + - "\tisTwoPanels:false\n" + - "\twindowX: 0.0px (0.0dp)\n" + - "\twindowY: 0.0px (0.0dp)\n" + - "\twidthPx: 1600.0px (800.0dp)\n" + - "\theightPx: 2560.0px (1280.0dp)\n" + - "\tavailableWidthPx: 1600.0px (800.0dp)\n" + - "\tavailableHeightPx: 2456.0px (1228.0dp)\n" + - "\tmInsets.left: 0.0px (0.0dp)\n" + - "\tmInsets.top: 104.0px (52.0dp)\n" + - "\tmInsets.right: 0.0px (0.0dp)\n" + - "\tmInsets.bottom: 0.0px (0.0dp)\n" + - "\taspectRatio:1.6\n" + - "\tisResponsiveGrid:false\n" + - "\tisScalableGrid:true\n" + - "\tinv.numRows: 5\n" + - "\tinv.numColumns: 6\n" + - "\tinv.numSearchContainerColumns: 3\n" + - "\tminCellSize: PointF(102.0, 120.0)dp\n" + - "\tcellWidthPx: 204.0px (102.0dp)\n" + - "\tcellHeightPx: 240.0px (120.0dp)\n" + - "\tgetCellSize().x: 204.0px (102.0dp)\n" + - "\tgetCellSize().y: 240.0px (120.0dp)\n" + - "\tcellLayoutBorderSpacePx Horizontal: 32.0px (16.0dp)\n" + - "\tcellLayoutBorderSpacePx Vertical: 128.0px (64.0dp)\n" + - "\tcellLayoutPaddingPx.left: 72.0px (36.0dp)\n" + - "\tcellLayoutPaddingPx.top: 72.0px (36.0dp)\n" + - "\tcellLayoutPaddingPx.right: 72.0px (36.0dp)\n" + - "\tcellLayoutPaddingPx.bottom: 72.0px (36.0dp)\n" + - "\ticonSizePx: 120.0px (60.0dp)\n" + - "\ticonTextSizePx: 28.0px (14.0dp)\n" + - "\ticonDrawablePaddingPx: 9.0px (4.5dp)\n" + - "\tinv.numFolderRows: 3\n" + - "\tinv.numFolderColumns: 3\n" + - "\tfolderCellWidthPx: 204.0px (102.0dp)\n" + - "\tfolderCellHeightPx: 240.0px (120.0dp)\n" + - "\tfolderChildIconSizePx: 120.0px (60.0dp)\n" + - "\tfolderChildTextSizePx: 28.0px (14.0dp)\n" + - "\tfolderChildDrawablePaddingPx: 22.0px (11.0dp)\n" + - "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" + - "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" + - "\tfolderContentPaddingLeftRight: 0.0px (0.0dp)\n" + - "\tfolderTopPadding: 48.0px (24.0dp)\n" + - "\tfolderFooterHeight: 112.0px (56.0dp)\n" + - "\tbottomSheetTopPadding: 704.0px (352.0dp)\n" + - "\tbottomSheetOpenDuration: 500\n" + - "\tbottomSheetCloseDuration: 500\n" + - "\tbottomSheetWorkspaceScale: 0.97\n" + - "\tbottomSheetDepth: 0.0\n" + - "\tallAppsShiftRange: 1810.0px (905.0dp)\n" + - "\tallAppsTopPadding: 750.0px (375.0dp)\n" + - "\tallAppsOpenDuration: 500\n" + - "\tallAppsCloseDuration: 500\n" + - "\tallAppsIconSizePx: 120.0px (60.0dp)\n" + - "\tallAppsIconTextSizePx: 28.0px (14.0dp)\n" + - "\tallAppsIconDrawablePaddingPx: 9.0px (4.5dp)\n" + - "\tallAppsCellHeightPx: 316.0px (158.0dp)\n" + - "\tallAppsCellWidthPx: 192.0px (96.0dp)\n" + - "\tallAppsBorderSpacePxX: 16.0px (8.0dp)\n" + - "\tallAppsBorderSpacePxY: 32.0px (16.0dp)\n" + - "\tnumShownAllAppsColumns: 6\n" + - "\tallAppsLeftRightPadding: 32.0px (16.0dp)\n" + - "\tallAppsLeftRightMargin: 152.0px (76.0dp)\n" + - "\thotseatBarSizePx: 272.0px (136.0dp)\n" + - "\tinv.hotseatColumnSpan: 6\n" + - "\thotseatCellHeightPx: 135.0px (67.5dp)\n" + - "\thotseatBarBottomSpacePx: 152.0px (76.0dp)\n" + - "\thotseatBarSidePaddingStartPx: 0.0px (0.0dp)\n" + - "\thotseatBarSidePaddingEndPx: 0.0px (0.0dp)\n" + - "\thotseatBarEndOffset: 0.0px (0.0dp)\n" + - "\thotseatQsbSpace: 0.0px (0.0dp)\n" + - "\thotseatQsbHeight: 0.0px (0.0dp)\n" + - "\tspringLoadedHotseatBarTopMarginPx: 216.0px (108.0dp)\n" + - "\tgetHotseatLayoutPadding(context).top: 0.0px (0.0dp)\n" + - "\tgetHotseatLayoutPadding(context).bottom: 137.0px (68.5dp)\n" + - "\tgetHotseatLayoutPadding(context).left: 150.0px (75.0dp)\n" + - "\tgetHotseatLayoutPadding(context).right: 150.0px (75.0dp)\n" + - "\tnumShownHotseatIcons: 6\n" + - "\thotseatBorderSpace: 116.0px (58.0dp)\n" + - "\tisQsbInline: false\n" + - "\thotseatQsbWidth: 1300.0px (650.0dp)\n" + - "\tisTaskbarPresent:false\n" + - "\tisTaskbarPresentInApps:true\n" + - "\ttaskbarHeight: 0.0px (0.0dp)\n" + - "\tstashedTaskbarHeight: 0.0px (0.0dp)\n" + - "\ttaskbarBottomMargin: 0.0px (0.0dp)\n" + - "\ttaskbarIconSize: 0.0px (0.0dp)\n" + - "\tdesiredWorkspaceHorizontalMarginPx: 108.0px (54.0dp)\n" + - "\tworkspacePadding.left: 36.0px (18.0dp)\n" + - "\tworkspacePadding.top: 132.0px (66.0dp)\n" + - "\tworkspacePadding.right: 36.0px (18.0dp)\n" + - "\tworkspacePadding.bottom: 468.0px (234.0dp)\n" + - "\ticonScale: 1.0px (0.5dp)\n" + - "\tcellScaleToFit : 1.0px (0.5dp)\n" + - "\textraSpace: 424.0px (212.0dp)\n" + - "\tunscaled extraSpace: 424.0px (212.0dp)\n" + - "\tmaxEmptySpace: 19998.0px (9999.0dp)\n" + - "\tworkspaceTopPadding: 204.0px (102.0dp)\n" + - "\tworkspaceBottomPadding: 220.0px (110.0dp)\n" + - "\toverviewTaskMarginPx: 0.0px (0.0dp)\n" + - "\toverviewTaskIconSizePx: 0.0px (0.0dp)\n" + - "\toverviewTaskIconDrawableSizePx: 0.0px (0.0dp)\n" + - "\toverviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)\n" + - "\toverviewTaskThumbnailTopMarginPx: 0.0px (0.0dp)\n" + - "\toverviewActionsTopMarginPx: 0.0px (0.0dp)\n" + - "\toverviewActionsHeight: 0.0px (0.0dp)\n" + - "\toverviewActionsClaimedSpaceBelow: 0.0px (0.0dp)\n" + - "\toverviewActionsButtonSpacing: 0.0px (0.0dp)\n" + - "\toverviewPageSpacing: 0.0px (0.0dp)\n" + - "\toverviewRowSpacing: 0.0px (0.0dp)\n" + - "\toverviewGridSideMargin: 0.0px (0.0dp)\n" + - "\tdropTargetBarTopMarginPx: 220.0px (110.0dp)\n" + - "\tdropTargetBarSizePx: 144.0px (72.0dp)\n" + - "\tdropTargetBarBottomMarginPx: 96.0px (48.0dp)\n" + - "\tgetCellLayoutSpringLoadShrunkTop(): 564.0px (282.0dp)\n" + - "\tgetCellLayoutSpringLoadShrunkBottom(): 2072.0px (1036.0dp)\n" + - "\tworkspaceSpringLoadedMinNextPageVisiblePx: 48.0px (24.0dp)\n" + - "\tgetWorkspaceSpringLoadScale(): 0.8125px (0.40625dp)\n" + - "\tgetCellLayoutHeight(): 1856.0px (928.0dp)\n" + - "\tgetCellLayoutWidth(): 1528.0px (764.0dp)\n" - ) + assertDump(dp, "tabletPortrait") } @Test @@ -1142,136 +111,7 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { val dp = getDeviceProfileForGrid("4_by_4") dp.isTaskbarPresentInApps = true - assertThat(dump(dp)) - .isEqualTo( - "DeviceProfile:\n" + - "\t1 dp = 2.625 px\n" + - "\tisTablet:true\n" + - "\tisPhone:false\n" + - "\ttransposeLayoutWithOrientation:false\n" + - "\tisGestureMode:false\n" + - "\tisLandscape:true\n" + - "\tisMultiWindowMode:false\n" + - "\tisTwoPanels:true\n" + - "\twindowX: 0.0px (0.0dp)\n" + - "\twindowY: 0.0px (0.0dp)\n" + - "\twidthPx: 2208.0px (841.1429dp)\n" + - "\theightPx: 1840.0px (700.9524dp)\n" + - "\tavailableWidthPx: 2208.0px (841.1429dp)\n" + - "\tavailableHeightPx: 1730.0px (659.0476dp)\n" + - "\tmInsets.left: 0.0px (0.0dp)\n" + - "\tmInsets.top: 110.0px (41.904762dp)\n" + - "\tmInsets.right: 0.0px (0.0dp)\n" + - "\tmInsets.bottom: 0.0px (0.0dp)\n" + - "\taspectRatio:1.2\n" + - "\tisResponsiveGrid:false\n" + - "\tisScalableGrid:false\n" + - "\tinv.numRows: 4\n" + - "\tinv.numColumns: 4\n" + - "\tinv.numSearchContainerColumns: 4\n" + - "\tminCellSize: PointF(0.0, 0.0)dp\n" + - "\tcellWidthPx: 154.0px (58.666668dp)\n" + - "\tcellHeightPx: 218.0px (83.04762dp)\n" + - "\tgetCellSize().x: 270.0px (102.85714dp)\n" + - "\tgetCellSize().y: 342.0px (130.28572dp)\n" + - "\tcellLayoutBorderSpacePx Horizontal: 0.0px (0.0dp)\n" + - "\tcellLayoutBorderSpacePx Vertical: 0.0px (0.0dp)\n" + - "\tcellLayoutPaddingPx.left: 0.0px (0.0dp)\n" + - "\tcellLayoutPaddingPx.top: 0.0px (0.0dp)\n" + - "\tcellLayoutPaddingPx.right: 0.0px (0.0dp)\n" + - "\tcellLayoutPaddingPx.bottom: 0.0px (0.0dp)\n" + - "\ticonSizePx: 141.0px (53.714287dp)\n" + - "\ticonTextSizePx: 34.0px (12.952381dp)\n" + - "\ticonDrawablePaddingPx: 13.0px (4.952381dp)\n" + - "\tinv.numFolderRows: 3\n" + - "\tinv.numFolderColumns: 4\n" + - "\tfolderCellWidthPx: 189.0px (72.0dp)\n" + - "\tfolderCellHeightPx: 219.0px (83.42857dp)\n" + - "\tfolderChildIconSizePx: 141.0px (53.714287dp)\n" + - "\tfolderChildTextSizePx: 34.0px (12.952381dp)\n" + - "\tfolderChildDrawablePaddingPx: 5.0px (1.9047619dp)\n" + - "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" + - "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" + - "\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" + - "\tfolderTopPadding: 63.0px (24.0dp)\n" + - "\tfolderFooterHeight: 147.0px (56.0dp)\n" + - "\tbottomSheetTopPadding: 110.0px (41.904762dp)\n" + - "\tbottomSheetOpenDuration: 500\n" + - "\tbottomSheetCloseDuration: 500\n" + - "\tbottomSheetWorkspaceScale: 0.97\n" + - "\tbottomSheetDepth: 1.0\n" + - "\tallAppsShiftRange: 1730.0px (659.0476dp)\n" + - "\tallAppsTopPadding: 110.0px (41.904762dp)\n" + - "\tallAppsOpenDuration: 500\n" + - "\tallAppsCloseDuration: 500\n" + - "\tallAppsIconSizePx: 141.0px (53.714287dp)\n" + - "\tallAppsIconTextSizePx: 34.0px (12.952381dp)\n" + - "\tallAppsIconDrawablePaddingPx: 21.0px (8.0dp)\n" + - "\tallAppsCellHeightPx: 315.0px (120.0dp)\n" + - "\tallAppsCellWidthPx: 183.0px (69.71429dp)\n" + - "\tallAppsBorderSpacePxX: 42.0px (16.0dp)\n" + - "\tallAppsBorderSpacePxY: 42.0px (16.0dp)\n" + - "\tnumShownAllAppsColumns: 8\n" + - "\tallAppsLeftRightPadding: 42.0px (16.0dp)\n" + - "\tallAppsLeftRightMargin: 183.0px (69.71429dp)\n" + - "\thotseatBarSizePx: 267.0px (101.71429dp)\n" + - "\tinv.hotseatColumnSpan: 4\n" + - "\thotseatCellHeightPx: 159.0px (60.57143dp)\n" + - "\thotseatBarBottomSpacePx: 126.0px (48.0dp)\n" + - "\thotseatBarSidePaddingStartPx: 0.0px (0.0dp)\n" + - "\thotseatBarSidePaddingEndPx: 0.0px (0.0dp)\n" + - "\thotseatBarEndOffset: 0.0px (0.0dp)\n" + - "\thotseatQsbSpace: 0.0px (0.0dp)\n" + - "\thotseatQsbHeight: 0.0px (0.0dp)\n" + - "\tspringLoadedHotseatBarTopMarginPx: 116.0px (44.190475dp)\n" + - "\tgetHotseatLayoutPadding(context).top: 0.0px (0.0dp)\n" + - "\tgetHotseatLayoutPadding(context).bottom: 108.0px (41.142857dp)\n" + - "\tgetHotseatLayoutPadding(context).left: 113.0px (43.04762dp)\n" + - "\tgetHotseatLayoutPadding(context).right: 113.0px (43.04762dp)\n" + - "\tnumShownHotseatIcons: 6\n" + - "\thotseatBorderSpace: 0.0px (0.0dp)\n" + - "\tisQsbInline: false\n" + - "\thotseatQsbWidth: 0.0px (0.0dp)\n" + - "\tisTaskbarPresent:false\n" + - "\tisTaskbarPresentInApps:true\n" + - "\ttaskbarHeight: 0.0px (0.0dp)\n" + - "\tstashedTaskbarHeight: 0.0px (0.0dp)\n" + - "\ttaskbarBottomMargin: 0.0px (0.0dp)\n" + - "\ttaskbarIconSize: 0.0px (0.0dp)\n" + - "\tdesiredWorkspaceHorizontalMarginPx: 21.0px (8.0dp)\n" + - "\tworkspacePadding.left: 21.0px (8.0dp)\n" + - "\tworkspacePadding.top: 30.0px (11.428572dp)\n" + - "\tworkspacePadding.right: 21.0px (8.0dp)\n" + - "\tworkspacePadding.bottom: 330.0px (125.71429dp)\n" + - "\ticonScale: 1.0px (0.3809524dp)\n" + - "\tcellScaleToFit : 1.0px (0.3809524dp)\n" + - "\textraSpace: 498.0px (189.71428dp)\n" + - "\tunscaled extraSpace: 498.0px (189.71428dp)\n" + - "\tmaxEmptySpace: 0.0px (0.0dp)\n" + - "\tworkspaceTopPadding: 0.0px (0.0dp)\n" + - "\tworkspaceBottomPadding: 0.0px (0.0dp)\n" + - "\toverviewTaskMarginPx: 0.0px (0.0dp)\n" + - "\toverviewTaskIconSizePx: 0.0px (0.0dp)\n" + - "\toverviewTaskIconDrawableSizePx: 0.0px (0.0dp)\n" + - "\toverviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)\n" + - "\toverviewTaskThumbnailTopMarginPx: 0.0px (0.0dp)\n" + - "\toverviewActionsTopMarginPx: 0.0px (0.0dp)\n" + - "\toverviewActionsHeight: 0.0px (0.0dp)\n" + - "\toverviewActionsClaimedSpaceBelow: 0.0px (0.0dp)\n" + - "\toverviewActionsButtonSpacing: 0.0px (0.0dp)\n" + - "\toverviewPageSpacing: 0.0px (0.0dp)\n" + - "\toverviewRowSpacing: 0.0px (0.0dp)\n" + - "\toverviewGridSideMargin: 0.0px (0.0dp)\n" + - "\tdropTargetBarTopMarginPx: 0.0px (0.0dp)\n" + - "\tdropTargetBarSizePx: 147.0px (56.0dp)\n" + - "\tdropTargetBarBottomMarginPx: 42.0px (16.0dp)\n" + - "\tgetCellLayoutSpringLoadShrunkTop(): 299.0px (113.90476dp)\n" + - "\tgetCellLayoutSpringLoadShrunkBottom(): 1457.0px (555.0476dp)\n" + - "\tworkspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)\n" + - "\tgetWorkspaceSpringLoadScale(): 0.8452555px (0.32200208dp)\n" + - "\tgetCellLayoutHeight(): 1370.0px (521.9048dp)\n" + - "\tgetCellLayoutWidth(): 1083.0px (412.57144dp)\n" - ) + assertDump(dp, "twoPanelLandscape3Button") } @Test @@ -1284,136 +124,7 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { val dp = getDeviceProfileForGrid("4_by_4") dp.isTaskbarPresentInApps = true - assertThat(dump(dp)) - .isEqualTo( - "DeviceProfile:\n" + - "\t1 dp = 2.625 px\n" + - "\tisTablet:true\n" + - "\tisPhone:false\n" + - "\ttransposeLayoutWithOrientation:false\n" + - "\tisGestureMode:true\n" + - "\tisLandscape:true\n" + - "\tisMultiWindowMode:false\n" + - "\tisTwoPanels:true\n" + - "\twindowX: 0.0px (0.0dp)\n" + - "\twindowY: 0.0px (0.0dp)\n" + - "\twidthPx: 2208.0px (841.1429dp)\n" + - "\theightPx: 1840.0px (700.9524dp)\n" + - "\tavailableWidthPx: 2208.0px (841.1429dp)\n" + - "\tavailableHeightPx: 1730.0px (659.0476dp)\n" + - "\tmInsets.left: 0.0px (0.0dp)\n" + - "\tmInsets.top: 110.0px (41.904762dp)\n" + - "\tmInsets.right: 0.0px (0.0dp)\n" + - "\tmInsets.bottom: 0.0px (0.0dp)\n" + - "\taspectRatio:1.2\n" + - "\tisResponsiveGrid:false\n" + - "\tisScalableGrid:false\n" + - "\tinv.numRows: 4\n" + - "\tinv.numColumns: 4\n" + - "\tinv.numSearchContainerColumns: 4\n" + - "\tminCellSize: PointF(0.0, 0.0)dp\n" + - "\tcellWidthPx: 154.0px (58.666668dp)\n" + - "\tcellHeightPx: 218.0px (83.04762dp)\n" + - "\tgetCellSize().x: 270.0px (102.85714dp)\n" + - "\tgetCellSize().y: 342.0px (130.28572dp)\n" + - "\tcellLayoutBorderSpacePx Horizontal: 0.0px (0.0dp)\n" + - "\tcellLayoutBorderSpacePx Vertical: 0.0px (0.0dp)\n" + - "\tcellLayoutPaddingPx.left: 0.0px (0.0dp)\n" + - "\tcellLayoutPaddingPx.top: 0.0px (0.0dp)\n" + - "\tcellLayoutPaddingPx.right: 0.0px (0.0dp)\n" + - "\tcellLayoutPaddingPx.bottom: 0.0px (0.0dp)\n" + - "\ticonSizePx: 141.0px (53.714287dp)\n" + - "\ticonTextSizePx: 34.0px (12.952381dp)\n" + - "\ticonDrawablePaddingPx: 13.0px (4.952381dp)\n" + - "\tinv.numFolderRows: 3\n" + - "\tinv.numFolderColumns: 4\n" + - "\tfolderCellWidthPx: 189.0px (72.0dp)\n" + - "\tfolderCellHeightPx: 219.0px (83.42857dp)\n" + - "\tfolderChildIconSizePx: 141.0px (53.714287dp)\n" + - "\tfolderChildTextSizePx: 34.0px (12.952381dp)\n" + - "\tfolderChildDrawablePaddingPx: 5.0px (1.9047619dp)\n" + - "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" + - "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" + - "\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" + - "\tfolderTopPadding: 63.0px (24.0dp)\n" + - "\tfolderFooterHeight: 147.0px (56.0dp)\n" + - "\tbottomSheetTopPadding: 110.0px (41.904762dp)\n" + - "\tbottomSheetOpenDuration: 500\n" + - "\tbottomSheetCloseDuration: 500\n" + - "\tbottomSheetWorkspaceScale: 0.97\n" + - "\tbottomSheetDepth: 1.0\n" + - "\tallAppsShiftRange: 1730.0px (659.0476dp)\n" + - "\tallAppsTopPadding: 110.0px (41.904762dp)\n" + - "\tallAppsOpenDuration: 500\n" + - "\tallAppsCloseDuration: 500\n" + - "\tallAppsIconSizePx: 141.0px (53.714287dp)\n" + - "\tallAppsIconTextSizePx: 34.0px (12.952381dp)\n" + - "\tallAppsIconDrawablePaddingPx: 21.0px (8.0dp)\n" + - "\tallAppsCellHeightPx: 315.0px (120.0dp)\n" + - "\tallAppsCellWidthPx: 183.0px (69.71429dp)\n" + - "\tallAppsBorderSpacePxX: 42.0px (16.0dp)\n" + - "\tallAppsBorderSpacePxY: 42.0px (16.0dp)\n" + - "\tnumShownAllAppsColumns: 8\n" + - "\tallAppsLeftRightPadding: 42.0px (16.0dp)\n" + - "\tallAppsLeftRightMargin: 183.0px (69.71429dp)\n" + - "\thotseatBarSizePx: 267.0px (101.71429dp)\n" + - "\tinv.hotseatColumnSpan: 4\n" + - "\thotseatCellHeightPx: 159.0px (60.57143dp)\n" + - "\thotseatBarBottomSpacePx: 126.0px (48.0dp)\n" + - "\thotseatBarSidePaddingStartPx: 0.0px (0.0dp)\n" + - "\thotseatBarSidePaddingEndPx: 0.0px (0.0dp)\n" + - "\thotseatBarEndOffset: 0.0px (0.0dp)\n" + - "\thotseatQsbSpace: 0.0px (0.0dp)\n" + - "\thotseatQsbHeight: 0.0px (0.0dp)\n" + - "\tspringLoadedHotseatBarTopMarginPx: 116.0px (44.190475dp)\n" + - "\tgetHotseatLayoutPadding(context).top: 0.0px (0.0dp)\n" + - "\tgetHotseatLayoutPadding(context).bottom: 108.0px (41.142857dp)\n" + - "\tgetHotseatLayoutPadding(context).left: 113.0px (43.04762dp)\n" + - "\tgetHotseatLayoutPadding(context).right: 113.0px (43.04762dp)\n" + - "\tnumShownHotseatIcons: 6\n" + - "\thotseatBorderSpace: 0.0px (0.0dp)\n" + - "\tisQsbInline: false\n" + - "\thotseatQsbWidth: 0.0px (0.0dp)\n" + - "\tisTaskbarPresent:false\n" + - "\tisTaskbarPresentInApps:true\n" + - "\ttaskbarHeight: 0.0px (0.0dp)\n" + - "\tstashedTaskbarHeight: 0.0px (0.0dp)\n" + - "\ttaskbarBottomMargin: 0.0px (0.0dp)\n" + - "\ttaskbarIconSize: 0.0px (0.0dp)\n" + - "\tdesiredWorkspaceHorizontalMarginPx: 21.0px (8.0dp)\n" + - "\tworkspacePadding.left: 21.0px (8.0dp)\n" + - "\tworkspacePadding.top: 30.0px (11.428572dp)\n" + - "\tworkspacePadding.right: 21.0px (8.0dp)\n" + - "\tworkspacePadding.bottom: 330.0px (125.71429dp)\n" + - "\ticonScale: 1.0px (0.3809524dp)\n" + - "\tcellScaleToFit : 1.0px (0.3809524dp)\n" + - "\textraSpace: 498.0px (189.71428dp)\n" + - "\tunscaled extraSpace: 498.0px (189.71428dp)\n" + - "\tmaxEmptySpace: 0.0px (0.0dp)\n" + - "\tworkspaceTopPadding: 0.0px (0.0dp)\n" + - "\tworkspaceBottomPadding: 0.0px (0.0dp)\n" + - "\toverviewTaskMarginPx: 0.0px (0.0dp)\n" + - "\toverviewTaskIconSizePx: 0.0px (0.0dp)\n" + - "\toverviewTaskIconDrawableSizePx: 0.0px (0.0dp)\n" + - "\toverviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)\n" + - "\toverviewTaskThumbnailTopMarginPx: 0.0px (0.0dp)\n" + - "\toverviewActionsTopMarginPx: 0.0px (0.0dp)\n" + - "\toverviewActionsHeight: 0.0px (0.0dp)\n" + - "\toverviewActionsClaimedSpaceBelow: 0.0px (0.0dp)\n" + - "\toverviewActionsButtonSpacing: 0.0px (0.0dp)\n" + - "\toverviewPageSpacing: 0.0px (0.0dp)\n" + - "\toverviewRowSpacing: 0.0px (0.0dp)\n" + - "\toverviewGridSideMargin: 0.0px (0.0dp)\n" + - "\tdropTargetBarTopMarginPx: 0.0px (0.0dp)\n" + - "\tdropTargetBarSizePx: 147.0px (56.0dp)\n" + - "\tdropTargetBarBottomMarginPx: 42.0px (16.0dp)\n" + - "\tgetCellLayoutSpringLoadShrunkTop(): 299.0px (113.90476dp)\n" + - "\tgetCellLayoutSpringLoadShrunkBottom(): 1457.0px (555.0476dp)\n" + - "\tworkspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)\n" + - "\tgetWorkspaceSpringLoadScale(): 0.8452555px (0.32200208dp)\n" + - "\tgetCellLayoutHeight(): 1370.0px (521.9048dp)\n" + - "\tgetCellLayoutWidth(): 1083.0px (412.57144dp)\n" - ) + assertDump(dp, "twoPanelLandscape") } @Test @@ -1426,136 +137,7 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { val dp = getDeviceProfileForGrid("4_by_4") dp.isTaskbarPresentInApps = true - assertThat(dump(dp)) - .isEqualTo( - "DeviceProfile:\n" + - "\t1 dp = 2.625 px\n" + - "\tisTablet:true\n" + - "\tisPhone:false\n" + - "\ttransposeLayoutWithOrientation:false\n" + - "\tisGestureMode:false\n" + - "\tisLandscape:false\n" + - "\tisMultiWindowMode:false\n" + - "\tisTwoPanels:true\n" + - "\twindowX: 0.0px (0.0dp)\n" + - "\twindowY: 0.0px (0.0dp)\n" + - "\twidthPx: 1840.0px (700.9524dp)\n" + - "\theightPx: 2208.0px (841.1429dp)\n" + - "\tavailableWidthPx: 1840.0px (700.9524dp)\n" + - "\tavailableHeightPx: 2075.0px (790.4762dp)\n" + - "\tmInsets.left: 0.0px (0.0dp)\n" + - "\tmInsets.top: 133.0px (50.666668dp)\n" + - "\tmInsets.right: 0.0px (0.0dp)\n" + - "\tmInsets.bottom: 0.0px (0.0dp)\n" + - "\taspectRatio:1.2\n" + - "\tisResponsiveGrid:false\n" + - "\tisScalableGrid:false\n" + - "\tinv.numRows: 4\n" + - "\tinv.numColumns: 4\n" + - "\tinv.numSearchContainerColumns: 4\n" + - "\tminCellSize: PointF(0.0, 0.0)dp\n" + - "\tcellWidthPx: 154.0px (58.666668dp)\n" + - "\tcellHeightPx: 218.0px (83.04762dp)\n" + - "\tgetCellSize().x: 224.0px (85.333336dp)\n" + - "\tgetCellSize().y: 430.0px (163.80952dp)\n" + - "\tcellLayoutBorderSpacePx Horizontal: 0.0px (0.0dp)\n" + - "\tcellLayoutBorderSpacePx Vertical: 0.0px (0.0dp)\n" + - "\tcellLayoutPaddingPx.left: 0.0px (0.0dp)\n" + - "\tcellLayoutPaddingPx.top: 0.0px (0.0dp)\n" + - "\tcellLayoutPaddingPx.right: 0.0px (0.0dp)\n" + - "\tcellLayoutPaddingPx.bottom: 0.0px (0.0dp)\n" + - "\ticonSizePx: 141.0px (53.714287dp)\n" + - "\ticonTextSizePx: 34.0px (12.952381dp)\n" + - "\ticonDrawablePaddingPx: 13.0px (4.952381dp)\n" + - "\tinv.numFolderRows: 3\n" + - "\tinv.numFolderColumns: 4\n" + - "\tfolderCellWidthPx: 189.0px (72.0dp)\n" + - "\tfolderCellHeightPx: 219.0px (83.42857dp)\n" + - "\tfolderChildIconSizePx: 141.0px (53.714287dp)\n" + - "\tfolderChildTextSizePx: 34.0px (12.952381dp)\n" + - "\tfolderChildDrawablePaddingPx: 5.0px (1.9047619dp)\n" + - "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" + - "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" + - "\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" + - "\tfolderTopPadding: 63.0px (24.0dp)\n" + - "\tfolderFooterHeight: 147.0px (56.0dp)\n" + - "\tbottomSheetTopPadding: 133.0px (50.666668dp)\n" + - "\tbottomSheetOpenDuration: 500\n" + - "\tbottomSheetCloseDuration: 500\n" + - "\tbottomSheetWorkspaceScale: 0.97\n" + - "\tbottomSheetDepth: 1.0\n" + - "\tallAppsShiftRange: 1826.0px (695.619dp)\n" + - "\tallAppsTopPadding: 382.0px (145.5238dp)\n" + - "\tallAppsOpenDuration: 500\n" + - "\tallAppsCloseDuration: 500\n" + - "\tallAppsIconSizePx: 141.0px (53.714287dp)\n" + - "\tallAppsIconTextSizePx: 34.0px (12.952381dp)\n" + - "\tallAppsIconDrawablePaddingPx: 21.0px (8.0dp)\n" + - "\tallAppsCellHeightPx: 315.0px (120.0dp)\n" + - "\tallAppsCellWidthPx: 183.0px (69.71429dp)\n" + - "\tallAppsBorderSpacePxX: 42.0px (16.0dp)\n" + - "\tallAppsBorderSpacePxY: 42.0px (16.0dp)\n" + - "\tnumShownAllAppsColumns: 8\n" + - "\tallAppsLeftRightPadding: 42.0px (16.0dp)\n" + - "\tallAppsLeftRightMargin: 1.0px (0.3809524dp)\n" + - "\thotseatBarSizePx: 267.0px (101.71429dp)\n" + - "\tinv.hotseatColumnSpan: 4\n" + - "\thotseatCellHeightPx: 159.0px (60.57143dp)\n" + - "\thotseatBarBottomSpacePx: 126.0px (48.0dp)\n" + - "\thotseatBarSidePaddingStartPx: 0.0px (0.0dp)\n" + - "\thotseatBarSidePaddingEndPx: 0.0px (0.0dp)\n" + - "\thotseatBarEndOffset: 0.0px (0.0dp)\n" + - "\thotseatQsbSpace: 0.0px (0.0dp)\n" + - "\thotseatQsbHeight: 0.0px (0.0dp)\n" + - "\tspringLoadedHotseatBarTopMarginPx: 171.0px (65.14286dp)\n" + - "\tgetHotseatLayoutPadding(context).top: 0.0px (0.0dp)\n" + - "\tgetHotseatLayoutPadding(context).bottom: 108.0px (41.142857dp)\n" + - "\tgetHotseatLayoutPadding(context).left: 98.0px (37.333332dp)\n" + - "\tgetHotseatLayoutPadding(context).right: 98.0px (37.333332dp)\n" + - "\tnumShownHotseatIcons: 6\n" + - "\thotseatBorderSpace: 0.0px (0.0dp)\n" + - "\tisQsbInline: false\n" + - "\thotseatQsbWidth: 0.0px (0.0dp)\n" + - "\tisTaskbarPresent:false\n" + - "\tisTaskbarPresentInApps:true\n" + - "\ttaskbarHeight: 0.0px (0.0dp)\n" + - "\tstashedTaskbarHeight: 0.0px (0.0dp)\n" + - "\ttaskbarBottomMargin: 0.0px (0.0dp)\n" + - "\ttaskbarIconSize: 0.0px (0.0dp)\n" + - "\tdesiredWorkspaceHorizontalMarginPx: 21.0px (8.0dp)\n" + - "\tworkspacePadding.left: 21.0px (8.0dp)\n" + - "\tworkspacePadding.top: 24.0px (9.142858dp)\n" + - "\tworkspacePadding.right: 21.0px (8.0dp)\n" + - "\tworkspacePadding.bottom: 330.0px (125.71429dp)\n" + - "\ticonScale: 1.0px (0.3809524dp)\n" + - "\tcellScaleToFit : 1.0px (0.3809524dp)\n" + - "\textraSpace: 849.0px (323.42856dp)\n" + - "\tunscaled extraSpace: 849.0px (323.42856dp)\n" + - "\tmaxEmptySpace: 0.0px (0.0dp)\n" + - "\tworkspaceTopPadding: 0.0px (0.0dp)\n" + - "\tworkspaceBottomPadding: 0.0px (0.0dp)\n" + - "\toverviewTaskMarginPx: 0.0px (0.0dp)\n" + - "\toverviewTaskIconSizePx: 0.0px (0.0dp)\n" + - "\toverviewTaskIconDrawableSizePx: 0.0px (0.0dp)\n" + - "\toverviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)\n" + - "\toverviewTaskThumbnailTopMarginPx: 0.0px (0.0dp)\n" + - "\toverviewActionsTopMarginPx: 0.0px (0.0dp)\n" + - "\toverviewActionsHeight: 0.0px (0.0dp)\n" + - "\toverviewActionsClaimedSpaceBelow: 0.0px (0.0dp)\n" + - "\toverviewActionsButtonSpacing: 0.0px (0.0dp)\n" + - "\toverviewPageSpacing: 0.0px (0.0dp)\n" + - "\toverviewRowSpacing: 0.0px (0.0dp)\n" + - "\toverviewGridSideMargin: 0.0px (0.0dp)\n" + - "\tdropTargetBarTopMarginPx: 168.0px (64.0dp)\n" + - "\tdropTargetBarSizePx: 147.0px (56.0dp)\n" + - "\tdropTargetBarBottomMarginPx: 42.0px (16.0dp)\n" + - "\tgetCellLayoutSpringLoadShrunkTop(): 490.0px (186.66667dp)\n" + - "\tgetCellLayoutSpringLoadShrunkBottom(): 1770.0px (674.2857dp)\n" + - "\tworkspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)\n" + - "\tgetWorkspaceSpringLoadScale(): 0.7437536px (0.2833347dp)\n" + - "\tgetCellLayoutHeight(): 1721.0px (655.619dp)\n" + - "\tgetCellLayoutWidth(): 899.0px (342.4762dp)\n" - ) + assertDump(dp, "twoPanelPortrait3Button") } @Test @@ -1564,147 +146,17 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { val dp = getDeviceProfileForGrid("4_by_4") dp.isTaskbarPresentInApps = true - assertThat(dump(dp)) - .isEqualTo( - "DeviceProfile:\n" + - "\t1 dp = 2.625 px\n" + - "\tisTablet:true\n" + - "\tisPhone:false\n" + - "\ttransposeLayoutWithOrientation:false\n" + - "\tisGestureMode:true\n" + - "\tisLandscape:false\n" + - "\tisMultiWindowMode:false\n" + - "\tisTwoPanels:true\n" + - "\twindowX: 0.0px (0.0dp)\n" + - "\twindowY: 0.0px (0.0dp)\n" + - "\twidthPx: 1840.0px (700.9524dp)\n" + - "\theightPx: 2208.0px (841.1429dp)\n" + - "\tavailableWidthPx: 1840.0px (700.9524dp)\n" + - "\tavailableHeightPx: 2075.0px (790.4762dp)\n" + - "\tmInsets.left: 0.0px (0.0dp)\n" + - "\tmInsets.top: 133.0px (50.666668dp)\n" + - "\tmInsets.right: 0.0px (0.0dp)\n" + - "\tmInsets.bottom: 0.0px (0.0dp)\n" + - "\taspectRatio:1.2\n" + - "\tisResponsiveGrid:false\n" + - "\tisScalableGrid:false\n" + - "\tinv.numRows: 4\n" + - "\tinv.numColumns: 4\n" + - "\tinv.numSearchContainerColumns: 4\n" + - "\tminCellSize: PointF(0.0, 0.0)dp\n" + - "\tcellWidthPx: 154.0px (58.666668dp)\n" + - "\tcellHeightPx: 218.0px (83.04762dp)\n" + - "\tgetCellSize().x: 224.0px (85.333336dp)\n" + - "\tgetCellSize().y: 430.0px (163.80952dp)\n" + - "\tcellLayoutBorderSpacePx Horizontal: 0.0px (0.0dp)\n" + - "\tcellLayoutBorderSpacePx Vertical: 0.0px (0.0dp)\n" + - "\tcellLayoutPaddingPx.left: 0.0px (0.0dp)\n" + - "\tcellLayoutPaddingPx.top: 0.0px (0.0dp)\n" + - "\tcellLayoutPaddingPx.right: 0.0px (0.0dp)\n" + - "\tcellLayoutPaddingPx.bottom: 0.0px (0.0dp)\n" + - "\ticonSizePx: 141.0px (53.714287dp)\n" + - "\ticonTextSizePx: 34.0px (12.952381dp)\n" + - "\ticonDrawablePaddingPx: 13.0px (4.952381dp)\n" + - "\tinv.numFolderRows: 3\n" + - "\tinv.numFolderColumns: 4\n" + - "\tfolderCellWidthPx: 189.0px (72.0dp)\n" + - "\tfolderCellHeightPx: 219.0px (83.42857dp)\n" + - "\tfolderChildIconSizePx: 141.0px (53.714287dp)\n" + - "\tfolderChildTextSizePx: 34.0px (12.952381dp)\n" + - "\tfolderChildDrawablePaddingPx: 5.0px (1.9047619dp)\n" + - "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" + - "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" + - "\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" + - "\tfolderTopPadding: 63.0px (24.0dp)\n" + - "\tfolderFooterHeight: 147.0px (56.0dp)\n" + - "\tbottomSheetTopPadding: 133.0px (50.666668dp)\n" + - "\tbottomSheetOpenDuration: 500\n" + - "\tbottomSheetCloseDuration: 500\n" + - "\tbottomSheetWorkspaceScale: 0.97\n" + - "\tbottomSheetDepth: 1.0\n" + - "\tallAppsShiftRange: 1826.0px (695.619dp)\n" + - "\tallAppsTopPadding: 382.0px (145.5238dp)\n" + - "\tallAppsOpenDuration: 500\n" + - "\tallAppsCloseDuration: 500\n" + - "\tallAppsIconSizePx: 141.0px (53.714287dp)\n" + - "\tallAppsIconTextSizePx: 34.0px (12.952381dp)\n" + - "\tallAppsIconDrawablePaddingPx: 21.0px (8.0dp)\n" + - "\tallAppsCellHeightPx: 315.0px (120.0dp)\n" + - "\tallAppsCellWidthPx: 183.0px (69.71429dp)\n" + - "\tallAppsBorderSpacePxX: 42.0px (16.0dp)\n" + - "\tallAppsBorderSpacePxY: 42.0px (16.0dp)\n" + - "\tnumShownAllAppsColumns: 8\n" + - "\tallAppsLeftRightPadding: 42.0px (16.0dp)\n" + - "\tallAppsLeftRightMargin: 1.0px (0.3809524dp)\n" + - "\thotseatBarSizePx: 267.0px (101.71429dp)\n" + - "\tinv.hotseatColumnSpan: 4\n" + - "\thotseatCellHeightPx: 159.0px (60.57143dp)\n" + - "\thotseatBarBottomSpacePx: 126.0px (48.0dp)\n" + - "\thotseatBarSidePaddingStartPx: 0.0px (0.0dp)\n" + - "\thotseatBarSidePaddingEndPx: 0.0px (0.0dp)\n" + - "\thotseatBarEndOffset: 0.0px (0.0dp)\n" + - "\thotseatQsbSpace: 0.0px (0.0dp)\n" + - "\thotseatQsbHeight: 0.0px (0.0dp)\n" + - "\tspringLoadedHotseatBarTopMarginPx: 171.0px (65.14286dp)\n" + - "\tgetHotseatLayoutPadding(context).top: 0.0px (0.0dp)\n" + - "\tgetHotseatLayoutPadding(context).bottom: 108.0px (41.142857dp)\n" + - "\tgetHotseatLayoutPadding(context).left: 98.0px (37.333332dp)\n" + - "\tgetHotseatLayoutPadding(context).right: 98.0px (37.333332dp)\n" + - "\tnumShownHotseatIcons: 6\n" + - "\thotseatBorderSpace: 0.0px (0.0dp)\n" + - "\tisQsbInline: false\n" + - "\thotseatQsbWidth: 0.0px (0.0dp)\n" + - "\tisTaskbarPresent:false\n" + - "\tisTaskbarPresentInApps:true\n" + - "\ttaskbarHeight: 0.0px (0.0dp)\n" + - "\tstashedTaskbarHeight: 0.0px (0.0dp)\n" + - "\ttaskbarBottomMargin: 0.0px (0.0dp)\n" + - "\ttaskbarIconSize: 0.0px (0.0dp)\n" + - "\tdesiredWorkspaceHorizontalMarginPx: 21.0px (8.0dp)\n" + - "\tworkspacePadding.left: 21.0px (8.0dp)\n" + - "\tworkspacePadding.top: 24.0px (9.142858dp)\n" + - "\tworkspacePadding.right: 21.0px (8.0dp)\n" + - "\tworkspacePadding.bottom: 330.0px (125.71429dp)\n" + - "\ticonScale: 1.0px (0.3809524dp)\n" + - "\tcellScaleToFit : 1.0px (0.3809524dp)\n" + - "\textraSpace: 849.0px (323.42856dp)\n" + - "\tunscaled extraSpace: 849.0px (323.42856dp)\n" + - "\tmaxEmptySpace: 0.0px (0.0dp)\n" + - "\tworkspaceTopPadding: 0.0px (0.0dp)\n" + - "\tworkspaceBottomPadding: 0.0px (0.0dp)\n" + - "\toverviewTaskMarginPx: 0.0px (0.0dp)\n" + - "\toverviewTaskIconSizePx: 0.0px (0.0dp)\n" + - "\toverviewTaskIconDrawableSizePx: 0.0px (0.0dp)\n" + - "\toverviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)\n" + - "\toverviewTaskThumbnailTopMarginPx: 0.0px (0.0dp)\n" + - "\toverviewActionsTopMarginPx: 0.0px (0.0dp)\n" + - "\toverviewActionsHeight: 0.0px (0.0dp)\n" + - "\toverviewActionsClaimedSpaceBelow: 0.0px (0.0dp)\n" + - "\toverviewActionsButtonSpacing: 0.0px (0.0dp)\n" + - "\toverviewPageSpacing: 0.0px (0.0dp)\n" + - "\toverviewRowSpacing: 0.0px (0.0dp)\n" + - "\toverviewGridSideMargin: 0.0px (0.0dp)\n" + - "\tdropTargetBarTopMarginPx: 168.0px (64.0dp)\n" + - "\tdropTargetBarSizePx: 147.0px (56.0dp)\n" + - "\tdropTargetBarBottomMarginPx: 42.0px (16.0dp)\n" + - "\tgetCellLayoutSpringLoadShrunkTop(): 490.0px (186.66667dp)\n" + - "\tgetCellLayoutSpringLoadShrunkBottom(): 1770.0px (674.2857dp)\n" + - "\tworkspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)\n" + - "\tgetWorkspaceSpringLoadScale(): 0.7437536px (0.2833347dp)\n" + - "\tgetCellLayoutHeight(): 1721.0px (655.619dp)\n" + - "\tgetCellLayoutWidth(): 899.0px (342.4762dp)\n" - ) + assertDump(dp, "twoPanelPortrait") } private fun getDeviceProfileForGrid(gridName: String): DeviceProfile { return InvariantDeviceProfile(context, gridName).getDeviceProfile(context) } - private fun dump(dp: DeviceProfile): String { - val stringWriter = StringWriter() - val printWriter = PrintWriter(stringWriter) - dp.dump(context, "", printWriter) - printWriter.flush() - return stringWriter.toString() + private fun assertDump(dp: DeviceProfile, filename: String) { + val dump = dump(context!!, dp, "${folderName}_$filename.txt") + val expected = readDumpFromAssets(testContext, "$folderName/$filename.txt") + + assertThat(dump).isEqualTo(expected) } } diff --git a/tests/src/com/android/launcher3/popup/PopupPopulatorTest.java b/tests/src/com/android/launcher3/popup/PopupPopulatorTest.java index 6764e09fa1..f3eb0a97c0 100644 --- a/tests/src/com/android/launcher3/popup/PopupPopulatorTest.java +++ b/tests/src/com/android/launcher3/popup/PopupPopulatorTest.java @@ -64,34 +64,14 @@ public class PopupPopulatorTest { MAX_SHORTCUTS - NUM_DYNAMIC, NUM_DYNAMIC); } - @Test - public void testDeDupeShortcutId() { - // Successfully remove one of the shortcuts - filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(3, 0), 2, 0, generateId(true, 1)); - filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(0, 3), 0, 2, generateId(false, 1)); - filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(2, 2), 2, 1, generateId(false, 1)); - filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(2, 2), 1, 2, generateId(true, 1)); - // Successfully keep all shortcuts when id doesn't exist - filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(3, 0), 3, 0, generateId(false, 1)); - filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(3, 0), 3, 0, generateId(true, 4)); - filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(2, 2), 2, 2, generateId(false, 4)); - filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(2, 2), 2, 2, generateId(true, 4)); - } - private String generateId(boolean isStatic, int rank) { return (isStatic ? "static" : "dynamic") + rank; } private void filterShortcutsAndAssertNumStaticAndDynamic( List<ShortcutInfo> shortcuts, int expectedStatic, int expectedDynamic) { - filterShortcutsAndAssertNumStaticAndDynamic(shortcuts, expectedStatic, expectedDynamic, null); - } - - private void filterShortcutsAndAssertNumStaticAndDynamic(List<ShortcutInfo> shortcuts, - int expectedStatic, int expectedDynamic, String shortcutIdToRemove) { Collections.shuffle(shortcuts); - List<ShortcutInfo> filteredShortcuts = PopupPopulator.sortAndFilterShortcuts( - shortcuts, shortcutIdToRemove); + List<ShortcutInfo> filteredShortcuts = PopupPopulator.sortAndFilterShortcuts(shortcuts); assertIsSorted(filteredShortcuts); int numStatic = 0; diff --git a/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java b/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java index 73bb5865ee..10d9133902 100644 --- a/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java +++ b/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java @@ -19,17 +19,30 @@ import static android.os.Process.myUserHandle; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static com.android.launcher3.LauncherPrefs.APP_WIDGET_IDS; +import static com.android.launcher3.LauncherPrefs.OLD_APP_WIDGET_IDS; +import static com.android.launcher3.LauncherPrefs.RESTORE_DEVICE; import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP; import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME; +import static com.android.launcher3.widget.LauncherWidgetHolder.APPWIDGET_HOST_ID; + +import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; import android.app.backup.BackupManager; +import android.appwidget.AppWidgetHost; import android.content.ContentValues; import android.content.Context; import android.content.Intent; @@ -43,15 +56,21 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.launcher3.LauncherAppState; +import com.android.launcher3.LauncherPrefs; import com.android.launcher3.LauncherSettings; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.model.ModelDbController; +import com.android.launcher3.util.IntArray; import com.android.launcher3.util.LauncherModelHelper; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mockito; + +import java.util.Arrays; +import java.util.stream.IntStream; /** * Tests for {@link RestoreDbTask} @@ -66,16 +85,27 @@ public class RestoreDbTaskTest { private LauncherModelHelper mModelHelper; private Context mContext; + private RestoreDbTask mTask; + private ModelDbController mMockController; + private SQLiteDatabase mMockDb; + private Cursor mMockCursor; + private LauncherPrefs mPrefs; @Before public void setup() { mModelHelper = new LauncherModelHelper(); mContext = mModelHelper.sandboxContext; + mTask = new RestoreDbTask(); + mMockController = Mockito.mock(ModelDbController.class); + mMockDb = mock(SQLiteDatabase.class); + mMockCursor = mock(Cursor.class); + mPrefs = new LauncherPrefs(mContext); } @After public void teardown() { mModelHelper.destroy(); + LauncherPrefs.get(mContext).removeSync(RESTORE_DEVICE); } @Test @@ -148,8 +178,7 @@ public class RestoreDbTaskTest { assertEquals(10, getItemCountForProfile(db, myProfileId_old)); assertEquals(6, getItemCountForProfile(db, workProfileId_old)); - RestoreDbTask task = new RestoreDbTask(); - task.sanitizeDB(mContext, controller, controller.getDb(), bm); + mTask.sanitizeDB(mContext, controller, controller.getDb(), bm); // All the data has been migrated to the new user ids assertEquals(0, getItemCountForProfile(db, myProfileId_old)); @@ -178,8 +207,7 @@ public class RestoreDbTaskTest { assertEquals(10, getItemCountForProfile(db, myProfileId_old)); assertEquals(6, getItemCountForProfile(db, workProfileId_old)); - RestoreDbTask task = new RestoreDbTask(); - task.sanitizeDB(mContext, controller, controller.getDb(), bm); + mTask.sanitizeDB(mContext, controller, controller.getDb(), bm); // All the data has been migrated to the new user ids assertEquals(0, getItemCountForProfile(db, myProfileId_old)); @@ -188,6 +216,83 @@ public class RestoreDbTaskTest { assertEquals(10, getCount(db, "select * from favorites")); } + @Test + public void givenLauncherPrefsHasNoIds_whenRestoreAppWidgetIdsIfExists_thenIdsAreRemoved() { + // When + mTask.restoreAppWidgetIdsIfExists(mContext, mMockController); + // Then + assertThat(mPrefs.has(OLD_APP_WIDGET_IDS, APP_WIDGET_IDS)).isFalse(); + } + + @Test + public void givenNoPendingRestore_WhenRestoreAppWidgetIds_ThenRemoveNewWidgetIds() { + // Given + AppWidgetHost expectedHost = new AppWidgetHost(mContext, APPWIDGET_HOST_ID); + int[] expectedOldIds = generateOldWidgetIds(expectedHost); + int[] expectedNewIds = generateNewWidgetIds(expectedHost, expectedOldIds); + when(mMockController.getDb()).thenReturn(mMockDb); + mPrefs.remove(RESTORE_DEVICE); + + // When + setRestoredAppWidgetIds(mContext, expectedOldIds, expectedNewIds); + mTask.restoreAppWidgetIdsIfExists(mContext, mMockController); + + // Then + assertThat(expectedHost.getAppWidgetIds()).isEqualTo(expectedOldIds); + assertThat(mPrefs.has(OLD_APP_WIDGET_IDS, APP_WIDGET_IDS)).isFalse(); + verifyZeroInteractions(mMockController); + } + + @Test + public void givenRestoreWithNonExistingWidgets_WhenRestoreAppWidgetIds_ThenRemoveNewIds() { + // Given + AppWidgetHost expectedHost = new AppWidgetHost(mContext, APPWIDGET_HOST_ID); + int[] expectedOldIds = generateOldWidgetIds(expectedHost); + int[] expectedNewIds = generateNewWidgetIds(expectedHost, expectedOldIds); + when(mMockController.getDb()).thenReturn(mMockDb); + when(mMockDb.query(any(), any(), any(), any(), any(), any(), any())).thenReturn( + mMockCursor); + when(mMockCursor.moveToFirst()).thenReturn(false); + RestoreDbTask.setPending(mContext); + + // When + setRestoredAppWidgetIds(mContext, expectedOldIds, expectedNewIds); + mTask.restoreAppWidgetIdsIfExists(mContext, mMockController); + + // Then + assertThat(expectedHost.getAppWidgetIds()).isEqualTo(expectedOldIds); + assertThat(mPrefs.has(OLD_APP_WIDGET_IDS, APP_WIDGET_IDS)).isFalse(); + verify(mMockController, times(expectedOldIds.length)).update(any(), any(), any(), any()); + } + + @Test + public void givenRestore_WhenRestoreAppWidgetIds_ThenAddNewIds() { + // Given + AppWidgetHost expectedHost = new AppWidgetHost(mContext, APPWIDGET_HOST_ID); + int[] expectedOldIds = generateOldWidgetIds(expectedHost); + int[] expectedNewIds = generateNewWidgetIds(expectedHost, expectedOldIds); + int[] allExpectedIds = IntStream.concat( + Arrays.stream(expectedOldIds), + Arrays.stream(expectedNewIds) + ).toArray(); + + when(mMockController.getDb()).thenReturn(mMockDb); + when(mMockDb.query(any(), any(), any(), any(), any(), any(), any())) + .thenReturn(mMockCursor); + when(mMockCursor.moveToFirst()).thenReturn(true); + when(mMockCursor.isAfterLast()).thenReturn(true); + RestoreDbTask.setPending(mContext); + + // When + setRestoredAppWidgetIds(mContext, expectedOldIds, expectedNewIds); + mTask.restoreAppWidgetIdsIfExists(mContext, mMockController); + + // Then + assertThat(expectedHost.getAppWidgetIds()).isEqualTo(allExpectedIds); + assertThat(mPrefs.has(OLD_APP_WIDGET_IDS, APP_WIDGET_IDS)).isFalse(); + verify(mMockController, times(expectedOldIds.length)).update(any(), any(), any(), any()); + } + private void addIconsBulk(MyModelDbController controller, int count, int screen, long profileId) { int columns = LauncherAppState.getIDP(mContext).numColumns; @@ -270,6 +375,19 @@ public class RestoreDbTaskTest { } } + private int[] generateOldWidgetIds(AppWidgetHost host) { + // generate some widget ids in case there are none + host.allocateAppWidgetId(); + host.allocateAppWidgetId(); + return host.getAppWidgetIds(); + } + + private int[] generateNewWidgetIds(AppWidgetHost host, int[] oldWidgetIds) { + // map as many new ids as old ids + return Arrays.stream(oldWidgetIds) + .map(id -> host.allocateAppWidgetId()).toArray(); + } + private class MyModelDbController extends ModelDbController { public final LongSparseArray<UserHandle> users = new LongSparseArray<>(); @@ -285,4 +403,10 @@ public class RestoreDbTaskTest { return index >= 0 ? users.keyAt(index) : -1; } } + + private void setRestoredAppWidgetIds(Context context, int[] oldIds, int[] newIds) { + LauncherPrefs.get(context).putSync( + OLD_APP_WIDGET_IDS.to(IntArray.wrap(oldIds).toConcatString()), + APP_WIDGET_IDS.to(IntArray.wrap(newIds).toConcatString())); + } } diff --git a/tests/src/com/android/launcher3/responsive/AllAppsSpecsTest.kt b/tests/src/com/android/launcher3/responsive/AllAppsSpecsTest.kt index cd95e99b99..c99da96917 100644 --- a/tests/src/com/android/launcher3/responsive/AllAppsSpecsTest.kt +++ b/tests/src/com/android/launcher3/responsive/AllAppsSpecsTest.kt @@ -21,6 +21,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import androidx.test.platform.app.InstrumentationRegistry import com.android.launcher3.AbstractDeviceProfileTest +import com.android.launcher3.responsive.ResponsiveSpec.Companion.ResponsiveSpecType import com.android.launcher3.tests.R as TestR import com.android.launcher3.util.TestResourceHelper import com.google.common.truth.Truth.assertThat @@ -32,22 +33,30 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class AllAppsSpecsTest : AbstractDeviceProfileTest() { override val runningContext: Context = InstrumentationRegistry.getInstrumentation().context + val deviceSpec = deviceSpecs["phone"]!! + val aspectRatio = deviceSpec.naturalSize.first.toFloat() / deviceSpec.naturalSize.second @Before fun setup() { - initializeVarsForPhone(deviceSpecs["phone"]!!) + initializeVarsForPhone(deviceSpec) } @Test fun parseValidFile() { val allAppsSpecs = - AllAppsSpecs.create(TestResourceHelper(context!!, TestR.xml.valid_all_apps_file)) - assertThat(allAppsSpecs.heightSpecs.size).isEqualTo(1) - assertThat(allAppsSpecs.heightSpecs[0].toString()) + ResponsiveSpecsProvider.create( + TestResourceHelper(context, TestR.xml.valid_all_apps_file), + ResponsiveSpecType.AllApps + ) + + val specs = allAppsSpecs.getSpecsByAspectRatio(aspectRatio) + assertThat(specs.heightSpecs.size).isEqualTo(1) + assertThat(specs.heightSpecs[0].toString()) .isEqualTo( - "AllAppsSpec(" + + "ResponsiveSpec(" + "maxAvailableSize=26247, " + - "specType=HEIGHT, " + + "dimensionType=HEIGHT, " + + "specType=AllApps, " + "startPadding=SizeSpec(fixedSize=0.0, " + "ofAvailableSpace=0.0, " + "ofRemainderSpace=0.0, " + @@ -71,12 +80,13 @@ class AllAppsSpecsTest : AbstractDeviceProfileTest() { ")" ) - assertThat(allAppsSpecs.widthSpecs.size).isEqualTo(1) - assertThat(allAppsSpecs.widthSpecs[0].toString()) + assertThat(specs.widthSpecs.size).isEqualTo(1) + assertThat(specs.widthSpecs[0].toString()) .isEqualTo( - "AllAppsSpec(" + + "ResponsiveSpec(" + "maxAvailableSize=26247, " + - "specType=WIDTH, " + + "dimensionType=WIDTH, " + + "specType=AllApps, " + "startPadding=SizeSpec(fixedSize=0.0, " + "ofAvailableSpace=0.0, " + "ofRemainderSpace=0.0, " + @@ -103,16 +113,25 @@ class AllAppsSpecsTest : AbstractDeviceProfileTest() { @Test(expected = IllegalStateException::class) fun parseInvalidFile_missingTag_throwsError() { - AllAppsSpecs.create(TestResourceHelper(context!!, TestR.xml.invalid_all_apps_file_case_1)) + ResponsiveSpecsProvider.create( + TestResourceHelper(context, TestR.xml.invalid_all_apps_file_case_1), + ResponsiveSpecType.AllApps + ) } @Test(expected = IllegalStateException::class) fun parseInvalidFile_moreThanOneValuePerTag_throwsError() { - AllAppsSpecs.create(TestResourceHelper(context!!, TestR.xml.invalid_all_apps_file_case_2)) + ResponsiveSpecsProvider.create( + TestResourceHelper(context, TestR.xml.invalid_all_apps_file_case_2), + ResponsiveSpecType.AllApps + ) } @Test(expected = IllegalStateException::class) fun parseInvalidFile_valueBiggerThan1_throwsError() { - AllAppsSpecs.create(TestResourceHelper(context!!, TestR.xml.invalid_all_apps_file_case_3)) + ResponsiveSpecsProvider.create( + TestResourceHelper(context, TestR.xml.invalid_all_apps_file_case_3), + ResponsiveSpecType.AllApps + ) } } diff --git a/tests/src/com/android/launcher3/responsive/CalculatedAllAppsSpecTest.kt b/tests/src/com/android/launcher3/responsive/CalculatedAllAppsSpecTest.kt index 0f12e58a33..1cc5ed2500 100644 --- a/tests/src/com/android/launcher3/responsive/CalculatedAllAppsSpecTest.kt +++ b/tests/src/com/android/launcher3/responsive/CalculatedAllAppsSpecTest.kt @@ -21,6 +21,8 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import androidx.test.platform.app.InstrumentationRegistry import com.android.launcher3.AbstractDeviceProfileTest +import com.android.launcher3.responsive.ResponsiveSpec.Companion.ResponsiveSpecType +import com.android.launcher3.responsive.ResponsiveSpec.DimensionType import com.android.launcher3.tests.R as TestR import com.android.launcher3.util.TestResourceHelper import com.google.common.truth.Truth.assertThat @@ -40,6 +42,7 @@ class CalculatedAllAppsSpecTest : AbstractDeviceProfileTest() { @Test fun normalPhone_copiesFromWorkspace() { val deviceSpec = deviceSpecs["phone"]!! + val aspectRatio = deviceSpec.naturalSize.first.toFloat() / deviceSpec.naturalSize.second initializeVarsForPhone(deviceSpec) val availableWidth = deviceSpec.naturalSize.first @@ -48,14 +51,30 @@ class CalculatedAllAppsSpecTest : AbstractDeviceProfileTest() { val availableHeight = deviceSpec.naturalSize.second - deviceSpec.statusBarNaturalPx - 495 val workspaceSpecs = - WorkspaceSpecs.create(TestResourceHelper(context!!, TestR.xml.valid_workspace_file)) - val widthSpec = workspaceSpecs.getCalculatedWidthSpec(4, availableWidth) - val heightSpec = workspaceSpecs.getCalculatedHeightSpec(5, availableHeight) + ResponsiveSpecsProvider.create( + TestResourceHelper(context, TestR.xml.valid_workspace_file), + ResponsiveSpecType.Workspace + ) + val widthSpec = + workspaceSpecs.getCalculatedSpec(aspectRatio, DimensionType.WIDTH, 4, availableWidth) + val heightSpec = + workspaceSpecs.getCalculatedSpec(aspectRatio, DimensionType.HEIGHT, 5, availableHeight) val allAppsSpecs = - AllAppsSpecs.create(TestResourceHelper(context!!, TestR.xml.valid_all_apps_file)) + ResponsiveSpecsProvider.create( + TestResourceHelper(context, TestR.xml.valid_all_apps_file), + ResponsiveSpecType.AllApps + ) - with(allAppsSpecs.getCalculatedWidthSpec(4, availableWidth, widthSpec)) { + with( + allAppsSpecs.getCalculatedSpec( + aspectRatio, + DimensionType.WIDTH, + 4, + availableWidth, + widthSpec + ) + ) { assertThat(availableSpace).isEqualTo(availableWidth) assertThat(cells).isEqualTo(4) assertThat(startPaddingPx).isEqualTo(widthSpec.startPaddingPx) @@ -64,7 +83,15 @@ class CalculatedAllAppsSpecTest : AbstractDeviceProfileTest() { assertThat(cellSizePx).isEqualTo(widthSpec.cellSizePx) } - with(allAppsSpecs.getCalculatedHeightSpec(5, availableHeight, heightSpec)) { + with( + allAppsSpecs.getCalculatedSpec( + aspectRatio, + DimensionType.HEIGHT, + 5, + availableHeight, + heightSpec + ) + ) { assertThat(availableSpace).isEqualTo(availableHeight) assertThat(cells).isEqualTo(5) assertThat(startPaddingPx).isEqualTo(0) diff --git a/tests/src/com/android/launcher3/responsive/CalculatedFolderSpecsTest.kt b/tests/src/com/android/launcher3/responsive/CalculatedFolderSpecTest.kt index f2a269a607..c4e2d2a328 100644 --- a/tests/src/com/android/launcher3/responsive/CalculatedFolderSpecsTest.kt +++ b/tests/src/com/android/launcher3/responsive/CalculatedFolderSpecTest.kt @@ -21,6 +21,8 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import androidx.test.platform.app.InstrumentationRegistry import com.android.launcher3.AbstractDeviceProfileTest +import com.android.launcher3.responsive.ResponsiveSpec.Companion.ResponsiveSpecType +import com.android.launcher3.responsive.ResponsiveSpec.DimensionType import com.android.launcher3.tests.R import com.android.launcher3.util.TestResourceHelper import com.google.common.truth.Truth.assertThat @@ -30,10 +32,10 @@ import org.junit.runner.RunWith @SmallTest @RunWith(AndroidJUnit4::class) -class CalculatedFolderSpecsTest : AbstractDeviceProfileTest() { +class CalculatedFolderSpecTest : AbstractDeviceProfileTest() { override val runningContext: Context = InstrumentationRegistry.getInstrumentation().context - private val deviceSpec = deviceSpecs["phone"]!! + private val aspectRatio = deviceSpec.naturalSize.first.toFloat() / deviceSpec.naturalSize.second @Before fun setup() { @@ -45,22 +47,37 @@ class CalculatedFolderSpecsTest : AbstractDeviceProfileTest() { val columns = 6 // Loading workspace specs - val resourceHelperWorkspace = TestResourceHelper(context!!, R.xml.valid_workspace_file) - val workspaceSpecs = WorkspaceSpecs.create(resourceHelperWorkspace) + val resourceHelperWorkspace = TestResourceHelper(context, R.xml.valid_workspace_file) + val workspaceSpecs = + ResponsiveSpecsProvider.create(resourceHelperWorkspace, ResponsiveSpecType.Workspace) // Loading folders specs - val resourceHelperFolder = TestResourceHelper(context!!, R.xml.valid_folders_specs) - val folderSpecs = FolderSpecs.create(resourceHelperFolder) + val resourceHelperFolder = TestResourceHelper(context, R.xml.valid_folders_specs) + val folderSpecs = + ResponsiveSpecsProvider.create(resourceHelperFolder, ResponsiveSpecType.Folder) + val specs = folderSpecs.getSpecsByAspectRatio(aspectRatio) - assertThat(folderSpecs.widthSpecs.size).isEqualTo(2) - assertThat(folderSpecs.widthSpecs[0].cellSize.matchWorkspace).isEqualTo(true) - assertThat(folderSpecs.widthSpecs[1].cellSize.matchWorkspace).isEqualTo(false) + assertThat(specs.widthSpecs.size).isEqualTo(2) + assertThat(specs.widthSpecs[0].cellSize.matchWorkspace).isEqualTo(true) + assertThat(specs.widthSpecs[1].cellSize.matchWorkspace).isEqualTo(false) // Validate width spec <= 800 var availableWidth = deviceSpec.naturalSize.first - var calculatedWorkspace = workspaceSpecs.getCalculatedWidthSpec(columns, availableWidth) + var calculatedWorkspace = + workspaceSpecs.getCalculatedSpec( + aspectRatio, + DimensionType.WIDTH, + columns, + availableWidth + ) var calculatedWidthFolderSpec = - folderSpecs.getCalculatedWidthSpec(columns, availableWidth, calculatedWorkspace) + folderSpecs.getCalculatedSpec( + aspectRatio, + DimensionType.WIDTH, + columns, + availableWidth, + calculatedWorkspace + ) with(calculatedWidthFolderSpec) { assertThat(availableSpace).isEqualTo(availableWidth) assertThat(cells).isEqualTo(columns) @@ -72,9 +89,21 @@ class CalculatedFolderSpecsTest : AbstractDeviceProfileTest() { // Validate width spec > 800 availableWidth = 2000.dpToPx() - calculatedWorkspace = workspaceSpecs.getCalculatedWidthSpec(columns, availableWidth) + calculatedWorkspace = + workspaceSpecs.getCalculatedSpec( + aspectRatio, + DimensionType.WIDTH, + columns, + availableWidth + ) calculatedWidthFolderSpec = - folderSpecs.getCalculatedWidthSpec(columns, availableWidth, calculatedWorkspace) + folderSpecs.getCalculatedSpec( + aspectRatio, + DimensionType.WIDTH, + columns, + availableWidth, + calculatedWorkspace + ) with(calculatedWidthFolderSpec) { assertThat(availableSpace).isEqualTo(availableWidth) assertThat(cells).isEqualTo(columns) @@ -94,20 +123,35 @@ class CalculatedFolderSpecsTest : AbstractDeviceProfileTest() { val rows = 5 // Loading workspace specs - val resourceHelperWorkspace = TestResourceHelper(context!!, R.xml.valid_workspace_file) - val workspaceSpecs = WorkspaceSpecs.create(resourceHelperWorkspace) + val resourceHelperWorkspace = TestResourceHelper(context, R.xml.valid_workspace_file) + val workspaceSpecs = + ResponsiveSpecsProvider.create(resourceHelperWorkspace, ResponsiveSpecType.Workspace) // Loading folders specs - val resourceHelperFolder = TestResourceHelper(context!!, R.xml.valid_folders_specs) - val folderSpecs = FolderSpecs.create(resourceHelperFolder) + val resourceHelperFolder = TestResourceHelper(context, R.xml.valid_folders_specs) + val folderSpecs = + ResponsiveSpecsProvider.create(resourceHelperFolder, ResponsiveSpecType.Folder) + val specs = folderSpecs.getSpecsByAspectRatio(aspectRatio) - assertThat(folderSpecs.heightSpecs.size).isEqualTo(1) - assertThat(folderSpecs.heightSpecs[0].cellSize.matchWorkspace).isEqualTo(true) + assertThat(specs.heightSpecs.size).isEqualTo(1) + assertThat(specs.heightSpecs[0].cellSize.matchWorkspace).isEqualTo(true) // Validate height spec - val calculatedWorkspace = workspaceSpecs.getCalculatedHeightSpec(rows, availableHeight) + val calculatedWorkspace = + workspaceSpecs.getCalculatedSpec( + aspectRatio, + DimensionType.HEIGHT, + rows, + availableHeight + ) val calculatedFolderSpec = - folderSpecs.getCalculatedHeightSpec(rows, availableHeight, calculatedWorkspace) + folderSpecs.getCalculatedSpec( + aspectRatio, + DimensionType.HEIGHT, + rows, + availableHeight, + calculatedWorkspace + ) with(calculatedFolderSpec) { assertThat(availableSpace).isEqualTo(availableHeight) assertThat(cells).isEqualTo(rows) diff --git a/tests/src/com/android/launcher3/responsive/CalculatedHotseatSpecTest.kt b/tests/src/com/android/launcher3/responsive/CalculatedHotseatSpecTest.kt index 0ecf7bae5a..1a564aced5 100644 --- a/tests/src/com/android/launcher3/responsive/CalculatedHotseatSpecTest.kt +++ b/tests/src/com/android/launcher3/responsive/CalculatedHotseatSpecTest.kt @@ -21,6 +21,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import androidx.test.platform.app.InstrumentationRegistry import com.android.launcher3.AbstractDeviceProfileTest +import com.android.launcher3.responsive.ResponsiveSpec.DimensionType import com.android.launcher3.tests.R as TestR import com.android.launcher3.util.TestResourceHelper import com.google.common.truth.Truth.assertThat @@ -31,6 +32,8 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class CalculatedHotseatSpecTest : AbstractDeviceProfileTest() { override val runningContext: Context = InstrumentationRegistry.getInstrumentation().context + val deviceSpec = deviceSpecs["phone"]!! + val aspectRatio = deviceSpec.naturalSize.first.toFloat() / deviceSpec.naturalSize.second /** * This test tests: @@ -38,18 +41,23 @@ class CalculatedHotseatSpecTest : AbstractDeviceProfileTest() { */ @Test fun normalPhone_returnsSecondBreakpointSpec() { - val deviceSpec = deviceSpecs["phone"]!! initializeVarsForPhone(deviceSpec) // Hotseat uses the whole device height val availableHeight = deviceSpec.naturalSize.second - val hotseatSpecs = - HotseatSpecs.create(TestResourceHelper(context!!, TestR.xml.valid_hotseat_file)) - val heightSpec = hotseatSpecs.getCalculatedHeightSpec(availableHeight) + val hotseatSpecsProvider = + HotseatSpecsProvider.create(TestResourceHelper(context, TestR.xml.valid_hotseat_file)) + val heightSpec = + hotseatSpecsProvider.getCalculatedSpec( + aspectRatio, + DimensionType.HEIGHT, + availableHeight + ) assertThat(heightSpec.availableSpace).isEqualTo(availableHeight) assertThat(heightSpec.hotseatQsbSpace).isEqualTo(95) + assertThat(heightSpec.edgePadding).isEqualTo(126) } /** @@ -58,18 +66,46 @@ class CalculatedHotseatSpecTest : AbstractDeviceProfileTest() { */ @Test fun smallPhone_returnsFirstBreakpointSpec() { - val deviceSpec = deviceSpecs["phone"]!! deviceSpec.densityDpi = 540 // larger display size initializeVarsForPhone(deviceSpec) // Hotseat uses the whole device height val availableHeight = deviceSpec.naturalSize.second - val hotseatSpecs = - HotseatSpecs.create(TestResourceHelper(context!!, TestR.xml.valid_hotseat_file)) - val heightSpec = hotseatSpecs.getCalculatedHeightSpec(availableHeight) + val hotseatSpecsProvider = + HotseatSpecsProvider.create(TestResourceHelper(context, TestR.xml.valid_hotseat_file)) + val heightSpec = + hotseatSpecsProvider.getCalculatedSpec( + aspectRatio, + DimensionType.HEIGHT, + availableHeight + ) assertThat(heightSpec.availableSpace).isEqualTo(availableHeight) assertThat(heightSpec.hotseatQsbSpace).isEqualTo(81) + assertThat(heightSpec.edgePadding).isEqualTo(162) + } + + /** + * This test tests: + * - (width spec) gets the correct breakpoint from the XML - skips the first breakpoint + */ + @Test + fun normalPhoneLandscape_returnsSecondBreakpointSpec() { + initializeVarsForPhone(deviceSpec, isVerticalBar = true) + + // Hotseat uses the whole device width + val availableWidth = deviceSpec.naturalSize.second + + val hotseatSpecsProvider = + HotseatSpecsProvider.create( + TestResourceHelper(context, TestR.xml.valid_hotseat_land_file) + ) + val widthSpec = + hotseatSpecsProvider.getCalculatedSpec(aspectRatio, DimensionType.WIDTH, availableWidth) + + assertThat(widthSpec.availableSpace).isEqualTo(availableWidth) + assertThat(widthSpec.hotseatQsbSpace).isEqualTo(0) + assertThat(widthSpec.edgePadding).isEqualTo(168) } } diff --git a/tests/src/com/android/launcher3/responsive/CalculatedWorkspaceSpecTest.kt b/tests/src/com/android/launcher3/responsive/CalculatedWorkspaceSpecTest.kt index 0af694e000..0c5d347412 100644 --- a/tests/src/com/android/launcher3/responsive/CalculatedWorkspaceSpecTest.kt +++ b/tests/src/com/android/launcher3/responsive/CalculatedWorkspaceSpecTest.kt @@ -21,6 +21,8 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import androidx.test.platform.app.InstrumentationRegistry import com.android.launcher3.AbstractDeviceProfileTest +import com.android.launcher3.responsive.ResponsiveSpec.Companion.ResponsiveSpecType +import com.android.launcher3.responsive.ResponsiveSpec.DimensionType import com.android.launcher3.tests.R as TestR import com.android.launcher3.util.TestResourceHelper import com.google.common.truth.Truth.assertThat @@ -41,6 +43,7 @@ class CalculatedWorkspaceSpecTest : AbstractDeviceProfileTest() { @Test fun normalPhone_returnsThirdBreakpointSpec() { val deviceSpec = deviceSpecs["phone"]!! + val aspectRatio = deviceSpec.naturalSize.first.toFloat() / deviceSpec.naturalSize.second initializeVarsForPhone(deviceSpec) val availableWidth = deviceSpec.naturalSize.first @@ -49,9 +52,14 @@ class CalculatedWorkspaceSpecTest : AbstractDeviceProfileTest() { val availableHeight = deviceSpec.naturalSize.second - deviceSpec.statusBarNaturalPx - 495 val workspaceSpecs = - WorkspaceSpecs.create(TestResourceHelper(context!!, TestR.xml.valid_workspace_file)) - val widthSpec = workspaceSpecs.getCalculatedWidthSpec(4, availableWidth) - val heightSpec = workspaceSpecs.getCalculatedHeightSpec(5, availableHeight) + ResponsiveSpecsProvider.create( + TestResourceHelper(context, TestR.xml.valid_workspace_file), + ResponsiveSpecType.Workspace + ) + val widthSpec = + workspaceSpecs.getCalculatedSpec(aspectRatio, DimensionType.WIDTH, 4, availableWidth) + val heightSpec = + workspaceSpecs.getCalculatedSpec(aspectRatio, DimensionType.HEIGHT, 5, availableHeight) assertThat(widthSpec.availableSpace).isEqualTo(availableWidth) assertThat(widthSpec.cells).isEqualTo(4) @@ -77,6 +85,7 @@ class CalculatedWorkspaceSpecTest : AbstractDeviceProfileTest() { @Test fun smallPhone_returnsFirstBreakpointSpec() { val deviceSpec = deviceSpecs["phone"]!! + val aspectRatio = deviceSpec.naturalSize.first.toFloat() / deviceSpec.naturalSize.second deviceSpec.densityDpi = 540 // larger display size initializeVarsForPhone(deviceSpec) @@ -86,9 +95,56 @@ class CalculatedWorkspaceSpecTest : AbstractDeviceProfileTest() { val availableHeight = deviceSpec.naturalSize.second - deviceSpec.statusBarNaturalPx - 640 val workspaceSpecs = - WorkspaceSpecs.create(TestResourceHelper(context!!, TestR.xml.valid_workspace_file)) - val widthSpec = workspaceSpecs.getCalculatedWidthSpec(4, availableWidth) - val heightSpec = workspaceSpecs.getCalculatedHeightSpec(5, availableHeight) + ResponsiveSpecsProvider.create( + TestResourceHelper(context, TestR.xml.valid_workspace_file), + ResponsiveSpecType.Workspace + ) + val widthSpec = + workspaceSpecs.getCalculatedSpec(aspectRatio, DimensionType.WIDTH, 4, availableWidth) + val heightSpec = + workspaceSpecs.getCalculatedSpec(aspectRatio, DimensionType.HEIGHT, 5, availableHeight) + + assertThat(widthSpec.availableSpace).isEqualTo(availableWidth) + assertThat(widthSpec.cells).isEqualTo(4) + assertThat(widthSpec.startPaddingPx).isEqualTo(74) + assertThat(widthSpec.endPaddingPx).isEqualTo(74) + assertThat(widthSpec.gutterPx).isEqualTo(54) + assertThat(widthSpec.cellSizePx).isEqualTo(193) + + assertThat(heightSpec.availableSpace).isEqualTo(availableHeight) + assertThat(heightSpec.cells).isEqualTo(5) + assertThat(heightSpec.startPaddingPx).isEqualTo(0) + assertThat(heightSpec.endPaddingPx).isEqualTo(108) + assertThat(heightSpec.gutterPx).isEqualTo(54) + assertThat(heightSpec.cellSizePx).isEqualTo(260) + } + + /** + * This test tests: + * - (height spec) gets the correct breakpoint from the XML - use the first breakpoint + * - (height spec) do the correct calculations for remainder space and fixed size + * - (width spec) do the correct calculations for remainder space and fixed size + */ + @Test + fun smallPhone_returnsFirstBreakpointSpec_unsortedFile() { + val deviceSpec = deviceSpecs["phone"]!! + val aspectRatio = deviceSpec.naturalSize.first.toFloat() / deviceSpec.naturalSize.second + deviceSpec.densityDpi = 540 // larger display size + initializeVarsForPhone(deviceSpec) + + val availableWidth = deviceSpec.naturalSize.first + // Hotseat size is roughly 640px on a real device, + // it doesn't need to be precise on unit tests + val availableHeight = deviceSpec.naturalSize.second - deviceSpec.statusBarNaturalPx - 640 + val workspaceSpecs = + ResponsiveSpecsProvider.create( + TestResourceHelper(context, TestR.xml.valid_workspace_unsorted_file), + ResponsiveSpecType.Workspace + ) + val widthSpec = + workspaceSpecs.getCalculatedSpec(aspectRatio, DimensionType.WIDTH, 4, availableWidth) + val heightSpec = + workspaceSpecs.getCalculatedSpec(aspectRatio, DimensionType.HEIGHT, 5, availableHeight) assertThat(widthSpec.availableSpace).isEqualTo(availableWidth) assertThat(widthSpec.cells).isEqualTo(4) diff --git a/tests/src/com/android/launcher3/responsive/FolderSpecsTest.kt b/tests/src/com/android/launcher3/responsive/FolderSpecTest.kt index 4b05949e35..5cfa49fb49 100644 --- a/tests/src/com/android/launcher3/responsive/FolderSpecsTest.kt +++ b/tests/src/com/android/launcher3/responsive/FolderSpecTest.kt @@ -21,7 +21,8 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import androidx.test.platform.app.InstrumentationRegistry import com.android.launcher3.AbstractDeviceProfileTest -import com.android.launcher3.responsive.ResponsiveSpec.SpecType +import com.android.launcher3.responsive.ResponsiveSpec.Companion.ResponsiveSpecType +import com.android.launcher3.responsive.ResponsiveSpec.DimensionType import com.android.launcher3.tests.R import com.android.launcher3.util.TestResourceHelper import com.google.common.truth.Truth.assertThat @@ -31,33 +32,38 @@ import org.junit.runner.RunWith @SmallTest @RunWith(AndroidJUnit4::class) -class FolderSpecsTest : AbstractDeviceProfileTest() { +class FolderSpecTest : AbstractDeviceProfileTest() { override val runningContext: Context = InstrumentationRegistry.getInstrumentation().context + val deviceSpec = deviceSpecs["tablet"]!! + val aspectRatio = deviceSpec.naturalSize.first.toFloat() / deviceSpec.naturalSize.second @Before fun setup() { - initializeVarsForPhone(deviceSpecs["tablet"]!!) + initializeVarsForPhone(deviceSpec) } @Test fun parseValidFile() { - val resourceHelper = TestResourceHelper(context!!, R.xml.valid_folders_specs) - val folderSpecs = FolderSpecs.create(resourceHelper) + val resourceHelper = TestResourceHelper(context, R.xml.valid_folders_specs) + val folderSpecs = ResponsiveSpecsProvider.create(resourceHelper, ResponsiveSpecType.Folder) + val specs = folderSpecs.getSpecsByAspectRatio(aspectRatio) val sizeSpec16 = SizeSpec(16f.dpToPx()) val widthSpecsExpected = listOf( - FolderSpec( + ResponsiveSpec( maxAvailableSize = 800.dpToPx(), - specType = SpecType.WIDTH, + dimensionType = DimensionType.WIDTH, + specType = ResponsiveSpecType.Folder, startPadding = sizeSpec16, endPadding = sizeSpec16, gutter = sizeSpec16, cellSize = SizeSpec(matchWorkspace = true) ), - FolderSpec( + ResponsiveSpec( maxAvailableSize = 9999.dpToPx(), - specType = SpecType.WIDTH, + dimensionType = DimensionType.WIDTH, + specType = ResponsiveSpecType.Folder, startPadding = sizeSpec16, endPadding = sizeSpec16, gutter = sizeSpec16, @@ -66,45 +72,46 @@ class FolderSpecsTest : AbstractDeviceProfileTest() { ) val heightSpecsExpected = - FolderSpec( + ResponsiveSpec( maxAvailableSize = 9999.dpToPx(), - specType = SpecType.HEIGHT, + dimensionType = DimensionType.HEIGHT, + specType = ResponsiveSpecType.Folder, startPadding = SizeSpec(24f.dpToPx()), endPadding = SizeSpec(64f.dpToPx()), gutter = sizeSpec16, cellSize = SizeSpec(matchWorkspace = true) ) - assertThat(folderSpecs.widthSpecs.size).isEqualTo(widthSpecsExpected.size) - assertThat(folderSpecs.widthSpecs[0]).isEqualTo(widthSpecsExpected[0]) - assertThat(folderSpecs.widthSpecs[1]).isEqualTo(widthSpecsExpected[1]) + assertThat(specs.widthSpecs.size).isEqualTo(widthSpecsExpected.size) + assertThat(specs.widthSpecs[0]).isEqualTo(widthSpecsExpected[0]) + assertThat(specs.widthSpecs[1]).isEqualTo(widthSpecsExpected[1]) - assertThat(folderSpecs.heightSpecs.size).isEqualTo(1) - assertThat(folderSpecs.heightSpecs[0]).isEqualTo(heightSpecsExpected) + assertThat(specs.heightSpecs.size).isEqualTo(1) + assertThat(specs.heightSpecs[0]).isEqualTo(heightSpecsExpected) } @Test(expected = IllegalStateException::class) fun parseInvalidFile_missingTag_throwsError() { - val resourceHelper = TestResourceHelper(context!!, R.xml.invalid_folders_specs_1) - FolderSpecs.create(resourceHelper) + val resourceHelper = TestResourceHelper(context, R.xml.invalid_folders_specs_1) + ResponsiveSpecsProvider.create(resourceHelper, ResponsiveSpecType.Folder) } @Test(expected = IllegalStateException::class) fun parseInvalidFile_moreThanOneValuePerTag_throwsError() { - val resourceHelper = TestResourceHelper(context!!, R.xml.invalid_folders_specs_2) - FolderSpecs.create(resourceHelper) + val resourceHelper = TestResourceHelper(context, R.xml.invalid_folders_specs_2) + ResponsiveSpecsProvider.create(resourceHelper, ResponsiveSpecType.Folder) } @Test(expected = IllegalStateException::class) fun parseInvalidFile_valueBiggerThan1_throwsError() { - val resourceHelper = TestResourceHelper(context!!, R.xml.invalid_folders_specs_3) - FolderSpecs.create(resourceHelper) + val resourceHelper = TestResourceHelper(context, R.xml.invalid_folders_specs_3) + ResponsiveSpecsProvider.create(resourceHelper, ResponsiveSpecType.Folder) } @Test(expected = IllegalStateException::class) fun parseInvalidFile_missingSpecs_throwsError() { - val resourceHelper = TestResourceHelper(context!!, R.xml.invalid_folders_specs_4) - FolderSpecs.create(resourceHelper) + val resourceHelper = TestResourceHelper(context, R.xml.invalid_folders_specs_4) + ResponsiveSpecsProvider.create(resourceHelper, ResponsiveSpecType.Folder) } @Test(expected = IllegalStateException::class) @@ -113,19 +120,27 @@ class FolderSpecsTest : AbstractDeviceProfileTest() { val cells = 3 val workspaceSpec = - WorkspaceSpec( + ResponsiveSpec( maxAvailableSize = availableSpace, - specType = SpecType.WIDTH, + dimensionType = DimensionType.WIDTH, + specType = ResponsiveSpecType.Folder, startPadding = SizeSpec(fixedSize = 10f), endPadding = SizeSpec(fixedSize = 10f), gutter = SizeSpec(fixedSize = 10f), cellSize = SizeSpec(fixedSize = 10f) ) - val calculatedWorkspaceSpec = CalculatedWorkspaceSpec(availableSpace, cells, workspaceSpec) - - val resourceHelper = TestResourceHelper(context!!, R.xml.invalid_folders_specs_5) - val folderSpecs = FolderSpecs.create(resourceHelper) - folderSpecs.getCalculatedWidthSpec(cells, availableSpace, calculatedWorkspaceSpec) + val calculatedWorkspaceSpec = + CalculatedResponsiveSpec(aspectRatio, availableSpace, cells, workspaceSpec) + + val resourceHelper = TestResourceHelper(context, R.xml.invalid_folders_specs_5) + val folderSpecs = ResponsiveSpecsProvider.create(resourceHelper, ResponsiveSpecType.Folder) + folderSpecs.getCalculatedSpec( + aspectRatio, + DimensionType.WIDTH, + cells, + availableSpace, + calculatedWorkspaceSpec + ) } @Test(expected = IllegalStateException::class) @@ -134,19 +149,27 @@ class FolderSpecsTest : AbstractDeviceProfileTest() { val cells = 3 val workspaceSpec = - WorkspaceSpec( + ResponsiveSpec( maxAvailableSize = availableSpace, - specType = SpecType.HEIGHT, + dimensionType = DimensionType.HEIGHT, + specType = ResponsiveSpecType.Folder, startPadding = SizeSpec(fixedSize = 10f), endPadding = SizeSpec(fixedSize = 10f), gutter = SizeSpec(fixedSize = 10f), cellSize = SizeSpec(fixedSize = 10f) ) - val calculatedWorkspaceSpec = CalculatedWorkspaceSpec(availableSpace, cells, workspaceSpec) - - val resourceHelper = TestResourceHelper(context!!, R.xml.invalid_folders_specs_5) - val folderSpecs = FolderSpecs.create(resourceHelper) - folderSpecs.getCalculatedHeightSpec(cells, availableSpace, calculatedWorkspaceSpec) + val calculatedWorkspaceSpec = + CalculatedResponsiveSpec(aspectRatio, availableSpace, cells, workspaceSpec) + + val resourceHelper = TestResourceHelper(context, R.xml.invalid_folders_specs_5) + val folderSpecs = ResponsiveSpecsProvider.create(resourceHelper, ResponsiveSpecType.Folder) + folderSpecs.getCalculatedSpec( + aspectRatio, + DimensionType.HEIGHT, + cells, + availableSpace, + calculatedWorkspaceSpec + ) } @Test @@ -155,20 +178,28 @@ class FolderSpecsTest : AbstractDeviceProfileTest() { val cells = 3 val workspaceSpec = - WorkspaceSpec( + ResponsiveSpec( maxAvailableSize = availableSpace, - specType = SpecType.WIDTH, + dimensionType = DimensionType.WIDTH, + specType = ResponsiveSpecType.Workspace, startPadding = SizeSpec(fixedSize = 10f), endPadding = SizeSpec(fixedSize = 10f), gutter = SizeSpec(fixedSize = 10f), cellSize = SizeSpec(fixedSize = 10f) ) - val calculatedWorkspaceSpec = CalculatedWorkspaceSpec(availableSpace, cells, workspaceSpec) + val calculatedWorkspaceSpec = + CalculatedResponsiveSpec(aspectRatio, availableSpace, cells, workspaceSpec) - val resourceHelper = TestResourceHelper(context!!, R.xml.valid_folders_specs) - val folderSpecs = FolderSpecs.create(resourceHelper) + val resourceHelper = TestResourceHelper(context, R.xml.valid_folders_specs) + val folderSpecs = ResponsiveSpecsProvider.create(resourceHelper, ResponsiveSpecType.Folder) val calculatedWidthSpec = - folderSpecs.getCalculatedWidthSpec(cells, availableSpace, calculatedWorkspaceSpec) + folderSpecs.getCalculatedSpec( + aspectRatio, + DimensionType.WIDTH, + cells, + availableSpace, + calculatedWorkspaceSpec + ) assertThat(calculatedWidthSpec.cells).isEqualTo(cells) assertThat(calculatedWidthSpec.availableSpace).isEqualTo(availableSpace) @@ -179,24 +210,32 @@ class FolderSpecsTest : AbstractDeviceProfileTest() { } @Test(expected = IllegalStateException::class) - fun retrievesCalculatedWidthSpec_invalidCalculatedWorkspaceSpecType_throwsError() { + fun retrievesCalculatedWidthSpec_invalidCalculatedResponsiveSpecType_throwsError() { val availableSpace = 10.dpToPx() val cells = 3 val workspaceSpec = - WorkspaceSpec( + ResponsiveSpec( maxAvailableSize = availableSpace, - specType = SpecType.HEIGHT, + dimensionType = DimensionType.HEIGHT, + specType = ResponsiveSpecType.Folder, startPadding = SizeSpec(fixedSize = 10f), endPadding = SizeSpec(fixedSize = 10f), gutter = SizeSpec(fixedSize = 10f), cellSize = SizeSpec(fixedSize = 10f) ) - val calculatedWorkspaceSpec = CalculatedWorkspaceSpec(availableSpace, cells, workspaceSpec) - - val resourceHelper = TestResourceHelper(context!!, R.xml.valid_folders_specs) - val folderSpecs = FolderSpecs.create(resourceHelper) - folderSpecs.getCalculatedWidthSpec(cells, availableSpace, calculatedWorkspaceSpec) + val calculatedWorkspaceSpec = + CalculatedResponsiveSpec(aspectRatio, availableSpace, cells, workspaceSpec) + + val resourceHelper = TestResourceHelper(context, R.xml.valid_folders_specs) + val folderSpecs = ResponsiveSpecsProvider.create(resourceHelper, ResponsiveSpecType.Folder) + folderSpecs.getCalculatedSpec( + aspectRatio, + DimensionType.WIDTH, + cells, + availableSpace, + calculatedWorkspaceSpec + ) } @Test @@ -205,20 +244,28 @@ class FolderSpecsTest : AbstractDeviceProfileTest() { val cells = 3 val workspaceSpec = - WorkspaceSpec( + ResponsiveSpec( maxAvailableSize = availableSpace, - specType = SpecType.HEIGHT, + dimensionType = DimensionType.HEIGHT, + specType = ResponsiveSpecType.Workspace, startPadding = SizeSpec(fixedSize = 10f), endPadding = SizeSpec(fixedSize = 10f), gutter = SizeSpec(fixedSize = 10f), cellSize = SizeSpec(fixedSize = 10f) ) - val calculatedWorkspaceSpec = CalculatedWorkspaceSpec(availableSpace, cells, workspaceSpec) + val calculatedWorkspaceSpec = + CalculatedResponsiveSpec(aspectRatio, availableSpace, cells, workspaceSpec) - val resourceHelper = TestResourceHelper(context!!, R.xml.valid_folders_specs) - val folderSpecs = FolderSpecs.create(resourceHelper) + val resourceHelper = TestResourceHelper(context, R.xml.valid_folders_specs) + val folderSpecs = ResponsiveSpecsProvider.create(resourceHelper, ResponsiveSpecType.Folder) val calculatedHeightSpec = - folderSpecs.getCalculatedHeightSpec(cells, availableSpace, calculatedWorkspaceSpec) + folderSpecs.getCalculatedSpec( + aspectRatio, + DimensionType.HEIGHT, + cells, + availableSpace, + calculatedWorkspaceSpec + ) assertThat(calculatedHeightSpec.cells).isEqualTo(cells) assertThat(calculatedHeightSpec.availableSpace).isEqualTo(availableSpace) @@ -229,23 +276,31 @@ class FolderSpecsTest : AbstractDeviceProfileTest() { } @Test(expected = IllegalStateException::class) - fun retrievesCalculatedHeightSpec_invalidCalculatedWorkspaceSpecType_throwsError() { + fun retrievesCalculatedHeightSpec_invalidCalculatedResponsiveSpecType_throwsError() { val availableSpace = 10.dpToPx() val cells = 3 val workspaceSpec = - WorkspaceSpec( + ResponsiveSpec( maxAvailableSize = availableSpace, - specType = SpecType.WIDTH, + dimensionType = DimensionType.WIDTH, + specType = ResponsiveSpecType.Folder, startPadding = SizeSpec(fixedSize = 10f), endPadding = SizeSpec(fixedSize = 10f), gutter = SizeSpec(fixedSize = 10f), cellSize = SizeSpec(fixedSize = 10f) ) - val calculatedWorkspaceSpec = CalculatedWorkspaceSpec(availableSpace, cells, workspaceSpec) - - val resourceHelper = TestResourceHelper(context!!, R.xml.valid_folders_specs) - val folderSpecs = FolderSpecs.create(resourceHelper) - folderSpecs.getCalculatedHeightSpec(cells, availableSpace, calculatedWorkspaceSpec) + val calculatedWorkspaceSpec = + CalculatedResponsiveSpec(aspectRatio, availableSpace, cells, workspaceSpec) + + val resourceHelper = TestResourceHelper(context, R.xml.valid_folders_specs) + val folderSpecs = ResponsiveSpecsProvider.create(resourceHelper, ResponsiveSpecType.Folder) + folderSpecs.getCalculatedSpec( + aspectRatio, + DimensionType.HEIGHT, + cells, + availableSpace, + calculatedWorkspaceSpec + ) } } diff --git a/tests/src/com/android/launcher3/responsive/HotseatSpecsProviderTest.kt b/tests/src/com/android/launcher3/responsive/HotseatSpecsProviderTest.kt new file mode 100644 index 0000000000..78cb1ac3ef --- /dev/null +++ b/tests/src/com/android/launcher3/responsive/HotseatSpecsProviderTest.kt @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.responsive + +import android.content.Context +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import androidx.test.platform.app.InstrumentationRegistry +import com.android.launcher3.AbstractDeviceProfileTest +import com.android.launcher3.responsive.ResponsiveSpec.Companion.ResponsiveSpecType +import com.android.launcher3.tests.R as TestR +import com.android.launcher3.util.TestResourceHelper +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class HotseatSpecsProviderTest : AbstractDeviceProfileTest() { + override val runningContext: Context = InstrumentationRegistry.getInstrumentation().context + val deviceSpec = deviceSpecs["phone"]!! + val aspectRatio = deviceSpec.naturalSize.first.toFloat() / deviceSpec.naturalSize.second + + @Before + fun setup() { + initializeVarsForPhone(deviceSpec) + } + + @Test + fun parseValidFile() { + val hotseatSpecsProvider = + HotseatSpecsProvider.create(TestResourceHelper(context, TestR.xml.valid_hotseat_file)) + val specs = hotseatSpecsProvider.getSpecsByAspectRatio(aspectRatio) + + val expectedHeightSpecs = + listOf( + HotseatSpec( + maxAvailableSize = 847.dpToPx(), + dimensionType = ResponsiveSpec.DimensionType.HEIGHT, + specType = ResponsiveSpecType.Hotseat, + hotseatQsbSpace = SizeSpec(24f.dpToPx()), + edgePadding = SizeSpec(48f.dpToPx()) + ), + HotseatSpec( + maxAvailableSize = 9999.dpToPx(), + dimensionType = ResponsiveSpec.DimensionType.HEIGHT, + specType = ResponsiveSpecType.Hotseat, + hotseatQsbSpace = SizeSpec(36f.dpToPx()), + edgePadding = SizeSpec(48f.dpToPx()) + ), + ) + + assertThat(specs.heightSpecs.size).isEqualTo(expectedHeightSpecs.size) + assertThat(specs.heightSpecs[0]).isEqualTo(expectedHeightSpecs[0]) + assertThat(specs.heightSpecs[1]).isEqualTo(expectedHeightSpecs[1]) + + assertThat(specs.widthSpecs.size).isEqualTo(0) + } + + @Test + fun parseValidLandscapeFile() { + val hotseatSpecsProvider = + HotseatSpecsProvider.create( + TestResourceHelper(context, TestR.xml.valid_hotseat_land_file) + ) + val specs = hotseatSpecsProvider.getSpecsByAspectRatio(aspectRatio) + assertThat(specs.heightSpecs.size).isEqualTo(0) + + val expectedWidthSpecs = + listOf( + HotseatSpec( + maxAvailableSize = 743.dpToPx(), + dimensionType = ResponsiveSpec.DimensionType.WIDTH, + specType = ResponsiveSpecType.Hotseat, + hotseatQsbSpace = SizeSpec(0f), + edgePadding = SizeSpec(48f.dpToPx()) + ), + HotseatSpec( + maxAvailableSize = 9999.dpToPx(), + dimensionType = ResponsiveSpec.DimensionType.WIDTH, + specType = ResponsiveSpecType.Hotseat, + hotseatQsbSpace = SizeSpec(0f), + edgePadding = SizeSpec(64f.dpToPx()) + ), + ) + + assertThat(specs.widthSpecs.size).isEqualTo(expectedWidthSpecs.size) + assertThat(specs.widthSpecs[0]).isEqualTo(expectedWidthSpecs[0]) + assertThat(specs.widthSpecs[1]).isEqualTo(expectedWidthSpecs[1]) + } + + @Test(expected = IllegalStateException::class) + fun parseInvalidFile_spaceIsNotFixedSize_throwsError() { + HotseatSpecsProvider.create( + TestResourceHelper(context, TestR.xml.invalid_hotseat_file_case_1) + ) + } + + @Test(expected = IllegalStateException::class) + fun parseInvalidFile_invalidFixedSize_throwsError() { + HotseatSpecsProvider.create( + TestResourceHelper(context, TestR.xml.invalid_hotseat_file_case_2) + ) + } +} diff --git a/tests/src/com/android/launcher3/responsive/HotseatSpecsTest.kt b/tests/src/com/android/launcher3/responsive/HotseatSpecsTest.kt deleted file mode 100644 index c764e47526..0000000000 --- a/tests/src/com/android/launcher3/responsive/HotseatSpecsTest.kt +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.launcher3.responsive - -import android.content.Context -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SmallTest -import androidx.test.platform.app.InstrumentationRegistry -import com.android.launcher3.AbstractDeviceProfileTest -import com.android.launcher3.tests.R as TestR -import com.android.launcher3.util.TestResourceHelper -import com.android.systemui.util.dpToPx -import com.google.common.truth.Truth.assertThat -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith - -@SmallTest -@RunWith(AndroidJUnit4::class) -class HotseatSpecsTest : AbstractDeviceProfileTest() { - override val runningContext: Context = InstrumentationRegistry.getInstrumentation().context - - @Before - fun setup() { - initializeVarsForPhone(deviceSpecs["phone"]!!) - } - - @Test - fun parseValidFile() { - val hotseatSpecs = - HotseatSpecs.create(TestResourceHelper(context!!, TestR.xml.valid_hotseat_file)) - assertThat(hotseatSpecs.specs.size).isEqualTo(2) - - val expectedSpecs = - listOf( - HotseatSpec( - maxAvailableSize = 847.dpToPx(), - specType = ResponsiveSpec.SpecType.HEIGHT, - hotseatQsbSpace = SizeSpec(24f.dpToPx()) - ), - HotseatSpec( - maxAvailableSize = 9999.dpToPx(), - specType = ResponsiveSpec.SpecType.HEIGHT, - hotseatQsbSpace = SizeSpec(36f.dpToPx()) - ), - ) - - assertThat(hotseatSpecs.specs.size).isEqualTo(expectedSpecs.size) - assertThat(hotseatSpecs.specs[0]).isEqualTo(expectedSpecs[0]) - assertThat(hotseatSpecs.specs[1]).isEqualTo(expectedSpecs[1]) - } - - @Test(expected = IllegalStateException::class) - fun parseInvalidFile_spaceIsNotFixedSize_throwsError() { - HotseatSpecs.create(TestResourceHelper(context!!, TestR.xml.invalid_hotseat_file_case_1)) - } -} diff --git a/tests/src/com/android/launcher3/responsive/ResponsiveCellSpecsProviderTest.kt b/tests/src/com/android/launcher3/responsive/ResponsiveCellSpecsProviderTest.kt new file mode 100644 index 0000000000..50cd358b7e --- /dev/null +++ b/tests/src/com/android/launcher3/responsive/ResponsiveCellSpecsProviderTest.kt @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.responsive + +import android.content.Context +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import androidx.test.platform.app.InstrumentationRegistry +import com.android.launcher3.AbstractDeviceProfileTest +import com.android.launcher3.responsive.ResponsiveSpec.Companion.ResponsiveSpecType +import com.android.launcher3.tests.R as TestR +import com.android.launcher3.util.TestResourceHelper +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class ResponsiveCellSpecsProviderTest : AbstractDeviceProfileTest() { + override val runningContext: Context = InstrumentationRegistry.getInstrumentation().context + val deviceSpec = deviceSpecs["phone"]!! + val aspectRatio = deviceSpec.naturalSize.first.toFloat() / deviceSpec.naturalSize.second + + @Before + fun setup() { + initializeVarsForPhone(deviceSpec) + } + + @Test + fun parseValidFile() { + val testResourceHelper = TestResourceHelper(context, TestR.xml.valid_cell_specs_file) + val provider = ResponsiveCellSpecsProvider.create(testResourceHelper) + + // Validate Portrait + val aspectRatioPortrait = 1.0f + val expectedPortraitSpecs = + listOf( + CellSpec( + maxAvailableSize = 606.dpToPx(), + dimensionType = ResponsiveSpec.DimensionType.HEIGHT, + specType = ResponsiveSpecType.Cell, + iconSize = SizeSpec(48f.dpToPx()), + iconTextSize = SizeSpec(12f.dpToPx()), + iconDrawablePadding = SizeSpec(8f.dpToPx()) + ), + CellSpec( + maxAvailableSize = 9999.dpToPx(), + dimensionType = ResponsiveSpec.DimensionType.HEIGHT, + specType = ResponsiveSpecType.Cell, + iconSize = SizeSpec(52f.dpToPx()), + iconTextSize = SizeSpec(12f.dpToPx()), + iconDrawablePadding = SizeSpec(11f.dpToPx()) + ) + ) + + val portraitSpecs = provider.getSpecsByAspectRatio(aspectRatioPortrait) + + assertThat(portraitSpecs.aspectRatio).isAtLeast(aspectRatioPortrait) + assertThat(portraitSpecs.widthSpecs.size).isEqualTo(0) + assertThat(portraitSpecs.heightSpecs.size).isEqualTo(2) + assertThat(portraitSpecs.heightSpecs[0]).isEqualTo(expectedPortraitSpecs[0]) + assertThat(portraitSpecs.heightSpecs[1]).isEqualTo(expectedPortraitSpecs[1]) + + // Validate Landscape + val aspectRatioLandscape = 1.051f + val expectedLandscapeSpec = + CellSpec( + maxAvailableSize = 9999.dpToPx(), + dimensionType = ResponsiveSpec.DimensionType.HEIGHT, + specType = ResponsiveSpecType.Cell, + iconSize = SizeSpec(52f.dpToPx()), + iconTextSize = SizeSpec(0f), + iconDrawablePadding = SizeSpec(0f) + ) + val landscapeSpecs = provider.getSpecsByAspectRatio(aspectRatioLandscape) + + assertThat(landscapeSpecs.aspectRatio).isAtLeast(aspectRatioLandscape) + assertThat(landscapeSpecs.widthSpecs.size).isEqualTo(0) + assertThat(landscapeSpecs.heightSpecs.size).isEqualTo(1) + assertThat(landscapeSpecs.heightSpecs[0]).isEqualTo(expectedLandscapeSpec) + } + + @Test(expected = IllegalStateException::class) + fun parseInvalidFile_IsNotFixedSizeOrMatchWorkspace_throwsError() { + ResponsiveCellSpecsProvider.create( + TestResourceHelper(context, TestR.xml.invalid_cell_specs_1) + ) + } + + @Test(expected = IllegalStateException::class) + fun parseInvalidFile_dimensionTypeIsNotHeight_throwsError() { + ResponsiveCellSpecsProvider.create( + TestResourceHelper(context, TestR.xml.invalid_cell_specs_2) + ) + } + + @Test(expected = IllegalStateException::class) + fun parseInvalidFile_invalidFixedSize_throwsError() { + ResponsiveCellSpecsProvider.create( + TestResourceHelper(context, TestR.xml.invalid_cell_specs_3) + ) + } +} diff --git a/tests/src/com/android/launcher3/responsive/ResponsiveSpecsProviderTest.kt b/tests/src/com/android/launcher3/responsive/ResponsiveSpecsProviderTest.kt new file mode 100644 index 0000000000..9681ca8bc9 --- /dev/null +++ b/tests/src/com/android/launcher3/responsive/ResponsiveSpecsProviderTest.kt @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.responsive + +import android.content.Context +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import androidx.test.platform.app.InstrumentationRegistry +import com.android.launcher3.AbstractDeviceProfileTest +import com.android.launcher3.responsive.ResponsiveSpec.Companion.ResponsiveSpecType +import com.android.launcher3.responsive.ResponsiveSpec.DimensionType +import com.android.launcher3.tests.R +import com.android.launcher3.util.TestResourceHelper +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class ResponsiveSpecsProviderTest : AbstractDeviceProfileTest() { + override val runningContext: Context = InstrumentationRegistry.getInstrumentation().context + val deviceSpec = deviceSpecs["tablet"]!! + var aspectRatio = deviceSpec.naturalSize.first.toFloat() / deviceSpec.naturalSize.second + + @Before + fun setup() { + initializeVarsForPhone(deviceSpec) + } + + @Test + fun parseValidFile() { + val resourceHelper = TestResourceHelper(context, R.xml.valid_responsive_spec_unsorted) + val provider = ResponsiveSpecsProvider.create(resourceHelper, ResponsiveSpecType.Workspace) + + // Validate Portrait + val aspectRatioPortrait = 1.0f + val portraitSpecs = provider.getSpecsByAspectRatio(aspectRatioPortrait) + + val (expectedPortWidthSpecs, expectedPortHeightSpecs) = expectedPortraitSpecs() + validateSpecs( + portraitSpecs, + aspectRatioPortrait, + expectedPortWidthSpecs, + expectedPortHeightSpecs + ) + + // Validate Landscape + val aspectRatioLandscape = 1.051f + val landscapeSpecs = provider.getSpecsByAspectRatio(aspectRatioLandscape) + + val (expectedLandWidthSpecs, expectedLandHeightSpecs) = expectedLandscapeSpecs() + validateSpecs( + landscapeSpecs, + aspectRatioLandscape, + expectedLandWidthSpecs, + expectedLandHeightSpecs + ) + + // Validate Extra Spec + val aspectRatioExtra = 10.1f + val extraSpecs = provider.getSpecsByAspectRatio(aspectRatioExtra) + + val expectedOtherWidthSpecs = + listOf( + ResponsiveSpec( + maxAvailableSize = 9999.dpToPx(), + dimensionType = DimensionType.WIDTH, + specType = ResponsiveSpecType.Workspace, + startPadding = SizeSpec(1f.dpToPx()), + endPadding = SizeSpec(1f.dpToPx()), + gutter = SizeSpec(8f.dpToPx()), + cellSize = SizeSpec(ofRemainderSpace = .25f) + ) + ) + + val expectedOtherHeightSpecs = + listOf( + ResponsiveSpec( + maxAvailableSize = 9999.dpToPx(), + dimensionType = DimensionType.HEIGHT, + specType = ResponsiveSpecType.Workspace, + startPadding = SizeSpec(2f.dpToPx()), + endPadding = SizeSpec(2f.dpToPx()), + gutter = SizeSpec(8f.dpToPx()), + cellSize = SizeSpec(ofRemainderSpace = .25f) + ), + ) + + validateSpecs( + extraSpecs, + aspectRatioExtra, + expectedOtherWidthSpecs, + expectedOtherHeightSpecs + ) + } + + @Test(expected = IllegalStateException::class) + fun parseValidFile_invalidAspectRatio_throwsError() { + val resourceHelper = TestResourceHelper(context, R.xml.valid_responsive_spec_unsorted) + val provider = ResponsiveSpecsProvider.create(resourceHelper, ResponsiveSpecType.Workspace) + provider.getSpecsByAspectRatio(0f) + } + + @Test(expected = InvalidResponsiveGridSpec::class) + fun parseInvalidFile_missingGroups_throwsError() { + val resourceHelper = TestResourceHelper(context, R.xml.invalid_responsive_spec_1) + ResponsiveSpecsProvider.create(resourceHelper, ResponsiveSpecType.Workspace) + } + + @Test(expected = InvalidResponsiveGridSpec::class) + fun parseInvalidFile_partialGroups_throwsError() { + val resourceHelper = TestResourceHelper(context, R.xml.invalid_responsive_spec_2) + ResponsiveSpecsProvider.create(resourceHelper, ResponsiveSpecType.Workspace) + } + + @Test(expected = IllegalStateException::class) + fun parseInvalidFile_invalidAspectRatio_throwsError() { + val resourceHelper = TestResourceHelper(context, R.xml.invalid_responsive_spec_3) + ResponsiveSpecsProvider.create(resourceHelper, ResponsiveSpecType.Workspace) + } + + @Test(expected = IllegalStateException::class) + fun parseInvalidFile_invalidRemainderSpace_throwsError() { + val resourceHelper = TestResourceHelper(context, R.xml.invalid_responsive_spec_4) + ResponsiveSpecsProvider.create(resourceHelper, ResponsiveSpecType.Workspace) + } + + @Test(expected = IllegalStateException::class) + fun parseInvalidFile_invalidAvailableSpace_throwsError() { + val resourceHelper = TestResourceHelper(context, R.xml.invalid_responsive_spec_5) + ResponsiveSpecsProvider.create(resourceHelper, ResponsiveSpecType.Workspace) + } + + @Test(expected = IllegalStateException::class) + fun parseInvalidFile_invalidFixedSize_throwsError() { + val resourceHelper = TestResourceHelper(context, R.xml.invalid_responsive_spec_6) + ResponsiveSpecsProvider.create(resourceHelper, ResponsiveSpecType.Workspace) + } + + private fun validateSpecs( + specs: ResponsiveSpecGroup<ResponsiveSpec>, + expectedAspectRatio: Float, + expectedWidthSpecs: List<ResponsiveSpec>, + expectedHeightSpecs: List<ResponsiveSpec> + ) { + assertThat(specs.aspectRatio).isAtLeast(expectedAspectRatio) + + assertThat(specs.widthSpecs.size).isEqualTo(expectedWidthSpecs.size) + specs.widthSpecs.forEachIndexed { index, responsiveSpec -> + assertThat(responsiveSpec).isEqualTo(expectedWidthSpecs[index]) + } + + assertThat(specs.heightSpecs.size).isEqualTo(expectedHeightSpecs.size) + specs.heightSpecs.forEachIndexed { index, responsiveSpec -> + assertThat(responsiveSpec).isEqualTo(expectedHeightSpecs[index]) + } + } + + private fun expectedPortraitSpecs(): Pair<List<ResponsiveSpec>, List<ResponsiveSpec>> { + val sizeSpec16 = SizeSpec(16f.dpToPx()) + val expectedWidthSpecs = + listOf( + ResponsiveSpec( + maxAvailableSize = 9999.dpToPx(), + dimensionType = DimensionType.WIDTH, + specType = ResponsiveSpecType.Workspace, + startPadding = SizeSpec(22f.dpToPx()), + endPadding = SizeSpec(22f.dpToPx()), + gutter = sizeSpec16, + cellSize = SizeSpec(ofRemainderSpace = .25f) + ) + ) + + val expectedHeightSpecs = + listOf( + ResponsiveSpec( + maxAvailableSize = 584.dpToPx(), + dimensionType = DimensionType.HEIGHT, + specType = ResponsiveSpecType.Workspace, + startPadding = SizeSpec(0f), + endPadding = SizeSpec(32f.dpToPx()), + gutter = sizeSpec16, + cellSize = SizeSpec(ofAvailableSpace = .15808f) + ), + ResponsiveSpec( + maxAvailableSize = 612.dpToPx(), + dimensionType = DimensionType.HEIGHT, + specType = ResponsiveSpecType.Workspace, + startPadding = SizeSpec(0f), + endPadding = SizeSpec(ofRemainderSpace = 1f), + gutter = sizeSpec16, + cellSize = SizeSpec(104f.dpToPx()) + ), + ResponsiveSpec( + maxAvailableSize = 9999.dpToPx(), + dimensionType = DimensionType.HEIGHT, + specType = ResponsiveSpecType.Workspace, + startPadding = SizeSpec(8f.dpToPx()), + endPadding = SizeSpec(ofRemainderSpace = 1f), + gutter = sizeSpec16, + cellSize = SizeSpec(104f.dpToPx()) + ), + ) + + return Pair(expectedWidthSpecs, expectedHeightSpecs) + } + + private fun expectedLandscapeSpecs(): Pair<List<ResponsiveSpec>, List<ResponsiveSpec>> { + val sizeSpec12 = SizeSpec(12f.dpToPx()) + val expectedWidthSpecs = + listOf( + ResponsiveSpec( + maxAvailableSize = 602.dpToPx(), + dimensionType = DimensionType.WIDTH, + specType = ResponsiveSpecType.Workspace, + startPadding = SizeSpec(0f.dpToPx()), + endPadding = SizeSpec(36f.dpToPx()), + gutter = sizeSpec12, + cellSize = SizeSpec(ofRemainderSpace = .25f) + ), + ResponsiveSpec( + maxAvailableSize = 716.dpToPx(), + dimensionType = DimensionType.WIDTH, + specType = ResponsiveSpecType.Workspace, + startPadding = SizeSpec(16f.dpToPx()), + endPadding = SizeSpec(64f.dpToPx()), + gutter = sizeSpec12, + cellSize = SizeSpec(ofRemainderSpace = .25f) + ), + ResponsiveSpec( + maxAvailableSize = 9999.dpToPx(), + dimensionType = DimensionType.WIDTH, + specType = ResponsiveSpecType.Workspace, + startPadding = SizeSpec(36f.dpToPx()), + endPadding = SizeSpec(80f.dpToPx()), + gutter = sizeSpec12, + cellSize = SizeSpec(ofRemainderSpace = .25f) + ) + ) + + val expectedHeightSpecs = + listOf( + ResponsiveSpec( + maxAvailableSize = 371.dpToPx(), + dimensionType = DimensionType.HEIGHT, + specType = ResponsiveSpecType.Workspace, + startPadding = SizeSpec(0f), + endPadding = SizeSpec(24f.dpToPx()), + gutter = sizeSpec12, + cellSize = SizeSpec(ofRemainderSpace = .25f) + ), + ResponsiveSpec( + maxAvailableSize = 9999.dpToPx(), + dimensionType = DimensionType.HEIGHT, + specType = ResponsiveSpecType.Workspace, + startPadding = SizeSpec(0f), + endPadding = SizeSpec(34f.dpToPx()), + gutter = sizeSpec12, + cellSize = SizeSpec(ofRemainderSpace = .25f) + ), + ) + + return Pair(expectedWidthSpecs, expectedHeightSpecs) + } +} diff --git a/tests/src/com/android/launcher3/responsive/WorkspaceSpecsTest.kt b/tests/src/com/android/launcher3/responsive/WorkspaceSpecsTest.kt index 0364069535..9781645602 100644 --- a/tests/src/com/android/launcher3/responsive/WorkspaceSpecsTest.kt +++ b/tests/src/com/android/launcher3/responsive/WorkspaceSpecsTest.kt @@ -21,6 +21,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import androidx.test.platform.app.InstrumentationRegistry import com.android.launcher3.AbstractDeviceProfileTest +import com.android.launcher3.responsive.ResponsiveSpec.Companion.ResponsiveSpecType import com.android.launcher3.tests.R as TestR import com.android.launcher3.util.TestResourceHelper import com.google.common.truth.Truth.assertThat @@ -32,22 +33,30 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class WorkspaceSpecsTest : AbstractDeviceProfileTest() { override val runningContext: Context = InstrumentationRegistry.getInstrumentation().context + val deviceSpec = deviceSpecs["phone"]!! + val aspectRatio = deviceSpec.naturalSize.first.toFloat() / deviceSpec.naturalSize.second @Before fun setup() { - initializeVarsForPhone(deviceSpecs["phone"]!!) + initializeVarsForPhone(deviceSpec) } @Test fun parseValidFile() { val workspaceSpecs = - WorkspaceSpecs.create(TestResourceHelper(context!!, TestR.xml.valid_workspace_file)) - assertThat(workspaceSpecs.heightSpecs.size).isEqualTo(3) - assertThat(workspaceSpecs.heightSpecs[0].toString()) + ResponsiveSpecsProvider.create( + TestResourceHelper(context, TestR.xml.valid_workspace_file), + ResponsiveSpecType.Workspace + ) + + val specs = workspaceSpecs.getSpecsByAspectRatio(aspectRatio) + assertThat(specs.heightSpecs.size).isEqualTo(3) + assertThat(specs.heightSpecs[0].toString()) .isEqualTo( - "WorkspaceSpec(" + + "ResponsiveSpec(" + "maxAvailableSize=1533, " + - "specType=HEIGHT, " + + "dimensionType=HEIGHT, " + + "specType=Workspace, " + "startPadding=SizeSpec(fixedSize=0.0, " + "ofAvailableSpace=0.0, " + "ofRemainderSpace=0.0, " + @@ -70,11 +79,12 @@ class WorkspaceSpecsTest : AbstractDeviceProfileTest() { "maxSize=2147483647)" + ")" ) - assertThat(workspaceSpecs.heightSpecs[1].toString()) + assertThat(specs.heightSpecs[1].toString()) .isEqualTo( - "WorkspaceSpec(" + + "ResponsiveSpec(" + "maxAvailableSize=1607, " + - "specType=HEIGHT, " + + "dimensionType=HEIGHT, " + + "specType=Workspace, " + "startPadding=SizeSpec(fixedSize=0.0, " + "ofAvailableSpace=0.0, " + "ofRemainderSpace=0.0, " + @@ -97,11 +107,12 @@ class WorkspaceSpecsTest : AbstractDeviceProfileTest() { "maxSize=2147483647)" + ")" ) - assertThat(workspaceSpecs.heightSpecs[2].toString()) + assertThat(specs.heightSpecs[2].toString()) .isEqualTo( - "WorkspaceSpec(" + + "ResponsiveSpec(" + "maxAvailableSize=26247, " + - "specType=HEIGHT, " + + "dimensionType=HEIGHT, " + + "specType=Workspace, " + "startPadding=SizeSpec(fixedSize=21.0, " + "ofAvailableSpace=0.0, " + "ofRemainderSpace=0.0, " + @@ -124,12 +135,13 @@ class WorkspaceSpecsTest : AbstractDeviceProfileTest() { "maxSize=2147483647)" + ")" ) - assertThat(workspaceSpecs.widthSpecs.size).isEqualTo(1) - assertThat(workspaceSpecs.widthSpecs[0].toString()) + assertThat(specs.widthSpecs.size).isEqualTo(1) + assertThat(specs.widthSpecs[0].toString()) .isEqualTo( - "WorkspaceSpec(" + + "ResponsiveSpec(" + "maxAvailableSize=26247, " + - "specType=WIDTH, " + + "dimensionType=WIDTH, " + + "specType=Workspace, " + "startPadding=SizeSpec(fixedSize=58.0, " + "ofAvailableSpace=0.0, " + "ofRemainderSpace=0.0, " + @@ -156,29 +168,33 @@ class WorkspaceSpecsTest : AbstractDeviceProfileTest() { @Test(expected = IllegalStateException::class) fun parseInvalidFile_missingTag_throwsError() { - WorkspaceSpecs.create( - TestResourceHelper(context!!, TestR.xml.invalid_workspace_file_case_1) + ResponsiveSpecsProvider.create( + TestResourceHelper(context, TestR.xml.invalid_workspace_file_case_1), + ResponsiveSpecType.Workspace ) } @Test(expected = IllegalStateException::class) fun parseInvalidFile_moreThanOneValuePerTag_throwsError() { - WorkspaceSpecs.create( - TestResourceHelper(context!!, TestR.xml.invalid_workspace_file_case_2) + ResponsiveSpecsProvider.create( + TestResourceHelper(context, TestR.xml.invalid_workspace_file_case_2), + ResponsiveSpecType.Workspace ) } @Test(expected = IllegalStateException::class) fun parseInvalidFile_valueBiggerThan1_throwsError() { - WorkspaceSpecs.create( - TestResourceHelper(context!!, TestR.xml.invalid_workspace_file_case_3) + ResponsiveSpecsProvider.create( + TestResourceHelper(context, TestR.xml.invalid_workspace_file_case_3), + ResponsiveSpecType.Workspace ) } @Test(expected = IllegalStateException::class) fun parseInvalidFile_matchWorkspace_true_throwsError() { - WorkspaceSpecs.create( - TestResourceHelper(context!!, TestR.xml.invalid_workspace_file_case_4) + ResponsiveSpecsProvider.create( + TestResourceHelper(context, TestR.xml.invalid_workspace_file_case_4), + ResponsiveSpecType.Workspace ) } } diff --git a/tests/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncherTest.java b/tests/src/com/android/launcher3/secondarydisplay/TaplSecondaryDisplayLauncherTest.java index c7431f20dd..d7b9638ef2 100644 --- a/tests/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncherTest.java +++ b/tests/src/com/android/launcher3/secondarydisplay/TaplSecondaryDisplayLauncherTest.java @@ -47,7 +47,7 @@ import org.junit.runner.RunWith; */ @LargeTest @RunWith(AndroidJUnit4.class) -public final class SecondaryDisplayLauncherTest extends AbstractLauncherUiTest { +public final class TaplSecondaryDisplayLauncherTest extends AbstractLauncherUiTest { private static final int WAIT_TIME_MS = 5000; private static final int LONG_PRESS_DURATION_MS = 1000; private static final int DRAG_TIME_MS = 160; diff --git a/tests/src/com/android/launcher3/settings/SettingsActivityTest.java b/tests/src/com/android/launcher3/settings/SettingsActivityTest.java index 837973fc40..10e0be8883 100644 --- a/tests/src/com/android/launcher3/settings/SettingsActivityTest.java +++ b/tests/src/com/android/launcher3/settings/SettingsActivityTest.java @@ -31,8 +31,9 @@ import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription import static androidx.test.espresso.matcher.ViewMatchers.withId; import static androidx.test.espresso.matcher.ViewMatchers.withText; -import static com.android.launcher3.settings.SettingsActivity.EXTRA_FRAGMENT; +import static com.android.launcher3.settings.SettingsActivity.DEVELOPER_OPTIONS_KEY; import static com.android.launcher3.settings.SettingsActivity.EXTRA_FRAGMENT_ARGS; +import static com.android.launcher3.settings.SettingsActivity.EXTRA_FRAGMENT_ROOT_KEY; import static com.google.common.truth.Truth.assertThat; @@ -43,18 +44,15 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; -import androidx.preference.PreferenceFragmentCompat; import androidx.test.core.app.ActivityScenario; import androidx.test.core.app.ApplicationProvider; import androidx.test.espresso.intent.Intents; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.android.launcher3.R; -import com.android.launcher3.uioverrides.flags.DeveloperOptionsFragment; import com.android.systemui.shared.plugins.PluginPrefs; import org.junit.After; -import org.junit.Assert; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; @@ -100,7 +98,7 @@ public class SettingsActivityTest { intended(allOf( hasComponent(SettingsActivity.class.getName()), - hasExtra(EXTRA_FRAGMENT, DeveloperOptionsFragment.class.getName()))); + hasExtra(EXTRA_FRAGMENT_ROOT_KEY, DEVELOPER_OPTIONS_KEY))); } @Test @@ -122,7 +120,7 @@ public class SettingsActivityTest { @Ignore // b/199309785 public void testSettings_developerOptionsFragmentIntent() { Intent intent = new Intent(mApplicationContext, SettingsActivity.class) - .putExtra(EXTRA_FRAGMENT, DeveloperOptionsFragment.class.getName()); + .putExtra(EXTRA_FRAGMENT_ROOT_KEY, DEVELOPER_OPTIONS_KEY); ActivityScenario.launch(intent); onView(withText("Developer Options")).check(matches(isDisplayed())); @@ -132,21 +130,6 @@ public class SettingsActivityTest { @Test @Ignore // b/199309785 - public void testSettings_intentWithUnknownFragment() { - String fragmentClass = PreferenceFragmentCompat.class.getName(); - Intent intent = new Intent(mApplicationContext, SettingsActivity.class) - .putExtra(EXTRA_FRAGMENT, fragmentClass); - - try { - ActivityScenario.launch(intent); - Assert.fail("Should have thrown an IllegalArgumentException."); - } catch (IllegalArgumentException e) { - assertThat(e.getMessage()).contains(fragmentClass); - } - } - - @Test - @Ignore // b/199309785 public void testSettings_backButtonFinishesActivity() { Bundle fragmentArgs = new Bundle(); fragmentArgs.putString(ARG_PREFERENCE_ROOT, "about_screen"); diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java index 5240e6a02b..79d8c60049 100644 --- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java +++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java @@ -15,10 +15,11 @@ */ package com.android.launcher3.ui; +import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT; + import static androidx.test.InstrumentationRegistry.getInstrumentation; import static com.android.launcher3.testing.shared.TestProtocol.ICON_MISSING; -import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static org.junit.Assert.assertEquals; @@ -40,9 +41,11 @@ import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; +import android.platform.test.flag.junit.SetFlagsRule; import android.system.OsConstants; import android.util.Log; +import androidx.annotation.NonNull; import androidx.test.InstrumentationRegistry; import androidx.test.uiautomator.By; import androidx.test.uiautomator.BySelector; @@ -68,6 +71,7 @@ import com.android.launcher3.util.rule.FailureWatcher; import com.android.launcher3.util.rule.SamplerRule; import com.android.launcher3.util.rule.ScreenRecordRule; import com.android.launcher3.util.rule.ShellCommandRule; +import com.android.launcher3.util.rule.TestIsolationRule; import com.android.launcher3.util.rule.TestStabilityRule; import com.android.launcher3.util.rule.ViewCaptureRule; @@ -101,35 +105,62 @@ public abstract class AbstractLauncherUiTest { private static boolean sDumpWasGenerated = false; private static boolean sActivityLeakReported = false; private static boolean sSeenKeyguard = false; + private static boolean sFirstTimeWaitingForWizard = true; private static final String SYSTEMUI_PACKAGE = "com.android.systemui"; protected LooperExecutor mMainThreadExecutor = MAIN_EXECUTOR; - protected final UiDevice mDevice = UiDevice.getInstance(getInstrumentation()); - protected final LauncherInstrumentation mLauncher = new LauncherInstrumentation(); + protected final UiDevice mDevice = getUiDevice(); + protected final LauncherInstrumentation mLauncher = createLauncherInstrumentation(); + + @NonNull + public static LauncherInstrumentation createLauncherInstrumentation() { + waitForSetupWizardDismissal(); // precondition for creating LauncherInstrumentation + return new LauncherInstrumentation(true); + } + protected Context mTargetContext; protected String mTargetPackage; private int mLauncherPid; + /** Detects activity leaks and throws an exception if a leak is found. */ public static void checkDetectedLeaks(LauncherInstrumentation launcher) { + checkDetectedLeaks(launcher, false); + } + + /** Detects activity leaks and throws an exception if a leak is found. */ + public static void checkDetectedLeaks(LauncherInstrumentation launcher, + boolean requireOneActiveActivityUnused) { + if (TestStabilityRule.isPresubmit()) return; // b/313501215 + + final boolean requireOneActiveActivity = + false; // workaround for leaks when there is an unexpected Recents activity + if (sActivityLeakReported) return; // Check whether activity leak detector has found leaked activities. - Wait.atMost(() -> getActivityLeakErrorMessage(launcher), + Wait.atMost(() -> getActivityLeakErrorMessage(launcher, requireOneActiveActivity), () -> { launcher.forceGc(); return MAIN_EXECUTOR.submit( - () -> launcher.noLeakedActivities()).get(); + () -> launcher.noLeakedActivities(requireOneActiveActivity)).get(); }, DEFAULT_UI_TIMEOUT, launcher); } - private static String getActivityLeakErrorMessage(LauncherInstrumentation launcher) { + public static String getAppPackageName() { + return getInstrumentation().getContext().getPackageName(); + } + + private static String getActivityLeakErrorMessage(LauncherInstrumentation launcher, + boolean requireOneActiveActivity) { sActivityLeakReported = true; - return "Activity leak detector has found leaked activities, " - + dumpHprofData(launcher, false) + "."; + return "Activity leak detector has found leaked activities, requirining 1 activity: " + + requireOneActiveActivity + "; " + + dumpHprofData(launcher, false, requireOneActiveActivity) + "."; } - public static String dumpHprofData(LauncherInstrumentation launcher, boolean intentionalLeak) { + private static String dumpHprofData(LauncherInstrumentation launcher, boolean intentionalLeak, + boolean requireOneActiveActivity) { if (intentionalLeak) return "intentional leak; not generating dump"; String result; @@ -143,12 +174,12 @@ public abstract class AbstractLauncherUiTest { if (TestHelpers.isInLauncherProcess()) { Debug.dumpHprofData(fileName); } else { - final UiDevice device = UiDevice.getInstance(getInstrumentation()); + final UiDevice device = getUiDevice(); device.executeShellCommand( "am dumpheap " + device.getLauncherPackageName() + " " + fileName); } Log.d(TAG, "Saved leak dump, the leak is still present: " - + !launcher.noLeakedActivities()); + + !launcher.noLeakedActivities(requireOneActiveActivity)); sDumpWasGenerated = true; result = "saved memory dump as an artifact"; } catch (Throwable e) { @@ -169,8 +200,8 @@ public abstract class AbstractLauncherUiTest { if (TestHelpers.isInLauncherProcess()) { Utilities.enableRunningInTestHarnessForTests(); mLauncher.setSystemHealthSupplier(startTime -> TestCommandReceiver.callCommand( - TestCommandReceiver.GET_SYSTEM_HEALTH_MESSAGE, startTime.toString()). - getString("result")); + TestCommandReceiver.GET_SYSTEM_HEALTH_MESSAGE, startTime.toString()) + .getString("result")); mLauncher.setOnSettledStateAction( containerType -> executeOnLauncher( launcher -> @@ -188,12 +219,32 @@ public abstract class AbstractLauncherUiTest { @Rule public ScreenRecordRule mScreenRecordRule = new ScreenRecordRule(); + @Rule + public SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT); + + public static void initialize(AbstractLauncherUiTest test) throws Exception { + initialize(test, false); + } + + public static void initialize( + AbstractLauncherUiTest test, boolean clearWorkspace) throws Exception { + test.reinitializeLauncherData(clearWorkspace); + test.mDevice.pressHome(); + test.waitForLauncherCondition("Launcher didn't start", launcher -> launcher != null); + test.waitForState("Launcher internal state didn't switch to Home", + () -> LauncherState.NORMAL); + test.waitForResumed("Launcher internal state is still Background"); + // Check that we switched to home. + test.mLauncher.getWorkspace(); + AbstractLauncherUiTest.checkDetectedLeaks(test.mLauncher, true); + } + protected void clearPackageData(String pkg) throws IOException, InterruptedException { final CountDownLatch count = new CountDownLatch(2); final SimpleBroadcastReceiver broadcastReceiver = new SimpleBroadcastReceiver(i -> count.countDown()); broadcastReceiver.registerPkgActions(mTargetContext, pkg, - Intent.ACTION_PACKAGE_RESTARTED, Intent.ACTION_PACKAGE_DATA_CLEARED); + Intent.ACTION_PACKAGE_RESTARTED, Intent.ACTION_PACKAGE_DATA_CLEARED); mDevice.executeShellCommand("pm clear " + pkg); assertTrue(pkg + " didn't restart", count.await(10, TimeUnit.SECONDS)); @@ -206,7 +257,8 @@ public abstract class AbstractLauncherUiTest { final RuleChain inner = RuleChain .outerRule(new PortraitLandscapeRunner(this)) .around(new FailureWatcher(mLauncher, viewCaptureRule::getViewCaptureData)) - .around(viewCaptureRule); + // .around(viewCaptureRule) // b/315482167 + .around(new TestIsolationRule(mLauncher, true)); return TestHelpers.isInLauncherProcess() ? RuleChain.outerRule(ShellCommandRule.setDefaultLauncher()).around(inner) @@ -227,8 +279,6 @@ public abstract class AbstractLauncherUiTest { public void setUp() throws Exception { mLauncher.onTestStart(); - verifyKeyguardInvisible(); - final String launcherPackageName = mDevice.getLauncherPackageName(); try { final Context context = InstrumentationRegistry.getContext(); @@ -258,6 +308,66 @@ public abstract class AbstractLauncherUiTest { } } } + + onTestStart(); + } + + /** Method that should be called when a test starts. */ + public static void onTestStart() { + waitForSetupWizardDismissal(); + + if (TestStabilityRule.isPresubmit()) { + aggressivelyUnlockSysUi(); + } else { + verifyKeyguardInvisible(); + } + } + + private static boolean hasSystemUiObject(String resId) { + return getUiDevice().hasObject( + By.res(SYSTEMUI_PACKAGE, resId)); + } + + @NonNull + private static UiDevice getUiDevice() { + return UiDevice.getInstance(getInstrumentation()); + } + + private static void aggressivelyUnlockSysUi() { + final UiDevice device = getUiDevice(); + for (int i = 0; i < 10 && hasSystemUiObject("keyguard_status_view"); ++i) { + Log.d(TAG, "Before attempting to unlock the phone"); + try { + device.executeShellCommand("input keyevent 82"); + } catch (IOException e) { + throw new RuntimeException(e); + } + device.waitForIdle(); + } + Assert.assertTrue("Keyguard still visible", + TestHelpers.wait( + Until.gone(By.res(SYSTEMUI_PACKAGE, "keyguard_status_view")), 60000)); + Log.d(TAG, "Keyguard is not visible"); + } + + /** Waits for setup wizard to go away. */ + private static void waitForSetupWizardDismissal() { + if (!TestStabilityRule.isPresubmit()) return; + + if (sFirstTimeWaitingForWizard) { + try { + getUiDevice().executeShellCommand( + "am force-stop com.google.android.setupwizard"); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + final boolean wizardDismissed = TestHelpers.wait( + Until.gone(By.pkg("com.google.android.setupwizard").depth(0)), + sFirstTimeWaitingForWizard ? 120000 : 0); + sFirstTimeWaitingForWizard = false; + Assert.assertTrue("Setup wizard is still visible", wizardDismissed); } private static void verifyKeyguardInvisible() { @@ -329,6 +439,15 @@ public abstract class AbstractLauncherUiTest { }); } + // Execute an action on Launcher, but forgive it when launcher is null. + // Launcher can be null if teardown is happening after a failed setup step where launcher + // activity failed to be created. + protected void executeOnLauncherInTearDown(Consumer<Launcher> f) { + executeOnLauncher(launcher -> { + if (launcher != null) f.accept(launcher); + }); + } + // Cannot be used in TaplTests between a Tapl call injecting a gesture and a tapl call // expecting the results of that gesture because the wait can hide flakeness. protected void waitForState(String message, Supplier<LauncherState> state) { @@ -473,6 +592,12 @@ public abstract class AbstractLauncherUiTest { getInstrumentation().getTargetContext().startActivity(intent); assertTrue("App didn't start: " + selector, TestHelpers.wait(Until.hasObject(selector), DEFAULT_UI_TIMEOUT)); + + // Wait for the Launcher to stop. + final LauncherInstrumentation launcherInstrumentation = new LauncherInstrumentation(); + Wait.atMost("Launcher activity didn't stop", + () -> !launcherInstrumentation.isLauncherActivityStarted(), + DEFAULT_ACTIVITY_TIMEOUT, launcherInstrumentation); } public static ActivityInfo resolveSystemAppInfo(String category) { diff --git a/tests/src/com/android/launcher3/ui/ActivityAllAppsContainerViewTest.java b/tests/src/com/android/launcher3/ui/ActivityAllAppsContainerViewTest.java new file mode 100644 index 0000000000..3411fc11db --- /dev/null +++ b/tests/src/com/android/launcher3/ui/ActivityAllAppsContainerViewTest.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.ui; + +import static androidx.test.core.app.ApplicationProvider.getApplicationContext; + +import static com.android.launcher3.model.data.AppInfo.EMPTY_ARRAY; +import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.when; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.os.Process; +import android.os.UserHandle; +import android.os.UserManager; +import android.platform.test.flag.junit.SetFlagsRule; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.launcher3.allapps.ActivityAllAppsContainerView; +import com.android.launcher3.allapps.WorkProfileManager; +import com.android.launcher3.logging.StatsLogManager; +import com.android.launcher3.model.data.AppInfo; +import com.android.launcher3.pm.UserCache; +import com.android.launcher3.util.ActivityContextWrapper; +import com.android.launcher3.util.UserIconInfo; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +import java.util.Arrays; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class ActivityAllAppsContainerViewTest { + + private static final UserHandle WORK_HANDLE = new UserHandle(13); + @Mock + private StatsLogManager mStatsLogManager; + @Mock + private UserCache mUserCache; + @Mock + private UserManager mUserManager; + private AppInfo[] mWorkAppInfo; + private ActivityAllAppsContainerView<?> mActivityAllAppsContainerView; + private WorkProfileManager mWorkManager; + private Context mContext; + + @Rule public final SetFlagsRule mSetFlagsRule = getFlagsRule(); + + private SetFlagsRule getFlagsRule() { + SetFlagsRule flagsRule = new SetFlagsRule(); + flagsRule.initAllFlagsToReleaseConfigDefault(); + return flagsRule; + } + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = new ActivityContextWrapper(getApplicationContext()); + mActivityAllAppsContainerView = new ActivityAllAppsContainerView(mContext); + when(mUserCache.getUserProfiles()) + .thenReturn(Arrays.asList(Process.myUserHandle(), WORK_HANDLE)); + when(mUserCache.getUserInfo(Process.myUserHandle())) + .thenReturn(new UserIconInfo(Process.myUserHandle(), 0)); + when(mUserCache.getUserInfo(WORK_HANDLE)) + .thenReturn(new UserIconInfo(WORK_HANDLE, 1)); + mWorkManager = new WorkProfileManager(mUserManager, mActivityAllAppsContainerView, + mStatsLogManager, mUserCache); + mActivityAllAppsContainerView.setWorkManager(mWorkManager); + ComponentName componentName = new ComponentName(mContext, + "com.android.launcher3.tests.Activity" + "Gmail"); + AppInfo gmailWorkAppInfo = new AppInfo(componentName, "Gmail", WORK_HANDLE, new Intent()); + mWorkAppInfo = new AppInfo[]{gmailWorkAppInfo}; + } + + @Test + public void testOnAppsUpdatedWithoutWorkApps_shouldShowTabsIsFalse() { + mActivityAllAppsContainerView.getAppsStore().setApps(EMPTY_ARRAY, 0, null); + + mActivityAllAppsContainerView.onAppsUpdated(); + + assertThat(mActivityAllAppsContainerView.shouldShowTabs()).isEqualTo(false); + } + + @Test + public void testOnAppsUpdatedWithWorkApps_shouldShowTabsIsTrue() { + mActivityAllAppsContainerView.getAppsStore().setApps(mWorkAppInfo, 0, null); + + mActivityAllAppsContainerView.onAppsUpdated(); + + assertThat(mActivityAllAppsContainerView.shouldShowTabs()).isEqualTo(true); + } + + @Test + public void testWorkProfileEnabled_requestQuietModeCalledCorrectly() throws Exception { + /* Setup */ + when(mUserManager.requestQuietModeEnabled(false, WORK_HANDLE)) + .thenReturn(true); + + /* Execution */ + mWorkManager.setWorkProfileEnabled(true); + + /* Assertion */ + awaitTasksCompleted(); + Mockito.verify(mUserManager).requestQuietModeEnabled(false, WORK_HANDLE); + } + + private static void awaitTasksCompleted() throws Exception { + UI_HELPER_EXECUTOR.submit(() -> null).get(); + } +} diff --git a/tests/src/com/android/launcher3/ui/BubbleTextViewTest.java b/tests/src/com/android/launcher3/ui/BubbleTextViewTest.java index ba17fdc8dd..58af51c564 100644 --- a/tests/src/com/android/launcher3/ui/BubbleTextViewTest.java +++ b/tests/src/com/android/launcher3/ui/BubbleTextViewTest.java @@ -20,26 +20,39 @@ import static androidx.test.core.app.ApplicationProvider.getApplicationContext; 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.config.FeatureFlags.ENABLE_TWOLINE_ALLAPPS; +import static com.google.common.truth.Truth.assertThat; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; +import android.content.ComponentName; import android.content.Context; +import android.content.Intent; import android.graphics.Typeface; +import android.os.UserHandle; +import android.platform.test.flag.junit.SetFlagsRule; import android.view.ViewGroup; import com.android.launcher3.BubbleTextView; +import com.android.launcher3.Flags; import com.android.launcher3.Utilities; +import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.model.data.ItemInfoWithIcon; import com.android.launcher3.search.StringMatcherUtility; import com.android.launcher3.util.ActivityContextWrapper; +import com.android.launcher3.util.FlagOp; import com.android.launcher3.util.IntArray; import com.android.launcher3.util.TestUtil; import com.android.launcher3.views.BaseDragLayer; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.mockito.MockitoAnnotations; /** * Unit tests for testing modifyTitleToSupportMultiLine() in BubbleTextView.java @@ -50,8 +63,12 @@ import org.junit.Test; */ public class BubbleTextViewTest { + @Rule public final SetFlagsRule mSetFlagsRule = + new SetFlagsRule(SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT); private static final StringMatcherUtility.StringMatcher MATCHER = StringMatcherUtility.StringMatcher.getInstance(); + private static final UserHandle WORK_HANDLE = new UserHandle(13); + private static final int WORK_FLAG = 1; private static final int ONE_LINE = 1; private static final int TWO_LINE = 2; private static final String TEST_STRING_WITH_SPACE_LONGER_THAN_CHAR_LIMIT = "Battery Stats"; @@ -69,14 +86,20 @@ public class BubbleTextViewTest { "LEGO®\nBuilder"; private static final String EMPTY_STRING = ""; private static final int CHAR_CNT = 7; + private static final int MAX_HEIGHT = Integer.MAX_VALUE; + private static final int LIMITED_HEIGHT = 357; /* allowedHeight in Pixel6 */ + private static final float SPACE_MULTIPLIER = 1; + private static final float SPACE_EXTRA = 0; private BubbleTextView mBubbleTextView; private ItemInfoWithIcon mItemInfoWithIcon; private Context mContext; private int mLimitedWidth; + private AppInfo mGmailAppInfo; @Before public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); Utilities.enableRunningInTestHarnessForTests(); mContext = new ActivityContextWrapper(getApplicationContext()); mBubbleTextView = new BubbleTextView(mContext); @@ -99,17 +122,23 @@ public class BubbleTextViewTest { return null; } }; + ComponentName componentName = new ComponentName(mContext, + "com.android.launcher3.tests.Activity" + "Gmail"); + mGmailAppInfo = new AppInfo(componentName, "Gmail", WORK_HANDLE, new Intent()); } @Test public void testEmptyString_flagOn() { + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TWOLINE_ALLAPPS); try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_TWOLINE_ALLAPPS, true)) { mItemInfoWithIcon.title = EMPTY_STRING; mBubbleTextView.setDisplay(DISPLAY_ALL_APPS); mBubbleTextView.applyLabel(mItemInfoWithIcon); mBubbleTextView.setTypeface(Typeface.MONOSPACE); - mBubbleTextView.measure(mLimitedWidth, 0); + mBubbleTextView.measure(mLimitedWidth, LIMITED_HEIGHT); + mBubbleTextView.onPreDraw(); + assertNotEquals(TWO_LINE, mBubbleTextView.getMaxLines()); } catch (Exception e) { throw new RuntimeException(e); @@ -118,13 +147,16 @@ public class BubbleTextViewTest { @Test public void testEmptyString_flagOff() { + mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_TWOLINE_ALLAPPS); try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_TWOLINE_ALLAPPS, false)) { mItemInfoWithIcon.title = EMPTY_STRING; mBubbleTextView.setDisplay(DISPLAY_ALL_APPS); mBubbleTextView.applyLabel(mItemInfoWithIcon); mBubbleTextView.setTypeface(Typeface.MONOSPACE); - mBubbleTextView.measure(mLimitedWidth, 0); + mBubbleTextView.measure(mLimitedWidth, LIMITED_HEIGHT); + mBubbleTextView.onPreDraw(); + assertEquals(ONE_LINE, mBubbleTextView.getLineCount()); } catch (Exception e) { throw new RuntimeException(e); @@ -133,14 +165,17 @@ public class BubbleTextViewTest { @Test public void testStringWithSpaceLongerThanCharLimit_flagOn() { + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TWOLINE_ALLAPPS); try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_TWOLINE_ALLAPPS, true)) { // test string: "Battery Stats" mItemInfoWithIcon.title = TEST_STRING_WITH_SPACE_LONGER_THAN_CHAR_LIMIT; mBubbleTextView.applyLabel(mItemInfoWithIcon); mBubbleTextView.setDisplay(DISPLAY_ALL_APPS); mBubbleTextView.setTypeface(Typeface.MONOSPACE); - mBubbleTextView.measure(mLimitedWidth, 0); + mBubbleTextView.measure(mLimitedWidth, MAX_HEIGHT); + mBubbleTextView.onPreDraw(); + assertEquals(TWO_LINE, mBubbleTextView.getLineCount()); } catch (Exception e) { throw new RuntimeException(e); @@ -149,14 +184,17 @@ public class BubbleTextViewTest { @Test public void testStringWithSpaceLongerThanCharLimit_flagOff() { + mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_TWOLINE_ALLAPPS); try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_TWOLINE_ALLAPPS, false)) { // test string: "Battery Stats" mItemInfoWithIcon.title = TEST_STRING_WITH_SPACE_LONGER_THAN_CHAR_LIMIT; mBubbleTextView.applyLabel(mItemInfoWithIcon); mBubbleTextView.setDisplay(DISPLAY_ALL_APPS); mBubbleTextView.setTypeface(Typeface.MONOSPACE); - mBubbleTextView.measure(mLimitedWidth, 0); + mBubbleTextView.measure(mLimitedWidth, LIMITED_HEIGHT); + mBubbleTextView.onPreDraw(); + assertEquals(ONE_LINE, mBubbleTextView.getLineCount()); } catch (Exception e) { throw new RuntimeException(e); @@ -165,14 +203,17 @@ public class BubbleTextViewTest { @Test public void testLongStringNoSpaceLongerThanCharLimit_flagOn() { + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TWOLINE_ALLAPPS); try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_TWOLINE_ALLAPPS, true)) { // test string: "flutterappflorafy" mItemInfoWithIcon.title = TEST_LONG_STRING_NO_SPACE_LONGER_THAN_CHAR_LIMIT; mBubbleTextView.applyLabel(mItemInfoWithIcon); mBubbleTextView.setDisplay(DISPLAY_ALL_APPS); mBubbleTextView.setTypeface(Typeface.MONOSPACE); - mBubbleTextView.measure(mLimitedWidth, 0); + mBubbleTextView.measure(mLimitedWidth, LIMITED_HEIGHT); + mBubbleTextView.onPreDraw(); + assertEquals(ONE_LINE, mBubbleTextView.getLineCount()); } catch (Exception e) { throw new RuntimeException(e); @@ -181,81 +222,96 @@ public class BubbleTextViewTest { @Test public void testLongStringNoSpaceLongerThanCharLimit_flagOff() { + mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_TWOLINE_ALLAPPS); try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_TWOLINE_ALLAPPS, false)) { // test string: "flutterappflorafy" mItemInfoWithIcon.title = TEST_LONG_STRING_NO_SPACE_LONGER_THAN_CHAR_LIMIT; mBubbleTextView.applyLabel(mItemInfoWithIcon); mBubbleTextView.setDisplay(DISPLAY_ALL_APPS); mBubbleTextView.setTypeface(Typeface.MONOSPACE); - mBubbleTextView.measure(mLimitedWidth, 0); + mBubbleTextView.measure(mLimitedWidth, LIMITED_HEIGHT); + mBubbleTextView.onPreDraw(); + assertEquals(ONE_LINE, mBubbleTextView.getLineCount()); } catch (Exception e) { - throw new RuntimeException(e); + throw new RuntimeException(e); } } @Test public void testLongStringWithSpaceLongerThanCharLimit_flagOn() { + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TWOLINE_ALLAPPS); try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_TWOLINE_ALLAPPS, true)) { // test string: "System UWB Field Test" mItemInfoWithIcon.title = TEST_LONG_STRING_WITH_SPACE_LONGER_THAN_CHAR_LIMIT; mBubbleTextView.applyLabel(mItemInfoWithIcon); mBubbleTextView.setDisplay(DISPLAY_ALL_APPS); mBubbleTextView.setTypeface(Typeface.MONOSPACE); - mBubbleTextView.measure(mLimitedWidth, 0); + mBubbleTextView.measure(mLimitedWidth, MAX_HEIGHT); + mBubbleTextView.onPreDraw(); + assertEquals(TWO_LINE, mBubbleTextView.getLineCount()); } catch (Exception e) { - throw new RuntimeException(e); + throw new RuntimeException(e); } } @Test public void testLongStringWithSpaceLongerThanCharLimit_flagOff() { + mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_TWOLINE_ALLAPPS); try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_TWOLINE_ALLAPPS, false)) { // test string: "System UWB Field Test" mItemInfoWithIcon.title = TEST_LONG_STRING_WITH_SPACE_LONGER_THAN_CHAR_LIMIT; mBubbleTextView.applyLabel(mItemInfoWithIcon); mBubbleTextView.setDisplay(DISPLAY_ALL_APPS); mBubbleTextView.setTypeface(Typeface.MONOSPACE); - mBubbleTextView.measure(mLimitedWidth, 0); + mBubbleTextView.measure(mLimitedWidth, LIMITED_HEIGHT); + mBubbleTextView.onPreDraw(); + assertEquals(ONE_LINE, mBubbleTextView.getLineCount()); } catch (Exception e) { - throw new RuntimeException(e); + throw new RuntimeException(e); } } @Test public void testLongStringSymbolLongerThanCharLimit_flagOn() { + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TWOLINE_ALLAPPS); try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_TWOLINE_ALLAPPS, true)) { // test string: "LEGO®Builder" mItemInfoWithIcon.title = TEST_LONG_STRING_SYMBOL_LONGER_THAN_CHAR_LIMIT; mBubbleTextView.applyLabel(mItemInfoWithIcon); mBubbleTextView.setDisplay(DISPLAY_ALL_APPS); mBubbleTextView.setTypeface(Typeface.MONOSPACE); - mBubbleTextView.measure(mLimitedWidth, 0); + mBubbleTextView.measure(mLimitedWidth, MAX_HEIGHT); + mBubbleTextView.onPreDraw(); + assertEquals(TWO_LINE, mBubbleTextView.getLineCount()); } catch (Exception e) { - throw new RuntimeException(e); + throw new RuntimeException(e); } } @Test public void testLongStringSymbolLongerThanCharLimit_flagOff() { + mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_TWOLINE_ALLAPPS); try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_TWOLINE_ALLAPPS, false)) { // test string: "LEGO®Builder" mItemInfoWithIcon.title = TEST_LONG_STRING_SYMBOL_LONGER_THAN_CHAR_LIMIT; mBubbleTextView.applyLabel(mItemInfoWithIcon); mBubbleTextView.setDisplay(DISPLAY_ALL_APPS); mBubbleTextView.setTypeface(Typeface.MONOSPACE); - mBubbleTextView.measure(mLimitedWidth, 0); + mBubbleTextView.measure(mLimitedWidth, LIMITED_HEIGHT); + mBubbleTextView.onPreDraw(); + assertEquals(ONE_LINE, mBubbleTextView.getLineCount()); } catch (Exception e) { - throw new RuntimeException(e); + throw new RuntimeException(e); } } @@ -265,8 +321,11 @@ public class BubbleTextViewTest { IntArray breakPoints = StringMatcherUtility.getListOfBreakpoints( TEST_STRING_WITH_SPACE_LONGER_THAN_CHAR_LIMIT, MATCHER); CharSequence newString = BubbleTextView.modifyTitleToSupportMultiLine(mLimitedWidth, + MAX_HEIGHT, TEST_STRING_WITH_SPACE_LONGER_THAN_CHAR_LIMIT, mBubbleTextView.getPaint(), - breakPoints); + breakPoints, + SPACE_MULTIPLIER, + SPACE_EXTRA); assertEquals(TEST_STRING_WITH_SPACE_LONGER_THAN_CHAR_LIMIT_RESULT, newString); } @@ -275,9 +334,11 @@ public class BubbleTextViewTest { // test string: "flutterappflorafy" IntArray breakPoints = StringMatcherUtility.getListOfBreakpoints( TEST_LONG_STRING_NO_SPACE_LONGER_THAN_CHAR_LIMIT, MATCHER); - CharSequence newString = BubbleTextView.modifyTitleToSupportMultiLine(mLimitedWidth, + CharSequence newString = BubbleTextView.modifyTitleToSupportMultiLine(mLimitedWidth, 0, TEST_LONG_STRING_NO_SPACE_LONGER_THAN_CHAR_LIMIT, mBubbleTextView.getPaint(), - breakPoints); + breakPoints, + SPACE_MULTIPLIER, + SPACE_EXTRA); assertEquals(TEST_LONG_STRING_NO_SPACE_LONGER_THAN_CHAR_LIMIT, newString); } @@ -287,8 +348,11 @@ public class BubbleTextViewTest { IntArray breakPoints = StringMatcherUtility.getListOfBreakpoints( TEST_LONG_STRING_WITH_SPACE_LONGER_THAN_CHAR_LIMIT, MATCHER); CharSequence newString = BubbleTextView.modifyTitleToSupportMultiLine(mLimitedWidth, + MAX_HEIGHT, TEST_LONG_STRING_WITH_SPACE_LONGER_THAN_CHAR_LIMIT, mBubbleTextView.getPaint(), - breakPoints); + breakPoints, + SPACE_MULTIPLIER, + SPACE_EXTRA); assertEquals(TEST_LONG_STRING_WITH_SPACE_LONGER_THAN_CHAR_LIMIT_RESULT, newString); } @@ -297,25 +361,93 @@ public class BubbleTextViewTest { // test string: "LEGO®Builder" IntArray breakPoints = StringMatcherUtility.getListOfBreakpoints( TEST_LONG_STRING_SYMBOL_LONGER_THAN_CHAR_LIMIT, MATCHER); - CharSequence newString = BubbleTextView.modifyTitleToSupportMultiLine(mLimitedWidth, + CharSequence newString = BubbleTextView.modifyTitleToSupportMultiLine( + mLimitedWidth, + MAX_HEIGHT, TEST_LONG_STRING_SYMBOL_LONGER_THAN_CHAR_LIMIT, mBubbleTextView.getPaint(), - breakPoints); + breakPoints, + SPACE_MULTIPLIER, + SPACE_EXTRA); assertEquals(TEST_LONG_STRING_SYMBOL_LONGER_THAN_CHAR_LIMIT_RESULT, newString); } @Test - public void testEnsurePredictionRowIsOneLine() { + public void testEnsurePredictionRowIsTwoLine() { + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TWOLINE_ALLAPPS); try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_TWOLINE_ALLAPPS, true)) { // test string: "Battery Stats" mItemInfoWithIcon.title = TEST_STRING_WITH_SPACE_LONGER_THAN_CHAR_LIMIT; mBubbleTextView.setDisplay(DISPLAY_PREDICTION_ROW); mBubbleTextView.applyLabel(mItemInfoWithIcon); mBubbleTextView.setTypeface(Typeface.MONOSPACE); - mBubbleTextView.measure(mLimitedWidth, 0); + mBubbleTextView.measure(mLimitedWidth, MAX_HEIGHT); + mBubbleTextView.onPreDraw(); + + assertEquals(TWO_LINE, mBubbleTextView.getLineCount()); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Test + public void modifyTitleToSupportMultiLine_whenLimitedHeight_shouldBeOneLine() { + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TWOLINE_ALLAPPS); + try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_TWOLINE_ALLAPPS, true)) { + // test string: "LEGO®Builder" + mItemInfoWithIcon.title = TEST_LONG_STRING_SYMBOL_LONGER_THAN_CHAR_LIMIT; + mBubbleTextView.applyLabel(mItemInfoWithIcon); + mBubbleTextView.setTypeface(Typeface.MONOSPACE); + mBubbleTextView.measure(mLimitedWidth, LIMITED_HEIGHT); + + mBubbleTextView.onPreDraw(); + assertEquals(ONE_LINE, mBubbleTextView.getLineCount()); } catch (Exception e) { throw new RuntimeException(e); } } + + @Test + public void modifyTitleToSupportMultiLine_whenUnlimitedHeight_shouldBeTwoLine() { + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TWOLINE_ALLAPPS); + try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_TWOLINE_ALLAPPS, true)) { + // test string: "LEGO®Builder" + mItemInfoWithIcon.title = TEST_LONG_STRING_SYMBOL_LONGER_THAN_CHAR_LIMIT; + mBubbleTextView.setDisplay(DISPLAY_ALL_APPS); + mBubbleTextView.applyLabel(mItemInfoWithIcon); + mBubbleTextView.setTypeface(Typeface.MONOSPACE); + mBubbleTextView.measure(mLimitedWidth, MAX_HEIGHT); + + mBubbleTextView.onPreDraw(); + + assertEquals(TWO_LINE, mBubbleTextView.getLineCount()); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Test + public void applyIconAndLabel_whenDisplay_DISPLAY_SEARCH_RESULT_SMALL_noBadge() { + FlagOp op = FlagOp.NO_OP; + // apply the WORK bitmap flag to show work badge + mGmailAppInfo.bitmap.flags = op.apply(WORK_FLAG); + mBubbleTextView.setDisplay(DISPLAY_SEARCH_RESULT_SMALL); + + mBubbleTextView.applyIconAndLabel(mGmailAppInfo); + + assertThat(mBubbleTextView.getIcon().hasBadge()).isEqualTo(false); + } + + @Test + public void applyIconAndLabel_whenDisplay_DISPLAY_SEARCH_RESULT_hasBadge() { + FlagOp op = FlagOp.NO_OP; + // apply the WORK bitmap flag to show work badge + mGmailAppInfo.bitmap.flags = op.apply(WORK_FLAG); + mBubbleTextView.setDisplay(DISPLAY_SEARCH_RESULT); + + mBubbleTextView.applyIconAndLabel(mGmailAppInfo); + + assertThat(mBubbleTextView.getIcon().hasBadge()).isEqualTo(true); + } } diff --git a/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java index f0875f8104..d94e4a56de 100644 --- a/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java +++ b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java @@ -4,6 +4,7 @@ import android.util.Log; import android.view.Surface; import com.android.launcher3.tapl.TestHelpers; +import com.android.launcher3.util.rule.TestStabilityRule; import org.junit.rules.TestRule; import org.junit.runner.Description; @@ -30,8 +31,12 @@ public class PortraitLandscapeRunner implements TestRule { @Override public Statement apply(Statement base, Description description) { - if (!TestHelpers.isInLauncherProcess() || - description.getAnnotation(PortraitLandscape.class) == null) { + if (!TestHelpers.isInLauncherProcess() + || description.getAnnotation(PortraitLandscape.class) == null + // If running in presubmit, don't run in both orientations. + // It's important to keep presubmits fast even if we will occasionally miss + // regressions in presubmit. + || TestStabilityRule.isPresubmit()) { return base; } @@ -67,7 +72,7 @@ public class PortraitLandscapeRunner implements TestRule { private void evaluateInPortrait() throws Throwable { mTest.mDevice.setOrientationNatural(); mTest.mLauncher.setExpectedRotation(Surface.ROTATION_0); - AbstractLauncherUiTest.checkDetectedLeaks(mTest.mLauncher); + AbstractLauncherUiTest.checkDetectedLeaks(mTest.mLauncher, true); base.evaluate(); mTest.getDevice().pressHome(); } @@ -75,7 +80,7 @@ public class PortraitLandscapeRunner implements TestRule { private void evaluateInLandscape() throws Throwable { mTest.mDevice.setOrientationLeft(); mTest.mLauncher.setExpectedRotation(Surface.ROTATION_90); - AbstractLauncherUiTest.checkDetectedLeaks(mTest.mLauncher); + AbstractLauncherUiTest.checkDetectedLeaks(mTest.mLauncher, true); base.evaluate(); mTest.getDevice().pressHome(); } diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java index 45b01f4a20..229ea4569d 100644 --- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java +++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java @@ -16,82 +16,18 @@ package com.android.launcher3.ui; -import static androidx.test.InstrumentationRegistry.getInstrumentation; - -import static com.android.launcher3.testing.shared.TestProtocol.ICON_MISSING; -import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL; -import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT; - -import static com.google.common.truth.Truth.assertThat; - -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 static org.junit.Assume.assumeFalse; -import static org.junit.Assume.assumeTrue; -import android.content.Intent; -import android.graphics.Point; -import android.os.SystemClock; -import android.platform.test.annotations.PlatinumTest; -import android.util.Log; - -import androidx.test.filters.FlakyTest; import androidx.test.filters.LargeTest; -import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; -import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherState; -import com.android.launcher3.config.FeatureFlags; -import com.android.launcher3.popup.ArrowPopup; -import com.android.launcher3.tapl.AllApps; -import com.android.launcher3.tapl.AppIcon; -import com.android.launcher3.tapl.AppIconMenu; -import com.android.launcher3.tapl.AppIconMenuItem; -import com.android.launcher3.tapl.Folder; -import com.android.launcher3.tapl.FolderIcon; -import com.android.launcher3.tapl.HomeAllApps; -import com.android.launcher3.tapl.HomeAppIcon; -import com.android.launcher3.tapl.HomeAppIconMenuItem; -import com.android.launcher3.tapl.Widgets; -import com.android.launcher3.tapl.Workspace; -import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape; -import com.android.launcher3.util.LauncherLayoutBuilder; -import com.android.launcher3.util.TestUtil; -import com.android.launcher3.util.Wait; -import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord; -import com.android.launcher3.util.rule.TISBindRule; -import com.android.launcher3.util.rule.TestStabilityRule.Stability; -import com.android.launcher3.widget.picker.WidgetsFullSheet; -import com.android.launcher3.widget.picker.WidgetsRecyclerView; - -import org.junit.After; import org.junit.Before; -import org.junit.Ignore; -import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; -import java.io.IOException; -import java.util.Map; - @LargeTest @RunWith(AndroidJUnit4.class) public class TaplTestsLauncher3 extends AbstractLauncherUiTest { - private static final String APP_NAME = "LauncherTestApp"; - private static final String DUMMY_APP_NAME = "Aardwolf"; - private static final String MAPS_APP_NAME = "Maps"; - private static final String STORE_APP_NAME = "Play Store"; - private static final String GMAIL_APP_NAME = "Gmail"; - private static final String READ_DEVICE_CONFIG_PERMISSION = - "android.permission.READ_DEVICE_CONFIG"; - - @Rule - public TISBindRule mTISBindRule = new TISBindRule(); - - private AutoCloseable mLauncherLayout; @Before public void setUp() throws Exception { @@ -99,53 +35,6 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { initialize(this); } - public static void initialize(AbstractLauncherUiTest test) throws Exception { - initialize(test, false); - } - - public static void initialize( - AbstractLauncherUiTest test, boolean clearWorkspace) throws Exception { - test.reinitializeLauncherData(clearWorkspace); - test.mDevice.pressHome(); - test.waitForLauncherCondition("Launcher didn't start", launcher -> launcher != null); - test.waitForState("Launcher internal state didn't switch to Home", - () -> LauncherState.NORMAL); - test.waitForResumed("Launcher internal state is still Background"); - // Check that we switched to home. - test.mLauncher.getWorkspace(); - AbstractLauncherUiTest.checkDetectedLeaks(test.mLauncher); - } - - @After - public void tearDown() throws Exception { - if (mLauncherLayout != null) { - mLauncherLayout.close(); - } - } - - // Please don't add negative test cases for methods that fail only after a long wait. - public static void expectFail(String message, Runnable action) { - boolean failed = false; - try { - action.run(); - } catch (AssertionError e) { - failed = true; - } - assertTrue(message, failed); - } - - public static boolean isWorkspaceScrollable(Launcher launcher) { - return launcher.getWorkspace().getPageCount() > launcher.getWorkspace().getPanelCount(); - } - - private int getCurrentWorkspacePage(Launcher launcher) { - return launcher.getWorkspace().getCurrentPage(); - } - - private WidgetsRecyclerView getWidgetsView(Launcher launcher) { - return WidgetsFullSheet.getWidgetsView(launcher); - } - @Test public void testDevicePressMenu() throws Exception { mDevice.pressMenu(); @@ -156,583 +45,4 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { // Check that pressHome works when the menu is shown. mLauncher.goHome(); } - - @Test - public void testPressHomeOnAllAppsContextMenu() throws Exception { - final AllApps allApps = mLauncher.getWorkspace().switchToAllApps(); - allApps.freeze(); - try { - allApps.getAppIcon("TestActivity7").openMenu(); - } finally { - allApps.unfreeze(); - } - mLauncher.goHome(); - } - - public static void runAllAppsTest(AbstractLauncherUiTest test, AllApps allApps) { - allApps.freeze(); - try { - assertNotNull("allApps parameter is null", allApps); - - assertTrue( - "Launcher internal state is not All Apps", - test.isInState(() -> LauncherState.ALL_APPS)); - - // Test flinging forward and backward. - test.executeOnLauncher(launcher -> assertEquals( - "All Apps started in already scrolled state", 0, - test.getAllAppsScroll(launcher))); - - allApps.flingForward(); - assertTrue("Launcher internal state is not All Apps", - test.isInState(() -> LauncherState.ALL_APPS)); - final Integer flingForwardY = test.getFromLauncher( - launcher -> test.getAllAppsScroll(launcher)); - test.executeOnLauncher( - launcher -> assertTrue("flingForward() didn't scroll App Apps", - flingForwardY > 0)); - - allApps.flingBackward(); - assertTrue( - "Launcher internal state is not All Apps", - test.isInState(() -> LauncherState.ALL_APPS)); - final Integer flingBackwardY = test.getFromLauncher( - launcher -> test.getAllAppsScroll(launcher)); - test.executeOnLauncher(launcher -> assertTrue("flingBackward() didn't scroll App Apps", - flingBackwardY < flingForwardY)); - - // Test scrolling down to YouTube. - assertNotNull("All apps: can't find YouTube", allApps.getAppIcon("YouTube")); - // Test scrolling up to Camera. - assertNotNull("All apps: can't find Camera", allApps.getAppIcon("Camera")); - // Test failing to find a non-existing app. - final AllApps allAppsFinal = allApps; - expectFail("All apps: could find a non-existing app", - () -> allAppsFinal.getAppIcon("NO APP")); - - assertTrue( - "Launcher internal state is not All Apps", - test.isInState(() -> LauncherState.ALL_APPS)); - } finally { - allApps.unfreeze(); - } - } - - @Test - @PortraitLandscape - public void testWorkspaceSwitchToAllApps() { - assertNotNull("switchToAllApps() returned null", - mLauncher.getWorkspace().switchToAllApps()); - assertTrue("Launcher internal state is not All Apps", - isInState(() -> LauncherState.ALL_APPS)); - } - - @Test - @PortraitLandscape - public void testAllAppsSwitchToWorkspace() { - assertNotNull("switchToWorkspace() returned null", - mLauncher.getWorkspace().switchToAllApps() - .switchToWorkspace(/* swipeDown= */ true)); - assertTrue("Launcher internal state is not Workspace", - isInState(() -> LauncherState.NORMAL)); - } - - @Test - @PortraitLandscape - public void testAllAppsSwipeUpToWorkspace() { - assertNotNull("testAllAppsSwipeUpToWorkspace() returned null", - mLauncher.getWorkspace().switchToAllApps() - .switchToWorkspace(/* swipeDown= */ false)); - assertTrue("Launcher internal state is not Workspace", - isInState(() -> LauncherState.NORMAL)); - } - - @Test - @PortraitLandscape - public void testAllAppsDeadzoneForTablet() throws Exception { - assumeTrue(mLauncher.isTablet()); - - mLauncher.getWorkspace().switchToAllApps().dismissByTappingOutsideForTablet( - true /* tapRight */); - mLauncher.getWorkspace().switchToAllApps().dismissByTappingOutsideForTablet( - false /* tapRight */); - } - - @PlatinumTest(focusArea = "launcher") - @Test - @ScreenRecord // b/202433017 - public void testWorkspace() throws Exception { - // Set workspace that includes the chrome Activity app icon on the hotseat. - LauncherLayoutBuilder builder = new LauncherLayoutBuilder() - .atHotseat(0).putApp("com.android.chrome", "com.google.android.apps.chrome.Main"); - mLauncherLayout = TestUtil.setLauncherDefaultLayout(mTargetContext, builder); - reinitializeLauncherData(); - - final Workspace workspace = mLauncher.getWorkspace(); - - // Test that ensureWorkspaceIsScrollable adds a page by dragging an icon there. - executeOnLauncher(launcher -> assertFalse("Initial workspace state is scrollable", - isWorkspaceScrollable(launcher))); - assertEquals("Initial workspace doesn't have the correct page", workspace.pagesPerScreen(), - workspace.getPageCount()); - workspace.verifyWorkspaceAppIconIsGone("Chrome app was found on empty workspace", "Chrome"); - workspace.ensureWorkspaceIsScrollable(); - - executeOnLauncher( - launcher -> assertEquals( - "Ensuring workspace scrollable didn't switch to next screen", - workspace.pagesPerScreen(), getCurrentWorkspacePage(launcher))); - executeOnLauncher( - launcher -> assertTrue("ensureScrollable didn't make workspace scrollable", - isWorkspaceScrollable(launcher))); - assertNotNull("ensureScrollable didn't add Chrome app", - workspace.getWorkspaceAppIcon("Chrome")); - - // Test flinging workspace. - workspace.flingBackward(); - assertTrue("Launcher internal state is not Home", isInState(() -> LauncherState.NORMAL)); - executeOnLauncher( - launcher -> assertEquals("Flinging back didn't switch workspace to page #0", - 0, getCurrentWorkspacePage(launcher))); - - workspace.flingForward(); - executeOnLauncher( - launcher -> assertEquals("Flinging forward didn't switch workspace to next screen", - workspace.pagesPerScreen(), getCurrentWorkspacePage(launcher))); - assertTrue("Launcher internal state is not Home", isInState(() -> LauncherState.NORMAL)); - - // Test starting a workspace app. - final HomeAppIcon app = workspace.getWorkspaceAppIcon("Chrome"); - assertNotNull("No Chrome app in workspace", app); - } - - public static void runIconLaunchFromAllAppsTest(AbstractLauncherUiTest test, AllApps allApps) { - allApps.freeze(); - try { - final AppIcon app = allApps.getAppIcon("TestActivity7"); - assertNotNull("AppIcon.launch returned null", app.launch(getAppPackageName())); - test.executeOnLauncher(launcher -> assertTrue( - "Launcher activity is the top activity; expecting another activity to be the " - + "top one", - test.isInLaunchedApp(launcher))); - } finally { - allApps.unfreeze(); - } - } - - @Test - @PortraitLandscape - public void testAppIconLaunchFromAllAppsFromHome() throws Exception { - final HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps(); - assertTrue("Launcher internal state is not All Apps", - isInState(() -> LauncherState.ALL_APPS)); - - runIconLaunchFromAllAppsTest(this, allApps); - } - - @Test - @Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/293191790 - @ScreenRecord - @PortraitLandscape - public void testWidgets() throws Exception { - // Test opening widgets. - executeOnLauncher(launcher -> - assertTrue("Widgets is initially opened", getWidgetsView(launcher) == null)); - Widgets widgets = mLauncher.getWorkspace().openAllWidgets(); - assertNotNull("openAllWidgets() returned null", widgets); - widgets = mLauncher.getAllWidgets(); - assertNotNull("getAllWidgets() returned null", widgets); - executeOnLauncher(launcher -> - assertTrue("Widgets is not shown", getWidgetsView(launcher).isShown())); - executeOnLauncher(launcher -> assertEquals("Widgets is scrolled upon opening", - 0, getWidgetsScroll(launcher))); - - // Test flinging widgets. - widgets.flingForward(); - Integer flingForwardY = getFromLauncher(launcher -> getWidgetsScroll(launcher)); - executeOnLauncher(launcher -> assertTrue("Flinging forward didn't scroll widgets", - flingForwardY > 0)); - - widgets.flingBackward(); - executeOnLauncher(launcher -> assertTrue("Flinging backward didn't scroll widgets", - getWidgetsScroll(launcher) < flingForwardY)); - - mLauncher.goHome(); - waitForLauncherCondition("Widgets were not closed", - launcher -> getWidgetsView(launcher) == null); - } - - private int getWidgetsScroll(Launcher launcher) { - return getWidgetsView(launcher).computeVerticalScrollOffset(); - } - - private boolean isOptionsPopupVisible(Launcher launcher) { - final ArrowPopup<?> popup = launcher.getOptionsPopup(); - return popup != null && popup.isShown(); - } - - @Test - @PortraitLandscape - @PlatinumTest(focusArea = "launcher") - public void testLaunchMenuItem() throws Exception { - final AllApps allApps = mLauncher.getWorkspace().switchToAllApps(); - allApps.freeze(); - try { - final AppIconMenu menu = allApps. - getAppIcon(APP_NAME). - openDeepShortcutMenu(); - - executeOnLauncher( - launcher -> assertTrue("Launcher internal state didn't switch to Showing Menu", - isOptionsPopupVisible(launcher))); - - final AppIconMenuItem menuItem = menu.getMenuItem(1); - assertEquals("Wrong menu item", "Shortcut 2", menuItem.getText()); - menuItem.launch(getAppPackageName()); - } finally { - allApps.unfreeze(); - } - } - - @Test - public void testLaunchHomeScreenMenuItem() { - // Drag the test app icon to home screen and open short cut menu from the icon - final HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps(); - allApps.freeze(); - try { - allApps.getAppIcon(APP_NAME).dragToWorkspace(false, false); - final AppIconMenu menu = mLauncher.getWorkspace().getWorkspaceAppIcon( - APP_NAME).openDeepShortcutMenu(); - - executeOnLauncher( - launcher -> assertTrue("Launcher internal state didn't switch to Showing Menu", - isOptionsPopupVisible(launcher))); - - final AppIconMenuItem menuItem = menu.getMenuItem(1); - assertEquals("Wrong menu item", "Shortcut 2", menuItem.getText()); - menuItem.launch(getAppPackageName()); - } finally { - allApps.unfreeze(); - } - } - - @PlatinumTest(focusArea = "launcher") - @Test - @PortraitLandscape - @ScreenRecord // b/256898879 - public void testDragAppIcon() throws Throwable { - // 1. Open all apps and wait for load complete. - // 2. Drag icon to homescreen. - // 3. Verify that the icon works on homescreen. - final HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps(); - allApps.freeze(); - try { - allApps.getAppIcon(APP_NAME).dragToWorkspace(false, false); - mLauncher.getWorkspace().getWorkspaceAppIcon(APP_NAME).launch(getAppPackageName()); - } finally { - allApps.unfreeze(); - } - executeOnLauncher(launcher -> assertTrue( - "Launcher activity is the top activity; expecting another activity to be the top " - + "one", - isInLaunchedApp(launcher))); - } - - @Test - @PortraitLandscape - @PlatinumTest(focusArea = "launcher") - public void testDragShortcut() throws Throwable { - // 1. Open all apps and wait for load complete. - // 2. Find the app and long press it to show shortcuts. - // 3. Press icon center until shortcuts appear - final HomeAllApps allApps = mLauncher - .getWorkspace() - .switchToAllApps(); - allApps.freeze(); - try { - final HomeAppIconMenuItem menuItem = allApps - .getAppIcon(APP_NAME) - .openDeepShortcutMenu() - .getMenuItem(0); - final String actualShortcutName = menuItem.getText(); - final String expectedShortcutName = "Shortcut 1"; - - assertEquals(expectedShortcutName, actualShortcutName); - menuItem.dragToWorkspace(false, false); - mLauncher.getWorkspace().getWorkspaceAppIcon(expectedShortcutName) - .launch(getAppPackageName()); - } finally { - allApps.unfreeze(); - } - } - - @Test - @PortraitLandscape - @ScreenRecord - @Ignore // b/233075289 - @PlatinumTest(focusArea = "launcher") - public void testDragToFolder() { - // TODO: add the use case to drag an icon to an existing folder. Currently it either fails - // on tablets or phones due to difference in resolution. - final HomeAppIcon playStoreIcon = createShortcutIfNotExist(STORE_APP_NAME, 0, 1); - final HomeAppIcon gmailIcon = createShortcutInCenterIfNotExist(GMAIL_APP_NAME); - - FolderIcon folderIcon = gmailIcon.dragToIcon(playStoreIcon); - Folder folder = folderIcon.open(); - folder.getAppIcon(STORE_APP_NAME); - folder.getAppIcon(GMAIL_APP_NAME); - Workspace workspace = folder.close(); - - workspace.verifyWorkspaceAppIconIsGone(STORE_APP_NAME + " should be moved to a folder.", - STORE_APP_NAME); - workspace.verifyWorkspaceAppIconIsGone(GMAIL_APP_NAME + " should be moved to a folder.", - GMAIL_APP_NAME); - - final HomeAppIcon mapIcon = createShortcutInCenterIfNotExist(MAPS_APP_NAME); - folderIcon = mapIcon.dragToIcon(folderIcon); - folder = folderIcon.open(); - folder.getAppIcon(MAPS_APP_NAME); - workspace = folder.close(); - - workspace.verifyWorkspaceAppIconIsGone(MAPS_APP_NAME + " should be moved to a folder.", - MAPS_APP_NAME); - } - - @FlakyTest(bugId = 256615483) - @Test - @PortraitLandscape - public void testPressBack() throws Exception { - InstrumentationRegistry.getInstrumentation().getUiAutomation().adoptShellPermissionIdentity( - READ_DEVICE_CONFIG_PERMISSION); - assumeFalse(FeatureFlags.ENABLE_BACK_SWIPE_LAUNCHER_ANIMATION.get()); - mLauncher.getWorkspace().switchToAllApps(); - mLauncher.pressBack(); - mLauncher.getWorkspace(); - waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL); - startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR)); - mLauncher.pressBack(); - mLauncher.getWorkspace(); - waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL); - } - - @Test - @PortraitLandscape - @PlatinumTest(focusArea = "launcher") - public void testDragAndCancelAppIcon() { - final HomeAppIcon homeAppIcon = createShortcutInCenterIfNotExist(GMAIL_APP_NAME); - Point positionBeforeDrag = - mLauncher.getWorkspace().getWorkspaceIconsPositions().get(GMAIL_APP_NAME); - assertNotNull("App not found in Workspace before dragging.", positionBeforeDrag); - - mLauncher.getWorkspace().dragAndCancelAppIcon(homeAppIcon); - - Point positionAfterDrag = - mLauncher.getWorkspace().getWorkspaceIconsPositions().get(GMAIL_APP_NAME); - assertNotNull("App not found in Workspace after dragging.", positionAfterDrag); - assertEquals("App not returned to same position in Workspace after drag & cancel", - positionBeforeDrag, positionAfterDrag); - } - - @Test - @PortraitLandscape - public void testDeleteFromWorkspace() throws Exception { - // test delete both built-in apps and user-installed app from workspace - for (String appName : new String[]{"Gmail", "Play Store", APP_NAME}) { - final HomeAppIcon homeAppIcon = createShortcutInCenterIfNotExist(appName); - Workspace workspace = mLauncher.getWorkspace().deleteAppIcon(homeAppIcon); - workspace.verifyWorkspaceAppIconIsGone( - appName + " app was found after being deleted from workspace", - appName); - } - } - - private void verifyAppUninstalledFromAllApps(Workspace workspace, String appName) { - final HomeAllApps allApps = workspace.switchToAllApps(); - Wait.atMost(appName + " app was found on all apps after being uninstalled", - () -> allApps.tryGetAppIcon(appName) == null, - DEFAULT_UI_TIMEOUT, mLauncher); - } - - @Test - @PortraitLandscape - // TODO(b/293944634): Remove Screenrecord after flaky debug, and add - // @PlatinumTest(focusArea = "launcher") back - @ScreenRecord - public void testUninstallFromWorkspace() throws Exception { - installDummyAppAndWaitForUIUpdate(); - try { - verifyAppUninstalledFromAllApps( - createShortcutInCenterIfNotExist(DUMMY_APP_NAME).uninstall(), DUMMY_APP_NAME); - } finally { - TestUtil.uninstallDummyApp(); - } - } - - @Test - @PortraitLandscape - @PlatinumTest(focusArea = "launcher") - public void testUninstallFromAllApps() throws Exception { - installDummyAppAndWaitForUIUpdate(); - try { - Workspace workspace = mLauncher.getWorkspace(); - final HomeAllApps allApps = workspace.switchToAllApps(); - workspace = allApps.getAppIcon(DUMMY_APP_NAME).uninstall(); - verifyAppUninstalledFromAllApps(workspace, DUMMY_APP_NAME); - } finally { - TestUtil.uninstallDummyApp(); - } - } - - @Test - @PortraitLandscape - @PlatinumTest(focusArea = "launcher") - public void testDragAppIconToWorkspaceCell() throws Exception { - long startTime, endTime, elapsedTime; - Point[] targets = getCornersAndCenterPositions(); - - for (Point target : targets) { - startTime = SystemClock.uptimeMillis(); - final HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps(); - allApps.freeze(); - try { - allApps.getAppIcon(APP_NAME).dragToWorkspace(target.x, target.y); - } finally { - allApps.unfreeze(); - } - // Reset the workspace for the next shortcut creation. - initialize(this, true); - endTime = SystemClock.uptimeMillis(); - elapsedTime = endTime - startTime; - Log.d("testDragAppIconToWorkspaceCellTime", - "Milliseconds taken to drag app icon to workspace cell: " + elapsedTime); - } - - // test to move a shortcut to other cell. - final HomeAppIcon launcherTestAppIcon = createShortcutInCenterIfNotExist(APP_NAME); - for (Point target : targets) { - startTime = SystemClock.uptimeMillis(); - launcherTestAppIcon.dragToWorkspace(target.x, target.y); - endTime = SystemClock.uptimeMillis(); - elapsedTime = endTime - startTime; - Log.d("testDragAppIconToWorkspaceCellTime", - "Milliseconds taken to move shortcut to other cell: " + elapsedTime); - } - } - - /** - * Adds three icons to the workspace and removes one of them by dragging to uninstall. - */ - @Test - @ScreenRecord // b/241821721 - @PlatinumTest(focusArea = "launcher") - public void uninstallWorkspaceIcon() throws IOException { - Point[] gridPositions = getCornersAndCenterPositions(); - StringBuilder sb = new StringBuilder(); - for (Point p : gridPositions) { - sb.append(p).append(", "); - } - Log.d(ICON_MISSING, "allGridPositions: " + sb); - createShortcutIfNotExist(STORE_APP_NAME, gridPositions[0]); - createShortcutIfNotExist(MAPS_APP_NAME, gridPositions[1]); - installDummyAppAndWaitForUIUpdate(); - try { - createShortcutIfNotExist(DUMMY_APP_NAME, gridPositions[2]); - Map<String, Point> initialPositions = - mLauncher.getWorkspace().getWorkspaceIconsPositions(); - assertThat(initialPositions.keySet()) - .containsAtLeast(DUMMY_APP_NAME, MAPS_APP_NAME, STORE_APP_NAME); - - mLauncher.getWorkspace().getWorkspaceAppIcon(DUMMY_APP_NAME).uninstall(); - mLauncher.getWorkspace().verifyWorkspaceAppIconIsGone( - DUMMY_APP_NAME + " was expected to disappear after uninstall.", DUMMY_APP_NAME); - - // Debug for b/288944469 I want to test if we are not waiting enough after removing - // the icon to request the list of icons again, since the items are not removed - // immediately. This should reduce the flake rate - SystemClock.sleep(500); - Map<String, Point> finalPositions = - mLauncher.getWorkspace().getWorkspaceIconsPositions(); - assertThat(finalPositions).doesNotContainKey(DUMMY_APP_NAME); - } finally { - TestUtil.uninstallDummyApp(); - } - } - - @Test - @PortraitLandscape - @PlatinumTest(focusArea = "launcher") - public void testDragShortcutToWorkspaceCell() throws Exception { - Point[] targets = getCornersAndCenterPositions(); - - for (Point target : targets) { - final HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps(); - allApps.freeze(); - try { - allApps.getAppIcon(APP_NAME) - .openDeepShortcutMenu() - .getMenuItem(0) - .dragToWorkspace(target.x, target.y); - } finally { - allApps.unfreeze(); - } - } - } - - @Test - @PortraitLandscape - public void testAddDeleteShortcutOnHotseat() { - mLauncher.getWorkspace() - .deleteAppIcon(mLauncher.getWorkspace().getHotseatAppIcon(0)) - .switchToAllApps() - .getAppIcon(APP_NAME) - .dragToHotseat(0); - mLauncher.getWorkspace().deleteAppIcon( - mLauncher.getWorkspace().getHotseatAppIcon(APP_NAME)); - } - - private void installDummyAppAndWaitForUIUpdate() throws IOException { - TestUtil.installDummyApp(); - waitForLauncherUIUpdate(); - } - - private void waitForLauncherUIUpdate() { - // Wait for model thread completion as it may be processing - // the install event from the SystemService - mLauncher.waitForModelQueueCleared(); - // Wait for Launcher UI thread completion, as it may be processing updating the UI in - // response to the model update. Not that `waitForLauncherInitialized` is just a proxy - // method, we can use any method which touches Launcher UI thread, - mLauncher.waitForLauncherInitialized(); - } - - /** - * @return List of workspace grid coordinates. Those are not pixels. See {@link - * Workspace#getIconGridDimensions()} - */ - private Point[] getCornersAndCenterPositions() { - final Point dimensions = mLauncher.getWorkspace().getIconGridDimensions(); - return new Point[]{ - new Point(0, 1), - new Point(0, dimensions.y - 2), - new Point(dimensions.x - 1, 1), - new Point(dimensions.x - 1, dimensions.y - 2), - new Point(dimensions.x / 2, dimensions.y / 2) - }; - } - - public static String getAppPackageName() { - return getInstrumentation().getContext().getPackageName(); - } - - @Test - public void testGetAppIconName() { - HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps(); - allApps.freeze(); - try { - // getAppIcon() already verifies that the icon is not null and is the correct icon name. - allApps.getAppIcon(APP_NAME); - } finally { - allApps.unfreeze(); - } - } } diff --git a/tests/src/com/android/launcher3/ui/WorkProfileTest.java b/tests/src/com/android/launcher3/ui/TaplWorkProfileTest.java index 5b9adcd80f..f8185641bd 100644 --- a/tests/src/com/android/launcher3/ui/WorkProfileTest.java +++ b/tests/src/com/android/launcher3/ui/TaplWorkProfileTest.java @@ -15,12 +15,11 @@ */ package com.android.launcher3.ui; +import static com.android.launcher3.LauncherPrefs.WORK_EDU_STEP; import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.allapps.AllAppsStore.DEFER_UPDATES_TEST; import static com.android.launcher3.util.TestUtil.installDummyAppForUser; -import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL; -import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -31,6 +30,7 @@ import android.view.View; import androidx.recyclerview.widget.RecyclerView.ViewHolder; +import com.android.launcher3.LauncherPrefs; import com.android.launcher3.R; import com.android.launcher3.allapps.ActivityAllAppsContainerView; import com.android.launcher3.allapps.AllAppsPagedView; @@ -39,7 +39,6 @@ import com.android.launcher3.allapps.WorkPausedCard; import com.android.launcher3.allapps.WorkProfileManager; import com.android.launcher3.tapl.LauncherInstrumentation; import com.android.launcher3.util.TestUtil; -import com.android.launcher3.util.rule.TestStabilityRule.Stability; import org.junit.After; import org.junit.Before; @@ -49,7 +48,7 @@ import org.junit.Test; import java.util.Objects; import java.util.function.Predicate; -public class WorkProfileTest extends AbstractLauncherUiTest { +public class TaplWorkProfileTest extends AbstractLauncherUiTest { private static final int WORK_PAGE = ActivityAllAppsContainerView.AdapterHolder.WORK; @@ -92,8 +91,8 @@ public class WorkProfileTest extends AbstractLauncherUiTest { @After public void removeWorkProfile() throws Exception { - executeOnLauncher(launcher -> { - if (launcher == null || launcher.getAppsView() == null) { + executeOnLauncherInTearDown(launcher -> { + if (launcher.getAppsView() == null) { return; } launcher.getAppsView().getAppsStore().disableDeferUpdates(DEFER_UPDATES_TEST); @@ -103,8 +102,6 @@ public class WorkProfileTest extends AbstractLauncherUiTest { } private void waitForWorkTabSetup() { - // Added for b/243688989 flake to determine if we really are in allApps or not at this point - mLauncher.getAllApps(); waitForLauncherCondition("Work tab not setup", launcher -> { if (launcher.getAppsView().getContentView() instanceof AllAppsPagedView) { launcher.getAppsView().getAppsStore().enableDeferUpdates(DEFER_UPDATES_TEST); @@ -115,7 +112,6 @@ public class WorkProfileTest extends AbstractLauncherUiTest { } @Test - @Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/243688989 public void workTabExists() { assumeTrue(mWorkProfileSetupSuccessful); waitForWorkTabSetup(); @@ -176,12 +172,11 @@ public class WorkProfileTest extends AbstractLauncherUiTest { } @Test - @Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/243688989 public void testEdu() { assumeTrue(mWorkProfileSetupSuccessful); waitForWorkTabSetup(); executeOnLauncher(l -> { - l.getSharedPrefs().edit().putInt(WorkProfileManager.KEY_WORK_EDU_STEP, 0).commit(); + LauncherPrefs.get(l).putSync(WORK_EDU_STEP.to(0)); ((AllAppsPagedView) l.getAppsView().getContentView()).setCurrentPage(WORK_PAGE); l.getAppsView().getWorkManager().reset(); }); diff --git a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/TaplAddConfigWidgetTest.java index b2ce400d8d..d96287f6f4 100644 --- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java +++ b/tests/src/com/android/launcher3/ui/widget/TaplAddConfigWidgetTest.java @@ -52,7 +52,7 @@ import org.junit.runner.RunWith; */ @LargeTest @RunWith(AndroidJUnit4.class) -public class AddConfigWidgetTest extends AbstractLauncherUiTest { +public class TaplAddConfigWidgetTest extends AbstractLauncherUiTest { @Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind(); @@ -101,7 +101,7 @@ public class AddConfigWidgetTest extends AbstractLauncherUiTest { // Verify that the widget id is valid and bound assertNotNull(mAppWidgetManager.getAppWidgetInfo(mWidgetId)); - setResult(acceptConfig); + setResultAndWaitForAnimation(acceptConfig); if (acceptConfig) { Wait.atMost("", new WidgetSearchCondition(), DEFAULT_ACTIVITY_TIMEOUT, mLauncher); assertNotNull(mAppWidgetManager.getAppWidgetInfo(mWidgetId)); @@ -112,12 +112,22 @@ public class AddConfigWidgetTest extends AbstractLauncherUiTest { } } - private void setResult(boolean success) { + private static void setResult(boolean success) { getInstrumentation().getTargetContext().sendBroadcast( WidgetConfigActivity.getCommandIntent(WidgetConfigActivity.class, success ? "clickOK" : "clickCancel")); } + private void setResultAndWaitForAnimation(boolean success) { + if (mLauncher.isLauncher3()) { + setResult(success); + } else { + mLauncher.executeAndWaitForWallpaperAnimation( + () -> setResult(success), + "setting widget coinfig result"); + } + } + /** * Condition for searching widget id */ diff --git a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java index 9dca24bb28..27fda9b680 100644 --- a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java +++ b/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java @@ -15,8 +15,6 @@ */ package com.android.launcher3.ui.widget; -import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName; - import static org.junit.Assert.assertNotNull; import android.platform.test.annotations.PlatinumTest; @@ -44,7 +42,7 @@ import org.junit.runner.RunWith; */ @LargeTest @RunWith(AndroidJUnit4.class) -public class AddWidgetTest extends AbstractLauncherUiTest { +public class TaplAddWidgetTest extends AbstractLauncherUiTest { @Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind(); @@ -96,4 +94,27 @@ public class AddWidgetTest extends AbstractLauncherUiTest { mLauncher.getWorkspace().getWorkspaceAppIcon("Shortcut") .launch(getAppPackageName()); } + + /** + * Test dragging a widget to the workspace and resize it. + */ + @PlatinumTest(focusArea = "launcher") + @Test + public void testResizeWidget() throws Throwable { + new FavoriteItemsTransaction(mTargetContext).commitAndLoadHome(mLauncher); + + waitForLauncherCondition("Workspace didn't finish loading", l -> !l.isWorkspaceLoading()); + + final LauncherAppWidgetProviderInfo widgetInfo = + TestViewHelpers.findWidgetProvider(false /* hasConfigureScreen */); + + WidgetResizeFrame resizeFrame = mLauncher + .getWorkspace() + .openAllWidgets() + .getWidget(widgetInfo.getLabel(mTargetContext.getPackageManager())) + .dragWidgetToWorkspace(); + + assertNotNull("Widget resize frame not shown after widget add", resizeFrame); + resizeFrame.resize(); + } } diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/TaplBindWidgetTest.java index 7db31618b1..6aa746d6d3 100644 --- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java +++ b/tests/src/com/android/launcher3/ui/widget/TaplBindWidgetTest.java @@ -23,6 +23,8 @@ import static com.android.launcher3.model.data.LauncherAppWidgetInfo.FLAG_RESTOR import static com.android.launcher3.provider.LauncherDbUtils.itemIdMatch; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; import static com.android.launcher3.util.WidgetUtils.createWidgetInfo; +import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL; +import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -53,6 +55,7 @@ import com.android.launcher3.tapl.Workspace; import com.android.launcher3.ui.AbstractLauncherUiTest; import com.android.launcher3.ui.TestViewHelpers; import com.android.launcher3.util.rule.ShellCommandRule; +import com.android.launcher3.util.rule.TestStabilityRule; import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; import com.android.launcher3.widget.WidgetManagerHelper; @@ -73,7 +76,7 @@ import java.util.function.Consumer; */ @LargeTest @RunWith(AndroidJUnit4.class) -public class BindWidgetTest extends AbstractLauncherUiTest { +public class TaplBindWidgetTest extends AbstractLauncherUiTest { @Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind(); @@ -139,6 +142,7 @@ public class BindWidgetTest extends AbstractLauncherUiTest { } @Test + @TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/310242894 public void testPendingWidget_withConfigScreen() { // A non-restored widget with config screen get bound and shows a 'Click to setup' UI. // Do not bind the widget @@ -188,6 +192,7 @@ public class BindWidgetTest extends AbstractLauncherUiTest { } @Test + @TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/310242894 public void testPendingWidget_notRestored_brokenInstall() { // A widget which is was being installed once, even if its not being // installed at the moment is not removed. diff --git a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/TaplRequestPinItemTest.java index a6b53692f6..f12f96153b 100644 --- a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java +++ b/tests/src/com/android/launcher3/ui/widget/TaplRequestPinItemTest.java @@ -18,8 +18,6 @@ package com.android.launcher3.ui.widget; import static android.app.PendingIntent.FLAG_MUTABLE; import static android.app.PendingIntent.FLAG_ONE_SHOT; -import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName; - import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotSame; @@ -44,11 +42,9 @@ import com.android.launcher3.testcomponent.AppWidgetNoConfig; import com.android.launcher3.testcomponent.AppWidgetWithConfig; import com.android.launcher3.testcomponent.RequestPinItemActivity; import com.android.launcher3.ui.AbstractLauncherUiTest; -import com.android.launcher3.ui.TaplTestsLauncher3; import com.android.launcher3.util.LauncherBindableItemsContainer.ItemOperator; import com.android.launcher3.util.Wait; import com.android.launcher3.util.Wait.Condition; -import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord; import com.android.launcher3.util.rule.ShellCommandRule; import org.junit.Before; @@ -63,7 +59,7 @@ import java.util.UUID; */ @LargeTest @RunWith(AndroidJUnit4.class) -public class RequestPinItemTest extends AbstractLauncherUiTest { +public class TaplRequestPinItemTest extends AbstractLauncherUiTest { @Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind(); @@ -78,14 +74,13 @@ public class RequestPinItemTest extends AbstractLauncherUiTest { super.setUp(); mCallbackAction = UUID.randomUUID().toString(); mShortcutId = UUID.randomUUID().toString(); - TaplTestsLauncher3.initialize(this); + AbstractLauncherUiTest.initialize(this); } @Test public void testEmpty() throws Throwable { /* needed while the broken tests are being fixed */ } @Test - @ScreenRecord // b/215673732 public void testPinWidgetNoConfig() throws Throwable { runTest("pinWidgetNoConfig", true, (info, view) -> info instanceof LauncherAppWidgetInfo && ((LauncherAppWidgetInfo) info).appWidgetId == mAppWidgetId && @@ -94,7 +89,6 @@ public class RequestPinItemTest extends AbstractLauncherUiTest { } @Test - @ScreenRecord // b/215673732 public void testPinWidgetNoConfig_customPreview() throws Throwable { // Command to set custom preview Intent command = RequestPinItemActivity.getCommandIntent( @@ -108,7 +102,6 @@ public class RequestPinItemTest extends AbstractLauncherUiTest { } @Test - @ScreenRecord // b/215673732 public void testPinWidgetWithConfig() throws Throwable { runTest("pinWidgetWithConfig", true, (info, view) -> info instanceof LauncherAppWidgetInfo && diff --git a/tests/src/com/android/launcher3/ui/widget/TaplWidgetPickerTest.java b/tests/src/com/android/launcher3/ui/widget/TaplWidgetPickerTest.java new file mode 100644 index 0000000000..bc736831b6 --- /dev/null +++ b/tests/src/com/android/launcher3/ui/widget/TaplWidgetPickerTest.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.ui.widget; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.MediumTest; + +import com.android.launcher3.Launcher; +import com.android.launcher3.tapl.Widgets; +import com.android.launcher3.ui.AbstractLauncherUiTest; +import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape; +import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord; +import com.android.launcher3.widget.picker.WidgetsFullSheet; +import com.android.launcher3.widget.picker.WidgetsRecyclerView; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * This test run in both Out of process (Oop) and in-process (Ipc). + * Make sure the basic interactions with the WidgetPicker works. + */ +@MediumTest +@RunWith(AndroidJUnit4.class) +public class TaplWidgetPickerTest extends AbstractLauncherUiTest { + + @Before + public void setUp() throws Exception { + super.setUp(); + initialize(this); + } + + private WidgetsRecyclerView getWidgetsView(Launcher launcher) { + return WidgetsFullSheet.getWidgetsView(launcher); + } + + private int getWidgetsScroll(Launcher launcher) { + return getWidgetsView(launcher).computeVerticalScrollOffset(); + } + + /** + * Open Widget picker, make sure the widget picker can scroll and then go to home screen. + */ + @Test + @ScreenRecord + @PortraitLandscape + public void testWidgets() { + mLauncher.goHome(); + // Test opening widgets. + executeOnLauncher(launcher -> + assertTrue("Widgets is initially opened", getWidgetsView(launcher) == null)); + Widgets widgets = mLauncher.getWorkspace().openAllWidgets(); + assertNotNull("openAllWidgets() returned null", widgets); + widgets = mLauncher.getAllWidgets(); + assertNotNull("getAllWidgets() returned null", widgets); + executeOnLauncher(launcher -> + assertTrue("Widgets is not shown", getWidgetsView(launcher).isShown())); + executeOnLauncher(launcher -> assertEquals("Widgets is scrolled upon opening", + 0, getWidgetsScroll(launcher))); + + // Test flinging widgets. + widgets.flingForward(); + Integer flingForwardY = getFromLauncher(launcher -> getWidgetsScroll(launcher)); + executeOnLauncher(launcher -> assertTrue("Flinging forward didn't scroll widgets", + flingForwardY > 0)); + + widgets.flingBackward(); + executeOnLauncher(launcher -> assertTrue("Flinging backward didn't scroll widgets", + getWidgetsScroll(launcher) < flingForwardY)); + + mLauncher.goHome(); + waitForLauncherCondition("Widgets were not closed", + launcher -> getWidgetsView(launcher) == null); + } +} diff --git a/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java b/tests/src/com/android/launcher3/ui/workspace/TaplThemeIconsTest.java index 8e5e9cc2a1..a3d33441d4 100644 --- a/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java +++ b/tests/src/com/android/launcher3/ui/workspace/TaplThemeIconsTest.java @@ -15,6 +15,8 @@ */ package com.android.launcher3.ui.workspace; +import static com.android.launcher3.util.TestConstants.AppNames.TEST_APP_NAME; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -34,7 +36,6 @@ import com.android.launcher3.tapl.HomeAllApps; import com.android.launcher3.tapl.HomeAppIcon; import com.android.launcher3.tapl.HomeAppIconMenuItem; import com.android.launcher3.ui.AbstractLauncherUiTest; -import com.android.launcher3.ui.TaplTestsLauncher3; import com.android.launcher3.util.Executors; import org.junit.Test; @@ -48,16 +49,15 @@ import java.util.Queue; * Note running these tests will clear the workspace on the device. */ @LargeTest -public class ThemeIconsTest extends AbstractLauncherUiTest { +public class TaplThemeIconsTest extends AbstractLauncherUiTest { private static final String APP_NAME = "IconThemedActivity"; - private static final String SHORTCUT_APP_NAME = "LauncherTestApp"; private static final String SHORTCUT_NAME = "Shortcut 1"; @Test public void testIconWithoutTheme() throws Exception { setThemeEnabled(false); - TaplTestsLauncher3.initialize(this); + AbstractLauncherUiTest.initialize(this); HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps(); allApps.freeze(); @@ -75,13 +75,13 @@ public class ThemeIconsTest extends AbstractLauncherUiTest { @Test public void testShortcutIconWithoutTheme() throws Exception { setThemeEnabled(false); - TaplTestsLauncher3.initialize(this); + AbstractLauncherUiTest.initialize(this); HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps(); allApps.freeze(); try { - HomeAppIcon icon = allApps.getAppIcon(SHORTCUT_APP_NAME); + HomeAppIcon icon = allApps.getAppIcon(TEST_APP_NAME); HomeAppIconMenuItem shortcutItem = (HomeAppIconMenuItem) icon.openDeepShortcutMenu().getMenuItem(SHORTCUT_NAME); shortcutItem.dragToWorkspace(false, false); @@ -94,7 +94,7 @@ public class ThemeIconsTest extends AbstractLauncherUiTest { @Test public void testIconWithTheme() throws Exception { setThemeEnabled(true); - TaplTestsLauncher3.initialize(this); + AbstractLauncherUiTest.initialize(this); HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps(); allApps.freeze(); @@ -112,13 +112,13 @@ public class ThemeIconsTest extends AbstractLauncherUiTest { @Test public void testShortcutIconWithTheme() throws Exception { setThemeEnabled(true); - TaplTestsLauncher3.initialize(this); + AbstractLauncherUiTest.initialize(this); HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps(); allApps.freeze(); try { - HomeAppIcon icon = allApps.getAppIcon(SHORTCUT_APP_NAME); + HomeAppIcon icon = allApps.getAppIcon(TEST_APP_NAME); HomeAppIconMenuItem shortcutItem = (HomeAppIconMenuItem) icon.openDeepShortcutMenu().getMenuItem(SHORTCUT_NAME); shortcutItem.dragToWorkspace(false, false); diff --git a/tests/src/com/android/launcher3/ui/workspace/TwoPanelWorkspaceTest.java b/tests/src/com/android/launcher3/ui/workspace/TaplTwoPanelWorkspaceTest.java index 62a8179a9d..3693163d21 100644 --- a/tests/src/com/android/launcher3/ui/workspace/TwoPanelWorkspaceTest.java +++ b/tests/src/com/android/launcher3/ui/workspace/TaplTwoPanelWorkspaceTest.java @@ -16,6 +16,11 @@ package com.android.launcher3.ui.workspace; +import static com.android.launcher3.util.TestConstants.AppNames.CHROME_APP_NAME; +import static com.android.launcher3.util.TestConstants.AppNames.MAPS_APP_NAME; +import static com.android.launcher3.util.TestConstants.AppNames.MESSAGES_APP_NAME; +import static com.android.launcher3.util.TestConstants.AppNames.STORE_APP_NAME; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -30,7 +35,6 @@ import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.tapl.Workspace; import com.android.launcher3.ui.AbstractLauncherUiTest; import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape; -import com.android.launcher3.ui.TaplTestsLauncher3; import com.android.launcher3.util.LauncherLayoutBuilder; import com.android.launcher3.util.TestUtil; @@ -50,7 +54,7 @@ import java.util.stream.Collectors; */ @LargeTest @RunWith(AndroidJUnit4.class) -public class TwoPanelWorkspaceTest extends AbstractLauncherUiTest { +public class TaplTwoPanelWorkspaceTest extends AbstractLauncherUiTest { private AutoCloseable mLauncherLayout; @@ -69,21 +73,21 @@ public class TwoPanelWorkspaceTest extends AbstractLauncherUiTest { .atWorkspace(3, -1, 0).putApp( "com.android.vending", "com.android.vending.AssetBrowserActivity"); mLauncherLayout = TestUtil.setLauncherDefaultLayout(mTargetContext, builder); - TaplTestsLauncher3.initialize(this); + AbstractLauncherUiTest.initialize(this); assumeTrue(mLauncher.isTwoPanels()); // Pre verifying the screens executeOnLauncher(launcher -> { launcher.enableHotseatEdu(false); assertPagesExist(launcher, 0, 1); - assertItemsOnPage(launcher, 0, "Play Store", "Maps"); + assertItemsOnPage(launcher, 0, STORE_APP_NAME, MAPS_APP_NAME); assertPageEmpty(launcher, 1); }); } @After public void tearDown() throws Exception { - executeOnLauncher(launcher -> launcher.enableHotseatEdu(true)); + executeOnLauncherInTearDown(launcher -> launcher.enableHotseatEdu(true)); if (mLauncherLayout != null) { mLauncherLayout.close(); } @@ -94,12 +98,12 @@ public class TwoPanelWorkspaceTest extends AbstractLauncherUiTest { public void testDragIconToRightPanel() { Workspace workspace = mLauncher.getWorkspace(); - workspace.dragIcon(workspace.getHotseatAppIcon("Chrome"), 1); + workspace.dragIcon(workspace.getHotseatAppIcon(CHROME_APP_NAME), 1); executeOnLauncher(launcher -> { assertPagesExist(launcher, 0, 1); - assertItemsOnPage(launcher, 0, "Maps", "Play Store"); - assertItemsOnPage(launcher, 1, "Chrome"); + assertItemsOnPage(launcher, 0, MAPS_APP_NAME, STORE_APP_NAME); + assertItemsOnPage(launcher, 1, CHROME_APP_NAME); }); } @@ -108,52 +112,52 @@ public class TwoPanelWorkspaceTest extends AbstractLauncherUiTest { public void testSinglePageDragIconWhenMultiplePageScrollingIsPossible() { Workspace workspace = mLauncher.getWorkspace(); - workspace.dragIcon(workspace.getHotseatAppIcon("Chrome"), 2); + workspace.dragIcon(workspace.getHotseatAppIcon(CHROME_APP_NAME), 2); workspace.flingBackward(); - workspace.dragIcon(workspace.getWorkspaceAppIcon("Maps"), 3); + workspace.dragIcon(workspace.getWorkspaceAppIcon(MAPS_APP_NAME), 3); executeOnLauncher(launcher -> { assertPagesExist(launcher, 0, 1, 2, 3); - assertItemsOnPage(launcher, 0, "Play Store"); + assertItemsOnPage(launcher, 0, STORE_APP_NAME); assertPageEmpty(launcher, 1); - assertItemsOnPage(launcher, 2, "Chrome"); - assertItemsOnPage(launcher, 3, "Maps"); + assertItemsOnPage(launcher, 2, CHROME_APP_NAME); + assertItemsOnPage(launcher, 3, MAPS_APP_NAME); }); - workspace.dragIcon(workspace.getWorkspaceAppIcon("Maps"), 3); + workspace.dragIcon(workspace.getWorkspaceAppIcon(MAPS_APP_NAME), 3); executeOnLauncher(launcher -> { assertPagesExist(launcher, 0, 1, 2, 3, 4, 5); - assertItemsOnPage(launcher, 0, "Play Store"); + assertItemsOnPage(launcher, 0, STORE_APP_NAME); assertPageEmpty(launcher, 1); - assertItemsOnPage(launcher, 2, "Chrome"); + assertItemsOnPage(launcher, 2, CHROME_APP_NAME); assertPageEmpty(launcher, 3); assertPageEmpty(launcher, 4); - assertItemsOnPage(launcher, 5, "Maps"); + assertItemsOnPage(launcher, 5, MAPS_APP_NAME); }); - workspace.dragIcon(workspace.getWorkspaceAppIcon("Maps"), -1); + workspace.dragIcon(workspace.getWorkspaceAppIcon(MAPS_APP_NAME), -1); executeOnLauncher(launcher -> { assertPagesExist(launcher, 0, 1, 2, 3); - assertItemsOnPage(launcher, 0, "Play Store"); + assertItemsOnPage(launcher, 0, STORE_APP_NAME); assertPageEmpty(launcher, 1); - assertItemsOnPage(launcher, 2, "Chrome"); - assertItemsOnPage(launcher, 3, "Maps"); + assertItemsOnPage(launcher, 2, CHROME_APP_NAME); + assertItemsOnPage(launcher, 3, MAPS_APP_NAME); }); - workspace.dragIcon(workspace.getWorkspaceAppIcon("Maps"), -1); + workspace.dragIcon(workspace.getWorkspaceAppIcon(MAPS_APP_NAME), -1); workspace.flingForward(); - workspace.dragIcon(workspace.getWorkspaceAppIcon("Chrome"), -2); + workspace.dragIcon(workspace.getWorkspaceAppIcon(CHROME_APP_NAME), -2); executeOnLauncher(launcher -> { assertPagesExist(launcher, 0, 1); - assertItemsOnPage(launcher, 0, "Chrome", "Play Store"); - assertItemsOnPage(launcher, 1, "Maps"); + assertItemsOnPage(launcher, 0, CHROME_APP_NAME, STORE_APP_NAME); + assertItemsOnPage(launcher, 1, MAPS_APP_NAME); }); } @@ -162,13 +166,13 @@ public class TwoPanelWorkspaceTest extends AbstractLauncherUiTest { public void testDragIconToPage2() { Workspace workspace = mLauncher.getWorkspace(); - workspace.dragIcon(workspace.getWorkspaceAppIcon("Maps"), 2); + workspace.dragIcon(workspace.getWorkspaceAppIcon(MAPS_APP_NAME), 2); executeOnLauncher(launcher -> { assertPagesExist(launcher, 0, 1, 2, 3); - assertItemsOnPage(launcher, 0, "Play Store"); + assertItemsOnPage(launcher, 0, STORE_APP_NAME); assertPageEmpty(launcher, 1); - assertItemsOnPage(launcher, 2, "Maps"); + assertItemsOnPage(launcher, 2, MAPS_APP_NAME); assertPageEmpty(launcher, 3); }); } @@ -179,14 +183,14 @@ public class TwoPanelWorkspaceTest extends AbstractLauncherUiTest { Workspace workspace = mLauncher.getWorkspace(); // b/299522368 sometimes the phone app is not present in the hotseat. - workspace.dragIcon(workspace.getHotseatAppIcon("Chrome"), 3); + workspace.dragIcon(workspace.getHotseatAppIcon(CHROME_APP_NAME), 3); executeOnLauncher(launcher -> { assertPagesExist(launcher, 0, 1, 2, 3); - assertItemsOnPage(launcher, 0, "Play Store", "Maps"); + assertItemsOnPage(launcher, 0, STORE_APP_NAME, MAPS_APP_NAME); assertPageEmpty(launcher, 1); assertPageEmpty(launcher, 2); - assertItemsOnPage(launcher, 3, "Chrome"); + assertItemsOnPage(launcher, 3, CHROME_APP_NAME); }); } @@ -195,44 +199,44 @@ public class TwoPanelWorkspaceTest extends AbstractLauncherUiTest { public void testMultiplePageDragIcon() { Workspace workspace = mLauncher.getWorkspace(); - workspace.dragIcon(workspace.getHotseatAppIcon("Messages"), 2); + workspace.dragIcon(workspace.getHotseatAppIcon(MESSAGES_APP_NAME), 2); workspace.flingBackward(); - workspace.dragIcon(workspace.getWorkspaceAppIcon("Maps"), 5); + workspace.dragIcon(workspace.getWorkspaceAppIcon(MAPS_APP_NAME), 5); executeOnLauncher(launcher -> { assertPagesExist(launcher, 0, 1, 2, 3, 4, 5); - assertItemsOnPage(launcher, 0, "Play Store"); + assertItemsOnPage(launcher, 0, STORE_APP_NAME); assertPageEmpty(launcher, 1); - assertItemsOnPage(launcher, 2, "Messages"); + assertItemsOnPage(launcher, 2, MESSAGES_APP_NAME); assertPageEmpty(launcher, 3); assertPageEmpty(launcher, 4); - assertItemsOnPage(launcher, 5, "Maps"); + assertItemsOnPage(launcher, 5, MAPS_APP_NAME); }); workspace.flingBackward(); - workspace.dragIcon(workspace.getWorkspaceAppIcon("Messages"), 4); + workspace.dragIcon(workspace.getWorkspaceAppIcon(MESSAGES_APP_NAME), 4); executeOnLauncher(launcher -> { assertPagesExist(launcher, 0, 1, 4, 5, 6, 7); - assertItemsOnPage(launcher, 0, "Play Store"); + assertItemsOnPage(launcher, 0, STORE_APP_NAME); assertPageEmpty(launcher, 1); assertPageEmpty(launcher, 4); - assertItemsOnPage(launcher, 5, "Maps"); - assertItemsOnPage(launcher, 6, "Messages"); + assertItemsOnPage(launcher, 5, MAPS_APP_NAME); + assertItemsOnPage(launcher, 6, MESSAGES_APP_NAME); assertPageEmpty(launcher, 7); }); - workspace.dragIcon(workspace.getWorkspaceAppIcon("Messages"), -3); + workspace.dragIcon(workspace.getWorkspaceAppIcon(MESSAGES_APP_NAME), -3); executeOnLauncher(launcher -> { assertPagesExist(launcher, 0, 1, 4, 5); - assertItemsOnPage(launcher, 0, "Play Store"); - assertItemsOnPage(launcher, 1, "Messages"); + assertItemsOnPage(launcher, 0, STORE_APP_NAME); + assertItemsOnPage(launcher, 1, MESSAGES_APP_NAME); assertPageEmpty(launcher, 4); - assertItemsOnPage(launcher, 5, "Maps"); + assertItemsOnPage(launcher, 5, MAPS_APP_NAME); }); } @@ -241,38 +245,38 @@ public class TwoPanelWorkspaceTest extends AbstractLauncherUiTest { public void testEmptyPageDoesNotGetRemovedIfPagePairIsNotEmpty() { Workspace workspace = mLauncher.getWorkspace(); - workspace.dragIcon(workspace.getWorkspaceAppIcon("Maps"), 3); - workspace.dragIcon(workspace.getHotseatAppIcon("Chrome"), 0); + workspace.dragIcon(workspace.getWorkspaceAppIcon(MAPS_APP_NAME), 3); + workspace.dragIcon(workspace.getHotseatAppIcon(CHROME_APP_NAME), 0); executeOnLauncher(launcher -> { assertPagesExist(launcher, 0, 1, 2, 3); - assertItemsOnPage(launcher, 0, "Play Store"); + assertItemsOnPage(launcher, 0, STORE_APP_NAME); assertPageEmpty(launcher, 1); - assertItemsOnPage(launcher, 2, "Chrome"); - assertItemsOnPage(launcher, 3, "Maps"); + assertItemsOnPage(launcher, 2, CHROME_APP_NAME); + assertItemsOnPage(launcher, 3, MAPS_APP_NAME); }); - workspace.dragIcon(workspace.getWorkspaceAppIcon("Maps"), -1); + workspace.dragIcon(workspace.getWorkspaceAppIcon(MAPS_APP_NAME), -1); executeOnLauncher(launcher -> { assertPagesExist(launcher, 0, 1, 2, 3); - assertItemsOnPage(launcher, 0, "Play Store"); - assertItemsOnPage(launcher, 1, "Maps"); - assertItemsOnPage(launcher, 2, "Chrome"); + assertItemsOnPage(launcher, 0, STORE_APP_NAME); + assertItemsOnPage(launcher, 1, MAPS_APP_NAME); + assertItemsOnPage(launcher, 2, CHROME_APP_NAME); assertPageEmpty(launcher, 3); }); // Move Chrome to the right panel as well, to make sure pages are not deleted whichever // page is the empty one workspace.flingForward(); - workspace.dragIcon(workspace.getWorkspaceAppIcon("Chrome"), 1); + workspace.dragIcon(workspace.getWorkspaceAppIcon(CHROME_APP_NAME), 1); executeOnLauncher(launcher -> { assertPagesExist(launcher, 0, 1, 2, 3); - assertItemsOnPage(launcher, 0, "Play Store"); - assertItemsOnPage(launcher, 1, "Maps"); + assertItemsOnPage(launcher, 0, STORE_APP_NAME); + assertItemsOnPage(launcher, 1, MAPS_APP_NAME); assertPageEmpty(launcher, 2); - assertItemsOnPage(launcher, 3, "Chrome"); + assertItemsOnPage(launcher, 3, CHROME_APP_NAME); }); } @@ -281,25 +285,25 @@ public class TwoPanelWorkspaceTest extends AbstractLauncherUiTest { public void testEmptyPagesGetRemovedIfBothPagesAreEmpty() { Workspace workspace = mLauncher.getWorkspace(); - workspace.dragIcon(workspace.getWorkspaceAppIcon("Play Store"), 2); - workspace.dragIcon(workspace.getHotseatAppIcon("Chrome"), 1); + workspace.dragIcon(workspace.getWorkspaceAppIcon(STORE_APP_NAME), 2); + workspace.dragIcon(workspace.getHotseatAppIcon(CHROME_APP_NAME), 1); executeOnLauncher(launcher -> { assertPagesExist(launcher, 0, 1, 2, 3); - assertItemsOnPage(launcher, 0, "Maps"); + assertItemsOnPage(launcher, 0, MAPS_APP_NAME); assertPageEmpty(launcher, 1); - assertItemsOnPage(launcher, 2, "Play Store"); - assertItemsOnPage(launcher, 3, "Chrome"); + assertItemsOnPage(launcher, 2, STORE_APP_NAME); + assertItemsOnPage(launcher, 3, CHROME_APP_NAME); }); - workspace.dragIcon(workspace.getWorkspaceAppIcon("Chrome"), -1); + workspace.dragIcon(workspace.getWorkspaceAppIcon(CHROME_APP_NAME), -1); workspace.flingForward(); - workspace.dragIcon(workspace.getWorkspaceAppIcon("Play Store"), -2); + workspace.dragIcon(workspace.getWorkspaceAppIcon(STORE_APP_NAME), -2); executeOnLauncher(launcher -> { assertPagesExist(launcher, 0, 1); - assertItemsOnPage(launcher, 0, "Play Store", "Maps"); - assertItemsOnPage(launcher, 1, "Chrome"); + assertItemsOnPage(launcher, 0, STORE_APP_NAME, MAPS_APP_NAME); + assertItemsOnPage(launcher, 1, CHROME_APP_NAME); }); } @@ -308,28 +312,28 @@ public class TwoPanelWorkspaceTest extends AbstractLauncherUiTest { public void testMiddleEmptyPagesGetRemoved() { Workspace workspace = mLauncher.getWorkspace(); - workspace.dragIcon(workspace.getWorkspaceAppIcon("Maps"), 2); - workspace.dragIcon(workspace.getHotseatAppIcon("Messages"), 3); + workspace.dragIcon(workspace.getWorkspaceAppIcon(MAPS_APP_NAME), 2); + workspace.dragIcon(workspace.getHotseatAppIcon(MESSAGES_APP_NAME), 3); executeOnLauncher(launcher -> { assertPagesExist(launcher, 0, 1, 2, 3, 4, 5); - assertItemsOnPage(launcher, 0, "Play Store"); + assertItemsOnPage(launcher, 0, STORE_APP_NAME); assertPageEmpty(launcher, 1); - assertItemsOnPage(launcher, 2, "Maps"); + assertItemsOnPage(launcher, 2, MAPS_APP_NAME); assertPageEmpty(launcher, 3); assertPageEmpty(launcher, 4); - assertItemsOnPage(launcher, 5, "Messages"); + assertItemsOnPage(launcher, 5, MESSAGES_APP_NAME); }); workspace.flingBackward(); - workspace.dragIcon(workspace.getWorkspaceAppIcon("Maps"), 2); + workspace.dragIcon(workspace.getWorkspaceAppIcon(MAPS_APP_NAME), 2); executeOnLauncher(launcher -> { assertPagesExist(launcher, 0, 1, 4, 5); - assertItemsOnPage(launcher, 0, "Play Store"); + assertItemsOnPage(launcher, 0, STORE_APP_NAME); assertPageEmpty(launcher, 1); - assertItemsOnPage(launcher, 4, "Maps"); - assertItemsOnPage(launcher, 5, "Messages"); + assertItemsOnPage(launcher, 4, MAPS_APP_NAME); + assertItemsOnPage(launcher, 5, MESSAGES_APP_NAME); }); } diff --git a/tests/src/com/android/launcher3/ui/workspace/TaplWorkspaceTest.java b/tests/src/com/android/launcher3/ui/workspace/TaplWorkspaceTest.java new file mode 100644 index 0000000000..59c82a7b2f --- /dev/null +++ b/tests/src/com/android/launcher3/ui/workspace/TaplWorkspaceTest.java @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.ui.workspace; + +import static com.android.launcher3.ui.AbstractLauncherUiTest.initialize; +import static com.android.launcher3.util.TestConstants.AppNames.CHROME_APP_NAME; + +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.platform.test.annotations.PlatinumTest; + +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherState; +import com.android.launcher3.tapl.HomeAppIcon; +import com.android.launcher3.tapl.Workspace; +import com.android.launcher3.ui.AbstractLauncherUiTest; +import com.android.launcher3.util.LauncherLayoutBuilder; +import com.android.launcher3.util.TestUtil; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +/** + * Test the basic interactions of the Workspace, adding pages, moving the pages and removing pages. + */ +public class TaplWorkspaceTest extends AbstractLauncherUiTest { + + private AutoCloseable mLauncherLayout; + + private static boolean isWorkspaceScrollable(Launcher launcher) { + return launcher.getWorkspace().getPageCount() > launcher.getWorkspace().getPanelCount(); + } + + private int getCurrentWorkspacePage(Launcher launcher) { + return launcher.getWorkspace().getCurrentPage(); + } + + @Before + public void setUp() throws Exception { + super.setUp(); + initialize(this); + } + + @After + public void tearDown() throws Exception { + if (mLauncherLayout != null) { + mLauncherLayout.close(); + } + } + + /** + * Add an icon and add a page to ensure the Workspace is scrollable and also make sure we can + * move between workspaces. After, make sure we can launch an app from the Workspace. + * @throws Exception if we can't set the defaults icons that will appear at the beginning. + */ + @PlatinumTest(focusArea = "launcher") + @Test + public void testWorkspace() throws Exception { + // Set workspace that includes the chrome Activity app icon on the hotseat. + LauncherLayoutBuilder builder = new LauncherLayoutBuilder() + .atHotseat(0).putApp("com.android.chrome", "com.google.android.apps.chrome.Main"); + mLauncherLayout = TestUtil.setLauncherDefaultLayout(mTargetContext, builder); + reinitializeLauncherData(); + + final Workspace workspace = mLauncher.getWorkspace(); + + // Test that ensureWorkspaceIsScrollable adds a page by dragging an icon there. + executeOnLauncher(launcher -> assertFalse("Initial workspace state is scrollable", + isWorkspaceScrollable(launcher))); + assertEquals("Initial workspace doesn't have the correct page", workspace.pagesPerScreen(), + workspace.getPageCount()); + workspace.verifyWorkspaceAppIconIsGone("Chrome app was found on empty workspace", + CHROME_APP_NAME); + workspace.ensureWorkspaceIsScrollable(); + + executeOnLauncher( + launcher -> assertEquals( + "Ensuring workspace scrollable didn't switch to next screen", + workspace.pagesPerScreen(), getCurrentWorkspacePage(launcher))); + executeOnLauncher( + launcher -> assertTrue("ensureScrollable didn't make workspace scrollable", + isWorkspaceScrollable(launcher))); + assertNotNull("ensureScrollable didn't add Chrome app", + workspace.getWorkspaceAppIcon(CHROME_APP_NAME)); + + // Test flinging workspace. + workspace.flingBackward(); + assertTrue("Launcher internal state is not Home", isInState(() -> LauncherState.NORMAL)); + executeOnLauncher( + launcher -> assertEquals("Flinging back didn't switch workspace to page #0", + 0, getCurrentWorkspacePage(launcher))); + + workspace.flingForward(); + executeOnLauncher( + launcher -> assertEquals("Flinging forward didn't switch workspace to next screen", + workspace.pagesPerScreen(), getCurrentWorkspacePage(launcher))); + assertTrue("Launcher internal state is not Home", isInState(() -> LauncherState.NORMAL)); + + // Test starting a workspace app. + final HomeAppIcon app = workspace.getWorkspaceAppIcon(CHROME_APP_NAME); + assertNotNull("No Chrome app in workspace", app); + } + + + /** + * Similar to {@link TaplWorkspaceTest#testWorkspace} but here we also make sure we can delete + * the pages. + */ + @PlatinumTest(focusArea = "launcher") + @Test + public void testAddAndDeletePageAndFling() { + Workspace workspace = mLauncher.getWorkspace(); + // Get the first app from the hotseat + HomeAppIcon hotSeatIcon = workspace.getHotseatAppIcon(0); + String appName = hotSeatIcon.getIconName(); + + // Add one page by dragging app to page 1. + workspace.dragIcon(hotSeatIcon, workspace.pagesPerScreen()); + assertEquals("Incorrect Page count Number", + workspace.pagesPerScreen() * 2, + workspace.getPageCount()); + + // Delete one page by dragging app to hot seat. + workspace.getWorkspaceAppIcon(appName).dragToHotseat(0); + + // Refresh workspace to avoid using stale container error. + workspace = mLauncher.getWorkspace(); + assertEquals("Incorrect Page count Number", + workspace.pagesPerScreen(), + workspace.getPageCount()); + } +} diff --git a/tests/src/com/android/launcher3/util/CellContentDimensionsTest.kt b/tests/src/com/android/launcher3/util/CellContentDimensionsTest.kt new file mode 100644 index 0000000000..4770546e51 --- /dev/null +++ b/tests/src/com/android/launcher3/util/CellContentDimensionsTest.kt @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.util + +import android.content.Context +import android.content.res.Configuration +import android.util.DisplayMetrics +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class CellContentDimensionsTest { + private var context: Context? = null + private val runningContext: Context = ApplicationProvider.getApplicationContext() + private lateinit var iconSizeSteps: IconSizeSteps + + @Before + fun setup() { + // 160dp makes 1px = 1dp + val config = + Configuration(runningContext.resources.configuration).apply { + this.densityDpi = DisplayMetrics.DENSITY_DEFAULT + } + context = runningContext.createConfigurationContext(config) + iconSizeSteps = IconSizeSteps(context!!.resources) + } + + @Test + fun dimensionsFitTheCell() { + val cellSize = Pair(80, 104) + val cellContentDimensions = + CellContentDimensions(iconSizePx = 66, iconDrawablePaddingPx = 8, iconTextSizePx = 14) + + val contentHeight = + cellContentDimensions.resizeToFitCellHeight(cellSize.second, iconSizeSteps) + + assertThat(contentHeight).isEqualTo(93) + cellContentDimensions.run { + assertThat(iconSizePx).isEqualTo(66) + assertThat(iconDrawablePaddingPx).isEqualTo(8) + assertThat(iconTextSizePx).isEqualTo(14) + } + } + + @Test + fun decreasePadding() { + val cellSize = Pair(67, 87) + val cellContentDimensions = + CellContentDimensions(iconSizePx = 66, iconDrawablePaddingPx = 8, iconTextSizePx = 14) + + val contentHeight = + cellContentDimensions.resizeToFitCellHeight(cellSize.second, iconSizeSteps) + + assertThat(contentHeight).isEqualTo(87) + cellContentDimensions.run { + assertThat(iconSizePx).isEqualTo(66) + assertThat(iconDrawablePaddingPx).isEqualTo(2) + assertThat(iconTextSizePx).isEqualTo(14) + } + } + + @Test + fun decreaseIcon() { + val cellSize = Pair(65, 84) + val cellContentDimensions = + CellContentDimensions(iconSizePx = 66, iconDrawablePaddingPx = 8, iconTextSizePx = 14) + + val contentHeight = + cellContentDimensions.resizeToFitCellHeight(cellSize.second, iconSizeSteps) + + assertThat(contentHeight).isEqualTo(82) + cellContentDimensions.run { + assertThat(iconSizePx).isEqualTo(63) + assertThat(iconDrawablePaddingPx).isEqualTo(0) + assertThat(iconTextSizePx).isEqualTo(14) + } + } + + @Test + fun decreaseText() { + val cellSize = Pair(63, 81) + val cellContentDimensions = + CellContentDimensions(iconSizePx = 66, iconDrawablePaddingPx = 8, iconTextSizePx = 14) + + val contentHeight = + cellContentDimensions.resizeToFitCellHeight(cellSize.second, iconSizeSteps) + + assertThat(contentHeight).isEqualTo(81) + cellContentDimensions.run { + assertThat(iconSizePx).isEqualTo(63) + assertThat(iconDrawablePaddingPx).isEqualTo(0) + assertThat(iconTextSizePx).isEqualTo(13) + } + } + + @Test + fun decreaseIconAndTextTwoSteps() { + val cellSize = Pair(60, 78) + val cellContentDimensions = + CellContentDimensions(iconSizePx = 66, iconDrawablePaddingPx = 8, iconTextSizePx = 14) + + val contentHeight = + cellContentDimensions.resizeToFitCellHeight(cellSize.second, iconSizeSteps) + + assertThat(contentHeight).isEqualTo(77) + cellContentDimensions.run { + assertThat(iconSizePx).isEqualTo(61) + assertThat(iconDrawablePaddingPx).isEqualTo(0) + assertThat(iconTextSizePx).isEqualTo(12) + } + } + + @Test + fun decreaseIconAndTextToMinimum() { + val cellSize = Pair(52, 63) + val cellContentDimensions = + CellContentDimensions(iconSizePx = 66, iconDrawablePaddingPx = 8, iconTextSizePx = 14) + + val contentHeight = + cellContentDimensions.resizeToFitCellHeight(cellSize.second, iconSizeSteps) + + assertThat(contentHeight).isEqualTo(63) + cellContentDimensions.run { + assertThat(iconSizePx).isEqualTo(52) + assertThat(iconDrawablePaddingPx).isEqualTo(0) + assertThat(iconTextSizePx).isEqualTo(8) + } + } +} diff --git a/tests/src/com/android/launcher3/util/DisplayControllerTest.kt b/tests/src/com/android/launcher3/util/DisplayControllerTest.kt index 8e4e99812e..8670d40f4e 100644 --- a/tests/src/com/android/launcher3/util/DisplayControllerTest.kt +++ b/tests/src/com/android/launcher3/util/DisplayControllerTest.kt @@ -30,8 +30,10 @@ import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.launcher3.LauncherPrefs +import com.android.launcher3.LauncherPrefs.Companion.TASKBAR_PINNING import com.android.launcher3.util.DisplayController.CHANGE_DENSITY import com.android.launcher3.util.DisplayController.CHANGE_ROTATION +import com.android.launcher3.util.DisplayController.CHANGE_TASKBAR_PINNING import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext import com.android.launcher3.util.window.CachedDisplayInfo @@ -40,11 +42,13 @@ import kotlin.math.min import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mock -import org.mockito.Mockito.doNothing -import org.mockito.Mockito.verify -import org.mockito.Mockito.`when` as whenever -import org.mockito.MockitoAnnotations +import org.mockito.kotlin.any +import org.mockito.kotlin.anyOrNull +import org.mockito.kotlin.doNothing +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever import org.mockito.stubbing.Answer /** Unit tests for {@link DisplayController} */ @@ -54,13 +58,13 @@ class DisplayControllerTest { private val appContext: Context = ApplicationProvider.getApplicationContext() - @Mock private lateinit var context: SandboxContext - @Mock private lateinit var windowManagerProxy: WindowManagerProxy - @Mock private lateinit var launcherPrefs: LauncherPrefs - @Mock private lateinit var displayManager: DisplayManager - @Mock private lateinit var display: Display - @Mock private lateinit var resources: Resources - @Mock private lateinit var displayInfoChangeListener: DisplayInfoChangeListener + private val context: SandboxContext = mock() + private val windowManagerProxy: WindowManagerProxy = mock() + private val launcherPrefs: LauncherPrefs = mock() + private val displayManager: DisplayManager = mock() + private val display: Display = mock() + private val resources: Resources = mock() + private val displayInfoChangeListener: DisplayInfoChangeListener = mock() private lateinit var displayController: DisplayController @@ -86,9 +90,9 @@ class DisplayControllerTest { @Before fun setUp() { - MockitoAnnotations.initMocks(this) whenever(context.getObject(eq(WindowManagerProxy.INSTANCE))).thenReturn(windowManagerProxy) whenever(context.getObject(eq(LauncherPrefs.INSTANCE))).thenReturn(launcherPrefs) + whenever(launcherPrefs.get(TASKBAR_PINNING)).thenReturn(false) // Mock WindowManagerProxy val displayInfo = @@ -107,11 +111,12 @@ class DisplayControllerTest { bounds[i.getArgument<CachedDisplayInfo>(1).rotation] } + whenever(windowManagerProxy.getNavigationMode(any())).thenReturn(NavigationMode.NO_BUTTON) // Mock context - whenever(context.createWindowContext(any(), any(), nullable())).thenReturn(context) + whenever(context.createWindowContext(any(), any(), anyOrNull())).thenReturn(context) whenever(context.getSystemService(eq(DisplayManager::class.java))) .thenReturn(displayManager) - doNothing().`when`(context).registerComponentCallbacks(any()) + doNothing().whenever(context).registerComponentCallbacks(any()) // Mock display whenever(display.rotation).thenReturn(displayInfo.rotation) @@ -156,4 +161,13 @@ class DisplayControllerTest { verify(displayInfoChangeListener).onDisplayInfoChanged(any(), any(), eq(CHANGE_DENSITY)) } + + @Test + @UiThreadTest + fun testTaskbarPinning() { + whenever(launcherPrefs.get(TASKBAR_PINNING)).thenReturn(true) + displayController.handleInfoChange(display) + verify(displayInfoChangeListener) + .onDisplayInfoChanged(any(), any(), eq(CHANGE_TASKBAR_PINNING)) + } } diff --git a/tests/src/com/android/launcher3/util/KotlinMockitoHelpers.kt b/tests/src/com/android/launcher3/util/KotlinMockitoHelpers.kt deleted file mode 100644 index c9c9616fb9..0000000000 --- a/tests/src/com/android/launcher3/util/KotlinMockitoHelpers.kt +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (C) 2022 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 - -/** - * Kotlin versions of popular mockito methods that can return null in situations when Kotlin expects - * a non-null value. Kotlin will throw an IllegalStateException when this takes place ("x must not - * be null"). To fix this, we can use methods that modify the return type to be nullable. This - * causes Kotlin to skip the null checks. - */ -import org.mockito.ArgumentCaptor -import org.mockito.Mockito - -/** - * Returns Mockito.eq() as nullable type to avoid java.lang.IllegalStateException when null is - * returned. - * - * Generic T is nullable because implicitly bounded by Any?. - */ -fun <T> eq(obj: T): T = Mockito.eq<T>(obj) - -/** - * Returns Mockito.same() as nullable type to avoid java.lang.IllegalStateException when null is - * returned. - * - * Generic T is nullable because implicitly bounded by Any?. - */ -fun <T> same(obj: T): T = Mockito.same<T>(obj) - -/** - * Returns Mockito.any() as nullable type to avoid java.lang.IllegalStateException when null is - * returned. - * - * Generic T is nullable because implicitly bounded by Any?. - */ -fun <T> any(type: Class<T>): T = Mockito.any<T>(type) - -inline fun <reified T> any(): T = any(T::class.java) - -/** Kotlin type-inferred version of Mockito.nullable() */ -inline fun <reified T> nullable(): T? = Mockito.nullable(T::class.java) - -/** - * Returns ArgumentCaptor.capture() as nullable type to avoid java.lang.IllegalStateException when - * null is returned. - * - * Generic T is nullable because implicitly bounded by Any?. - */ -fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture() - -/** - * Helper function for creating an argumentCaptor in kotlin. - * - * Generic T is nullable because implicitly bounded by Any?. - */ -inline fun <reified T : Any> argumentCaptor(): ArgumentCaptor<T> = - ArgumentCaptor.forClass(T::class.java) - -/** - * Helper function for creating new mocks, without the need to pass in a [Class] instance. - * - * Generic T is nullable because implicitly bounded by Any?. - */ -inline fun <reified T : Any> mock(): T = Mockito.mock(T::class.java) - -/** - * A kotlin implemented wrapper of [ArgumentCaptor] which prevents the following exception when - * kotlin tests are mocking kotlin objects and the methods take non-null parameters: - * ``` - * java.lang.NullPointerException: capture() must not be null - * ``` - */ -class KotlinArgumentCaptor<T> constructor(clazz: Class<T>) { - private val wrapped: ArgumentCaptor<T> = ArgumentCaptor.forClass(clazz) - fun capture(): T = wrapped.capture() - val value: T - get() = wrapped.value -} - -/** - * Helper function for creating an argumentCaptor in kotlin. - * - * Generic T is nullable because implicitly bounded by Any?. - */ -inline fun <reified T : Any> kotlinArgumentCaptor(): KotlinArgumentCaptor<T> = - KotlinArgumentCaptor(T::class.java) - -/** - * Helper function for creating and using a single-use ArgumentCaptor in kotlin. - * - * ``` - * val captor = argumentCaptor<Foo>() - * verify(...).someMethod(captor.capture()) - * val captured = captor.value - * ``` - * - * becomes: - * ``` - * val captured = withArgCaptor<Foo> { verify(...).someMethod(capture()) } - * ``` - * - * NOTE: this uses the KotlinArgumentCaptor to avoid the NullPointerException. - */ -inline fun <reified T : Any> withArgCaptor(block: KotlinArgumentCaptor<T>.() -> Unit): T = - kotlinArgumentCaptor<T>().apply { block() }.value diff --git a/tests/src/com/android/launcher3/util/LauncherModelHelper.java b/tests/src/com/android/launcher3/util/LauncherModelHelper.java index 261436b3ac..244dc269b6 100644 --- a/tests/src/com/android/launcher3/util/LauncherModelHelper.java +++ b/tests/src/com/android/launcher3/util/LauncherModelHelper.java @@ -84,6 +84,17 @@ public class LauncherModelHelper { public static final String TEST_ACTIVITY = "com.android.launcher3.tests.Activity2"; public static final String TEST_ACTIVITY2 = "com.android.launcher3.tests.Activity3"; public static final String TEST_ACTIVITY3 = "com.android.launcher3.tests.Activity4"; + public static final String TEST_ACTIVITY4 = "com.android.launcher3.tests.Activity5"; + public static final String TEST_ACTIVITY5 = "com.android.launcher3.tests.Activity6"; + public static final String TEST_ACTIVITY6 = "com.android.launcher3.tests.Activity7"; + public static final String TEST_ACTIVITY7 = "com.android.launcher3.tests.Activity8"; + public static final String TEST_ACTIVITY8 = "com.android.launcher3.tests.Activity9"; + public static final String TEST_ACTIVITY9 = "com.android.launcher3.tests.Activity10"; + public static final String TEST_ACTIVITY10 = "com.android.launcher3.tests.Activity11"; + public static final String TEST_ACTIVITY11 = "com.android.launcher3.tests.Activity12"; + public static final String TEST_ACTIVITY12 = "com.android.launcher3.tests.Activity13"; + public static final String TEST_ACTIVITY13 = "com.android.launcher3.tests.Activity14"; + public static final String TEST_ACTIVITY14 = "com.android.launcher3.tests.Activity15"; // Authority for providing a test default-workspace-layout data. private static final String TEST_PROVIDER_AUTHORITY = diff --git a/tests/src/com/android/launcher3/util/LockedUserStateTest.kt b/tests/src/com/android/launcher3/util/LockedUserStateTest.kt index 92ab2cb90b..2c4a54f9d8 100644 --- a/tests/src/com/android/launcher3/util/LockedUserStateTest.kt +++ b/tests/src/com/android/launcher3/util/LockedUserStateTest.kt @@ -26,29 +26,27 @@ import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mock -import org.mockito.Mockito.verify -import org.mockito.Mockito.verifyZeroInteractions -import org.mockito.Mockito.`when` -import org.mockito.MockitoAnnotations +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.verifyZeroInteractions +import org.mockito.kotlin.whenever /** Unit tests for {@link LockedUserState} */ @SmallTest @RunWith(AndroidJUnit4::class) class LockedUserStateTest { - @Mock lateinit var userManager: UserManager - @Mock lateinit var context: Context + private val userManager: UserManager = mock() + private val context: Context = mock() @Before fun setup() { - MockitoAnnotations.initMocks(this) - `when`(context.getSystemService(UserManager::class.java)).thenReturn(userManager) + whenever(context.getSystemService(UserManager::class.java)).thenReturn(userManager) } @Test fun runOnUserUnlocked_runs_action_immediately_if_already_unlocked() { - `when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(true) + whenever(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(true) val action: Runnable = mock() LockedUserState(context).runOnUserUnlocked(action) verify(action).run() @@ -56,7 +54,7 @@ class LockedUserStateTest { @Test fun runOnUserUnlocked_waits_to_run_action_until_user_is_unlocked() { - `when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(false) + whenever(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(false) val action: Runnable = mock() val state = LockedUserState(context) state.runOnUserUnlocked(action) @@ -67,13 +65,13 @@ class LockedUserStateTest { @Test fun isUserUnlocked_returns_true_when_user_is_unlocked() { - `when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(true) + whenever(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(true) assertThat(LockedUserState(context).isUserUnlocked).isTrue() } @Test fun isUserUnlocked_returns_false_when_user_is_locked() { - `when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(false) + whenever(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(false) assertThat(LockedUserState(context).isUserUnlocked).isFalse() } } diff --git a/tests/src/com/android/launcher3/util/ModelTestExtensions.kt b/tests/src/com/android/launcher3/util/ModelTestExtensions.kt new file mode 100644 index 0000000000..61ec6692ee --- /dev/null +++ b/tests/src/com/android/launcher3/util/ModelTestExtensions.kt @@ -0,0 +1,30 @@ +package com.android.launcher3.util + +import com.android.launcher3.LauncherModel +import com.android.launcher3.model.BgDataModel + +object ModelTestExtensions { + /** Clears and reloads Launcher db to cleanup the workspace */ + fun LauncherModel.clearModelDb() { + // Load the model once so that there is no pending migration: + loadModelSync() + TestUtil.runOnExecutorSync(Executors.MODEL_EXECUTOR) { + modelDbController.run { + tryMigrateDB() + createEmptyDB() + clearEmptyDbFlag() + } + } + // Reload model + TestUtil.runOnExecutorSync(Executors.MAIN_EXECUTOR) { forceReload() } + loadModelSync() + } + + fun LauncherModel.loadModelSync() { + val mockCb: BgDataModel.Callbacks = object : BgDataModel.Callbacks {} + TestUtil.runOnExecutorSync(Executors.MAIN_EXECUTOR) { addCallbacksAndLoad(mockCb) } + TestUtil.runOnExecutorSync(Executors.MODEL_EXECUTOR) {} + TestUtil.runOnExecutorSync(Executors.MAIN_EXECUTOR) {} + TestUtil.runOnExecutorSync(Executors.MAIN_EXECUTOR) { removeCallbacks(mockCb) } + } +} diff --git a/tests/src/com/android/launcher3/util/TestConstants.java b/tests/src/com/android/launcher3/util/TestConstants.java new file mode 100644 index 0000000000..6f3c63ae02 --- /dev/null +++ b/tests/src/com/android/launcher3/util/TestConstants.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.util; + +public class TestConstants { + public static class AppNames { + + public static final String TEST_APP_NAME = "LauncherTestApp"; + public static final String DUMMY_APP_NAME = "Aardwolf"; + public static final String MAPS_APP_NAME = "Maps"; + public static final String STORE_APP_NAME = "Play Store"; + public static final String GMAIL_APP_NAME = "Gmail"; + public static final String CHROME_APP_NAME = "Chrome"; + public static final String MESSAGES_APP_NAME = "Messages"; + } +} diff --git a/tests/src/com/android/launcher3/util/TestResourceHelper.kt b/tests/src/com/android/launcher3/util/TestResourceHelper.kt index cf80ece740..b4d3ba853a 100644 --- a/tests/src/com/android/launcher3/util/TestResourceHelper.kt +++ b/tests/src/com/android/launcher3/util/TestResourceHelper.kt @@ -32,6 +32,8 @@ class TestResourceHelper(private val context: Context, specsFileId: Int) : styleId.contentEquals(R.styleable.WorkspaceSpec) -> TestR.styleable.WorkspaceSpec styleId.contentEquals(R.styleable.FolderSpec) -> TestR.styleable.FolderSpec styleId.contentEquals(R.styleable.AllAppsSpec) -> TestR.styleable.AllAppsSpec + styleId.contentEquals(R.styleable.ResponsiveSpecGroup) -> + TestR.styleable.ResponsiveSpecGroup else -> styleId.clone() } diff --git a/tests/src/com/android/launcher3/util/TestUtil.java b/tests/src/com/android/launcher3/util/TestUtil.java index 21059e6075..683f3238a0 100644 --- a/tests/src/com/android/launcher3/util/TestUtil.java +++ b/tests/src/com/android/launcher3/util/TestUtil.java @@ -24,12 +24,15 @@ import static com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_KEY; import static com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_LABEL; import static com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_TAG; +import static org.junit.Assert.assertTrue; + import android.app.Instrumentation; import android.app.blob.BlobHandle; import android.app.blob.BlobStoreManager; import android.content.Context; import android.content.pm.LauncherApps; import android.content.res.Resources; +import android.graphics.Point; import android.os.AsyncTask; import android.os.Handler; import android.os.Looper; @@ -46,6 +49,9 @@ import androidx.test.uiautomator.UiDevice; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.config.FeatureFlags.BooleanFlag; import com.android.launcher3.config.FeatureFlags.IntFlag; +import com.android.launcher3.tapl.LauncherInstrumentation; +import com.android.launcher3.tapl.Workspace; +import com.android.launcher3.util.rule.TestStabilityRule; import org.junit.Assert; @@ -125,6 +131,28 @@ public class TestUtil { } /** + * @return Grid coordinates from the center and corners of the Workspace. Those are not pixels. + * See {@link Workspace#getIconGridDimensions()} + */ + public static Point[] getCornersAndCenterPositions(LauncherInstrumentation launcher) { + final Point dimensions = launcher.getWorkspace().getIconGridDimensions(); + if (TestStabilityRule.isPresubmit()) { + // Return only center in presubmit to fit under the presubmit SLO. + return new Point[]{ + new Point(dimensions.x / 2, dimensions.y / 2) + }; + } else { + return new Point[]{ + new Point(0, 1), + new Point(0, dimensions.y - 2), + new Point(dimensions.x - 1, 1), + new Point(dimensions.x - 1, dimensions.y - 2), + new Point(dimensions.x / 2, dimensions.y / 2) + }; + } + } + + /** * Utility class to override a boolean flag during test. Note that the returned SafeCloseable * must be closed to restore the original state */ @@ -226,6 +254,17 @@ public class TestUtil { } } + // Please don't add negative test cases for methods that fail only after a long wait. + public static void expectFail(String message, Runnable action) { + boolean failed = false; + try { + action.run(); + } catch (AssertionError e) { + failed = true; + } + assertTrue(message, failed); + } + /** Interface to indicate a runnable which can throw any exception. */ public interface UncheckedRunnable { /** Method to run the task */ diff --git a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java index 62d70ad1cd..10b428a111 100644 --- a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java +++ b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java @@ -59,8 +59,9 @@ public class FailureWatcher extends TestWatcher { throw new AssertionError( "Launcher received events not sent by the test. This may mean " + "that the touch screen of the lab device has sent false" - + " events. See the logcat for TaplEvents tag and look " - + "for events with deviceId != -1"); + + " events. See the logcat for " + + "TaplEvents|LauncherEvents|TaplTarget tag and look for " + + "events with deviceId != -1"); } } } diff --git a/tests/src/com/android/launcher3/util/rule/SetFlagsRuleExt.kt b/tests/src/com/android/launcher3/util/rule/SetFlagsRuleExt.kt new file mode 100644 index 0000000000..d6824561c1 --- /dev/null +++ b/tests/src/com/android/launcher3/util/rule/SetFlagsRuleExt.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2008 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.rule + +import android.platform.test.flag.junit.SetFlagsRule + +fun SetFlagsRule.setFlags(enabled: Boolean, vararg flagName: String) { + if (enabled) enableFlags(*flagName) else disableFlags(*flagName) +} diff --git a/tests/src/com/android/launcher3/util/rule/StaticMockitoRule.java b/tests/src/com/android/launcher3/util/rule/StaticMockitoRule.java new file mode 100644 index 0000000000..6b91474f02 --- /dev/null +++ b/tests/src/com/android/launcher3/util/rule/StaticMockitoRule.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2022 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.rule; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; + +import com.android.dx.mockito.inline.extended.StaticMockitoSession; +import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder; + +import org.junit.rules.MethodRule; +import org.junit.runners.model.FrameworkMethod; +import org.junit.runners.model.Statement; +import org.mockito.junit.MockitoRule; +import org.mockito.quality.Strictness; + +/** + * Similar to {@link MockitoRule}, but uses {@link StaticMockitoSession}, which allows mocking + * static methods. + */ +public class StaticMockitoRule implements MethodRule { + private Class<?>[] mClasses; + + public StaticMockitoRule(Class<?>... classes) { + mClasses = classes; + } + + @Override + public Statement apply(Statement base, FrameworkMethod method, Object target) { + return new Statement() { + public void evaluate() throws Throwable { + StaticMockitoSessionBuilder builder = + mockitoSession() + .name(target.getClass().getSimpleName() + "." + method.getName()) + .initMocks(target) + .strictness(Strictness.STRICT_STUBS); + + for (Class<?> clazz : mClasses) { + builder.mockStatic(clazz); + } + + StaticMockitoSession session = builder.startMocking(); + Throwable testFailure = evaluateSafely(base); + session.finishMocking(testFailure); + if (testFailure != null) { + throw testFailure; + } + } + + private Throwable evaluateSafely(Statement base) { + try { + base.evaluate(); + return null; + } catch (Throwable throwable) { + return throwable; + } + } + }; + } +} diff --git a/tests/src/com/android/launcher3/util/rule/TISBindRule.java b/tests/src/com/android/launcher3/util/rule/TISBindRule.java deleted file mode 100644 index 3ec4a29cdf..0000000000 --- a/tests/src/com/android/launcher3/util/rule/TISBindRule.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.launcher3.util.rule; - -import android.app.UiAutomation; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; -import android.os.IBinder; -import android.util.Log; - -import androidx.annotation.NonNull; -import androidx.test.platform.app.InstrumentationRegistry; - -import org.junit.rules.TestRule; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; - -public class TISBindRule implements TestRule { - public static String TAG = "TISBindRule"; - public static String INTENT_FILTER = "android.intent.action.QUICKSTEP_SERVICE"; - public static String TIS_PERMISSIONS = "android.permission.STATUS_BAR_SERVICE"; - - private String getLauncherPackageName(Context context) { - return ComponentName.unflattenFromString(context.getString( - com.android.internal.R.string.config_recentsComponentName)).getPackageName(); - } - - private ServiceConnection createConnection() { - return new ServiceConnection() { - @Override - public void onServiceConnected(ComponentName componentName, IBinder iBinder) { - Log.d(TAG, "Connected to TouchInteractionService"); - } - - @Override - public void onServiceDisconnected(ComponentName componentName) { - Log.d(TAG, "Disconnected from TouchInteractionService"); - } - }; - } - - @NonNull - @Override - public Statement apply(@NonNull Statement base, @NonNull Description description) { - return new Statement() { - - @Override - public void evaluate() throws Throwable { - Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); - final ServiceConnection connection = createConnection(); - UiAutomation uiAutomation = - InstrumentationRegistry.getInstrumentation().getUiAutomation(); - uiAutomation.adoptShellPermissionIdentity(TIS_PERMISSIONS); - Intent launchIntent = new Intent(INTENT_FILTER); - launchIntent.setPackage(getLauncherPackageName(context)); - context.bindService(launchIntent, connection, Context.BIND_AUTO_CREATE); - uiAutomation.dropShellPermissionIdentity(); - try { - base.evaluate(); - } finally { - context.unbindService(connection); - } - } - }; - } -} diff --git a/tests/src/com/android/launcher3/util/rule/TestIsolationRule.java b/tests/src/com/android/launcher3/util/rule/TestIsolationRule.java new file mode 100644 index 0000000000..2b45902813 --- /dev/null +++ b/tests/src/com/android/launcher3/util/rule/TestIsolationRule.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.util.rule; + +import androidx.annotation.NonNull; +import androidx.test.InstrumentationRegistry; +import androidx.test.uiautomator.UiDevice; + +import com.android.launcher3.tapl.LauncherInstrumentation; +import com.android.launcher3.ui.AbstractLauncherUiTest; + +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +/** + * Isolates tests from some of the state created by the previous test. + */ +public class TestIsolationRule implements TestRule { + private final LauncherInstrumentation mLauncher; + private final boolean mRequireOneActiveActivity; + + public TestIsolationRule(LauncherInstrumentation launcher, boolean requireOneActiveActivity) { + mLauncher = launcher; + mRequireOneActiveActivity = requireOneActiveActivity; + } + + @NonNull + @Override + public Statement apply(@NonNull Statement base, @NonNull Description description) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + base.evaluate(); + // Make sure that Launcher workspace looks correct. + + UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()).pressHome(); + AbstractLauncherUiTest.checkDetectedLeaks(mLauncher, mRequireOneActiveActivity); + } + }; + } +} diff --git a/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java b/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java index 38de07192a..b51045fc09 100644 --- a/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java +++ b/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java @@ -146,4 +146,8 @@ public class TestStabilityRule implements TestRule { return sRunFlavor; } + + public static boolean isPresubmit() { + return getRunFlavor() == PLATFORM_PRESUBMIT; + } } diff --git a/tests/src/com/android/launcher3/util/rule/ViewCaptureRule.kt b/tests/src/com/android/launcher3/util/rule/ViewCaptureRule.kt index ccbae4fb0c..e70ea18f7a 100644 --- a/tests/src/com/android/launcher3/util/rule/ViewCaptureRule.kt +++ b/tests/src/com/android/launcher3/util/rule/ViewCaptureRule.kt @@ -132,7 +132,9 @@ class ViewCaptureRule(var alreadyOpenActivitySupplier: Supplier<Activity?>) : Te for (i in 0 until viewCaptureData!!.windowDataCount) { frameCount += viewCaptureData!!.getWindowData(i).frameDataCount } - assertTrue("Empty ViewCapture data", frameCount > 0) + + val mayProduceNoFrames = description.getAnnotation(MayProduceNoFrames::class.java) != null + assertTrue("Empty ViewCapture data", mayProduceNoFrames || frameCount > 0) val anomalies: Map<String, String> = ViewCaptureAnalyzer.getAnomalies(viewCaptureData) if (!anomalies.isEmpty()) { @@ -159,4 +161,8 @@ class ViewCaptureRule(var alreadyOpenActivitySupplier: Supplier<Activity?>) : Te ) } } + + @Retention(AnnotationRetention.RUNTIME) + @Target(AnnotationTarget.FUNCTION) + annotation class MayProduceNoFrames } diff --git a/tests/src/com/android/launcher3/util/viewcapture_analysis/AlphaJumpDetector.java b/tests/src/com/android/launcher3/util/viewcapture_analysis/AlphaJumpDetector.java index 4b65439db5..dfccffc1f5 100644 --- a/tests/src/com/android/launcher3/util/viewcapture_analysis/AlphaJumpDetector.java +++ b/tests/src/com/android/launcher3/util/viewcapture_analysis/AlphaJumpDetector.java @@ -33,17 +33,17 @@ final class AlphaJumpDetector extends AnomalyDetector { private static final IgnoreNode IGNORED_NODES_ROOT = buildIgnoreNodesTree(List.of( CONTENT - + "AddItemDragLayer:id/add_item_drag_layer|AddItemWidgetsBottomSheet:id" + + "SimpleDragLayer:id/add_item_drag_layer|AddItemWidgetsBottomSheet:id" + "/add_item_bottom_sheet|LinearLayout:id/add_item_bottom_sheet_content" + "|ScrollView:id/widget_preview_scroll_view|WidgetCell:id/widget_cell" + "|WidgetCellPreview:id/widget_preview_container|ImageView:id/widget_badge", CONTENT - + "AddItemDragLayer:id/add_item_drag_layer|AddItemWidgetsBottomSheet:id" + + "SimpleDragLayer:id/add_item_drag_layer|AddItemWidgetsBottomSheet:id" + "/add_item_bottom_sheet|LinearLayout:id/add_item_bottom_sheet_content" + "|ScrollView:id/widget_preview_scroll_view|WidgetCell:id/widget_cell" + "|WidgetCellPreview:id/widget_preview_container|WidgetCell$1|FrameLayout" + "|ImageView:id/icon", - CONTENT + "AddItemDragLayer:id/add_item_drag_layer|View", + CONTENT + "SimpleDragLayer:id/add_item_drag_layer|View", DRAG_LAYER + "AppWidgetResizeFrame|FrameLayout|ImageButton:id/widget_reconfigure_button", DRAG_LAYER @@ -62,18 +62,6 @@ final class AlphaJumpDetector extends AnomalyDetector { + "NexusOverviewActionsView:id/overview_actions_view|FrameLayout:id" + "/select_mode_buttons|ImageButton:id/close", DRAG_LAYER - + "NexusOverviewActionsView:id/overview_actions_view|LinearLayout:id" - + "/action_buttons|Button:id/action_screenshot", - DRAG_LAYER - + "NexusOverviewActionsView:id/overview_actions_view|LinearLayout:id" - + "/action_buttons|Button:id/action_select", - DRAG_LAYER - + "NexusOverviewActionsView:id/overview_actions_view|LinearLayout:id" - + "/action_buttons|Button:id/action_split", - DRAG_LAYER - + "NexusOverviewActionsView:id/overview_actions_view|LinearLayout:id" - + "/action_buttons|Space:id/action_split_space", - DRAG_LAYER + "PopupContainerWithArrow:id/popup_container|LinearLayout:id" + "/deep_shortcuts_container|DeepShortcutView:id/deep_shortcut_material" + "|DeepShortcutTextView:id/bubble_text", @@ -116,23 +104,14 @@ final class AlphaJumpDetector extends AnomalyDetector { RECENTS_DRAG_LAYER + "FallbackRecentsView:id/overview_panel", DRAG_LAYER + "NexusOverviewActionsView:id/overview_actions_view" - + "|LinearLayout:id/action_buttons|Button:id/action_screenshot", + + "|LinearLayout:id/action_buttons", RECENTS_DRAG_LAYER + "NexusOverviewActionsView:id/overview_actions_view" - + "|LinearLayout:id/action_buttons|Button:id/action_screenshot", + + "|LinearLayout:id/action_buttons", + DRAG_LAYER + "IconView", DRAG_LAYER - + "NexusOverviewActionsView:id/overview_actions_view" - + "|LinearLayout:id/action_buttons|Button:id/action_select", - RECENTS_DRAG_LAYER - + "NexusOverviewActionsView:id/overview_actions_view" - + "|LinearLayout:id/action_buttons|Button:id/action_select", - DRAG_LAYER - + "NexusOverviewActionsView:id/overview_actions_view" - + "|LinearLayout:id/action_buttons|Button:id/action_split", - RECENTS_DRAG_LAYER - + "NexusOverviewActionsView:id/overview_actions_view" - + "|LinearLayout:id/action_buttons|Button:id/action_split", - DRAG_LAYER + "IconView" + + "OptionsPopupView:id/popup_container|DeepShortcutView:id/system_shortcut" + + "|BubbleTextView:id/bubble_text" )); // Minimal increase or decrease of view's alpha between frames that triggers the error. diff --git a/tests/src/com/android/launcher3/util/viewcapture_analysis/FlashDetector.java b/tests/src/com/android/launcher3/util/viewcapture_analysis/FlashDetector.java index 8b88ace181..fc8f818bf5 100644 --- a/tests/src/com/android/launcher3/util/viewcapture_analysis/FlashDetector.java +++ b/tests/src/com/android/launcher3/util/viewcapture_analysis/FlashDetector.java @@ -38,22 +38,25 @@ final class FlashDetector extends AnomalyDetector { private static final IgnoreNode IGNORED_NODES_ROOT = buildIgnoreNodesTree(List.of( CONTENT + "LauncherRootView:id/launcher|FloatingIconView", - DRAG_LAYER + "LauncherRecentsView:id/overview_panel|TaskView|TextView", + DRAG_LAYER + "LauncherRecentsView:id/overview_panel|TaskView", + DRAG_LAYER + "LauncherRecentsView:id/overview_panel|ClearAllButton:id/clear_all", DRAG_LAYER + "LauncherAllAppsContainerView:id/apps_view|AllAppsRecyclerView:id" + "/apps_list_view|BubbleTextView:id/icon", CONTENT - + "AddItemDragLayer:id/add_item_drag_layer|AddItemWidgetsBottomSheet:id" + + "SimpleDragLayer:id/add_item_drag_layer|AddItemWidgetsBottomSheet:id" + "/add_item_bottom_sheet|LinearLayout:id/add_item_bottom_sheet_content" + "|ScrollView:id/widget_preview_scroll_view|WidgetCell:id/widget_cell" + "|WidgetCellPreview:id/widget_preview_container|WidgetImageView:id" + "/widget_preview", CONTENT - + "AddItemDragLayer:id/add_item_drag_layer|AddItemWidgetsBottomSheet:id" + + "SimpleDragLayer:id/add_item_drag_layer|AddItemWidgetsBottomSheet:id" + "/add_item_bottom_sheet|LinearLayout:id/add_item_bottom_sheet_content" + "|ScrollView:id/widget_preview_scroll_view|WidgetCell:id/widget_cell" + "|WidgetCellPreview:id/widget_preview_container|ImageView:id/widget_badge", - RECENTS_DRAG_LAYER + "FallbackRecentsView:id/overview_panel|TaskView|IconView:id/icon", + RECENTS_DRAG_LAYER + "FallbackRecentsView:id/overview_panel|TaskView", + RECENTS_DRAG_LAYER + + "FallbackRecentsView:id/overview_panel|ClearAllButton:id/clear_all", DRAG_LAYER + "SearchContainerView:id/apps_view", DRAG_LAYER + "LauncherDragView", DRAG_LAYER + "FloatingTaskView|FloatingTaskThumbnailView:id/thumbnail", @@ -64,7 +67,8 @@ final class FlashDetector extends AnomalyDetector { + "WidgetsTwoPaneSheet|SpringRelativeLayout:id/container|LinearLayout:id" + "/linear_layout_container|FrameLayout:id/recycler_view_container" + "|FrameLayout:id/widgets_two_pane_sheet_recyclerview|WidgetsRecyclerView:id" - + "/primary_widgets_list_view|WidgetsListHeader:id/widgets_list_header" + + "/primary_widgets_list_view|WidgetsListHeader:id/widgets_list_header", + DRAG_LAYER + "NexusOverviewActionsView:id/overview_actions_view" )); // Per-AnalysisNode data that's specific to this detector. diff --git a/tests/src/com/android/launcher3/util/viewcapture_analysis/PositionJumpDetector.java b/tests/src/com/android/launcher3/util/viewcapture_analysis/PositionJumpDetector.java index a1ddcb054e..88ace68d53 100644 --- a/tests/src/com/android/launcher3/util/viewcapture_analysis/PositionJumpDetector.java +++ b/tests/src/com/android/launcher3/util/viewcapture_analysis/PositionJumpDetector.java @@ -41,12 +41,12 @@ final class PositionJumpDetector extends AnomalyDetector { DRAG_LAYER + "AppWidgetResizeFrame", DRAG_LAYER + "LauncherAllAppsContainerView:id/apps_view", CONTENT - + "AddItemDragLayer:id/add_item_drag_layer|AddItemWidgetsBottomSheet:id" + + "SimpleDragLayer:id/add_item_drag_layer|AddItemWidgetsBottomSheet:id" + "/add_item_bottom_sheet|LinearLayout:id/add_item_bottom_sheet_content", DRAG_LAYER + "WidgetsTwoPaneSheet|SpringRelativeLayout:id/container", DRAG_LAYER + "WidgetsFullSheet|SpringRelativeLayout:id/container", DRAG_LAYER + "LauncherDragView", - RECENTS_DRAG_LAYER + "FallbackRecentsView:id/overview_panel|TaskView", + RECENTS_DRAG_LAYER + "FallbackRecentsView:id/overview_panel", CONTENT + "LauncherRootView:id/launcher|FloatingIconView", DRAG_LAYER + "FloatingTaskView", DRAG_LAYER + "LauncherRecentsView:id/overview_panel" diff --git a/tests/src/com/android/launcher3/util/viewcapture_analysis/ViewCaptureAnalyzer.java b/tests/src/com/android/launcher3/util/viewcapture_analysis/ViewCaptureAnalyzer.java index 9459cc2d13..b27ccbfd2f 100644 --- a/tests/src/com/android/launcher3/util/viewcapture_analysis/ViewCaptureAnalyzer.java +++ b/tests/src/com/android/launcher3/util/viewcapture_analysis/ViewCaptureAnalyzer.java @@ -35,8 +35,8 @@ public class ViewCaptureAnalyzer { // All detectors. They will be invoked in the order listed here. private static final AnomalyDetector[] ANOMALY_DETECTORS = { - new AlphaJumpDetector(), - new FlashDetector(), +// new AlphaJumpDetector(), // b/309014345 +// new FlashDetector(), // b/309014345 new PositionJumpDetector() }; |