private void assertCacheInvalidAndWithCanceledRM() { Assert.assertFalse(fTestCache.isValid()); try { fTestCache.getData(); Assert.fail("Expected an IllegalStateException"); } catch (IllegalStateException e) { } try { fTestCache.getError(); Assert.fail("Expected an IllegalStateException"); } catch (IllegalStateException e) { } Assert.assertTrue(fRetrieveRm.isCanceled()); }
/** * This test forces a race condition where a client that requested data cancels. While shortly * after a second client starts a new request. The first request's cancel should not interfere * with the second request. */ public void testCancelAfterCompletedRaceCondition() throws InterruptedException, ExecutionException { // Create a client request with a badly behaved cancel implementation. final Callback[] rmBad = new Callback[1]; final boolean qBadCanceled[] = new boolean[] {false}; Query<Integer> qBad = new Query<Integer>() { @Override protected void execute(final DataCallback<Integer> rm) { rmBad[0] = new Callback(rm) { @Override public synchronized void removeCancelListener(ICanceledListener listener) { // Do not add the cancel listener so that the cancel request is not // propagated to the cache. } @Override public void cancel() { if (qBadCanceled[0]) { super.cancel(); } } @Override public synchronized boolean isCanceled() { return qBadCanceled[0]; } @Override public synchronized void done() { // Avoid clearing cancel listeners list }; @Override protected void handleSuccess() { rm.setData(fTestCache.getData()); rm.done(); }; }; fTestCache.wait(rmBad[0]); } }; qBad.invoke(); // Wait until the cache starts data retrieval. waitForRetrieveRm(); // Reset the cache Protocol.invokeAndWait( new Runnable() { public void run() { fRetrieveRm = null; fTestCache.set(null, null, true); fTestCache.reset(); } }); Query<Integer> qGood = new TestQuery(); qGood.invoke(); // Wait until the cache starts data retrieval. waitForRetrieveRm(); qBadCanceled[0] = true; rmBad[0].cancel(); Assert.assertFalse(fRetrieveRm.isCanceled()); // Completed the retrieve RM Protocol.invokeAndWait( new Runnable() { public void run() { fRetrieveRm.setData(1); fRetrieveRm.done(); } }); qGood.get(); assertCacheValidWithData(1); }
public void testCancelWhilePendingWithoutClientNotification() throws InterruptedException, ExecutionException { final boolean canceledCalled[] = new boolean[] {false}; fTestCache = new TestCache() { protected synchronized void canceled() { canceledCalled[0] = true; }; }; // Request data from cache Query<Integer> q = new Query<Integer>() { @Override protected void execute(final DataCallback<Integer> rm) { fTestCache.wait( new Callback(rm) { @Override public synchronized void addCancelListener(ICanceledListener listener) { // Do not add the cancel listener so that the cancel request is not // propagated to the cache. } @Override protected void handleSuccess() { rm.setData(fTestCache.getData()); rm.done(); } }); } }; q.invoke(); // Wait until the cache starts data retrieval. waitForRetrieveRm(); // Cancel the client request q.cancel(true); assertCacheInvalidAndWithCanceledRM(); // AbstractCache.canceled() should be called after isCanceled() // discovers that the client has canceled its request. The canceled() method is // called in a separate dispatch cycle, so we have to wait one cycle of the executor // after is canceled is called. fRetrieveRm.isCanceled(); Protocol.invokeAndWait( new Runnable() { public void run() {} }); Assert.assertTrue(canceledCalled[0]); try { q.get(); Assert.fail("Expected a cancellation exception"); } catch (CancellationException e) { } // Expected exception; // Completed the retrieve RM Protocol.invokeAndWait( new Runnable() { public void run() { fRetrieveRm.setData(1); fRetrieveRm.done(); } }); // Validate that cache didn't accept the result after its RM was canceled assertCacheInvalidAndWithCanceledRM(); }