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