Wed, 31 Dec 2014 07:22:50 +0100
Correct previous dual key logic pending first delivery installment.
1 /* Any copyright is dedicated to the Public Domain.
2 http://creativecommons.org/publicdomain/zero/1.0/ */
4 package org.mozilla.gecko.background.testhelpers;
6 import java.util.concurrent.ArrayBlockingQueue;
7 import java.util.concurrent.BlockingQueue;
8 import java.util.concurrent.TimeUnit;
10 import org.mozilla.gecko.background.common.log.Logger;
12 /**
13 * Implements waiting for asynchronous test events.
14 *
15 * Call WaitHelper.getTestWaiter() to get the unique instance.
16 *
17 * Call performWait(runnable) to execute runnable synchronously.
18 * runnable *must* call performNotify() on all exit paths to signal to
19 * the TestWaiter that the runnable has completed.
20 *
21 * @author rnewman
22 * @author nalexander
23 */
24 public class WaitHelper {
26 public static final String LOG_TAG = "WaitHelper";
28 public static class Result {
29 public Throwable error;
30 public Result() {
31 error = null;
32 }
34 public Result(Throwable error) {
35 this.error = error;
36 }
37 }
39 public static abstract class WaitHelperError extends Error {
40 private static final long serialVersionUID = 7074690961681883619L;
41 }
43 /**
44 * Immutable.
45 *
46 * @author rnewman
47 */
48 public static class TimeoutError extends WaitHelperError {
49 private static final long serialVersionUID = 8591672555848651736L;
50 public final int waitTimeInMillis;
52 public TimeoutError(int waitTimeInMillis) {
53 this.waitTimeInMillis = waitTimeInMillis;
54 }
55 }
57 public static class MultipleNotificationsError extends WaitHelperError {
58 private static final long serialVersionUID = -9072736521571635495L;
59 }
61 public static class InterruptedError extends WaitHelperError {
62 private static final long serialVersionUID = 8383948170038639308L;
63 }
65 public static class InnerError extends WaitHelperError {
66 private static final long serialVersionUID = 3008502618576773778L;
67 public Throwable innerError;
69 public InnerError(Throwable e) {
70 innerError = e;
71 if (e != null) {
72 // Eclipse prints the stack trace of the cause.
73 this.initCause(e);
74 }
75 }
76 }
78 public BlockingQueue<Result> queue = new ArrayBlockingQueue<Result>(1);
80 /**
81 * How long performWait should wait for, in milliseconds, with the
82 * convention that a negative value means "wait forever".
83 */
84 public static int defaultWaitTimeoutInMillis = -1;
86 public void performWait(Runnable action) throws WaitHelperError {
87 this.performWait(defaultWaitTimeoutInMillis, action);
88 }
90 public void performWait(int waitTimeoutInMillis, Runnable action) throws WaitHelperError {
91 Logger.debug(LOG_TAG, "performWait called.");
93 Result result = null;
95 try {
96 if (action != null) {
97 try {
98 action.run();
99 Logger.debug(LOG_TAG, "Action done.");
100 } catch (Exception ex) {
101 Logger.debug(LOG_TAG, "Performing action threw: " + ex.getMessage());
102 throw new InnerError(ex);
103 }
104 }
106 if (waitTimeoutInMillis < 0) {
107 result = queue.take();
108 } else {
109 result = queue.poll(waitTimeoutInMillis, TimeUnit.MILLISECONDS);
110 }
111 Logger.debug(LOG_TAG, "Got result from queue: " + result);
112 } catch (InterruptedException e) {
113 // We were interrupted.
114 Logger.debug(LOG_TAG, "performNotify interrupted with InterruptedException " + e);
115 final InterruptedError interruptedError = new InterruptedError();
116 interruptedError.initCause(e);
117 throw interruptedError;
118 }
120 if (result == null) {
121 // We timed out.
122 throw new TimeoutError(waitTimeoutInMillis);
123 } else if (result.error != null) {
124 Logger.debug(LOG_TAG, "Notified with error: " + result.error.getMessage());
126 // Rethrow any assertion with which we were notified.
127 InnerError innerError = new InnerError(result.error);
128 throw innerError;
129 }
130 // Success!
131 }
133 public void performNotify(final Throwable e) {
134 if (e != null) {
135 Logger.debug(LOG_TAG, "performNotify called with Throwable: " + e.getMessage());
136 } else {
137 Logger.debug(LOG_TAG, "performNotify called.");
138 }
140 if (!queue.offer(new Result(e))) {
141 // This could happen if performNotify is called multiple times (which is an error).
142 throw new MultipleNotificationsError();
143 }
144 }
146 public void performNotify() {
147 this.performNotify(null);
148 }
150 public static Runnable onThreadRunnable(final Runnable r) {
151 return new Runnable() {
152 @Override
153 public void run() {
154 new Thread(r).start();
155 }
156 };
157 }
159 private static WaitHelper singleWaiter = new WaitHelper();
160 public static WaitHelper getTestWaiter() {
161 return singleWaiter;
162 }
164 public static void resetTestWaiter() {
165 singleWaiter = new WaitHelper();
166 }
168 public boolean isIdle() {
169 return queue.isEmpty();
170 }
171 }