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