Merge "Moving some tests to robolectric" into ub-launcher3-master

This commit is contained in:
TreeHugger Robot
2019-12-09 20:04:35 +00:00
committed by Android (Google) Code Review
11 changed files with 425 additions and 144 deletions
+2 -2
View File
@@ -27,7 +27,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := \
mockito-robolectric-prebuilt \
truth-prebuilt
LOCAL_JAVA_LIBRARIES := \
platform-robolectric-3.6.1-prebuilt
platform-robolectric-4.3-prebuilt
LOCAL_JAVA_RESOURCE_DIRS := resources config
@@ -54,4 +54,4 @@ LOCAL_INSTRUMENT_SOURCE_DIRS := $(dir $(LOCAL_PATH))../src \
LOCAL_ROBOTEST_TIMEOUT := 36000
include prebuilts/misc/common/robolectric/3.6.1/run_robotests.mk
include prebuilts/misc/common/robolectric/4.3/run_robotests.mk
@@ -1,10 +1,13 @@
package com.android.launcher3.model;
import static com.android.launcher3.shadows.ShadowLooperExecutor.reinitializeStaticExecutors;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.robolectric.util.ReflectionHelpers.setField;
import android.content.ComponentName;
import android.content.Context;
@@ -29,6 +32,7 @@ import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.icons.cache.CachingLogic;
import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.pm.PackageInstallerCompat;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.TestLauncherProvider;
@@ -53,7 +57,7 @@ import java.util.function.Supplier;
public class BaseModelUpdateTaskTestCase {
public final HashMap<Class, HashMap<String, Field>> fieldCache = new HashMap<>();
private TestLauncherProvider mProvider;
public TestLauncherProvider provider;
public Context targetContext;
public UserHandle myUser;
@@ -71,9 +75,11 @@ public class BaseModelUpdateTaskTestCase {
@Before
public void setUp() throws Exception {
ShadowLog.stream = System.out;
reinitializeStaticExecutors();
setField(PackageInstallerCompat.class, null, "sInstance", null);
mProvider = Robolectric.setupContentProvider(TestLauncherProvider.class);
ShadowContentResolver.registerProviderInternal(LauncherProvider.AUTHORITY, mProvider);
provider = Robolectric.setupContentProvider(TestLauncherProvider.class);
ShadowContentResolver.registerProviderInternal(LauncherProvider.AUTHORITY, provider);
callbacks = mock(Callbacks.class);
appState = mock(LauncherAppState.class);
@@ -0,0 +1,165 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher3.model;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.robolectric.Shadows.shadowOf;
import static org.robolectric.util.ReflectionHelpers.setField;
import android.content.ComponentName;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionInfo;
import android.content.pm.PackageInstaller.SessionParams;
import android.net.Uri;
import android.provider.Settings;
import com.android.launcher3.FolderInfo;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherProvider;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.shadows.LShadowLauncherApps;
import com.android.launcher3.shadows.LShadowUserManager;
import com.android.launcher3.shadows.ShadowLooperExecutor;
import com.android.launcher3.util.Executors;
import com.android.launcher3.util.LauncherLayoutBuilder;
import com.android.launcher3.widget.custom.CustomWidgetManager;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.LooperMode;
import org.robolectric.annotation.LooperMode.Mode;
import org.robolectric.shadows.ShadowPackageManager;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.OutputStreamWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
/**
* Tests for layout parser for remote layout
*/
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {LShadowUserManager.class, LShadowLauncherApps.class, ShadowLooperExecutor.class})
@LooperMode(Mode.PAUSED)
public class DefaultLayoutProviderTest extends BaseModelUpdateTaskTestCase {
private static final String SETTINGS_APP = "com.android.settings";
private static final String TEST_PROVIDER_AUTHORITY =
DefaultLayoutProviderTest.class.getName().toLowerCase();
private static final int BITMAP_SIZE = 10;
private static final int GRID_SIZE = 4;
@Before
public void setUp() throws Exception {
super.setUp();
InvariantDeviceProfile.INSTANCE.initializeForTesting(idp);
CustomWidgetManager.INSTANCE.initializeForTesting(mock(CustomWidgetManager.class));
idp.numRows = idp.numColumns = idp.numHotseatIcons = GRID_SIZE;
idp.iconBitmapSize = BITMAP_SIZE;
provider.setAllowLoadDefaultFavorites(true);
Settings.Secure.putString(targetContext.getContentResolver(),
"launcher3.layout.provider", TEST_PROVIDER_AUTHORITY);
ShadowPackageManager spm = shadowOf(targetContext.getPackageManager());
spm.addProviderIfNotPresent(new ComponentName("com.test", "Dummy")).authority =
TEST_PROVIDER_AUTHORITY;
spm.addActivityIfNotPresent(new ComponentName(SETTINGS_APP, SETTINGS_APP));
}
@After
public void cleanup() {
InvariantDeviceProfile.INSTANCE.initializeForTesting(null);
CustomWidgetManager.INSTANCE.initializeForTesting(null);
}
@Test
public void testCustomProfileLoaded_with_icon_on_hotseat() throws Exception {
writeLayoutAndLoad(new LauncherLayoutBuilder().atHotseat(0)
.putApp(SETTINGS_APP, SETTINGS_APP));
// Verify one item in hotseat
assertEquals(1, bgDataModel.workspaceItems.size());
ItemInfo info = bgDataModel.workspaceItems.get(0);
assertEquals(LauncherSettings.Favorites.CONTAINER_HOTSEAT, info.container);
assertEquals(LauncherSettings.Favorites.ITEM_TYPE_APPLICATION, info.itemType);
}
@Test
public void testCustomProfileLoaded_with_folder() throws Exception {
writeLayoutAndLoad(new LauncherLayoutBuilder().atHotseat(0).putFolder(android.R.string.copy)
.addApp(SETTINGS_APP, SETTINGS_APP)
.addApp(SETTINGS_APP, SETTINGS_APP)
.addApp(SETTINGS_APP, SETTINGS_APP)
.build());
// Verify folder
assertEquals(1, bgDataModel.workspaceItems.size());
ItemInfo info = bgDataModel.workspaceItems.get(0);
assertEquals(LauncherSettings.Favorites.ITEM_TYPE_FOLDER, info.itemType);
assertEquals(3, ((FolderInfo) info).contents.size());
}
@Test
public void testCustomProfileLoaded_with_widget() throws Exception {
String pendingAppPkg = "com.test.pending";
// Add a dummy session info so that the widget exists
SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
params.setAppPackageName(pendingAppPkg);
PackageInstaller installer = targetContext.getPackageManager().getPackageInstaller();
int sessionId = installer.createSession(params);
SessionInfo sessionInfo = installer.getSessionInfo(sessionId);
setField(sessionInfo, "installerPackageName", "com.test");
setField(sessionInfo, "appIcon", BitmapInfo.LOW_RES_ICON);
writeLayoutAndLoad(new LauncherLayoutBuilder().atWorkspace(0, 1, 0)
.putWidget(pendingAppPkg, "DummyWidget", 2, 2));
// Verify widget
assertEquals(1, bgDataModel.appWidgets.size());
ItemInfo info = bgDataModel.appWidgets.get(0);
assertEquals(LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET, info.itemType);
assertEquals(2, info.spanX);
assertEquals(2, info.spanY);
}
private void writeLayoutAndLoad(LauncherLayoutBuilder builder) throws Exception {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
builder.build(new OutputStreamWriter(bos));
Uri layoutUri = LauncherProvider.getLayoutUri(TEST_PROVIDER_AUTHORITY, targetContext);
shadowOf(targetContext.getContentResolver()).registerInputStream(layoutUri,
new ByteArrayInputStream(bos.toByteArray()));
LoaderResults results = new LoaderResults(appState, bgDataModel, allAppsList, 0,
new WeakReference<>(callbacks));
LoaderTask task = new LoaderTask(appState, allAppsList, bgDataModel, results);
Executors.MODEL_EXECUTOR.submit(() -> task.loadWorkspace(new ArrayList<>())).get();
}
}
@@ -0,0 +1,96 @@
/*
* Copyright (C) 2019 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.shadows;
import static org.robolectric.util.ReflectionHelpers.ClassParameter;
import static org.robolectric.util.ReflectionHelpers.callConstructor;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ShortcutInfo;
import android.os.Process;
import android.os.UserHandle;
import android.util.ArraySet;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.PackageUserKey;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.shadows.ShadowLauncherApps;
import java.util.Collections;
import java.util.List;
/**
* Extension of {@link ShadowLauncherApps} with missing shadow methods
*/
@Implements(value = LauncherApps.class)
public class LShadowLauncherApps extends ShadowLauncherApps {
public final ArraySet<PackageUserKey> disabledApps = new ArraySet<>();
public final ArraySet<ComponentKey> disabledActivities = new ArraySet<>();
@Implementation
@Override
protected List<ShortcutInfo> getShortcuts(LauncherApps.ShortcutQuery query, UserHandle user) {
try {
return super.getShortcuts(query, user);
} catch (UnsupportedOperationException e) {
return Collections.emptyList();
}
}
@Implementation
protected boolean isPackageEnabled(String packageName, UserHandle user) {
return !disabledApps.contains(new PackageUserKey(packageName, user));
}
@Implementation
protected boolean isActivityEnabled(ComponentName component, UserHandle user) {
return !disabledActivities.contains(new ComponentKey(component, user));
}
@Implementation
protected LauncherActivityInfo resolveActivity(Intent intent, UserHandle user) {
ResolveInfo ri = RuntimeEnvironment.application.getPackageManager()
.resolveActivity(intent, 0);
return getLauncherActivityInfo(ri.activityInfo);
}
public LauncherActivityInfo getLauncherActivityInfo(ActivityInfo activityInfo) {
return callConstructor(LauncherActivityInfo.class,
ClassParameter.from(Context.class, RuntimeEnvironment.application),
ClassParameter.from(ActivityInfo.class, activityInfo),
ClassParameter.from(UserHandle.class, Process.myUserHandle()));
}
@Implementation
public ApplicationInfo getApplicationInfo(String packageName, int flags, UserHandle user)
throws PackageManager.NameNotFoundException {
return RuntimeEnvironment.application.getPackageManager()
.getApplicationInfo(packageName, flags);
}
}
@@ -0,0 +1,53 @@
/*
* Copyright (C) 2019 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.shadows;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.SparseBooleanArray;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.shadows.ShadowUserManager;
/**
* Extension of {@link ShadowUserManager} with missing shadow methods
*/
@Implements(value = UserManager.class)
public class LShadowUserManager extends ShadowUserManager {
private final SparseBooleanArray mQuietUsers = new SparseBooleanArray();
private final SparseBooleanArray mLockedUsers = new SparseBooleanArray();
@Implementation
protected boolean isQuietModeEnabled(UserHandle userHandle) {
return mQuietUsers.get(userHandle.hashCode());
}
public void setQuietModeEnabled(UserHandle userHandle, boolean enabled) {
mQuietUsers.put(userHandle.hashCode(), enabled);
}
@Implementation
protected boolean isUserUnlocked(UserHandle userHandle) {
return !mLockedUsers.get(userHandle.hashCode());
}
public void setUserLocked(UserHandle userHandle, boolean enabled) {
mLockedUsers.put(userHandle.hashCode(), enabled);
}
}
@@ -0,0 +1,68 @@
/*
* Copyright (C) 2019 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.shadows;
import static com.android.launcher3.util.Executors.createAndStartNewLooper;
import static org.robolectric.shadow.api.Shadow.invokeConstructor;
import static org.robolectric.util.ReflectionHelpers.ClassParameter.from;
import static org.robolectric.util.ReflectionHelpers.setField;
import android.os.Handler;
import android.os.Looper;
import com.android.launcher3.util.Executors;
import com.android.launcher3.util.LooperExecutor;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.RealObject;
import org.robolectric.util.ReflectionHelpers;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Set;
import java.util.WeakHashMap;
/**
* Shadow for {@link LooperExecutor} to provide reset functionality for static executors.
*/
@Implements(value = LooperExecutor.class, isInAndroidSdk = false)
public class ShadowLooperExecutor {
// Keep reference to all created Loopers so they can be torn down after test
private static Set<LooperExecutor> executors =
Collections.synchronizedSet(Collections.newSetFromMap(new WeakHashMap<>()));
@RealObject private LooperExecutor realExecutor;
@Implementation
protected void __constructor__(Looper looper) {
invokeConstructor(LooperExecutor.class, realExecutor, from(Looper.class, looper));
executors.add(realExecutor);
}
/**
* Re-initializes any executor which may have been reset when a test finished
*/
public static void reinitializeStaticExecutors() {
for (LooperExecutor executor : new ArrayList<>(executors)) {
setField(executor, "mHandler",
new Handler(createAndStartNewLooper(executor.getThread().getName())));
}
}
}
@@ -10,6 +10,8 @@ import com.android.launcher3.LauncherProvider;
*/
public class TestLauncherProvider extends LauncherProvider {
private boolean mAllowLoadDefaultFavorites;
@Override
public boolean onCreate() {
return true;
@@ -18,18 +20,26 @@ public class TestLauncherProvider extends LauncherProvider {
@Override
protected synchronized void createDbIfNotExists() {
if (mOpenHelper == null) {
mOpenHelper = new MyDatabaseHelper(getContext());
mOpenHelper = new MyDatabaseHelper(getContext(), mAllowLoadDefaultFavorites);
}
}
public void setAllowLoadDefaultFavorites(boolean allowLoadDefaultFavorites) {
mAllowLoadDefaultFavorites = allowLoadDefaultFavorites;
}
public SQLiteDatabase getDb() {
createDbIfNotExists();
return mOpenHelper.getWritableDatabase();
}
private static class MyDatabaseHelper extends DatabaseHelper {
public MyDatabaseHelper(Context context) {
private final boolean mAllowLoadDefaultFavorites;
MyDatabaseHelper(Context context, boolean allowLoadDefaultFavorites) {
super(context, null);
mAllowLoadDefaultFavorites = allowLoadDefaultFavorites;
initIds();
}
@@ -39,7 +49,11 @@ public class TestLauncherProvider extends LauncherProvider {
}
@Override
protected void onEmptyDbCreated() { }
protected void onEmptyDbCreated() {
if (mAllowLoadDefaultFavorites) {
super.onEmptyDbCreated();
}
}
@Override
protected void handleOneTimeDataUpgrade(SQLiteDatabase db) { }
@@ -484,8 +484,6 @@ public class LauncherProvider extends ContentProvider {
*/
private AutoInstallsLayout createWorkspaceLoaderFromAppRestriction(AppWidgetHost widgetHost) {
Context ctx = getContext();
InvariantDeviceProfile grid = LauncherAppState.getIDP(ctx);
String authority = Settings.Secure.getString(ctx.getContentResolver(),
"launcher3.layout.provider");
if (TextUtils.isEmpty(authority)) {
@@ -497,13 +495,7 @@ public class LauncherProvider extends ContentProvider {
Log.e(TAG, "No provider found for authority " + authority);
return null;
}
Uri uri = new Uri.Builder().scheme("content").authority(authority).path("launcher_layout")
.appendQueryParameter("version", "1")
.appendQueryParameter("gridWidth", Integer.toString(grid.numColumns))
.appendQueryParameter("gridHeight", Integer.toString(grid.numRows))
.appendQueryParameter("hotseatSize", Integer.toString(grid.numHotseatIcons))
.build();
Uri uri = getLayoutUri(authority, ctx);
try (InputStream in = ctx.getContentResolver().openInputStream(uri)) {
// Read the full xml so that we fail early in case of any IO error.
String layout = new String(IOUtils.toByteArray(in));
@@ -520,6 +512,16 @@ public class LauncherProvider extends ContentProvider {
}
}
public static Uri getLayoutUri(String authority, Context ctx) {
InvariantDeviceProfile grid = LauncherAppState.getIDP(ctx);
return new Uri.Builder().scheme("content").authority(authority).path("launcher_layout")
.appendQueryParameter("version", "1")
.appendQueryParameter("gridWidth", Integer.toString(grid.numColumns))
.appendQueryParameter("gridHeight", Integer.toString(grid.numRows))
.appendQueryParameter("hotseatSize", Integer.toString(grid.numHotseatIcons))
.build();
}
private DefaultLayoutParser getDefaultLayoutParser(AppWidgetHost widgetHost) {
InvariantDeviceProfile idp = LauncherAppState.getIDP(getContext());
int defaultLayout = idp.defaultLayoutId;
@@ -42,6 +42,8 @@ import android.util.LongSparseArray;
import android.util.MutableInt;
import android.util.TimingLogger;
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.AppInfo;
import com.android.launcher3.FolderInfo;
import com.android.launcher3.InstallShortcutReceiver;
@@ -271,7 +273,8 @@ public class LoaderTask implements Runnable {
this.notify();
}
private void loadWorkspace(List<ShortcutInfo> allDeepShortcuts) {
@VisibleForTesting
void loadWorkspace(List<ShortcutInfo> allDeepShortcuts) {
final Context context = mApp.getContext();
final ContentResolver contentResolver = context.getContentResolver();
final PackageManagerHelper pmHelper = new PackageManagerHelper(context);
@@ -1,126 +0,0 @@
/**
* Copyright (C) 2019 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 org.junit.Assert.assertTrue;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.os.ParcelFileDescriptor.AutoCloseOutputStream;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.testcomponent.TestCommandProvider;
import com.android.launcher3.util.LauncherLayoutBuilder;
import com.android.launcher3.util.rule.ShellCommandRule;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.OutputStreamWriter;
@MediumTest
@RunWith(AndroidJUnit4.class)
public class DefaultLayoutProviderTest extends AbstractLauncherUiTest {
@Rule
public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
private static final String SETTINGS_APP = "com.android.settings";
private Context mContext;
private String mAuthority;
@Before
@Override
public void setUp() throws Exception {
super.setUp();
mContext = InstrumentationRegistry.getContext();
PackageManager pm = mTargetContext.getPackageManager();
ProviderInfo pi = pm.getProviderInfo(new ComponentName(mContext,
TestCommandProvider.class), 0);
mAuthority = pi.authority;
}
@Test
public void testCustomProfileLoaded_with_icon_on_hotseat() throws Exception {
writeLayout(new LauncherLayoutBuilder().atHotseat(0).putApp(SETTINGS_APP, SETTINGS_APP));
// Launch the home activity
mDevice.pressHome();
mLauncher.getWorkspace().getHotseatAppIcon(getSettingsApp().getLabel().toString());
}
@Test
public void testCustomProfileLoaded_with_widget() throws Exception {
// A non-restored widget with no config screen gets restored automatically.
LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false);
writeLayout(new LauncherLayoutBuilder().atWorkspace(0, 1, 0)
.putWidget(info.getComponent().getPackageName(),
info.getComponent().getClassName(), 2, 2));
// Launch the home activity
mDevice.pressHome();
// Verify widget present
assertTrue("Widget is not present",
mLauncher.getWorkspace().tryGetWidget(info.label, DEFAULT_UI_TIMEOUT) != null);
}
@Test
public void testCustomProfileLoaded_with_folder() throws Exception {
writeLayout(new LauncherLayoutBuilder().atHotseat(0).putFolder(android.R.string.copy)
.addApp(SETTINGS_APP, SETTINGS_APP)
.addApp(SETTINGS_APP, SETTINGS_APP)
.addApp(SETTINGS_APP, SETTINGS_APP)
.build());
// Launch the home activity
mDevice.pressHome();
mLauncher.getWorkspace().getHotseatFolder("Folder: Copy");
}
@After
public void cleanup() throws Exception {
mDevice.executeShellCommand("settings delete secure launcher3.layout.provider");
}
private void writeLayout(LauncherLayoutBuilder builder) throws Exception {
mDevice.executeShellCommand("settings put secure launcher3.layout.provider " + mAuthority);
ParcelFileDescriptor pfd = mTargetContext.getContentResolver().openFileDescriptor(
Uri.parse("content://" + mAuthority + "/launcher_layout"), "w");
try (OutputStreamWriter writer = new OutputStreamWriter(new AutoCloseOutputStream(pfd))) {
builder.build(writer);
}
clearLauncherData();
}
}