EMMA Coverage Report (generated Fri Aug 23 16:39:17 PDT 2013)
[all classes][org.chromium.content.browser.test.util]

COVERAGE SUMMARY FOR SOURCE FILE [CallbackHelper.java]

nameclass, %method, %block, %line, %
CallbackHelper.java100% (1/1)100% (9/9)83%  (150/180)91%  (34.4/38)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class CallbackHelper100% (1/1)100% (9/9)83%  (150/180)91%  (34.4/38)
getCallCount (): int 100% (1/1)67%  (10/15)67%  (2/3)
<static initializer> 100% (1/1)75%  (6/8)75%  (0.8/1)
notifyCalled (): void 100% (1/1)78%  (18/23)95%  (4.8/5)
waitUntilCriteria (Criteria, long, TimeUnit): void 100% (1/1)78%  (36/46)85%  (7.6/9)
waitForCallback (int, int, long, TimeUnit): void 100% (1/1)86%  (51/59)92%  (11.1/12)
CallbackHelper (): void 100% (1/1)100% (11/11)100% (3/3)
waitForCallback (int): void 100% (1/1)100% (5/5)100% (2/2)
waitForCallback (int, int): void 100% (1/1)100% (7/7)100% (2/2)
waitUntilCriteria (Criteria): void 100% (1/1)100% (6/6)100% (2/2)

