19c747ac625d212a10176082fba6b087035f5d144Hugo Hudson/*
29c747ac625d212a10176082fba6b087035f5d144Hugo Hudson * Copyright (C) 2011 The Android Open Source Project
39c747ac625d212a10176082fba6b087035f5d144Hugo Hudson *
49c747ac625d212a10176082fba6b087035f5d144Hugo Hudson * Licensed under the Apache License, Version 2.0 (the "License");
59c747ac625d212a10176082fba6b087035f5d144Hugo Hudson * you may not use this file except in compliance with the License.
69c747ac625d212a10176082fba6b087035f5d144Hugo Hudson * You may obtain a copy of the License at
79c747ac625d212a10176082fba6b087035f5d144Hugo Hudson *
89c747ac625d212a10176082fba6b087035f5d144Hugo Hudson *      http://www.apache.org/licenses/LICENSE-2.0
99c747ac625d212a10176082fba6b087035f5d144Hugo Hudson *
109c747ac625d212a10176082fba6b087035f5d144Hugo Hudson * Unless required by applicable law or agreed to in writing, software
119c747ac625d212a10176082fba6b087035f5d144Hugo Hudson * distributed under the License is distributed on an "AS IS" BASIS,
129c747ac625d212a10176082fba6b087035f5d144Hugo Hudson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139c747ac625d212a10176082fba6b087035f5d144Hugo Hudson * See the License for the specific language governing permissions and
149c747ac625d212a10176082fba6b087035f5d144Hugo Hudson * limitations under the License.
159c747ac625d212a10176082fba6b087035f5d144Hugo Hudson */
169c747ac625d212a10176082fba6b087035f5d144Hugo Hudson
179c747ac625d212a10176082fba6b087035f5d144Hugo Hudsonpackage com.android.contacts.util;
189c747ac625d212a10176082fba6b087035f5d144Hugo Hudson
19c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudsonimport static com.google.common.base.Preconditions.checkNotNull;
20c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudsonimport static com.google.common.base.Preconditions.checkState;
21c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson
229c747ac625d212a10176082fba6b087035f5d144Hugo Hudsonimport com.google.common.base.Preconditions;
239c747ac625d212a10176082fba6b087035f5d144Hugo Hudsonimport com.google.common.collect.Lists;
249c747ac625d212a10176082fba6b087035f5d144Hugo Hudson
259c747ac625d212a10176082fba6b087035f5d144Hugo Hudsonimport android.app.Instrumentation;
269c747ac625d212a10176082fba6b087035f5d144Hugo Hudsonimport android.os.AsyncTask;
279c747ac625d212a10176082fba6b087035f5d144Hugo Hudson
289c747ac625d212a10176082fba6b087035f5d144Hugo Hudsonimport junit.framework.Assert;
299c747ac625d212a10176082fba6b087035f5d144Hugo Hudson
309c747ac625d212a10176082fba6b087035f5d144Hugo Hudsonimport java.util.Iterator;
319c747ac625d212a10176082fba6b087035f5d144Hugo Hudsonimport java.util.List;
329c747ac625d212a10176082fba6b087035f5d144Hugo Hudsonimport java.util.concurrent.CountDownLatch;
339c747ac625d212a10176082fba6b087035f5d144Hugo Hudsonimport java.util.concurrent.Executor;
349c747ac625d212a10176082fba6b087035f5d144Hugo Hudsonimport java.util.concurrent.TimeUnit;
359c747ac625d212a10176082fba6b087035f5d144Hugo Hudson
369c747ac625d212a10176082fba6b087035f5d144Hugo Hudsonimport javax.annotation.concurrent.GuardedBy;
379c747ac625d212a10176082fba6b087035f5d144Hugo Hudsonimport javax.annotation.concurrent.ThreadSafe;
389c747ac625d212a10176082fba6b087035f5d144Hugo Hudson
399c747ac625d212a10176082fba6b087035f5d144Hugo Hudson/**
409c747ac625d212a10176082fba6b087035f5d144Hugo Hudson * Test implementation of AsyncTaskExecutor.
419c747ac625d212a10176082fba6b087035f5d144Hugo Hudson * <p>
429c747ac625d212a10176082fba6b087035f5d144Hugo Hudson * This class is thread-safe. As per the contract of the AsyncTaskExecutor, the submit methods must
439c747ac625d212a10176082fba6b087035f5d144Hugo Hudson * be called from the main ui thread, however the other public methods may be called from any thread
449c747ac625d212a10176082fba6b087035f5d144Hugo Hudson * (most commonly the test thread).
459c747ac625d212a10176082fba6b087035f5d144Hugo Hudson * <p>
469c747ac625d212a10176082fba6b087035f5d144Hugo Hudson * Tasks submitted to this executor will not be run immediately. Rather they will be stored in a
479c747ac625d212a10176082fba6b087035f5d144Hugo Hudson * list of submitted tasks, where they can be examined. They can also be run on-demand using the run
489c747ac625d212a10176082fba6b087035f5d144Hugo Hudson * methods, so that different ordering of AsyncTask execution can be simulated.
49c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson * <p>
50c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson * The onPreExecute method of the submitted AsyncTask will be called synchronously during the
51c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson * call to {@link #submit(Object, AsyncTask, Object...)}.
529c747ac625d212a10176082fba6b087035f5d144Hugo Hudson */
539c747ac625d212a10176082fba6b087035f5d144Hugo Hudson@ThreadSafe
549c747ac625d212a10176082fba6b087035f5d144Hugo Hudsonpublic class FakeAsyncTaskExecutor implements AsyncTaskExecutor {
559c747ac625d212a10176082fba6b087035f5d144Hugo Hudson    private static final long DEFAULT_TIMEOUT_MS = 10000;
569c747ac625d212a10176082fba6b087035f5d144Hugo Hudson
579c747ac625d212a10176082fba6b087035f5d144Hugo Hudson    /** The maximum length of time in ms to wait for tasks to execute during tests. */
589c747ac625d212a10176082fba6b087035f5d144Hugo Hudson    private final long mTimeoutMs = DEFAULT_TIMEOUT_MS;
599c747ac625d212a10176082fba6b087035f5d144Hugo Hudson
609c747ac625d212a10176082fba6b087035f5d144Hugo Hudson    private final Object mLock = new Object();
61c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson    @GuardedBy("mLock") private final List<SubmittedTask> mSubmittedTasks = Lists.newArrayList();
629c747ac625d212a10176082fba6b087035f5d144Hugo Hudson
63c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson    private final DelayedExecutor mBlockingExecutor = new DelayedExecutor();
649c747ac625d212a10176082fba6b087035f5d144Hugo Hudson    private final Instrumentation mInstrumentation;
659c747ac625d212a10176082fba6b087035f5d144Hugo Hudson
669c747ac625d212a10176082fba6b087035f5d144Hugo Hudson    /** Create a fake AsyncTaskExecutor for use in unit tests. */
679c747ac625d212a10176082fba6b087035f5d144Hugo Hudson    public FakeAsyncTaskExecutor(Instrumentation instrumentation) {
68c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson        mInstrumentation = checkNotNull(instrumentation);
699c747ac625d212a10176082fba6b087035f5d144Hugo Hudson    }
709c747ac625d212a10176082fba6b087035f5d144Hugo Hudson
719c747ac625d212a10176082fba6b087035f5d144Hugo Hudson    /** Encapsulates an async task with the params and identifier it was submitted with. */
72c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson    public interface SubmittedTask {
73c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson        Runnable getRunnable();
749c747ac625d212a10176082fba6b087035f5d144Hugo Hudson        Object getIdentifier();
75c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson        AsyncTask<?, ?, ?> getAsyncTask();
769c747ac625d212a10176082fba6b087035f5d144Hugo Hudson    }
779c747ac625d212a10176082fba6b087035f5d144Hugo Hudson
78c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson    private static final class SubmittedTaskImpl implements SubmittedTask {
799c747ac625d212a10176082fba6b087035f5d144Hugo Hudson        private final Object mIdentifier;
80c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson        private final Runnable mRunnable;
81c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson        private final AsyncTask<?, ?, ?> mAsyncTask;
829c747ac625d212a10176082fba6b087035f5d144Hugo Hudson
83c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson        public SubmittedTaskImpl(Object identifier, Runnable runnable,
84c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson                AsyncTask<?, ?, ?> asyncTask) {
859c747ac625d212a10176082fba6b087035f5d144Hugo Hudson            mIdentifier = identifier;
86c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson            mRunnable = runnable;
87c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson            mAsyncTask = asyncTask;
889c747ac625d212a10176082fba6b087035f5d144Hugo Hudson        }
899c747ac625d212a10176082fba6b087035f5d144Hugo Hudson
909c747ac625d212a10176082fba6b087035f5d144Hugo Hudson        @Override
919c747ac625d212a10176082fba6b087035f5d144Hugo Hudson        public Object getIdentifier() {
929c747ac625d212a10176082fba6b087035f5d144Hugo Hudson            return mIdentifier;
939c747ac625d212a10176082fba6b087035f5d144Hugo Hudson        }
949c747ac625d212a10176082fba6b087035f5d144Hugo Hudson
959c747ac625d212a10176082fba6b087035f5d144Hugo Hudson        @Override
96c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson        public Runnable getRunnable() {
97c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson            return mRunnable;
989c747ac625d212a10176082fba6b087035f5d144Hugo Hudson        }
999c747ac625d212a10176082fba6b087035f5d144Hugo Hudson
1009c747ac625d212a10176082fba6b087035f5d144Hugo Hudson        @Override
101c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson        public AsyncTask<?, ?, ?> getAsyncTask() {
102c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson            return mAsyncTask;
1039c747ac625d212a10176082fba6b087035f5d144Hugo Hudson        }
1049c747ac625d212a10176082fba6b087035f5d144Hugo Hudson
1059c747ac625d212a10176082fba6b087035f5d144Hugo Hudson        @Override
1069c747ac625d212a10176082fba6b087035f5d144Hugo Hudson        public String toString() {
1079c747ac625d212a10176082fba6b087035f5d144Hugo Hudson            return "SubmittedTaskImpl [mIdentifier=" + mIdentifier + "]";
1089c747ac625d212a10176082fba6b087035f5d144Hugo Hudson        }
1099c747ac625d212a10176082fba6b087035f5d144Hugo Hudson    }
1109c747ac625d212a10176082fba6b087035f5d144Hugo Hudson
111c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson    private class DelayedExecutor implements Executor {
112c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson        private final Object mNextLock = new Object();
113c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson        @GuardedBy("mNextLock") private Object mNextIdentifier;
114c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson        @GuardedBy("mNextLock") private AsyncTask<?, ?, ?> mNextTask;
115c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson
116c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson        @Override
117c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson        public void execute(Runnable command) {
118c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson            synchronized (mNextLock) {
119c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson                mSubmittedTasks.add(new SubmittedTaskImpl(mNextIdentifier,
120c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson                        command, checkNotNull(mNextTask)));
121c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson                mNextIdentifier = null;
122c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson                mNextTask = null;
123c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson            }
124c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson        }
125c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson
126c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson        public <T> AsyncTask<T, ?, ?> submit(Object identifier,
127c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson                AsyncTask<T, ?, ?> task, T... params) {
128c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson            synchronized (mNextLock) {
129c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson                checkState(mNextIdentifier == null);
130c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson                checkState(mNextTask == null);
131c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson                mNextIdentifier = identifier;
132c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson                mNextTask = checkNotNull(task, "Already had a valid task.\n"
133c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson                        + "Are you calling AsyncTaskExecutor.submit(...) from within the "
134c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson                        + "onPreExecute() method of another task being submitted?\n"
135c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson                        + "Sorry!  Not that's not supported.");
136c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson            }
137c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson            return task.executeOnExecutor(this, params);
138c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson        }
139c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson    }
140c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson
1419c747ac625d212a10176082fba6b087035f5d144Hugo Hudson    @Override
1429c747ac625d212a10176082fba6b087035f5d144Hugo Hudson    public <T> AsyncTask<T, ?, ?> submit(Object identifier, AsyncTask<T, ?, ?> task, T... params) {
1439c747ac625d212a10176082fba6b087035f5d144Hugo Hudson        AsyncTaskExecutors.checkCalledFromUiThread();
144c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson        return mBlockingExecutor.submit(identifier, task, params);
1459c747ac625d212a10176082fba6b087035f5d144Hugo Hudson    }
1469c747ac625d212a10176082fba6b087035f5d144Hugo Hudson
1479c747ac625d212a10176082fba6b087035f5d144Hugo Hudson    /**
1489c747ac625d212a10176082fba6b087035f5d144Hugo Hudson     * Runs a single task matching the given identifier.
1499c747ac625d212a10176082fba6b087035f5d144Hugo Hudson     * <p>
1509c747ac625d212a10176082fba6b087035f5d144Hugo Hudson     * Removes the matching task from the list of submitted tasks, then runs it. The executor used
1519c747ac625d212a10176082fba6b087035f5d144Hugo Hudson     * to execute this async task will be a same-thread executor.
1529c747ac625d212a10176082fba6b087035f5d144Hugo Hudson     * <p>
1539c747ac625d212a10176082fba6b087035f5d144Hugo Hudson     * Fails if there was not exactly one task matching the given identifier.
1549c747ac625d212a10176082fba6b087035f5d144Hugo Hudson     * <p>
1559c747ac625d212a10176082fba6b087035f5d144Hugo Hudson     * This method blocks until the AsyncTask has completely finished executing.
1569c747ac625d212a10176082fba6b087035f5d144Hugo Hudson     */
157c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson    public void runTask(Object identifier) throws InterruptedException {
158c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson        List<SubmittedTask> tasks = getSubmittedTasksByIdentifier(identifier, true);
1599c747ac625d212a10176082fba6b087035f5d144Hugo Hudson        Assert.assertEquals("Expected one task " + identifier + ", got " + tasks, 1, tasks.size());
1609c747ac625d212a10176082fba6b087035f5d144Hugo Hudson        runTask(tasks.get(0));
1619c747ac625d212a10176082fba6b087035f5d144Hugo Hudson    }
1629c747ac625d212a10176082fba6b087035f5d144Hugo Hudson
1639c747ac625d212a10176082fba6b087035f5d144Hugo Hudson    /**
1649c747ac625d212a10176082fba6b087035f5d144Hugo Hudson     * Runs all tasks whose identifier matches the given identifier.
1659c747ac625d212a10176082fba6b087035f5d144Hugo Hudson     * <p>
1669c747ac625d212a10176082fba6b087035f5d144Hugo Hudson     * Removes all matching tasks from the list of submitted tasks, and runs them. The executor used
1679c747ac625d212a10176082fba6b087035f5d144Hugo Hudson     * to execute these async tasks will be a same-thread executor.
1689c747ac625d212a10176082fba6b087035f5d144Hugo Hudson     * <p>
1699c747ac625d212a10176082fba6b087035f5d144Hugo Hudson     * Fails if there were no tasks matching the given identifier.
1709c747ac625d212a10176082fba6b087035f5d144Hugo Hudson     * <p>
1719c747ac625d212a10176082fba6b087035f5d144Hugo Hudson     * This method blocks until the AsyncTask objects have completely finished executing.
1729c747ac625d212a10176082fba6b087035f5d144Hugo Hudson     */
173c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson    public void runAllTasks(Object identifier) throws InterruptedException {
174c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson        List<SubmittedTask> tasks = getSubmittedTasksByIdentifier(identifier, true);
1759c747ac625d212a10176082fba6b087035f5d144Hugo Hudson        Assert.assertTrue("There were no tasks with identifier " + identifier, tasks.size() > 0);
176c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson        for (SubmittedTask task : tasks) {
1779c747ac625d212a10176082fba6b087035f5d144Hugo Hudson            runTask(task);
1789c747ac625d212a10176082fba6b087035f5d144Hugo Hudson        }
1799c747ac625d212a10176082fba6b087035f5d144Hugo Hudson    }
1809c747ac625d212a10176082fba6b087035f5d144Hugo Hudson
1819c747ac625d212a10176082fba6b087035f5d144Hugo Hudson    /**
182c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson     * Executes a single {@link SubmittedTask}.
1839c747ac625d212a10176082fba6b087035f5d144Hugo Hudson     * <p>
1849c747ac625d212a10176082fba6b087035f5d144Hugo Hudson     * Blocks until the task has completed running.
1859c747ac625d212a10176082fba6b087035f5d144Hugo Hudson     */
186c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson    private <T> void runTask(final SubmittedTask submittedTask) throws InterruptedException {
187c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson        submittedTask.getRunnable().run();
1889c747ac625d212a10176082fba6b087035f5d144Hugo Hudson        // Block until the onPostExecute or onCancelled has finished.
1899c747ac625d212a10176082fba6b087035f5d144Hugo Hudson        // Unfortunately we can't be sure when the AsyncTask will have posted its result handling
1909c747ac625d212a10176082fba6b087035f5d144Hugo Hudson        // code to the main ui thread, the best we can do is wait for the Status to be FINISHED.
1919c747ac625d212a10176082fba6b087035f5d144Hugo Hudson        final CountDownLatch latch = new CountDownLatch(1);
1929c747ac625d212a10176082fba6b087035f5d144Hugo Hudson        class AsyncTaskHasFinishedRunnable implements Runnable {
1939c747ac625d212a10176082fba6b087035f5d144Hugo Hudson            @Override
1949c747ac625d212a10176082fba6b087035f5d144Hugo Hudson            public void run() {
195c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson                if (submittedTask.getAsyncTask().getStatus() == AsyncTask.Status.FINISHED) {
1969c747ac625d212a10176082fba6b087035f5d144Hugo Hudson                    latch.countDown();
1979c747ac625d212a10176082fba6b087035f5d144Hugo Hudson                } else {
1989c747ac625d212a10176082fba6b087035f5d144Hugo Hudson                    mInstrumentation.waitForIdle(this);
1999c747ac625d212a10176082fba6b087035f5d144Hugo Hudson                }
2009c747ac625d212a10176082fba6b087035f5d144Hugo Hudson            }
2019c747ac625d212a10176082fba6b087035f5d144Hugo Hudson        }
2029c747ac625d212a10176082fba6b087035f5d144Hugo Hudson        mInstrumentation.waitForIdle(new AsyncTaskHasFinishedRunnable());
2039c747ac625d212a10176082fba6b087035f5d144Hugo Hudson        Assert.assertTrue(latch.await(mTimeoutMs, TimeUnit.MILLISECONDS));
2049c747ac625d212a10176082fba6b087035f5d144Hugo Hudson    }
2059c747ac625d212a10176082fba6b087035f5d144Hugo Hudson
206c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson    private List<SubmittedTask> getSubmittedTasksByIdentifier(
207c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson            Object identifier, boolean remove) {
2089c747ac625d212a10176082fba6b087035f5d144Hugo Hudson        Preconditions.checkNotNull(identifier, "can't lookup tasks by 'null' identifier");
209c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson        List<SubmittedTask> results = Lists.newArrayList();
2109c747ac625d212a10176082fba6b087035f5d144Hugo Hudson        synchronized (mLock) {
211c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson            Iterator<SubmittedTask> iter = mSubmittedTasks.iterator();
2129c747ac625d212a10176082fba6b087035f5d144Hugo Hudson            while (iter.hasNext()) {
213c06f21bd72611b335f4d34586f656225fb02f0fbHugo Hudson                SubmittedTask task = iter.next();
2149c747ac625d212a10176082fba6b087035f5d144Hugo Hudson                if (identifier.equals(task.getIdentifier())) {
2159c747ac625d212a10176082fba6b087035f5d144Hugo Hudson                    results.add(task);
2169c747ac625d212a10176082fba6b087035f5d144Hugo Hudson                    iter.remove();
2179c747ac625d212a10176082fba6b087035f5d144Hugo Hudson                }
2189c747ac625d212a10176082fba6b087035f5d144Hugo Hudson            }
2199c747ac625d212a10176082fba6b087035f5d144Hugo Hudson        }
2209c747ac625d212a10176082fba6b087035f5d144Hugo Hudson        return results;
2219c747ac625d212a10176082fba6b087035f5d144Hugo Hudson    }
2229c747ac625d212a10176082fba6b087035f5d144Hugo Hudson
2239c747ac625d212a10176082fba6b087035f5d144Hugo Hudson    /** Get a factory that will return this instance - useful for testing. */
2249c747ac625d212a10176082fba6b087035f5d144Hugo Hudson    public AsyncTaskExecutors.AsyncTaskExecutorFactory getFactory() {
2259c747ac625d212a10176082fba6b087035f5d144Hugo Hudson        return new AsyncTaskExecutors.AsyncTaskExecutorFactory() {
2269c747ac625d212a10176082fba6b087035f5d144Hugo Hudson            @Override
2279c747ac625d212a10176082fba6b087035f5d144Hugo Hudson            public AsyncTaskExecutor createAsyncTaskExeuctor() {
2289c747ac625d212a10176082fba6b087035f5d144Hugo Hudson                return FakeAsyncTaskExecutor.this;
2299c747ac625d212a10176082fba6b087035f5d144Hugo Hudson            }
2309c747ac625d212a10176082fba6b087035f5d144Hugo Hudson        };
2319c747ac625d212a10176082fba6b087035f5d144Hugo Hudson    }
2329c747ac625d212a10176082fba6b087035f5d144Hugo Hudson}
233