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