Bug 991167 added BundleEventListener that allow dispatching an event directly to the UI thread or to the background thread. This patch adds tests to testEventDispatcher, to test that the events are dispatched on the correct threads and that the Bundle messages are correct. Because these events are asynchronous, the test waits for each event to be processed before continuing.
456 lines
22 KiB
Java
456 lines
22 KiB
Java
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
package org.mozilla.gecko.tests;
|
|
|
|
import static org.mozilla.gecko.tests.helpers.AssertionHelper.*;
|
|
|
|
import org.mozilla.gecko.tests.helpers.*;
|
|
|
|
import org.mozilla.gecko.EventDispatcher;
|
|
import org.mozilla.gecko.util.BundleEventListener;
|
|
import org.mozilla.gecko.util.EventCallback;
|
|
import org.mozilla.gecko.util.GeckoEventListener;
|
|
import org.mozilla.gecko.util.NativeEventListener;
|
|
import org.mozilla.gecko.util.NativeJSObject;
|
|
import org.mozilla.gecko.util.ThreadUtils;
|
|
|
|
import android.os.Bundle;
|
|
|
|
import org.json.JSONArray;
|
|
import org.json.JSONException;
|
|
import org.json.JSONObject;
|
|
|
|
/**
|
|
* Tests the proper operation of EventDispatcher,
|
|
* including associated NativeJSObject objects.
|
|
*/
|
|
public class testEventDispatcher extends UITest
|
|
implements BundleEventListener, GeckoEventListener, NativeEventListener {
|
|
|
|
private static final String TEST_JS = "testEventDispatcher.js";
|
|
private static final String GECKO_EVENT = "Robocop:TestGeckoEvent";
|
|
private static final String GECKO_RESPONSE_EVENT = "Robocop:TestGeckoResponse";
|
|
private static final String NATIVE_EVENT = "Robocop:TestNativeEvent";
|
|
private static final String NATIVE_RESPONSE_EVENT = "Robocop:TestNativeResponse";
|
|
private static final String NATIVE_EXCEPTION_EVENT = "Robocop:TestNativeException";
|
|
private static final String UI_EVENT = "Robocop:TestUIEvent";
|
|
private static final String UI_RESPONSE_EVENT = "Robocop:TestUIResponse";
|
|
private static final String BACKGROUND_EVENT = "Robocop:TestBackgroundEvent";
|
|
private static final String BACKGROUND_RESPONSE_EVENT = "Robocop:TestBackgrondResponse";
|
|
|
|
private static final long WAIT_FOR_BUNDLE_EVENT_TIMEOUT_MILLIS = 10000; // 10 seconds
|
|
|
|
private JavascriptBridge js;
|
|
private NativeJSObject savedMessage;
|
|
|
|
private boolean handledGeckoEvent;
|
|
private boolean handledNativeEvent;
|
|
private boolean handledAsyncEvent;
|
|
|
|
@Override
|
|
public void setUp() throws Exception {
|
|
super.setUp();
|
|
js = new JavascriptBridge(this);
|
|
|
|
EventDispatcher.getInstance().registerGeckoThreadListener(
|
|
(GeckoEventListener) this, GECKO_EVENT, GECKO_RESPONSE_EVENT);
|
|
EventDispatcher.getInstance().registerGeckoThreadListener(
|
|
(NativeEventListener) this,
|
|
NATIVE_EVENT, NATIVE_RESPONSE_EVENT, NATIVE_EXCEPTION_EVENT);
|
|
EventDispatcher.getInstance().registerUiThreadListener(
|
|
this, UI_EVENT, UI_RESPONSE_EVENT);
|
|
EventDispatcher.getInstance().registerBackgroundThreadListener(
|
|
this, BACKGROUND_EVENT, BACKGROUND_RESPONSE_EVENT);
|
|
}
|
|
|
|
@Override
|
|
public void tearDown() throws Exception {
|
|
EventDispatcher.getInstance().unregisterGeckoThreadListener(
|
|
(GeckoEventListener) this, GECKO_EVENT, GECKO_RESPONSE_EVENT);
|
|
EventDispatcher.getInstance().unregisterGeckoThreadListener(
|
|
(NativeEventListener) this,
|
|
NATIVE_EVENT, NATIVE_RESPONSE_EVENT, NATIVE_EXCEPTION_EVENT);
|
|
EventDispatcher.getInstance().unregisterUiThreadListener(
|
|
this, UI_EVENT, UI_RESPONSE_EVENT);
|
|
EventDispatcher.getInstance().unregisterBackgroundThreadListener(
|
|
this, BACKGROUND_EVENT, BACKGROUND_RESPONSE_EVENT);
|
|
|
|
js.disconnect();
|
|
super.tearDown();
|
|
}
|
|
|
|
private synchronized void waitForAsyncEvent() {
|
|
final long startTime = System.nanoTime();
|
|
while (!handledAsyncEvent) {
|
|
if (System.nanoTime() - startTime
|
|
>= WAIT_FOR_BUNDLE_EVENT_TIMEOUT_MILLIS * 1e6 /* ns per ms */) {
|
|
fFail("Should have completed event before timeout");
|
|
}
|
|
try {
|
|
wait(1000); // Wait for 1 second at a time.
|
|
} catch (final InterruptedException e) {
|
|
// Attempt waiting again.
|
|
}
|
|
}
|
|
handledAsyncEvent = false;
|
|
}
|
|
|
|
private synchronized void notifyAsyncEvent() {
|
|
handledAsyncEvent = true;
|
|
notifyAll();
|
|
}
|
|
|
|
public void testEventDispatcher() {
|
|
GeckoHelper.blockForReady();
|
|
NavigationHelper.enterAndLoadUrl(mStringHelper.getHarnessUrlForJavascript(TEST_JS));
|
|
|
|
js.syncCall("send_test_message", GECKO_EVENT);
|
|
fAssertTrue("Should have handled Gecko event synchronously", handledGeckoEvent);
|
|
|
|
js.syncCall("send_message_for_response", GECKO_RESPONSE_EVENT, "success");
|
|
js.syncCall("send_message_for_response", GECKO_RESPONSE_EVENT, "error");
|
|
|
|
js.syncCall("send_test_message", NATIVE_EVENT);
|
|
fAssertTrue("Should have handled native event synchronously", handledNativeEvent);
|
|
|
|
js.syncCall("send_message_for_response", NATIVE_RESPONSE_EVENT, "success");
|
|
js.syncCall("send_message_for_response", NATIVE_RESPONSE_EVENT, "error");
|
|
|
|
js.syncCall("send_test_message", NATIVE_EXCEPTION_EVENT);
|
|
fAssertNotSame("Should have saved a message", null, savedMessage);
|
|
try {
|
|
savedMessage.toString();
|
|
fFail("Using NativeJSContainer should throw after disposal");
|
|
} catch (final NullPointerException e) {
|
|
}
|
|
|
|
js.syncCall("send_test_message", UI_EVENT);
|
|
waitForAsyncEvent();
|
|
|
|
js.syncCall("send_message_for_response", UI_RESPONSE_EVENT, "success");
|
|
waitForAsyncEvent();
|
|
|
|
js.syncCall("send_message_for_response", UI_RESPONSE_EVENT, "error");
|
|
waitForAsyncEvent();
|
|
|
|
js.syncCall("send_test_message", BACKGROUND_EVENT);
|
|
waitForAsyncEvent();
|
|
|
|
js.syncCall("send_message_for_response", BACKGROUND_RESPONSE_EVENT, "success");
|
|
waitForAsyncEvent();
|
|
|
|
js.syncCall("send_message_for_response", BACKGROUND_RESPONSE_EVENT, "error");
|
|
waitForAsyncEvent();
|
|
|
|
js.syncCall("finish_test");
|
|
}
|
|
|
|
@Override
|
|
public void handleMessage(final String event, final Bundle message,
|
|
final EventCallback callback) {
|
|
|
|
if (UI_EVENT.equals(event) || UI_RESPONSE_EVENT.equals(event)) {
|
|
fAssertTrue("UI event should be on UI thread", ThreadUtils.isOnUiThread());
|
|
|
|
} else if (BACKGROUND_EVENT.equals(event) || BACKGROUND_RESPONSE_EVENT.equals(event)) {
|
|
fAssertTrue("Background event should be on background thread",
|
|
ThreadUtils.isOnBackgroundThread());
|
|
|
|
} else {
|
|
fFail("Event type should be valid: " + event);
|
|
}
|
|
|
|
if (UI_EVENT.equals(event) || BACKGROUND_EVENT.equals(event)) {
|
|
checkBundle(message);
|
|
checkBundle(message.getBundle("object"));
|
|
|
|
} else if (UI_RESPONSE_EVENT.equals(event) || BACKGROUND_RESPONSE_EVENT.equals(event)) {
|
|
final String response = message.getString("response");
|
|
if ("success".equals(response)) {
|
|
callback.sendSuccess(response);
|
|
} else if ("error".equals(response)) {
|
|
callback.sendError(response);
|
|
} else {
|
|
fFail("Response type should be valid: " + response);
|
|
}
|
|
|
|
} else {
|
|
fFail("Event type should be valid: " + event);
|
|
}
|
|
|
|
notifyAsyncEvent();
|
|
}
|
|
|
|
@Override
|
|
public void handleMessage(final String event, final JSONObject message) {
|
|
ThreadUtils.assertOnGeckoThread();
|
|
|
|
try {
|
|
if (GECKO_EVENT.equals(event)) {
|
|
checkJSONObject(message);
|
|
checkJSONObject(message.getJSONObject("object"));
|
|
handledGeckoEvent = true;
|
|
|
|
} else if (GECKO_RESPONSE_EVENT.equals(event)) {
|
|
final String response = message.getString("response");
|
|
if ("success".equals(response)) {
|
|
EventDispatcher.sendResponse(message, response);
|
|
} else if ("error".equals(response)) {
|
|
EventDispatcher.sendError(message, response);
|
|
} else {
|
|
fFail("Response type should be valid: " + response);
|
|
}
|
|
|
|
} else {
|
|
fFail("Event type should be valid: " + event);
|
|
}
|
|
} catch (final JSONException e) {
|
|
fFail(e.toString());
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void handleMessage(final String event, final NativeJSObject message,
|
|
final EventCallback callback) {
|
|
ThreadUtils.assertOnGeckoThread();
|
|
|
|
if (NATIVE_EVENT.equals(event)) {
|
|
checkNativeJSObject(message);
|
|
checkNativeJSObject(message.getObject("object"));
|
|
fAssertNotSame("optObject returns existent value",
|
|
null, message.optObject("object", null));
|
|
fAssertSame("optObject returns fallback value if nonexistent",
|
|
null, message.optObject("nonexistent_object", null));
|
|
|
|
final NativeJSObject[] objectArray = message.getObjectArray("objectArray");
|
|
fAssertNotNull("Native object array should exist", objectArray);
|
|
fAssertEquals("Native object array has correct length", 2, objectArray.length);
|
|
fAssertSame("Native object array index 0 has correct value", null, objectArray[0]);
|
|
fAssertNotSame("Native object array index 1 has correct value", null, objectArray[1]);
|
|
checkNativeJSObject(objectArray[1]);
|
|
fAssertNotSame("optObjectArray returns existent value",
|
|
null, message.optObjectArray("objectArray", null));
|
|
fAssertSame("optObjectArray returns fallback value if nonexistent",
|
|
null, message.optObjectArray("nonexistent_objectArray", null));
|
|
|
|
final Bundle bundle = message.toBundle();
|
|
checkBundle(bundle);
|
|
checkBundle(bundle.getBundle("object"));
|
|
fAssertNotSame("optBundle returns property value if it exists",
|
|
null, message.optBundle("object", null));
|
|
fAssertSame("optBundle returns fallback value if property does not exist",
|
|
null, message.optBundle("nonexistent_object", null));
|
|
|
|
final Bundle[] bundleArray = message.getBundleArray("objectArray");
|
|
fAssertNotNull("Native bundle array should exist", bundleArray);
|
|
fAssertEquals("Native bundle array has correct length", 2, bundleArray.length);
|
|
fAssertSame("Native bundle array index 0 has correct value", null, bundleArray[0]);
|
|
fAssertNotSame("Native bundle array index 1 has correct value", null, bundleArray[1]);
|
|
checkBundle(bundleArray[1]);
|
|
fAssertNotSame("optBundleArray returns existent value",
|
|
null, message.optBundleArray("objectArray", null));
|
|
fAssertSame("optBundleArray returns fallback value if nonexistent",
|
|
null, message.optBundleArray("nonexistent_objectArray", null));
|
|
|
|
handledNativeEvent = true;
|
|
|
|
} else if (NATIVE_RESPONSE_EVENT.equals(event)) {
|
|
final String response = message.getString("response");
|
|
if ("success".equals(response)) {
|
|
callback.sendSuccess(response);
|
|
} else if ("error".equals(response)) {
|
|
callback.sendError(response);
|
|
} else {
|
|
fFail("Response type should be valid: " + response);
|
|
}
|
|
|
|
} else if (NATIVE_EXCEPTION_EVENT.equals(event)) {
|
|
// Make sure we throw the right exceptions.
|
|
try {
|
|
message.getString(null);
|
|
fFail("null property name should throw IllegalArgumentException");
|
|
} catch (final IllegalArgumentException e) {
|
|
}
|
|
|
|
try {
|
|
message.getString("nonexistent_string");
|
|
fFail("Nonexistent property name should throw InvalidPropertyException");
|
|
} catch (final NativeJSObject.InvalidPropertyException e) {
|
|
}
|
|
|
|
try {
|
|
message.getString("int");
|
|
fFail("Wrong property type should throw InvalidPropertyException");
|
|
} catch (final NativeJSObject.InvalidPropertyException e) {
|
|
}
|
|
|
|
// Save this message for post-disposal check.
|
|
savedMessage = message;
|
|
|
|
// Save this test for last; make sure EventDispatcher catches InvalidPropertyException.
|
|
message.getString("nonexistent_string");
|
|
fFail("EventDispatcher should catch InvalidPropertyException");
|
|
|
|
} else {
|
|
fFail("Event type should be valid: " + event);
|
|
}
|
|
}
|
|
|
|
private void checkBundle(final Bundle bundle) {
|
|
fAssertEquals("Bundle boolean has correct value", true, bundle.getBoolean("boolean"));
|
|
fAssertEquals("Bundle int has correct value", 1, bundle.getInt("int"));
|
|
fAssertEquals("Bundle double has correct value", 0.5, bundle.getDouble("double"));
|
|
fAssertEquals("Bundle string has correct value", "foo", bundle.getString("string"));
|
|
|
|
final boolean[] booleanArray = bundle.getBooleanArray("booleanArray");
|
|
fAssertNotNull("Bundle boolean array should exist", booleanArray);
|
|
fAssertEquals("Bundle boolean array has correct length", 2, booleanArray.length);
|
|
fAssertEquals("Bundle boolean array index 0 has correct value", false, booleanArray[0]);
|
|
fAssertEquals("Bundle boolean array index 1 has correct value", true, booleanArray[1]);
|
|
|
|
final int[] intArray = bundle.getIntArray("intArray");
|
|
fAssertNotNull("Bundle int array should exist", intArray);
|
|
fAssertEquals("Bundle int array has correct length", 2, intArray.length);
|
|
fAssertEquals("Bundle int array index 0 has correct value", 2, intArray[0]);
|
|
fAssertEquals("Bundle int array index 1 has correct value", 3, intArray[1]);
|
|
|
|
final double[] doubleArray = bundle.getDoubleArray("doubleArray");
|
|
fAssertNotNull("Bundle double array should exist", doubleArray);
|
|
fAssertEquals("Bundle double array has correct length", 2, doubleArray.length);
|
|
fAssertEquals("Bundle double array index 0 has correct value", 1.5, doubleArray[0]);
|
|
fAssertEquals("Bundle double array index 1 has correct value", 2.5, doubleArray[1]);
|
|
|
|
final String[] stringArray = bundle.getStringArray("stringArray");
|
|
fAssertNotNull("Bundle string array should exist", stringArray);
|
|
fAssertEquals("Bundle string array has correct length", 2, stringArray.length);
|
|
fAssertEquals("Bundle string array index 0 has correct value", "bar", stringArray[0]);
|
|
fAssertEquals("Bundle string array index 1 has correct value", "baz", stringArray[1]);
|
|
}
|
|
|
|
private void checkJSONObject(final JSONObject object) throws JSONException {
|
|
fAssertEquals("JSON boolean has correct value", true, object.getBoolean("boolean"));
|
|
fAssertEquals("JSON int has correct value", 1, object.getInt("int"));
|
|
fAssertEquals("JSON double has correct value", 0.5, object.getDouble("double"));
|
|
fAssertEquals("JSON string has correct value", "foo", object.getString("string"));
|
|
|
|
final JSONArray booleanArray = object.getJSONArray("booleanArray");
|
|
fAssertNotNull("JSON boolean array should exist", booleanArray);
|
|
fAssertEquals("JSON boolean array has correct length", 2, booleanArray.length());
|
|
fAssertEquals("JSON boolean array index 0 has correct value",
|
|
false, booleanArray.getBoolean(0));
|
|
fAssertEquals("JSON boolean array index 1 has correct value",
|
|
true, booleanArray.getBoolean(1));
|
|
|
|
final JSONArray intArray = object.getJSONArray("intArray");
|
|
fAssertNotNull("JSON int array should exist", intArray);
|
|
fAssertEquals("JSON int array has correct length", 2, intArray.length());
|
|
fAssertEquals("JSON int array index 0 has correct value",
|
|
2, intArray.getInt(0));
|
|
fAssertEquals("JSON int array index 1 has correct value",
|
|
3, intArray.getInt(1));
|
|
|
|
final JSONArray doubleArray = object.getJSONArray("doubleArray");
|
|
fAssertNotNull("JSON double array should exist", doubleArray);
|
|
fAssertEquals("JSON double array has correct length", 2, doubleArray.length());
|
|
fAssertEquals("JSON double array index 0 has correct value",
|
|
1.5, doubleArray.getDouble(0));
|
|
fAssertEquals("JSON double array index 1 has correct value",
|
|
2.5, doubleArray.getDouble(1));
|
|
|
|
final JSONArray stringArray = object.getJSONArray("stringArray");
|
|
fAssertNotNull("JSON string array should exist", stringArray);
|
|
fAssertEquals("JSON string array has correct length", 2, stringArray.length());
|
|
fAssertEquals("JSON string array index 0 has correct value",
|
|
"bar", stringArray.getString(0));
|
|
fAssertEquals("JSON string array index 1 has correct value",
|
|
"baz", stringArray.getString(1));
|
|
}
|
|
|
|
private void checkNativeJSObject(final NativeJSObject object) {
|
|
fAssertEquals("Native boolean has correct value",
|
|
true, object.getBoolean("boolean"));
|
|
fAssertEquals("optBoolean returns existent value",
|
|
true, object.optBoolean("boolean", false));
|
|
fAssertEquals("optBoolean returns fallback value if nonexistent",
|
|
false, object.optBoolean("nonexistent_boolean", false));
|
|
|
|
fAssertEquals("Native int has correct value",
|
|
1, object.getInt("int"));
|
|
fAssertEquals("optInt returns existent value",
|
|
1, object.optInt("int", 0));
|
|
fAssertEquals("optInt returns fallback value if nonexistent",
|
|
0, object.optInt("nonexistent_int", 0));
|
|
|
|
fAssertEquals("Native double has correct value",
|
|
0.5, object.getDouble("double"));
|
|
fAssertEquals("optDouble returns existent value",
|
|
0.5, object.optDouble("double", -0.5));
|
|
fAssertEquals("optDouble returns fallback value if nonexistent",
|
|
-0.5, object.optDouble("nonexistent_double", -0.5));
|
|
|
|
fAssertEquals("Native string has correct value",
|
|
"foo", object.getString("string"));
|
|
fAssertEquals("optDouble returns existent value",
|
|
"foo", object.optString("string", "bar"));
|
|
fAssertEquals("optDouble returns fallback value if nonexistent",
|
|
"bar", object.optString("nonexistent_string", "bar"));
|
|
|
|
final boolean[] booleanArray = object.getBooleanArray("booleanArray");
|
|
fAssertNotNull("Native boolean array should exist", booleanArray);
|
|
fAssertEquals("Native boolean array has correct length", 2, booleanArray.length);
|
|
fAssertEquals("Native boolean array index 0 has correct value", false, booleanArray[0]);
|
|
fAssertEquals("Native boolean array index 1 has correct value", true, booleanArray[1]);
|
|
fAssertNotSame("optBooleanArray returns existent value",
|
|
null, object.optBooleanArray("booleanArray", null));
|
|
fAssertSame("optBooleanArray returns fallback value if nonexistent",
|
|
null, object.optBooleanArray("nonexistent_booleanArray", null));
|
|
|
|
final int[] intArray = object.getIntArray("intArray");
|
|
fAssertNotNull("Native int array should exist", intArray);
|
|
fAssertEquals("Native int array has correct length", 2, intArray.length);
|
|
fAssertEquals("Native int array index 0 has correct value", 2, intArray[0]);
|
|
fAssertEquals("Native int array index 1 has correct value", 3, intArray[1]);
|
|
fAssertNotSame("optIntArray returns existent value",
|
|
null, object.optIntArray("intArray", null));
|
|
fAssertSame("optIntArray returns fallback value if nonexistent",
|
|
null, object.optIntArray("nonexistent_intArray", null));
|
|
|
|
final double[] doubleArray = object.getDoubleArray("doubleArray");
|
|
fAssertNotNull("Native double array should exist", doubleArray);
|
|
fAssertEquals("Native double array has correct length", 2, doubleArray.length);
|
|
fAssertEquals("Native double array index 0 has correct value", 1.5, doubleArray[0]);
|
|
fAssertEquals("Native double array index 1 has correct value", 2.5, doubleArray[1]);
|
|
fAssertNotSame("optDoubleArray returns existent value",
|
|
null, object.optDoubleArray("doubleArray", null));
|
|
fAssertSame("optDoubleArray returns fallback value if nonexistent",
|
|
null, object.optDoubleArray("nonexistent_doubleArray", null));
|
|
|
|
final String[] stringArray = object.getStringArray("stringArray");
|
|
fAssertNotNull("Native string array should exist", stringArray);
|
|
fAssertEquals("Native string array has correct length", 2, stringArray.length);
|
|
fAssertEquals("Native string array index 0 has correct value", "bar", stringArray[0]);
|
|
fAssertEquals("Native string array index 1 has correct value", "baz", stringArray[1]);
|
|
fAssertNotSame("optStringArray returns existent value",
|
|
null, object.optStringArray("stringArray", null));
|
|
fAssertSame("optStringArray returns fallback value if nonexistent",
|
|
null, object.optStringArray("nonexistent_stringArray", null));
|
|
|
|
fAssertEquals("Native has(null) is false", false, object.has("null"));
|
|
fAssertEquals("Native has(emptyString) is true", true, object.has("emptyString"));
|
|
|
|
fAssertEquals("Native optBoolean returns fallback value if null",
|
|
true, object.optBoolean("null", true));
|
|
fAssertEquals("Native optInt returns fallback value if null",
|
|
42, object.optInt("null", 42));
|
|
fAssertEquals("Native optDouble returns fallback value if null",
|
|
-3.1415926535, object.optDouble("null", -3.1415926535));
|
|
fAssertEquals("Native optString returns fallback value if null",
|
|
"baz", object.optString("null", "baz"));
|
|
|
|
fAssertNotEquals("Native optString does not return fallback value if emptyString",
|
|
"baz", object.optString("emptyString", "baz"));
|
|
}
|
|
}
|