build/mobile/robocop/FennecNativeDriver.java.in
author Joel Maher <jmaher@mozilla.com>
Tue, 10 Jan 2012 11:14:31 -0500
changeset 84105 36e7f9692e36100b5c48d1f84571f06f7136baa4
parent 84010 8b1c24ce4537fdbd311f16f645d27975e54db80f
child 85776 2934da670f6247a91c99c2b764131b68a2431434
permissions -rw-r--r--
Bug 715312 - Robotium tests for Fennec Native do not close file input stream. r=kats

#filter substitution
/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is Firefox Mobile Test Framework.
 *
 * The Initial Developer of the Original Code is Mozilla.
 * Portions created by the Initial Developer are Copyright (C) 2011
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 * Trevor Fairey <tnfairey@gmail.com>
 * David Burns <dburns@mozilla.com>
 * Joel Maher <joel.maher@gmail.com>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

package @ANDROID_PACKAGE_NAME@;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.HashMap;
import java.util.List;

import java.lang.Class;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.Long;

import android.app.Activity;
import android.util.Log;
import android.view.View;

import org.json.*;

import com.jayway.android.robotium.solo.Solo;

public class FennecNativeDriver implements Driver {
  // Map of IDs to element names.
  private HashMap locators = null;
  private Activity activity;
  private Solo solo;

  // Objects for reflexive access of fennec classes.
  private ClassLoader classLoader;
  private Class gel;
  private Class ge;
  private Class gas;
  private Method registerGEL;
  private Method unregisterGEL;
  private Method sendGE;
  private Method _startFrameRecording;
  private Method _stopFrameRecording;

  public FennecNativeDriver(Activity activity, Solo robocop){
    this.activity = activity;
    this.solo = robocop;

    // Set up table of fennec_ids.
    locators = convertTextToTable(getFile("/mnt/sdcard/fennec_ids.txt"));

    // Set up reflexive access of java classes and methods.
    try {
      classLoader = activity.getClassLoader();
      gel = classLoader.loadClass("org.mozilla.gecko.GeckoEventListener");
      ge = classLoader.loadClass("org.mozilla.gecko.GeckoEvent");
      gas = classLoader.loadClass("org.mozilla.gecko.GeckoAppShell");
      Class [] parameters = new Class[2];
      parameters[0] = String.class;
      parameters[1] = gel;
      registerGEL = gas.getMethod("registerGeckoEventListener", parameters);
      unregisterGEL = gas.getMethod("unregisterGeckoEventListener", parameters);
      parameters = new Class[1];
      parameters[0] = ge;
      sendGE = gas.getMethod("sendEventToGecko", parameters);

      Class gfx = classLoader.loadClass("org.mozilla.gecko.gfx.PanningPerfAPI");
      _startFrameRecording = gfx.getDeclaredMethod("startFrameTimeRecording");
      _stopFrameRecording = gfx.getDeclaredMethod("stopFrameTimeRecording");
     } catch (ClassNotFoundException e) {
       e.printStackTrace();
     } catch (SecurityException e) {
       e.printStackTrace();
     } catch (NoSuchMethodException e) {
       e.printStackTrace();
     } catch (IllegalArgumentException e) {
       e.printStackTrace();
     }
  }

  //Information on the location of the Gecko Frame.
  private boolean geckoInfo = false;
  private int geckoTop = 100;
  private int geckoLeft = 0;
  private int geckoHeight= 700;
  private int geckoWidth = 1024;

  private void getGeckoInfo() {
    View geckoLayout = activity.findViewById(Integer.decode((String)locators.get("gecko_layout")));
    if (geckoLayout != null) {
      geckoTop = geckoLayout.getTop();
      geckoLeft = geckoLayout.getLeft();
      geckoWidth = geckoLayout.getWidth();
      geckoHeight = geckoLayout.getHeight();
      geckoInfo = true;
    }
  }

  public int getGeckoTop() {
    if(!geckoInfo) {
      getGeckoInfo();
    }
    return geckoTop;
  }

  public int getGeckoLeft() {
    if(!geckoInfo) {
      getGeckoInfo();
    }
    return geckoLeft;
  }

