CTS tests for Contactables api
Bug 8507318
Change-Id: I7336efd2ddcdd83518ecf12a14f69ce74da9e9cb
diff --git a/tests/tests/provider/src/android/provider/cts/ContactsContract_DataTest.java b/tests/tests/provider/src/android/provider/cts/ContactsContract_DataTest.java
index 612163c..51d62a3 100644
--- a/tests/tests/provider/src/android/provider/cts/ContactsContract_DataTest.java
+++ b/tests/tests/provider/src/android/provider/cts/ContactsContract_DataTest.java
@@ -22,9 +22,16 @@
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.ContentValues;
+import android.database.Cursor;
import android.net.Uri;
import android.os.SystemClock;
import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.Contactables;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Organization;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.RawContacts;
import android.provider.cts.ContactsContract_TestDataBuilder.TestContact;
@@ -36,6 +43,8 @@
import android.provider.cts.contacts.RawContactUtil;
import android.test.InstrumentationTestCase;
+import java.util.ArrayList;
+
public class ContactsContract_DataTest extends InstrumentationTestCase {
private ContentResolver mResolver;
private ContactsContract_TestDataBuilder mBuilder;
@@ -93,6 +102,86 @@
lookupContact.getId(), data.load().getRawContact().load().getContactId());
}
+ public void testContactablesUri() throws Exception {
+ TestRawContact rawContact = mBuilder.newRawContact()
+ .with(RawContacts.ACCOUNT_TYPE, "test_account")
+ .with(RawContacts.ACCOUNT_NAME, "test_name")
+ .insert();
+ rawContact.newDataRow(CommonDataKinds.Email.CONTENT_ITEM_TYPE)
+ .with(Email.DATA, "test@test.com")
+ .with(Email.TYPE, Email.TYPE_WORK)
+ .insert();
+ ContentValues cv = new ContentValues();
+ cv.put(Email.DATA, "test@test.com");
+ cv.put(Email.TYPE, Email.TYPE_WORK);
+
+ Uri contentUri = ContactsContract.CommonDataKinds.Contactables.CONTENT_URI;
+ try {
+ assertCursorStoredValuesWithRawContactsFilter(contentUri,
+ new long[] {rawContact.getId()}, cv);
+ rawContact.newDataRow(CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE)
+ .with(CommonDataKinds.StructuredPostal.DATA1, "100 Sesame Street")
+ .insert();
+
+ rawContact.newDataRow(Phone.CONTENT_ITEM_TYPE)
+ .with(Phone.DATA, "123456789")
+ .with(Phone.TYPE, Phone.TYPE_MOBILE)
+ .insert();
+
+ ContentValues cv2 = new ContentValues();
+ cv.put(Phone.DATA, "123456789");
+ cv.put(Phone.TYPE, Phone.TYPE_MOBILE);
+
+ // Contactables Uri should return only email and phone data items.
+ DatabaseAsserts.assertStoredValuesInUriMatchExactly(mResolver, contentUri, null,
+ Data.RAW_CONTACT_ID + "=?", new String[] {String.valueOf(rawContact.getId())},
+ null, cv, cv2);
+ } finally {
+ // Clean up
+ rawContact.delete();
+ }
+ }
+
+ public void testContactablesFilterByLastName_returnsCorrectDataRows() throws Exception {
+ long[] ids = setupContactablesTestData();
+ Uri filterUri = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "tamale");
+ assertCursorStoredValuesWithRawContactsFilter(filterUri, ids,
+ ContactablesTestHelper.getContentValues(0));
+ }
+
+ public void testContactablesFilterByFirstName_returnsCorrectDataRows() throws Exception {
+ long[] ids = setupContactablesTestData();
+ Uri filterUri = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "hot");
+ assertCursorStoredValuesWithRawContactsFilter(filterUri, ids,
+ ContactablesTestHelper.getContentValues(0));
+ Uri filterUri2 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "tam");
+ assertCursorStoredValuesWithRawContactsFilter(filterUri2, ids,
+ ContactablesTestHelper.getContentValues(0, 1));
+ }
+
+ public void testContactablesFilterByPhonePrefix_returnsCorrectDataRows() throws Exception {
+ long[] ids = setupContactablesTestData();
+ Uri filterUri = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "518");
+ assertCursorStoredValuesWithRawContactsFilter(filterUri, ids,
+ ContactablesTestHelper.getContentValues(2));
+ Uri filterUri2 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "51");
+ assertCursorStoredValuesWithRawContactsFilter(filterUri2, ids,
+ ContactablesTestHelper.getContentValues(0, 2));
+ }
+
+ public void testContactablesFilterByEmailPrefix_returnsCorrectDataRows() throws Exception {
+ long[] ids = setupContactablesTestData();
+ Uri filterUri = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "doeassoc");
+ assertCursorStoredValuesWithRawContactsFilter(filterUri, ids,
+ ContactablesTestHelper.getContentValues(2));
+ }
+
+ public void testContactablesFilter_doesNotExist_returnsCorrectDataRows() throws Exception {
+ long[] ids = setupContactablesTestData();
+ Uri filterUri = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "doesnotexist");
+ assertCursorStoredValuesWithRawContactsFilter(filterUri, ids, new ContentValues[0]);
+ }
+
public void testDataInsert_updatesContactLastUpdatedTimestamp() {
DatabaseAsserts.ContactIdPair ids = DatabaseAsserts.assertAndCreateContact(mResolver);
long baseTime = ContactUtil.queryContactLastUpdatedTimestamp(mResolver, ids.mContactId);
@@ -151,5 +240,155 @@
values.put(CommonDataKinds.Phone.LABEL, "free directory assistance");
return DataUtil.insertData(mResolver, rawContactId, values);
}
+
+ private void assertCursorStoredValuesWithRawContactsFilter(Uri uri, long[] rawContactsId,
+ ContentValues... expected) {
+ // We need this helper function to add a filter for specific raw contacts because
+ // otherwise tests will fail if performed on a device with existing contacts data
+ StringBuilder sb = new StringBuilder();
+ sb.append(Data.RAW_CONTACT_ID + " in ");
+ sb.append("(");
+ for (int i = 0; i < rawContactsId.length; i++) {
+ if (i != 0) sb.append(",");
+ sb.append(rawContactsId[i]);
+ }
+ sb.append(")");
+ DatabaseAsserts.assertStoredValuesInUriMatchExactly(mResolver, uri, null, sb.toString(),
+ null, null, expected);
+ }
+
+
+ private long[] setupContactablesTestData() throws Exception {
+ TestRawContact rawContact = mBuilder.newRawContact()
+ .with(RawContacts.ACCOUNT_TYPE, "test_account")
+ .with(RawContacts.ACCOUNT_NAME, "test_name")
+ .insert();
+ rawContact.newDataRow(StructuredName.CONTENT_ITEM_TYPE)
+ .with(StructuredName.DISPLAY_NAME, "Hot Tamale")
+ .insert();
+ rawContact.newDataRow(Email.CONTENT_ITEM_TYPE)
+ .with(Email.DATA, "tamale@acme.com")
+ .with(Email.TYPE, Email.TYPE_HOME)
+ .insert();
+ rawContact.newDataRow(Email.CONTENT_ITEM_TYPE)
+ .with(Email.DATA, "hot@google.com")
+ .with(Email.TYPE, Email.TYPE_WORK)
+ .insert();
+ rawContact.newDataRow(Phone.CONTENT_ITEM_TYPE)
+ .with(Phone.DATA, "510-123-5769")
+ .with(Email.TYPE, Phone.TYPE_HOME)
+ .insert();
+
+ TestRawContact rawContact2 = mBuilder.newRawContact()
+ .with(RawContacts.ACCOUNT_TYPE, "test_account")
+ .with(RawContacts.ACCOUNT_NAME, "test_name")
+ .insert();
+ rawContact2.newDataRow(StructuredName.CONTENT_ITEM_TYPE)
+ .with(StructuredName.DISPLAY_NAME, "Cold Tamago")
+ .insert();
+ rawContact2.newDataRow(Email.CONTENT_ITEM_TYPE)
+ .with(Email.DATA, "eggs@farmers.org")
+ .with(Email.TYPE, Email.TYPE_HOME)
+ .insert();
+
+ TestRawContact rawContact3 = mBuilder.newRawContact()
+ .with(RawContacts.ACCOUNT_TYPE, "test_account")
+ .with(RawContacts.ACCOUNT_NAME, "test_name")
+ .insert();
+ rawContact3.newDataRow(StructuredName.CONTENT_ITEM_TYPE)
+ .with(StructuredName.DISPLAY_NAME, "John Doe")
+ .insert();
+ rawContact3.newDataRow(Email.CONTENT_ITEM_TYPE)
+ .with(Email.DATA, "doeassociates@deer.com")
+ .with(Email.TYPE, Email.TYPE_WORK)
+ .insert();
+ rawContact3.newDataRow(Phone.CONTENT_ITEM_TYPE)
+ .with(Phone.DATA, "518-354-1111")
+ .with(Phone.TYPE, Phone.TYPE_HOME)
+ .insert();
+ rawContact3.newDataRow(Organization.CONTENT_ITEM_TYPE)
+ .with(Organization.DATA, "Doe Industries")
+ .insert();
+ return new long[] {rawContact.getId(), rawContact2.getId(), rawContact3.getId()};
+ }
+
+ // Provides functionality to set up content values for the Contactables tests
+ private static class ContactablesTestHelper {
+ private static ContentValues[] sContentValues = new ContentValues[6];
+ static {
+ ContentValues cv1 = new ContentValues();
+ cv1.put(Contacts.DISPLAY_NAME, "Hot Tamale");
+ cv1.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
+ cv1.put(Email.DATA, "tamale@acme.com");
+ cv1.put(Email.TYPE, Email.TYPE_HOME);
+ sContentValues[0] = cv1;
+
+ ContentValues cv2 = new ContentValues();
+ cv2.put(Contacts.DISPLAY_NAME, "Hot Tamale");
+ cv2.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
+ cv2.put(Phone.DATA, "510-123-5769");
+ cv2.put(Phone.TYPE, Phone.TYPE_HOME);
+ sContentValues[1] = cv2;
+
+ ContentValues cv3 = new ContentValues();
+ cv3.put(Contacts.DISPLAY_NAME, "Hot Tamale");
+ cv3.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
+ cv3.put(Email.DATA, "hot@google.com");
+ cv3.put(Email.TYPE, Email.TYPE_WORK);
+ sContentValues[2] = cv3;
+
+ ContentValues cv4 = new ContentValues();
+ cv4.put(Contacts.DISPLAY_NAME, "Cold Tamago");
+ cv4.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
+ cv4.put(Email.DATA, "eggs@farmers.org");
+ cv4.put(Email.TYPE, Email.TYPE_HOME);
+ sContentValues[3] = cv4;
+
+ ContentValues cv5 = new ContentValues();
+ cv5.put(Contacts.DISPLAY_NAME, "John Doe");
+ cv5.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
+ cv5.put(Email.DATA, "doeassociates@deer.com");
+ cv5.put(Email.TYPE, Email.TYPE_WORK);
+ sContentValues[4] = cv5;
+
+ ContentValues cv6 = new ContentValues();
+ cv6.put(Contacts.DISPLAY_NAME, "John Doe");
+ cv6.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
+ cv6.put(Phone.DATA, "518-354-1111");
+ cv6.put(Phone.TYPE, Phone.TYPE_HOME);
+ sContentValues[5] = cv6;
+ }
+
+ /**
+ * @return An arraylist of contentValues that correspond to the provided raw contacts
+ */
+ public static ContentValues[] getContentValues(int... rawContacts) {
+ ArrayList<ContentValues> cv = new ArrayList<ContentValues>();
+ for (int i = 0; i < rawContacts.length; i++) {
+ switch (rawContacts[i]) {
+ case 0:
+ // rawContact 0 "Hot Tamale" contains ContentValues 0, 1, and 2
+ cv.add(sContentValues[0]);
+ cv.add(sContentValues[1]);
+ cv.add(sContentValues[2]);
+ break;
+ case 1:
+ // rawContact 1 "Cold Tamago" contains ContentValues 3
+ cv.add(sContentValues[3]);
+ break;
+ case 2:
+ // rawContact 1 "John Doe" contains ContentValues 4, 5
+ cv.add(sContentValues[4]);
+ cv.add(sContentValues[5]);
+ break;
+ }
+ }
+ ContentValues[] toReturn = new ContentValues[cv.size()];
+ for (int i = 0; i < cv.size(); i++) {
+ toReturn[i] = cv.get(i);
+ }
+ return toReturn;
+ }
+ }
}
diff --git a/tests/tests/provider/src/android/provider/cts/contacts/DatabaseAsserts.java b/tests/tests/provider/src/android/provider/cts/contacts/DatabaseAsserts.java
index a163960..80a0a65 100644
--- a/tests/tests/provider/src/android/provider/cts/contacts/DatabaseAsserts.java
+++ b/tests/tests/provider/src/android/provider/cts/contacts/DatabaseAsserts.java
@@ -18,11 +18,14 @@
import android.content.ContentResolver;
import android.content.ContentValues;
+import android.database.Cursor;
import android.net.Uri;
import android.test.MoreAsserts;
import junit.framework.Assert;
+import java.util.BitSet;
+
/**
* Common methods for asserting database related operations.
*/
@@ -92,4 +95,107 @@
this.mRawContactId = rawContactId;
}
}
+
+ /**
+ * Queries for a given {@link Uri} against a provided {@link ContentResolver}, and
+ * ensures that the returned cursor contains exactly the expected values.
+ *
+ * @param resolver - ContentResolver to query against
+ * @param uri - {@link Uri} to perform the query for
+ * contained in <code>expectedValues</code> in order for the assert to pass.
+ * @param expectedValues - Array of {@link ContentValues} which the cursor returned from the
+ * query should contain.
+ */
+ public static void assertStoredValuesInUriMatchExactly(ContentResolver resolver, Uri uri,
+ ContentValues... expectedValues) {
+ assertStoredValuesInUriMatchExactly(resolver, uri, null, null, null, null, expectedValues);
+ }
+
+ /**
+ * Queries for a given {@link Uri} against a provided {@link ContentResolver}, and
+ * ensures that the returned cursor contains exactly the expected values.
+ *
+ * @param resolver - ContentResolver to query against
+ * @param uri - {@link Uri} to perform the query for
+ * @param projection - Projection to use for the query. Must contain at least the columns
+ * contained in <code>expectedValues</code> in order for the assert to pass.
+ * @param selection - Selection string to use for the query.
+ * @param selectionArgs - Selection arguments to use for the query.
+ * @param sortOrder - Sort order to use for the query.
+ * @param expectedValues - Array of {@link ContentValues} which the cursor returned from the
+ * query should contain.
+ */
+ public static void assertStoredValuesInUriMatchExactly(ContentResolver resolver, Uri uri, String[] projection,
+ String selection, String[] selectionArgs, String sortOrder,
+ ContentValues... expectedValues) {
+ final Cursor cursor = resolver.query(uri, projection, selection, selectionArgs, sortOrder);
+ try {
+ assertCursorValuesMatchExactly(cursor, expectedValues);
+ } finally {
+ cursor.close();
+ }
+ }
+
+ /**
+ * Ensures that the rows in the cursor match the rows in the expected values exactly. However,
+ * does not require that the rows in the cursor are ordered the same way as those in the
+ * expected values.
+ *
+ * @param cursor - Cursor containing the values to check for
+ * @param expectedValues - Array of ContentValues that the cursor should be expected to
+ * contain.
+ */
+ public static void assertCursorValuesMatchExactly(Cursor cursor,
+ ContentValues... expectedValues) {
+ Assert.assertEquals("Cursor does not contain the number of expected rows",
+ expectedValues.length, cursor.getCount());
+ StringBuilder message = new StringBuilder();
+ // In case if expectedValues contains multiple identical values, remember which cursor
+ // rows are "consumed" to prevent multiple ContentValues from hitting the same row.
+ final BitSet used = new BitSet(cursor.getCount());
+
+ for (ContentValues v : expectedValues) {
+ boolean found = false;
+ cursor.moveToPosition(-1);
+ while (cursor.moveToNext()) {
+ final int pos = cursor.getPosition();
+ if (used.get(pos)) continue;
+ found = equalsWithExpectedValues(cursor, v, message);
+ if (found) {
+ used.set(pos);
+ break;
+ }
+ }
+ Assert.assertTrue("Expected values can not be found " + v + "," + message.toString(),
+ found);
+ }
+ }
+
+ private static boolean equalsWithExpectedValues(Cursor cursor, ContentValues expectedValues,
+ StringBuilder msgBuffer) {
+ for (String column : expectedValues.keySet()) {
+ int index = cursor.getColumnIndex(column);
+ if (index == -1) {
+ msgBuffer.append(" No such column: ").append(column);
+ return false;
+ }
+ Object expectedValue = expectedValues.get(column);
+ String value;
+ expectedValue = expectedValues.getAsString(column);
+ value = cursor.getString(cursor.getColumnIndex(column));
+ if (expectedValue != null && !expectedValue.equals(value) || value != null
+ && !value.equals(expectedValue)) {
+ msgBuffer
+ .append(" Column value ")
+ .append(column)
+ .append(" expected <")
+ .append(expectedValue)
+ .append(">, but was <")
+ .append(value)
+ .append('>');
+ return false;
+ }
+ }
+ return true;
+ }
}