Bug 1003670 - Add testEventDispatcher. r=bnicholson, r=mcomella
authorJim Chen <nchen@mozilla.com>
Mon, 05 May 2014 13:18:00 -0400
changeset 181845 4ed94f132f5b784cabee8ea97a72605f6935aa02
parent 181844 4236bc861bebe7ec47ff18639f048ccf6f7360a4
child 181846 dfba6113cb029b923db32ccf3b54aa1d5756bdd3
push id26735
push userkwierso@gmail.com
push dateWed, 07 May 2014 01:04:15 +0000
treeherdermozilla-central@5bbc85136202 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbnicholson, mcomella
bugs1003670
milestone32.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1003670 - Add testEventDispatcher. r=bnicholson, r=mcomella
mobile/android/base/tests/robocop.ini
mobile/android/base/tests/testEventDispatcher.java
mobile/android/base/tests/testEventDispatcher.js
mobile/android/base/util/EventCallback.java
mobile/android/base/util/ThreadUtils.java
--- a/mobile/android/base/tests/robocop.ini
+++ b/mobile/android/base/tests/robocop.ini
@@ -129,16 +129,17 @@ skip-if = android_version == "8"
 #[testCheck2]
 #[testBrowserProviderPerf]
 
 # Using UITest
 #[testAboutHomePageNavigation] # see bug 947550, bug 979038 and bug 977952
 [testAboutHomeVisibility]
 # disabled on Android 2.3; bug 979597
 skip-if = android_version == "10"
+[testEventDispatcher]
 [testInputConnection]
 # disabled on Android 2.3; bug 983440
 skip-if = android_version == "10"
 [testJavascriptBridge]
 [testNativeCrypto]
 [testSessionHistory]
 
 # testSelectionHandler disabled on Android 2.3 by trailing skip-if, due to bug 980074
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/tests/testEventDispatcher.java
@@ -0,0 +1,159 @@
+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.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 org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * Tests the proper operation of EventDispatcher,
+ * including associated NativeJSObject objects.
+ */
+public class testEventDispatcher extends UITest
+        implements 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 JavascriptBridge js;
+
+    @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);
+    }
+
+    @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);
+
+        js.disconnect();
+        super.tearDown();
+    }
+
+    public void testEventDispatcher() {
+        GeckoHelper.blockForReady();
+        NavigationHelper.enterAndLoadUrl(StringHelper.ROBOCOP_JS_HARNESS_URL +
+                                         "?path=" + TEST_JS);
+
+        js.syncCall("send_test_message", GECKO_EVENT);
+        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);
+        js.syncCall("send_message_for_response", NATIVE_RESPONSE_EVENT, "success");
+        js.syncCall("send_message_for_response", NATIVE_RESPONSE_EVENT, "error");
+        js.syncCall("send_message_for_response", NATIVE_RESPONSE_EVENT, "cancel");
+        js.syncCall("finish_test");
+    }
+
+    @Override
+    public void handleMessage(final String event, final JSONObject message) {
+        ThreadUtils.assertOnGeckoThread();
+
+        try {
+            if (GECKO_EVENT.equals(event)) {
+                checkJSONObject(message);
+                checkJSONObject(message.getJSONObject("object"));
+
+            } else if (GECKO_RESPONSE_EVENT.equals(event)) {
+                final String response = message.getString("response");
+                if ("success".equals(response)) {
+                    EventDispatcher.getInstance().sendResponse(message, response);
+                } else if ("error".equals(response)) {
+                    EventDispatcher.getInstance().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));
+
+        } 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 if ("cancel".equals(response)) {
+                callback.sendCancel();
+            } else {
+                fFail("Response type should be valid: " + response);
+            }
+
+        } else {
+            fFail("Event type should be valid: " + event);
+        }
+    }
+
+    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"));
+    }
+
+    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"));
+    }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/tests/testEventDispatcher.js
@@ -0,0 +1,45 @@
+Components.utils.import("resource://gre/modules/Messaging.jsm");
+
+let java = new JavaBridge(this);
+
+do_register_cleanup(() => {
+  java.disconnect();
+});
+do_test_pending();
+
+function send_test_message(type) {
+  sendMessageToJava({
+    type: type,
+    boolean: true,
+    int: 1,
+    double: 0.5,
+    string: "foo",
+    object: {
+      boolean: true,
+      int: 1,
+      double: 0.5,
+      string: "foo",
+    },
+  });
+}
+
+function send_message_for_response(type, response) {
+  sendMessageToJava({
+    type: type,
+    response: response,
+  }, (success, error) => {
+    if (response === "success") {
+      do_check_eq(success, response);
+      do_check_eq(error, null);
+    } else if (response === "error") {
+      do_check_eq(success, null);
+      do_check_eq(error, response);
+    } else {
+      do_throw("Unexpected response: " + response);
+    }
+  });
+}
+
+function finish_test() {
+  do_test_finished();
+}
--- a/mobile/android/base/util/EventCallback.java
+++ b/mobile/android/base/util/EventCallback.java
@@ -1,17 +1,20 @@
 package org.mozilla.gecko.util;
 
+import org.mozilla.gecko.mozglue.RobocopTarget;
+
 /**
  * Callback interface for Gecko requests.
  *
  * For each instance of EventCallback, exactly one of sendResponse, sendError, or sendCancel
  * must be called to prevent observer leaks. If more than one send* method is called, or if a
  * single send method is called multiple times, an {@link IllegalStateException} will be thrown.
  */
+@RobocopTarget
 public interface EventCallback {
     /**
      * Sends a success response with the given data.
      *
      * @param response The response data to send to Gecko. Can be any of the types accepted by
      *                 JSONObject#put(String, Object).
      */
     public void sendSuccess(Object response);
--- a/mobile/android/base/util/ThreadUtils.java
+++ b/mobile/android/base/util/ThreadUtils.java
@@ -1,15 +1,17 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * 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.util;
 
+import org.mozilla.gecko.mozglue.RobocopTarget;
+
 import java.util.Map;
 
 import android.os.Handler;
 import android.os.MessageQueue;
 import android.util.Log;
 
 public final class ThreadUtils {
     private static final String LOGTAG = "ThreadUtils";
@@ -113,16 +115,17 @@ public final class ThreadUtils {
     public static void assertOnUiThread(final AssertBehavior assertBehavior) {
         assertOnThread(getUiThread(), assertBehavior);
     }
 
     public static void assertOnUiThread() {
         assertOnThread(getUiThread(), AssertBehavior.THROW);
     }
 
+    @RobocopTarget
     public static void assertOnGeckoThread() {
         assertOnThread(sGeckoThread, AssertBehavior.THROW);
     }
 
     public static void assertOnBackgroundThread() {
         assertOnThread(getBackgroundThread(), AssertBehavior.THROW);
     }