Today I learned how to test asynchronous callback with LinkedBlockingQueue by reading TestKeepaliveCallback implementation in AOSP.
The basic idea is to use LinkedBlockingQueue.poll with timeout to wait for a method to be called. A private inner class is designed to capture the details of a method callback. A few expectSomething
helper methods are defined to make the test code more readable.
Here is the test class for testing callback to PacketKeepaliveCallback
:
private static class TestKeepaliveCallback extends PacketKeepaliveCallback {
public static enum CallbackType { ON_STARTED, ON_STOPPED, ON_ERROR };
private class CallbackValue {
public CallbackType callbackType;
public int error;
public CallbackValue(CallbackType type) {
this.callbackType = type;
this.error = PacketKeepalive.SUCCESS;
assertTrue("onError callback must have error", type != CallbackType.ON_ERROR);
}
public CallbackValue(CallbackType type, int error) {
this.callbackType = type;
this.error = error;
assertEquals("error can only be set for onError", type, CallbackType.ON_ERROR);
}
@Override
public boolean equals(Object o) {
return o instanceof CallbackValue &&
this.callbackType == ((CallbackValue) o).callbackType &&
this.error == ((CallbackValue) o).error;
}
@Override
public String toString() {
return String.format("%s(%s, %d)", getClass().getSimpleName(), callbackType, error);
}
}
private LinkedBlockingQueue<CallbackValue> mCallbacks = new LinkedBlockingQueue<>();
@Override
public void onStarted() {
mCallbacks.add(new CallbackValue(CallbackType.ON_STARTED));
}
@Override
public void onStopped() {
mCallbacks.add(new CallbackValue(CallbackType.ON_STOPPED));
}
@Override
public void onError(int error) {
mCallbacks.add(new CallbackValue(CallbackType.ON_ERROR, error));
}
private void expectCallback(CallbackValue callbackValue) {
try {
assertEquals(
callbackValue,
mCallbacks.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS));
} catch (InterruptedException e) {
fail(callbackValue.callbackType + " callback not seen after " + TIMEOUT_MS + " ms");
}
}
public void expectStarted() {
expectCallback(new CallbackValue(CallbackType.ON_STARTED));
}
public void expectStopped() {
expectCallback(new CallbackValue(CallbackType.ON_STOPPED));
}
public void expectError(int error) {
expectCallback(new CallbackValue(CallbackType.ON_ERROR, error));
}
}