Add more tests
Test: ./run-all-tests.sh
Change-Id: Ib7db8e429a6da41363cfdb430e8ab3b0cda408dd
diff --git a/src/com/android/providers/contacts/ContactsDatabaseHelper.java b/src/com/android/providers/contacts/ContactsDatabaseHelper.java
index 8d0d06a..f0c53de 100644
--- a/src/com/android/providers/contacts/ContactsDatabaseHelper.java
+++ b/src/com/android/providers/contacts/ContactsDatabaseHelper.java
@@ -4952,16 +4952,26 @@
* Ensure (a piece of) SQL is valid and doesn't contain disallowed tokens.
*/
public void validateSql(String callerPackage, String sqlPiece) {
- runSqlValidation(callerPackage, () -> getSqlChecker().ensureNoInvalidTokens(sqlPiece));
+ // TODO Replace the Runnable with a lambda -- which would crash right now due to an art bug?
+ runSqlValidation(callerPackage, new Runnable() {
+ @Override
+ public void run() {
+ ContactsDatabaseHelper.this.getSqlChecker().ensureNoInvalidTokens(sqlPiece);
+ }
+ });
}
/**
* Ensure all keys in {@code values} are valid. (i.e. they're all single token.)
*/
public void validateContentValues(String callerPackage, ContentValues values) {
- runSqlValidation(callerPackage, () -> {
- for (String key : values.keySet()) {
- getSqlChecker().ensureSingleTokenOnly(key);
+ // TODO Replace the Runnable with a lambda -- which would crash right now due to an art bug?
+ runSqlValidation(callerPackage, new Runnable() {
+ @Override
+ public void run() {
+ for (String key : values.keySet()) {
+ ContactsDatabaseHelper.this.getSqlChecker().ensureSingleTokenOnly(key);
+ }
}
});
}
@@ -4970,17 +4980,20 @@
* Ensure all column names in {@code projection} are valid. (i.e. they're all single token.)
*/
public void validateProjection(String callerPackage, String[] projection) {
+ // TODO Replace the Runnable with a lambda -- which would crash right now due to an art bug?
if (projection != null) {
- runSqlValidation(callerPackage, () -> {
- for (String column : projection) {
- getSqlChecker().ensureSingleTokenOnly(column);
+ runSqlValidation(callerPackage, new Runnable() {
+ @Override
+ public void run() {
+ for (String column : projection) {
+ ContactsDatabaseHelper.this.getSqlChecker().ensureSingleTokenOnly(column);
+ }
}
});
}
}
private void runSqlValidation(String callerPackage, Runnable r) {
- // STOPSHIP Allow certain system apps to access it
try {
r.run();
} catch (InvalidSqlException e) {
diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java
index 8399441..bbbde37 100644
--- a/src/com/android/providers/contacts/ContactsProvider2.java
+++ b/src/com/android/providers/contacts/ContactsProvider2.java
@@ -8893,6 +8893,9 @@
}
case PROFILE_AS_VCARD: {
+ if (!mode.equals("r")) {
+ throw new IllegalArgumentException("Write is not supported.");
+ }
// When opening a contact as file, we pass back contents as a
// vCard-encoded stream. We build into a local buffer first,
// then pipe into MemoryFile once the exact size is known.
@@ -8902,6 +8905,9 @@
}
case CONTACTS_AS_VCARD: {
+ if (!mode.equals("r")) {
+ throw new IllegalArgumentException("Write is not supported.");
+ }
// When opening a contact as file, we pass back contents as a
// vCard-encoded stream. We build into a local buffer first,
// then pipe into MemoryFile once the exact size is known.
@@ -8911,6 +8917,9 @@
}
case CONTACTS_AS_MULTI_VCARD: {
+ if (!mode.equals("r")) {
+ throw new IllegalArgumentException("Write is not supported.");
+ }
final String lookupKeys = uri.getPathSegments().get(2);
final String[] lookupKeyList = lookupKeys.split(":");
final StringBuilder inBuilder = new StringBuilder();
@@ -8956,7 +8965,8 @@
default:
throw new FileNotFoundException(
- mDbHelper.get().exceptionMessage("File does not exist", uri));
+ mDbHelper.get().exceptionMessage(
+ "Stream I/O not supported on this URI.", uri));
}
}
diff --git a/tests2/src/com/android/providers/contacts/tests2/AllUriTest.java b/tests2/src/com/android/providers/contacts/tests2/AllUriTest.java
index a38a4a6..f7747cc 100644
--- a/tests2/src/com/android/providers/contacts/tests2/AllUriTest.java
+++ b/tests2/src/com/android/providers/contacts/tests2/AllUriTest.java
@@ -15,20 +15,46 @@
*/
package com.android.providers.contacts.tests2;
+import android.content.ContentResolver;
+import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.CancellationSignal;
import android.provider.ContactsContract;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.RawContacts;
+import android.provider.ContactsContract.SyncState;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.LargeTest;
import android.util.Log;
import junit.framework.AssertionFailedError;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.util.ArrayList;
+/*
+ * TODO The following operations would fail, not because they're not supported, but because of
+ * missing parameters. Fix them.
+insert for 'content://com.android.contacts/contacts' failed: Aggregate contacts are created automatically
+insert for 'content://com.android.contacts/raw_contacts/1/data' failed: mimetype is required
+update for 'content://com.android.contacts/raw_contacts/1/stream_items/1' failed: Empty values
+insert for 'content://com.android.contacts/data' failed: raw_contact_id is required
+insert for 'content://com.android.contacts/settings' failed: Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE; URI: content://com.android.contacts/settings?account_type=1, calling user: com.android.providers.contacts.tests2, calling package:com.android.providers.contacts.tests2
+insert for 'content://com.android.contacts/status_updates' failed: PROTOCOL and IM_HANDLE are required
+insert for 'content://com.android.contacts/profile' failed: The profile contact is created automatically
+insert for 'content://com.android.contacts/profile/data' failed: raw_contact_id is required
+insert for 'content://com.android.contacts/profile/raw_contacts/1/data' failed: mimetype is required
+insert for 'content://com.android.contacts/profile/status_updates' failed: PROTOCOL and IM_HANDLE are required
+
+
+openInputStream for 'content://com.android.contacts/contacts/as_multi_vcard/XXX' failed: Caught Exception: Invalid lookup id: XXX
+openInputStream for 'content://com.android.contacts/directory_file_enterprise/XXX?directory=0' failed: Caught Exception: java.lang.IllegalArgumentException: Directory is not a remote directory: content://com.android.contacts/directory_file_enterprise/XXX?directory=0
+openOutputStream for 'content://com.android.contacts/directory_file_enterprise/XXX?directory=0' failed: Caught Exception: java.lang.IllegalArgumentException: Directory is not a remote directory: content://com.android.contacts/directory_file_enterprise/XXX?directory=0
+*/
+
/**
* TODO Add test for delete/update/insert too.
* TODO Copy it to CTS
@@ -42,67 +68,74 @@
// The following markers are planned, but not implemented and the definition below is not all
// correct yet.
// "d" : supports delete.
- // "u" : supports delete.
+ // "u" : supports update.
// "i" : supports insert.
// "r" : supports read.
// "w" : supports write.
// "s" : has x_times_contacted and x_last_time_contacted.
// "t" : has x_times_used and x_last_time_used.
private static final String[][] URIs = {
- {"content://com.android.contacts/contacts", "s"},
- {"content://com.android.contacts/contacts/1", "s"},
+ {"content://com.android.contacts/contacts", "sud"},
+ {"content://com.android.contacts/contacts/1", "sud"},
{"content://com.android.contacts/contacts/1/data", "t"},
{"content://com.android.contacts/contacts/1/entities", "t"},
{"content://com.android.contacts/contacts/1/suggestions"},
{"content://com.android.contacts/contacts/1/suggestions/XXX"},
- {"content://com.android.contacts/contacts/1/photo"},
- {"content://com.android.contacts/contacts/1/display_photo", "-rw"},
- {"content://com.android.contacts/contacts_corp/1/photo", "-rw"},
- {"content://com.android.contacts/contacts_corp/1/display_photo", "-rw"},
- {"content://com.android.contacts/contacts/1/stream_items"},
+ {"content://com.android.contacts/contacts/1/photo", "r"},
+ {"content://com.android.contacts/contacts/1/display_photo", "-r"},
+ {"content://com.android.contacts/contacts_corp/1/photo", "-r"},
+ {"content://com.android.contacts/contacts_corp/1/display_photo", "-r"},
+
{"content://com.android.contacts/contacts/filter", "s"},
{"content://com.android.contacts/contacts/filter/XXX", "s"},
- {"content://com.android.contacts/contacts/lookup/nlookup", "s"},
+
+ {"content://com.android.contacts/contacts/lookup/nlookup", "sud"},
{"content://com.android.contacts/contacts/lookup/nlookup/data", "t"},
- {"content://com.android.contacts/contacts/lookup/nlookup/photo", "t"},
- {"content://com.android.contacts/contacts/lookup/nlookup/1", "s"},
+ {"content://com.android.contacts/contacts/lookup/nlookup/photo", "tr"},
+
+ {"content://com.android.contacts/contacts/lookup/nlookup/1", "sud"},
{"content://com.android.contacts/contacts/lookup/nlookup/1/data"},
- {"content://com.android.contacts/contacts/lookup/nlookup/1/photo"},
- {"content://com.android.contacts/contacts/lookup/nlookup/display_photo", "-rw"},
- {"content://com.android.contacts/contacts/lookup/nlookup/1/display_photo", "-rw"},
+ {"content://com.android.contacts/contacts/lookup/nlookup/1/photo", "r"},
+ {"content://com.android.contacts/contacts/lookup/nlookup/display_photo", "-r"},
+ {"content://com.android.contacts/contacts/lookup/nlookup/1/display_photo", "-r"},
{"content://com.android.contacts/contacts/lookup/nlookup/entities"},
{"content://com.android.contacts/contacts/lookup/nlookup/1/entities"},
- {"content://com.android.contacts/contacts/lookup/nlookup/stream_items"},
- {"content://com.android.contacts/contacts/lookup/nlookup/1/stream_items"},
- {"content://com.android.contacts/contacts/as_vcard/nlookup"},
+
+ {"content://com.android.contacts/contacts/as_vcard/nlookup", "r"},
{"content://com.android.contacts/contacts/as_multi_vcard/XXX"},
+
{"content://com.android.contacts/contacts/strequent/", "s"},
{"content://com.android.contacts/contacts/strequent/filter/XXX", "s"},
+
{"content://com.android.contacts/contacts/group/XXX"},
+
{"content://com.android.contacts/contacts/frequent", "s"},
{"content://com.android.contacts/contacts/delete_usage", "-d"},
{"content://com.android.contacts/contacts/filter_enterprise?directory=0", "s"},
{"content://com.android.contacts/contacts/filter_enterprise/XXX?directory=0", "s"},
- {"content://com.android.contacts/raw_contacts", "s"},
- {"content://com.android.contacts/raw_contacts/1", "s"},
- {"content://com.android.contacts/raw_contacts/1/data", "t"},
+
+ {"content://com.android.contacts/raw_contacts", "siud"},
+ {"content://com.android.contacts/raw_contacts/1", "sud"},
+ {"content://com.android.contacts/raw_contacts/1/data", "tu"},
{"content://com.android.contacts/raw_contacts/1/display_photo", "-rw"},
{"content://com.android.contacts/raw_contacts/1/entity"},
- {"content://com.android.contacts/raw_contacts/1/stream_items"},
- {"content://com.android.contacts/raw_contacts/1/stream_items/1"},
+
{"content://com.android.contacts/raw_contact_entities"},
{"content://com.android.contacts/raw_contact_entities_corp", "!"},
- {"content://com.android.contacts/data", "t"},
- {"content://com.android.contacts/data/1", "t"},
+
+ {"content://com.android.contacts/data", "tud"},
+ {"content://com.android.contacts/data/1", "tudr"},
{"content://com.android.contacts/data/phones", "t"},
{"content://com.android.contacts/data_enterprise/phones", "!"},
- {"content://com.android.contacts/data/phones/1", "t"},
+ {"content://com.android.contacts/data/phones/1", "tud"},
{"content://com.android.contacts/data/phones/filter", "t"},
{"content://com.android.contacts/data/phones/filter/XXX", "t"},
+
{"content://com.android.contacts/data/phones/filter_enterprise?directory=0", "t"},
{"content://com.android.contacts/data/phones/filter_enterprise/XXX?directory=0", "t"},
+
{"content://com.android.contacts/data/emails", "t"},
- {"content://com.android.contacts/data/emails/1", "t"},
+ {"content://com.android.contacts/data/emails/1", "tud"},
{"content://com.android.contacts/data/emails/lookup", "t"},
{"content://com.android.contacts/data/emails/lookup/XXX", "t"},
{"content://com.android.contacts/data/emails/filter", "t"},
@@ -112,10 +145,10 @@
{"content://com.android.contacts/data/emails/lookup_enterprise", "t"},
{"content://com.android.contacts/data/emails/lookup_enterprise/XXX", "t"},
{"content://com.android.contacts/data/postals", "t"},
- {"content://com.android.contacts/data/postals/1", "t"},
- {"content://com.android.contacts/data/usagefeedback/XXX", "-u"},
+ {"content://com.android.contacts/data/postals/1", "tud"},
+ {"content://com.android.contacts/data/usagefeedback/1,2,3", "-u"},
{"content://com.android.contacts/data/callables/", "t"},
- {"content://com.android.contacts/data/callables/1", "t"},
+ {"content://com.android.contacts/data/callables/1", "tud"},
{"content://com.android.contacts/data/callables/filter", "t"},
{"content://com.android.contacts/data/callables/filter/XXX", "t"},
{"content://com.android.contacts/data/callables/filter_enterprise?directory=0", "t"},
@@ -124,95 +157,54 @@
{"content://com.android.contacts/data/contactables/", "t"},
{"content://com.android.contacts/data/contactables/filter", "t"},
{"content://com.android.contacts/data/contactables/filter/XXX", "t"},
- {"content://com.android.contacts/groups"},
- {"content://com.android.contacts/groups/1"},
+
+ {"content://com.android.contacts/groups", "iud"},
+ {"content://com.android.contacts/groups/1", "ud"},
{"content://com.android.contacts/groups_summary"},
- {"content://com.android.contacts/syncstate"},
- {"content://com.android.contacts/syncstate/1", "-du"},
- {"content://com.android.contacts/profile/syncstate"},
+ {"content://com.android.contacts/syncstate", "iud"},
+ {"content://com.android.contacts/syncstate/1", "-ud"},
+ {"content://com.android.contacts/profile/syncstate", "iud"},
{"content://com.android.contacts/phone_lookup/XXX"},
{"content://com.android.contacts/phone_lookup_enterprise/XXX"},
- {"content://com.android.contacts/aggregation_exceptions"},
- {"content://com.android.contacts/settings"},
- {"content://com.android.contacts/status_updates"},
+ {"content://com.android.contacts/aggregation_exceptions", "u"},
+ {"content://com.android.contacts/settings", "ud"},
+ {"content://com.android.contacts/status_updates", "ud"},
{"content://com.android.contacts/status_updates/1"},
{"content://com.android.contacts/search_suggest_query"},
{"content://com.android.contacts/search_suggest_query/XXX"},
{"content://com.android.contacts/search_suggest_shortcut/XXX"},
{"content://com.android.contacts/provider_status"},
- {"content://com.android.contacts/directories"},
+ {"content://com.android.contacts/directories", "u"},
{"content://com.android.contacts/directories/1"},
{"content://com.android.contacts/directories_enterprise"},
{"content://com.android.contacts/directories_enterprise/1"},
{"content://com.android.contacts/complete_name"},
- {"content://com.android.contacts/profile", "s"},
+ {"content://com.android.contacts/profile", "su"},
{"content://com.android.contacts/profile/entities", "s"},
- {"content://com.android.contacts/profile/data", "t"},
- {"content://com.android.contacts/profile/data/1", "t"},
+ {"content://com.android.contacts/profile/data", "tud"},
+ {"content://com.android.contacts/profile/data/1", "td"},
{"content://com.android.contacts/profile/photo", "t"},
- {"content://com.android.contacts/profile/display_photo", "-rw"},
- {"content://com.android.contacts/profile/as_vcard"},
- {"content://com.android.contacts/profile/raw_contacts", "s"},
- {"content://com.android.contacts/profile/raw_contacts/1", "s"},
- {"content://com.android.contacts/profile/raw_contacts/1/data", "t"},
+ {"content://com.android.contacts/profile/display_photo", "-r"},
+ {"content://com.android.contacts/profile/as_vcard", "r"},
+ {"content://com.android.contacts/profile/raw_contacts", "siud"},
+
+ // Note this should have supported update... Too late to add.
+ {"content://com.android.contacts/profile/raw_contacts/1", "sd"},
+ {"content://com.android.contacts/profile/raw_contacts/1/data", "tu"},
{"content://com.android.contacts/profile/raw_contacts/1/entity"},
- {"content://com.android.contacts/profile/status_updates"},
+ {"content://com.android.contacts/profile/status_updates", "ud"},
{"content://com.android.contacts/profile/raw_contact_entities"},
- {"content://com.android.contacts/stream_items"},
- {"content://com.android.contacts/stream_items/photo"},
- {"content://com.android.contacts/stream_items/1"},
- {"content://com.android.contacts/stream_items/1/photo"},
- {"content://com.android.contacts/stream_items/1/photo/1"},
- {"content://com.android.contacts/stream_items_limit"},
- {"content://com.android.contacts/display_photo/1", "-rw"},
+ {"content://com.android.contacts/display_photo/1", "-r"},
{"content://com.android.contacts/photo_dimensions"},
{"content://com.android.contacts/deleted_contacts"},
{"content://com.android.contacts/deleted_contacts/1"},
- {"content://com.android.contacts/directory_file_enterprise/XXX", "-rw"},
-
- {"content://contacts/extensions"},
- {"content://contacts/extensions/1"},
- {"content://contacts/groups"},
- {"content://contacts/groups/1"},
- {"content://contacts/groups/name/XXX/members"},
- {"content://contacts/groups/system_id/XXX/members"},
- {"content://contacts/groupmembership"},
- {"content://contacts/groupmembership/1"},
- {"content://contacts/people", "s"},
- {"content://contacts/people/filter/XXX"},
- {"content://contacts/people/1"},
- {"content://contacts/people/1/extensions"},
- {"content://contacts/people/1/extensions/1"},
- {"content://contacts/people/1/phones"},
- {"content://contacts/people/1/phones/1"},
- {"content://contacts/people/1/photo"},
- {"content://contacts/people/1/contact_methods"},
- {"content://contacts/people/1/contact_methods/1"},
- {"content://contacts/people/1/organizations"},
- {"content://contacts/people/1/organizations/1"},
- {"content://contacts/people/1/groupmembership"},
- {"content://contacts/people/1/groupmembership/1"},
- {"content://contacts/people/1/update_contact_time", "-u"},
- {"content://contacts/deleted_people", "-"},
- {"content://contacts/deleted_groups", "-"},
- {"content://contacts/phones"},
- {"content://contacts/phones/filter/XXX"},
- {"content://contacts/phones/1"},
- {"content://contacts/photos"},
- {"content://contacts/photos/1"},
- {"content://contacts/contact_methods"},
- {"content://contacts/contact_methods/email"},
- {"content://contacts/contact_methods/1"},
- {"content://contacts/organizations"},
- {"content://contacts/organizations/1"},
- {"content://contacts/search_suggest_query"},
- {"content://contacts/search_suggest_query/XXX"},
- {"content://contacts/search_suggest_shortcut/XXX"},
- {"content://contacts/settings"},
+ {"content://com.android.contacts/directory_file_enterprise/XXX?directory=0", "-"},
};
private static final String[] ARG1 = {"-1"};
+ private ContentResolver mResolver;
+
private ArrayList<String> mFailures;
@Override
@@ -220,6 +212,7 @@
super.setUp();
mFailures = new ArrayList<>();
+ mResolver = getContext().getContentResolver();
}
@Override
@@ -231,11 +224,14 @@
super.tearDown();
}
- private void addFailure(String message) {
- mFailures.add(message);
- Log.e(TAG, "Failed: " + message);
- if (mFailures.size() > 100) {
- // Too many failures...
+ private void addFailure(String message, Throwable th) {
+ Log.e(TAG, "Failed: " + message, th);
+
+ final int MAX = 100;
+ if (mFailures.size() == MAX) {
+ mFailures.add("Too many failures.");
+ } else if (mFailures.size() > MAX) {
+ // Too many failures already...
} else {
mFailures.add(message);
}
@@ -264,6 +260,10 @@
mFailures = null;
}
+ private static Uri getUri(String[] path) {
+ return Uri.parse(path[0]);
+ }
+
private static boolean supportsQuery(String[] path) {
if (path.length == 1) {
return true; // supports query by default.
@@ -271,12 +271,28 @@
return !(path[1].contains("-") || path[1].contains("!"));
}
- private static Uri getUri(String[] path) {
- return Uri.parse(path[0]);
+ private static boolean supportsInsert(String[] path) {
+ return (path.length) >= 2 && path[1].contains("i");
+ }
+
+ private static boolean supportsUpdate(String[] path) {
+ return (path.length) >= 2 && path[1].contains("u");
+ }
+
+ private static boolean supportsDelete(String[] path) {
+ return (path.length) >= 2 && path[1].contains("d");
+ }
+
+ private static boolean supportsRead(String[] path) {
+ return (path.length) >= 2 && path[1].contains("r");
+ }
+
+ private static boolean supportsWrite(String[] path) {
+ return (path.length) >= 2 && path[1].contains("w");
}
private String[] getColumns(Uri uri) {
- try (Cursor c = getContext().getContentResolver().query(uri,
+ try (Cursor c = mResolver.query(uri,
null, // projection
"1=2", // selection
null, // selection args
@@ -290,32 +306,47 @@
String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
try {
- try (Cursor c = getContext().getContentResolver().query(uri, projection, selection,
+ try (Cursor c = mResolver.query(uri, projection, selection,
selectionArgs, sortOrder)) {
c.moveToFirst();
}
} catch (Throwable th) {
- addFailure("Query failed: URI=" + uri + " Message=" + th.getMessage());
+ addFailure("Query failed: URI=" + uri + " Message=" + th.getMessage(), th);
}
try {
// With CancellationSignal.
- try (Cursor c = getContext().getContentResolver().query(uri, projection, selection,
+ try (Cursor c = mResolver.query(uri, projection, selection,
selectionArgs, sortOrder, new CancellationSignal())) {
c.moveToFirst();
}
} catch (Throwable th) {
- addFailure("Query with cancel failed: URI=" + uri + " Message=" + th.getMessage());
+ addFailure("Query with cancel failed: URI=" + uri + " Message=" + th.getMessage(), th);
}
try {
// With limit.
- try (Cursor c = getContext().getContentResolver().query(
+ try (Cursor c = mResolver.query(
uri.buildUpon().appendQueryParameter(
ContactsContract.LIMIT_PARAM_KEY, "0").build(),
projection, selection, selectionArgs, sortOrder)) {
c.moveToFirst();
}
} catch (Throwable th) {
- addFailure("Query with limit failed: URI=" + uri + " Message=" + th.getMessage());
+ addFailure("Query with limit failed: URI=" + uri + " Message=" + th.getMessage(), th);
+ }
+
+ try {
+ // With account.
+ try (Cursor c = mResolver.query(
+ uri.buildUpon()
+ .appendQueryParameter(RawContacts.ACCOUNT_NAME, "a")
+ .appendQueryParameter(RawContacts.ACCOUNT_TYPE, "b")
+ .appendQueryParameter(RawContacts.DATA_SET, "c")
+ .build(),
+ projection, selection, selectionArgs, sortOrder)) {
+ c.moveToFirst();
+ }
+ } catch (Throwable th) {
+ addFailure("Query with limit failed: URI=" + uri + " Message=" + th.getMessage(), th);
}
}
@@ -323,7 +354,7 @@
String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
try {
- try (Cursor c = getContext().getContentResolver().query(uri, projection, selection,
+ try (Cursor c = mResolver.query(uri, projection, selection,
selectionArgs, sortOrder)) {
c.moveToFirst();
}
@@ -331,7 +362,7 @@
// pass.
return;
}
- addFailure("Query on " + uri + " expected to fail, but succeeded.");
+ addFailure("Query on " + uri + " expected to fail, but succeeded.", null);
}
/**
@@ -358,8 +389,8 @@
final Uri uri = getUri(path);
for (String column : getColumns(uri)) {
- if (column.toLowerCase().startsWith(ContactsContract.HIDDEN_COLUMN_PREFIX)) { // doesn't seem to be working
- addFailure("Uri " + uri + " returned hidden column " + column);
+ if (column.toLowerCase().startsWith(ContactsContract.HIDDEN_COLUMN_PREFIX)) {
+ addFailure("Uri " + uri + " returned hidden column " + column, null);
}
}
}
@@ -517,13 +548,13 @@
private void checkColumnAccessible(Uri uri, String column) {
try {
- try (Cursor c = getContext().getContentResolver().query(
+ try (Cursor c = mResolver.query(
uri, new String[]{column}, column + "=0", null, column
)) {
c.moveToFirst();
}
} catch (Throwable th) {
- addFailure("Query failed: URI=" + uri + " Message=" + th.getMessage());
+ addFailure("Query failed: URI=" + uri + " Message=" + th.getMessage(), th);
}
}
@@ -541,7 +572,7 @@
private void checkColumnNotAccessibleInner(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
try {
- try (Cursor c = getContext().getContentResolver().query(uri, projection, selection,
+ try (Cursor c = mResolver.query(uri, projection, selection,
selectionArgs, sortOrder)) {
c.moveToFirst();
}
@@ -550,7 +581,7 @@
return;
}
addFailure("Query on " + uri +
- " expected to throw IllegalArgumentException, but succeeded.");
+ " expected to throw IllegalArgumentException, but succeeded.", null);
}
private void checkColumnNotAccessible(Uri uri, String column) {
@@ -593,4 +624,93 @@
}
failIfFailed();
}
+
+ private void checkExecutable(String operation, Uri uri, boolean shouldWork, Runnable r) {
+ if (shouldWork) {
+ try {
+ r.run();
+ } catch (Exception e) {
+ addFailure(operation + " for '" + uri + "' failed: " + e.getMessage(), e);
+ }
+ } else {
+ try {
+ r.run();
+ addFailure(operation + " for '" + uri + "' NOT failed.", null);
+ } catch (Exception expected) {
+ }
+ }
+ }
+
+ public void testAllOperations() {
+ final ContentValues cv = new ContentValues();
+
+ for (String[] path : URIs) {
+ final Uri uri = getUri(path);
+
+ cv.clear();
+ if (supportsQuery(path)) {
+ cv.put(getColumns(uri)[0], 1);
+ } else {
+ cv.put("_id", 1);
+ }
+ if (uri.toString().contains("syncstate")) {
+ cv.put(SyncState.ACCOUNT_NAME, "abc");
+ cv.put(SyncState.ACCOUNT_TYPE, "def");
+ }
+
+ checkExecutable("insert", uri, supportsInsert(path), () -> {
+ final Uri newUri = mResolver.insert(uri, cv);
+ if (newUri == null) {
+ addFailure("Insert for '" + uri + "' returned null.", null);
+ } else {
+ // "profile/raw_contacts/#" is missing update support. too late to add, so
+ // just skip.
+ if (!newUri.toString().startsWith(
+ "content://com.android.contacts/profile/raw_contacts/")) {
+ checkExecutable("insert -> update", newUri, true, () -> {
+ mResolver.update(newUri, cv, null, null);
+ });
+ }
+ checkExecutable("insert -> delete", newUri, true, () -> {
+ mResolver.delete(newUri, null, null);
+ });
+ }
+ });
+ checkExecutable("update", uri, supportsUpdate(path), () -> {
+ mResolver.update(uri, cv, "1=2", null);
+ });
+ checkExecutable("delete", uri, supportsDelete(path), () -> {
+ mResolver.delete(uri, "1=2", null);
+ });
+ }
+ failIfFailed();
+ }
+
+ public void testAllFileOperations() {
+ for (String[] path : URIs) {
+ final Uri uri = getUri(path);
+
+ checkExecutable("openInputStream", uri, supportsRead(path), () -> {
+ try (InputStream st = mResolver.openInputStream(uri)) {
+ } catch (FileNotFoundException e) {
+ // TODO This happens because we try to read nonexistent photos. Ideally
+ // we should actually check it's readable.
+ if (e.getMessage().contains("Stream I/O not supported")) {
+ throw new RuntimeException("Caught Exception: " + e.toString(), e);
+ }
+ } catch (Exception e) {
+ throw new RuntimeException("Caught Exception: " + e.toString(), e);
+ }
+ });
+ checkExecutable("openOutputStream", uri, supportsWrite(path), () -> {
+ try (OutputStream st = mResolver.openOutputStream(uri)) {
+ } catch (Exception e) {
+ throw new RuntimeException("Caught Exception: " + e.toString(), e);
+ }
+ });
+ }
+ failIfFailed();
+ }
}
+
+