diff options
author | mkosiba@chromium.org <mkosiba@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-15 15:46:34 +0000 |
---|---|---|
committer | mkosiba@chromium.org <mkosiba@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-15 15:46:34 +0000 |
commit | 20855f88ab2c1057a67f5ea119708df853dc335a (patch) | |
tree | f1021bf5fe373ac8a422f284e318269a78d3e788 /content | |
parent | 3d218815b543d1ee8229273678971581c9dc592d (diff) | |
download | chromium_src-20855f88ab2c1057a67f5ea119708df853dc335a.zip chromium_src-20855f88ab2c1057a67f5ea119708df853dc335a.tar.gz chromium_src-20855f88ab2c1057a67f5ea119708df853dc335a.tar.bz2 |
[android] Improve CallbackHelper documentation.
After spending way too much time explaining the use of this class
(crrev.com/12652003) I thought that improving the docs would be
a good idea.
Also, making mCallCount private as subclasses shouldn't read the
value without synchronizing on the lock.
BUG=None
TEST=Builds
NOTRY=True
Review URL: https://chromiumcodereview.appspot.com/12645009
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@188394 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content')
-rw-r--r-- | content/public/test/android/javatests/src/org/chromium/content/browser/test/util/CallbackHelper.java | 114 |
1 files changed, 112 insertions, 2 deletions
diff --git a/content/public/test/android/javatests/src/org/chromium/content/browser/test/util/CallbackHelper.java b/content/public/test/android/javatests/src/org/chromium/content/browser/test/util/CallbackHelper.java index 95646e2..f2e9a292 100644 --- a/content/public/test/android/javatests/src/org/chromium/content/browser/test/util/CallbackHelper.java +++ b/content/public/test/android/javatests/src/org/chromium/content/browser/test/util/CallbackHelper.java @@ -8,13 +8,123 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; /** - * A helper class for listening to callbacks. + * A helper class that encapsulates listening and blocking for callbacks. + * + * Sample usage: + * + * // Let us assume that this interface is defined by some piece of production code and is used + * // to communicate events that occur in that piece of code. Let us further assume that the + * // production code runs on the main thread test code runs on a separate test thread. + * // An instance that implements this interface would be injected by test code to ensure that the + * // methods are being called on another thread. + * interface Delegate { + * void onOperationFailed(String errorMessage); + * void onDataPersisted(); + * } + * + * // This is the inner class you'd write in your test case to later inject into the production + * // code. + * class TestDelegate implements Delegate { + * // This is the preferred way to create a helper that stores the parameters it receives + * // when called by production code. + * public static class OnOperationFailedHelper extends CallbackHelper { + * private String mErrorMessage; + * + * public void getErrorMessage() { + * assert getCallCount() > 0; + * return mErrorMessage; + * } + * + * public void notifyCalled(String errorMessage) { + * mErrorMessage = errorMessage; + * // It's important to call this after all parameter assignments. + * notifyCalled(); + * } + * } + * + * // There should be one CallbackHelper instance per method. + * private OnOperationFailedHelper mOnOperationFailedHelper; + * private CallbackHelper mOnDataPersistedHelper; + * + * public OnOperationFailedHelper getOnOperationFailedHelper() { + * return mOnOperationFailedHelper; + * } + * + * public CallbackHelper getOnDataPersistedHelper() { + * return mOnDataPersistedHelper; + * } + * + * @Override + * public void onOperationFailed(String errorMessage) { + * mOnOperationFailedHelper.notifyCalled(errorMessage); + * } + * + * @Override + * public void onDataPersisted() { + * mOnDataPersistedHelper.notifyCalled(); + * } + * } + * + * // This is a sample test case. + * public void testCase() throws Exception { + * // Create the TestDelegate to inject into production code. + * TestDelegate delegate = new TestDelegate(); + * // Create the production class instance that is being tested and inject the test delegate. + * CodeUnderTest codeUnderTest = new CodeUnderTest(); + * codeUnderTest.setDelegate(delegate); + * + * // Typically you'd get the current call count before performing the operation you expect to + * // trigger the callback. There can't be any callbacks 'in flight' at this moment, otherwise + * // the call count is unpredictable and the test will be flaky. + * int onOperationFailedCallCount = delegate.getOnOperationFailedHelper().getCallCount(); + * codeUnderTest.doSomethingThatEndsUpCallingOnOperationFailedFromAnotherThread(); + * // It's safe to do other stuff here, if needed. + * .... + * // Wait for the callback if it hadn't been called yet, otherwise return immediately. This + * // can throw an exception if the callback doesn't arrive within the timeout. + * delegate.getOnOperationFailedHelper().waitForCallback(onOperationFailedCallCount); + * // Access to method parameters is now safe. + * assertEquals("server error", delegate.getOnOperationFailedHelper().getErrorMessage()); + * + * // Being able to pass the helper around lets us build methods which encapsulate commonly + * // performed tasks. + * doSomeOperationAndWait(codeUnerTest, delegate.getOnOperationFailedHelper()); + * + * // The helper can be resued for as many calls as needed, just be sure to get the count each + * // time. + * onOperationFailedCallCount = delegate.getOnOperationFailedHelper().getCallCount(); + * codeUnderTest.doSomethingElseButStillFailOnAnotherThread(); + * delegate.getOnOperationFailedHelper().waitForCallback(onOperationFailedCallCount); + * + * // It is also possible to use more than one helper at a time. + * onOperationFailedCallCount = delegate.getOnOperationFailedHelper().getCallCount(); + * int onDataPersistedCallCount = delegate.getOnDataPersistedHelper().getCallCount(); + * codeUnderTest.doSomethingThatPersistsDataButFailsInSomeOtherWayOnAnotherThread(); + * delegate.getOnDataPersistedHelper().waitForCallback(onDataPersistedCallCount); + * delegate.getOnOperationFailedHelper().waitForCallback(onOperationFailedCallCount); + * } + * + * // Shows how to turn an async operation + completion callback into a synchronous operation. + * private void doSomeOperationAndWait(final CodeUnderTest underTest, + * CallbackHelper operationHelper) throws InterruptedException, TimeoutException { + * final int callCount = operaitonHelper.getCallCount(); + * getInstrumentaiton().runOnMainSync(new Runnable() { + * @Override + * public void run() { + * // This schedules a call to a method on the injected TestDelegate. The TestDelegate + * // implementation will then call operationHelper.notifyCalled(). + * underTest.operation(); + * } + * }); + * operationHelper.waitForCallback(callCount); + * } + * */ public class CallbackHelper { protected static int WAIT_TIMEOUT_SECONDS = 5; private final Object mLock = new Object(); - protected int mCallCount = 0; + private int mCallCount = 0; /** * Gets the number of times the callback has been called. |