• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.server.pm.shortcutmanagertest;
17 
18 import static com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity;
19 
20 import static junit.framework.Assert.assertEquals;
21 import static junit.framework.Assert.assertFalse;
22 import static junit.framework.Assert.assertNotNull;
23 import static junit.framework.Assert.assertNull;
24 import static junit.framework.Assert.assertTrue;
25 import static junit.framework.Assert.fail;
26 
27 import static org.junit.Assert.assertNotEquals;
28 import static org.mockito.Matchers.any;
29 import static org.mockito.Matchers.anyList;
30 import static org.mockito.Matchers.anyString;
31 import static org.mockito.Matchers.eq;
32 import static org.mockito.Mockito.atLeastOnce;
33 import static org.mockito.Mockito.mock;
34 import static org.mockito.Mockito.reset;
35 import static org.mockito.Mockito.times;
36 import static org.mockito.Mockito.verify;
37 
38 import android.app.Instrumentation;
39 import android.app.role.RoleManager;
40 import android.content.ComponentName;
41 import android.content.Context;
42 import android.content.LocusId;
43 import android.content.pm.LauncherApps;
44 import android.content.pm.LauncherApps.Callback;
45 import android.content.pm.ShortcutInfo;
46 import android.graphics.Bitmap;
47 import android.graphics.BitmapFactory;
48 import android.os.BaseBundle;
49 import android.os.Bundle;
50 import android.os.Handler;
51 import android.os.Looper;
52 import android.os.Parcel;
53 import android.os.ParcelFileDescriptor;
54 import android.os.PersistableBundle;
55 import android.os.UserHandle;
56 import android.os.UserManager;
57 import android.test.MoreAsserts;
58 import android.util.Log;
59 
60 import junit.framework.Assert;
61 
62 import org.hamcrest.BaseMatcher;
63 import org.hamcrest.Description;
64 import org.hamcrest.Matcher;
65 import org.json.JSONException;
66 import org.json.JSONObject;
67 import org.mockito.ArgumentCaptor;
68 import org.mockito.ArgumentMatchers;
69 import org.mockito.hamcrest.MockitoHamcrest;
70 
71 import java.io.BufferedReader;
72 import java.io.File;
73 import java.io.FileNotFoundException;
74 import java.io.FileReader;
75 import java.io.IOException;
76 import java.util.ArrayList;
77 import java.util.Arrays;
78 import java.util.Collection;
79 import java.util.Collections;
80 import java.util.Comparator;
81 import java.util.LinkedHashSet;
82 import java.util.List;
83 import java.util.Set;
84 import java.util.SortedSet;
85 import java.util.TreeSet;
86 import java.util.concurrent.CountDownLatch;
87 import java.util.function.BooleanSupplier;
88 import java.util.function.Consumer;
89 import java.util.function.Function;
90 import java.util.function.Predicate;
91 
92 /**
93  * Common utility methods for ShortcutManager tests.  This is used by both CTS and the unit tests.
94  * Because it's used by CTS too, it can only access the public APIs.
95  */
96 public class ShortcutManagerTestUtils {
97     private static final String TAG = "ShortcutManagerUtils";
98 
99     private static final boolean ENABLE_DUMPSYS = true; // DO NOT SUBMIT WITH true
100 
101     private static final int STANDARD_TIMEOUT_SEC = 5;
102 
103     private static final String[] EMPTY_STRINGS = new String[0];
104 
ShortcutManagerTestUtils()105     private ShortcutManagerTestUtils() {
106     }
107 
readAll(File file)108     public static List<String> readAll(File file) throws FileNotFoundException {
109         return readAll(ParcelFileDescriptor.open(
110                 file.getAbsoluteFile(), ParcelFileDescriptor.MODE_READ_ONLY));
111     }
112 
readAll(ParcelFileDescriptor pfd)113     public static List<String> readAll(ParcelFileDescriptor pfd) {
114         try {
115             try {
116                 final ArrayList<String> ret = new ArrayList<>();
117                 try (BufferedReader r = new BufferedReader(
118                         new FileReader(pfd.getFileDescriptor()))) {
119                     String line;
120                     while ((line = r.readLine()) != null) {
121                         ret.add(line);
122                     }
123                     r.readLine();
124                 }
125                 return ret;
126             } finally {
127                 pfd.close();
128             }
129         } catch (IOException e) {
130             throw new RuntimeException(e);
131         }
132     }
133 
concatResult(List<String> result)134     public static String concatResult(List<String> result) {
135         final StringBuilder sb = new StringBuilder();
136         for (String s : result) {
137             sb.append(s);
138             sb.append("\n");
139         }
140         return sb.toString();
141     }
142 
extractShortcutIds(List<String> result)143     public static List<String> extractShortcutIds(List<String> result) {
144         final String prefix = "ShortcutInfo {id=";
145         final String postfix = ", ";
146 
147         List<String> ids = new ArrayList<>();
148         for (String line : result) {
149             if (line.contains(prefix)) {
150                 ids.add(line.substring(
151                         line.indexOf(prefix) + prefix.length(), line.indexOf(postfix)));
152             }
153         }
154         return ids;
155     }
156 
resultContains(List<String> result, String expected)157     public static boolean resultContains(List<String> result, String expected) {
158         for (String line : result) {
159             if (line.contains(expected)) {
160                 return true;
161             }
162         }
163         return false;
164     }
165 
assertSuccess(List<String> result)166     public static List<String> assertSuccess(List<String> result) {
167         if (!resultContains(result, "Success")) {
168             fail("Command failed.  Result was:\n" + concatResult(result));
169         }
170         return result;
171     }
172 
assertContains(List<String> result, String expected)173     public static List<String> assertContains(List<String> result, String expected) {
174         if (!resultContains(result, expected)) {
175             fail("Didn't contain expected string=" + expected
176                     + "\nActual:\n" + concatResult(result));
177         }
178         return result;
179     }
180 
assertHaveIds(List<String> result, String... expectedIds)181     public static List<String> assertHaveIds(List<String> result, String... expectedIds) {
182         assertSuccess(result);
183 
184         final SortedSet<String> expected = new TreeSet<>(list(expectedIds));
185         final SortedSet<String> actual = new TreeSet<>(extractShortcutIds(result));
186         assertEquals(expected, actual);
187 
188         return result;
189     }
190 
runCommand(Instrumentation instrumentation, String command)191     public static List<String> runCommand(Instrumentation instrumentation, String command) {
192         return runCommand(instrumentation, command, null);
193     }
runCommand(Instrumentation instrumentation, String command, Predicate<List<String>> resultAsserter)194     public static List<String> runCommand(Instrumentation instrumentation, String command,
195             Predicate<List<String>> resultAsserter) {
196         Log.d(TAG, "Running command: " + command);
197         final List<String> result;
198         try {
199             result = readAll(
200                     instrumentation.getUiAutomation().executeShellCommand(command));
201         } catch (Exception e) {
202             throw new RuntimeException(e);
203         }
204         if (resultAsserter != null && !resultAsserter.test(result)) {
205             fail("Command '" + command + "' failed, output was:\n" + concatResult(result));
206         }
207         return result;
208     }
209 
runCommandForNoOutput(Instrumentation instrumentation, String command)210     public static void runCommandForNoOutput(Instrumentation instrumentation, String command) {
211         runCommand(instrumentation, command, result -> result.size() == 0);
212     }
213 
runShortcutCommand(Instrumentation instrumentation, String command, Predicate<List<String>> resultAsserter)214     public static List<String> runShortcutCommand(Instrumentation instrumentation, String command,
215             Predicate<List<String>> resultAsserter) {
216         return runCommand(instrumentation, "cmd shortcut " + command, resultAsserter);
217     }
218 
runShortcutCommandForSuccess(Instrumentation instrumentation, String command)219     public static List<String> runShortcutCommandForSuccess(Instrumentation instrumentation,
220             String command) {
221         return runShortcutCommand(instrumentation, command, result -> result.contains("Success"));
222     }
223 
getParentUser(Context context)224     private static UserHandle getParentUser(Context context) {
225         final UserHandle user = context.getUser();
226         final UserManager userManager = context.getSystemService(UserManager.class);
227         if (!userManager.isManagedProfile(user.getIdentifier())) {
228             return user;
229         }
230 
231         final List<UserHandle> profiles = userManager.getUserProfiles();
232         for (UserHandle handle : profiles) {
233             if (!userManager.isManagedProfile(handle.getIdentifier())) {
234                 return handle;
235             }
236         }
237         return null;
238     }
239 
getDefaultLauncher(Instrumentation instrumentation)240     public static String getDefaultLauncher(Instrumentation instrumentation) throws Exception {
241         final Context context = instrumentation.getContext();
242         final RoleManager roleManager = context.getSystemService(RoleManager.class);
243         final UserHandle user = getParentUser(context);
244         List<String> roleHolders = callWithShellPermissionIdentity(
245                 () -> roleManager.getRoleHoldersAsUser(RoleManager.ROLE_HOME, user));
246         int size = roleHolders.size();
247         if (size == 1) {
248             return roleHolders.get(0);
249         }
250 
251         if (size > 1) {
252             fail("Too many launchers for user " + user.getIdentifier() + " using role "
253                     + RoleManager.ROLE_HOME + ": " + roleHolders);
254         } else {
255             fail("No default launcher for user " + user.getIdentifier() + " using role "
256                     + RoleManager.ROLE_HOME);
257         }
258         return null;
259     }
260 
setDefaultLauncher(Instrumentation instrumentation, String packageName)261     public static void setDefaultLauncher(Instrumentation instrumentation, String packageName) {
262         runCommandForNoOutput(instrumentation, "cmd role add-role-holder --user "
263                 + instrumentation.getContext().getUserId() + " " + RoleManager.ROLE_HOME + " "
264                 + packageName + " 0");
265         waitUntil("Failed to get shortcut access",
266                 () -> hasShortcutAccess(instrumentation, packageName), 20);
267     }
268 
setDefaultLauncher(Instrumentation instrumentation, Context packageContext)269     public static void setDefaultLauncher(Instrumentation instrumentation, Context packageContext) {
270         setDefaultLauncher(instrumentation, packageContext.getPackageName());
271     }
272 
hasShortcutAccess(Instrumentation instrumentation, String packageName)273     public static boolean hasShortcutAccess(Instrumentation instrumentation, String packageName) {
274         final List<String> result = runShortcutCommandForSuccess(instrumentation,
275                 "has-shortcut-access --user " + instrumentation.getContext().getUserId()
276                         + " " + packageName);
277         for (String s : result) {
278             if (s.startsWith("true")) {
279                 return true;
280             } else if (s.startsWith("false")) {
281                 return false;
282             }
283         }
284         fail("Failed to check shortcut access");
285         return false;
286     }
287 
overrideConfig(Instrumentation instrumentation, String config)288     public static void overrideConfig(Instrumentation instrumentation, String config) {
289         runShortcutCommandForSuccess(instrumentation, "override-config " + config);
290     }
291 
resetConfig(Instrumentation instrumentation)292     public static void resetConfig(Instrumentation instrumentation) {
293         runShortcutCommandForSuccess(instrumentation, "reset-config");
294     }
295 
resetThrottling(Instrumentation instrumentation)296     public static void resetThrottling(Instrumentation instrumentation) {
297         runShortcutCommandForSuccess(instrumentation, "reset-throttling");
298     }
299 
resetAllThrottling(Instrumentation instrumentation)300     public static void resetAllThrottling(Instrumentation instrumentation) {
301         runShortcutCommandForSuccess(instrumentation, "reset-all-throttling");
302     }
303 
clearShortcuts(Instrumentation instrumentation, int userId, String packageName)304     public static void clearShortcuts(Instrumentation instrumentation, int userId,
305             String packageName) {
306         runShortcutCommandForSuccess(instrumentation, "clear-shortcuts "
307                 + " --user " + userId + " " + packageName);
308     }
309 
anyContains(List<String> result, String expected)310     public static void anyContains(List<String> result, String expected) {
311         for (String l : result) {
312             if (l.contains(expected)) {
313                 return;
314             }
315         }
316         fail("Result didn't contain '" + expected + "': was\n" + result);
317     }
318 
enableComponent(Instrumentation instrumentation, ComponentName cn, boolean enable)319     public static void enableComponent(Instrumentation instrumentation, ComponentName cn,
320             boolean enable) {
321 
322         final String word = (enable ? "enable" : "disable");
323         runCommand(instrumentation,
324                 "pm " + word + " " + cn.flattenToString()
325                 , result ->concatResult(result).contains(word));
326     }
327 
appOps(Instrumentation instrumentation, String packageName, String op, String mode)328     public static void appOps(Instrumentation instrumentation, String packageName,
329             String op, String mode) {
330         runCommand(instrumentation, "appops set " + packageName + " " + op + " " + mode);
331     }
332 
dumpsysShortcut(Instrumentation instrumentation)333     public static void dumpsysShortcut(Instrumentation instrumentation) {
334         if (!ENABLE_DUMPSYS) {
335             return;
336         }
337         Log.e(TAG, "Dumpsys shortcut");
338         for (String s : runCommand(instrumentation, "dumpsys shortcut")) {
339             Log.e(TAG, s);
340         }
341     }
342 
getCheckinDump(Instrumentation instrumentation)343     public static JSONObject getCheckinDump(Instrumentation instrumentation) throws JSONException {
344         return new JSONObject(concatResult(runCommand(instrumentation, "dumpsys shortcut -c")));
345     }
346 
isLowRamDevice(Instrumentation instrumentation)347     public static boolean isLowRamDevice(Instrumentation instrumentation) throws JSONException {
348         return getCheckinDump(instrumentation).getBoolean("lowRam");
349     }
350 
getIconSize(Instrumentation instrumentation)351     public static int getIconSize(Instrumentation instrumentation) throws JSONException {
352         return getCheckinDump(instrumentation).getInt("iconSize");
353     }
354 
makeBundle(Object... keysAndValues)355     public static Bundle makeBundle(Object... keysAndValues) {
356         assertTrue((keysAndValues.length % 2) == 0);
357 
358         if (keysAndValues.length == 0) {
359             return null;
360         }
361         final Bundle ret = new Bundle();
362 
363         for (int i = keysAndValues.length - 2; i >= 0; i -= 2) {
364             final String key = keysAndValues[i].toString();
365             final Object value = keysAndValues[i + 1];
366 
367             if (value == null) {
368                 ret.putString(key, null);
369             } else if (value instanceof Integer) {
370                 ret.putInt(key, (Integer) value);
371             } else if (value instanceof String) {
372                 ret.putString(key, (String) value);
373             } else if (value instanceof Bundle) {
374                 ret.putBundle(key, (Bundle) value);
375             } else {
376                 fail("Type not supported yet: " + value.getClass().getName());
377             }
378         }
379         return ret;
380     }
381 
makePersistableBundle(Object... keysAndValues)382     public static PersistableBundle makePersistableBundle(Object... keysAndValues) {
383         assertTrue((keysAndValues.length % 2) == 0);
384 
385         if (keysAndValues.length == 0) {
386             return null;
387         }
388         final PersistableBundle ret = new PersistableBundle();
389 
390         for (int i = keysAndValues.length - 2; i >= 0; i -= 2) {
391             final String key = keysAndValues[i].toString();
392             final Object value = keysAndValues[i + 1];
393 
394             if (value == null) {
395                 ret.putString(key, null);
396             } else if (value instanceof Integer) {
397                 ret.putInt(key, (Integer) value);
398             } else if (value instanceof String) {
399                 ret.putString(key, (String) value);
400             } else if (value instanceof PersistableBundle) {
401                 ret.putPersistableBundle(key, (PersistableBundle) value);
402             } else {
403                 fail("Type not supported yet: " + value.getClass().getName());
404             }
405         }
406         return ret;
407     }
408 
array(T... array)409     public static <T> T[] array(T... array) {
410         return array;
411     }
412 
list(T... array)413     public static <T> List<T> list(T... array) {
414         return Arrays.asList(array);
415     }
416 
hashSet(Set<T> in)417     public static <T> Set<T> hashSet(Set<T> in) {
418         return new LinkedHashSet<>(in);
419     }
420 
set(T... values)421     public static <T> Set<T> set(T... values) {
422         return set(v -> v, values);
423     }
424 
set(Function<V, T> converter, V... values)425     public static <T, V> Set<T> set(Function<V, T> converter, V... values) {
426         return set(converter, Arrays.asList(values));
427     }
428 
set(Function<V, T> converter, List<V> values)429     public static <T, V> Set<T> set(Function<V, T> converter, List<V> values) {
430         final LinkedHashSet<T> ret = new LinkedHashSet<>();
431         for (V v : values) {
432             ret.add(converter.apply(v));
433         }
434         return ret;
435     }
436 
locusId(String id)437     public static LocusId locusId(String id) {
438         return new LocusId(id);
439     }
440 
resetAll(Collection<?> mocks)441     public static void resetAll(Collection<?> mocks) {
442         for (Object o : mocks) {
443             reset(o);
444         }
445     }
446 
assertEmpty(T collection)447     public static <T extends Collection<?>> T assertEmpty(T collection) {
448         if (collection == null) {
449             return collection; // okay.
450         }
451         assertEquals(0, collection.size());
452         return collection;
453     }
454 
filter(List<ShortcutInfo> list, Predicate<ShortcutInfo> p)455     public static List<ShortcutInfo> filter(List<ShortcutInfo> list, Predicate<ShortcutInfo> p) {
456         final ArrayList<ShortcutInfo> ret = new ArrayList<>(list);
457         ret.removeIf(si -> !p.test(si));
458         return ret;
459     }
460 
filterByActivity(List<ShortcutInfo> list, ComponentName activity)461     public static List<ShortcutInfo> filterByActivity(List<ShortcutInfo> list,
462             ComponentName activity) {
463         return filter(list, si ->
464                 (si.getActivity().equals(activity)
465                         && (si.isDeclaredInManifest() || si.isDynamic())));
466     }
467 
changedSince(List<ShortcutInfo> list, long time)468     public static List<ShortcutInfo> changedSince(List<ShortcutInfo> list, long time) {
469         return filter(list, si -> si.getLastChangedTimestamp() >= time);
470     }
471 
472     @FunctionalInterface
473     public interface ExceptionRunnable {
run()474         void run() throws Exception;
475     }
476 
assertExpectException(Class<? extends Throwable> expectedExceptionType, String expectedExceptionMessageRegex, ExceptionRunnable r)477     public static void assertExpectException(Class<? extends Throwable> expectedExceptionType,
478             String expectedExceptionMessageRegex, ExceptionRunnable r) {
479         assertExpectException("", expectedExceptionType, expectedExceptionMessageRegex, r);
480     }
481 
assertCannotUpdateImmutable(Runnable r)482     public static void assertCannotUpdateImmutable(Runnable r) {
483         assertExpectException(
484                 IllegalArgumentException.class, "may not be manipulated via APIs", r::run);
485     }
486 
assertDynamicShortcutCountExceeded(Runnable r)487     public static void assertDynamicShortcutCountExceeded(Runnable r) {
488         assertExpectException(IllegalArgumentException.class,
489                 "Max number of dynamic shortcuts exceeded", r::run);
490     }
491 
assertExpectException(String message, Class<? extends Throwable> expectedExceptionType, String expectedExceptionMessageRegex, ExceptionRunnable r)492     public static void assertExpectException(String message,
493             Class<? extends Throwable> expectedExceptionType,
494             String expectedExceptionMessageRegex, ExceptionRunnable r) {
495         try {
496             r.run();
497         } catch (Throwable e) {
498             Assert.assertTrue(
499                     "Expected exception type was " + expectedExceptionType.getName()
500                             + " but caught " + e + " (message=" + message + ")",
501                     expectedExceptionType.isAssignableFrom(e.getClass()));
502             if (expectedExceptionMessageRegex != null) {
503                 MoreAsserts.assertContainsRegex(expectedExceptionMessageRegex, e.getMessage());
504             }
505             return; // Pass
506         }
507         Assert.fail("Expected exception type " + expectedExceptionType.getName()
508                 + " was not thrown");
509     }
510 
assertShortcutIds(List<ShortcutInfo> actualShortcuts, String... expectedIds)511     public static List<ShortcutInfo> assertShortcutIds(List<ShortcutInfo> actualShortcuts,
512             String... expectedIds) {
513         final SortedSet<String> expected = new TreeSet<>(list(expectedIds));
514         final SortedSet<String> actual = new TreeSet<>();
515         for (ShortcutInfo s : actualShortcuts) {
516             actual.add(s.getId());
517         }
518 
519         // Compare the sets.
520         assertEquals(expected, actual);
521         return actualShortcuts;
522     }
523 
assertShortcutIdsOrdered(List<ShortcutInfo> actualShortcuts, String... expectedIds)524     public static List<ShortcutInfo> assertShortcutIdsOrdered(List<ShortcutInfo> actualShortcuts,
525             String... expectedIds) {
526         final ArrayList<String> expected = new ArrayList<>(list(expectedIds));
527         final ArrayList<String> actual = new ArrayList<>();
528         for (ShortcutInfo s : actualShortcuts) {
529             actual.add(s.getId());
530         }
531         assertEquals(expected, actual);
532         return actualShortcuts;
533     }
534 
assertAllHaveIntents( List<ShortcutInfo> actualShortcuts)535     public static List<ShortcutInfo> assertAllHaveIntents(
536             List<ShortcutInfo> actualShortcuts) {
537         for (ShortcutInfo s : actualShortcuts) {
538             assertNotNull("ID " + s.getId(), s.getIntent());
539         }
540         return actualShortcuts;
541     }
542 
assertAllNotHaveIntents( List<ShortcutInfo> actualShortcuts)543     public static List<ShortcutInfo> assertAllNotHaveIntents(
544             List<ShortcutInfo> actualShortcuts) {
545         for (ShortcutInfo s : actualShortcuts) {
546             assertNull("ID " + s.getId(), s.getIntent());
547         }
548         return actualShortcuts;
549     }
550 
assertAllHaveTitle( List<ShortcutInfo> actualShortcuts)551     public static List<ShortcutInfo> assertAllHaveTitle(
552             List<ShortcutInfo> actualShortcuts) {
553         for (ShortcutInfo s : actualShortcuts) {
554             assertNotNull("ID " + s.getId(), s.getShortLabel());
555         }
556         return actualShortcuts;
557     }
558 
assertAllNotHaveTitle( List<ShortcutInfo> actualShortcuts)559     public static List<ShortcutInfo> assertAllNotHaveTitle(
560             List<ShortcutInfo> actualShortcuts) {
561         for (ShortcutInfo s : actualShortcuts) {
562             assertNull("ID " + s.getId(), s.getShortLabel());
563         }
564         return actualShortcuts;
565     }
566 
assertAllKeyFieldsOnly( List<ShortcutInfo> actualShortcuts)567     public static List<ShortcutInfo> assertAllKeyFieldsOnly(
568             List<ShortcutInfo> actualShortcuts) {
569         for (ShortcutInfo s : actualShortcuts) {
570             assertTrue("ID " + s.getId(), s.hasKeyFieldsOnly());
571         }
572         return actualShortcuts;
573     }
574 
assertAllNotKeyFieldsOnly( List<ShortcutInfo> actualShortcuts)575     public static List<ShortcutInfo> assertAllNotKeyFieldsOnly(
576             List<ShortcutInfo> actualShortcuts) {
577         for (ShortcutInfo s : actualShortcuts) {
578             assertFalse("ID " + s.getId(), s.hasKeyFieldsOnly());
579         }
580         return actualShortcuts;
581     }
582 
assertAllDynamic(List<ShortcutInfo> actualShortcuts)583     public static List<ShortcutInfo> assertAllDynamic(List<ShortcutInfo> actualShortcuts) {
584         for (ShortcutInfo s : actualShortcuts) {
585             assertTrue("ID " + s.getId(), s.isDynamic());
586         }
587         return actualShortcuts;
588     }
589 
assertAllPinned(List<ShortcutInfo> actualShortcuts)590     public static List<ShortcutInfo> assertAllPinned(List<ShortcutInfo> actualShortcuts) {
591         for (ShortcutInfo s : actualShortcuts) {
592             assertTrue("ID " + s.getId(), s.isPinned());
593         }
594         return actualShortcuts;
595     }
596 
assertAllDynamicOrPinned( List<ShortcutInfo> actualShortcuts)597     public static List<ShortcutInfo> assertAllDynamicOrPinned(
598             List<ShortcutInfo> actualShortcuts) {
599         for (ShortcutInfo s : actualShortcuts) {
600             assertTrue("ID " + s.getId(), s.isDynamic() || s.isPinned());
601         }
602         return actualShortcuts;
603     }
604 
assertAllManifest( List<ShortcutInfo> actualShortcuts)605     public static List<ShortcutInfo> assertAllManifest(
606             List<ShortcutInfo> actualShortcuts) {
607         for (ShortcutInfo s : actualShortcuts) {
608             assertTrue("ID " + s.getId(), s.isDeclaredInManifest());
609         }
610         return actualShortcuts;
611     }
612 
assertAllNotManifest( List<ShortcutInfo> actualShortcuts)613     public static List<ShortcutInfo> assertAllNotManifest(
614             List<ShortcutInfo> actualShortcuts) {
615         for (ShortcutInfo s : actualShortcuts) {
616             assertFalse("ID " + s.getId(), s.isDeclaredInManifest());
617         }
618         return actualShortcuts;
619     }
620 
assertAllDisabled( List<ShortcutInfo> actualShortcuts)621     public static List<ShortcutInfo> assertAllDisabled(
622             List<ShortcutInfo> actualShortcuts) {
623         for (ShortcutInfo s : actualShortcuts) {
624             assertTrue("ID " + s.getId(), !s.isEnabled());
625         }
626         return actualShortcuts;
627     }
628 
assertAllEnabled( List<ShortcutInfo> actualShortcuts)629     public static List<ShortcutInfo> assertAllEnabled(
630             List<ShortcutInfo> actualShortcuts) {
631         for (ShortcutInfo s : actualShortcuts) {
632             assertTrue("ID " + s.getId(), s.isEnabled());
633         }
634         return actualShortcuts;
635     }
636 
assertAllImmutable( List<ShortcutInfo> actualShortcuts)637     public static List<ShortcutInfo> assertAllImmutable(
638             List<ShortcutInfo> actualShortcuts) {
639         for (ShortcutInfo s : actualShortcuts) {
640             assertTrue("ID " + s.getId(), s.isImmutable());
641         }
642         return actualShortcuts;
643     }
644 
assertDynamicOnly(ShortcutInfo si)645     public static void assertDynamicOnly(ShortcutInfo si) {
646         assertTrue(si.isDynamic());
647         assertFalse(si.isPinned());
648     }
649 
assertPinnedOnly(ShortcutInfo si)650     public static void assertPinnedOnly(ShortcutInfo si) {
651         assertFalse(si.isDynamic());
652         assertFalse(si.isDeclaredInManifest());
653         assertTrue(si.isPinned());
654     }
655 
assertDynamicAndPinned(ShortcutInfo si)656     public static void assertDynamicAndPinned(ShortcutInfo si) {
657         assertTrue(si.isDynamic());
658         assertTrue(si.isPinned());
659     }
660 
assertBitmapSize(int expectedWidth, int expectedHeight, Bitmap bitmap)661     public static void assertBitmapSize(int expectedWidth, int expectedHeight, Bitmap bitmap) {
662         assertEquals("width", expectedWidth, bitmap.getWidth());
663         assertEquals("height", expectedHeight, bitmap.getHeight());
664     }
665 
assertAllUnique(Collection<T> list)666     public static <T> void assertAllUnique(Collection<T> list) {
667         final Set<Object> set = new LinkedHashSet<>();
668         for (T item : list) {
669             if (set.contains(item)) {
670                 fail("Duplicate item found: " + item + " (in the list: " + list + ")");
671             }
672             set.add(item);
673         }
674     }
675 
findShortcut(List<ShortcutInfo> list, String id)676     public static ShortcutInfo findShortcut(List<ShortcutInfo> list, String id) {
677         for (ShortcutInfo si : list) {
678             if (si.getId().equals(id)) {
679                 return si;
680             }
681         }
682         fail("Shortcut " + id + " not found in the list");
683         return null;
684     }
685 
pfdToBitmap(ParcelFileDescriptor pfd)686     public static Bitmap pfdToBitmap(ParcelFileDescriptor pfd) {
687         assertNotNull(pfd);
688         try {
689             try {
690                 return BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor());
691             } finally {
692                 pfd.close();
693             }
694         } catch (IOException e) {
695             throw new RuntimeException(e);
696         }
697     }
698 
assertBundleEmpty(BaseBundle b)699     public static void assertBundleEmpty(BaseBundle b) {
700         assertTrue(b == null || b.size() == 0);
701     }
702 
assertCallbackNotReceived(LauncherApps.Callback mock)703     public static void assertCallbackNotReceived(LauncherApps.Callback mock) {
704         verify(mock, times(0)).onShortcutsChanged(anyString(), anyList(),
705                 any(UserHandle.class));
706     }
707 
assertCallbackReceived(LauncherApps.Callback mock, UserHandle user, String packageName, String... ids)708     public static void assertCallbackReceived(LauncherApps.Callback mock,
709             UserHandle user, String packageName, String... ids) {
710         verify(mock).onShortcutsChanged(eq(packageName), checkShortcutIds(ids),
711                 eq(user));
712     }
713 
checkAssertSuccess(Runnable r)714     public static boolean checkAssertSuccess(Runnable r) {
715         try {
716             r.run();
717             return true;
718         } catch (AssertionError e) {
719             return false;
720         }
721     }
722 
checkArgument(Predicate<T> checker, String description, List<T> matchedCaptor)723     public static <T> T checkArgument(Predicate<T> checker, String description,
724             List<T> matchedCaptor) {
725         final Matcher<T> m = new BaseMatcher<T>() {
726             @Override
727             public boolean matches(Object item) {
728                 if (item == null) {
729                     return false;
730                 }
731                 final T value = (T) item;
732                 if (!checker.test(value)) {
733                     return false;
734                 }
735 
736                 if (matchedCaptor != null) {
737                     matchedCaptor.add(value);
738                 }
739                 return true;
740             }
741 
742             @Override
743             public void describeTo(Description d) {
744                 d.appendText(description);
745             }
746         };
747         return MockitoHamcrest.argThat(m);
748     }
749 
checkShortcutIds(String... ids)750     public static List<ShortcutInfo> checkShortcutIds(String... ids) {
751         return checkArgument((List<ShortcutInfo> list) -> {
752             final Set<String> actualSet = set(si -> si.getId(), list);
753             return actualSet.equals(set(ids));
754 
755         }, "Shortcut IDs=[" + Arrays.toString(ids) + "]", null);
756     }
757 
parceled(ShortcutInfo si)758     public static ShortcutInfo parceled(ShortcutInfo si) {
759         Parcel p = Parcel.obtain();
760         p.writeParcelable(si, 0);
761         p.setDataPosition(0);
762         ShortcutInfo si2 = p.readParcelable(ShortcutManagerTestUtils.class.getClassLoader());
763         p.recycle();
764         return si2;
765     }
766 
cloneShortcutList(List<ShortcutInfo> list)767     public static List<ShortcutInfo> cloneShortcutList(List<ShortcutInfo> list) {
768         if (list == null) {
769             return null;
770         }
771         final List<ShortcutInfo> ret = new ArrayList<>(list.size());
772         for (ShortcutInfo si : list) {
773             ret.add(parceled(si));
774         }
775 
776         return ret;
777     }
778 
779     private static final Comparator<ShortcutInfo> sRankComparator =
780             (ShortcutInfo a, ShortcutInfo b) -> Integer.compare(a.getRank(), b.getRank());
781 
sortedByRank(List<ShortcutInfo> shortcuts)782     public static List<ShortcutInfo> sortedByRank(List<ShortcutInfo> shortcuts) {
783         final ArrayList<ShortcutInfo> ret = new ArrayList<>(shortcuts);
784         Collections.sort(ret, sRankComparator);
785         return ret;
786     }
787 
waitUntil(String message, BooleanSupplier condition)788     public static void waitUntil(String message, BooleanSupplier condition) {
789         waitUntil(message, condition, STANDARD_TIMEOUT_SEC);
790     }
791 
waitUntil(String message, BooleanSupplier condition, int timeoutSeconds)792     public static void waitUntil(String message, BooleanSupplier condition, int timeoutSeconds) {
793         final long timeout = System.currentTimeMillis() + (timeoutSeconds * 1000L);
794         while (System.currentTimeMillis() < timeout) {
795             if (condition.getAsBoolean()) {
796                 return;
797             }
798             try {
799                 Thread.sleep(100);
800             } catch (InterruptedException e) {
801                 throw new RuntimeException(e);
802             }
803         }
804         fail("Timed out for: " + message);
805     }
806 
anyOrNull(Class<T> clazz)807     public static final <T> T anyOrNull(Class<T> clazz) {
808         return ArgumentMatchers.argThat(value -> true);
809     }
810 
anyStringOrNull()811     public static final String anyStringOrNull() {
812         return ArgumentMatchers.argThat(value -> true);
813     }
814 
assertWith(List<ShortcutInfo> list)815     public static ShortcutListAsserter assertWith(List<ShortcutInfo> list) {
816         return new ShortcutListAsserter(list);
817     }
818 
assertWith(ShortcutInfo... list)819     public static ShortcutListAsserter assertWith(ShortcutInfo... list) {
820         return assertWith(list(list));
821     }
822 
823     /**
824      * New style assertion that allows chained calls.
825      */
826     public static class ShortcutListAsserter {
827         private final ShortcutListAsserter mOriginal;
828         private final List<ShortcutInfo> mList;
829 
ShortcutListAsserter(List<ShortcutInfo> list)830         ShortcutListAsserter(List<ShortcutInfo> list) {
831             this(null, list);
832         }
833 
ShortcutListAsserter(ShortcutListAsserter original, List<ShortcutInfo> list)834         private ShortcutListAsserter(ShortcutListAsserter original, List<ShortcutInfo> list) {
835             mOriginal = (original == null) ? this : original;
836             mList = (list == null) ? new ArrayList<>(0) : new ArrayList<>(list);
837         }
838 
revertToOriginalList()839         public ShortcutListAsserter revertToOriginalList() {
840             return mOriginal;
841         }
842 
selectDynamic()843         public ShortcutListAsserter selectDynamic() {
844             return new ShortcutListAsserter(this,
845                     filter(mList, ShortcutInfo::isDynamic));
846         }
847 
selectManifest()848         public ShortcutListAsserter selectManifest() {
849             return new ShortcutListAsserter(this,
850                     filter(mList, ShortcutInfo::isDeclaredInManifest));
851         }
852 
selectPinned()853         public ShortcutListAsserter selectPinned() {
854             return new ShortcutListAsserter(this,
855                     filter(mList, ShortcutInfo::isPinned));
856         }
857 
selectFloating()858         public ShortcutListAsserter selectFloating() {
859             return new ShortcutListAsserter(this,
860                     filter(mList, (si -> si.isPinned()
861                             && !(si.isDynamic() || si.isDeclaredInManifest()))));
862         }
863 
selectByActivity(ComponentName activity)864         public ShortcutListAsserter selectByActivity(ComponentName activity) {
865             return new ShortcutListAsserter(this,
866                     ShortcutManagerTestUtils.filterByActivity(mList, activity));
867         }
868 
selectByChangedSince(long time)869         public ShortcutListAsserter selectByChangedSince(long time) {
870             return new ShortcutListAsserter(this,
871                     ShortcutManagerTestUtils.changedSince(mList, time));
872         }
873 
selectByIds(String... ids)874         public ShortcutListAsserter selectByIds(String... ids) {
875             final Set<String> idSet = set(ids);
876             final ArrayList<ShortcutInfo> selected = new ArrayList<>();
877             for (ShortcutInfo si : mList) {
878                 if (idSet.contains(si.getId())) {
879                     selected.add(si);
880                     idSet.remove(si.getId());
881                 }
882             }
883             if (idSet.size() > 0) {
884                 fail("Shortcuts not found for IDs=" + idSet);
885             }
886 
887             return new ShortcutListAsserter(this, selected);
888         }
889 
toSortByRank()890         public ShortcutListAsserter toSortByRank() {
891             return new ShortcutListAsserter(this,
892                     ShortcutManagerTestUtils.sortedByRank(mList));
893         }
894 
call(Consumer<List<ShortcutInfo>> c)895         public ShortcutListAsserter call(Consumer<List<ShortcutInfo>> c) {
896             c.accept(mList);
897             return this;
898         }
899 
haveIds(String... expectedIds)900         public ShortcutListAsserter haveIds(String... expectedIds) {
901             assertShortcutIds(mList, expectedIds);
902             return this;
903         }
904 
haveIdsOrdered(String... expectedIds)905         public ShortcutListAsserter haveIdsOrdered(String... expectedIds) {
906             assertShortcutIdsOrdered(mList, expectedIds);
907             return this;
908         }
909 
haveSequentialRanks()910         private ShortcutListAsserter haveSequentialRanks() {
911             for (int i = 0; i < mList.size(); i++) {
912                 final ShortcutInfo si = mList.get(i);
913                 assertEquals("Rank not sequential: id=" + si.getId(), i, si.getRank());
914             }
915             return this;
916         }
917 
haveRanksInOrder(String... expectedIds)918         public ShortcutListAsserter haveRanksInOrder(String... expectedIds) {
919             toSortByRank()
920                     .haveSequentialRanks()
921                     .haveIdsOrdered(expectedIds);
922             return this;
923         }
924 
isEmpty()925         public ShortcutListAsserter isEmpty() {
926             assertEquals(0, mList.size());
927             return this;
928         }
929 
areAllDynamic()930         public ShortcutListAsserter areAllDynamic() {
931             forAllShortcuts(s -> assertTrue("id=" + s.getId(), s.isDynamic()));
932             return this;
933         }
934 
areAllNotDynamic()935         public ShortcutListAsserter areAllNotDynamic() {
936             forAllShortcuts(s -> assertFalse("id=" + s.getId(), s.isDynamic()));
937             return this;
938         }
939 
areAllPinned()940         public ShortcutListAsserter areAllPinned() {
941             forAllShortcuts(s -> assertTrue("id=" + s.getId(), s.isPinned()));
942             return this;
943         }
944 
areAllNotPinned()945         public ShortcutListAsserter areAllNotPinned() {
946             forAllShortcuts(s -> assertFalse("id=" + s.getId(), s.isPinned()));
947             return this;
948         }
949 
areAllManifest()950         public ShortcutListAsserter areAllManifest() {
951             forAllShortcuts(s -> assertTrue("id=" + s.getId(), s.isDeclaredInManifest()));
952             return this;
953         }
954 
areAllNotManifest()955         public ShortcutListAsserter areAllNotManifest() {
956             forAllShortcuts(s -> assertFalse("id=" + s.getId(), s.isDeclaredInManifest()));
957             return this;
958         }
959 
areAllImmutable()960         public ShortcutListAsserter areAllImmutable() {
961             forAllShortcuts(s -> assertTrue("id=" + s.getId(), s.isImmutable()));
962             return this;
963         }
964 
areAllMutable()965         public ShortcutListAsserter areAllMutable() {
966             forAllShortcuts(s -> assertFalse("id=" + s.getId(), s.isImmutable()));
967             return this;
968         }
969 
areAllEnabled()970         public ShortcutListAsserter areAllEnabled() {
971             forAllShortcuts(s -> assertTrue("id=" + s.getId(), s.isEnabled()));
972             areAllWithDisabledReason(ShortcutInfo.DISABLED_REASON_NOT_DISABLED);
973             return this;
974         }
975 
areAllDisabled()976         public ShortcutListAsserter areAllDisabled() {
977             forAllShortcuts(s -> assertFalse("id=" + s.getId(), s.isEnabled()));
978             forAllShortcuts(s -> assertNotEquals("id=" + s.getId(),
979                     ShortcutInfo.DISABLED_REASON_NOT_DISABLED, s.getDisabledReason()));
980             return this;
981         }
982 
areAllFloating()983         public ShortcutListAsserter areAllFloating() {
984             forAllShortcuts(s -> assertTrue("id=" + s.getId(),
985                     s.isPinned() && !s.isDeclaredInManifest() && !s.isDynamic()));
986             return this;
987         }
988 
areAllNotFloating()989         public ShortcutListAsserter areAllNotFloating() {
990             forAllShortcuts(s -> assertTrue("id=" + s.getId(),
991                     !(s.isPinned() && !s.isDeclaredInManifest() && !s.isDynamic())));
992             return this;
993         }
994 
areAllOrphan()995         public ShortcutListAsserter areAllOrphan() {
996             forAllShortcuts(s -> assertTrue("id=" + s.getId(),
997                     !s.isPinned() && !s.isDeclaredInManifest() && !s.isDynamic()));
998             return this;
999         }
1000 
areAllNotOrphan()1001         public ShortcutListAsserter areAllNotOrphan() {
1002             forAllShortcuts(s -> assertTrue("id=" + s.getId(),
1003                     s.isPinned() || s.isDeclaredInManifest() || s.isDynamic()));
1004             return this;
1005         }
1006 
areAllVisibleToPublisher()1007         public ShortcutListAsserter areAllVisibleToPublisher() {
1008             forAllShortcuts(s -> assertTrue("id=" + s.getId(), s.isVisibleToPublisher()));
1009             return this;
1010         }
1011 
areAllNotVisibleToPublisher()1012         public ShortcutListAsserter areAllNotVisibleToPublisher() {
1013             forAllShortcuts(s -> assertFalse("id=" + s.getId(), s.isVisibleToPublisher()));
1014             return this;
1015         }
1016 
areAllWithKeyFieldsOnly()1017         public ShortcutListAsserter areAllWithKeyFieldsOnly() {
1018             forAllShortcuts(s -> assertTrue("id=" + s.getId(), s.hasKeyFieldsOnly()));
1019             return this;
1020         }
1021 
areAllNotWithKeyFieldsOnly()1022         public ShortcutListAsserter areAllNotWithKeyFieldsOnly() {
1023             forAllShortcuts(s -> assertFalse("id=" + s.getId(), s.hasKeyFieldsOnly()));
1024             return this;
1025         }
1026 
areAllWithActivity(ComponentName activity)1027         public ShortcutListAsserter areAllWithActivity(ComponentName activity) {
1028             forAllShortcuts(s -> assertEquals("id=" + s.getId(), activity, s.getActivity()));
1029             return this;
1030         }
1031 
areAllWithNoActivity()1032         public ShortcutListAsserter areAllWithNoActivity() {
1033             forAllShortcuts(s -> assertNull("id=" + s.getId(), s.getActivity()));
1034             return this;
1035         }
1036 
areAllWithIntent()1037         public ShortcutListAsserter areAllWithIntent() {
1038             forAllShortcuts(s -> assertNotNull("id=" + s.getId(), s.getIntent()));
1039             return this;
1040         }
1041 
areAllWithNoIntent()1042         public ShortcutListAsserter areAllWithNoIntent() {
1043             forAllShortcuts(s -> assertNull("id=" + s.getId(), s.getIntent()));
1044             return this;
1045         }
1046 
areAllWithDisabledReason(int disabledReason)1047         public ShortcutListAsserter areAllWithDisabledReason(int disabledReason) {
1048             forAllShortcuts(s -> assertEquals("id=" + s.getId(),
1049                     disabledReason, s.getDisabledReason()));
1050             if (disabledReason >= ShortcutInfo.DISABLED_REASON_VERSION_LOWER) {
1051                 areAllNotVisibleToPublisher();
1052             } else {
1053                 areAllVisibleToPublisher();
1054             }
1055             return this;
1056         }
1057 
forAllShortcuts(Consumer<ShortcutInfo> sa)1058         public ShortcutListAsserter forAllShortcuts(Consumer<ShortcutInfo> sa) {
1059             boolean found = false;
1060             for (int i = 0; i < mList.size(); i++) {
1061                 final ShortcutInfo si = mList.get(i);
1062                 found = true;
1063                 sa.accept(si);
1064             }
1065             assertTrue("No shortcuts found.", found);
1066             return this;
1067         }
1068 
forShortcut(Predicate<ShortcutInfo> p, Consumer<ShortcutInfo> sa)1069         public ShortcutListAsserter forShortcut(Predicate<ShortcutInfo> p,
1070                 Consumer<ShortcutInfo> sa) {
1071             boolean found = false;
1072             for (int i = 0; i < mList.size(); i++) {
1073                 final ShortcutInfo si = mList.get(i);
1074                 if (p.test(si)) {
1075                     found = true;
1076                     try {
1077                         sa.accept(si);
1078                     } catch (Throwable e) {
1079                         throw new AssertionError("Assertion failed for shortcut " + si.getId(), e);
1080                     }
1081                 }
1082             }
1083             assertTrue("Shortcut with the given condition not found.", found);
1084             return this;
1085         }
1086 
forShortcutWithId(String id, Consumer<ShortcutInfo> sa)1087         public ShortcutListAsserter forShortcutWithId(String id, Consumer<ShortcutInfo> sa) {
1088             forShortcut(si -> si.getId().equals(id), sa);
1089 
1090             return this;
1091         }
1092     }
1093 
assertBundlesEqual(BaseBundle b1, BaseBundle b2)1094     public static void assertBundlesEqual(BaseBundle b1, BaseBundle b2) {
1095         if (b1 == null && b2 == null) {
1096             return; // pass
1097         }
1098         assertNotNull("b1 is null but b2 is not", b1);
1099         assertNotNull("b2 is null but b1 is not", b2);
1100 
1101         // HashSet makes the error message readable.
1102         assertEquals(set(b1.keySet()), set(b2.keySet()));
1103 
1104         for (String key : b1.keySet()) {
1105             final Object v1 = b1.get(key);
1106             final Object v2 = b2.get(key);
1107             if (v1 == null) {
1108                 if (v2 == null) {
1109                     return;
1110                 }
1111             }
1112             if (v1.equals(v2)) {
1113                 return;
1114             }
1115 
1116             assertTrue("Only either value is null: key=" + key
1117                     + " b1=" + b1 + " b2=" + b2, v1 != null && v2 != null);
1118             assertEquals("Class mismatch: key=" + key, v1.getClass(), v2.getClass());
1119 
1120             if (v1 instanceof BaseBundle) {
1121                 assertBundlesEqual((BaseBundle) v1, (BaseBundle) v2);
1122 
1123             } else if (v1 instanceof boolean[]) {
1124                 assertTrue(Arrays.equals((boolean[]) v1, (boolean[]) v2));
1125 
1126             } else if (v1 instanceof int[]) {
1127                 MoreAsserts.assertEquals((int[]) v1, (int[]) v2);
1128 
1129             } else if (v1 instanceof double[]) {
1130                 MoreAsserts.assertEquals((double[]) v1, (double[]) v2);
1131 
1132             } else if (v1 instanceof String[]) {
1133                 MoreAsserts.assertEquals((String[]) v1, (String[]) v2);
1134 
1135             } else if (v1 instanceof Double) {
1136                 if (((Double) v1).isNaN()) {
1137                     assertTrue(((Double) v2).isNaN());
1138                 } else {
1139                     assertEquals(v1, v2);
1140                 }
1141 
1142             } else {
1143                 assertEquals(v1, v2);
1144             }
1145         }
1146     }
1147 
waitOnMainThread()1148     public static void waitOnMainThread() throws InterruptedException {
1149         final CountDownLatch latch = new CountDownLatch(1);
1150 
1151         new Handler(Looper.getMainLooper()).post(() -> latch.countDown());
1152 
1153         latch.await();
1154     }
1155 
1156     public static class LauncherCallbackAsserter {
1157         private final LauncherApps.Callback mCallback = mock(LauncherApps.Callback.class);
1158 
getMockCallback()1159         private Callback getMockCallback() {
1160             return mCallback;
1161         }
1162 
assertNoCallbackCalled()1163         public LauncherCallbackAsserter assertNoCallbackCalled() {
1164             verify(mCallback, times(0)).onShortcutsChanged(
1165                     anyString(),
1166                     any(List.class),
1167                     any(UserHandle.class));
1168             return this;
1169         }
1170 
assertNoCallbackCalledForPackage( String publisherPackageName)1171         public LauncherCallbackAsserter assertNoCallbackCalledForPackage(
1172                 String publisherPackageName) {
1173             verify(mCallback, times(0)).onShortcutsChanged(
1174                     eq(publisherPackageName),
1175                     any(List.class),
1176                     any(UserHandle.class));
1177             return this;
1178         }
1179 
assertNoCallbackCalledForPackageAndUser( String publisherPackageName, UserHandle publisherUserHandle)1180         public LauncherCallbackAsserter assertNoCallbackCalledForPackageAndUser(
1181                 String publisherPackageName, UserHandle publisherUserHandle) {
1182             verify(mCallback, times(0)).onShortcutsChanged(
1183                     eq(publisherPackageName),
1184                     any(List.class),
1185                     eq(publisherUserHandle));
1186             return this;
1187         }
1188 
assertCallbackCalledForPackageAndUser( String publisherPackageName, UserHandle publisherUserHandle)1189         public ShortcutListAsserter assertCallbackCalledForPackageAndUser(
1190                 String publisherPackageName, UserHandle publisherUserHandle) {
1191             final ArgumentCaptor<List> shortcuts = ArgumentCaptor.forClass(List.class);
1192             verify(mCallback, atLeastOnce()).onShortcutsChanged(
1193                     eq(publisherPackageName),
1194                     shortcuts.capture(),
1195                     eq(publisherUserHandle));
1196             return new ShortcutListAsserter(shortcuts.getValue());
1197         }
1198     }
1199 
assertForLauncherCallback( LauncherApps launcherApps, Runnable body)1200     public static LauncherCallbackAsserter assertForLauncherCallback(
1201             LauncherApps launcherApps, Runnable body) throws InterruptedException {
1202         final LauncherCallbackAsserter asserter = new LauncherCallbackAsserter();
1203         launcherApps.registerCallback(asserter.getMockCallback(),
1204                 new Handler(Looper.getMainLooper()));
1205 
1206         body.run();
1207 
1208         waitOnMainThread();
1209 
1210         // TODO unregister doesn't work well during unit tests.  Figure out and fix it.
1211         // launcherApps.unregisterCallback(asserter.getMockCallback());
1212 
1213         return asserter;
1214     }
1215 
assertForLauncherCallbackNoThrow( LauncherApps launcherApps, Runnable body)1216     public static LauncherCallbackAsserter assertForLauncherCallbackNoThrow(
1217             LauncherApps launcherApps, Runnable body) {
1218         try {
1219             return assertForLauncherCallback(launcherApps, body);
1220         } catch (InterruptedException e) {
1221             fail("Caught InterruptedException");
1222             return null; // Never happens.
1223         }
1224     }
1225 
retryUntil(BooleanSupplier checker, String message)1226     public static void retryUntil(BooleanSupplier checker, String message) {
1227         retryUntil(checker, message, 30);
1228     }
1229 
retryUntil(BooleanSupplier checker, String message, long timeoutSeconds)1230     public static void retryUntil(BooleanSupplier checker, String message, long timeoutSeconds) {
1231         final long timeOut = System.currentTimeMillis() + timeoutSeconds * 1000;
1232         while (!checker.getAsBoolean()) {
1233             if (System.currentTimeMillis() > timeOut) {
1234                 break;
1235             }
1236             try {
1237                 Thread.sleep(200);
1238             } catch (InterruptedException ignore) {
1239             }
1240         }
1241         assertTrue(message, checker.getAsBoolean());
1242     }
1243 }
1244