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 id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersmargaret
bugs896199
milestone25.0a1
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;