blob: 2077ecb2799e9a62588b0ecbf9d835cb4c791a3c [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
Makoto Onukia4f89b12017-10-05 10:37:55 -070025import static org.junit.Assert.assertNotEquals;
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -070026import static org.mockito.Matchers.any;
27import static org.mockito.Matchers.anyList;
28import static org.mockito.Matchers.anyString;
29import static org.mockito.Matchers.eq;
Makoto Onuki82fb2eb2017-03-31 16:58:26 -070030import static org.mockito.Mockito.atLeastOnce;
Makoto Onukib08790c2016-06-23 14:05:46 -070031import static org.mockito.Mockito.mock;
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -070032import static org.mockito.Mockito.reset;
33import static org.mockito.Mockito.times;
34import static org.mockito.Mockito.verify;
35
36import android.app.Instrumentation;
Makoto Onuki7001a612016-05-27 13:24:28 -070037import android.content.ComponentName;
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -070038import android.content.Context;
Felipe Leme90205ef2019-03-05 09:59:52 -080039import android.content.LocusId;
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -070040import android.content.pm.LauncherApps;
Makoto Onukib08790c2016-06-23 14:05:46 -070041import android.content.pm.LauncherApps.Callback;
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -070042import android.content.pm.ShortcutInfo;
43import android.graphics.Bitmap;
44import android.graphics.BitmapFactory;
45import android.os.BaseBundle;
46import android.os.Bundle;
Makoto Onukib08790c2016-06-23 14:05:46 -070047import android.os.Handler;
48import android.os.Looper;
Makoto Onuki7001a612016-05-27 13:24:28 -070049import android.os.Parcel;
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -070050import android.os.ParcelFileDescriptor;
Makoto Onukib5a012f2016-06-21 11:13:53 -070051import android.os.PersistableBundle;
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -070052import android.os.UserHandle;
53import android.test.MoreAsserts;
54import android.util.Log;
55
56import junit.framework.Assert;
57
58import org.hamcrest.BaseMatcher;
59import org.hamcrest.Description;
60import org.hamcrest.Matcher;
Makoto Onuki9c850012016-07-26 15:50:50 -070061import org.json.JSONException;
62import org.json.JSONObject;
Makoto Onukib08790c2016-06-23 14:05:46 -070063import org.mockito.ArgumentCaptor;
Makoto Onukid0010c52017-03-30 14:17:35 -070064import org.mockito.ArgumentMatchers;
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 Onuki1e091c82018-02-01 11:58:03 -0800214 runCommand(instrumentation, "cmd shortcut clear-default-launcher --user "
215 + instrumentation.getContext().getUserId(),
216 result -> result.contains("Success"));
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700217 }
218
219 public static void setDefaultLauncher(Instrumentation instrumentation, Context packageContext) {
220 setDefaultLauncher(instrumentation, packageContext.getPackageName()
221 + "/android.content.pm.cts.shortcutmanager.packages.Launcher");
222 }
223
224 public static void overrideConfig(Instrumentation instrumentation, String config) {
225 runShortcutCommandForSuccess(instrumentation, "override-config " + config);
226 }
227
228 public static void resetConfig(Instrumentation instrumentation) {
229 runShortcutCommandForSuccess(instrumentation, "reset-config");
230 }
231
232 public static void resetThrottling(Instrumentation instrumentation) {
233 runShortcutCommandForSuccess(instrumentation, "reset-throttling");
234 }
235
236 public static void resetAllThrottling(Instrumentation instrumentation) {
237 runShortcutCommandForSuccess(instrumentation, "reset-all-throttling");
238 }
239
240 public static void clearShortcuts(Instrumentation instrumentation, int userId,
241 String packageName) {
242 runShortcutCommandForSuccess(instrumentation, "clear-shortcuts "
243 + " --user " + userId + " " + packageName);
244 }
245
Makoto Onuki9c850012016-07-26 15:50:50 -0700246 public static void anyContains(List<String> result, String expected) {
247 for (String l : result) {
248 if (l.contains(expected)) {
249 return;
250 }
251 }
252 fail("Result didn't contain '" + expected + "': was\n" + result);
253 }
254
255 public static void enableComponent(Instrumentation instrumentation, ComponentName cn,
256 boolean enable) {
257
258 final String word = (enable ? "enable" : "disable");
259 runCommand(instrumentation,
260 "pm " + word + " " + cn.flattenToString()
261 , result ->concatResult(result).contains(word));
262 }
263
264 public static void appOps(Instrumentation instrumentation, String packageName,
265 String op, String mode) {
266 runCommand(instrumentation, "appops set " + packageName + " " + op + " " + mode);
267 }
268
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700269 public static void dumpsysShortcut(Instrumentation instrumentation) {
270 if (!ENABLE_DUMPSYS) {
271 return;
272 }
Makoto Onuki3bdbf982016-06-23 16:56:35 -0700273 Log.e(TAG, "Dumpsys shortcut");
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700274 for (String s : runCommand(instrumentation, "dumpsys shortcut")) {
275 Log.e(TAG, s);
276 }
277 }
278
Makoto Onuki9c850012016-07-26 15:50:50 -0700279 public static JSONObject getCheckinDump(Instrumentation instrumentation) throws JSONException {
280 return new JSONObject(concatResult(runCommand(instrumentation, "dumpsys shortcut -c")));
281 }
282
283 public static boolean isLowRamDevice(Instrumentation instrumentation) throws JSONException {
284 return getCheckinDump(instrumentation).getBoolean("lowRam");
285 }
286
287 public static int getIconSize(Instrumentation instrumentation) throws JSONException {
288 return getCheckinDump(instrumentation).getInt("iconSize");
289 }
290
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700291 public static Bundle makeBundle(Object... keysAndValues) {
292 assertTrue((keysAndValues.length % 2) == 0);
293
294 if (keysAndValues.length == 0) {
295 return null;
296 }
297 final Bundle ret = new Bundle();
298
299 for (int i = keysAndValues.length - 2; i >= 0; i -= 2) {
300 final String key = keysAndValues[i].toString();
301 final Object value = keysAndValues[i + 1];
302
303 if (value == null) {
304 ret.putString(key, null);
305 } else if (value instanceof Integer) {
306 ret.putInt(key, (Integer) value);
307 } else if (value instanceof String) {
308 ret.putString(key, (String) value);
309 } else if (value instanceof Bundle) {
310 ret.putBundle(key, (Bundle) value);
311 } else {
312 fail("Type not supported yet: " + value.getClass().getName());
313 }
314 }
315 return ret;
316 }
317
Makoto Onukib5a012f2016-06-21 11:13:53 -0700318 public static PersistableBundle makePersistableBundle(Object... keysAndValues) {
319 assertTrue((keysAndValues.length % 2) == 0);
320
321 if (keysAndValues.length == 0) {
322 return null;
323 }
324 final PersistableBundle ret = new PersistableBundle();
325
326 for (int i = keysAndValues.length - 2; i >= 0; i -= 2) {
327 final String key = keysAndValues[i].toString();
328 final Object value = keysAndValues[i + 1];
329
330 if (value == null) {
331 ret.putString(key, null);
332 } else if (value instanceof Integer) {
333 ret.putInt(key, (Integer) value);
334 } else if (value instanceof String) {
335 ret.putString(key, (String) value);
336 } else if (value instanceof PersistableBundle) {
337 ret.putPersistableBundle(key, (PersistableBundle) value);
338 } else {
339 fail("Type not supported yet: " + value.getClass().getName());
340 }
341 }
342 return ret;
343 }
344
Makoto Onuki50a320e2017-05-31 14:38:42 -0700345 public static <T> T[] array(T... array) {
346 return array;
347 }
348
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700349 public static <T> List<T> list(T... array) {
350 return Arrays.asList(array);
351 }
352
353 public static <T> Set<T> hashSet(Set<T> in) {
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700354 return new LinkedHashSet<>(in);
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700355 }
356
357 public static <T> Set<T> set(T... values) {
358 return set(v -> v, values);
359 }
360
361 public static <T, V> Set<T> set(Function<V, T> converter, V... values) {
362 return set(converter, Arrays.asList(values));
363 }
364
365 public static <T, V> Set<T> set(Function<V, T> converter, List<V> values) {
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700366 final LinkedHashSet<T> ret = new LinkedHashSet<>();
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700367 for (V v : values) {
368 ret.add(converter.apply(v));
369 }
370 return ret;
371 }
372
Felipe Leme90205ef2019-03-05 09:59:52 -0800373 public static LocusId locusId(String id) {
374 return new LocusId(id);
375 }
376
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700377 public static void resetAll(Collection<?> mocks) {
378 for (Object o : mocks) {
379 reset(o);
380 }
381 }
Makoto Onuki22fcc682016-05-17 14:52:19 -0700382
Makoto Onukidf6da042016-06-16 09:51:40 -0700383 public static <T extends Collection<?>> T assertEmpty(T collection) {
384 if (collection == null) {
385 return collection; // okay.
386 }
387 assertEquals(0, collection.size());
388 return collection;
Makoto Onuki22fcc682016-05-17 14:52:19 -0700389 }
390
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700391 public static List<ShortcutInfo> filter(List<ShortcutInfo> list, Predicate<ShortcutInfo> p) {
392 final ArrayList<ShortcutInfo> ret = new ArrayList<>(list);
393 ret.removeIf(si -> !p.test(si));
394 return ret;
395 }
396
Makoto Onuki7001a612016-05-27 13:24:28 -0700397 public static List<ShortcutInfo> filterByActivity(List<ShortcutInfo> list,
398 ComponentName activity) {
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700399 return filter(list, si ->
400 (si.getActivity().equals(activity)
Makoto Onukib5a012f2016-06-21 11:13:53 -0700401 && (si.isDeclaredInManifest() || si.isDynamic())));
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700402 }
403
404 public static List<ShortcutInfo> changedSince(List<ShortcutInfo> list, long time) {
405 return filter(list, si -> si.getLastChangedTimestamp() >= time);
Makoto Onuki7001a612016-05-27 13:24:28 -0700406 }
407
Makoto Onuki0b9d1db2016-07-18 14:16:41 -0700408 @FunctionalInterface
409 public interface ExceptionRunnable {
410 void run() throws Exception;
411 }
412
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700413 public static void assertExpectException(Class<? extends Throwable> expectedExceptionType,
Makoto Onuki0b9d1db2016-07-18 14:16:41 -0700414 String expectedExceptionMessageRegex, ExceptionRunnable r) {
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700415 assertExpectException("", expectedExceptionType, expectedExceptionMessageRegex, r);
416 }
417
Makoto Onuki22fcc682016-05-17 14:52:19 -0700418 public static void assertCannotUpdateImmutable(Runnable r) {
419 assertExpectException(
Makoto Onuki0b9d1db2016-07-18 14:16:41 -0700420 IllegalArgumentException.class, "may not be manipulated via APIs", r::run);
Makoto Onuki22fcc682016-05-17 14:52:19 -0700421 }
422
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700423 public static void assertDynamicShortcutCountExceeded(Runnable r) {
424 assertExpectException(IllegalArgumentException.class,
Makoto Onuki0b9d1db2016-07-18 14:16:41 -0700425 "Max number of dynamic shortcuts exceeded", r::run);
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700426 }
427
428 public static void assertExpectException(String message,
429 Class<? extends Throwable> expectedExceptionType,
Makoto Onuki0b9d1db2016-07-18 14:16:41 -0700430 String expectedExceptionMessageRegex, ExceptionRunnable r) {
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700431 try {
432 r.run();
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700433 } catch (Throwable e) {
434 Assert.assertTrue(
435 "Expected exception type was " + expectedExceptionType.getName()
436 + " but caught " + e + " (message=" + message + ")",
437 expectedExceptionType.isAssignableFrom(e.getClass()));
438 if (expectedExceptionMessageRegex != null) {
439 MoreAsserts.assertContainsRegex(expectedExceptionMessageRegex, e.getMessage());
440 }
Makoto Onukia1d38b32016-06-10 15:32:26 -0700441 return; // Pass
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700442 }
Makoto Onukia1d38b32016-06-10 15:32:26 -0700443 Assert.fail("Expected exception type " + expectedExceptionType.getName()
444 + " was not thrown");
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700445 }
446
447 public static List<ShortcutInfo> assertShortcutIds(List<ShortcutInfo> actualShortcuts,
448 String... expectedIds) {
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700449 final SortedSet<String> expected = new TreeSet<>(list(expectedIds));
450 final SortedSet<String> actual = new TreeSet<>();
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700451 for (ShortcutInfo s : actualShortcuts) {
452 actual.add(s.getId());
453 }
454
455 // Compare the sets.
456 assertEquals(expected, actual);
457 return actualShortcuts;
458 }
459
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700460 public static List<ShortcutInfo> assertShortcutIdsOrdered(List<ShortcutInfo> actualShortcuts,
461 String... expectedIds) {
462 final ArrayList<String> expected = new ArrayList<>(list(expectedIds));
463 final ArrayList<String> actual = new ArrayList<>();
464 for (ShortcutInfo s : actualShortcuts) {
465 actual.add(s.getId());
466 }
467 assertEquals(expected, actual);
468 return actualShortcuts;
469 }
470
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700471 public static List<ShortcutInfo> assertAllHaveIntents(
472 List<ShortcutInfo> actualShortcuts) {
473 for (ShortcutInfo s : actualShortcuts) {
474 assertNotNull("ID " + s.getId(), s.getIntent());
475 }
476 return actualShortcuts;
477 }
478
479 public static List<ShortcutInfo> assertAllNotHaveIntents(
480 List<ShortcutInfo> actualShortcuts) {
481 for (ShortcutInfo s : actualShortcuts) {
482 assertNull("ID " + s.getId(), s.getIntent());
483 }
484 return actualShortcuts;
485 }
486
487 public static List<ShortcutInfo> assertAllHaveTitle(
488 List<ShortcutInfo> actualShortcuts) {
489 for (ShortcutInfo s : actualShortcuts) {
Makoto Onukieddbfec2016-05-31 17:04:34 -0700490 assertNotNull("ID " + s.getId(), s.getShortLabel());
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700491 }
492 return actualShortcuts;
493 }
494
495 public static List<ShortcutInfo> assertAllNotHaveTitle(
496 List<ShortcutInfo> actualShortcuts) {
497 for (ShortcutInfo s : actualShortcuts) {
Makoto Onukieddbfec2016-05-31 17:04:34 -0700498 assertNull("ID " + s.getId(), s.getShortLabel());
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700499 }
500 return actualShortcuts;
501 }
502
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700503 public static List<ShortcutInfo> assertAllKeyFieldsOnly(
504 List<ShortcutInfo> actualShortcuts) {
505 for (ShortcutInfo s : actualShortcuts) {
506 assertTrue("ID " + s.getId(), s.hasKeyFieldsOnly());
507 }
508 return actualShortcuts;
509 }
510
511 public static List<ShortcutInfo> assertAllNotKeyFieldsOnly(
512 List<ShortcutInfo> actualShortcuts) {
513 for (ShortcutInfo s : actualShortcuts) {
514 assertFalse("ID " + s.getId(), s.hasKeyFieldsOnly());
515 }
516 return actualShortcuts;
517 }
518
519 public static List<ShortcutInfo> assertAllDynamic(List<ShortcutInfo> actualShortcuts) {
520 for (ShortcutInfo s : actualShortcuts) {
521 assertTrue("ID " + s.getId(), s.isDynamic());
522 }
523 return actualShortcuts;
524 }
525
526 public static List<ShortcutInfo> assertAllPinned(List<ShortcutInfo> actualShortcuts) {
527 for (ShortcutInfo s : actualShortcuts) {
528 assertTrue("ID " + s.getId(), s.isPinned());
529 }
530 return actualShortcuts;
531 }
532
533 public static List<ShortcutInfo> assertAllDynamicOrPinned(
534 List<ShortcutInfo> actualShortcuts) {
535 for (ShortcutInfo s : actualShortcuts) {
536 assertTrue("ID " + s.getId(), s.isDynamic() || s.isPinned());
537 }
538 return actualShortcuts;
539 }
540
Makoto Onuki22fcc682016-05-17 14:52:19 -0700541 public static List<ShortcutInfo> assertAllManifest(
542 List<ShortcutInfo> actualShortcuts) {
543 for (ShortcutInfo s : actualShortcuts) {
Makoto Onukib5a012f2016-06-21 11:13:53 -0700544 assertTrue("ID " + s.getId(), s.isDeclaredInManifest());
Makoto Onuki22fcc682016-05-17 14:52:19 -0700545 }
546 return actualShortcuts;
547 }
548
549 public static List<ShortcutInfo> assertAllNotManifest(
550 List<ShortcutInfo> actualShortcuts) {
551 for (ShortcutInfo s : actualShortcuts) {
Makoto Onukib5a012f2016-06-21 11:13:53 -0700552 assertFalse("ID " + s.getId(), s.isDeclaredInManifest());
Makoto Onuki22fcc682016-05-17 14:52:19 -0700553 }
554 return actualShortcuts;
555 }
556
557 public static List<ShortcutInfo> assertAllDisabled(
558 List<ShortcutInfo> actualShortcuts) {
559 for (ShortcutInfo s : actualShortcuts) {
560 assertTrue("ID " + s.getId(), !s.isEnabled());
561 }
562 return actualShortcuts;
563 }
564
565 public static List<ShortcutInfo> assertAllEnabled(
566 List<ShortcutInfo> actualShortcuts) {
567 for (ShortcutInfo s : actualShortcuts) {
568 assertTrue("ID " + s.getId(), s.isEnabled());
569 }
570 return actualShortcuts;
571 }
572
573 public static List<ShortcutInfo> assertAllImmutable(
574 List<ShortcutInfo> actualShortcuts) {
575 for (ShortcutInfo s : actualShortcuts) {
576 assertTrue("ID " + s.getId(), s.isImmutable());
577 }
578 return actualShortcuts;
579 }
580
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700581 public static void assertDynamicOnly(ShortcutInfo si) {
582 assertTrue(si.isDynamic());
583 assertFalse(si.isPinned());
584 }
585
586 public static void assertPinnedOnly(ShortcutInfo si) {
587 assertFalse(si.isDynamic());
Makoto Onukib5a012f2016-06-21 11:13:53 -0700588 assertFalse(si.isDeclaredInManifest());
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700589 assertTrue(si.isPinned());
590 }
591
592 public static void assertDynamicAndPinned(ShortcutInfo si) {
593 assertTrue(si.isDynamic());
594 assertTrue(si.isPinned());
595 }
596
597 public static void assertBitmapSize(int expectedWidth, int expectedHeight, Bitmap bitmap) {
598 assertEquals("width", expectedWidth, bitmap.getWidth());
599 assertEquals("height", expectedHeight, bitmap.getHeight());
600 }
601
602 public static <T> void assertAllUnique(Collection<T> list) {
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700603 final Set<Object> set = new LinkedHashSet<>();
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700604 for (T item : list) {
605 if (set.contains(item)) {
606 fail("Duplicate item found: " + item + " (in the list: " + list + ")");
607 }
608 set.add(item);
609 }
610 }
611
Makoto Onuki39686e82016-04-13 18:03:00 -0700612 public static ShortcutInfo findShortcut(List<ShortcutInfo> list, String id) {
613 for (ShortcutInfo si : list) {
614 if (si.getId().equals(id)) {
615 return si;
616 }
617 }
618 fail("Shortcut " + id + " not found in the list");
619 return null;
620 }
621
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700622 public static Bitmap pfdToBitmap(ParcelFileDescriptor pfd) {
623 assertNotNull(pfd);
624 try {
625 try {
626 return BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor());
627 } finally {
628 pfd.close();
629 }
630 } catch (IOException e) {
631 throw new RuntimeException(e);
632 }
633 }
634
635 public static void assertBundleEmpty(BaseBundle b) {
636 assertTrue(b == null || b.size() == 0);
637 }
638
639 public static void assertCallbackNotReceived(LauncherApps.Callback mock) {
640 verify(mock, times(0)).onShortcutsChanged(anyString(), anyList(),
641 any(UserHandle.class));
642 }
643
644 public static void assertCallbackReceived(LauncherApps.Callback mock,
645 UserHandle user, String packageName, String... ids) {
646 verify(mock).onShortcutsChanged(eq(packageName), checkShortcutIds(ids),
647 eq(user));
648 }
649
650 public static boolean checkAssertSuccess(Runnable r) {
651 try {
652 r.run();
653 return true;
654 } catch (AssertionError e) {
655 return false;
656 }
657 }
658
659 public static <T> T checkArgument(Predicate<T> checker, String description,
660 List<T> matchedCaptor) {
661 final Matcher<T> m = new BaseMatcher<T>() {
662 @Override
663 public boolean matches(Object item) {
664 if (item == null) {
665 return false;
666 }
667 final T value = (T) item;
668 if (!checker.test(value)) {
669 return false;
670 }
671
672 if (matchedCaptor != null) {
673 matchedCaptor.add(value);
674 }
675 return true;
676 }
677
678 @Override
679 public void describeTo(Description d) {
680 d.appendText(description);
681 }
682 };
Paul Duffin192bb0b2017-03-09 18:49:41 +0000683 return MockitoHamcrest.argThat(m);
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700684 }
685
686 public static List<ShortcutInfo> checkShortcutIds(String... ids) {
687 return checkArgument((List<ShortcutInfo> list) -> {
688 final Set<String> actualSet = set(si -> si.getId(), list);
689 return actualSet.equals(set(ids));
690
691 }, "Shortcut IDs=[" + Arrays.toString(ids) + "]", null);
692 }
693
Makoto Onuki7001a612016-05-27 13:24:28 -0700694 public static ShortcutInfo parceled(ShortcutInfo si) {
695 Parcel p = Parcel.obtain();
696 p.writeParcelable(si, 0);
697 p.setDataPosition(0);
698 ShortcutInfo si2 = p.readParcelable(ShortcutManagerTestUtils.class.getClassLoader());
699 p.recycle();
700 return si2;
701 }
702
703 public static List<ShortcutInfo> cloneShortcutList(List<ShortcutInfo> list) {
704 if (list == null) {
705 return null;
706 }
707 final List<ShortcutInfo> ret = new ArrayList<>(list.size());
708 for (ShortcutInfo si : list) {
709 ret.add(parceled(si));
710 }
711
712 return ret;
713 }
714
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700715 private static final Comparator<ShortcutInfo> sRankComparator =
716 (ShortcutInfo a, ShortcutInfo b) -> Integer.compare(a.getRank(), b.getRank());
717
718 public static List<ShortcutInfo> sortedByRank(List<ShortcutInfo> shortcuts) {
719 final ArrayList<ShortcutInfo> ret = new ArrayList<>(shortcuts);
720 Collections.sort(ret, sRankComparator);
721 return ret;
722 }
723
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -0700724 public static void waitUntil(String message, BooleanSupplier condition) {
725 waitUntil(message, condition, STANDARD_TIMEOUT_SEC);
726 }
727
728 public static void waitUntil(String message, BooleanSupplier condition, int timeoutSeconds) {
729 final long timeout = System.currentTimeMillis() + (timeoutSeconds * 1000L);
730 while (System.currentTimeMillis() < timeout) {
731 if (condition.getAsBoolean()) {
732 return;
733 }
734 try {
735 Thread.sleep(100);
736 } catch (InterruptedException e) {
737 throw new RuntimeException(e);
738 }
739 }
740 fail("Timed out for: " + message);
741 }
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700742
Makoto Onukid0010c52017-03-30 14:17:35 -0700743 public static final <T> T anyOrNull(Class<T> clazz) {
744 return ArgumentMatchers.argThat(value -> true);
745 }
746
747 public static final String anyStringOrNull() {
748 return ArgumentMatchers.argThat(value -> true);
749 }
750
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700751 public static ShortcutListAsserter assertWith(List<ShortcutInfo> list) {
752 return new ShortcutListAsserter(list);
753 }
754
Makoto Onuki2d895c32016-12-02 15:48:40 -0800755 public static ShortcutListAsserter assertWith(ShortcutInfo... list) {
756 return assertWith(list(list));
757 }
758
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700759 /**
760 * New style assertion that allows chained calls.
761 */
762 public static class ShortcutListAsserter {
Makoto Onukidf6da042016-06-16 09:51:40 -0700763 private final ShortcutListAsserter mOriginal;
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700764 private final List<ShortcutInfo> mList;
765
766 ShortcutListAsserter(List<ShortcutInfo> list) {
Makoto Onukidf6da042016-06-16 09:51:40 -0700767 this(null, list);
768 }
769
770 private ShortcutListAsserter(ShortcutListAsserter original, List<ShortcutInfo> list) {
Makoto Onukib08790c2016-06-23 14:05:46 -0700771 mOriginal = (original == null) ? this : original;
772 mList = (list == null) ? new ArrayList<>(0) : new ArrayList<>(list);
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700773 }
774
Makoto Onukidf6da042016-06-16 09:51:40 -0700775 public ShortcutListAsserter revertToOriginalList() {
776 return mOriginal;
777 }
778
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700779 public ShortcutListAsserter selectDynamic() {
Makoto Onukidf6da042016-06-16 09:51:40 -0700780 return new ShortcutListAsserter(this,
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700781 filter(mList, ShortcutInfo::isDynamic));
782 }
783
784 public ShortcutListAsserter selectManifest() {
Makoto Onukidf6da042016-06-16 09:51:40 -0700785 return new ShortcutListAsserter(this,
Makoto Onukib5a012f2016-06-21 11:13:53 -0700786 filter(mList, ShortcutInfo::isDeclaredInManifest));
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700787 }
788
789 public ShortcutListAsserter selectPinned() {
Makoto Onukidf6da042016-06-16 09:51:40 -0700790 return new ShortcutListAsserter(this,
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700791 filter(mList, ShortcutInfo::isPinned));
792 }
793
Makoto Onuki106ff7a2016-12-01 10:17:57 -0800794 public ShortcutListAsserter selectFloating() {
795 return new ShortcutListAsserter(this,
796 filter(mList, (si -> si.isPinned()
797 && !(si.isDynamic() || si.isDeclaredInManifest()))));
798 }
799
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700800 public ShortcutListAsserter selectByActivity(ComponentName activity) {
Makoto Onukidf6da042016-06-16 09:51:40 -0700801 return new ShortcutListAsserter(this,
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700802 ShortcutManagerTestUtils.filterByActivity(mList, activity));
803 }
804
805 public ShortcutListAsserter selectByChangedSince(long time) {
Makoto Onukidf6da042016-06-16 09:51:40 -0700806 return new ShortcutListAsserter(this,
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700807 ShortcutManagerTestUtils.changedSince(mList, time));
808 }
809
Makoto Onukidf6da042016-06-16 09:51:40 -0700810 public ShortcutListAsserter selectByIds(String... ids) {
811 final Set<String> idSet = set(ids);
812 final ArrayList<ShortcutInfo> selected = new ArrayList<>();
813 for (ShortcutInfo si : mList) {
814 if (idSet.contains(si.getId())) {
815 selected.add(si);
816 idSet.remove(si.getId());
817 }
818 }
819 if (idSet.size() > 0) {
820 fail("Shortcuts not found for IDs=" + idSet);
821 }
822
823 return new ShortcutListAsserter(this, selected);
824 }
825
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700826 public ShortcutListAsserter toSortByRank() {
Makoto Onukidf6da042016-06-16 09:51:40 -0700827 return new ShortcutListAsserter(this,
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700828 ShortcutManagerTestUtils.sortedByRank(mList));
829 }
830
Makoto Onukidf6da042016-06-16 09:51:40 -0700831 public ShortcutListAsserter call(Consumer<List<ShortcutInfo>> c) {
832 c.accept(mList);
833 return this;
834 }
835
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700836 public ShortcutListAsserter haveIds(String... expectedIds) {
837 assertShortcutIds(mList, expectedIds);
838 return this;
839 }
840
841 public ShortcutListAsserter haveIdsOrdered(String... expectedIds) {
842 assertShortcutIdsOrdered(mList, expectedIds);
843 return this;
844 }
845
846 private ShortcutListAsserter haveSequentialRanks() {
847 for (int i = 0; i < mList.size(); i++) {
Makoto Onukidf6da042016-06-16 09:51:40 -0700848 final ShortcutInfo si = mList.get(i);
849 assertEquals("Rank not sequential: id=" + si.getId(), i, si.getRank());
Makoto Onuki9e1f5592016-06-08 12:30:23 -0700850 }
851 return this;
852 }
853
854 public ShortcutListAsserter haveRanksInOrder(String... expectedIds) {
855 toSortByRank()
856 .haveSequentialRanks()
857 .haveIdsOrdered(expectedIds);
858 return this;
859 }
860
861 public ShortcutListAsserter isEmpty() {
862 assertEquals(0, mList.size());
863 return this;
864 }
Makoto Onukidf6da042016-06-16 09:51:40 -0700865
866 public ShortcutListAsserter areAllDynamic() {
867 forAllShortcuts(s -> assertTrue("id=" + s.getId(), s.isDynamic()));
868 return this;
869 }
870
871 public ShortcutListAsserter areAllNotDynamic() {
872 forAllShortcuts(s -> assertFalse("id=" + s.getId(), s.isDynamic()));
873 return this;
874 }
875
876 public ShortcutListAsserter areAllPinned() {
877 forAllShortcuts(s -> assertTrue("id=" + s.getId(), s.isPinned()));
878 return this;
879 }
880
881 public ShortcutListAsserter areAllNotPinned() {
882 forAllShortcuts(s -> assertFalse("id=" + s.getId(), s.isPinned()));
883 return this;
884 }
885
886 public ShortcutListAsserter areAllManifest() {
Makoto Onukib5a012f2016-06-21 11:13:53 -0700887 forAllShortcuts(s -> assertTrue("id=" + s.getId(), s.isDeclaredInManifest()));
Makoto Onukidf6da042016-06-16 09:51:40 -0700888 return this;
889 }
890
891 public ShortcutListAsserter areAllNotManifest() {
Makoto Onukib5a012f2016-06-21 11:13:53 -0700892 forAllShortcuts(s -> assertFalse("id=" + s.getId(), s.isDeclaredInManifest()));
Makoto Onukidf6da042016-06-16 09:51:40 -0700893 return this;
894 }
895
896 public ShortcutListAsserter areAllImmutable() {
897 forAllShortcuts(s -> assertTrue("id=" + s.getId(), s.isImmutable()));
898 return this;
899 }
900
901 public ShortcutListAsserter areAllMutable() {
902 forAllShortcuts(s -> assertFalse("id=" + s.getId(), s.isImmutable()));
903 return this;
904 }
905
906 public ShortcutListAsserter areAllEnabled() {
907 forAllShortcuts(s -> assertTrue("id=" + s.getId(), s.isEnabled()));
Makoto Onukia4f89b12017-10-05 10:37:55 -0700908 areAllWithDisabledReason(ShortcutInfo.DISABLED_REASON_NOT_DISABLED);
Makoto Onukidf6da042016-06-16 09:51:40 -0700909 return this;
910 }
911
912 public ShortcutListAsserter areAllDisabled() {
913 forAllShortcuts(s -> assertFalse("id=" + s.getId(), s.isEnabled()));
Makoto Onukia4f89b12017-10-05 10:37:55 -0700914 forAllShortcuts(s -> assertNotEquals("id=" + s.getId(),
915 ShortcutInfo.DISABLED_REASON_NOT_DISABLED, s.getDisabledReason()));
Makoto Onukidf6da042016-06-16 09:51:40 -0700916 return this;
917 }
918
Makoto Onuki2d895c32016-12-02 15:48:40 -0800919 public ShortcutListAsserter areAllFloating() {
920 forAllShortcuts(s -> assertTrue("id=" + s.getId(),
921 s.isPinned() && !s.isDeclaredInManifest() && !s.isDynamic()));
922 return this;
923 }
924
925 public ShortcutListAsserter areAllNotFloating() {
926 forAllShortcuts(s -> assertTrue("id=" + s.getId(),
927 !(s.isPinned() && !s.isDeclaredInManifest() && !s.isDynamic())));
928 return this;
929 }
930
931 public ShortcutListAsserter areAllOrphan() {
932 forAllShortcuts(s -> assertTrue("id=" + s.getId(),
933 !s.isPinned() && !s.isDeclaredInManifest() && !s.isDynamic()));
934 return this;
935 }
936
937 public ShortcutListAsserter areAllNotOrphan() {
938 forAllShortcuts(s -> assertTrue("id=" + s.getId(),
939 s.isPinned() || s.isDeclaredInManifest() || s.isDynamic()));
940 return this;
941 }
942
Makoto Onukia4f89b12017-10-05 10:37:55 -0700943 public ShortcutListAsserter areAllVisibleToPublisher() {
944 forAllShortcuts(s -> assertTrue("id=" + s.getId(), s.isVisibleToPublisher()));
945 return this;
946 }
947
948 public ShortcutListAsserter areAllNotVisibleToPublisher() {
949 forAllShortcuts(s -> assertFalse("id=" + s.getId(), s.isVisibleToPublisher()));
950 return this;
951 }
952
Makoto Onuki4d6b87f2016-06-17 13:47:40 -0700953 public ShortcutListAsserter areAllWithKeyFieldsOnly() {
954 forAllShortcuts(s -> assertTrue("id=" + s.getId(), s.hasKeyFieldsOnly()));
955 return this;
956 }
957
958 public ShortcutListAsserter areAllNotWithKeyFieldsOnly() {
959 forAllShortcuts(s -> assertFalse("id=" + s.getId(), s.hasKeyFieldsOnly()));
960 return this;
961 }
962
Makoto Onukib08790c2016-06-23 14:05:46 -0700963 public ShortcutListAsserter areAllWithActivity(ComponentName activity) {
Makoto Onuki255461f2017-01-10 11:47:25 -0800964 forAllShortcuts(s -> assertEquals("id=" + s.getId(), activity, s.getActivity()));
Makoto Onukib08790c2016-06-23 14:05:46 -0700965 return this;
966 }
967
Makoto Onuki106ff7a2016-12-01 10:17:57 -0800968 public ShortcutListAsserter areAllWithNoActivity() {
969 forAllShortcuts(s -> assertNull("id=" + s.getId(), s.getActivity()));
970 return this;
971 }
972
Makoto Onukia01f4f02016-12-15 15:58:41 -0800973 public ShortcutListAsserter areAllWithIntent() {
974 forAllShortcuts(s -> assertNotNull("id=" + s.getId(), s.getIntent()));
975 return this;
976 }
977
978 public ShortcutListAsserter areAllWithNoIntent() {
979 forAllShortcuts(s -> assertNull("id=" + s.getId(), s.getIntent()));
980 return this;
981 }
982
Makoto Onukia4f89b12017-10-05 10:37:55 -0700983 public ShortcutListAsserter areAllWithDisabledReason(int disabledReason) {
984 forAllShortcuts(s -> assertEquals("id=" + s.getId(),
985 disabledReason, s.getDisabledReason()));
Makoto Onukib1588c02017-10-12 15:11:45 -0700986 if (disabledReason >= ShortcutInfo.DISABLED_REASON_VERSION_LOWER) {
Makoto Onukia4f89b12017-10-05 10:37:55 -0700987 areAllNotVisibleToPublisher();
Makoto Onukib1588c02017-10-12 15:11:45 -0700988 } else {
989 areAllVisibleToPublisher();
Makoto Onukia4f89b12017-10-05 10:37:55 -0700990 }
991 return this;
992 }
993
Makoto Onukidf6da042016-06-16 09:51:40 -0700994 public ShortcutListAsserter forAllShortcuts(Consumer<ShortcutInfo> sa) {
Makoto Onuki4d6b87f2016-06-17 13:47:40 -0700995 boolean found = false;
Makoto Onukidf6da042016-06-16 09:51:40 -0700996 for (int i = 0; i < mList.size(); i++) {
997 final ShortcutInfo si = mList.get(i);
Makoto Onuki4d6b87f2016-06-17 13:47:40 -0700998 found = true;
Makoto Onukidf6da042016-06-16 09:51:40 -0700999 sa.accept(si);
1000 }
Makoto Onuki4d6b87f2016-06-17 13:47:40 -07001001 assertTrue("No shortcuts found.", found);
Makoto Onukidf6da042016-06-16 09:51:40 -07001002 return this;
1003 }
1004
1005 public ShortcutListAsserter forShortcut(Predicate<ShortcutInfo> p,
1006 Consumer<ShortcutInfo> sa) {
1007 boolean found = false;
1008 for (int i = 0; i < mList.size(); i++) {
1009 final ShortcutInfo si = mList.get(i);
1010 if (p.test(si)) {
1011 found = true;
1012 try {
1013 sa.accept(si);
1014 } catch (Throwable e) {
1015 throw new AssertionError("Assertion failed for shortcut " + si.getId(), e);
1016 }
1017 }
1018 }
1019 assertTrue("Shortcut with the given condition not found.", found);
1020 return this;
1021 }
1022
1023 public ShortcutListAsserter forShortcutWithId(String id, Consumer<ShortcutInfo> sa) {
1024 forShortcut(si -> si.getId().equals(id), sa);
1025
1026 return this;
1027 }
Makoto Onuki9e1f5592016-06-08 12:30:23 -07001028 }
Makoto Onukib5a012f2016-06-21 11:13:53 -07001029
1030 public static void assertBundlesEqual(BaseBundle b1, BaseBundle b2) {
1031 if (b1 == null && b2 == null) {
1032 return; // pass
1033 }
Makoto Onuki440a1ea2016-07-20 14:21:18 -07001034 assertNotNull("b1 is null but b2 is not", b1);
1035 assertNotNull("b2 is null but b1 is not", b2);
Makoto Onukib5a012f2016-06-21 11:13:53 -07001036
1037 // HashSet makes the error message readable.
1038 assertEquals(set(b1.keySet()), set(b2.keySet()));
1039
1040 for (String key : b1.keySet()) {
1041 final Object v1 = b1.get(key);
1042 final Object v2 = b2.get(key);
1043 if (v1 == null) {
1044 if (v2 == null) {
1045 return;
1046 }
1047 }
1048 if (v1.equals(v2)) {
1049 return;
1050 }
1051
1052 assertTrue("Only either value is null: key=" + key
1053 + " b1=" + b1 + " b2=" + b2, v1 != null && v2 != null);
1054 assertEquals("Class mismatch: key=" + key, v1.getClass(), v2.getClass());
1055
1056 if (v1 instanceof BaseBundle) {
1057 assertBundlesEqual((BaseBundle) v1, (BaseBundle) v2);
1058
1059 } else if (v1 instanceof boolean[]) {
1060 assertTrue(Arrays.equals((boolean[]) v1, (boolean[]) v2));
1061
1062 } else if (v1 instanceof int[]) {
1063 MoreAsserts.assertEquals((int[]) v1, (int[]) v2);
1064
1065 } else if (v1 instanceof double[]) {
1066 MoreAsserts.assertEquals((double[]) v1, (double[]) v2);
1067
1068 } else if (v1 instanceof String[]) {
1069 MoreAsserts.assertEquals((String[]) v1, (String[]) v2);
1070
1071 } else if (v1 instanceof Double) {
1072 if (((Double) v1).isNaN()) {
1073 assertTrue(((Double) v2).isNaN());
1074 } else {
1075 assertEquals(v1, v2);
1076 }
1077
1078 } else {
1079 assertEquals(v1, v2);
1080 }
1081 }
1082 }
Makoto Onukib08790c2016-06-23 14:05:46 -07001083
1084 public static void waitOnMainThread() throws InterruptedException {
1085 final CountDownLatch latch = new CountDownLatch(1);
1086
1087 new Handler(Looper.getMainLooper()).post(() -> latch.countDown());
1088
1089 latch.await();
1090 }
1091
1092 public static class LauncherCallbackAsserter {
1093 private final LauncherApps.Callback mCallback = mock(LauncherApps.Callback.class);
1094
1095 private Callback getMockCallback() {
1096 return mCallback;
1097 }
1098
1099 public LauncherCallbackAsserter assertNoCallbackCalled() {
1100 verify(mCallback, times(0)).onShortcutsChanged(
1101 anyString(),
1102 any(List.class),
1103 any(UserHandle.class));
1104 return this;
1105 }
1106
1107 public LauncherCallbackAsserter assertNoCallbackCalledForPackage(
1108 String publisherPackageName) {
1109 verify(mCallback, times(0)).onShortcutsChanged(
1110 eq(publisherPackageName),
1111 any(List.class),
1112 any(UserHandle.class));
1113 return this;
1114 }
1115
1116 public LauncherCallbackAsserter assertNoCallbackCalledForPackageAndUser(
1117 String publisherPackageName, UserHandle publisherUserHandle) {
1118 verify(mCallback, times(0)).onShortcutsChanged(
1119 eq(publisherPackageName),
1120 any(List.class),
1121 eq(publisherUserHandle));
1122 return this;
1123 }
1124
1125 public ShortcutListAsserter assertCallbackCalledForPackageAndUser(
1126 String publisherPackageName, UserHandle publisherUserHandle) {
1127 final ArgumentCaptor<List> shortcuts = ArgumentCaptor.forClass(List.class);
Makoto Onuki82fb2eb2017-03-31 16:58:26 -07001128 verify(mCallback, atLeastOnce()).onShortcutsChanged(
Makoto Onukib08790c2016-06-23 14:05:46 -07001129 eq(publisherPackageName),
1130 shortcuts.capture(),
1131 eq(publisherUserHandle));
1132 return new ShortcutListAsserter(shortcuts.getValue());
1133 }
1134 }
1135
1136 public static LauncherCallbackAsserter assertForLauncherCallback(
1137 LauncherApps launcherApps, Runnable body) throws InterruptedException {
1138 final LauncherCallbackAsserter asserter = new LauncherCallbackAsserter();
1139 launcherApps.registerCallback(asserter.getMockCallback(),
1140 new Handler(Looper.getMainLooper()));
1141
1142 body.run();
1143
1144 waitOnMainThread();
1145
Makoto Onuki440a1ea2016-07-20 14:21:18 -07001146 // TODO unregister doesn't work well during unit tests. Figure out and fix it.
1147 // launcherApps.unregisterCallback(asserter.getMockCallback());
Makoto Onuki4e6cef42016-07-13 16:14:01 -07001148
Makoto Onukib08790c2016-06-23 14:05:46 -07001149 return asserter;
1150 }
Makoto Onuki9c850012016-07-26 15:50:50 -07001151
Makoto Onukia01f4f02016-12-15 15:58:41 -08001152 public static LauncherCallbackAsserter assertForLauncherCallbackNoThrow(
1153 LauncherApps launcherApps, Runnable body) {
1154 try {
1155 return assertForLauncherCallback(launcherApps, body);
1156 } catch (InterruptedException e) {
1157 fail("Caught InterruptedException");
1158 return null; // Never happens.
1159 }
1160 }
1161
Makoto Onuki9c850012016-07-26 15:50:50 -07001162 public static void retryUntil(BooleanSupplier checker, String message) {
Makoto Onukid67bf6a2016-08-19 09:10:08 -07001163 retryUntil(checker, message, 30);
1164 }
1165
1166 public static void retryUntil(BooleanSupplier checker, String message, long timeoutSeconds) {
1167 final long timeOut = System.currentTimeMillis() + timeoutSeconds * 1000;
Makoto Onuki9c850012016-07-26 15:50:50 -07001168 while (!checker.getAsBoolean()) {
Makoto Onukia210ccf2016-07-27 14:08:48 -07001169 if (System.currentTimeMillis() > timeOut) {
1170 break;
1171 }
Makoto Onuki9c850012016-07-26 15:50:50 -07001172 try {
1173 Thread.sleep(200);
1174 } catch (InterruptedException ignore) {
1175 }
1176 }
1177 assertTrue(message, checker.getAsBoolean());
1178 }
Makoto Onuki5ba0d3e2016-04-11 14:03:46 -07001179}