CTS for Strequent projection

We'll cover other URLs and other parameters (selection, etc) in M or later.

Bug 19797150

Change-Id: I8feb09effc362188faa4ad8cab555b698155f3f7
diff --git a/tests/tests/provider/src/android/provider/cts/ContactsContract_FrequentsStrequentsTest.java b/tests/tests/provider/src/android/provider/cts/ContactsContract_FrequentsStrequentsTest.java
index fcb48cf..b515550 100644
--- a/tests/tests/provider/src/android/provider/cts/ContactsContract_FrequentsStrequentsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/ContactsContract_FrequentsStrequentsTest.java
@@ -29,6 +29,7 @@
 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.DataUsageFeedback;
 import android.provider.ContactsContract.RawContacts;
 import android.provider.cts.ContactsContract_TestDataBuilder.TestContact;
@@ -174,6 +175,57 @@
         assertCursorStoredValuesWithContactsFilter(uri, ids, false, sContentValues[1], sContentValues[0]);
     }
 
+    public void testStrequents_projection() throws Exception {
+        long[] ids = setupTestData();
+
+        // Start contact 0 and mark contact 2 as frequent
+        starContact(ids[0]);
+        markDataAsUsed(mDataIds[2], 1);
+
+        // Construct a uri for phone only favorites.
+        Uri uri = Contacts.CONTENT_STREQUENT_URI;
+
+        DatabaseAsserts.checkProjection(mResolver, uri,
+                new String[]{
+                        Contacts._ID,
+                        Contacts.HAS_PHONE_NUMBER,
+                        Contacts.NAME_RAW_CONTACT_ID,
+                        Contacts.IS_USER_PROFILE,
+                        Contacts.CUSTOM_RINGTONE,
+                        Contacts.DISPLAY_NAME,
+                        Contacts.DISPLAY_NAME_ALTERNATIVE,
+                        Contacts.DISPLAY_NAME_SOURCE,
+                        Contacts.IN_DEFAULT_DIRECTORY,
+                        Contacts.IN_VISIBLE_GROUP,
+                        Contacts.LAST_TIME_CONTACTED,
+                        Contacts.LOOKUP_KEY,
+                        Contacts.PHONETIC_NAME,
+                        Contacts.PHONETIC_NAME_STYLE,
+                        Contacts.PHOTO_ID,
+                        Contacts.PHOTO_FILE_ID,
+                        Contacts.PHOTO_URI,
+                        Contacts.PHOTO_THUMBNAIL_URI,
+                        Contacts.SEND_TO_VOICEMAIL,
+                        Contacts.SORT_KEY_ALTERNATIVE,
+                        Contacts.SORT_KEY_PRIMARY,
+                        Contacts.STARRED,
+                        Contacts.PINNED,
+                        Contacts.TIMES_CONTACTED,
+                        Contacts.CONTACT_LAST_UPDATED_TIMESTAMP,
+                        Contacts.CONTACT_PRESENCE,
+                        Contacts.CONTACT_CHAT_CAPABILITY,
+                        Contacts.CONTACT_STATUS,
+                        Contacts.CONTACT_STATUS_TIMESTAMP,
+                        Contacts.CONTACT_STATUS_RES_PACKAGE,
+                        Contacts.CONTACT_STATUS_LABEL,
+                        Contacts.CONTACT_STATUS_ICON,
+                        Data.TIMES_USED,
+                        Data.LAST_TIME_USED,
+                },
+                new long[]{ids[0], ids[2]}
+        );
+    }
+
     public void testStrequents_phoneOnly() throws Exception {
         long[] ids = setupTestData();
 
@@ -213,6 +265,63 @@
                 sContentValues[2], sContentValues[0]);
     }
 
