Bug 896199 - Add event queue for Robocop; r=margaret
authorGeoff Brown <gbrown@mozilla.com>
Fri, 26 Jul 2013 15:16:43 -0700
changeset 140228 2ee2dc1d2bcc0d8210fcf1dfe61e66284cec4026
parent 140227 95cdd796f48126be2f0b275c1e526cd4b973422a
child 140229 f570fc641c5f087fc00e87708ecda8053495c3ad
push id25020
push userryanvm@gmail.com
push dateSat, 27 Jul 2013 22:13:14 +0000
treeherdermozilla-central@75e2498668df [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmargaret
bugs896199
milestone25.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 896199 - Add event queue for Robocop; r=margaret
build/mobile/robocop/FennecNativeActions.java.in
build/mobile/robocop/FennecNativeElement.java.in
--- a/build/mobile/robocop/FennecNativeActions.java.in
+++ b/build/mobile/robocop/FennecNativeActions.java.in
@@ -4,30 +4,30 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package @ANDROID_PACKAGE_NAME@;
 
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
 import java.lang.reflect.InvocationHandler;
-import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
 import java.util.ArrayList;
 
 import android.app.Activity;
+import android.app.Instrumentation;
 import android.content.Context;
-import android.app.Instrumentation;
 import android.database.Cursor;
 import android.os.SystemClock;
 import android.text.TextUtils;
-import android.view.View;
+import android.util.Log;
 import android.view.KeyEvent;
-import android.util.Log;
-
-import org.json.*;
+import android.view.View;
 
 import com.jayway.android.robotium.solo.Solo;
 
 import static @ANDROID_PACKAGE_NAME@.FennecNativeDriver.LogLevel;
 
 public class FennecNativeActions implements Actions {
     private Solo mSolo;
     private Instrumentation mInstr;
@@ -102,123 +102,107 @@ public class FennecNativeActions impleme
             mEventExpecter.notifyOfEvent(args);
             return null;
         }
     }
 
     class GeckoEventExpecter implements RepeatedEventExpecter {
         private final String mGeckoEvent;
         private Object[] mRegistrationParams;
-        private boolean mEventReceived;
         private boolean mEventEverReceived;
         private String mEventData;
+        private BlockingQueue<String> mEventDataQueue;
         private static final int MAX_WAIT_MS = 90000;
 
         GeckoEventExpecter(String geckoEvent, Object[] registrationParams) {
             if (TextUtils.isEmpty(geckoEvent)) {
                 throw new IllegalArgumentException("geckoEvent must not be empty");
             }
             if (registrationParams == null || registrationParams.length == 0) {
                 throw new IllegalArgumentException("registrationParams must not be empty");
             }
 
             mGeckoEvent = geckoEvent;
             mRegistrationParams = registrationParams;
+            mEventDataQueue = new LinkedBlockingQueue<String>();
         }
 
-        public synchronized void blockForEvent() {
+        public void blockForEvent() {
             blockForEvent(MAX_WAIT_MS, true);
         }
 
-        private synchronized void blockForEvent(long millis, boolean failOnTimeout) {
+        private void blockForEvent(long millis, boolean failOnTimeout) {
             if (mRegistrationParams == null) {
                 throw new IllegalStateException("listener not registered");
             }
-            long startTime = SystemClock.uptimeMillis();
-            long endTime = 0;
-            while (! mEventReceived) {
-                try {
-                    this.wait(millis);
-                } catch (InterruptedException ie) {
-                    FennecNativeDriver.log(LogLevel.ERROR, ie);
-                    break;
+            try {
+                mEventData = mEventDataQueue.poll(millis, TimeUnit.MILLISECONDS);
+            } catch (InterruptedException ie) {
+                FennecNativeDriver.log(LogLevel.ERROR, ie);
+            }
+            if (mEventData == null) {
+                if (failOnTimeout) {
+                    FennecNativeDriver.logAllStackTraces(FennecNativeDriver.LogLevel.ERROR);
+                    mAsserter.ok(false, "GeckoEventExpecter",
+                        "blockForEvent timeout: "+mGeckoEvent);
+                } else {
+                    FennecNativeDriver.log(FennecNativeDriver.LogLevel.DEBUG,
+                        "blockForEvent timeout: "+mGeckoEvent);
                 }
-                endTime = SystemClock.uptimeMillis();
-                if (!mEventReceived && (endTime - startTime >= millis)) {
-                    if (failOnTimeout) {
-                        FennecNativeDriver.logAllStackTraces(FennecNativeDriver.LogLevel.ERROR);
-                        mAsserter.ok(false, "GeckoEventExpecter", 
-                            "blockForEvent timeout: "+mGeckoEvent);
-                    }
-                    mEventData = null;
-                    return;
-                }
+            } else {
+                FennecNativeDriver.log(FennecNativeDriver.LogLevel.DEBUG,
+                    "unblocked on expecter for " + mGeckoEvent);
             }
-            FennecNativeDriver.log(FennecNativeDriver.LogLevel.DEBUG,
-                "unblocked on expecter for " + mGeckoEvent);
-            mEventReceived = false;
         }
 
-        public synchronized void blockUntilClear(long millis) {
+        public void blockUntilClear(long millis) {
             if (mRegistrationParams == null) {
                 throw new IllegalStateException("listener not registered");
             }
             if (millis <= 0) {
                 throw new IllegalArgumentException("millis must be > 0");
             }
             // wait for at least one event
-            long startTime = SystemClock.uptimeMillis();
-            long endTime = 0;
-            while (!mEventReceived) {
-                try {
-                    this.wait(MAX_WAIT_MS);
-                } catch (InterruptedException ie) {
-                    FennecNativeDriver.log(LogLevel.ERROR, ie);
-                    break;
-                }
-                endTime = SystemClock.uptimeMillis();
-                if (!mEventReceived && (endTime - startTime >= MAX_WAIT_MS)) {
-                    FennecNativeDriver.logAllStackTraces(FennecNativeDriver.LogLevel.ERROR);
-                    mAsserter.ok(false, "GeckoEventExpecter", "blockUtilClear timeout");
-                    return;
-                }
+            try {
+                mEventData = mEventDataQueue.poll(MAX_WAIT_MS, TimeUnit.MILLISECONDS);
+            } catch (InterruptedException ie) {
+                FennecNativeDriver.log(LogLevel.ERROR, ie);
+            }
+            if (mEventData == null) {
+                FennecNativeDriver.logAllStackTraces(FennecNativeDriver.LogLevel.ERROR);
+                mAsserter.ok(false, "GeckoEventExpecter", "blockUntilClear timeout");
+                return;
             }
             // now wait for a period of millis where we don't get an event
-            startTime = SystemClock.uptimeMillis();
             while (true) {
                 try {
-                    this.wait(millis);
+                    mEventData = mEventDataQueue.poll(millis, TimeUnit.MILLISECONDS);
                 } catch (InterruptedException ie) {
-                    FennecNativeDriver.log(LogLevel.ERROR, ie);
-                    break;
+                    FennecNativeDriver.log(LogLevel.INFO, ie);
                 }
-                endTime = SystemClock.uptimeMillis();
-                if (endTime - startTime >= millis) {
+                if (mEventData == null) {
                     // success
                     break;
                 }
-                // we got a notify() before we could wait long enough, so we need to start over
-                startTime = endTime;
             }
             FennecNativeDriver.log(FennecNativeDriver.LogLevel.DEBUG,
                 "unblocked on expecter for " + mGeckoEvent);
-            mEventReceived = false;
         }
 
-        public synchronized String blockForEventData() {
+        public String blockForEventData() {
             blockForEvent();
             return mEventData;
         }
 
-        public synchronized String blockForEventDataWithTimeout(long millis) {
+        public String blockForEventDataWithTimeout(long millis) {
             blockForEvent(millis, false);
             return mEventData;
         }
 
-        public synchronized void unregisterListener() {
+        public void unregisterListener() {
             if (mRegistrationParams == null) {
                 throw new IllegalStateException("listener not registered");
             }
             try {
                 FennecNativeDriver.log(LogLevel.INFO, "EventExpecter: no longer listening for "+mGeckoEvent);
                 mUnregisterEventListener.invoke(mRobocopApi, mRegistrationParams);
                 mRegistrationParams = null;
             } catch (IllegalAccessException e) {
@@ -231,24 +215,28 @@ public class FennecNativeActions impleme
         public synchronized boolean eventReceived() {
             return mEventEverReceived;
         }
 
         void notifyOfEvent(Object[] args) {
             FennecNativeDriver.log(FennecNativeDriver.LogLevel.DEBUG,
                 "received event " + mGeckoEvent);
             synchronized (this) {
-                mEventReceived = true;
                 mEventEverReceived = true;
-                mEventData = args[1].toString();
-                this.notifyAll();
+            }
+            try {
+                mEventDataQueue.put(args[1].toString());
+            } catch (InterruptedException e) {
+                FennecNativeDriver.log(LogLevel.ERROR,
+                    "EventExpecter dropped event: "+args[1].toString());
+                FennecNativeDriver.log(LogLevel.ERROR, e);
             }
         }
     }
-    
+
     public RepeatedEventExpecter expectGeckoEvent(String geckoEvent) {
         FennecNativeDriver.log(FennecNativeDriver.LogLevel.DEBUG,
             "waiting for "+geckoEvent);
         try {
             Object[] finalParams = new Object[2];
             finalParams[0] = geckoEvent;
             GeckoEventExpecter expecter = new GeckoEventExpecter(geckoEvent, finalParams);
             wakeInvocationHandler wIH = new wakeInvocationHandler(expecter);
--- a/build/mobile/robocop/FennecNativeElement.java.in
+++ b/build/mobile/robocop/FennecNativeElement.java.in
@@ -1,29 +1,26 @@
 #filter substitution
 /* 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 @ANDROID_PACKAGE_NAME@;
 
-import java.util.List;
-
 import android.app.Activity;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.Button;
 import android.widget.EditText;
 import android.widget.ListView;
 import android.widget.TextView;
 import android.widget.TextSwitcher;
 import android.app.Instrumentation;
 import com.jayway.android.robotium.solo.Solo;
-import java.util.concurrent.SynchronousQueue;
-import java.util.concurrent.TimeUnit;
+import java.util.List;
 
 public class FennecNativeElement implements Element {
     private final Activity mActivity;
     private Integer mId;
     private Solo mSolo;
     // max time to wait for thread synchronization
     private static final int MAX_WAIT_MS = 60000;