blob: c3429334781431c425902cb5c069c8b417b2e5b8 [file] [log] [blame]
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07001/*
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 */
16package com.android.server.pm.shortcutmanagertest;
17
18import static junit.framework.Assert.assertEquals;
19import static junit.framework.Assert.assertFalse;
20import static junit.framework.Assert.assertNotNull;
21import static junit.framework.Assert.assertNull;
22import static junit.framework.Assert.assertTrue;
23import static junit.framework.Assert.fail;
24
25import static org.mockito.Matchers.any;
26import static org.mockito.Matchers.anyList;
27import static org.mockito.Matchers.anyString;
28import static org.mockito.Matchers.eq;
Makoto Onuki82fb2eb2017-03-31 16:58:26 -070029import static org.mockito.Mockito.atLeastOnce;
Makoto Onukib08790c2016-06-23 14:05:46 -070030import static org.mockito.Mockito.mock;
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -070031import static org.mockito.Mockito.reset;
32import static org.mockito.Mockito.times;
33import static org.mockito.Mockito.verify;
34
35import android.app.Instrumentation;
Makoto Onuki7001a612016-05-27 13:24:28 -070036import android.content.ComponentName;
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -070037import android.content.Context;
38import android.content.pm.LauncherApps;
Makoto Onukib08790c2016-06-23 14:05:46 -070039import android.content.pm.LauncherApps.Callback;
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -070040import android.content.pm.ShortcutInfo;
41import android.graphics.Bitmap;
42import android.graphics.BitmapFactory;
43import android.os.BaseBundle;
44import android.os.Bundle;
Makoto Onukib08790c2016-06-23 14:05:46 -070045import android.os.Handler;
46import android.os.Looper;
Makoto Onuki7001a612016-05-27 13:24:28 -070047import android.os.Parcel;
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -070048import android.os.ParcelFileDescriptor;
Makoto Onukib5a012f2016-06-21 11:13:53 -070049import android.os.PersistableBundle;
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -070050import android.os.UserHandle;
51import android.test.MoreAsserts;
52import android.util.Log;
53
54import junit.framework.Assert;
55
56import org.hamcrest.BaseMatcher;
57import org.hamcrest.Description;
58import org.hamcrest.Matcher;
Makoto Onuki9c850012016-07-26 15:50:50 -070059import org.json.JSONException;
60import org.json.JSONObject;
Makoto Onukib08790c2016-06-23 14:05:46 -070061import org.mockito.ArgumentCaptor;
Makoto Onukid0010c52017-03-30 14:17:35 -070062import org.mockito.ArgumentMatcher;
63import org.mockito.ArgumentMatchers;
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -070064import org.mockito.Mockito;
Paul Duffin192bb0b2017-03-09 18:49:41 +000065import org.mockito.hamcrest.MockitoHamcrest;
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -070066
67import java.io.BufferedReader;
Makoto Onuki0b9d1db2016-07-18 14:16:41 -070068import java.io.File;
69import java.io.FileNotFoundException;
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -070070import java.io.FileReader;
71import java.io.IOException;
72import java.util.ArrayList;
73import java.util.Arrays;
74import java.util.Collection;
Makoto Onuki9e1f5592016-06-08 12:30:23 -070075import java.util.Collections;
76import java.util.Comparator;
77import java.util.LinkedHashSet;
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -070078import java.util.List;
79import java.util.Set;
Makoto Onuki9e1f5592016-06-08 12:30:23 -070080import java.util.SortedSet;
81import java.util.TreeSet;
Makoto Onukib08790c2016-06-23 14:05:46 -070082import java.util.concurrent.CountDownLatch;
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -070083import java.util.function.BooleanSupplier;
Makoto Onukidf6da042016-06-16 09:51:40 -070084import java.util.function.Consumer;
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -070085import java.util.function.Function;
86import java.util.function.Predicate;
87
Makoto Onuki51ab2b32016-06-02 11:03:51 -070088/**
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 */
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -070092public class ShortcutManagerTestUtils {
93 private static final String TAG = "ShortcutManagerUtils";
94
Makoto Onuki9c850012016-07-26 15:50:50 -070095 private static final boolean ENABLE_DUMPSYS = true; // DO NOT SUBMIT WITH true
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -070096
97 private static final int STANDARD_TIMEOUT_SEC = 5;
98
Makoto Onuki9e1f5592016-06-08 12:30:23 -070099 private static final String[] EMPTY_STRINGS = new String[0];
100
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700101 private ShortcutManagerTestUtils() {
102 }
103
Makoto Onuki0b9d1db2016-07-18 14:16:41 -0700104 public static List<String> readAll(File file) throws FileNotFoundException {
105 return readAll(ParcelFileDescriptor.open(
106 file.getAbsoluteFile(), ParcelFileDescriptor.MODE_READ_ONLY));
107 }
108
109 public static List<String> readAll(ParcelFileDescriptor pfd) {
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700110 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
Makoto Onuki0b9d1db2016-07-18 14:16:41 -0700130 public static String concatResult(List<String> result) {
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700131 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
Makoto Onuki0b9d1db2016-07-18 14:16:41 -0700139 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
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
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
Makoto Onuki7051d162016-08-19 14:39:43 -0700163 public static List<String> runCommand(Instrumentation instrumentation, String command) {
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700164 return runCommand(instrumentation, command, null);
165 }
Makoto Onuki7051d162016-08-19 14:39:43 -0700166 public static List<String> runCommand(Instrumentation instrumentation, String command,
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700167 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
Makoto Onuki7051d162016-08-19 14:39:43 -0700182 public static void runCommandForNoOutput(Instrumentation instrumentation, String command) {
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700183 runCommand(instrumentation, command, result -> result.size() == 0);
184 }
185
Makoto Onuki7051d162016-08-19 14:39:43 -0700186 public static List<String> runShortcutCommand(Instrumentation instrumentation, String command,
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700187 Predicate<List<String>> resultAsserter) {
188 return runCommand(instrumentation, "cmd shortcut " + command, resultAsserter);
189 }
190
191 public static List<String> runShortcutCommandForSuccess(Instrumentation instrumentation,
192 String command) {
193 return runShortcutCommand(instrumentation, command, result -> result.contains("Success"));
194 }
195
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
210 public static void setDefaultLauncher(Instrumentation instrumentation, String component) {
Makoto Onuki1ae63b02016-08-22 13:15:36 -0700211 runCommand(instrumentation, "cmd package set-home-activity --user "
212 + instrumentation.getContext().getUserId() + " " + component,
Makoto Onuki3bdbf982016-06-23 16:56:35 -0700213 result -> result.contains("Success"));
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700214 }
215
216 public static void setDefaultLauncher(Instrumentation instrumentation, Context packageContext) {
217 setDefaultLauncher(instrumentation, packageContext.getPackageName()
218 + "/android.content.pm.cts.shortcutmanager.packages.Launcher");
219 }
220
221 public static void overrideConfig(Instrumentation instrumentation, String config) {
222 runShortcutCommandForSuccess(instrumentation, "override-config " + config);
223 }
224
225 public static void resetConfig(Instrumentation instrumentation) {
226 runShortcutCommandForSuccess(instrumentation, "reset-config");
227 }
228
229 public static void resetThrottling(Instrumentation instrumentation) {
230 runShortcutCommandForSuccess(instrumentation, "reset-throttling");
231 }
232
233 public static void resetAllThrottling(Instrumentation instrumentation) {
234 runShortcutCommandForSuccess(instrumentation, "reset-all-throttling");
235 }
236
237 public static void clearShortcuts(Instrumentation instrumentation, int userId,
238 String packageName) {
239 runShortcutCommandForSuccess(instrumentation, "clear-shortcuts "
240 + " --user " + userId + " " + packageName);
241 }
242
Makoto Onuki9c850012016-07-26 15:50:50 -0700243 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
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
261 public static void appOps(Instrumentation instrumentation, String packageName,
262 String op, String mode) {
263 runCommand(instrumentation, "appops set " + packageName + " " + op + " " + mode);
264 }
265
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700266 public static void dumpsysShortcut(Instrumentation instrumentation) {
267 if (!ENABLE_DUMPSYS) {
268 return;
269 }
Makoto Onuki3bdbf982016-06-23 16:56:35 -0700270 Log.e(TAG, "Dumpsys shortcut");
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700271 for (String s : runCommand(instrumentation, "dumpsys shortcut")) {
272 Log.e(TAG, s);
273 }
274 }
275
Makoto Onuki9c850012016-07-26 15:50:50 -0700276 public static JSONObject getCheckinDump(Instrumentation instrumentation) throws JSONException {
277 return new JSONObject(concatResult(runCommand(instrumentation, "dumpsys shortcut -c")));
278 }
279
280 public static boolean isLowRamDevice(Instrumentation instrumentation) throws JSONException {
281 return getCheckinDump(instrumentation).getBoolean("lowRam");
282 }
283
284 public static int getIconSize(Instrumentation instrumentation) throws JSONException {
285 return getCheckinDump(instrumentation).getInt("iconSize");
286 }
287
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700288 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
Makoto Onukib5a012f2016-06-21 11:13:53 -0700315 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
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700342 public static <T> List<T> list(T... array) {
343 return Arrays.asList(array);
344 }
345
346 public static <T> Set<T> hashSet(Set<T> in) {
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700347 return new LinkedHashSet<>(in);
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700348 }
349
350 public static <T> Set<T> set(T... values) {
351 return set(v -> v, values);
352 }
353
354 public static <T, V> Set<T> set(Function<V, T> converter, V... values) {
355 return set(converter, Arrays.asList(values));
356 }
357
358 public static <T, V> Set<T> set(Function<V, T> converter, List<V> values) {
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700359 final LinkedHashSet<T> ret = new LinkedHashSet<>();
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700360 for (V v : values) {
361 ret.add(converter.apply(v));
362 }
363 return ret;
364 }
365
366 public static void resetAll(Collection<?> mocks) {
367 for (Object o : mocks) {
368 reset(o);
369 }
370 }
Makoto Onuki22fcc682016-05-17 14:52:19 -0700371
Makoto Onukidf6da042016-06-16 09:51:40 -0700372 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;
Makoto Onuki22fcc682016-05-17 14:52:19 -0700378 }
379
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700380 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
Makoto Onuki7001a612016-05-27 13:24:28 -0700386 public static List<ShortcutInfo> filterByActivity(List<ShortcutInfo> list,
387 ComponentName activity) {
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700388 return filter(list, si ->
389 (si.getActivity().equals(activity)
Makoto Onukib5a012f2016-06-21 11:13:53 -0700390 && (si.isDeclaredInManifest() || si.isDynamic())));
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700391 }
392
393 public static List<ShortcutInfo> changedSince(List<ShortcutInfo> list, long time) {
394 return filter(list, si -> si.getLastChangedTimestamp() >= time);
Makoto Onuki7001a612016-05-27 13:24:28 -0700395 }
396
Makoto Onuki0b9d1db2016-07-18 14:16:41 -0700397 @FunctionalInterface
398 public interface ExceptionRunnable {
399 void run() throws Exception;
400 }
401
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700402 public static void assertExpectException(Class<? extends Throwable> expectedExceptionType,
Makoto Onuki0b9d1db2016-07-18 14:16:41 -0700403 String expectedExceptionMessageRegex, ExceptionRunnable r) {
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700404 assertExpectException("", expectedExceptionType, expectedExceptionMessageRegex, r);
405 }
406
Makoto Onuki22fcc682016-05-17 14:52:19 -0700407 public static void assertCannotUpdateImmutable(Runnable r) {
408 assertExpectException(
Makoto Onuki0b9d1db2016-07-18 14:16:41 -0700409 IllegalArgumentException.class, "may not be manipulated via APIs", r::run);
Makoto Onuki22fcc682016-05-17 14:52:19 -0700410 }
411
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700412 public static void assertDynamicShortcutCountExceeded(Runnable r) {
413 assertExpectException(IllegalArgumentException.class,
Makoto Onuki0b9d1db2016-07-18 14:16:41 -0700414 "Max number of dynamic shortcuts exceeded", r::run);
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700415 }
416
417 public static void assertExpectException(String message,
418 Class<? extends Throwable> expectedExceptionType,
Makoto Onuki0b9d1db2016-07-18 14:16:41 -0700419 String expectedExceptionMessageRegex, ExceptionRunnable r) {
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700420 try {
421 r.run();
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700422 } 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 }
Makoto Onukia1d38b32016-06-10 15:32:26 -0700430 return; // Pass
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700431 }
Makoto Onukia1d38b32016-06-10 15:32:26 -0700432 Assert.fail("Expected exception type " + expectedExceptionType.getName()
433 + " was not thrown");
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700434 }
435
436 public static List<ShortcutInfo> assertShortcutIds(List<ShortcutInfo> actualShortcuts,
437 String... expectedIds) {
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700438 final SortedSet<String> expected = new TreeSet<>(list(expectedIds));
439 final SortedSet<String> actual = new TreeSet<>();
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700440 for (ShortcutInfo s : actualShortcuts) {
441 actual.add(s.getId());
442 }
443
444 // Compare the sets.
445 assertEquals(expected, actual);
446 return actualShortcuts;
447 }
448
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700449 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
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700460 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
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
476 public static List<ShortcutInfo> assertAllHaveTitle(
477 List<ShortcutInfo> actualShortcuts) {
478 for (ShortcutInfo s : actualShortcuts) {
Makoto Onukieddbfec2016-05-31 17:04:34 -0700479 assertNotNull("ID " + s.getId(), s.getShortLabel());
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700480 }
481 return actualShortcuts;
482 }
483
484 public static List<ShortcutInfo> assertAllNotHaveTitle(
485 List<ShortcutInfo> actualShortcuts) {
486 for (ShortcutInfo s : actualShortcuts) {
Makoto Onukieddbfec2016-05-31 17:04:34 -0700487 assertNull("ID " + s.getId(), s.getShortLabel());
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700488 }
489 return actualShortcuts;
490 }
491
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700492 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
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
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
Hakan Seyalioglu58fc95d2016-12-13 15:23:22 -0800515 public static List<ShortcutInfo> assertAllChooser(List<ShortcutInfo> actualShortcuts) {
516 for (ShortcutInfo s : actualShortcuts) {
517 assertTrue("ID " + s.getId(), s.isChooser());
518 }
519 return actualShortcuts;
520 }
521
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700522 public static List<ShortcutInfo> assertAllPinned(List<ShortcutInfo> actualShortcuts) {
523 for (ShortcutInfo s : actualShortcuts) {
524 assertTrue("ID " + s.getId(), s.isPinned());
525 }
526 return actualShortcuts;
527 }
528
529 public static List<ShortcutInfo> assertAllDynamicOrPinned(
530 List<ShortcutInfo> actualShortcuts) {
531 for (ShortcutInfo s : actualShortcuts) {
532 assertTrue("ID " + s.getId(), s.isDynamic() || s.isPinned());
533 }
534 return actualShortcuts;
535 }
536
Makoto Onuki22fcc682016-05-17 14:52:19 -0700537 public static List<ShortcutInfo> assertAllManifest(
538 List<ShortcutInfo> actualShortcuts) {
539 for (ShortcutInfo s : actualShortcuts) {
Makoto Onukib5a012f2016-06-21 11:13:53 -0700540 assertTrue("ID " + s.getId(), s.isDeclaredInManifest());
Makoto Onuki22fcc682016-05-17 14:52:19 -0700541 }
542 return actualShortcuts;
543 }
544
545 public static List<ShortcutInfo> assertAllNotManifest(
546 List<ShortcutInfo> actualShortcuts) {
547 for (ShortcutInfo s : actualShortcuts) {
Makoto Onukib5a012f2016-06-21 11:13:53 -0700548 assertFalse("ID " + s.getId(), s.isDeclaredInManifest());
Makoto Onuki22fcc682016-05-17 14:52:19 -0700549 }
550 return actualShortcuts;
551 }
552
553 public static List<ShortcutInfo> assertAllDisabled(
554 List<ShortcutInfo> actualShortcuts) {
555 for (ShortcutInfo s : actualShortcuts) {
556 assertTrue("ID " + s.getId(), !s.isEnabled());
557 }
558 return actualShortcuts;
559 }
560
561 public static List<ShortcutInfo> assertAllEnabled(
562 List<ShortcutInfo> actualShortcuts) {
563 for (ShortcutInfo s : actualShortcuts) {
564 assertTrue("ID " + s.getId(), s.isEnabled());
565 }
566 return actualShortcuts;
567 }
568
569 public static List<ShortcutInfo> assertAllImmutable(
570 List<ShortcutInfo> actualShortcuts) {
571 for (ShortcutInfo s : actualShortcuts) {
572 assertTrue("ID " + s.getId(), s.isImmutable());
573 }
574 return actualShortcuts;
575 }
576
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700577 public static void assertDynamicOnly(ShortcutInfo si) {
578 assertTrue(si.isDynamic());
579 assertFalse(si.isPinned());
580 }
581
582 public static void assertPinnedOnly(ShortcutInfo si) {
583 assertFalse(si.isDynamic());
Makoto Onukib5a012f2016-06-21 11:13:53 -0700584 assertFalse(si.isDeclaredInManifest());
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700585 assertTrue(si.isPinned());
586 }
587
588 public static void assertDynamicAndPinned(ShortcutInfo si) {
589 assertTrue(si.isDynamic());
590 assertTrue(si.isPinned());
591 }
592
593 public static void assertBitmapSize(int expectedWidth, int expectedHeight, Bitmap bitmap) {
594 assertEquals("width", expectedWidth, bitmap.getWidth());
595 assertEquals("height", expectedHeight, bitmap.getHeight());
596 }
597
598 public static <T> void assertAllUnique(Collection<T> list) {
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700599 final Set<Object> set = new LinkedHashSet<>();
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700600 for (T item : list) {
601 if (set.contains(item)) {
602 fail("Duplicate item found: " + item + " (in the list: " + list + ")");
603 }
604 set.add(item);
605 }
606 }
607
Makoto Onuki39686e82016-04-13 18:03:00 -0700608 public static ShortcutInfo findShortcut(List<ShortcutInfo> list, String id) {
609 for (ShortcutInfo si : list) {
610 if (si.getId().equals(id)) {
611 return si;
612 }
613 }
614 fail("Shortcut " + id + " not found in the list");
615 return null;
616 }
617
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700618 public static Bitmap pfdToBitmap(ParcelFileDescriptor pfd) {
619 assertNotNull(pfd);
620 try {
621 try {
622 return BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor());
623 } finally {
624 pfd.close();
625 }
626 } catch (IOException e) {
627 throw new RuntimeException(e);
628 }
629 }
630
631 public static void assertBundleEmpty(BaseBundle b) {
632 assertTrue(b == null || b.size() == 0);
633 }
634
635 public static void assertCallbackNotReceived(LauncherApps.Callback mock) {
636 verify(mock, times(0)).onShortcutsChanged(anyString(), anyList(),
637 any(UserHandle.class));
638 }
639
640 public static void assertCallbackReceived(LauncherApps.Callback mock,
641 UserHandle user, String packageName, String... ids) {
642 verify(mock).onShortcutsChanged(eq(packageName), checkShortcutIds(ids),
643 eq(user));
644 }
645
646 public static boolean checkAssertSuccess(Runnable r) {
647 try {
648 r.run();
649 return true;
650 } catch (AssertionError e) {
651 return false;
652 }
653 }
654
655 public static <T> T checkArgument(Predicate<T> checker, String description,
656 List<T> matchedCaptor) {
657 final Matcher<T> m = new BaseMatcher<T>() {
658 @Override
659 public boolean matches(Object item) {
660 if (item == null) {
661 return false;
662 }
663 final T value = (T) item;
664 if (!checker.test(value)) {
665 return false;
666 }
667
668 if (matchedCaptor != null) {
669 matchedCaptor.add(value);
670 }
671 return true;
672 }
673
674 @Override
675 public void describeTo(Description d) {
676 d.appendText(description);
677 }
678 };
Paul Duffin192bb0b2017-03-09 18:49:41 +0000679 return MockitoHamcrest.argThat(m);
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700680 }
681
682 public static List<ShortcutInfo> checkShortcutIds(String... ids) {
683 return checkArgument((List<ShortcutInfo> list) -> {
684 final Set<String> actualSet = set(si -> si.getId(), list);
685 return actualSet.equals(set(ids));
686
687 }, "Shortcut IDs=[" + Arrays.toString(ids) + "]", null);
688 }
689
Makoto Onuki7001a612016-05-27 13:24:28 -0700690 public static ShortcutInfo parceled(ShortcutInfo si) {
691 Parcel p = Parcel.obtain();
692 p.writeParcelable(si, 0);
693 p.setDataPosition(0);
694 ShortcutInfo si2 = p.readParcelable(ShortcutManagerTestUtils.class.getClassLoader());
695 p.recycle();
696 return si2;
697 }
698
699 public static List<ShortcutInfo> cloneShortcutList(List<ShortcutInfo> list) {
700 if (list == null) {
701 return null;
702 }
703 final List<ShortcutInfo> ret = new ArrayList<>(list.size());
704 for (ShortcutInfo si : list) {
705 ret.add(parceled(si));
706 }
707
708 return ret;
709 }
710
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700711 private static final Comparator<ShortcutInfo> sRankComparator =
712 (ShortcutInfo a, ShortcutInfo b) -> Integer.compare(a.getRank(), b.getRank());
713
714 public static List<ShortcutInfo> sortedByRank(List<ShortcutInfo> shortcuts) {
715 final ArrayList<ShortcutInfo> ret = new ArrayList<>(shortcuts);
716 Collections.sort(ret, sRankComparator);
717 return ret;
718 }
719
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700720 public static void waitUntil(String message, BooleanSupplier condition) {
721 waitUntil(message, condition, STANDARD_TIMEOUT_SEC);
722 }
723
724 public static void waitUntil(String message, BooleanSupplier condition, int timeoutSeconds) {
725 final long timeout = System.currentTimeMillis() + (timeoutSeconds * 1000L);
726 while (System.currentTimeMillis() < timeout) {
727 if (condition.getAsBoolean()) {
728 return;
729 }
730 try {
731 Thread.sleep(100);
732 } catch (InterruptedException e) {
733 throw new RuntimeException(e);
734 }
735 }
736 fail("Timed out for: " + message);
737 }
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700738
Makoto Onukid0010c52017-03-30 14:17:35 -0700739 public static final <T> T anyOrNull(Class<T> clazz) {
740 return ArgumentMatchers.argThat(value -> true);
741 }
742
743 public static final String anyStringOrNull() {
744 return ArgumentMatchers.argThat(value -> true);
745 }
746
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700747 public static ShortcutListAsserter assertWith(List<ShortcutInfo> list) {
748 return new ShortcutListAsserter(list);
749 }
750
Makoto Onuki2d895c32016-12-02 15:48:40 -0800751 public static ShortcutListAsserter assertWith(ShortcutInfo... list) {
752 return assertWith(list(list));
753 }
754
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700755 /**
756 * New style assertion that allows chained calls.
757 */
758 public static class ShortcutListAsserter {
Makoto Onukidf6da042016-06-16 09:51:40 -0700759 private final ShortcutListAsserter mOriginal;
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700760 private final List<ShortcutInfo> mList;
761
762 ShortcutListAsserter(List<ShortcutInfo> list) {
Makoto Onukidf6da042016-06-16 09:51:40 -0700763 this(null, list);
764 }
765
766 private ShortcutListAsserter(ShortcutListAsserter original, List<ShortcutInfo> list) {
Makoto Onukib08790c2016-06-23 14:05:46 -0700767 mOriginal = (original == null) ? this : original;
768 mList = (list == null) ? new ArrayList<>(0) : new ArrayList<>(list);
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700769 }
770
Makoto Onukidf6da042016-06-16 09:51:40 -0700771 public ShortcutListAsserter revertToOriginalList() {
772 return mOriginal;
773 }
774
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700775 public ShortcutListAsserter selectDynamic() {
Makoto Onukidf6da042016-06-16 09:51:40 -0700776 return new ShortcutListAsserter(this,
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700777 filter(mList, ShortcutInfo::isDynamic));
778 }
779
780 public ShortcutListAsserter selectManifest() {
Makoto Onukidf6da042016-06-16 09:51:40 -0700781 return new ShortcutListAsserter(this,
Makoto Onukib5a012f2016-06-21 11:13:53 -0700782 filter(mList, ShortcutInfo::isDeclaredInManifest));
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700783 }
784
785 public ShortcutListAsserter selectPinned() {
Makoto Onukidf6da042016-06-16 09:51:40 -0700786 return new ShortcutListAsserter(this,
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700787 filter(mList, ShortcutInfo::isPinned));
788 }
789
Makoto Onuki106ff7a2016-12-01 10:17:57 -0800790 public ShortcutListAsserter selectFloating() {
791 return new ShortcutListAsserter(this,
792 filter(mList, (si -> si.isPinned()
793 && !(si.isDynamic() || si.isDeclaredInManifest()))));
794 }
795
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700796 public ShortcutListAsserter selectByActivity(ComponentName activity) {
Makoto Onukidf6da042016-06-16 09:51:40 -0700797 return new ShortcutListAsserter(this,
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700798 ShortcutManagerTestUtils.filterByActivity(mList, activity));
799 }
800
801 public ShortcutListAsserter selectByChangedSince(long time) {
Makoto Onukidf6da042016-06-16 09:51:40 -0700802 return new ShortcutListAsserter(this,
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700803 ShortcutManagerTestUtils.changedSince(mList, time));
804 }
805
Makoto Onukidf6da042016-06-16 09:51:40 -0700806 public ShortcutListAsserter selectByIds(String... ids) {
807 final Set<String> idSet = set(ids);
808 final ArrayList<ShortcutInfo> selected = new ArrayList<>();
809 for (ShortcutInfo si : mList) {
810 if (idSet.contains(si.getId())) {
811 selected.add(si);
812 idSet.remove(si.getId());
813 }
814 }
815 if (idSet.size() > 0) {
816 fail("Shortcuts not found for IDs=" + idSet);
817 }
818
819 return new ShortcutListAsserter(this, selected);
820 }
821
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700822 public ShortcutListAsserter toSortByRank() {
Makoto Onukidf6da042016-06-16 09:51:40 -0700823 return new ShortcutListAsserter(this,
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700824 ShortcutManagerTestUtils.sortedByRank(mList));
825 }
826
Makoto Onukidf6da042016-06-16 09:51:40 -0700827 public ShortcutListAsserter call(Consumer<List<ShortcutInfo>> c) {
828 c.accept(mList);
829 return this;
830 }
831
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700832 public ShortcutListAsserter haveIds(String... expectedIds) {
833 assertShortcutIds(mList, expectedIds);
834 return this;
835 }
836
837 public ShortcutListAsserter haveIdsOrdered(String... expectedIds) {
838 assertShortcutIdsOrdered(mList, expectedIds);
839 return this;
840 }
841
842 private ShortcutListAsserter haveSequentialRanks() {
843 for (int i = 0; i < mList.size(); i++) {
Makoto Onukidf6da042016-06-16 09:51:40 -0700844 final ShortcutInfo si = mList.get(i);
845 assertEquals("Rank not sequential: id=" + si.getId(), i, si.getRank());
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700846 }
847 return this;
848 }
849
850 public ShortcutListAsserter haveRanksInOrder(String... expectedIds) {
851 toSortByRank()
852 .haveSequentialRanks()
853 .haveIdsOrdered(expectedIds);
854 return this;
855 }
856
857 public ShortcutListAsserter isEmpty() {
858 assertEquals(0, mList.size());
859 return this;
860 }
Makoto Onukidf6da042016-06-16 09:51:40 -0700861
862 public ShortcutListAsserter areAllDynamic() {
863 forAllShortcuts(s -> assertTrue("id=" + s.getId(), s.isDynamic()));
864 return this;
865 }
866
867 public ShortcutListAsserter areAllNotDynamic() {
868 forAllShortcuts(s -> assertFalse("id=" + s.getId(), s.isDynamic()));
869 return this;
870 }
871
872 public ShortcutListAsserter areAllPinned() {
873 forAllShortcuts(s -> assertTrue("id=" + s.getId(), s.isPinned()));
874 return this;
875 }
876
877 public ShortcutListAsserter areAllNotPinned() {
878 forAllShortcuts(s -> assertFalse("id=" + s.getId(), s.isPinned()));
879 return this;
880 }
881
882 public ShortcutListAsserter areAllManifest() {
Makoto Onukib5a012f2016-06-21 11:13:53 -0700883 forAllShortcuts(s -> assertTrue("id=" + s.getId(), s.isDeclaredInManifest()));
Makoto Onukidf6da042016-06-16 09:51:40 -0700884 return this;
885 }
886
887 public ShortcutListAsserter areAllNotManifest() {
Makoto Onukib5a012f2016-06-21 11:13:53 -0700888 forAllShortcuts(s -> assertFalse("id=" + s.getId(), s.isDeclaredInManifest()));
Makoto Onukidf6da042016-06-16 09:51:40 -0700889 return this;
890 }
891
892 public ShortcutListAsserter areAllImmutable() {
893 forAllShortcuts(s -> assertTrue("id=" + s.getId(), s.isImmutable()));
894 return this;
895 }
896
897 public ShortcutListAsserter areAllMutable() {
898 forAllShortcuts(s -> assertFalse("id=" + s.getId(), s.isImmutable()));
899 return this;
900 }
901
902 public ShortcutListAsserter areAllEnabled() {
903 forAllShortcuts(s -> assertTrue("id=" + s.getId(), s.isEnabled()));
904 return this;
905 }
906
907 public ShortcutListAsserter areAllDisabled() {
908 forAllShortcuts(s -> assertFalse("id=" + s.getId(), s.isEnabled()));
909 return this;
910 }
911
Makoto Onuki2d895c32016-12-02 15:48:40 -0800912 public ShortcutListAsserter areAllFloating() {
913 forAllShortcuts(s -> assertTrue("id=" + s.getId(),
914 s.isPinned() && !s.isDeclaredInManifest() && !s.isDynamic()));
915 return this;
916 }
917
918 public ShortcutListAsserter areAllNotFloating() {
919 forAllShortcuts(s -> assertTrue("id=" + s.getId(),
920 !(s.isPinned() && !s.isDeclaredInManifest() && !s.isDynamic())));
921 return this;
922 }
923
924 public ShortcutListAsserter areAllOrphan() {
925 forAllShortcuts(s -> assertTrue("id=" + s.getId(),
926 !s.isPinned() && !s.isDeclaredInManifest() && !s.isDynamic()));
927 return this;
928 }
929
930 public ShortcutListAsserter areAllNotOrphan() {
931 forAllShortcuts(s -> assertTrue("id=" + s.getId(),
932 s.isPinned() || s.isDeclaredInManifest() || s.isDynamic()));
933 return this;
934 }
935
Makoto Onuki4d6b87f2016-06-17 13:47:40 -0700936 public ShortcutListAsserter areAllWithKeyFieldsOnly() {
937 forAllShortcuts(s -> assertTrue("id=" + s.getId(), s.hasKeyFieldsOnly()));
938 return this;
939 }
940
941 public ShortcutListAsserter areAllNotWithKeyFieldsOnly() {
942 forAllShortcuts(s -> assertFalse("id=" + s.getId(), s.hasKeyFieldsOnly()));
943 return this;
944 }
945
Makoto Onukib08790c2016-06-23 14:05:46 -0700946 public ShortcutListAsserter areAllWithActivity(ComponentName activity) {
Makoto Onuki255461f2017-01-10 11:47:25 -0800947 forAllShortcuts(s -> assertEquals("id=" + s.getId(), activity, s.getActivity()));
Makoto Onukib08790c2016-06-23 14:05:46 -0700948 return this;
949 }
950
Makoto Onuki106ff7a2016-12-01 10:17:57 -0800951 public ShortcutListAsserter areAllWithNoActivity() {
952 forAllShortcuts(s -> assertNull("id=" + s.getId(), s.getActivity()));
953 return this;
954 }
955
Makoto Onukia01f4f02016-12-15 15:58:41 -0800956 public ShortcutListAsserter areAllWithIntent() {
957 forAllShortcuts(s -> assertNotNull("id=" + s.getId(), s.getIntent()));
958 return this;
959 }
960
961 public ShortcutListAsserter areAllWithNoIntent() {
962 forAllShortcuts(s -> assertNull("id=" + s.getId(), s.getIntent()));
963 return this;
964 }
965
Makoto Onukidf6da042016-06-16 09:51:40 -0700966 public ShortcutListAsserter forAllShortcuts(Consumer<ShortcutInfo> sa) {
Makoto Onuki4d6b87f2016-06-17 13:47:40 -0700967 boolean found = false;
Makoto Onukidf6da042016-06-16 09:51:40 -0700968 for (int i = 0; i < mList.size(); i++) {
969 final ShortcutInfo si = mList.get(i);
Makoto Onuki4d6b87f2016-06-17 13:47:40 -0700970 found = true;
Makoto Onukidf6da042016-06-16 09:51:40 -0700971 sa.accept(si);
972 }
Makoto Onuki4d6b87f2016-06-17 13:47:40 -0700973 assertTrue("No shortcuts found.", found);
Makoto Onukidf6da042016-06-16 09:51:40 -0700974 return this;
975 }
976
977 public ShortcutListAsserter forShortcut(Predicate<ShortcutInfo> p,
978 Consumer<ShortcutInfo> sa) {
979 boolean found = false;
980 for (int i = 0; i < mList.size(); i++) {
981 final ShortcutInfo si = mList.get(i);
982 if (p.test(si)) {
983 found = true;
984 try {
985 sa.accept(si);
986 } catch (Throwable e) {
987 throw new AssertionError("Assertion failed for shortcut " + si.getId(), e);
988 }
989 }
990 }
991 assertTrue("Shortcut with the given condition not found.", found);
992 return this;
993 }
994
995 public ShortcutListAsserter forShortcutWithId(String id, Consumer<ShortcutInfo> sa) {
996 forShortcut(si -> si.getId().equals(id), sa);
997
998 return this;
999 }
Makoto Onuki9e1f5592016-06-08 12:30:23 -07001000 }
Makoto Onukib5a012f2016-06-21 11:13:53 -07001001
1002 public static void assertBundlesEqual(BaseBundle b1, BaseBundle b2) {
1003 if (b1 == null && b2 == null) {
1004 return; // pass
1005 }
Makoto Onuki440a1ea2016-07-20 14:21:18 -07001006 assertNotNull("b1 is null but b2 is not", b1);
1007 assertNotNull("b2 is null but b1 is not", b2);
Makoto Onukib5a012f2016-06-21 11:13:53 -07001008
1009 // HashSet makes the error message readable.
1010 assertEquals(set(b1.keySet()), set(b2.keySet()));
1011
1012 for (String key : b1.keySet()) {
1013 final Object v1 = b1.get(key);
1014 final Object v2 = b2.get(key);
1015 if (v1 == null) {
1016 if (v2 == null) {
1017 return;
1018 }
1019 }
1020 if (v1.equals(v2)) {
1021 return;
1022 }
1023
1024 assertTrue("Only either value is null: key=" + key
1025 + " b1=" + b1 + " b2=" + b2, v1 != null && v2 != null);
1026 assertEquals("Class mismatch: key=" + key, v1.getClass(), v2.getClass());
1027
1028 if (v1 instanceof BaseBundle) {
1029 assertBundlesEqual((BaseBundle) v1, (BaseBundle) v2);
1030
1031 } else if (v1 instanceof boolean[]) {
1032 assertTrue(Arrays.equals((boolean[]) v1, (boolean[]) v2));
1033
1034 } else if (v1 instanceof int[]) {
1035 MoreAsserts.assertEquals((int[]) v1, (int[]) v2);
1036
1037 } else if (v1 instanceof double[]) {
1038 MoreAsserts.assertEquals((double[]) v1, (double[]) v2);
1039
1040 } else if (v1 instanceof String[]) {
1041 MoreAsserts.assertEquals((String[]) v1, (String[]) v2);
1042
1043 } else if (v1 instanceof Double) {
1044 if (((Double) v1).isNaN()) {
1045 assertTrue(((Double) v2).isNaN());
1046 } else {
1047 assertEquals(v1, v2);
1048 }
1049
1050 } else {
1051 assertEquals(v1, v2);
1052 }
1053 }
1054 }
Makoto Onukib08790c2016-06-23 14:05:46 -07001055
1056 public static void waitOnMainThread() throws InterruptedException {
1057 final CountDownLatch latch = new CountDownLatch(1);
1058
1059 new Handler(Looper.getMainLooper()).post(() -> latch.countDown());
1060
1061 latch.await();
1062 }
1063
1064 public static class LauncherCallbackAsserter {
1065 private final LauncherApps.Callback mCallback = mock(LauncherApps.Callback.class);
1066
1067 private Callback getMockCallback() {
1068 return mCallback;
1069 }
1070
1071 public LauncherCallbackAsserter assertNoCallbackCalled() {
1072 verify(mCallback, times(0)).onShortcutsChanged(
1073 anyString(),
1074 any(List.class),
1075 any(UserHandle.class));
1076 return this;
1077 }
1078
1079 public LauncherCallbackAsserter assertNoCallbackCalledForPackage(
1080 String publisherPackageName) {
1081 verify(mCallback, times(0)).onShortcutsChanged(
1082 eq(publisherPackageName),
1083 any(List.class),
1084 any(UserHandle.class));
1085 return this;
1086 }
1087
1088 public LauncherCallbackAsserter assertNoCallbackCalledForPackageAndUser(
1089 String publisherPackageName, UserHandle publisherUserHandle) {
1090 verify(mCallback, times(0)).onShortcutsChanged(
1091 eq(publisherPackageName),
1092 any(List.class),
1093 eq(publisherUserHandle));
1094 return this;
1095 }
1096
1097 public ShortcutListAsserter assertCallbackCalledForPackageAndUser(
1098 String publisherPackageName, UserHandle publisherUserHandle) {
1099 final ArgumentCaptor<List> shortcuts = ArgumentCaptor.forClass(List.class);
Makoto Onuki82fb2eb2017-03-31 16:58:26 -07001100 verify(mCallback, atLeastOnce()).onShortcutsChanged(
Makoto Onukib08790c2016-06-23 14:05:46 -07001101 eq(publisherPackageName),
1102 shortcuts.capture(),
1103 eq(publisherUserHandle));
1104 return new ShortcutListAsserter(shortcuts.getValue());
1105 }
1106 }
1107
1108 public static LauncherCallbackAsserter assertForLauncherCallback(
1109 LauncherApps launcherApps, Runnable body) throws InterruptedException {
1110 final LauncherCallbackAsserter asserter = new LauncherCallbackAsserter();
1111 launcherApps.registerCallback(asserter.getMockCallback(),
1112 new Handler(Looper.getMainLooper()));
1113
1114 body.run();
1115
1116 waitOnMainThread();
1117
Makoto Onuki440a1ea2016-07-20 14:21:18 -07001118 // TODO unregister doesn't work well during unit tests. Figure out and fix it.
1119 // launcherApps.unregisterCallback(asserter.getMockCallback());
Makoto Onuki4e6cef42016-07-13 16:14:01 -07001120
Makoto Onukib08790c2016-06-23 14:05:46 -07001121 return asserter;
1122 }
Makoto Onuki9c850012016-07-26 15:50:50 -07001123
Makoto Onukia01f4f02016-12-15 15:58:41 -08001124 public static LauncherCallbackAsserter assertForLauncherCallbackNoThrow(
1125 LauncherApps launcherApps, Runnable body) {
1126 try {
1127 return assertForLauncherCallback(launcherApps, body);
1128 } catch (InterruptedException e) {
1129 fail("Caught InterruptedException");
1130 return null; // Never happens.
1131 }
1132 }
1133
Makoto Onuki9c850012016-07-26 15:50:50 -07001134 public static void retryUntil(BooleanSupplier checker, String message) {
Makoto Onukid67bf6a2016-08-19 09:10:08 -07001135 retryUntil(checker, message, 30);
1136 }
1137
1138 public static void retryUntil(BooleanSupplier checker, String message, long timeoutSeconds) {
1139 final long timeOut = System.currentTimeMillis() + timeoutSeconds * 1000;
Makoto Onuki9c850012016-07-26 15:50:50 -07001140 while (!checker.getAsBoolean()) {
Makoto Onukia210ccf2016-07-27 14:08:48 -07001141 if (System.currentTimeMillis() > timeOut) {
1142 break;
1143 }
Makoto Onuki9c850012016-07-26 15:50:50 -07001144 try {
1145 Thread.sleep(200);
1146 } catch (InterruptedException ignore) {
1147 }
1148 }
1149 assertTrue(message, checker.getAsBoolean());
1150 }
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07001151}