+    public void testStrequents_phoneOnly_projection() throws Exception {
+        long[] ids = setupTestData();
+
+        // Start contact 0 and mark contact 2 as frequent
+        starContact(ids[0]);
+        markDataAsUsed(mDataIds[2], 1);
+
+        // Construct a uri for phone only favorites.
+        Uri uri = Contacts.CONTENT_STREQUENT_URI.buildUpon().
+                appendQueryParameter(ContactsContract.STREQUENT_PHONE_ONLY, "true").build();
+
+        DatabaseAsserts.checkProjection(mResolver, uri,
+                new String[] {
+                        Data._ID,
+                        Contacts.HAS_PHONE_NUMBER,
+                        Contacts.NAME_RAW_CONTACT_ID,
+                        Contacts.IS_USER_PROFILE,
+                        Contacts.CUSTOM_RINGTONE,
+                        Contacts.DISPLAY_NAME,
+                        Contacts.DISPLAY_NAME_ALTERNATIVE,
+                        Contacts.DISPLAY_NAME_SOURCE,
+                        Contacts.IN_DEFAULT_DIRECTORY,
+                        Contacts.IN_VISIBLE_GROUP,
+                        Contacts.LAST_TIME_CONTACTED,
+                        Contacts.LOOKUP_KEY,
+                        Contacts.PHONETIC_NAME,
+                        Contacts.PHONETIC_NAME_STYLE,
+                        Contacts.PHOTO_ID,
+                        Contacts.PHOTO_FILE_ID,
+                        Contacts.PHOTO_URI,
+                        Contacts.PHOTO_THUMBNAIL_URI,
+                        Contacts.SEND_TO_VOICEMAIL,
+                        Contacts.SORT_KEY_ALTERNATIVE,
+                        Contacts.SORT_KEY_PRIMARY,
+                        Contacts.STARRED,
+                        Contacts.PINNED,
+                        Contacts.TIMES_CONTACTED,
+                        Contacts.CONTACT_LAST_UPDATED_TIMESTAMP,
+                        Contacts.CONTACT_PRESENCE,
+                        Contacts.CONTACT_CHAT_CAPABILITY,
+                        Contacts.CONTACT_STATUS,
+                        Contacts.CONTACT_STATUS_TIMESTAMP,
+                        Contacts.CONTACT_STATUS_RES_PACKAGE,
+                        Contacts.CONTACT_STATUS_LABEL,
+                        Contacts.CONTACT_STATUS_ICON,
+                        Data.TIMES_USED,
+                        Data.LAST_TIME_USED,
+                        Phone.NUMBER,
+                        Phone.TYPE,
+                        Phone.LABEL,
+                        Phone.IS_SUPER_PRIMARY,
+                        Phone.CONTACT_ID,
+                },
+                new long[] {mDataIds[0], mDataIds[2]} // Note _id from phone_only is data._id
+        );
+    }
+
     public void testFrequents_noFrequentsReturnsEmptyCursor() throws Exception {
         long[] ids = setupTestData();
         assertCursorStoredValuesWithContactsFilter(Contacts.CONTENT_FREQUENT_URI, ids, false);
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 7546066..7f98284 100644
--- a/tests/tests/provider/src/android/provider/cts/contacts/DatabaseAsserts.java
+++ b/tests/tests/provider/src/android/provider/cts/contacts/DatabaseAsserts.java
@@ -21,6 +21,8 @@
 import android.content.ContentValues;
 import android.database.Cursor;
 import android.net.Uri;
+import android.provider.BaseColumns;
+import android.provider.ContactsContract.Contacts;
 import android.provider.cts.contacts.account.StaticAccountAuthenticator;
 import android.test.MoreAsserts;
 
@@ -261,4 +263,76 @@
         }
         return true;
     }
+
+    public static String buildIdSelection(long[] ids) {
+        StringBuilder selection = new StringBuilder();
+        selection.append(BaseColumns._ID + " in ");
+        selection.append("(");
+        for (int i = 0; i < ids.length; i++) {
+            if (i != 0) selection.append(",");
+            selection.append(ids[i]);
+        }
+        selection.append(")");
+        return selection.toString();
+    }
+
+    /**
+     * Check if a query accepts all columns in the projection, and a resulting cursor contains
+     * all expected columns.  This also checks the number of resulting rows, but don't check
+     * actual content in a returned cursor.
+     */
+    public static void checkProjection(ContentResolver resolver,
+            Uri uri, String[] allColumns, long[] ids) {
+        final String selection = buildIdSelection(ids);
+
+        // First, check the null projection (i.e. all columns).
+        checkProjectionInner(resolver, uri, null, allColumns, selection, ids.length,
+                /* keepPosition =*/ false);
+
+        // All columns.
+        checkProjectionInner(resolver, uri, allColumns, allColumns, selection, ids.length,
+                /* keepPosition =*/ true);
+
+        // Select each column one by one.
+        for (int i = 0; i < allColumns.length; i++) {
+            final String[] columns = new String[] {allColumns[i]};
+            checkProjectionInner(resolver, uri, columns, columns, selection, ids.length,
+                        /* keepPosition =*/ true);
+        }
+
+        // Select two columns.
+        for (int i = 0; i < allColumns.length; i++) {
+            for (int j = 0; j < allColumns.length; j++) {
+                // Requesting the same column multiple times is okay, but it'd make the column
+                // order check harder.
+                if (i == j) continue;
+                final String[] columns = new String[] {allColumns[i], allColumns[j]};
+                checkProjectionInner(resolver, uri, columns, columns, selection, ids.length,
+                        /* keepPosition =*/ true);
+            }
+        }
+    }
+
+    private static void checkProjectionInner(ContentResolver resolver,
+            Uri uri, String[] projection, String[] expectedColumns,
+            String selection, int expectedRowCount, boolean keepPosition) {
+        final Cursor c = resolver.query(uri, projection, selection,
+                /* args =*/ null, /* sort =*/ null);
+        Assert.assertNotNull(c);
+        try {
+            Assert.assertEquals("# of rows", expectedRowCount, c.getCount());
+
+            // Make sure expected columns exist.
+            for (int i = 0; i < expectedColumns.length; i++) {
+                final String column = expectedColumns[i];
+                if (keepPosition) {
+                    Assert.assertEquals(column, c.getColumnName(i));
+                } else {
+                    Assert.assertTrue(column, c.getColumnIndex(column) >= 0);
+                }
+            }
+        } finally {
+            c.close();
+        }
+    }
 }