diff options
author | jcivelli@chromium.org <jcivelli@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-12-13 20:02:38 +0000 |
---|---|---|
committer | jcivelli@chromium.org <jcivelli@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-12-13 20:02:38 +0000 |
commit | b1ac1410fc53c8b30332ebfecff98578941c21c3 (patch) | |
tree | ccc0c743ad08a255358d31c92db5b0ffcba7267e /base/test | |
parent | 094020855126721827342da98992a8c057d1a135 (diff) | |
download | chromium_src-b1ac1410fc53c8b30332ebfecff98578941c21c3.zip chromium_src-b1ac1410fc53c8b30332ebfecff98578941c21c3.tar.gz chromium_src-b1ac1410fc53c8b30332ebfecff98578941c21c3.tar.bz2 |
Adding a new utility class for instrumentation tests.
Some instrumentation tests that exercise class that post tasks require the
message loop to be flushed.
This TestThread provides a way to run that class on a different thread that
takes care of processing all pending events.
BUG=
TEST=
Review URL: https://chromiumcodereview.appspot.com/11473014
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@172942 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/test')
-rw-r--r-- | base/test/android/javatests/src/org/chromium/base/test/util/TestThread.java | 143 |
1 files changed, 143 insertions, 0 deletions
diff --git a/base/test/android/javatests/src/org/chromium/base/test/util/TestThread.java b/base/test/android/javatests/src/org/chromium/base/test/util/TestThread.java new file mode 100644 index 0000000..11e6afd --- /dev/null +++ b/base/test/android/javatests/src/org/chromium/base/test/util/TestThread.java @@ -0,0 +1,143 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.base.test.util; + +import android.os.Handler; +import android.os.Looper; + +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * This class is usefull when writing instrumentation tests that exercise code that posts tasks + * (to the same thread). + * Since the test code is run in a single thread, the posted tasks are never executed. + * The TestThread class lets you run that code on a specific thread synchronously and flush the + * message loop on that thread. + * + * Example of test using this: + * + * public void testMyAwesomeClass() { + * TestThread testThread = new TestThread(); + * testThread.startAndWaitForReadyState(); + * + * testThread.runOnTestThreadSyncAndProcessPendingTasks(new Runnable() { + * @Override + * public void run() { + * MyAwesomeClass.doStuffAsync(); + * } + * }); + * // Once we get there we know doStuffAsync has been executed and all the tasks it posted. + * assertTrue(MyAwesomeClass.stuffWasDone()); + * } + * + * Notes: + * - this is only for tasks posted to the same thread. Anyway if you were posting to a different + * thread, you'd probably need to set that other thread up. + * - this only supports tasks posted using Handler.post(), it won't work with postDelayed and + * postAtTime. + * - if your test instanciates an object and that object is the one doing the posting of tasks, you + * probably want to instanciate it on the test thread as it might create the Handler it posts + * tasks to in the constructor. + */ + +public class TestThread extends Thread { + private Object mThreadReadyLock; + private AtomicBoolean mThreadReady; + private Handler mMainThreadHandler; + private Handler mTestThreadHandler; + + public TestThread() { + mMainThreadHandler = new Handler(); + // We can't use the AtomicBoolean as the lock or findbugs will freak out... + mThreadReadyLock = new Object(); + mThreadReady = new AtomicBoolean(); + } + + @Override + public void run() { + Looper.prepare(); + mTestThreadHandler = new Handler(); + mTestThreadHandler.post(new Runnable() { + @Override + public void run() { + synchronized (mThreadReadyLock) { + mThreadReady.set(true); + mThreadReadyLock.notify(); + } + } + }); + Looper.loop(); + } + + /** + * Starts this TestThread and blocks until it's ready to accept calls. + */ + public void startAndWaitForReadyState() { + checkOnMainThread(); + start(); + synchronized (mThreadReadyLock) { + try { + // Note the mThreadReady and while are not really needed. + // There are there so findbugs don't report warnings. + while (!mThreadReady.get()) { + mThreadReadyLock.wait(); + } + } catch (InterruptedException ie) { + System.err.println("Error starting TestThread."); + ie.printStackTrace(); + } + } + } + + /** + * Runs the passed Runnable synchronously on the TestThread and returns when all pending + * runnables have been excuted. + * Should be called from the main thread. + */ + public void runOnTestThreadSyncAndProcessPendingTasks(Runnable r) { + checkOnMainThread(); + + runOnTestThreadSync(r); + + // Run another task, when it's done it means all pendings tasks have executed. + runOnTestThreadSync(null); + } + + /** + * Runs the passed Runnable on the test thread and blocks until it has finished executing. + * Should be called from the main thread. + * @param r The runnable to be executed. + */ + public void runOnTestThreadSync(final Runnable r) { + checkOnMainThread(); + final Object lock = new Object(); + // Task executed is not really needed since we are only on one thread, it is here to appease + // findbugs. + final AtomicBoolean taskExecuted = new AtomicBoolean(); + mTestThreadHandler.post(new Runnable() { + @Override + public void run() { + if (r != null) r.run(); + synchronized (lock) { + taskExecuted.set(true); + lock.notify(); + } + } + }); + synchronized (lock) { + try { + while (!taskExecuted.get()) { + lock.wait(); + } + } catch (InterruptedException ie) { + ie.printStackTrace(); + } + } + } + + private void checkOnMainThread() { + assert Looper.myLooper() == mMainThreadHandler.getLooper(); + } +} |