1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mobile/android/tests/background/junit3/src/testhelpers/WaitHelper.java Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,171 @@ 1.4 +/* Any copyright is dedicated to the Public Domain. 1.5 + http://creativecommons.org/publicdomain/zero/1.0/ */ 1.6 + 1.7 +package org.mozilla.gecko.background.testhelpers; 1.8 + 1.9 +import java.util.concurrent.ArrayBlockingQueue; 1.10 +import java.util.concurrent.BlockingQueue; 1.11 +import java.util.concurrent.TimeUnit; 1.12 + 1.13 +import org.mozilla.gecko.background.common.log.Logger; 1.14 + 1.15 +/** 1.16 + * Implements waiting for asynchronous test events. 1.17 + * 1.18 + * Call WaitHelper.getTestWaiter() to get the unique instance. 1.19 + * 1.20 + * Call performWait(runnable) to execute runnable synchronously. 1.21 + * runnable *must* call performNotify() on all exit paths to signal to 1.22 + * the TestWaiter that the runnable has completed. 1.23 + * 1.24 + * @author rnewman 1.25 + * @author nalexander 1.26 + */ 1.27 +public class WaitHelper { 1.28 + 1.29 + public static final String LOG_TAG = "WaitHelper"; 1.30 + 1.31 + public static class Result { 1.32 + public Throwable error; 1.33 + public Result() { 1.34 + error = null; 1.35 + } 1.36 + 1.37 + public Result(Throwable error) { 1.38 + this.error = error; 1.39 + } 1.40 + } 1.41 + 1.42 + public static abstract class WaitHelperError extends Error { 1.43 + private static final long serialVersionUID = 7074690961681883619L; 1.44 + } 1.45 + 1.46 + /** 1.47 + * Immutable. 1.48 + * 1.49 + * @author rnewman 1.50 + */ 1.51 + public static class TimeoutError extends WaitHelperError { 1.52 + private static final long serialVersionUID = 8591672555848651736L; 1.53 + public final int waitTimeInMillis; 1.54 + 1.55 + public TimeoutError(int waitTimeInMillis) { 1.56 + this.waitTimeInMillis = waitTimeInMillis; 1.57 + } 1.58 + } 1.59 + 1.60 + public static class MultipleNotificationsError extends WaitHelperError { 1.61 + private static final long serialVersionUID = -9072736521571635495L; 1.62 + } 1.63 + 1.64 + public static class InterruptedError extends WaitHelperError { 1.65 + private static final long serialVersionUID = 8383948170038639308L; 1.66 + } 1.67 + 1.68 + public static class InnerError extends WaitHelperError { 1.69 + private static final long serialVersionUID = 3008502618576773778L; 1.70 + public Throwable innerError; 1.71 + 1.72 + public InnerError(Throwable e) { 1.73 + innerError = e; 1.74 + if (e != null) { 1.75 + // Eclipse prints the stack trace of the cause. 1.76 + this.initCause(e); 1.77 + } 1.78 + } 1.79 + } 1.80 + 1.81 + public BlockingQueue<Result> queue = new ArrayBlockingQueue<Result>(1); 1.82 + 1.83 + /** 1.84 + * How long performWait should wait for, in milliseconds, with the 1.85 + * convention that a negative value means "wait forever". 1.86 + */ 1.87 + public static int defaultWaitTimeoutInMillis = -1; 1.88 + 1.89 + public void performWait(Runnable action) throws WaitHelperError { 1.90 + this.performWait(defaultWaitTimeoutInMillis, action); 1.91 + } 1.92 + 1.93 + public void performWait(int waitTimeoutInMillis, Runnable action) throws WaitHelperError { 1.94 + Logger.debug(LOG_TAG, "performWait called."); 1.95 + 1.96 + Result result = null; 1.97 + 1.98 + try { 1.99 + if (action != null) { 1.100 + try { 1.101 + action.run(); 1.102 + Logger.debug(LOG_TAG, "Action done."); 1.103 + } catch (Exception ex) { 1.104 + Logger.debug(LOG_TAG, "Performing action threw: " + ex.getMessage()); 1.105 + throw new InnerError(ex); 1.106 + } 1.107 + } 1.108 + 1.109 + if (waitTimeoutInMillis < 0) { 1.110 + result = queue.take(); 1.111 + } else { 1.112 + result = queue.poll(waitTimeoutInMillis, TimeUnit.MILLISECONDS); 1.113 + } 1.114 + Logger.debug(LOG_TAG, "Got result from queue: " + result); 1.115 + } catch (InterruptedException e) { 1.116 + // We were interrupted. 1.117 + Logger.debug(LOG_TAG, "performNotify interrupted with InterruptedException " + e); 1.118 + final InterruptedError interruptedError = new InterruptedError(); 1.119 + interruptedError.initCause(e); 1.120 + throw interruptedError; 1.121 + } 1.122 + 1.123 + if (result == null) { 1.124 + // We timed out. 1.125 + throw new TimeoutError(waitTimeoutInMillis); 1.126 + } else if (result.error != null) { 1.127 + Logger.debug(LOG_TAG, "Notified with error: " + result.error.getMessage()); 1.128 + 1.129 + // Rethrow any assertion with which we were notified. 1.130 + InnerError innerError = new InnerError(result.error); 1.131 + throw innerError; 1.132 + } 1.133 + // Success! 1.134 + } 1.135 + 1.136 + public void performNotify(final Throwable e) { 1.137 + if (e != null) { 1.138 + Logger.debug(LOG_TAG, "performNotify called with Throwable: " + e.getMessage()); 1.139 + } else { 1.140 + Logger.debug(LOG_TAG, "performNotify called."); 1.141 + } 1.142 + 1.143 + if (!queue.offer(new Result(e))) { 1.144 + // This could happen if performNotify is called multiple times (which is an error). 1.145 + throw new MultipleNotificationsError(); 1.146 + } 1.147 + } 1.148 + 1.149 + public void performNotify() { 1.150 + this.performNotify(null); 1.151 + } 1.152 + 1.153 + public static Runnable onThreadRunnable(final Runnable r) { 1.154 + return new Runnable() { 1.155 + @Override 1.156 + public void run() { 1.157 + new Thread(r).start(); 1.158 + } 1.159 + }; 1.160 + } 1.161 + 1.162 + private static WaitHelper singleWaiter = new WaitHelper(); 1.163 + public static WaitHelper getTestWaiter() { 1.164 + return singleWaiter; 1.165 + } 1.166 + 1.167 + public static void resetTestWaiter() { 1.168 + singleWaiter = new WaitHelper(); 1.169 + } 1.170 + 1.171 + public boolean isIdle() { 1.172 + return queue.isEmpty(); 1.173 + } 1.174 +}