Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4b5b0eb6 authored by Sunny Goyal's avatar Sunny Goyal
Browse files

Moving some tests to robolectric

> Adding support for simulating model load

Bug: 130562632
Change-Id: I1de8c0abe2e74d4e7e47e18914316c339920609a
parent 0c3fd982
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -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
+9 −3
Original line number Diff line number Diff line
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);
+165 −0
Original line number Diff line number Diff line
/*
 * 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();
    }
}
+96 −0
Original line number Diff line number Diff line
/*
 * 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);
    }
}
+53 −0
Original line number Diff line number Diff line
/*
 * 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);
    }
}
Loading