Robocop is a Java-based testing framework which is an extension of the Android Robotium test tool with features needed to perform automation on Firefox for Android builds.

Limitations of Robocop and where it can be used

Getting started with Robocop tests

Start writing a new test

hg pull -u
make -f client.mk
hg qnew <patch_name>
hg add mobile/android/base/tests/<test_file_complete_name>
hg qpop

Basic structure of a test

#filter substitution
package @ANDROID_PACKAGE_NAME@.tests;

import @ANDROID_PACKAGE_NAME@.*;

/*
Insert info about the test here - a basic description of what the test does
*/
public class test<Feature_to_be_tested> extends BaseTest {
    @Override
    // This method is needed in the BaseTest class
    protected int getTestType() {
        return TEST_MOCHITEST; // Test type should remain Mochitest
    }
    /*
    * This is the class constructor
    * This method will contain the test that will be run
    */
    public void test<Feature_to_be_tested>() {

    }
}

APIs

Robotium itself provides a rich API through the Solo class - javadocs for Solo are available at [1].

Robocop provides an additional API to make common tasks easier. The main interfaces are Actions, Element, and Driver.

Actions provides commonly used non-element-specific actions that can be taken on the application, such as dragging and sending key events.

// Actions
  //This will cause this process to spin until Gecko fires a specific JSON event, such as DOMContentLoaded
  void waitForGeckoEvent(String geckoEvent);
  //Clicks the given Key (Actions.SpecialKey.[DOWN|UP|LEFT|RIGHT|ENTER])
  void sendSpecialKey(SpecialKey button)
  //Sends a string of characters to the system. (Most have been implemented but not all)
  void sendKeys(String keysToSend);
  //Sends a drag action across the screen
  void drag(int startingX, int endingX, int startingY, int endingY)
  // Run a SQL query on the specified database
  public Cursor querySql(String dbPath, String sql);

Element represents each of the available UI objects in Fennec including the Awesomebar, the tabs button, and different lists and menus.

// Element
  //To click the element
  void click()
  //Returns true if the element is currently displayed
  boolean isDisplayed();
  //Returns the text currently displayed on the element, or direct sub-elements
  String getText();

Driver finds elements and provides info about the UI.

// Driver
  //This is used to find elements given their id's name
  Element findElement(String name);
  //This is used for getting information on scrolls. NOTE: It must be used for the next three methods to return useful information
  void setupScrollHandling();
  int getPageHeight(); //The total height of the page
  int getScrollHeight(); //How far down the screen the client has scrolled
  int getHeight(); //The height of the client's view

  //The following are used to give information on the graphical location of the Gecko view on the screen
  int getGeckoTop();
  int getGeckoLeft();
  int getGeckoHeight();
  int getGeckoWidth();

Finally, an evolving set of test base classes - BaseTest, PixelTest, etc. - can be leveraged for some types of tests.

Usable IDs

The following is a list of IDs that can be used with Driver.findElement(). Most of the following IDs have not been tested, so you might have unexpected results, or require increased APIs for them. To know how a given object is used, in mobile/android/base, grep R.id.[id-name] * (from Objdir/mobile/android/base/R.java#id)

abouthome_content
add_tab
addons
address_bar
agent_mode
all_pages_list
awesome_bar
awesome_screen
awesomebar_button
awesomebar_tabs
awesomebar_text
background
bookmark
bookmark_icon
bookmark_title
bookmark_url
bookmarks_list
browser_toolbar
close
container
doorhanger_choices
doorhanger_container
doorhanger_title
favicon
forward
gecko_layout
grid
history_list
info
list
main_layout
notification_image
notification_progressbar
notification_text
notification_title
outline
plugin_container
preferences
quit
recommended_addon_list
reload
save_as_pdf
screenshot
select_list
share
site_security
stop
tabs
tabs_count
title
url

Basic code sequences to do different actions

Load Page

String url = "<insert_link_here>";
loadUrl(url);
String url = getAbsoluteUrl("/robocop/robocop_blank_01.html");
loadUrl(url);

Check Page Title

Element awesomebar = mDriver.findElement(getActivity(), "awesome_bar_title"); // search the awesomebar title
mAsserter.isnot(awesomebar, null, "Got the awesomebar"); // log "Got the awesomebar" in the logs
assertMatches(awesomebar.getText(), "<insert_title_here", "page title match"); // do a match between expected and actual title

Open app menu

mActions.sendSpecialKey(Actions.SpecialKey.MENU);
//Open the More menu for devices with old style menus
if (mSolo.waitForText("^More$")) {
mSolo.clickOnText("^More$");
}

Simulate a Back key press:

mActions.sendSpecialKey(Actions.SpecialKey.BACK);

Set up a listener to catch a new page load in a new tab

Actions.EventExpecter tabEventExpecter = mActions.expectGeckoEvent("Tab:Added"); // Look for a new tab event
Actions.EventExpecter contentEventExpecter = mActions.expectGeckoEvent("DOMContentLoaded"); // Look for page load complete

Wait for the tab and page load

tabEventExpecter.blockForEvent();
contentEventExpecter.blockForEvent();

Wait for Gecko to complete loading

blockForGeckoReady();

Open the Bookmarks tab

private ListView openBookmarksList() {
    Activity awesomeBarActivity = clickOnAwesomeBar();
    // Click the "Bookmarks" tab to switch to bookmarks list
    mSolo.clickOnText("Bookmarks");

    TabHost tabHost = (TabHost)mSolo.getView(TabHost.class, 0);
    return (ListView)tabHost.getCurrentView();
   }

Open the bookmark context menu

View child = list.getChildAt(<nr>); //bookmark nr- numbering starts at 0 
mSolo.clickLongOnView(child); //long tap to open context menu

Open a new tab and check the tab count

expectedTabCount = <nr_of_expected_tabs>;
tabCountText = tabCount.getText();
tabCountInt = Integer.parseInt(tabCountText);
mAsserter.is(tabCountInt, expectedTabCount, "Number of tabs increased");

Loging info in the Android Logs

Test that an object is not the same as a second object

public void isnot(Object a, Object b, String name)
Element awesomebar = mDriver.findElement(getActivity(), "awesome_bar_title");
mAsserter.isnot(awesomebar, null, "Got the awesomebar"); // Checks that the awesomebar title is not null and posts in the logs

Test that an object is the same as a second object

public void is(Object a, Object b, String name)
ListView list = getAllPagesList(url);
mSolo.waitForText(url);

mAsserter.is(list.getChildCount(), 5, "all pages list has 5 children (the default bookmarks)"); // Checks that
there are 5 entries in the list and posts in the logs

Write text in the Logs

public void dumpLog(String message) // writes text in the logs

Log info about test progression

public void ok(boolean condition, String name, String diag)
ListView list = getAllPagesList(url);
mSolo.waitForText(url);

mAsserter.ok(list != null, "checking that all pages list exists", list.toString()); // tests if the list is null and
                                                                                    // if it isn't it prints the list
                                                                                    // after printing the message of
                                                                                    // what the test does

Updating your test directory

It is not always necessary to rebuild Fennec to use new tests. If you are updating the build with your own test, ensure that your test has been added to the manifest in /PATH/TO/FENNEC/SOURCE/mobile/android/base/tests/robocop.ini.

Otherwise, you can retrieve tests from the repository by executing:

hg pull
hg update

Now you can build the Robocop part of Fennec by executing:

cd objdir
sudo make -C build/mobile/robocop/
sudo make package 

Testing and submitting the test as a patch