summaryrefslogtreecommitdiff
path: root/tests/src/com/android/dialer/util/FakeAsyncTaskExecutor.java
diff options
context:
space:
mode:
authorChiao Cheng <chiaocheng@google.com>2012-08-17 16:59:12 -0700
committerChiao Cheng <chiaocheng@google.com>2012-08-21 13:31:19 -0700
commit94b10b530c0fc297e2974e57e094c500d3ee6003 (patch)
treeb74d663c2663b5db2f6da888081648ce054480f5 /tests/src/com/android/dialer/util/FakeAsyncTaskExecutor.java
parentdab5cd8890c0d0ca9001a13c2197114a4002338a (diff)
Initial move of dialer features from contacts app.
Bug: 6993891 Change-Id: I758ce359ca7e87a1d184303822979318be171921
Diffstat (limited to 'tests/src/com/android/dialer/util/FakeAsyncTaskExecutor.java')
-rw-r--r--tests/src/com/android/dialer/util/FakeAsyncTaskExecutor.java233
1 files changed, 233 insertions, 0 deletions
diff --git a/tests/src/com/android/dialer/util/FakeAsyncTaskExecutor.java b/tests/src/com/android/dialer/util/FakeAsyncTaskExecutor.java
new file mode 100644
index 000000000..064587e4b
--- /dev/null
+++ b/tests/src/com/android/dialer/util/FakeAsyncTaskExecutor.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dialer.util;
+
+import android.app.Instrumentation;
+import android.os.AsyncTask;
+
+import com.android.contacts.util.AsyncTaskExecutor;
+import com.android.contacts.util.AsyncTaskExecutors;
+import com.google.common.collect.Lists;
+
+import junit.framework.Assert;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+
+import javax.annotation.concurrent.GuardedBy;
+import javax.annotation.concurrent.ThreadSafe;
+
+/**
+ * Test implementation of AsyncTaskExecutor.
+ * <p>
+ * This class is thread-safe. As per the contract of the AsyncTaskExecutor, the submit methods must
+ * be called from the main ui thread, however the other public methods may be called from any thread
+ * (most commonly the test thread).
+ * <p>
+ * Tasks submitted to this executor will not be run immediately. Rather they will be stored in a
+ * list of submitted tasks, where they can be examined. They can also be run on-demand using the run
+ * methods, so that different ordering of AsyncTask execution can be simulated.
+ * <p>
+ * The onPreExecute method of the submitted AsyncTask will be called synchronously during the
+ * call to {@link #submit(Object, AsyncTask, Object...)}.
+ */
+@ThreadSafe
+public class FakeAsyncTaskExecutor implements AsyncTaskExecutor {
+ private static final long DEFAULT_TIMEOUT_MS = 10000;
+
+ /** The maximum length of time in ms to wait for tasks to execute during tests. */
+ private final long mTimeoutMs = DEFAULT_TIMEOUT_MS;
+
+ private final Object mLock = new Object();
+ @GuardedBy("mLock") private final List<SubmittedTask> mSubmittedTasks = Lists.newArrayList();
+
+ private final DelayedExecutor mBlockingExecutor = new DelayedExecutor();
+ private final Instrumentation mInstrumentation;
+
+ /** Create a fake AsyncTaskExecutor for use in unit tests. */
+ public FakeAsyncTaskExecutor(Instrumentation instrumentation) {
+ Assert.assertNotNull(instrumentation);
+ mInstrumentation = instrumentation;
+ }
+
+ /** Encapsulates an async task with the params and identifier it was submitted with. */
+ public interface SubmittedTask {
+ Runnable getRunnable();
+ Object getIdentifier();
+ AsyncTask<?, ?, ?> getAsyncTask();
+ }
+
+ private static final class SubmittedTaskImpl implements SubmittedTask {
+ private final Object mIdentifier;
+ private final Runnable mRunnable;
+ private final AsyncTask<?, ?, ?> mAsyncTask;
+
+ public SubmittedTaskImpl(Object identifier, Runnable runnable,
+ AsyncTask<?, ?, ?> asyncTask) {
+ mIdentifier = identifier;
+ mRunnable = runnable;
+ mAsyncTask = asyncTask;
+ }
+
+ @Override
+ public Object getIdentifier() {
+ return mIdentifier;
+ }
+
+ @Override
+ public Runnable getRunnable() {
+ return mRunnable;
+ }
+
+ @Override
+ public AsyncTask<?, ?, ?> getAsyncTask() {
+ return mAsyncTask;
+ }
+
+ @Override
+ public String toString() {
+ return "SubmittedTaskImpl [mIdentifier=" + mIdentifier + "]";
+ }
+ }
+
+ private class DelayedExecutor implements Executor {
+ private final Object mNextLock = new Object();
+ @GuardedBy("mNextLock") private Object mNextIdentifier;
+ @GuardedBy("mNextLock") private AsyncTask<?, ?, ?> mNextTask;
+
+ @Override
+ public void execute(Runnable command) {
+ synchronized (mNextLock) {
+ Assert.assertNotNull(mNextTask);
+ mSubmittedTasks.add(new SubmittedTaskImpl(mNextIdentifier,
+ command, mNextTask));
+ mNextIdentifier = null;
+ mNextTask = null;
+ }
+ }
+
+ public <T> AsyncTask<T, ?, ?> submit(Object identifier,
+ AsyncTask<T, ?, ?> task, T... params) {
+ synchronized (mNextLock) {
+ Assert.assertNull(mNextIdentifier);
+ Assert.assertNull(mNextTask);
+ mNextIdentifier = identifier;
+ Assert.assertNotNull("Already had a valid task.\n"
+ + "Are you calling AsyncTaskExecutor.submit(...) from within the "
+ + "onPreExecute() method of another task being submitted?\n"
+ + "Sorry! Not that's not supported.", task);
+ mNextTask = task;
+ }
+ return task.executeOnExecutor(this, params);
+ }
+ }
+
+ @Override
+ public <T> AsyncTask<T, ?, ?> submit(Object identifier, AsyncTask<T, ?, ?> task, T... params) {
+ AsyncTaskExecutors.checkCalledFromUiThread();
+ return mBlockingExecutor.submit(identifier, task, params);
+ }
+
+ /**
+ * Runs a single task matching the given identifier.
+ * <p>
+ * Removes the matching task from the list of submitted tasks, then runs it. The executor used
+ * to execute this async task will be a same-thread executor.
+ * <p>
+ * Fails if there was not exactly one task matching the given identifier.
+ * <p>
+ * This method blocks until the AsyncTask has completely finished executing.
+ */
+ public void runTask(Object identifier) throws InterruptedException {
+ List<SubmittedTask> tasks = getSubmittedTasksByIdentifier(identifier, true);
+ Assert.assertEquals("Expected one task " + identifier + ", got " + tasks, 1, tasks.size());
+ runTask(tasks.get(0));
+ }
+
+ /**
+ * Runs all tasks whose identifier matches the given identifier.
+ * <p>
+ * Removes all matching tasks from the list of submitted tasks, and runs them. The executor used
+ * to execute these async tasks will be a same-thread executor.
+ * <p>
+ * Fails if there were no tasks matching the given identifier.
+ * <p>
+ * This method blocks until the AsyncTask objects have completely finished executing.
+ */
+ public void runAllTasks(Object identifier) throws InterruptedException {
+ List<SubmittedTask> tasks = getSubmittedTasksByIdentifier(identifier, true);
+ Assert.assertTrue("There were no tasks with identifier " + identifier, tasks.size() > 0);
+ for (SubmittedTask task : tasks) {
+ runTask(task);
+ }
+ }
+
+ /**
+ * Executes a single {@link SubmittedTask}.
+ * <p>
+ * Blocks until the task has completed running.
+ */
+ private <T> void runTask(final SubmittedTask submittedTask) throws InterruptedException {
+ submittedTask.getRunnable().run();
+ // Block until the onPostExecute or onCancelled has finished.
+ // Unfortunately we can't be sure when the AsyncTask will have posted its result handling
+ // code to the main ui thread, the best we can do is wait for the Status to be FINISHED.
+ final CountDownLatch latch = new CountDownLatch(1);
+ class AsyncTaskHasFinishedRunnable implements Runnable {
+ @Override
+ public void run() {
+ if (submittedTask.getAsyncTask().getStatus() == AsyncTask.Status.FINISHED) {
+ latch.countDown();
+ } else {
+ mInstrumentation.waitForIdle(this);
+ }
+ }
+ }
+ mInstrumentation.waitForIdle(new AsyncTaskHasFinishedRunnable());
+ Assert.assertTrue(latch.await(mTimeoutMs, TimeUnit.MILLISECONDS));
+ }
+
+ private List<SubmittedTask> getSubmittedTasksByIdentifier(
+ Object identifier, boolean remove) {
+ Assert.assertNotNull(identifier);
+ List<SubmittedTask> results = Lists.newArrayList();
+ synchronized (mLock) {
+ Iterator<SubmittedTask> iter = mSubmittedTasks.iterator();
+ while (iter.hasNext()) {
+ SubmittedTask task = iter.next();
+ if (identifier.equals(task.getIdentifier())) {
+ results.add(task);
+ iter.remove();
+ }
+ }
+ }
+ return results;
+ }
+
+ /** Get a factory that will return this instance - useful for testing. */
+ public AsyncTaskExecutors.AsyncTaskExecutorFactory getFactory() {
+ return new AsyncTaskExecutors.AsyncTaskExecutorFactory() {
+ @Override
+ public AsyncTaskExecutor createAsyncTaskExeuctor() {
+ return FakeAsyncTaskExecutor.this;
+ }
+ };
+ }
+}