• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
setUp()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      */
executeTaskForTest(ModelUpdateTask task)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      */
initializeData(String resourceName)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 
initItem(Class clazz, String[] fieldDef, int startIndex)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 
MyIconCache(Context context, InvariantDeviceProfile idp)200         public MyIconCache(Context context, InvariantDeviceProfile idp) {
201             super(context, idp);
202         }
203 
204         @Override
cacheLocked( @onNull ComponentName componentName, @NonNull Provider<LauncherActivityInfo> infoProvider, UserHandle user, boolean usePackageIcon, boolean useLowResIcon)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 
addCache(ComponentName key, String title)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 
newIcon()225         public Bitmap newIcon() {
226             return Bitmap.createBitmap(1, 1, Config.ARGB_8888);
227         }
228 
229         @Override
makeDefaultIcon(UserHandle user)230         protected BitmapInfo makeDefaultIcon(UserHandle user) {
231             return BitmapInfo.fromBitmap(newIcon());
232         }
233     }
234 }
235