Bumping gaia.json for 2 gaia revision(s) a=gaia-bump
[gecko.git] / build / mobile / robocop / FennecNativeActions.java
blob2870ca25fcd4121bc4c49c3bdaf93b94010ae444
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());
43 class GeckoEventExpecter implements RepeatedEventExpecter {
44 private static final int MAX_WAIT_MS = 180000;
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");
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);
75 EventDispatcher.getInstance().registerGeckoThreadListener(mListener, mGeckoEvent);
76 mIsRegistered = true;
79 public void blockForEvent() {
80 blockForEvent(MAX_WAIT_MS, true);
83 public void blockForEvent(long millis, boolean failOnTimeout) {
84 if (!mIsRegistered) {
85 throw new IllegalStateException("listener not registered");
88 try {
89 mEventData = mEventDataQueue.poll(millis, TimeUnit.MILLISECONDS);
90 } catch (InterruptedException ie) {
91 FennecNativeDriver.log(LogLevel.ERROR, ie);
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);
102 } else {
103 FennecNativeDriver.log(FennecNativeDriver.LogLevel.DEBUG,
104 "unblocked on expecter for " + mGeckoEvent);
108 public void blockUntilClear(long millis) {
109 if (!mIsRegistered) {
110 throw new IllegalStateException("listener not registered");
112 if (millis <= 0) {
113 throw new IllegalArgumentException("millis must be > 0");
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);
122 if (mEventData == null) {
123 FennecNativeDriver.logAllStackTraces(FennecNativeDriver.LogLevel.ERROR);
124 mAsserter.ok(false, "GeckoEventExpecter", "blockUntilClear timeout");
125 return;
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);
134 if (mEventData == null) {
135 // success
136 break;
139 FennecNativeDriver.log(FennecNativeDriver.LogLevel.DEBUG,
140 "unblocked on expecter for " + mGeckoEvent);
143 public String blockForEventData() {
144 blockForEvent();
145 return mEventData;
148 public String blockForEventDataWithTimeout(long millis) {
149 blockForEvent(millis, false);
150 return mEventData;
153 public void unregisterListener() {
154 if (!mIsRegistered) {
155 throw new IllegalStateException("listener not registered");
158 FennecNativeDriver.log(LogLevel.INFO,
159 "EventExpecter: no longer listening for " + mGeckoEvent);
161 EventDispatcher.getInstance().unregisterGeckoThreadListener(mListener, mGeckoEvent);
162 mIsRegistered = false;
165 public boolean eventReceived() {
166 return mEventEverReceived;
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);
184 public RepeatedEventExpecter expectGeckoEvent(final String geckoEvent) {
185 FennecNativeDriver.log(FennecNativeDriver.LogLevel.DEBUG, "waiting for " + geckoEvent);
186 return new GeckoEventExpecter(geckoEvent);
189 public void sendGeckoEvent(final String geckoEvent, final String data) {
190 GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent(geckoEvent, data));
193 public void sendPreferencesGetEvent(int requestId, String[] prefNames) {
194 GeckoAppShell.sendEventToGecko(GeckoEvent.createPreferencesGetEvent(requestId, prefNames));
197 public void sendPreferencesObserveEvent(int requestId, String[] prefNames) {
198 GeckoAppShell.sendEventToGecko(GeckoEvent.createPreferencesObserveEvent(requestId, prefNames));
201 public void sendPreferencesRemoveObserversEvent(int requestId) {
202 GeckoAppShell.sendEventToGecko(GeckoEvent.createPreferencesRemoveObserversEvent(requestId));
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();
225 mLayerView.addDrawListener(mDrawListener);
226 mListening = true;
229 private synchronized void notifyOfEvent() {
230 mPaintDone = true;
231 this.notifyAll();
234 public synchronized void blockForEvent(long millis, boolean failOnTimeout) {
235 if (!mListening) {
236 throw new IllegalStateException("draw listener not registered");
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;
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");
253 return;
258 public synchronized void blockForEvent() {
259 blockForEvent(MAX_WAIT_MS, true);
262 public synchronized String blockForEventData() {
263 blockForEvent();
264 return null;
267 public synchronized String blockForEventDataWithTimeout(long millis) {
268 blockForEvent(millis, false);
269 return null;
272 public synchronized boolean eventReceived() {
273 return mPaintDone;
276 public synchronized void blockUntilClear(long millis) {
277 if (!mListening) {
278 throw new IllegalStateException("draw listener not registered");
280 if (millis <= 0) {
281 throw new IllegalArgumentException("millis must be > 0");
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;
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;
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;
309 endTime = SystemClock.uptimeMillis();
310 if (endTime - startTime >= millis) {
311 // success
312 break;
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;
321 public synchronized void unregisterListener() {
322 if (!mListening) {
323 throw new IllegalStateException("listener not registered");
326 FennecNativeDriver.log(LogLevel.INFO,
327 "PaintExpecter: no longer listening for events");
328 mLayerView.removeDrawListener(mDrawListener);
329 mListening = false;
333 public RepeatedEventExpecter expectPaint() {
334 return new PaintExpecter();
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;
366 public void sendKeyCode(int keyCode) {
367 if (keyCode <= 0 || keyCode > KeyEvent.getMaxKeyCode()) {
368 mAsserter.ok(false, "sendKeyCode", "Unknown keyCode " + keyCode);
370 mInstr.sendCharacterSync(keyCode);
373 @Override
374 public void sendKeys(String input) {
375 mInstr.sendStringSync(input);
378 public void drag(int startingX, int endingX, int startingY, int endingY) {
379 mSolo.drag(startingX, endingX, startingY, endingY, 10);
382 public Cursor querySql(final String dbPath, final String sql) {
383 return new SQLiteBridge(dbPath).rawQuery(sql, null);