  public int getGeckoHeight() {
    if(!geckoInfo) {
      getGeckoInfo();
    }
    return geckoHeight;
  }
  public int getGeckoWidth() {
    if(!geckoInfo) {
      getGeckoInfo();
    }
    return geckoWidth;
  }

  public Element findElement(Activity activity, String name) {
    if (name == null)
      throw new IllegalArgumentException("Can not findElements when passed a null");
    if (locators.containsKey(name)){
      return new FennecNativeElement(Integer.decode((String)locators.get(name)), activity, solo);
    }
    throw new RoboCopException("Element does not exist in the list");
  }

  public void startFrameRecording() {
    try {
      Object [] params = null;
      _startFrameRecording.invoke(null, params);
    } catch (IllegalAccessException e) {
      e.printStackTrace();
    } catch (InvocationTargetException e) {
      e.printStackTrace();
    }
  }

  public int stopFrameRecording() {
    Class [] parameters = new Class[1];
    parameters[0] = null;
    List frames;

    try {
      Object [] params = null;
      frames = (List)_stopFrameRecording.invoke(null, params);
      Object [] framearray = frames.toArray();
      Long last = new Long(0);
      Long threshold = new Long(17);
      int numDelays = 0;
      for (int i=0; i < framearray.length; i++) {
        Long val = (Long)framearray[i];
        if ((val - last) > threshold) {
          numDelays++;
        }
        last = val;
      }
      return numDelays;
    } catch (IllegalAccessException e) {
      e.printStackTrace();
    } catch (InvocationTargetException e) {
      e.printStackTrace();
    }

    return 0;
  }

  class scrollHandler implements InvocationHandler {
    public scrollHandler(){};
    public Object invoke(Object proxy, Method method, Object[] args) {
      try{
        //Disect the JSON object into the appropriate variables 
        JSONObject jo = ((JSONObject)args[1]);
        scrollHeight = jo.getInt("y");
        height = jo.getInt("cheight");
        //We don't want a height of 0. That means it's a bad response.
        if( height > 0) {
          pageHeight = jo.getInt("height");
        }

      } catch( Throwable e) {
        Log.i("Robocop", "WARNING: ScrollReceived, but read wrong!");
      }
      return null;
    }
  }
  public int getScrollHeight() {
    return scrollHeight;
  }
  public int getPageHeight() {
    return pageHeight;
  }
  public int getHeight() {
    return height;
  }

  public int height=0;
  public int scrollHeight=0;
  public int pageHeight=10;
  public void setupScrollHandling() {
    //Setup scrollHandler to catch "robocop:scroll" events. 
    try {
      Class [] interfaces = new Class[1];
      interfaces[0] = gel;
      Object[] finalParams = new Object[2];
      finalParams[0] = "robocop:scroll";
      finalParams[1] = Proxy.newProxyInstance(classLoader, interfaces, new scrollHandler());
      registerGEL.invoke(null, finalParams);
    } catch (IllegalAccessException e) {
      e.printStackTrace();
    } catch (InvocationTargetException e) {
      e.printStackTrace();
    }

  }

  //Takes a filename, loads the file, 
  //  and returns a string version of the entire file.
  public static String getFile(String filename)
  {
    StringBuilder text = new StringBuilder();

    BufferedReader br = null;
    try {
      br = new BufferedReader(new FileReader(filename));
      String line;

      while ((line = br.readLine()) != null) {
        text.append(line);
        text.append('\n');
      }
    } catch(IOException e) {
      e.printStackTrace();
    } finally {
      try {
        br.close();
      } catch (IOException e) {
      }
    }
    return text.toString();  
  }

  // Takes a string of "key=value" pairs split by \n and creates a hash table.
  public static HashMap convertTextToTable(String data)
  {
    HashMap retVal = new HashMap();

    String[] lines = data.split("\n");
    for (int i = 0; i < lines.length; i++) {
      String[] parts = lines[i].split("=");
      retVal.put(parts[0].trim(), parts[1].trim());
    }
    return retVal;
  }
}