Home | History | Annotate | Download | only in model
      1 package com.android.launcher3.model;
      2 
      3 import android.content.ComponentName;
      4 import android.content.Context;
      5 import android.content.Intent;
      6 import android.content.pm.LauncherActivityInfo;
      7 import android.content.res.Resources;
      8 import android.graphics.Bitmap;
      9 import android.graphics.Bitmap.Config;
     10 import android.os.Process;
     11 import android.os.UserHandle;
     12 import android.support.annotation.NonNull;
     13 import android.support.test.InstrumentationRegistry;
     14 import android.test.ProviderTestCase2;
     15 
     16 import com.android.launcher3.AllAppsList;
     17 import com.android.launcher3.AppFilter;
     18 import com.android.launcher3.AppInfo;
     19 import com.android.launcher3.DeferredHandler;
     20 import com.android.launcher3.IconCache;
     21 import com.android.launcher3.InvariantDeviceProfile;
     22 import com.android.launcher3.ItemInfo;
     23 import com.android.launcher3.LauncherAppState;
     24 import com.android.launcher3.LauncherModel;
     25 import com.android.launcher3.LauncherModel.BaseModelUpdateTask;
     26 import com.android.launcher3.LauncherModel.Callbacks;
     27 import com.android.launcher3.config.ProviderConfig;
     28 import com.android.launcher3.util.ComponentKey;
     29 import com.android.launcher3.util.Provider;
     30 import com.android.launcher3.util.TestLauncherProvider;
     31 
     32 import org.mockito.ArgumentCaptor;
     33 
     34 import java.io.BufferedReader;
     35 import java.io.InputStreamReader;
     36 import java.lang.reflect.Field;
     37 import java.util.HashMap;
     38 import java.util.List;
     39 
     40 import static org.mockito.Matchers.anyBoolean;
     41 import static org.mockito.Mockito.atLeast;
     42 import static org.mockito.Mockito.mock;
     43 import static org.mockito.Mockito.verify;
     44 import static org.mockito.Mockito.when;
     45 
     46 /**
     47  * Base class for writing tests for Model update tasks.
     48  */
     49 public class BaseModelUpdateTaskTestCase extends ProviderTestCase2<TestLauncherProvider> {
     50 
     51     public final HashMap<Class, HashMap<String, Field>> fieldCache = new HashMap<>();
     52 
     53     public Context targetContext;
     54     public UserHandle myUser;
     55 
     56     public InvariantDeviceProfile idp;
     57     public LauncherAppState appState;
     58     public LauncherModel model;
     59     public ModelWriter modelWriter;
     60     public MyIconCache iconCache;
     61 
     62     public BgDataModel bgDataModel;
     63     public AllAppsList allAppsList;
     64     public Callbacks callbacks;
     65 
     66     public BaseModelUpdateTaskTestCase() {
     67         super(TestLauncherProvider.class, ProviderConfig.AUTHORITY);
     68     }
     69 
     70     @Override
     71     protected void setUp() throws Exception {
     72         super.setUp();
     73 
     74         callbacks = mock(Callbacks.class);
     75         appState = mock(LauncherAppState.class);
     76         model = mock(LauncherModel.class);
     77         modelWriter = mock(ModelWriter.class);
     78         when(appState.getModel()).thenReturn(model);
     79         when(model.getWriter(anyBoolean())).thenReturn(modelWriter);
     80 
     81         myUser = Process.myUserHandle();
     82 
     83         bgDataModel = new BgDataModel();
     84         targetContext = InstrumentationRegistry.getTargetContext();
     85         idp = new InvariantDeviceProfile();
     86         iconCache = new MyIconCache(targetContext, idp);
     87 
     88         allAppsList = new AllAppsList(iconCache, new AppFilter());
     89 
     90         when(appState.getIconCache()).thenReturn(iconCache);
     91         when(appState.getInvariantDeviceProfile()).thenReturn(idp);
     92     }
     93 
     94     /**
     95      * Synchronously executes the task and returns all the UI callbacks posted.
     96      */
     97     public List<Runnable> executeTaskForTest(BaseModelUpdateTask task) throws Exception {
     98         LauncherModel mockModel = mock(LauncherModel.class);
     99         when(mockModel.getCallback()).thenReturn(callbacks);
    100 
    101         Field f = BaseModelUpdateTask.class.getDeclaredField("mModel");
    102         f.setAccessible(true);
    103         f.set(task, mockModel);
    104 
    105         DeferredHandler mockHandler = mock(DeferredHandler.class);
    106         f = BaseModelUpdateTask.class.getDeclaredField("mUiHandler");
    107         f.setAccessible(true);
    108         f.set(task, mockHandler);
    109 
    110         task.execute(appState, bgDataModel, allAppsList);
    111         ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
    112         verify(mockHandler, atLeast(0)).post(captor.capture());
    113 
    114         return captor.getAllValues();
    115     }
    116 
    117     /**
    118      * Initializes mock data for the test.
    119      */
    120     public void initializeData(String resourceName) throws Exception {
    121         Context myContext = InstrumentationRegistry.getContext();
    122         Resources res = myContext.getResources();
    123         int id = res.getIdentifier(resourceName, "raw", myContext.getPackageName());
    124         try (BufferedReader reader =
    125                      new BufferedReader(new InputStreamReader(res.openRawResource(id)))) {
    126             String line;
    127             HashMap<String, Class> classMap = new HashMap<>();
    128             while((line = reader.readLine()) != null) {
    129                 line = line.trim();
    130                 if (line.startsWith("#") || line.isEmpty()) {
    131                     continue;
    132                 }
    133                 String[] commands = line.split(" ");
    134                 switch (commands[0]) {
    135                     case "classMap":
    136                         classMap.put(commands[1], Class.forName(commands[2]));
    137                         break;
    138                     case "bgItem":
    139                         bgDataModel.addItem(targetContext,
    140                                 (ItemInfo) initItem(classMap.get(commands[1]), commands, 2), false);
    141                         break;
    142                     case "allApps":
    143                         allAppsList.add((AppInfo) initItem(AppInfo.class, commands, 1), null);
    144                         break;
    145                 }
    146             }
    147         }
    148     }
    149 
    150     private Object initItem(Class clazz, String[] fieldDef, int startIndex) throws Exception {
    151         HashMap<String, Field> cache = fieldCache.get(clazz);
    152         if (cache == null) {
    153             cache = new HashMap<>();
    154             Class c = clazz;
    155             while (c != null) {
    156                 for (Field f : c.getDeclaredFields()) {
    157                     f.setAccessible(true);
    158                     cache.put(f.getName(), f);
    159                 }
    160                 c = c.getSuperclass();
    161             }
    162             fieldCache.put(clazz, cache);
    163         }
    164 
    165         Object item = clazz.newInstance();
    166         for (int i = startIndex; i < fieldDef.length; i++) {
    167             String[] fieldData = fieldDef[i].split("=", 2);
    168             Field f = cache.get(fieldData[0]);
    169             Class type = f.getType();
    170             if (type == int.class || type == long.class) {
    171                 f.set(item, Integer.parseInt(fieldData[1]));
    172             } else if (type == CharSequence.class || type == String.class) {
    173                 f.set(item, fieldData[1]);
    174             } else if (type == Intent.class) {
    175                 if (!fieldData[1].startsWith("#Intent")) {
    176                     fieldData[1] = "#Intent;" + fieldData[1] + ";end";
    177                 }
    178                 f.set(item, Intent.parseUri(fieldData[1], 0));
    179             } else if (type == ComponentName.class) {
    180                 f.set(item, ComponentName.unflattenFromString(fieldData[1]));
    181             } else {
    182                 throw new Exception("Added parsing logic for "
    183                         + f.getName() + " of type " + f.getType());
    184             }
    185         }
    186         return item;
    187     }
    188 
    189     public static class MyIconCache extends IconCache {
    190 
    191         private final HashMap<ComponentKey, CacheEntry> mCache = new HashMap<>();
    192 
    193         public MyIconCache(Context context, InvariantDeviceProfile idp) {
    194             super(context, idp);
    195         }
    196 
    197         @Override
    198         protected CacheEntry cacheLocked(
    199                 @NonNull ComponentName componentName,
    200                 @NonNull Provider<LauncherActivityInfo> infoProvider,
    201                 UserHandle user, boolean usePackageIcon, boolean useLowResIcon) {
    202             CacheEntry entry = mCache.get(new ComponentKey(componentName, user));
    203             if (entry == null) {
    204                 entry = new CacheEntry();
    205                 entry.icon = getDefaultIcon(user);
    206             }
    207             return entry;
    208         }
    209 
    210         public void addCache(ComponentName key, String title) {
    211             CacheEntry entry = new CacheEntry();
    212             entry.icon = newIcon();
    213             entry.title = title;
    214             mCache.put(new ComponentKey(key, Process.myUserHandle()), entry);
    215         }
    216 
    217         public Bitmap newIcon() {
    218             return Bitmap.createBitmap(1, 1, Config.ARGB_8888);
    219         }
    220 
    221         @Override
    222         protected Bitmap makeDefaultIcon(UserHandle user) {
    223             return newIcon();
    224         }
    225     }
    226 }
    227