|
1 /* Any copyright is dedicated to the Public Domain. |
|
2 http://creativecommons.org/publicdomain/zero/1.0/ */ |
|
3 |
|
4 package org.mozilla.gecko.background.common; |
|
5 |
|
6 import junit.framework.AssertionFailedError; |
|
7 |
|
8 import org.mozilla.gecko.background.helpers.AndroidSyncTestCase; |
|
9 import org.mozilla.gecko.background.testhelpers.WaitHelper; |
|
10 import org.mozilla.gecko.background.testhelpers.WaitHelper.InnerError; |
|
11 import org.mozilla.gecko.background.testhelpers.WaitHelper.TimeoutError; |
|
12 import org.mozilla.gecko.sync.ThreadPool; |
|
13 |
|
14 public class TestWaitHelper extends AndroidSyncTestCase { |
|
15 private static final String ERROR_UNIQUE_IDENTIFIER = "error unique identifier"; |
|
16 |
|
17 public static int NO_WAIT = 1; // Milliseconds. |
|
18 public static int SHORT_WAIT = 100; // Milliseconds. |
|
19 public static int LONG_WAIT = 3 * SHORT_WAIT; |
|
20 |
|
21 private Object notifyMonitor = new Object(); |
|
22 // Guarded by notifyMonitor. |
|
23 private boolean performNotifyCalled = false; |
|
24 private boolean performNotifyErrorCalled = false; |
|
25 private void setPerformNotifyCalled() { |
|
26 synchronized (notifyMonitor) { |
|
27 performNotifyCalled = true; |
|
28 } |
|
29 } |
|
30 private void setPerformNotifyErrorCalled() { |
|
31 synchronized (notifyMonitor) { |
|
32 performNotifyErrorCalled = true; |
|
33 } |
|
34 } |
|
35 private void resetNotifyCalled() { |
|
36 synchronized (notifyMonitor) { |
|
37 performNotifyCalled = false; |
|
38 performNotifyErrorCalled = false; |
|
39 } |
|
40 } |
|
41 private void assertBothCalled() { |
|
42 synchronized (notifyMonitor) { |
|
43 assertTrue(performNotifyCalled); |
|
44 assertTrue(performNotifyErrorCalled); |
|
45 } |
|
46 } |
|
47 private void assertErrorCalled() { |
|
48 synchronized (notifyMonitor) { |
|
49 assertFalse(performNotifyCalled); |
|
50 assertTrue(performNotifyErrorCalled); |
|
51 } |
|
52 } |
|
53 private void assertCalled() { |
|
54 synchronized (notifyMonitor) { |
|
55 assertTrue(performNotifyCalled); |
|
56 assertFalse(performNotifyErrorCalled); |
|
57 } |
|
58 } |
|
59 |
|
60 public WaitHelper waitHelper; |
|
61 |
|
62 public TestWaitHelper() { |
|
63 super(); |
|
64 } |
|
65 |
|
66 public void setUp() { |
|
67 WaitHelper.resetTestWaiter(); |
|
68 waitHelper = WaitHelper.getTestWaiter(); |
|
69 resetNotifyCalled(); |
|
70 } |
|
71 |
|
72 public void tearDown() { |
|
73 assertTrue(waitHelper.isIdle()); |
|
74 } |
|
75 |
|
76 public Runnable performNothingRunnable() { |
|
77 return new Runnable() { |
|
78 public void run() { |
|
79 } |
|
80 }; |
|
81 } |
|
82 |
|
83 public Runnable performNotifyRunnable() { |
|
84 return new Runnable() { |
|
85 public void run() { |
|
86 setPerformNotifyCalled(); |
|
87 waitHelper.performNotify(); |
|
88 } |
|
89 }; |
|
90 } |
|
91 |
|
92 public Runnable performNotifyAfterDelayRunnable(final int delayInMillis) { |
|
93 return new Runnable() { |
|
94 public void run() { |
|
95 try { |
|
96 Thread.sleep(delayInMillis); |
|
97 } catch (InterruptedException e) { |
|
98 fail("Interrupted."); |
|
99 } |
|
100 |
|
101 setPerformNotifyCalled(); |
|
102 waitHelper.performNotify(); |
|
103 } |
|
104 }; |
|
105 } |
|
106 |
|
107 public Runnable performNotifyErrorRunnable() { |
|
108 return new Runnable() { |
|
109 public void run() { |
|
110 setPerformNotifyCalled(); |
|
111 waitHelper.performNotify(new AssertionFailedError(ERROR_UNIQUE_IDENTIFIER)); |
|
112 } |
|
113 }; |
|
114 } |
|
115 |
|
116 public Runnable inThreadPool(final Runnable runnable) { |
|
117 return new Runnable() { |
|
118 @Override |
|
119 public void run() { |
|
120 ThreadPool.run(runnable); |
|
121 } |
|
122 }; |
|
123 } |
|
124 |
|
125 public Runnable inThread(final Runnable runnable) { |
|
126 return new Runnable() { |
|
127 @Override |
|
128 public void run() { |
|
129 new Thread(runnable).start(); |
|
130 } |
|
131 }; |
|
132 } |
|
133 |
|
134 protected void expectAssertionFailedError(Runnable runnable) { |
|
135 try { |
|
136 waitHelper.performWait(runnable); |
|
137 } catch (InnerError e) { |
|
138 AssertionFailedError inner = (AssertionFailedError)e.innerError; |
|
139 setPerformNotifyErrorCalled(); |
|
140 String message = inner.getMessage(); |
|
141 assertTrue("Expected '" + message + "' to contain '" + ERROR_UNIQUE_IDENTIFIER + "'", |
|
142 message.contains(ERROR_UNIQUE_IDENTIFIER)); |
|
143 } |
|
144 } |
|
145 |
|
146 protected void expectAssertionFailedErrorAfterDelay(int wait, Runnable runnable) { |
|
147 try { |
|
148 waitHelper.performWait(wait, runnable); |
|
149 } catch (InnerError e) { |
|
150 AssertionFailedError inner = (AssertionFailedError)e.innerError; |
|
151 setPerformNotifyErrorCalled(); |
|
152 String message = inner.getMessage(); |
|
153 assertTrue("Expected '" + message + "' to contain '" + ERROR_UNIQUE_IDENTIFIER + "'", |
|
154 message.contains(ERROR_UNIQUE_IDENTIFIER)); |
|
155 } |
|
156 } |
|
157 |
|
158 public void testPerformWait() { |
|
159 waitHelper.performWait(performNotifyRunnable()); |
|
160 assertCalled(); |
|
161 } |
|
162 |
|
163 public void testPerformWaitInThread() { |
|
164 waitHelper.performWait(inThread(performNotifyRunnable())); |
|
165 assertCalled(); |
|
166 } |
|
167 |
|
168 public void testPerformWaitInThreadPool() { |
|
169 waitHelper.performWait(inThreadPool(performNotifyRunnable())); |
|
170 assertCalled(); |
|
171 } |
|
172 |
|
173 public void testPerformTimeoutWait() { |
|
174 waitHelper.performWait(SHORT_WAIT, performNotifyRunnable()); |
|
175 assertCalled(); |
|
176 } |
|
177 |
|
178 public void testPerformTimeoutWaitInThread() { |
|
179 waitHelper.performWait(SHORT_WAIT, inThread(performNotifyRunnable())); |
|
180 assertCalled(); |
|
181 } |
|
182 |
|
183 public void testPerformTimeoutWaitInThreadPool() { |
|
184 waitHelper.performWait(SHORT_WAIT, inThreadPool(performNotifyRunnable())); |
|
185 assertCalled(); |
|
186 } |
|
187 |
|
188 public void testPerformErrorWaitInThread() { |
|
189 expectAssertionFailedError(inThread(performNotifyErrorRunnable())); |
|
190 assertBothCalled(); |
|
191 } |
|
192 |
|
193 public void testPerformErrorWaitInThreadPool() { |
|
194 expectAssertionFailedError(inThreadPool(performNotifyErrorRunnable())); |
|
195 assertBothCalled(); |
|
196 } |
|
197 |
|
198 public void testPerformErrorTimeoutWaitInThread() { |
|
199 expectAssertionFailedErrorAfterDelay(SHORT_WAIT, inThread(performNotifyErrorRunnable())); |
|
200 assertBothCalled(); |
|
201 } |
|
202 |
|
203 public void testPerformErrorTimeoutWaitInThreadPool() { |
|
204 expectAssertionFailedErrorAfterDelay(SHORT_WAIT, inThreadPool(performNotifyErrorRunnable())); |
|
205 assertBothCalled(); |
|
206 } |
|
207 |
|
208 public void testTimeout() { |
|
209 try { |
|
210 waitHelper.performWait(SHORT_WAIT, performNothingRunnable()); |
|
211 } catch (TimeoutError e) { |
|
212 setPerformNotifyErrorCalled(); |
|
213 assertEquals(SHORT_WAIT, e.waitTimeInMillis); |
|
214 } |
|
215 assertErrorCalled(); |
|
216 } |
|
217 |
|
218 /** |
|
219 * This will pass. The sequence in the main thread is: |
|
220 * - A short delay. |
|
221 * - performNotify is called. |
|
222 * - performWait is called and immediately finds that performNotify was called before. |
|
223 */ |
|
224 public void testDelay() { |
|
225 try { |
|
226 waitHelper.performWait(1, performNotifyAfterDelayRunnable(SHORT_WAIT)); |
|
227 } catch (AssertionFailedError e) { |
|
228 setPerformNotifyErrorCalled(); |
|
229 assertTrue(e.getMessage(), e.getMessage().contains("TIMEOUT")); |
|
230 } |
|
231 assertCalled(); |
|
232 } |
|
233 |
|
234 public Runnable performNotifyMultipleTimesRunnable() { |
|
235 return new Runnable() { |
|
236 public void run() { |
|
237 waitHelper.performNotify(); |
|
238 setPerformNotifyCalled(); |
|
239 waitHelper.performNotify(); |
|
240 } |
|
241 }; |
|
242 } |
|
243 |
|
244 public void testPerformNotifyMultipleTimesFails() { |
|
245 try { |
|
246 waitHelper.performWait(NO_WAIT, performNotifyMultipleTimesRunnable()); // Not run on thread, so runnable executes before performWait looks for notifications. |
|
247 } catch (WaitHelper.MultipleNotificationsError e) { |
|
248 setPerformNotifyErrorCalled(); |
|
249 } |
|
250 assertBothCalled(); |
|
251 assertFalse(waitHelper.isIdle()); // First perform notify should be hanging around. |
|
252 waitHelper.performWait(NO_WAIT, performNothingRunnable()); |
|
253 } |
|
254 |
|
255 public void testNestedWaitsAndNotifies() { |
|
256 waitHelper.performWait(new Runnable() { |
|
257 @Override |
|
258 public void run() { |
|
259 waitHelper.performWait(new Runnable() { |
|
260 public void run() { |
|
261 setPerformNotifyCalled(); |
|
262 waitHelper.performNotify(); |
|
263 } |
|
264 }); |
|
265 setPerformNotifyErrorCalled(); |
|
266 waitHelper.performNotify(); |
|
267 } |
|
268 }); |
|
269 assertBothCalled(); |
|
270 } |
|
271 |
|
272 public void testAssertIsReported() { |
|
273 try { |
|
274 waitHelper.performWait(1, new Runnable() { |
|
275 @Override |
|
276 public void run() { |
|
277 assertTrue("unique identifier", false); |
|
278 } |
|
279 }); |
|
280 } catch (AssertionFailedError e) { |
|
281 setPerformNotifyErrorCalled(); |
|
282 assertTrue(e.getMessage(), e.getMessage().contains("unique identifier")); |
|
283 } |
|
284 assertErrorCalled(); |
|
285 } |
|
286 |
|
287 /** |
|
288 * The inner wait will timeout, but the outer wait will succeed. The sequence in the helper thread is: |
|
289 * - A short delay. |
|
290 * - performNotify is called. |
|
291 * |
|
292 * The sequence in the main thread is: |
|
293 * - performWait is called and times out because the helper thread does not call |
|
294 * performNotify quickly enough. |
|
295 */ |
|
296 public void testDelayInThread() throws InterruptedException { |
|
297 waitHelper.performWait(new Runnable() { |
|
298 @Override |
|
299 public void run() { |
|
300 try { |
|
301 waitHelper.performWait(NO_WAIT, inThread(new Runnable() { |
|
302 public void run() { |
|
303 try { |
|
304 Thread.sleep(SHORT_WAIT); |
|
305 } catch (InterruptedException e) { |
|
306 fail("Interrupted."); |
|
307 } |
|
308 |
|
309 setPerformNotifyCalled(); |
|
310 waitHelper.performNotify(); |
|
311 } |
|
312 })); |
|
313 } catch (WaitHelper.TimeoutError e) { |
|
314 setPerformNotifyErrorCalled(); |
|
315 assertEquals(NO_WAIT, e.waitTimeInMillis); |
|
316 } |
|
317 } |
|
318 }); |
|
319 assertBothCalled(); |
|
320 } |
|
321 |
|
322 /** |
|
323 * The inner wait will timeout, but the outer wait will succeed. The sequence in the helper thread is: |
|
324 * - A short delay. |
|
325 * - performNotify is called. |
|
326 * |
|
327 * The sequence in the main thread is: |
|
328 * - performWait is called and times out because the helper thread does not call |
|
329 * performNotify quickly enough. |
|
330 */ |
|
331 public void testDelayInThreadPool() throws InterruptedException { |
|
332 waitHelper.performWait(new Runnable() { |
|
333 @Override |
|
334 public void run() { |
|
335 try { |
|
336 waitHelper.performWait(NO_WAIT, inThreadPool(new Runnable() { |
|
337 public void run() { |
|
338 try { |
|
339 Thread.sleep(SHORT_WAIT); |
|
340 } catch (InterruptedException e) { |
|
341 fail("Interrupted."); |
|
342 } |
|
343 |
|
344 setPerformNotifyCalled(); |
|
345 waitHelper.performNotify(); |
|
346 } |
|
347 })); |
|
348 } catch (WaitHelper.TimeoutError e) { |
|
349 setPerformNotifyErrorCalled(); |
|
350 assertEquals(NO_WAIT, e.waitTimeInMillis); |
|
351 } |
|
352 } |
|
353 }); |
|
354 assertBothCalled(); |
|
355 } |
|
356 } |