mobile/android/base/tests/BaseTest.java.in
author Geoff Brown <gbrown@mozilla.com>
Sat, 09 Jun 2012 12:24:57 -0400
changeset 96253 c3b1df7ffdab612eda76eec9d9ef80740c65b174
parent 96184 05da351c9b570a14899b3a902a721155c88d1dc0
child 96337 c39d36167b99139c4e39c2180f21cebae7ea6dbd
permissions -rw-r--r--
Bug 758792 - Robocop: remove waitForIdleSync calls in getActivityFromClick. r=jmaher

#filter substitution
package @ANDROID_PACKAGE_NAME@.tests;

import com.jayway.android.robotium.solo.Solo;
import @ANDROID_PACKAGE_NAME@.*;

import android.app.Activity;
import android.app.Instrumentation;
import android.content.ContentValues;
import android.content.Intent;
import android.content.res.AssetManager;
import android.database.Cursor;
import android.os.SystemClock;
import android.test.ActivityInstrumentationTestCase2;
import java.io.File;
import java.io.InputStream;
import java.io.IOException;

import java.util.HashMap;

/**
 *  A convenient base class suitable for most Robocop tests.
 */
abstract class BaseTest extends ActivityInstrumentationTestCase2<Activity> {
    private static final String TARGET_PACKAGE_ID = "org.mozilla.gecko";
    private static final String LAUNCH_ACTIVITY_FULL_CLASSNAME="@ANDROID_PACKAGE_NAME@.App";

    private static Class<Activity> mLauncherActivityClass;
    private Activity mActivity;
    protected Solo mSolo;
    protected Driver mDriver;
    protected Assert mAsserter;
    protected Actions mActions;
    protected String mBaseUrl;
    protected String mRawBaseUrl;
    private String mTestType;
    private String mLogFile;
    protected String mProfile;

    static {
        try {
            mLauncherActivityClass = (Class<Activity>)Class.forName(LAUNCH_ACTIVITY_FULL_CLASSNAME);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    public BaseTest() {
        super(TARGET_PACKAGE_ID, mLauncherActivityClass);
    }

    @Override
    protected void setUp() throws Exception {
        // Load config file from sdcard (setup by python script)
        String configFile = FennecNativeDriver.getFile("/mnt/sdcard/robotium.config");
        HashMap config = FennecNativeDriver.convertTextToTable(configFile);

        // Create the intent to be used with all the important arguments.
        Intent i = new Intent(Intent.ACTION_MAIN);
        mProfile = (String)config.get("profile");
        i.putExtra("args", "-no-remote -profile " + mProfile);

        // Start the activity
        setActivityIntent(i);
        mActivity = getActivity();

        // Set up Robotium.solo and Driver objects
        mSolo = new Solo(getInstrumentation());
        mDriver = new FennecNativeDriver(mActivity, mSolo);
        mActions = new FennecNativeActions(mActivity, mSolo, getInstrumentation());

        mLogFile = (String)config.get("logfile");
        mBaseUrl = ((String)config.get("host")).replaceAll("(/$)", "");
        mRawBaseUrl = ((String)config.get("rawhost")).replaceAll("(/$)", "");
    }

    public void setTestType(String type) {
        mTestType = type;

        if (mTestType.equals("talos")) {
            mAsserter = new FennecTalosAssert();
        } else {
            mAsserter = new FennecMochitestAssert();
        }
        mAsserter.setLogFile(mLogFile);
        mAsserter.setTestName(this.getClass().getName());
    }

    @Override
    public void tearDown() throws Exception {
        try {
            mAsserter.finalize();
            mSolo.finalize();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        getActivity().finish();
        super.tearDown();
    }

    public void assertMatches(String value, String regex, String name) {
        if (value == null) {
            mAsserter.ok(false, name, "Expected /" + regex + "/, got null");
            return;
        }
        mAsserter.ok(value.matches(regex), name, "Expected /" + regex +"/, got \"" + value + "\"");
    }

    /**
     * Click on the specified element and return the resulting activity.
     * @return The created activity, or null if the element cannot be clicked.
     */
    protected final Activity getActivityFromClick(Element element) {
        Instrumentation inst = getInstrumentation();
        Instrumentation.ActivityMonitor monitor = inst.addMonitor((String)null, null, false);
        boolean clicked = element.click();
        if (!clicked) {
            mAsserter.ok(clicked != false, "checking that awesome bar clicked", "awesome bar was clicked");
            return null;
        }
        // Wait for click to take effect before waiting for activity
        // (otherwise we sometimes get the previous activity).
        // Previously, waitForIdleSync was used here but it was found
        // to hang very occasionally.
        mSolo.sleep(2000);
        Activity activity = inst.waitForMonitor(monitor);
        // Give the activity time to render itself and initialize views 
        // before continuing, so that views are created before access
        // attempts are made. Again, waitForIdleSync was used here
        // previously, but replaced with a sleep to avoid hangs.
        // TODO: Investigate and document why these pauses are required.
        mSolo.sleep(2000);
        return activity;
    }

    /**
     * Click on the awesome bar element and return the resulting activity.
     * @return The created activity, or null if the awesome bar cannot be clicked.
     */
    protected final Activity clickOnAwesomeBar() {
        Activity activity = null;
        Element awesomebar = mDriver.findElement(mActivity, "awesome_bar");
        if (awesomebar != null) {
            activity = getActivityFromClick(awesomebar);
            if (activity == null) {
                mAsserter.dumpLog("failed to click on awesome bar!");
            }
        }
        return activity;
    }

    protected final void enterUrl(String url) {
        Activity awesomeBarActivity = clickOnAwesomeBar();
        Element urlbar = mDriver.findElement(awesomeBarActivity, "awesomebar_text");
        mActions.sendKeys(url);
        String urlbarText = null;
        if (urlbar != null) {
            urlbarText = urlbar.getText();
        }
        mAsserter.is(urlbarText, url, "Awesomebar URL typed properly");
    }

    protected final void hitEnterAndWait() {
        Actions.EventExpecter contentEventExpecter = mActions.expectGeckoEvent("DOMContentLoaded");
        mActions.sendSpecialKey(Actions.SpecialKey.ENTER);
        // wait for screen to load
        contentEventExpecter.blockForEvent();
    }

    protected final void loadUrl(String url) {
        enterUrl(url);
        hitEnterAndWait();
    }

    protected final void verifyUrl(String url) {
        Activity awesomeBarActivity = clickOnAwesomeBar();
        Element urlbar = mDriver.findElement(awesomeBarActivity, "awesomebar_text");
        String urlbarText = null;
        if (urlbar != null) {
            urlbarText = urlbar.getText();
        }
        mAsserter.is(urlbarText, url, "Awesomebar URL stayed the same");
    }

    protected final String getAbsoluteUrl(String url) {
        return mBaseUrl + "/" + url.replaceAll("(^/)", "");
    }

    protected final String getAbsoluteRawUrl(String url) {
        return mRawBaseUrl + "/" + url.replaceAll("(^/)", "");
    }

    protected final boolean waitForTest(BooleanTest t, int timeout) {
        long end = SystemClock.uptimeMillis() + timeout;
        while (SystemClock.uptimeMillis() < end) {
            if (t.test())
                return true;

            mSolo.sleep(100);
        }
        return false;
    }

    protected interface BooleanTest {
        public boolean test();
    }

    @SuppressWarnings({"unchecked", "non-varargs"})
    public void SqliteCompare(String dbName, String sqlCommand, ContentValues[] cvs) {
        File profile = new File(mProfile);
        String dbPath = new File(profile, dbName).getPath();

        Cursor c = mActions.querySql(dbPath, sqlCommand);
        SqliteCompare(c, cvs);
    }

    private boolean CursorMatches(Cursor c, String[] columns, ContentValues cv) {
        for (int i = 0; i < columns.length; i++) {
            String column = columns[i];
            if (cv.containsKey(column)) {
                mAsserter.info("Comparing", "Column values for: " + column);
                Object value = cv.get(column);
                if (value == null) {
                    if (!c.isNull(i))
                        return false;
                } else {
                    if (c.isNull(i) || !value.toString().equals(c.getString(i)))
                        return false;
                }
            }
        }
        return true;
    }

    @SuppressWarnings({"unchecked", "non-varargs"})
    public void SqliteCompare(Cursor c, ContentValues[] cvs) {
        mAsserter.is(c.getCount(), cvs.length, "List is correct length");
        if (c.moveToFirst()) {
            do {
                boolean found = false;
                for (int i = 0; !found && i < cvs.length; i++) {
                    if (CursorMatches(c, cvs[i])) {
                        found = true;
                    }
                }
                mAsserter.is(found, true, "Password was found");
            } while(c.moveToNext());
        }
    }

    public boolean CursorMatches(Cursor c, ContentValues cv) {
        for (int i = 0; i < c.getColumnCount(); i++) {
            String column = c.getColumnName(i);
             if (cv.containsKey(column)) {
                mAsserter.info("Comparing", "Column values for: " + column);
                Object value = cv.get(column);
                if (value == null) {
                    if (!c.isNull(i))
                        return false;
                } else {
                    if (c.isNull(i) || !value.toString().equals(c.getString(i)))
                        return false;
                }
            }
        }
        return true;
    }

    public InputStream getAsset(String filename) throws IOException {
        AssetManager assets = getInstrumentation().getContext().getAssets();
        return assets.open(filename);
    }
}