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