diff -r 000000000000 -r 6474c204b198 mobile/android/tests/background/junit3/src/testhelpers/WaitHelper.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mobile/android/tests/background/junit3/src/testhelpers/WaitHelper.java Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,171 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +package org.mozilla.gecko.background.testhelpers; + +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.TimeUnit; + +import org.mozilla.gecko.background.common.log.Logger; + +/** + * Implements waiting for asynchronous test events. + * + * Call WaitHelper.getTestWaiter() to get the unique instance. + * + * Call performWait(runnable) to execute runnable synchronously. + * runnable *must* call performNotify() on all exit paths to signal to + * the TestWaiter that the runnable has completed. + * + * @author rnewman + * @author nalexander + */ +public class WaitHelper { + + public static final String LOG_TAG = "WaitHelper"; + + public static class Result { + public Throwable error; + public Result() { + error = null; + } + + public Result(Throwable error) { + this.error = error; + } + } + + public static abstract class WaitHelperError extends Error { + private static final long serialVersionUID = 7074690961681883619L; + } + + /** + * Immutable. + * + * @author rnewman + */ + public static class TimeoutError extends WaitHelperError { + private static final long serialVersionUID = 8591672555848651736L; + public final int waitTimeInMillis; + + public TimeoutError(int waitTimeInMillis) { + this.waitTimeInMillis = waitTimeInMillis; + } + } + + public static class MultipleNotificationsError extends WaitHelperError { + private static final long serialVersionUID = -9072736521571635495L; + } + + public static class InterruptedError extends WaitHelperError { + private static final long serialVersionUID = 8383948170038639308L; + } + + public static class InnerError extends WaitHelperError { + private static final long serialVersionUID = 3008502618576773778L; + public Throwable innerError; + + public InnerError(Throwable e) { + innerError = e; + if (e != null) { + // Eclipse prints the stack trace of the cause. + this.initCause(e); + } + } + } + + public BlockingQueue queue = new ArrayBlockingQueue(1); + + /** + * How long performWait should wait for, in milliseconds, with the + * convention that a negative value means "wait forever". + */ + public static int defaultWaitTimeoutInMillis = -1; + + public void performWait(Runnable action) throws WaitHelperError { + this.performWait(defaultWaitTimeoutInMillis, action); + } + + public void performWait(int waitTimeoutInMillis, Runnable action) throws WaitHelperError { + Logger.debug(LOG_TAG, "performWait called."); + + Result result = null; + + try { + if (action != null) { + try { + action.run(); + Logger.debug(LOG_TAG, "Action done."); + } catch (Exception ex) { + Logger.debug(LOG_TAG, "Performing action threw: " + ex.getMessage()); + throw new InnerError(ex); + } + } + + if (waitTimeoutInMillis < 0) { + result = queue.take(); + } else { + result = queue.poll(waitTimeoutInMillis, TimeUnit.MILLISECONDS); + } + Logger.debug(LOG_TAG, "Got result from queue: " + result); + } catch (InterruptedException e) { + // We were interrupted. + Logger.debug(LOG_TAG, "performNotify interrupted with InterruptedException " + e); + final InterruptedError interruptedError = new InterruptedError(); + interruptedError.initCause(e); + throw interruptedError; + } + + if (result == null) { + // We timed out. + throw new TimeoutError(waitTimeoutInMillis); + } else if (result.error != null) { + Logger.debug(LOG_TAG, "Notified with error: " + result.error.getMessage()); + + // Rethrow any assertion with which we were notified. + InnerError innerError = new InnerError(result.error); + throw innerError; + } + // Success! + } + + public void performNotify(final Throwable e) { + if (e != null) { + Logger.debug(LOG_TAG, "performNotify called with Throwable: " + e.getMessage()); + } else { + Logger.debug(LOG_TAG, "performNotify called."); + } + + if (!queue.offer(new Result(e))) { + // This could happen if performNotify is called multiple times (which is an error). + throw new MultipleNotificationsError(); + } + } + + public void performNotify() { + this.performNotify(null); + } + + public static Runnable onThreadRunnable(final Runnable r) { + return new Runnable() { + @Override + public void run() { + new Thread(r).start(); + } + }; + } + + private static WaitHelper singleWaiter = new WaitHelper(); + public static WaitHelper getTestWaiter() { + return singleWaiter; + } + + public static void resetTestWaiter() { + singleWaiter = new WaitHelper(); + } + + public boolean isIdle() { + return queue.isEmpty(); + } +}