OSDN Git Service

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