mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-28 23:31:56 +00:00
Bug 720144 - Expose methods in robocop to grab the painted surface and to compare pixels. r=jmaher
This commit is contained in:
parent
de6b967c0a
commit
f34c6b083c
@ -60,6 +60,12 @@ public interface Actions {
|
||||
* @param geckoEvent The geckoEvent JSONObject's type
|
||||
*/
|
||||
EventExpecter expectGeckoEvent(String geckoEvent);
|
||||
|
||||
/**
|
||||
* Listens for a paint event.
|
||||
*/
|
||||
EventExpecter expectPaint();
|
||||
|
||||
// Send the string kewsToSend to the application
|
||||
void sendKeys(String keysToSend);
|
||||
//Send any of the above keys to the element
|
||||
|
@ -52,4 +52,7 @@ public interface Assert {
|
||||
void todo_is(Object a, Object b, String name);
|
||||
void todo_isnot(Object a, Object b, String name);
|
||||
void info(String name, String message);
|
||||
|
||||
// robocop-specific asserts
|
||||
void ispixel(int actual, int r, int g, int b, String name);
|
||||
}
|
||||
|
@ -68,4 +68,11 @@ public interface Driver {
|
||||
|
||||
void startFrameRecording();
|
||||
int stopFrameRecording();
|
||||
|
||||
/**
|
||||
* Get a copy of the painted content region.
|
||||
* @return A 2-D array of pixels (indexed by y, then x). The pixels
|
||||
* are in ARGB-8888 format.
|
||||
*/
|
||||
int[][] getPaintedSurface();
|
||||
}
|
||||
|
@ -73,20 +73,24 @@ public class FennecNativeActions implements Actions {
|
||||
// Map of IDs to element names.
|
||||
private Solo solo;
|
||||
private Instrumentation instr;
|
||||
private Activity geckoApp;
|
||||
|
||||
// Objects for reflexive access of fennec classes.
|
||||
private ClassLoader classLoader;
|
||||
private Class gel;
|
||||
private Class ge;
|
||||
private Class gas;
|
||||
private Class drawListener;
|
||||
private Method registerGEL;
|
||||
private Method unregisterGEL;
|
||||
private Method sendGE;
|
||||
|
||||
private Method getLayerClient;
|
||||
private Method setDrawListener;
|
||||
|
||||
public FennecNativeActions(Activity activity, Solo robocop, Instrumentation instrumentation){
|
||||
this.solo = robocop;
|
||||
this.instr = instrumentation;
|
||||
this.geckoApp = activity;
|
||||
// Set up reflexive access of java classes and methods.
|
||||
try {
|
||||
classLoader = activity.getClassLoader();
|
||||
@ -101,6 +105,11 @@ public class FennecNativeActions implements Actions {
|
||||
parameters = new Class[1];
|
||||
parameters[0] = ge;
|
||||
sendGE = gas.getMethod("sendEventToGecko", parameters);
|
||||
|
||||
getLayerClient = activity.getClass().getMethod("getSoftwareLayerClient");
|
||||
Class gslc = classLoader.loadClass("org.mozilla.gecko.gfx.GeckoSoftwareLayerClient");
|
||||
drawListener = classLoader.loadClass("org.mozilla.gecko.gfx.GeckoSoftwareLayerClient$DrawListener");
|
||||
setDrawListener = gslc.getDeclaredMethod("setDrawListener", drawListener);
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (SecurityException e) {
|
||||
@ -205,6 +214,75 @@ public class FennecNativeActions implements Actions {
|
||||
return null;
|
||||
}
|
||||
|
||||
class DrawListenerProxy implements InvocationHandler {
|
||||
private final PaintExpecter mPaintExpecter;
|
||||
|
||||
DrawListenerProxy(PaintExpecter paintExpecter) {
|
||||
mPaintExpecter = paintExpecter;
|
||||
}
|
||||
|
||||
public Object invoke(Object proxy, Method method, Object[] args) {
|
||||
String methodName = method.getName();
|
||||
if ("drawFinished".equals(methodName)) {
|
||||
Log.i("Robocop", "Received drawFinished notification");
|
||||
mPaintExpecter.notifyOfEvent();
|
||||
} else if ("toString".equals(methodName)) {
|
||||
return "DrawListenerProxy";
|
||||
} else if ("equals".equals(methodName)) {
|
||||
return false;
|
||||
} else if ("hashCode".equals(methodName)) {
|
||||
return 0;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class PaintExpecter implements EventExpecter {
|
||||
private Object mLayerClient;
|
||||
private boolean mPaintDone;
|
||||
|
||||
PaintExpecter() throws IllegalAccessException, InvocationTargetException {
|
||||
mLayerClient = getLayerClient.invoke(geckoApp);
|
||||
setDrawListener.invoke(mLayerClient, Proxy.newProxyInstance(classLoader, new Class[] { drawListener }, new DrawListenerProxy(this)));
|
||||
}
|
||||
|
||||
void notifyOfEvent() {
|
||||
try {
|
||||
setDrawListener.invoke(mLayerClient, (Object)null);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
synchronized (this) {
|
||||
mPaintDone = true;
|
||||
this.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void blockForEvent() {
|
||||
while (! mPaintDone) {
|
||||
try {
|
||||
this.wait();
|
||||
} catch (InterruptedException ie) {
|
||||
ie.printStackTrace();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized boolean eventReceived() {
|
||||
return mPaintDone;
|
||||
}
|
||||
}
|
||||
|
||||
public EventExpecter expectPaint() {
|
||||
try {
|
||||
return new PaintExpecter();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void sendSpecialKey(SpecialKey button) {
|
||||
switch( button) {
|
||||
case DOWN:
|
||||
|
@ -243,6 +243,23 @@ public class FennecNativeAssert implements Assert {
|
||||
ok(pass, name, diag);
|
||||
}
|
||||
|
||||
public void ispixel(int actual, int r, int g, int b, String name) {
|
||||
// When we read GL pixels the GPU has already processed them and they
|
||||
// are usually off by a little bit. For example a CSS-color pixel of color #64FFF5
|
||||
// was turned into #63FFF7 when it came out of glReadPixels. So in order to compare
|
||||
// against the expected value, we use a little fuzz factor. For the alpha we just
|
||||
// make sure it is always 0xFF.
|
||||
int aAlpha = ((actual >> 24) & 0xFF);
|
||||
int aR = ((actual >> 16) & 0xFF);
|
||||
int aG = ((actual >> 8) & 0xFF);
|
||||
int aB = (actual & 0xFF);
|
||||
boolean pass = (aAlpha == 0xFF) /* alpha */
|
||||
&& (Math.abs(aR - r) < 8) /* red */
|
||||
&& (Math.abs(aG - g) < 8) /* green */
|
||||
&& (Math.abs(aB - b) < 8); /* blue */
|
||||
ok(pass, name, "Color rgba(" + aR + "," + aG + "," + aB + "," + aAlpha + ")" + (pass ? " " : " not") + " close enough to expected rgb(" + r + "," + g + "," + b + ")");
|
||||
}
|
||||
|
||||
public void todo(boolean condition, String name, String diag) {
|
||||
testInfo test = new testInfo(condition, name, diag, true);
|
||||
_logMochitestResult(test, "TEST-UNEXPECTED-PASS", "TEST-KNOWN-FAIL");
|
||||
|
@ -45,6 +45,7 @@ import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.HashMap;
|
||||
@ -58,6 +59,7 @@ import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.Long;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.opengl.GLSurfaceView;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
@ -81,6 +83,7 @@ public class FennecNativeDriver implements Driver {
|
||||
private Method sendGE;
|
||||
private Method _startFrameRecording;
|
||||
private Method _stopFrameRecording;
|
||||
private Method _getPixels;
|
||||
|
||||
public FennecNativeDriver(Activity activity, Solo robocop){
|
||||
this.activity = activity;
|
||||
@ -107,6 +110,9 @@ public class FennecNativeDriver implements Driver {
|
||||
Class gfx = classLoader.loadClass("org.mozilla.gecko.gfx.PanningPerfAPI");
|
||||
_startFrameRecording = gfx.getDeclaredMethod("startFrameTimeRecording");
|
||||
_stopFrameRecording = gfx.getDeclaredMethod("stopFrameTimeRecording");
|
||||
|
||||
Class layerView = classLoader.loadClass("org.mozilla.gecko.gfx.LayerView");
|
||||
_getPixels = layerView.getDeclaredMethod("getPixels");
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (SecurityException e) {
|
||||
@ -212,6 +218,43 @@ public class FennecNativeDriver implements Driver {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private GLSurfaceView getSurfaceView() {
|
||||
for (View v : solo.getCurrentViews()) {
|
||||
if (v instanceof GLSurfaceView) {
|
||||
return (GLSurfaceView)v;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public int[][] getPaintedSurface() {
|
||||
GLSurfaceView view = getSurfaceView();
|
||||
if (view == null) {
|
||||
return null;
|
||||
}
|
||||
IntBuffer pixelBuffer;
|
||||
try {
|
||||
pixelBuffer = (IntBuffer)_getPixels.invoke(view);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
|
||||
// now we need to (1) flip the image, because GL likes to do things up-side-down,
|
||||
// and (2) rearrange the bits from AGBR-8888 to ARGB-8888.
|
||||
int w = view.getWidth();
|
||||
int h = view.getHeight();
|
||||
pixelBuffer.position(0);
|
||||
int[][] pixels = new int[h][w];
|
||||
for (int y = h - 1; y >= 0; y--) {
|
||||
for (int x = 0; x < w; x++) {
|
||||
int agbr = pixelBuffer.get();
|
||||
pixels[y][x] = (agbr & 0xFF00FF00) | ((agbr >> 16) & 0x000000FF) | ((agbr << 16) & 0x00FF0000);
|
||||
}
|
||||
}
|
||||
return pixels;
|
||||
}
|
||||
|
||||
class scrollHandler implements InvocationHandler {
|
||||
public scrollHandler(){};
|
||||
public Object invoke(Object proxy, Method method, Object[] args) {
|
||||
|
@ -545,12 +545,12 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL
|
||||
return Color.rgb(r, g, b);
|
||||
}
|
||||
|
||||
/** Used by robocop for testing purposes. Not for production use! */
|
||||
/** Used by robocop for testing purposes. Not for production use! This is called via reflection by robocop. */
|
||||
public void setDrawListener(DrawListener listener) {
|
||||
mDrawListener = listener;
|
||||
}
|
||||
|
||||
/** Used by robocop for testing purposes. Not for production use! */
|
||||
/** Used by robocop for testing purposes. Not for production use! This is used via reflection by robocop. */
|
||||
public interface DrawListener {
|
||||
public void drawFinished(int x, int y, int width, int height);
|
||||
}
|
||||
|
@ -239,7 +239,7 @@ public class LayerView extends GLSurfaceView
|
||||
return mRenderer.getMaxTextureSize();
|
||||
}
|
||||
|
||||
/** Used by robocop for testing purposes. Not for production use! */
|
||||
/** Used by robocop for testing purposes. Not for production use! This is called via reflection by robocop. */
|
||||
public IntBuffer getPixels() {
|
||||
return mRenderer.getPixels();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user