1 // Copyright 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.base.test.util; 6 7 import android.os.Handler; 8 import android.os.Looper; 9 10 import org.hamcrest.Matchers; 11 12 import org.chromium.base.ThreadUtils; 13 14 import java.lang.reflect.InvocationTargetException; 15 import java.util.concurrent.Callable; 16 import java.util.concurrent.atomic.AtomicBoolean; 17 import java.util.concurrent.atomic.AtomicReference; 18 19 /** 20 * Helper methods for creating and managing criteria. 21 * 22 * <p> 23 * If possible, use callbacks or testing delegates instead of criteria as they 24 * do not introduce any polling delays. Should only use criteria if no suitable 25 * other approach exists. 26 * 27 * <p> 28 * The Runnable variation of the CriteriaHelper methods allows a flexible way of verifying any 29 * number of conditions are met prior to proceeding. 30 * 31 * <pre> 32 * Example: 33 * <code> 34 * private void verifyMenuShown() { 35 * CriteriaHelper.pollUiThread(() -> { 36 * Criteria.checkThat("App menu was null", getActivity().getAppMenuHandler(), 37 * Matchers.notNullValue()); 38 * Criteria.checkThat("App menu was not shown", 39 * getActivity().getAppMenuHandler().isAppMenuShowing(), Matchers.is(true)); 40 * }); 41 * } 42 * </code> 43 * </pre> 44 * 45 * <p> 46 * To verify simple conditions, the Callback variation can be less verbose. 47 * 48 * <pre> 49 * Example: 50 * <code> 51 * private void assertMenuShown() { 52 * CriteriaHelper.pollUiThread(() -> getActivity().getAppMenuHandler().isAppMenuShowing(), 53 * "App menu was not shown"); 54 * } 55 * </code> 56 * </pre> 57 */ 58 public class CriteriaHelper { 59 /** The default maximum time to wait for a criteria to become valid. */ 60 public static final long DEFAULT_MAX_TIME_TO_POLL = 3000L; 61 /** The default polling interval to wait between checking for a satisfied criteria. */ 62 public static final long DEFAULT_POLLING_INTERVAL = 50; 63 64 private static final long DEFAULT_JUNIT_MAX_TIME_TO_POLL = 1000; 65 private static final long DEFAULT_JUNIT_POLLING_INTERVAL = 1; 66 67 /** 68 * Checks whether the given Runnable completes without exception at a given interval, until 69 * either the Runnable successfully completes, or the maxTimeoutMs number of ms has elapsed. 70 * 71 * <p> 72 * This evaluates the Criteria on the Instrumentation thread, which more often than not is not 73 * correct in an InstrumentationTest. Use 74 * {@link #pollUiThread(Runnable, long, long)} instead. 75 * 76 * @param criteria The Runnable that will be attempted. 77 * @param maxTimeoutMs The maximum number of ms that this check will be performed for 78 * before timeout. 79 * @param checkIntervalMs The number of ms between checks. 80 */ pollInstrumentationThread( Runnable criteria, long maxTimeoutMs, long checkIntervalMs)81 public static void pollInstrumentationThread( 82 Runnable criteria, long maxTimeoutMs, long checkIntervalMs) { 83 assert !ThreadUtils.runningOnUiThread(); 84 pollThreadInternal(criteria, maxTimeoutMs, checkIntervalMs, false); 85 } 86 pollThreadInternal( Runnable criteria, long maxTimeoutMs, long checkIntervalMs, boolean shouldNest)87 private static void pollThreadInternal( 88 Runnable criteria, long maxTimeoutMs, long checkIntervalMs, boolean shouldNest) { 89 Throwable throwable; 90 try { 91 criteria.run(); 92 return; 93 } catch (Throwable e) { 94 // Espresso catches, wraps, and re-throws the exception we want the CriteriaHelper 95 // to catch. 96 if (e instanceof CriteriaNotSatisfiedException 97 || e.getCause() instanceof CriteriaNotSatisfiedException) { 98 throwable = e; 99 } else { 100 throw e; 101 } 102 } 103 TimeoutTimer timer = new TimeoutTimer(maxTimeoutMs); 104 while (!timer.isTimedOut()) { 105 if (shouldNest) { 106 nestThread(checkIntervalMs); 107 } else { 108 sleepThread(checkIntervalMs); 109 } 110 try { 111 criteria.run(); 112 return; 113 } catch (Throwable e) { 114 if (e instanceof CriteriaNotSatisfiedException 115 || e.getCause() instanceof CriteriaNotSatisfiedException) { 116 throwable = e; 117 } else { 118 throw e; 119 } 120 } 121 } 122 throw new AssertionError(throwable); 123 } 124 sleepThread(long checkIntervalMs)125 private static void sleepThread(long checkIntervalMs) { 126 try { 127 Thread.sleep(checkIntervalMs); 128 } catch (InterruptedException e) { 129 // Catch the InterruptedException. If the exception occurs before maxTimeoutMs 130 // and the criteria is not satisfied, the while loop will run again. 131 } 132 } 133 nestThread(long checkIntervalMs)134 private static void nestThread(long checkIntervalMs) { 135 AtomicBoolean called = new AtomicBoolean(false); 136 137 // Ensure we pump the message handler in case no new tasks arrive. 138 new Handler(Looper.myLooper()).postDelayed(() -> { called.set(true); }, checkIntervalMs); 139 140 TimeoutTimer timer = new TimeoutTimer(checkIntervalMs); 141 // To allow a checkInterval of 0ms, ensure we at least run a single task, which allows a 142 // test to check conditions between each task run on the thread. 143 do { 144 try { 145 LooperUtils.runSingleNestedLooperTask(); 146 } catch (IllegalArgumentException | IllegalAccessException | SecurityException 147 | InvocationTargetException e) { 148 throw new RuntimeException(e); 149 } 150 } while (!timer.isTimedOut() && !called.get()); 151 } 152 153 /** 154 * Checks whether the given Runnable completes without exception at the default interval. 155 * 156 * <p> 157 * This evaluates the Runnable on the test thread, which more often than not is not correct 158 * in an InstrumentationTest. Use {@link #pollUiThread(Runnable)} instead. 159 * 160 * @param criteria The Runnable that will be attempted. 161 * 162 * @see #pollInstrumentationThread(Criteria, long, long) 163 */ pollInstrumentationThread(Runnable criteria)164 public static void pollInstrumentationThread(Runnable criteria) { 165 pollInstrumentationThread(criteria, DEFAULT_MAX_TIME_TO_POLL, DEFAULT_POLLING_INTERVAL); 166 } 167 168 /** 169 * Checks whether the given Callable<Boolean> is satisfied at a given interval, until either 170 * the criteria is satisfied, or the specified maxTimeoutMs number of ms has elapsed. 171 * 172 * <p> 173 * This evaluates the Callable<Boolean> on the test thread, which more often than not is not 174 * correct in an InstrumentationTest. Use {@link #pollUiThread(Callable)} instead. 175 * 176 * @param criteria The Callable<Boolean> that will be checked. 177 * @param failureReason The static failure reason 178 * @param maxTimeoutMs The maximum number of ms that this check will be performed for 179 * before timeout. 180 * @param checkIntervalMs The number of ms between checks. 181 */ pollInstrumentationThread(final Callable<Boolean> criteria, String failureReason, long maxTimeoutMs, long checkIntervalMs)182 public static void pollInstrumentationThread(final Callable<Boolean> criteria, 183 String failureReason, long maxTimeoutMs, long checkIntervalMs) { 184 pollInstrumentationThread( 185 toNotSatisfiedRunnable(criteria, failureReason), maxTimeoutMs, checkIntervalMs); 186 } 187 188 /** 189 * Checks whether the given Callable<Boolean> is satisfied at a given interval, until either 190 * the criteria is satisfied, or the specified maxTimeoutMs number of ms has elapsed. 191 * 192 * <p> 193 * This evaluates the Callable<Boolean> on the test thread, which more often than not is not 194 * correct in an InstrumentationTest. Use {@link #pollUiThread(Callable)} instead. 195 * 196 * @param criteria The Callable<Boolean> that will be checked. 197 * @param maxTimeoutMs The maximum number of ms that this check will be performed for 198 * before timeout. 199 * @param checkIntervalMs The number of ms between checks. 200 */ pollInstrumentationThread( final Callable<Boolean> criteria, long maxTimeoutMs, long checkIntervalMs)201 public static void pollInstrumentationThread( 202 final Callable<Boolean> criteria, long maxTimeoutMs, long checkIntervalMs) { 203 pollInstrumentationThread(criteria, null, maxTimeoutMs, checkIntervalMs); 204 } 205 206 /** 207 * Checks whether the given Callable<Boolean> is satisfied polling at a default interval. 208 * 209 * <p> 210 * This evaluates the Callable<Boolean> on the test thread, which more often than not is not 211 * correct in an InstrumentationTest. Use {@link #pollUiThread(Callable)} instead. 212 * 213 * @param criteria The Callable<Boolean> that will be checked. 214 * @param failureReason The static failure reason 215 */ pollInstrumentationThread(Callable<Boolean> criteria, String failureReason)216 public static void pollInstrumentationThread(Callable<Boolean> criteria, String failureReason) { 217 pollInstrumentationThread( 218 criteria, failureReason, DEFAULT_MAX_TIME_TO_POLL, DEFAULT_POLLING_INTERVAL); 219 } 220 221 /** 222 * Checks whether the given Callable<Boolean> is satisfied polling at a default interval. 223 * 224 * <p> 225 * This evaluates the Callable<Boolean> on the test thread, which more often than not is not 226 * correct in an InstrumentationTest. Use {@link #pollUiThread(Callable)} instead. 227 * 228 * @param criteria The Callable<Boolean> that will be checked. 229 */ pollInstrumentationThread(Callable<Boolean> criteria)230 public static void pollInstrumentationThread(Callable<Boolean> criteria) { 231 pollInstrumentationThread(criteria, null); 232 } 233 234 /** 235 * Checks whether the given Runnable completes without exception at a given interval on the UI 236 * thread, until either the Runnable successfully completes, or the maxTimeoutMs number of ms 237 * has elapsed. 238 * 239 * @param criteria The Runnable that will be attempted. 240 * @param maxTimeoutMs The maximum number of ms that this check will be performed for 241 * before timeout. 242 * @param checkIntervalMs The number of ms between checks. 243 * 244 * @see #pollInstrumentationThread(Runnable) 245 */ pollUiThread( final Runnable criteria, long maxTimeoutMs, long checkIntervalMs)246 public static void pollUiThread( 247 final Runnable criteria, long maxTimeoutMs, long checkIntervalMs) { 248 assert !ThreadUtils.runningOnUiThread(); 249 pollInstrumentationThread(() -> { 250 AtomicReference<Throwable> throwableRef = new AtomicReference<>(); 251 ThreadUtils.runOnUiThreadBlocking(() -> { 252 try { 253 criteria.run(); 254 } catch (Throwable t) { 255 throwableRef.set(t); 256 } 257 }); 258 Throwable throwable = throwableRef.get(); 259 if (throwable != null) { 260 if (throwable instanceof CriteriaNotSatisfiedException) { 261 throw new CriteriaNotSatisfiedException(throwable); 262 } else if (throwable instanceof RuntimeException) { 263 throw(RuntimeException) throwable; 264 } else { 265 throw new RuntimeException(throwable); 266 } 267 } 268 }, maxTimeoutMs, checkIntervalMs); 269 } 270 271 /** 272 * Checks whether the given Runnable completes without exception at the default interval on 273 * the UI thread. 274 * @param criteria The Runnable that will be attempted. 275 * 276 * @see #pollInstrumentationThread(Runnable) 277 */ pollUiThread(final Runnable criteria)278 public static void pollUiThread(final Runnable criteria) { 279 pollUiThread(criteria, DEFAULT_MAX_TIME_TO_POLL, DEFAULT_POLLING_INTERVAL); 280 } 281 282 /** 283 * Checks whether the given Callable<Boolean> is satisfied polling at a given interval on the UI 284 * thread, until either the criteria is satisfied, or the maxTimeoutMs number of ms has elapsed. 285 * 286 * @param criteria The Callable<Boolean> that will be checked. 287 * @param failureReason The static failure reason 288 * @param maxTimeoutMs The maximum number of ms that this check will be performed for 289 * before timeout. 290 * @param checkIntervalMs The number of ms between checks. 291 * 292 * @see #pollInstrumentationThread(Criteria) 293 */ pollUiThread(final Callable<Boolean> criteria, String failureReason, long maxTimeoutMs, long checkIntervalMs)294 public static void pollUiThread(final Callable<Boolean> criteria, String failureReason, 295 long maxTimeoutMs, long checkIntervalMs) { 296 pollUiThread( 297 toNotSatisfiedRunnable(criteria, failureReason), maxTimeoutMs, checkIntervalMs); 298 } 299 300 /** 301 * Checks whether the given Callable<Boolean> is satisfied polling at a given interval on the UI 302 * thread, until either the criteria is satisfied, or the maxTimeoutMs number of ms has elapsed. 303 * 304 * @param criteria The Callable<Boolean> that will be checked. 305 * @param maxTimeoutMs The maximum number of ms that this check will be performed for 306 * before timeout. 307 * @param checkIntervalMs The number of ms between checks. 308 * 309 * @see #pollInstrumentationThread(Criteria) 310 */ pollUiThread( final Callable<Boolean> criteria, long maxTimeoutMs, long checkIntervalMs)311 public static void pollUiThread( 312 final Callable<Boolean> criteria, long maxTimeoutMs, long checkIntervalMs) { 313 pollUiThread(criteria, null, maxTimeoutMs, checkIntervalMs); 314 } 315 316 /** 317 * Checks whether the given Callable<Boolean> is satisfied polling at a default interval on the 318 * UI thread. A static failure reason is given. 319 * @param criteria The Callable<Boolean> that will be checked. 320 * @param failureReason The static failure reason 321 * 322 * @see #pollInstrumentationThread(Criteria) 323 */ pollUiThread(final Callable<Boolean> criteria, String failureReason)324 public static void pollUiThread(final Callable<Boolean> criteria, String failureReason) { 325 pollUiThread(criteria, failureReason, DEFAULT_MAX_TIME_TO_POLL, DEFAULT_POLLING_INTERVAL); 326 } 327 328 /** 329 * Checks whether the given Callable<Boolean> is satisfied polling at a default interval on the 330 * UI thread. 331 * @param criteria The Callable<Boolean> that will be checked. 332 * 333 * @see #pollInstrumentationThread(Criteria) 334 */ pollUiThread(final Callable<Boolean> criteria)335 public static void pollUiThread(final Callable<Boolean> criteria) { 336 pollUiThread(criteria, null); 337 } 338 339 /** 340 * Checks whether the given Runnable completes without exception at a given interval on the UI 341 * thread, until either the Runnable successfully completes, or the maxTimeoutMs number of ms 342 * has elapsed. 343 * This call will nest the Looper in order to wait for the Runnable to complete. 344 * 345 * @param criteria The Runnable that will be attempted. 346 * @param maxTimeoutMs The maximum number of ms that this check will be performed for 347 * before timeout. 348 * @param checkIntervalMs The number of ms between checks. 349 * 350 * @see #pollInstrumentationThread(Runnable) 351 */ pollUiThreadNested( Runnable criteria, long maxTimeoutMs, long checkIntervalMs)352 public static void pollUiThreadNested( 353 Runnable criteria, long maxTimeoutMs, long checkIntervalMs) { 354 assert ThreadUtils.runningOnUiThread(); 355 pollThreadInternal(criteria, maxTimeoutMs, checkIntervalMs, true); 356 } 357 358 /** 359 * Checks whether the given Runnable is satisfied polling at a given interval on the UI 360 * thread, until either the criteria is satisfied, or the maxTimeoutMs number of ms has elapsed. 361 * This call will nest the Looper in order to wait for the Criteria to be satisfied. 362 * 363 * @param criteria The Callable<Boolean> that will be checked. 364 * @param maxTimeoutMs The maximum number of ms that this check will be performed for 365 * before timeout. 366 * @param checkIntervalMs The number of ms between checks. 367 * 368 * @see #pollInstrumentationThread(Criteria) 369 */ pollUiThreadNested( final Callable<Boolean> criteria, long maxTimeoutMs, long checkIntervalMs)370 public static void pollUiThreadNested( 371 final Callable<Boolean> criteria, long maxTimeoutMs, long checkIntervalMs) { 372 pollUiThreadNested(toNotSatisfiedRunnable(criteria, null), maxTimeoutMs, checkIntervalMs); 373 } 374 375 /** 376 * Checks whether the given Runnable completes without exception at the default interval on 377 * the UI thread. This call will nest the Looper in order to wait for the Runnable to complete. 378 * @param criteria The Runnable that will be attempted. 379 * 380 * @see #pollInstrumentationThread(Runnable) 381 */ pollUiThreadNested(final Runnable criteria)382 public static void pollUiThreadNested(final Runnable criteria) { 383 pollUiThreadNested(criteria, DEFAULT_MAX_TIME_TO_POLL, DEFAULT_POLLING_INTERVAL); 384 } 385 386 /** 387 * Checks whether the given Callable<Boolean> is satisfied polling at a default interval on the 388 * UI thread. This call will nest the Looper in order to wait for the Criteria to be satisfied. 389 * @param criteria The Callable<Boolean> that will be checked. 390 * 391 * @see #pollInstrumentationThread(Criteria) 392 */ pollUiThreadNested(final Callable<Boolean> criteria)393 public static void pollUiThreadNested(final Callable<Boolean> criteria) { 394 pollUiThreadNested(toNotSatisfiedRunnable(criteria, null)); 395 } 396 397 /** 398 * Sleeps the JUnit UI thread to wait on the condition. The condition must be met by a 399 * background thread that does not block on the UI thread. 400 * 401 * @param criteria The Callable<Boolean> that will be checked. 402 * 403 * @see #pollInstrumentationThread(Criteria) 404 */ pollUiThreadForJUnit(final Callable<Boolean> criteria)405 public static void pollUiThreadForJUnit(final Callable<Boolean> criteria) { 406 pollUiThreadForJUnit(toNotSatisfiedRunnable(criteria, null)); 407 } 408 409 /** 410 * Sleeps the JUnit UI thread to wait on the criteria. The criteria must be met by a 411 * background thread that does not block on the UI thread. 412 * 413 * @param criteria The Runnable that will be attempted. 414 * 415 * @see #pollInstrumentationThread(Criteria) 416 */ pollUiThreadForJUnit(final Runnable criteria)417 public static void pollUiThreadForJUnit(final Runnable criteria) { 418 assert ThreadUtils.runningOnUiThread(); 419 pollThreadInternal( 420 criteria, DEFAULT_JUNIT_MAX_TIME_TO_POLL, DEFAULT_JUNIT_POLLING_INTERVAL, false); 421 } 422 toNotSatisfiedRunnable( Callable<Boolean> criteria, String failureReason)423 private static Runnable toNotSatisfiedRunnable( 424 Callable<Boolean> criteria, String failureReason) { 425 return () -> { 426 boolean isSatisfied; 427 try { 428 isSatisfied = criteria.call(); 429 } catch (RuntimeException re) { 430 throw re; 431 } catch (Exception e) { 432 throw new RuntimeException(e); 433 } 434 Criteria.checkThat(failureReason, isSatisfied, Matchers.is(true)); 435 }; 436 } 437 } 438