1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4 
5package org.chromium.content.browser.test.util;
6 
7import java.util.concurrent.TimeUnit;
8import java.util.concurrent.TimeoutException;
9 
10/**
11 * A helper class that encapsulates listening and blocking for callbacks.
12 *
13 * Sample usage:
14 *
15 * // Let us assume that this interface is defined by some piece of production code and is used
16 * // to communicate events that occur in that piece of code. Let us further assume that the
17 * // production code runs on the main thread test code runs on a separate test thread.
18 * // An instance that implements this interface would be injected by test code to ensure that the
19 * // methods are being called on another thread.
20 * interface Delegate {
21 *     void onOperationFailed(String errorMessage);
22 *     void onDataPersisted();
23 * }
24 *
25 * // This is the inner class you'd write in your test case to later inject into the production
26 * // code.
27 * class TestDelegate implements Delegate {
28 *     // This is the preferred way to create a helper that stores the parameters it receives
29 *     // when called by production code.
30 *     public static class OnOperationFailedHelper extends CallbackHelper {
31 *         private String mErrorMessage;
32 *
33 *         public void getErrorMessage() {
34 *             assert getCallCount() > 0;
35 *             return mErrorMessage;
36 *         }
37 *
38 *         public void notifyCalled(String errorMessage) {
39 *             mErrorMessage = errorMessage;
40 *             // It's important to call this after all parameter assignments.
41 *             notifyCalled();
42 *         }
43 *     }
44 *
45 *     // There should be one CallbackHelper instance per method.
46 *     private OnOperationFailedHelper mOnOperationFailedHelper;
47 *     private CallbackHelper mOnDataPersistedHelper;
48 *
49 *     public OnOperationFailedHelper getOnOperationFailedHelper() {
50 *         return mOnOperationFailedHelper;
51 *     }
52 *
53 *     public CallbackHelper getOnDataPersistedHelper() {
54 *         return mOnDataPersistedHelper;
55 *     }
56 *
57 *     @Override
58 *     public void onOperationFailed(String errorMessage) {
59 *         mOnOperationFailedHelper.notifyCalled(errorMessage);
60 *     }
61 *
62 *     @Override
63 *     public void onDataPersisted() {
64 *         mOnDataPersistedHelper.notifyCalled();
65 *     }
66 * }
67 *
68 * // This is a sample test case.
69 * public void testCase() throws Exception {
70 *     // Create the TestDelegate to inject into production code.
71 *     TestDelegate delegate = new TestDelegate();
72 *     // Create the production class instance that is being tested and inject the test delegate.
73 *     CodeUnderTest codeUnderTest = new CodeUnderTest();
74 *     codeUnderTest.setDelegate(delegate);
75 *
76 *     // Typically you'd get the current call count before performing the operation you expect to
77 *     // trigger the callback. There can't be any callbacks 'in flight' at this moment, otherwise
78 *     // the call count is unpredictable and the test will be flaky.
79 *     int onOperationFailedCallCount = delegate.getOnOperationFailedHelper().getCallCount();
80 *     codeUnderTest.doSomethingThatEndsUpCallingOnOperationFailedFromAnotherThread();
81 *     // It's safe to do other stuff here, if needed.
82 *     ....
83 *     // Wait for the callback if it hadn't been called yet, otherwise return immediately. This
84 *     // can throw an exception if the callback doesn't arrive within the timeout.
85 *     delegate.getOnOperationFailedHelper().waitForCallback(onOperationFailedCallCount);
86 *     // Access to method parameters is now safe.
87 *     assertEquals("server error", delegate.getOnOperationFailedHelper().getErrorMessage());
88 *
89 *     // Being able to pass the helper around lets us build methods which encapsulate commonly
90 *     // performed tasks.
91 *     doSomeOperationAndWait(codeUnerTest, delegate.getOnOperationFailedHelper());
92 *
93 *     // The helper can be resued for as many calls as needed, just be sure to get the count each
94 *     // time.
95 *     onOperationFailedCallCount = delegate.getOnOperationFailedHelper().getCallCount();
96 *     codeUnderTest.doSomethingElseButStillFailOnAnotherThread();
97 *     delegate.getOnOperationFailedHelper().waitForCallback(onOperationFailedCallCount);
98 *
99 *     // It is also possible to use more than one helper at a time.
100 *     onOperationFailedCallCount = delegate.getOnOperationFailedHelper().getCallCount();
101 *     int onDataPersistedCallCount = delegate.getOnDataPersistedHelper().getCallCount();
102 *     codeUnderTest.doSomethingThatPersistsDataButFailsInSomeOtherWayOnAnotherThread();
103 *     delegate.getOnDataPersistedHelper().waitForCallback(onDataPersistedCallCount);
104 *     delegate.getOnOperationFailedHelper().waitForCallback(onOperationFailedCallCount);
105 * }
106 *
107 * // Shows how to turn an async operation + completion callback into a synchronous operation.
108 * private void doSomeOperationAndWait(final CodeUnderTest underTest,
109 *         CallbackHelper operationHelper) throws InterruptedException, TimeoutException {
110 *     final int callCount = operaitonHelper.getCallCount();
111 *     getInstrumentaiton().runOnMainSync(new Runnable() {
112 *         @Override
113 *         public void run() {
114 *             // This schedules a call to a method on the injected TestDelegate. The TestDelegate
115 *             // implementation will then call operationHelper.notifyCalled().
116 *             underTest.operation();
117 *         }
118 *      });
119 *      operationHelper.waitForCallback(callCount);
120 * }
121 *
122 */
123public class CallbackHelper {
124    protected static final int WAIT_TIMEOUT_SECONDS = 5;
125 
126    private final Object mLock = new Object();
127    private int mCallCount = 0;
128 
129    /**
130     * Gets the number of times the callback has been called.
131     *
132     * The call count can be used with the waitForCallback() method, indicating a point
133     * in time after which the caller wishes to record calls to the callback.
134     *
135     * In order to wait for a callback caused by X, the call count should be obtained
136     * before X occurs.
137     *
138     * NOTE: any call to the callback that occurs after the call count is obtained
139     * will result in the corresponding wait call to resume execution. The call count
140     * is intended to 'catch' callbacks that occur after X but before waitForCallback()
141     * is called.
142     */
143    public int getCallCount() {
144        synchronized(mLock) {
145            return mCallCount;
146        }
147    }
148 
149    /**
150     * Blocks until the callback is called the specified number of
151     * times or throws an exception if we exceeded the specified time frame.
152     *
153     * This will wait for a callback to be called a specified number of times after
154     * the point in time at which the call count was obtained.  The method will return
155     * immediately if a call occurred the specified number of times after the
156     * call count was obtained but before the method was called, otherwise the method will
157     * block until the specified call count is reached.
158     *
159     * @param currentCallCount the value obtained by calling getCallCount().
160     * @param numberOfCallsToWaitFor number of calls (counting since
161     *                               currentCallCount was obtained) that we will wait for.
162     * @param timeout timeout value. We will wait the specified amount of time for a single
163     *                callback to occur so the method call may block up to
164     *                <code>numberOfCallsToWaitFor * timeout</code> units.
165     * @param unit timeout unit.
166     * @throws InterruptedException
167     * @throws TimeoutException Thrown if the method times out before onPageFinished is called.
168     */
169    public void waitForCallback(int currentCallCount, int numberOfCallsToWaitFor, long timeout,
170            TimeUnit unit) throws InterruptedException, TimeoutException {
171        assert mCallCount >= currentCallCount;
172        assert numberOfCallsToWaitFor > 0;
173        synchronized(mLock) {
174            int callCountWhenDoneWaiting = currentCallCount + numberOfCallsToWaitFor;
175            while (callCountWhenDoneWaiting > mCallCount) {
176                int callCountBeforeWait = mCallCount;
177                mLock.wait(unit.toMillis(timeout));
178                if (callCountBeforeWait == mCallCount) {
179                    throw new TimeoutException("waitForCallback timed out!");
180                }
181            }
182        }
183    }
184 
185    public void waitForCallback(int currentCallCount, int numberOfCallsToWaitFor)
186            throws InterruptedException, TimeoutException {
187        waitForCallback(currentCallCount, numberOfCallsToWaitFor,
188                WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS);
189    }
190 
191    public void waitForCallback(int currentCallCount)
192            throws InterruptedException, TimeoutException {
193        waitForCallback(currentCallCount, 1);
194    }
195 
196    /**
197     * Blocks until the criteria is satisfied or throws an exception
198     * if the specified time frame is exceeded.
199     * @param timeout timeout value.
200     * @param unit timeout unit.
201     */
202    public void waitUntilCriteria(Criteria criteria, long timeout, TimeUnit unit)
203            throws InterruptedException, TimeoutException {
204        synchronized(mLock) {
205            final long startTime = System.currentTimeMillis();
206            boolean isSatisfied = criteria.isSatisfied();
207            while (!isSatisfied &&
208                    System.currentTimeMillis() - startTime < unit.toMillis(timeout)) {
209                mLock.wait(unit.toMillis(timeout));
210                isSatisfied = criteria.isSatisfied();
211            }
212            if (!isSatisfied) throw new TimeoutException("waitUntilCriteria timed out!");
213        }
214    }
215 
216    public void waitUntilCriteria(Criteria criteria)
217            throws InterruptedException, TimeoutException {
218        waitUntilCriteria(criteria, WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS);
219    }
220 
221    /**
222     * Should be called when the callback associated with this helper object is called.
223     */
224    public void notifyCalled() {
225        synchronized(mLock) {
226            mCallCount++;
227            mLock.notifyAll();
228        }
229    }
230}

[all classes][org.chromium.content.browser.test.util]
EMMA 2.0.5312 (C) Vladimir Roubtsov