build/mobile/robocop/FennecNativeActions.java

Wed, 31 Dec 2014 07:16:47 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:16:47 +0100
branch
TOR_BUG_9701
changeset 3
141e0f1194b1
permissions
-rw-r--r--

Revert simplistic fix pending revisit of Mozilla integration attempt.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 package org.mozilla.gecko;
     7 import java.util.concurrent.BlockingQueue;
     8 import java.util.concurrent.LinkedBlockingQueue;
     9 import java.util.concurrent.TimeUnit;
    11 import org.json.JSONObject;
    12 import org.mozilla.gecko.FennecNativeDriver.LogLevel;
    13 import org.mozilla.gecko.gfx.LayerView;
    14 import org.mozilla.gecko.gfx.LayerView.DrawListener;
    15 import org.mozilla.gecko.mozglue.GeckoLoader;
    16 import org.mozilla.gecko.sqlite.SQLiteBridge;
    17 import org.mozilla.gecko.util.GeckoEventListener;
    19 import android.app.Activity;
    20 import android.app.Instrumentation;
    21 import android.database.Cursor;
    22 import android.os.SystemClock;
    23 import android.text.TextUtils;
    24 import android.view.KeyEvent;
    26 import com.jayway.android.robotium.solo.Solo;
    28 public class FennecNativeActions implements Actions {
    29     private static final String LOGTAG = "FennecNativeActions";
    31     private Solo mSolo;
    32     private Instrumentation mInstr;
    33     private Assert mAsserter;
    35     public FennecNativeActions(Activity activity, Solo robocop, Instrumentation instrumentation, Assert asserter) {
    36         mSolo = robocop;
    37         mInstr = instrumentation;
    38         mAsserter = asserter;
    40         GeckoLoader.loadSQLiteLibs(activity, activity.getApplication().getPackageResourcePath());
    41     }
    43     class GeckoEventExpecter implements RepeatedEventExpecter {
    44         private static final int MAX_WAIT_MS = 90000;
    46         private volatile boolean mIsRegistered;
    48         private final String mGeckoEvent;
    49         private final GeckoEventListener mListener;
    51         private volatile boolean mEventEverReceived;
    52         private String mEventData;
    53         private BlockingQueue<String> mEventDataQueue;
    55         GeckoEventExpecter(final String geckoEvent) {
    56             if (TextUtils.isEmpty(geckoEvent)) {
    57                 throw new IllegalArgumentException("geckoEvent must not be empty");
    58             }
    60             mGeckoEvent = geckoEvent;
    61             mEventDataQueue = new LinkedBlockingQueue<String>();
    63             final GeckoEventExpecter expecter = this;
    64             mListener = new GeckoEventListener() {
    65                 @Override
    66                 public void handleMessage(final String event, final JSONObject message) {
    67                     FennecNativeDriver.log(FennecNativeDriver.LogLevel.DEBUG,
    68                             "handleMessage called for: " + event + "; expecting: " + mGeckoEvent);
    69                     mAsserter.is(event, mGeckoEvent, "Given message occurred for registered event: " + message);
    71                     expecter.notifyOfEvent(message);
    72                 }
    73             };
    75             GeckoAppShell.registerEventListener(mGeckoEvent, mListener);
    76             mIsRegistered = true;
    77         }
    79         public void blockForEvent() {
    80             blockForEvent(MAX_WAIT_MS, true);
    81         }
    83         public void blockForEvent(long millis, boolean failOnTimeout) {
    84             if (!mIsRegistered) {
    85                 throw new IllegalStateException("listener not registered");
    86             }
    88             try {
    89                 mEventData = mEventDataQueue.poll(millis, TimeUnit.MILLISECONDS);
    90             } catch (InterruptedException ie) {
    91                 FennecNativeDriver.log(LogLevel.ERROR, ie);
    92             }
    93             if (mEventData == null) {
    94                 if (failOnTimeout) {
    95                     FennecNativeDriver.logAllStackTraces(FennecNativeDriver.LogLevel.ERROR);
    96                     mAsserter.ok(false, "GeckoEventExpecter",
    97                         "blockForEvent timeout: "+mGeckoEvent);
    98                 } else {
    99                     FennecNativeDriver.log(FennecNativeDriver.LogLevel.DEBUG,
   100                         "blockForEvent timeout: "+mGeckoEvent);
   101                 }
   102             } else {
   103                 FennecNativeDriver.log(FennecNativeDriver.LogLevel.DEBUG,
   104                     "unblocked on expecter for " + mGeckoEvent);
   105             }
   106         }
   108         public void blockUntilClear(long millis) {
   109             if (!mIsRegistered) {
   110                 throw new IllegalStateException("listener not registered");
   111             }
   112             if (millis <= 0) {
   113                 throw new IllegalArgumentException("millis must be > 0");
   114             }
   116             // wait for at least one event
   117             try {
   118                 mEventData = mEventDataQueue.poll(MAX_WAIT_MS, TimeUnit.MILLISECONDS);
   119             } catch (InterruptedException ie) {
   120                 FennecNativeDriver.log(LogLevel.ERROR, ie);
   121             }
   122             if (mEventData == null) {
   123                 FennecNativeDriver.logAllStackTraces(FennecNativeDriver.LogLevel.ERROR);
   124                 mAsserter.ok(false, "GeckoEventExpecter", "blockUntilClear timeout");
   125                 return;
   126             }
   127             // now wait for a period of millis where we don't get an event
   128             while (true) {
   129                 try {
   130                     mEventData = mEventDataQueue.poll(millis, TimeUnit.MILLISECONDS);
   131                 } catch (InterruptedException ie) {
   132                     FennecNativeDriver.log(LogLevel.INFO, ie);
   133                 }
   134                 if (mEventData == null) {
   135                     // success
   136                     break;
   137                 }
   138             }
   139             FennecNativeDriver.log(FennecNativeDriver.LogLevel.DEBUG,
   140                 "unblocked on expecter for " + mGeckoEvent);
   141         }
   143         public String blockForEventData() {
   144             blockForEvent();
   145             return mEventData;
   146         }
   148         public String blockForEventDataWithTimeout(long millis) {
   149             blockForEvent(millis, false);
   150             return mEventData;
   151         }
   153         public void unregisterListener() {
   154             if (!mIsRegistered) {
   155                 throw new IllegalStateException("listener not registered");
   156             }
   158             FennecNativeDriver.log(LogLevel.INFO,
   159                     "EventExpecter: no longer listening for " + mGeckoEvent);
   161             GeckoAppShell.unregisterEventListener(mGeckoEvent, mListener);
   162             mIsRegistered = false;
   163         }
   165         public boolean eventReceived() {
   166             return mEventEverReceived;
   167         }
   169         void notifyOfEvent(final JSONObject message) {
   170             FennecNativeDriver.log(FennecNativeDriver.LogLevel.DEBUG,
   171                     "received event " + mGeckoEvent);
   173             mEventEverReceived = true;
   175             try {
   176                 mEventDataQueue.put(message.toString());
   177             } catch (InterruptedException e) {
   178                 FennecNativeDriver.log(LogLevel.ERROR,
   179                     "EventExpecter dropped event: " + message.toString(), e);
   180             }
   181         }
   182     }
   184     public RepeatedEventExpecter expectGeckoEvent(final String geckoEvent) {
   185         FennecNativeDriver.log(FennecNativeDriver.LogLevel.DEBUG, "waiting for " + geckoEvent);
   186         return new GeckoEventExpecter(geckoEvent);
   187     }
   189     public void sendGeckoEvent(final String geckoEvent, final String data) {
   190         GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent(geckoEvent, data));
   191     }
   193     public void sendPreferencesGetEvent(int requestId, String[] prefNames) {
   194         GeckoAppShell.sendEventToGecko(GeckoEvent.createPreferencesGetEvent(requestId, prefNames));
   195     }
   197     public void sendPreferencesObserveEvent(int requestId, String[] prefNames) {
   198         GeckoAppShell.sendEventToGecko(GeckoEvent.createPreferencesObserveEvent(requestId, prefNames));
   199     }
   201     public void sendPreferencesRemoveObserversEvent(int requestId) {
   202         GeckoAppShell.sendEventToGecko(GeckoEvent.createPreferencesRemoveObserversEvent(requestId));
   203     }
   205     class PaintExpecter implements RepeatedEventExpecter {
   206         private static final int MAX_WAIT_MS = 90000;
   208         private boolean mPaintDone;
   209         private boolean mListening;
   211         private final LayerView mLayerView;
   212         private final DrawListener mDrawListener;
   214         PaintExpecter() {
   215             final PaintExpecter expecter = this;
   216             mLayerView = GeckoAppShell.getLayerView();
   217             mDrawListener = new DrawListener() {
   218                 @Override
   219                 public void drawFinished() {
   220                     FennecNativeDriver.log(FennecNativeDriver.LogLevel.DEBUG,
   221                             "Received drawFinished notification");
   222                     expecter.notifyOfEvent();
   223                 }
   224             };
   225             mLayerView.addDrawListener(mDrawListener);
   226             mListening = true;
   227         }
   229         private synchronized void notifyOfEvent() {
   230             mPaintDone = true;
   231             this.notifyAll();
   232         }
   234         public synchronized void blockForEvent(long millis, boolean failOnTimeout) {
   235             if (!mListening) {
   236                 throw new IllegalStateException("draw listener not registered");
   237             }
   238             long startTime = SystemClock.uptimeMillis();
   239             long endTime = 0;
   240             while (!mPaintDone) {
   241                 try {
   242                     this.wait(millis);
   243                 } catch (InterruptedException ie) {
   244                     FennecNativeDriver.log(LogLevel.ERROR, ie);
   245                     break;
   246                 }
   247                 endTime = SystemClock.uptimeMillis();
   248                 if (!mPaintDone && (endTime - startTime >= millis)) {
   249                     if (failOnTimeout) {
   250                         FennecNativeDriver.logAllStackTraces(FennecNativeDriver.LogLevel.ERROR);
   251                         mAsserter.ok(false, "PaintExpecter", "blockForEvent timeout");
   252                     }
   253                     return;
   254                 }
   255             }
   256         }
   258         public synchronized void blockForEvent() {
   259             blockForEvent(MAX_WAIT_MS, true);
   260         }
   262         public synchronized String blockForEventData() {
   263             blockForEvent();
   264             return null;
   265         }
   267         public synchronized String blockForEventDataWithTimeout(long millis) {
   268             blockForEvent(millis, false);
   269             return null;
   270         }
   272         public synchronized boolean eventReceived() {
   273             return mPaintDone;
   274         }
   276         public synchronized void blockUntilClear(long millis) {
   277             if (!mListening) {
   278                 throw new IllegalStateException("draw listener not registered");
   279             }
   280             if (millis <= 0) {
   281                 throw new IllegalArgumentException("millis must be > 0");
   282             }
   283             // wait for at least one event
   284             long startTime = SystemClock.uptimeMillis();
   285             long endTime = 0;
   286             while (!mPaintDone) {
   287                 try {
   288                     this.wait(MAX_WAIT_MS);
   289                 } catch (InterruptedException ie) {
   290                     FennecNativeDriver.log(LogLevel.ERROR, ie);
   291                     break;
   292                 }
   293                 endTime = SystemClock.uptimeMillis();
   294                 if (!mPaintDone && (endTime - startTime >= MAX_WAIT_MS)) {
   295                     FennecNativeDriver.logAllStackTraces(FennecNativeDriver.LogLevel.ERROR);
   296                     mAsserter.ok(false, "PaintExpecter", "blockUtilClear timeout");
   297                     return;
   298                 }
   299             }
   300             // now wait for a period of millis where we don't get an event
   301             startTime = SystemClock.uptimeMillis();
   302             while (true) {
   303                 try {
   304                     this.wait(millis);
   305                 } catch (InterruptedException ie) {
   306                     FennecNativeDriver.log(LogLevel.ERROR, ie);
   307                     break;
   308                 }
   309                 endTime = SystemClock.uptimeMillis();
   310                 if (endTime - startTime >= millis) {
   311                     // success
   312                     break;
   313                 }
   315                 // we got a notify() before we could wait long enough, so we need to start over
   316                 // Note, moving the goal post might have us race against a "drawFinished" flood
   317                 startTime = endTime;
   318             }
   319         }
   321         public synchronized void unregisterListener() {
   322             if (!mListening) {
   323                 throw new IllegalStateException("listener not registered");
   324             }
   326             FennecNativeDriver.log(LogLevel.INFO,
   327                     "PaintExpecter: no longer listening for events");
   328             mLayerView.removeDrawListener(mDrawListener);
   329             mListening = false;
   330         }
   331     }
   333     public RepeatedEventExpecter expectPaint() {
   334         return new PaintExpecter();
   335     }
   337     public void sendSpecialKey(SpecialKey button) {
   338         switch(button) {
   339             case DOWN:
   340                 sendKeyCode(KeyEvent.KEYCODE_DPAD_DOWN);
   341                 break;
   342             case UP:
   343                 sendKeyCode(KeyEvent.KEYCODE_DPAD_UP);
   344                 break;
   345             case LEFT:
   346                 sendKeyCode(KeyEvent.KEYCODE_DPAD_LEFT);
   347                 break;
   348             case RIGHT:
   349                 sendKeyCode(KeyEvent.KEYCODE_DPAD_RIGHT);
   350                 break;
   351             case ENTER:
   352                 sendKeyCode(KeyEvent.KEYCODE_ENTER);
   353                 break;
   354             case MENU:
   355                 sendKeyCode(KeyEvent.KEYCODE_MENU);
   356                 break;
   357             case BACK:
   358                 sendKeyCode(KeyEvent.KEYCODE_BACK);
   359                 break;
   360             default:
   361                 mAsserter.ok(false, "sendSpecialKey", "Unknown SpecialKey " + button);
   362                 break;
   363         }
   364     }
   366     public void sendKeyCode(int keyCode) {
   367         if (keyCode <= 0 || keyCode > KeyEvent.getMaxKeyCode()) {
   368             mAsserter.ok(false, "sendKeyCode", "Unknown keyCode " + keyCode);
   369         }
   370         mInstr.sendCharacterSync(keyCode);
   371     }
   373     @Override
   374     public void sendKeys(String input) {
   375         mInstr.sendStringSync(input);
   376     }
   378     public void drag(int startingX, int endingX, int startingY, int endingY) {
   379         mSolo.drag(startingX, endingX, startingY, endingY, 10);
   380     }
   382     public Cursor querySql(final String dbPath, final String sql) {
   383         return new SQLiteBridge(dbPath).rawQuery(sql, null);
   384     }
   385 }

mercurial