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