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