Commit 1d325446 authored by Changwan Ryu's avatar Changwan Ryu Committed by Commit Bot

Add tests around cancel with mayInterruptIfRunning=true

When mayInterruptIfRunning is true, and if task is already running,
then cancel() should interrupt the task.

These tests verify the behavior.

Also adding some more checks and comments.

Bug: 1021775
Change-Id: I5a51c09a9bf1731acf84280c8e9e40aef429732f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1903754Reviewed-by: default avatarSam Maier <smaier@chromium.org>
Reviewed-by: default avatarRichard Coles <torne@chromium.org>
Commit-Queue: Changwan Ryu <changwan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#713597}
parent 799fcff0
...@@ -4,11 +4,14 @@ ...@@ -4,11 +4,14 @@
package org.chromium.base.task; package org.chromium.base.task;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import android.support.test.filters.SmallTest; import android.support.test.filters.SmallTest;
import org.junit.After;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule; import org.junit.Rule;
...@@ -40,6 +43,9 @@ public class AsyncTaskThreadTest { ...@@ -40,6 +43,9 @@ public class AsyncTaskThreadTest {
private static class BlockAndGetFeedDataTask extends AsyncTask<Boolean> { private static class BlockAndGetFeedDataTask extends AsyncTask<Boolean> {
private LinkedBlockingQueue<Boolean> mIncomingQueue = new LinkedBlockingQueue<Boolean>(); private LinkedBlockingQueue<Boolean> mIncomingQueue = new LinkedBlockingQueue<Boolean>();
private LinkedBlockingQueue<Boolean> mOutgoingQueue = new LinkedBlockingQueue<Boolean>(); private LinkedBlockingQueue<Boolean> mOutgoingQueue = new LinkedBlockingQueue<Boolean>();
private LinkedBlockingQueue<Boolean> mInterruptedExceptionQueue =
new LinkedBlockingQueue<Boolean>();
private Boolean mPostExecuteResult;
@Override @Override
protected Boolean doInBackground() { protected Boolean doInBackground() {
...@@ -51,6 +57,7 @@ public class AsyncTaskThreadTest { ...@@ -51,6 +57,7 @@ public class AsyncTaskThreadTest {
@Override @Override
protected void onPostExecute(Boolean result) { protected void onPostExecute(Boolean result) {
if (DEBUG) Log.i(TAG, "onPostExecute: " + result); if (DEBUG) Log.i(TAG, "onPostExecute: " + result);
mPostExecuteResult = result;
} }
public void feedData(Boolean data) { public void feedData(Boolean data) {
...@@ -61,6 +68,7 @@ public class AsyncTaskThreadTest { ...@@ -61,6 +68,7 @@ public class AsyncTaskThreadTest {
try { try {
return mIncomingQueue.poll(3, TimeUnit.SECONDS); return mIncomingQueue.poll(3, TimeUnit.SECONDS);
} catch (InterruptedException e) { } catch (InterruptedException e) {
mInterruptedExceptionQueue.add(true);
return false; return false;
} }
} }
...@@ -68,8 +76,17 @@ public class AsyncTaskThreadTest { ...@@ -68,8 +76,17 @@ public class AsyncTaskThreadTest {
public void blockUntilDoInBackgroundStarts() throws Exception { public void blockUntilDoInBackgroundStarts() throws Exception {
mOutgoingQueue.poll(3, TimeUnit.SECONDS); mOutgoingQueue.poll(3, TimeUnit.SECONDS);
} }
public Boolean getPostExecuteResult() {
return mPostExecuteResult;
}
public LinkedBlockingQueue<Boolean> getInterruptedExceptionQueue() {
return mInterruptedExceptionQueue;
}
} }
private final BlockAndGetFeedDataTask mTask = new BlockAndGetFeedDataTask();
private final RoboExecutorService mRoboExecutorService = new RoboExecutorService(); private final RoboExecutorService mRoboExecutorService = new RoboExecutorService();
private final Scheduler mBackgroundScheduler = Robolectric.getBackgroundThreadScheduler(); private final Scheduler mBackgroundScheduler = Robolectric.getBackgroundThreadScheduler();
...@@ -85,42 +102,96 @@ public class AsyncTaskThreadTest { ...@@ -85,42 +102,96 @@ public class AsyncTaskThreadTest {
mBackgroundScheduler.pause(); mBackgroundScheduler.pause();
} }
@After
public void tearDown() {
// No unexpected interrupted exception.
assertNull(mTask.getInterruptedExceptionQueue().poll());
Assert.assertTrue(mRoboExecutorService.shutdownNow().isEmpty());
}
@Test @Test
@SmallTest @SmallTest
public void testCancel_ReturnsFalseOnceTaskFinishes() throws Exception { public void testCancel_ReturnsFalseOnceTaskFinishes() throws Exception {
BlockAndGetFeedDataTask task = new BlockAndGetFeedDataTask();
// This test requires robo executor service such that we can run // This test requires robo executor service such that we can run
// one background task. // one background task.
task.executeOnExecutor(mRoboExecutorService); mTask.executeOnExecutor(mRoboExecutorService);
// Ensure that the background thread is not blocked.
mTask.feedData(true);
// We feed the background thread, then cancel.
task.feedData(true);
mBackgroundScheduler.runOneTask(); mBackgroundScheduler.runOneTask();
// Cannot cancel. The task is already run. // Cannot cancel. The task is already run.
assertFalse(task.cancel(false)); assertFalse(mTask.cancel(false /* mayInterruptIfRunning */));
assertTrue(task.get()); assertTrue(mTask.get());
assertEquals(Boolean.valueOf(true), mTask.getPostExecuteResult());
} }
@Test @Test
@SmallTest @SmallTest
public void testCancel_CanReturnTrueEvenAfterTaskStarts() throws Exception { public void testCancel_CanReturnTrueEvenAfterTaskStarts() throws Exception {
BlockAndGetFeedDataTask task = new BlockAndGetFeedDataTask(); mTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
// The task has started. // Wait until the task is started. Note that data is not yet fed.
task.blockUntilDoInBackgroundStarts(); mTask.blockUntilDoInBackgroundStarts();
// This reflects FutureTask#cancel() behavior. Note that the task is // This reflects FutureTask#cancel() behavior. Note that the task is
// started but cancel can still return true. // started but cancel can still return true.
assertTrue(task.cancel(false)); assertTrue(mTask.cancel(false /* mayInterruptIfRunning */));
task.feedData(true);
// Continue the task.
mTask.feedData(true);
// get() will raise an exception although the task is started.
try {
mTask.get();
Assert.fail();
} catch (CancellationException e) {
// expected
}
assertNull(mTask.getPostExecuteResult()); // onPostExecute did not run.
}
@Test
@SmallTest
public void testCancel_MayInterrupt_ReturnsFalseOnceTaskFinishes() throws Exception {
// This test requires robo executor service such that we can run
// one background task.
mTask.executeOnExecutor(mRoboExecutorService);
// Ensure that the background thread is not blocked.
mTask.feedData(true);
mBackgroundScheduler.runOneTask();
// Cannot cancel. The task is already run.
assertFalse(mTask.cancel(true /* mayInterruptIfRunning */));
assertTrue(mTask.get());
assertEquals(Boolean.valueOf(true), mTask.getPostExecuteResult());
}
@Test
@SmallTest
public void testCancel_MayInterrupt_TaskIsInterrupted() throws Exception {
mTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
// Wait until the task is started. Note that data is not yet fed.
mTask.blockUntilDoInBackgroundStarts();
// Cancel and interrupt the current task.
assertTrue(mTask.cancel(true /* mayInterruptIfRunning */));
// Ensure that the background thread is not blocked.
mTask.feedData(true);
// get() will raise an exception although the task is run. // get() will raise an exception although the task is started.
try { try {
task.get(); mTask.get();
Assert.fail(); Assert.fail();
} catch (CancellationException e) { } catch (CancellationException e) {
// expected // expected
} }
assertNull(mTask.getPostExecuteResult()); // onPostExecute did not run.
// Task was interrupted.
assertEquals(Boolean.valueOf(true), mTask.getInterruptedExceptionQueue().poll());
} }
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment