#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 { public static final int TEST_MOCHITEST = 0; public static final int TEST_TALOS = 1; private static final String TARGET_PACKAGE_ID = "org.mozilla.gecko"; private static final String LAUNCH_ACTIVITY_FULL_CLASSNAME="@ANDROID_PACKAGE_NAME@.App"; private static final int VERIFY_URL_TIMEOUT = 2000; private static Class mLauncherActivityClass; private Activity mActivity; protected Solo mSolo; protected Driver mDriver; protected Assert mAsserter; protected Actions mActions; protected String mBaseUrl; protected String mRawBaseUrl; private String mLogFile; protected String mProfile; static { try { mLauncherActivityClass = (Class)Class.forName(LAUNCH_ACTIVITY_FULL_CLASSNAME); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } } public BaseTest() { super(TARGET_PACKAGE_ID, mLauncherActivityClass); } protected abstract int getTestType(); @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(); mLogFile = (String)config.get("logfile"); mBaseUrl = ((String)config.get("host")).replaceAll("(/$)", ""); mRawBaseUrl = ((String)config.get("rawhost")).replaceAll("(/$)", ""); // Initialize the asserter if (getTestType() == TEST_TALOS) { mAsserter = new FennecTalosAssert(); } else { mAsserter = new FennecMochitestAssert(); } mAsserter.setLogFile(mLogFile); mAsserter.setTestName(this.getClass().getName()); // Set up Robotium.solo and Driver objects mSolo = new Solo(getInstrumentation()); mDriver = new FennecNativeDriver(mActivity, mSolo); mActions = new FennecNativeActions(mActivity, mSolo, getInstrumentation(), mAsserter); } @Override protected void runTest() throws Throwable { try { super.runTest(); } catch (Throwable t) { if (mAsserter != null) { mAsserter.dumpLog("Exception caught during test!", t); mAsserter.ok(false, "Exception caught", t.toString()); } // re-throw to continue bail-out throw t; } } @Override public void tearDown() throws Exception { try { mAsserter.endTest(); 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(); } public final void verifyUrl(String url) { Activity awesomeBarActivity = clickOnAwesomeBar(); Element urlbar = mDriver.findElement(awesomeBarActivity, "awesomebar_text"); String urlbarText = null; if (urlbar != null) { // wait for a short time for the expected text, in case there is a delay // in updating the view waitForTest(new VerifyUrlTest(urlbar, url), VERIFY_URL_TIMEOUT); urlbarText = urlbar.getText(); } mAsserter.is(urlbarText, url, "Awesomebar URL stayed the same"); } class VerifyUrlTest implements BooleanTest { private Element mUrlbar; private String mUrl; public VerifyUrlTest(Element urlbar, String url) { mUrlbar = urlbar; mUrl = url; } public boolean test() { String urlbarText = mUrlbar.getText(); if (urlbarText.equals(mUrl)) { return true; } return false; } } 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); } }