1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mobile/android/tests/background/junit3/src/common/TestWaitHelper.java Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,356 @@ 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.common; 1.8 + 1.9 +import junit.framework.AssertionFailedError; 1.10 + 1.11 +import org.mozilla.gecko.background.helpers.AndroidSyncTestCase; 1.12 +import org.mozilla.gecko.background.testhelpers.WaitHelper; 1.13 +import org.mozilla.gecko.background.testhelpers.WaitHelper.InnerError; 1.14 +import org.mozilla.gecko.background.testhelpers.WaitHelper.TimeoutError; 1.15 +import org.mozilla.gecko.sync.ThreadPool; 1.16 + 1.17 +public class TestWaitHelper extends AndroidSyncTestCase { 1.18 + private static final String ERROR_UNIQUE_IDENTIFIER = "error unique identifier"; 1.19 + 1.20 + public static int NO_WAIT = 1; // Milliseconds. 1.21 + public static int SHORT_WAIT = 100; // Milliseconds. 1.22 + public static int LONG_WAIT = 3 * SHORT_WAIT; 1.23 + 1.24 + private Object notifyMonitor = new Object(); 1.25 + // Guarded by notifyMonitor. 1.26 + private boolean performNotifyCalled = false; 1.27 + private boolean performNotifyErrorCalled = false; 1.28 + private void setPerformNotifyCalled() { 1.29 + synchronized (notifyMonitor) { 1.30 + performNotifyCalled = true; 1.31 + } 1.32 + } 1.33 + private void setPerformNotifyErrorCalled() { 1.34 + synchronized (notifyMonitor) { 1.35 + performNotifyErrorCalled = true; 1.36 + } 1.37 + } 1.38 + private void resetNotifyCalled() { 1.39 + synchronized (notifyMonitor) { 1.40 + performNotifyCalled = false; 1.41 + performNotifyErrorCalled = false; 1.42 + } 1.43 + } 1.44 + private void assertBothCalled() { 1.45 + synchronized (notifyMonitor) { 1.46 + assertTrue(performNotifyCalled); 1.47 + assertTrue(performNotifyErrorCalled); 1.48 + } 1.49 + } 1.50 + private void assertErrorCalled() { 1.51 + synchronized (notifyMonitor) { 1.52 + assertFalse(performNotifyCalled); 1.53 + assertTrue(performNotifyErrorCalled); 1.54 + } 1.55 + } 1.56 + private void assertCalled() { 1.57 + synchronized (notifyMonitor) { 1.58 + assertTrue(performNotifyCalled); 1.59 + assertFalse(performNotifyErrorCalled); 1.60 + } 1.61 + } 1.62 + 1.63 + public WaitHelper waitHelper; 1.64 + 1.65 + public TestWaitHelper() { 1.66 + super(); 1.67 + } 1.68 + 1.69 + public void setUp() { 1.70 + WaitHelper.resetTestWaiter(); 1.71 + waitHelper = WaitHelper.getTestWaiter(); 1.72 + resetNotifyCalled(); 1.73 + } 1.74 + 1.75 + public void tearDown() { 1.76 + assertTrue(waitHelper.isIdle()); 1.77 + } 1.78 + 1.79 + public Runnable performNothingRunnable() { 1.80 + return new Runnable() { 1.81 + public void run() { 1.82 + } 1.83 + }; 1.84 + } 1.85 + 1.86 + public Runnable performNotifyRunnable() { 1.87 + return new Runnable() { 1.88 + public void run() { 1.89 + setPerformNotifyCalled(); 1.90 + waitHelper.performNotify(); 1.91 + } 1.92 + }; 1.93 + } 1.94 + 1.95 + public Runnable performNotifyAfterDelayRunnable(final int delayInMillis) { 1.96 + return new Runnable() { 1.97 + public void run() { 1.98 + try { 1.99 + Thread.sleep(delayInMillis); 1.100 + } catch (InterruptedException e) { 1.101 + fail("Interrupted."); 1.102 + } 1.103 + 1.104 + setPerformNotifyCalled(); 1.105 + waitHelper.performNotify(); 1.106 + } 1.107 + }; 1.108 + } 1.109 + 1.110 + public Runnable performNotifyErrorRunnable() { 1.111 + return new Runnable() { 1.112 + public void run() { 1.113 + setPerformNotifyCalled(); 1.114 + waitHelper.performNotify(new AssertionFailedError(ERROR_UNIQUE_IDENTIFIER)); 1.115 + } 1.116 + }; 1.117 + } 1.118 + 1.119 + public Runnable inThreadPool(final Runnable runnable) { 1.120 + return new Runnable() { 1.121 + @Override 1.122 + public void run() { 1.123 + ThreadPool.run(runnable); 1.124 + } 1.125 + }; 1.126 + } 1.127 + 1.128 + public Runnable inThread(final Runnable runnable) { 1.129 + return new Runnable() { 1.130 + @Override 1.131 + public void run() { 1.132 + new Thread(runnable).start(); 1.133 + } 1.134 + }; 1.135 + } 1.136 + 1.137 + protected void expectAssertionFailedError(Runnable runnable) { 1.138 + try { 1.139 + waitHelper.performWait(runnable); 1.140 + } catch (InnerError e) { 1.141 + AssertionFailedError inner = (AssertionFailedError)e.innerError; 1.142 + setPerformNotifyErrorCalled(); 1.143 + String message = inner.getMessage(); 1.144 + assertTrue("Expected '" + message + "' to contain '" + ERROR_UNIQUE_IDENTIFIER + "'", 1.145 + message.contains(ERROR_UNIQUE_IDENTIFIER)); 1.146 + } 1.147 + } 1.148 + 1.149 + protected void expectAssertionFailedErrorAfterDelay(int wait, Runnable runnable) { 1.150 + try { 1.151 + waitHelper.performWait(wait, runnable); 1.152 + } catch (InnerError e) { 1.153 + AssertionFailedError inner = (AssertionFailedError)e.innerError; 1.154 + setPerformNotifyErrorCalled(); 1.155 + String message = inner.getMessage(); 1.156 + assertTrue("Expected '" + message + "' to contain '" + ERROR_UNIQUE_IDENTIFIER + "'", 1.157 + message.contains(ERROR_UNIQUE_IDENTIFIER)); 1.158 + } 1.159 + } 1.160 + 1.161 + public void testPerformWait() { 1.162 + waitHelper.performWait(performNotifyRunnable()); 1.163 + assertCalled(); 1.164 + } 1.165 + 1.166 + public void testPerformWaitInThread() { 1.167 + waitHelper.performWait(inThread(performNotifyRunnable())); 1.168 + assertCalled(); 1.169 + } 1.170 + 1.171 + public void testPerformWaitInThreadPool() { 1.172 + waitHelper.performWait(inThreadPool(performNotifyRunnable())); 1.173 + assertCalled(); 1.174 + } 1.175 + 1.176 + public void testPerformTimeoutWait() { 1.177 + waitHelper.performWait(SHORT_WAIT, performNotifyRunnable()); 1.178 + assertCalled(); 1.179 + } 1.180 + 1.181 + public void testPerformTimeoutWaitInThread() { 1.182 + waitHelper.performWait(SHORT_WAIT, inThread(performNotifyRunnable())); 1.183 + assertCalled(); 1.184 + } 1.185 + 1.186 + public void testPerformTimeoutWaitInThreadPool() { 1.187 + waitHelper.performWait(SHORT_WAIT, inThreadPool(performNotifyRunnable())); 1.188 + assertCalled(); 1.189 + } 1.190 + 1.191 + public void testPerformErrorWaitInThread() { 1.192 + expectAssertionFailedError(inThread(performNotifyErrorRunnable())); 1.193 + assertBothCalled(); 1.194 + } 1.195 + 1.196 + public void testPerformErrorWaitInThreadPool() { 1.197 + expectAssertionFailedError(inThreadPool(performNotifyErrorRunnable())); 1.198 + assertBothCalled(); 1.199 + } 1.200 + 1.201 + public void testPerformErrorTimeoutWaitInThread() { 1.202 + expectAssertionFailedErrorAfterDelay(SHORT_WAIT, inThread(performNotifyErrorRunnable())); 1.203 + assertBothCalled(); 1.204 + } 1.205 + 1.206 + public void testPerformErrorTimeoutWaitInThreadPool() { 1.207 + expectAssertionFailedErrorAfterDelay(SHORT_WAIT, inThreadPool(performNotifyErrorRunnable())); 1.208 + assertBothCalled(); 1.209 + } 1.210 + 1.211 + public void testTimeout() { 1.212 + try { 1.213 + waitHelper.performWait(SHORT_WAIT, performNothingRunnable()); 1.214 + } catch (TimeoutError e) { 1.215 + setPerformNotifyErrorCalled(); 1.216 + assertEquals(SHORT_WAIT, e.waitTimeInMillis); 1.217 + } 1.218 + assertErrorCalled(); 1.219 + } 1.220 + 1.221 + /** 1.222 + * This will pass. The sequence in the main thread is: 1.223 + * - A short delay. 1.224 + * - performNotify is called. 1.225 + * - performWait is called and immediately finds that performNotify was called before. 1.226 + */ 1.227 + public void testDelay() { 1.228 + try { 1.229 + waitHelper.performWait(1, performNotifyAfterDelayRunnable(SHORT_WAIT)); 1.230 + } catch (AssertionFailedError e) { 1.231 + setPerformNotifyErrorCalled(); 1.232 + assertTrue(e.getMessage(), e.getMessage().contains("TIMEOUT")); 1.233 + } 1.234 + assertCalled(); 1.235 + } 1.236 + 1.237 + public Runnable performNotifyMultipleTimesRunnable() { 1.238 + return new Runnable() { 1.239 + public void run() { 1.240 + waitHelper.performNotify(); 1.241 + setPerformNotifyCalled(); 1.242 + waitHelper.performNotify(); 1.243 + } 1.244 + }; 1.245 + } 1.246 + 1.247 + public void testPerformNotifyMultipleTimesFails() { 1.248 + try { 1.249 + waitHelper.performWait(NO_WAIT, performNotifyMultipleTimesRunnable()); // Not run on thread, so runnable executes before performWait looks for notifications. 1.250 + } catch (WaitHelper.MultipleNotificationsError e) { 1.251 + setPerformNotifyErrorCalled(); 1.252 + } 1.253 + assertBothCalled(); 1.254 + assertFalse(waitHelper.isIdle()); // First perform notify should be hanging around. 1.255 + waitHelper.performWait(NO_WAIT, performNothingRunnable()); 1.256 + } 1.257 + 1.258 + public void testNestedWaitsAndNotifies() { 1.259 + waitHelper.performWait(new Runnable() { 1.260 + @Override 1.261 + public void run() { 1.262 + waitHelper.performWait(new Runnable() { 1.263 + public void run() { 1.264 + setPerformNotifyCalled(); 1.265 + waitHelper.performNotify(); 1.266 + } 1.267 + }); 1.268 + setPerformNotifyErrorCalled(); 1.269 + waitHelper.performNotify(); 1.270 + } 1.271 + }); 1.272 + assertBothCalled(); 1.273 + } 1.274 + 1.275 + public void testAssertIsReported() { 1.276 + try { 1.277 + waitHelper.performWait(1, new Runnable() { 1.278 + @Override 1.279 + public void run() { 1.280 + assertTrue("unique identifier", false); 1.281 + } 1.282 + }); 1.283 + } catch (AssertionFailedError e) { 1.284 + setPerformNotifyErrorCalled(); 1.285 + assertTrue(e.getMessage(), e.getMessage().contains("unique identifier")); 1.286 + } 1.287 + assertErrorCalled(); 1.288 + } 1.289 + 1.290 + /** 1.291 + * The inner wait will timeout, but the outer wait will succeed. The sequence in the helper thread is: 1.292 + * - A short delay. 1.293 + * - performNotify is called. 1.294 + * 1.295 + * The sequence in the main thread is: 1.296 + * - performWait is called and times out because the helper thread does not call 1.297 + * performNotify quickly enough. 1.298 + */ 1.299 + public void testDelayInThread() throws InterruptedException { 1.300 + waitHelper.performWait(new Runnable() { 1.301 + @Override 1.302 + public void run() { 1.303 + try { 1.304 + waitHelper.performWait(NO_WAIT, inThread(new Runnable() { 1.305 + public void run() { 1.306 + try { 1.307 + Thread.sleep(SHORT_WAIT); 1.308 + } catch (InterruptedException e) { 1.309 + fail("Interrupted."); 1.310 + } 1.311 + 1.312 + setPerformNotifyCalled(); 1.313 + waitHelper.performNotify(); 1.314 + } 1.315 + })); 1.316 + } catch (WaitHelper.TimeoutError e) { 1.317 + setPerformNotifyErrorCalled(); 1.318 + assertEquals(NO_WAIT, e.waitTimeInMillis); 1.319 + } 1.320 + } 1.321 + }); 1.322 + assertBothCalled(); 1.323 + } 1.324 + 1.325 + /** 1.326 + * The inner wait will timeout, but the outer wait will succeed. The sequence in the helper thread is: 1.327 + * - A short delay. 1.328 + * - performNotify is called. 1.329 + * 1.330 + * The sequence in the main thread is: 1.331 + * - performWait is called and times out because the helper thread does not call 1.332 + * performNotify quickly enough. 1.333 + */ 1.334 + public void testDelayInThreadPool() throws InterruptedException { 1.335 + waitHelper.performWait(new Runnable() { 1.336 + @Override 1.337 + public void run() { 1.338 + try { 1.339 + waitHelper.performWait(NO_WAIT, inThreadPool(new Runnable() { 1.340 + public void run() { 1.341 + try { 1.342 + Thread.sleep(SHORT_WAIT); 1.343 + } catch (InterruptedException e) { 1.344 + fail("Interrupted."); 1.345 + } 1.346 + 1.347 + setPerformNotifyCalled(); 1.348 + waitHelper.performNotify(); 1.349 + } 1.350 + })); 1.351 + } catch (WaitHelper.TimeoutError e) { 1.352 + setPerformNotifyErrorCalled(); 1.353 + assertEquals(NO_WAIT, e.waitTimeInMillis); 1.354 + } 1.355 + } 1.356 + }); 1.357 + assertBothCalled(); 1.358 + } 1.359 +}