Bug 725881 - Tests for form history and passwords providers. r=gbrown

This commit is contained in:
Wes Johnston 2012-03-08 10:25:44 -08:00
parent a61be3e2cb
commit cb47dc2842
8 changed files with 336 additions and 30 deletions

View File

@ -39,6 +39,9 @@
package @ANDROID_PACKAGE_NAME@;
import java.util.List;
import java.util.ArrayList;
import android.database.Cursor;
public interface Actions {
@ -92,4 +95,9 @@ public interface Actions {
void sendSpecialKey(SpecialKey key);
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);
}

View File

@ -166,21 +166,13 @@ public class FennecMochitestAssert implements Assert {
}
public void is(Object a, Object b, String name) {
boolean pass = a.equals(b);
String diag = "got " + a.toString() + ", expected " + b.toString();
if (pass) {
diag = a.toString() + " should equal " + b.toString();
}
ok(pass, name, diag);
boolean pass = checkObjectsEqual(a,b);
ok(pass, name, getEqualString(a,b, pass));
}
public void isnot(Object a, Object b, String name) {
boolean pass = !a.equals(b);
String diag = "didn't expect " + a.toString() + ", but got it";
if (pass) {
diag = a.toString() + " should not equal " + b.toString();
}
ok(pass, name, diag);
boolean pass = checkObjectsNotEqual(a,b);
ok(pass, name, getNotEqualString(a,b,pass));
}
public void ispixel(int actual, int r, int g, int b, String name) {
@ -207,21 +199,50 @@ public class FennecMochitestAssert implements Assert {
}
public void todo_is(Object a, Object b, String name) {
boolean pass = a.equals(b);
String diag = "got " + a.toString() + ", expected " + b.toString();
if (pass) {
diag = a.toString() + " should equal " + b.toString();
}
todo(pass, name, diag);
boolean pass = checkObjectsEqual(a,b);
todo(pass, name, getEqualString(a,b,pass));
}
public void todo_isnot(Object a, Object b, String name) {
boolean pass = !a.equals(b);
String diag = "didn't expect " + a.toString() + ", but got it";
if (pass) {
diag = a.toString() + " should not equal " + b.toString();
boolean pass = checkObjectsNotEqual(a,b);
todo(pass, name, getNotEqualString(a,b,pass));
}
private boolean checkObjectsEqual(Object a, Object b) {
if (a == null || b == null) {
if (a == null && b == null) {
return true;
}
return false;
} else {
return a.equals(b);
}
todo(pass, name, diag);
}
private String getEqualString(Object a, Object b, boolean pass) {
if (pass) {
return a + " should equal " + b;
}
return "got " + a + ", expected " + b;
}
private boolean checkObjectsNotEqual(Object a, Object b) {
if (a == null || b == null) {
if ((a == null && b != null) || (a != null && b == null)) {
return true;
} else {
return false;
}
} else {
return !a.equals(b);
}
}
private String getNotEqualString(Object a, Object b, boolean pass) {
if(pass) {
return a + " should not equal " + b;
}
return "didn't expect " + a + ", but got it";
}
public void info(String name, String message) {

View File

@ -40,19 +40,24 @@
package @ANDROID_PACKAGE_NAME@;
import java.lang.Class;
import java.lang.ClassLoader;
import java.lang.reflect.Constructor;
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 java.util.concurrent.SynchronousQueue;
import java.util.ArrayList;
import android.app.Activity;
import android.content.Context;
import android.app.Instrumentation;
import android.database.Cursor;
import android.os.SystemClock;
import android.view.View;
import android.view.KeyEvent;
import java.util.concurrent.SynchronousQueue;
import android.util.Log;
import org.json.*;
@ -74,6 +79,7 @@ public class FennecNativeActions implements Actions {
private Method mSendGE;
private Method mGetLayerClient;
private Method mSetDrawListener;
private static final String LOGTAG = "FennecNativeActions";
public FennecNativeActions(Activity activity, Solo robocop, Instrumentation instrumentation) {
mSolo = robocop;
@ -349,4 +355,36 @@ public class FennecNativeActions implements Actions {
public void drag(int startingX, int endingX, int startingY, int endingY) {
mSolo.drag(startingX, endingX, startingY, endingY, 10);
}
public Cursor querySql(String dbPath, String sql) {
try {
ClassLoader classLoader = mGeckoApp.getClassLoader();
Class sqlClass = classLoader.loadClass("org.mozilla.gecko.sqlite.SQLiteBridge");
Class stringClass = String.class;
Class stringArrayClass = String[].class;
Class appshell = classLoader.loadClass("org.mozilla.gecko.GeckoAppShell");
Class contextClass = Context.class;
Constructor bridgeConstructor = sqlClass.getConstructor(stringClass);
Method query = sqlClass.getMethod("rawQuery", stringClass, stringArrayClass);
Method loadSQLiteLibs = appshell.getMethod("loadSQLiteLibs", contextClass, stringClass);
Object bridge = bridgeConstructor.newInstance(dbPath);
String resourcePath = mGeckoApp.getApplication().getPackageResourcePath();
loadSQLiteLibs.invoke(null, mGeckoApp, resourcePath);
return (Cursor)query.invoke(bridge, sql, null);
} catch(ClassNotFoundException ex) {
Log.e(LOGTAG, "Error getting class", ex);
} catch(NoSuchMethodException ex) {
Log.e(LOGTAG, "Error getting method", ex);
} catch(InvocationTargetException ex) {
Log.e(LOGTAG, "Error invoking method", ex);
} catch(InstantiationException ex) {
Log.e(LOGTAG, "Error calling constructor", ex);
} catch(IllegalAccessException ex) {
Log.e(LOGTAG, "Error using field", ex);
}
return null;
}
}

View File

@ -168,9 +168,6 @@ public class PasswordsProvider extends GeckoProvider {
values.put(Passwords.GUID, guid);
}
String nowString = new Long(now).toString();
DBUtils.replaceKey(values, CommonColumns._ID, Passwords.ID, "");
DBUtils.replaceKey(values, SyncColumns.DATE_CREATED, Passwords.TIME_CREATED, nowString);
DBUtils.replaceKey(values, SyncColumns.DATE_MODIFIED, Passwords.TIME_PASSWORD_CHANGED, nowString);
DBUtils.replaceKey(values, null, Passwords.HOSTNAME, "");
DBUtils.replaceKey(values, null, Passwords.HTTP_REALM, "");
DBUtils.replaceKey(values, null, Passwords.FORM_SUBMIT_URL, "");

View File

@ -6,9 +6,12 @@ import @ANDROID_PACKAGE_NAME@.*;
import android.app.Activity;
import android.app.Instrumentation;
import android.database.Cursor;
import android.content.ContentValues;
import android.content.Intent;
import android.os.SystemClock;
import android.test.ActivityInstrumentationTestCase2;
import java.io.File;
import java.util.HashMap;
@ -25,6 +28,7 @@ abstract class BaseTest extends ActivityInstrumentationTestCase2<Activity> {
protected String mBaseUrl;
private String mTestType;
private String mLogFile;
protected String mProfile;
static {
try {
@ -46,7 +50,8 @@ abstract class BaseTest extends ActivityInstrumentationTestCase2<Activity> {
// Create the intent to be used with all the important arguments.
Intent i = new Intent(Intent.ACTION_MAIN);
i.putExtra("args", "-no-remote -profile " + (String)config.get("profile"));
mProfile = (String)config.get("profile");
i.putExtra("args", "-no-remote -profile " + mProfile);
// Start the activity
setActivityIntent(i);
@ -148,4 +153,55 @@ abstract class BaseTest extends ActivityInstrumentationTestCase2<Activity> {
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 value " + c.getString(i) + " ?= " + cv.get(column).toString());
if (!cv.get(column).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("Pass","Column value " + c.getString(i) + " ?= " + cv.get(column).toString());
if (!cv.get(column).toString().equals(c.getString(i))) {
return false;
}
}
}
return true;
}
}

View File

@ -10,6 +10,8 @@
[testAxisLocking]
[testAboutPage]
[testWebContentContextMenu]
[testPasswordProvider]
[testFormHistory]
# Used for Talos, please don't use in mochitest
#[testPan]

View File

@ -0,0 +1,91 @@
#filter substitution
package @ANDROID_PACKAGE_NAME@.tests;
import @ANDROID_PACKAGE_NAME@.*;
import android.app.Activity;
import android.content.ContentValues;
import android.content.ContentResolver;
import android.database.Cursor;
import android.content.Context;
import android.net.Uri;
import java.io.File;
import java.lang.ClassLoader;
import java.util.ArrayList;
/**
* A basic form history contentprovider test.
* - inserts an element in form history when it is not yet set up
* - inserts an element in form history
* - updates an element in form history
* - deletes an element in form history
*/
public class testFormHistory extends BaseTest {
private static final String DB_NAME = "formhistory.sqlite";
public void testFormHistory() {
setTestType("mochitest");
Context context = (Context)getActivity();
ContentResolver cr = context.getContentResolver();
ContentValues[] cvs = new ContentValues[1];
cvs[0] = new ContentValues();
Actions.EventExpecter contentEventExpecter = mActions.expectGeckoEvent("Gecko:Ready");
contentEventExpecter.blockForEvent();
Uri formHistoryUri;
try {
ClassLoader classLoader = getActivity().getClassLoader();
Class fh = classLoader.loadClass("org.mozilla.gecko.db.BrowserContract$FormHistory");
cvs[0].put("fieldname", "fieldname");
cvs[0].put("value", "value");
cvs[0].put("timesUsed", "0");
cvs[0].put("guid", "guid");
// Attempt to insert into the db
formHistoryUri = (Uri)fh.getField("CONTENT_URI").get(null);
Uri.Builder builder = formHistoryUri.buildUpon();
formHistoryUri = builder.appendQueryParameter("profilePath", mProfile).build();
} catch(ClassNotFoundException ex) {
mAsserter.is(false, true, "Error getting class");
return;
} catch(NoSuchFieldException ex) {
mAsserter.is(false, true, "Error getting field");
return;
} catch(IllegalAccessException ex) {
mAsserter.is(false, true, "Error using field");
return;
}
// Trying to insert should fail the first time round because there is no form history database
// Wait for gecko to reply and then we'll try again
contentEventExpecter = mActions.expectGeckoEvent("FormHistory:Init:Return");
Uri uri = cr.insert(formHistoryUri, cvs[0]);
mAsserter.is(uri, null, "Insert returned null correctly");
contentEventExpecter.blockForEvent();
uri = cr.insert(formHistoryUri, cvs[0]);
Uri expectedUri = formHistoryUri.buildUpon().appendPath("1").build();
mAsserter.is(expectedUri.toString(), uri.toString(), "Insert returned correct uri");
SqliteCompare(DB_NAME, "SELECT * FROM moz_formhistory", cvs);
cvs[0].put("fieldname", "fieldname2");
int numUpdated = cr.update(formHistoryUri, cvs[0], null, null);
mAsserter.is(1, numUpdated, "Correct number updated");
SqliteCompare(DB_NAME, "SELECT * FROM moz_formhistory", cvs);
int numDeleted = cr.delete(formHistoryUri, null, null);
mAsserter.is(1, numDeleted, "Correct number deleted");
cvs = new ContentValues[0];
SqliteCompare(DB_NAME, "SELECT * FROM moz_formhistory", cvs);
}
public void tearDown() throws Exception {
super.tearDown();
// remove the entire signons.sqlite file
File profile = new File(mProfile);
File db = new File(profile, "formhistory.sqlite");
db.delete();
}
}

View File

@ -0,0 +1,93 @@
#filter substitution
package @ANDROID_PACKAGE_NAME@.tests;
import @ANDROID_PACKAGE_NAME@.*;
import android.app.Activity;
import android.content.ContentValues;
import android.content.ContentResolver;
import android.database.Cursor;
import android.content.Context;
import android.net.Uri;
import java.io.File;
/**
* A basic password contentprovider test.
* - inserts a password when the database is not yet set up
* - inserts a password
* - updates a password
* - deletes a password
*/
public class testPasswordProvider extends BaseTest {
private static final String DB_NAME = "signons.sqlite";
public void testPasswordProvider() {
setTestType("mochitest");
Context context = (Context)getActivity();
ContentResolver cr = context.getContentResolver();
ContentValues[] cvs = new ContentValues[1];
cvs[0] = new ContentValues();
Actions.EventExpecter contentEventExpecter = mActions.expectGeckoEvent("Gecko:Ready");
contentEventExpecter.blockForEvent();
Uri passwordUri;
try {
ClassLoader classLoader = getActivity().getClassLoader();
Class pwds = classLoader.loadClass("org.mozilla.gecko.db.BrowserContract$Passwords");
cvs[0].put("hostname", "http://www.example.com");
cvs[0].put("httpRealm", "http://www.example.com");
cvs[0].put("formSubmitURL", "http://www.example.com");
cvs[0].put("usernameField", "usernameField");
cvs[0].put("passwordField", "passwordField");
cvs[0].put("encryptedUsername", "username");
cvs[0].put("encryptedPassword", "password");
cvs[0].put("encType", "0");
// Attempt to insert into the db
passwordUri = (Uri)pwds.getField("CONTENT_URI").get(null);
Uri.Builder builder = passwordUri.buildUpon();
passwordUri = builder.appendQueryParameter("profilePath", mProfile).build();
} catch(ClassNotFoundException ex) {
mAsserter.is(false, true, "Error getting class");
return;
} catch(NoSuchFieldException ex) {
mAsserter.is(false, true, "Error getting field");
return;
} catch(IllegalAccessException ex) {
mAsserter.is(false, true, "Error using field");
return;
}
// Trying to inset should fail the first time round because there is no pw database
// Wait for gecko to reply and then we'll try again
contentEventExpecter = mActions.expectGeckoEvent("Passwords:Init:Return");
Uri uri = cr.insert(passwordUri, cvs[0]);
mAsserter.is(uri, null, "Insert returned null correctly");
contentEventExpecter.blockForEvent();
uri = cr.insert(passwordUri, cvs[0]);
SqliteCompare(DB_NAME, "SELECT * FROM moz_logins", cvs);
Uri expectedUri = passwordUri.buildUpon().appendPath("1").build();
mAsserter.is(expectedUri.toString(), uri.toString(), "Insert returned correct uri");
cvs[0].put("usernameField", "usernameField2");
cvs[0].put("passwordField", "passwordField2");
int numUpdated = cr.update(passwordUri, cvs[0], null, null);
mAsserter.is(1, numUpdated, "Correct number updated");
SqliteCompare(DB_NAME, "SELECT * FROM moz_logins", cvs);
int numDeleted = cr.delete(passwordUri, null, null);
mAsserter.is(1, numDeleted, "Correct number deleted");
cvs = new ContentValues[0];
SqliteCompare(DB_NAME, "SELECT * FROM moz_logins", cvs);
}
public void tearDown() throws Exception {
super.tearDown();
// remove the entire signons.sqlite file
File profile = new File(mProfile);
File db = new File(profile, "signons.sqlite");
db.delete();
}
}