Speedup query by using a more narrow View. Change the Loader tests to use a Mock-ContentProvider to not change any real data

Change-Id: Ia87a53038249c8739cf66fc3a77686a1132441c3
diff --git a/src/com/android/contacts/views/detail/ContactLoader.java b/src/com/android/contacts/views/detail/ContactLoader.java
index 103136a..06dd68e 100644
--- a/src/com/android/contacts/views/detail/ContactLoader.java
+++ b/src/com/android/contacts/views/detail/ContactLoader.java
@@ -44,6 +44,7 @@
  * Loads a single Contact and all it constituent RawContacts.
  */
 public class ContactLoader extends Loader<ContactLoader.Result> {
+    private static boolean sIsSynchronous;
     private Uri mLookupUri;
     private Result mContact;
     private ForceLoadContentObserver mObserver;
@@ -203,8 +204,7 @@
          * Tries to lookup a contact using both Id and lookup key of the given Uri. Returns a
          * valid Result instance if successful or {@link Result#NOT_FOUND} if empty
          */
-        private Result loadContactHeaderData(final ContentResolver resolver,
-                final Uri lookupUri) {
+        private Result loadContactHeaderData(final ContentResolver resolver, final Uri lookupUri) {
             if (resolver == null) throw new IllegalArgumentException("resolver must not be null");
             if (lookupUri == null) {
                 // This can happen if the row was removed
@@ -222,9 +222,8 @@
             final long uriContactId = Long.parseLong(segments.get(3));
             final String uriLookupKey = Uri.encode(segments.get(2));
             final Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, uriContactId);
-            final Uri dataUri = Uri.withAppendedPath(contactUri, Contacts.Data.CONTENT_DIRECTORY);
 
-            final Cursor cursor = resolver.query(dataUri,
+            final Cursor cursor = resolver.query(contactUri,
                     new String[] {
                         Contacts.NAME_RAW_CONTACT_ID,
                         Contacts.DISPLAY_NAME_SOURCE,
@@ -356,7 +355,7 @@
     }
 
     public ContactLoader(Context context, Uri lookupUri) {
-            super(context);
+        super(context);
         mLookupUri = lookupUri;
     }
 
@@ -371,7 +370,12 @@
 
     @Override
     public void forceLoad() {
-        new LoadContactTask().execute((Void[])null);
+        LoadContactTask task = new LoadContactTask();
+        if (sIsSynchronous) {
+            task.onPostExecute(task.doInBackground((Void[])null));
+            return;
+        }
+        task.execute((Void[])null);
     }
 
     @Override
@@ -387,4 +391,9 @@
         mContact = null;
         mDestroyed = true;
     }
+
+
+    public static void setSynchronous(boolean value) {
+        sIsSynchronous = value;
+    }
 }
diff --git a/tests/src/com/android/contacts/ContactDetailLoaderTest.java b/tests/src/com/android/contacts/ContactDetailLoaderTest.java
index f7a7a84..981ed74 100644
--- a/tests/src/com/android/contacts/ContactDetailLoaderTest.java
+++ b/tests/src/com/android/contacts/ContactDetailLoaderTest.java
@@ -16,33 +16,44 @@
 
 package com.android.contacts;
 
+import com.android.contacts.tests.mocks.ContactsMockContext;
+import com.android.contacts.tests.mocks.MockContentProvider;
 import com.android.contacts.views.detail.ContactLoader;
 
 import android.content.ContentUris;
-import android.content.ContentValues;
-import android.database.Cursor;
 import android.net.Uri;
+import android.provider.ContactsContract.CommonDataKinds;
 import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.DisplayNameSources;
 import android.provider.ContactsContract.RawContacts;
+import android.provider.ContactsContract.RawContactsEntity;
+import android.provider.ContactsContract.StatusUpdates;
 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.provider.ContactsContract.RawContacts.Data;
+import android.provider.ContactsContract.RawContacts.Entity;
 import android.test.AndroidTestCase;
 
 import junit.framework.AssertionFailedError;
 
 /**
  * Runs ContactLoader tests for the the contact-detail view.
- * TODO: Warning: This currently only works on wiped phones as this will wipe
- * your contact data
- * TODO: Test all fields returned by the Loader
- * TODO: Test social entries returned by the Loader
  */
 public class ContactDetailLoaderTest extends AndroidTestCase {
+    private ContactsMockContext mMockContext;
+    private MockContentProvider mContactsProvider;
+
     @Override
     protected void setUp() throws Exception {
         super.setUp();
-        //mContext.getContentResolver().delete(Data.CONTENT_URI, null, null);
-        //mContext.getContentResolver().delete(RawContacts.CONTENT_URI, null, null);
+        ContactLoader.setSynchronous(true);
+        mMockContext = new ContactsMockContext(getContext());
+        mContactsProvider = mMockContext.getContactsProvider();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        ContactLoader.setSynchronous(false);
     }
 
     /**
@@ -65,89 +76,11 @@
     }
 
     private ContactLoader.Result assertLoadContact(Uri uri) {
-        final ContactLoader loader = new ContactLoader(mContext, uri);
+        final ContactLoader loader = new ContactLoader(mMockContext, uri);
         final ContactLoader.LoadContactTask loadContactTask = loader.new LoadContactTask();
         return loadContactTask.testExecute();
     }
 
-    protected Uri insertStructuredName(long rawContactId, String givenName, String familyName) {
-        ContentValues values = new ContentValues();
-        StringBuilder sb = new StringBuilder();
-        if (givenName != null) {
-            sb.append(givenName);
-        }
-        if (givenName != null && familyName != null) {
-            sb.append(" ");
-        }
-        if (familyName != null) {
-            sb.append(familyName);
-        }
-        values.put(StructuredName.DISPLAY_NAME, sb.toString());
-        values.put(StructuredName.GIVEN_NAME, givenName);
-        values.put(StructuredName.FAMILY_NAME, familyName);
-
-        return insertStructuredName(rawContactId, values);
-    }
-
-    protected Uri insertStructuredName(long rawContactId, ContentValues values) {
-        values.put(Data.RAW_CONTACT_ID, rawContactId);
-        values.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
-        Uri resultUri = getContext().getContentResolver().insert(Data.CONTENT_URI, values);
-        return resultUri;
-    }
-
-    protected Cursor queryRawContact(long rawContactId) {
-        return getContext().getContentResolver().query(ContentUris.withAppendedId(
-                RawContacts.CONTENT_URI, rawContactId), null, null, null, null);
-    }
-
-    protected Cursor queryContact(long contactId) {
-        return getContext().getContentResolver().query(ContentUris.withAppendedId(
-                Contacts.CONTENT_URI, contactId), null, null, null, null);
-    }
-
-    private long getContactIdByRawContactId(long rawContactId) {
-        Cursor c = queryRawContact(rawContactId);
-        assertTrue(c.moveToFirst());
-        long contactId = c.getLong(c.getColumnIndex(RawContacts.CONTACT_ID));
-        c.close();
-        return contactId;
-    }
-
-    private String getContactLookupByContactId(long contactId) {
-        Cursor c = queryContact(contactId);
-        assertTrue(c.moveToFirst());
-        String lookup = c.getString(c.getColumnIndex(Contacts.LOOKUP_KEY));
-        c.close();
-        return lookup;
-    }
-
-    public long createRawContact(String sourceId, String givenName, String familyName) {
-        ContentValues values = new ContentValues();
-
-        values.put(RawContacts.ACCOUNT_NAME, "aa");
-        values.put(RawContacts.ACCOUNT_TYPE, "mock");
-        values.put(RawContacts.SOURCE_ID, sourceId);
-        values.put(RawContacts.VERSION, 1);
-        values.put(RawContacts.DELETED, 0);
-        values.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DEFAULT);
-        values.put(RawContacts.CUSTOM_RINGTONE, "d");
-        values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
-        values.put(RawContacts.LAST_TIME_CONTACTED, 12345);
-        values.put(RawContacts.STARRED, 1);
-        values.put(RawContacts.SYNC1, "e");
-        values.put(RawContacts.SYNC2, "f");
-        values.put(RawContacts.SYNC3, "g");
-        values.put(RawContacts.SYNC4, "h");
-
-        Uri rawContactUri =
-            getContext().getContentResolver().insert(RawContacts.CONTENT_URI, values);
-
-        long rawContactId = ContentUris.parseId(rawContactUri);
-        insertStructuredName(rawContactId, givenName, familyName);
-        return rawContactId;
-    }
-
     public void testNullUri() {
         IllegalArgumentException e =
             assertThrows(IllegalArgumentException.class, new Runnable() {
@@ -180,117 +113,346 @@
 
     public void testLoadContactWithContactIdUri() {
         // Use content Uris that only contain the ID
-        // Use some special characters in the source id to ensure that Encode/Decode properly
-        // works in Uris
-        long rawContactId1 = createRawContact("JohnDoe:;\"'[]{}=+-_\\|/.,<>?!@#$", "John", "Doe");
-        long rawContactId2 = createRawContact("JaneDuh%12%%^&*()", "Jane", "Duh");
+        final long contactId = 1;
+        final long rawContactId = 11;
+        final long dataId = 21;
 
-        long contactId1 = getContactIdByRawContactId(rawContactId1);
-        long contactId2 = getContactIdByRawContactId(rawContactId2);
+        final String encodedLookup = Uri.encode("aa%12%@!");
+        final Uri baseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
+        final Uri lookupUri = ContentUris.withAppendedId(
+                Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, encodedLookup),
+                contactId);
+        final Uri dataUri = Uri.withAppendedPath(baseUri, Contacts.Data.CONTENT_DIRECTORY);
 
-        Uri contactUri1 = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId1);
-        Uri contactUri2 = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId2);
+        ContactQueries queries = new ContactQueries();
+        queries.fetchLookupAndId(baseUri, contactId, encodedLookup);
+        queries.fetchHeaderData(baseUri, rawContactId, encodedLookup);
+        queries.fetchSocial(dataUri, contactId);
+        queries.fetchRawContacts(contactId, dataId, rawContactId);
 
-        ContactLoader.Result contact1 = assertLoadContact(contactUri1);
-        ContactLoader.Result contact2 = assertLoadContact(contactUri2);
+        ContactLoader.Result contact = assertLoadContact(baseUri);
 
-        assertEquals(contactId1, contact1.getId());
-        assertEquals(contactId2, contact2.getId());
+        assertEquals(contactId, contact.getId());
+        assertEquals(rawContactId, contact.getNameRawContactId());
+        assertEquals(DisplayNameSources.STRUCTURED_NAME, contact.getDisplayNameSource());
+        assertEquals(encodedLookup, contact.getLookupKey());
+        assertEquals(lookupUri, contact.getLookupUri());
+        assertEquals(1, contact.getEntities().size());
+        assertEquals(1, contact.getStatuses().size());
+        mContactsProvider.verify();
     }
 
     public void testLoadContactWithOldStyleUri() {
         // Use content Uris that only contain the ID but use the format used in Donut
-        long rawContactId1 = createRawContact("JohnDoe", "John", "Doe");
-        long rawContactId2 = createRawContact("JaneDuh", "Jane", "Duh");
+        final long contactId = 1;
+        final long rawContactId = 11;
+        final long dataId = 21;
 
-        Uri oldUri1 = ContentUris.withAppendedId(Uri.parse("content://contacts"), rawContactId1);
-        Uri oldUri2 = ContentUris.withAppendedId(Uri.parse("content://contacts"), rawContactId2);
+        final String encodedLookup = Uri.encode("aa%12%@!");
+        final Uri legacyUri = ContentUris.withAppendedId(
+                Uri.parse("content://contacts"), rawContactId);
+        final Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
+        final Uri baseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
+        final Uri lookupUri = ContentUris.withAppendedId(
+                Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, encodedLookup),
+                contactId);
+        final Uri dataUri = Uri.withAppendedPath(baseUri, Contacts.Data.CONTENT_DIRECTORY);
 
-        ContactLoader.Result contact1 = assertLoadContact(oldUri1);
-        ContactLoader.Result contact2 = assertLoadContact(oldUri2);
+        ContactQueries queries = new ContactQueries();
+        queries.fetchContactIdAndLookupFromRawContactUri(rawContactUri, contactId, encodedLookup);
+        queries.fetchHeaderData(baseUri, rawContactId, encodedLookup);
+        queries.fetchSocial(dataUri, contactId);
+        queries.fetchRawContacts(contactId, dataId, rawContactId);
 
-        long contactId1 = getContactIdByRawContactId(rawContactId1);
-        long contactId2 = getContactIdByRawContactId(rawContactId2);
+        ContactLoader.Result contact = assertLoadContact(legacyUri);
 
-        assertEquals(contactId1, contact1.getId());
-        assertEquals(contactId2, contact2.getId());
+        assertEquals(contactId, contact.getId());
+        assertEquals(rawContactId, contact.getNameRawContactId());
+        assertEquals(DisplayNameSources.STRUCTURED_NAME, contact.getDisplayNameSource());
+        assertEquals(encodedLookup, contact.getLookupKey());
+        assertEquals(lookupUri, contact.getLookupUri());
+        assertEquals(1, contact.getEntities().size());
+        assertEquals(1, contact.getStatuses().size());
+        mContactsProvider.verify();
     }
 
     public void testLoadContactWithContactLookupUri() {
         // Use lookup-style Uris that do not contain the Contact-ID
-        long rawContactId1 = createRawContact("JohnDoe", "John", "Doe");
-        long rawContactId2 = createRawContact("JaneDuh", "Jane", "Duh");
 
-        assertTrue(rawContactId1 != rawContactId2);
+        final long contactId = 1;
+        final long rawContactId = 11;
+        final long dataId = 21;
 
-        long contactId1 = getContactIdByRawContactId(rawContactId1);
-        long contactId2 = getContactIdByRawContactId(rawContactId2);
+        final String encodedLookup = Uri.encode("aa%12%@!");
+        final Uri baseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
+        final Uri lookupNoIdUri = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, encodedLookup);
+        final Uri lookupUri = ContentUris.withAppendedId(lookupNoIdUri, contactId);
+        final Uri dataUri = Uri.withAppendedPath(baseUri, Contacts.Data.CONTENT_DIRECTORY);
 
-        assertTrue(contactId1 != contactId2);
+        ContactQueries queries = new ContactQueries();
+        queries.fetchLookupAndId(lookupNoIdUri, contactId, encodedLookup);
+        queries.fetchHeaderData(baseUri, rawContactId, encodedLookup);
+        queries.fetchSocial(dataUri, contactId);
+        queries.fetchRawContacts(contactId, dataId, rawContactId);
 
-        String lookupKey1 = getContactLookupByContactId(contactId1);
-        String lookupKey2 = getContactLookupByContactId(contactId2);
-        assertFalse(lookupKey1.equals(lookupKey2));
+        ContactLoader.Result contact = assertLoadContact(lookupNoIdUri);
 
-        Uri contactLookupUri1 = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey1);
-        Uri contactLookupUri2 = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey2);
-
-        ContactLoader.Result contact1 = assertLoadContact(contactLookupUri1);
-        ContactLoader.Result contact2 = assertLoadContact(contactLookupUri2);
-
-        assertEquals(contactId1, contact1.getId());
-        assertEquals(contactId2, contact2.getId());
+        assertEquals(contactId, contact.getId());
+        assertEquals(rawContactId, contact.getNameRawContactId());
+        assertEquals(DisplayNameSources.STRUCTURED_NAME, contact.getDisplayNameSource());
+        assertEquals(encodedLookup, contact.getLookupKey());
+        assertEquals(lookupUri, contact.getLookupUri());
+        assertEquals(1, contact.getEntities().size());
+        assertEquals(1, contact.getStatuses().size());
+        mContactsProvider.verify();
     }
 
     public void testLoadContactWithContactLookupAndIdUri() {
         // Use lookup-style Uris that also contain the Contact-ID
-        long rawContactId1 = createRawContact("JohnDoe", "John", "Doe");
-        long rawContactId2 = createRawContact("JaneDuh", "Jane", "Duh");
+        final long contactId = 1;
+        final long rawContactId = 11;
+        final long dataId = 21;
 
-        long contactId1 = getContactIdByRawContactId(rawContactId1);
-        long contactId2 = getContactIdByRawContactId(rawContactId2);
+        final String encodedLookup = Uri.encode("aa%12%@!");
+        final Uri baseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
+        final Uri lookupUri = ContentUris.withAppendedId(
+                Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, encodedLookup),
+                contactId);
+        final Uri dataUri = Uri.withAppendedPath(baseUri, Contacts.Data.CONTENT_DIRECTORY);
 
-        String lookupKey1 = getContactLookupByContactId(contactId1);
-        String lookupKey2 = getContactLookupByContactId(contactId2);
+        ContactQueries queries = new ContactQueries();
+        queries.fetchHeaderData(baseUri, rawContactId, encodedLookup);
+        queries.fetchSocial(dataUri, contactId);
+        queries.fetchRawContacts(contactId, dataId, rawContactId);
 
-        Uri contactLookupUri1 = ContentUris.withAppendedId(
-                Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey1), contactId1);
-        Uri contactLookupUri2 = ContentUris.withAppendedId(
-                Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey2), contactId2);
+        ContactLoader.Result contact = assertLoadContact(lookupUri);
 
-        ContactLoader.Result contact1 = assertLoadContact(contactLookupUri1);
-        ContactLoader.Result contact2 = assertLoadContact(contactLookupUri2);
-
-        assertEquals(contactId1, contact1.getId());
-        assertEquals(contactId2, contact2.getId());
+        assertEquals(contactId, contact.getId());
+        assertEquals(rawContactId, contact.getNameRawContactId());
+        assertEquals(DisplayNameSources.STRUCTURED_NAME, contact.getDisplayNameSource());
+        assertEquals(encodedLookup, contact.getLookupKey());
+        assertEquals(lookupUri, contact.getLookupUri());
+        assertEquals(1, contact.getEntities().size());
+        assertEquals(1, contact.getStatuses().size());
+        mContactsProvider.verify();
     }
 
     public void testLoadContactWithContactLookupWithIncorrectIdUri() {
         // Use lookup-style Uris that contain incorrect Contact-ID
         // (we want to ensure that still the correct contact is chosen)
+        // In this test, the incorrect Id references another Contact
 
-        long rawContactId1 = createRawContact("JohnDoe", "John", "Doe");
-        long rawContactId2 = createRawContact("JaneDuh", "Jane", "Duh");
+        final long contactId = 1;
+        final long wrongContactId = 2;
+        final long rawContactId = 11;
+        final long wrongRawContactId = 12;
+        final long dataId = 21;
 
-        long contactId1 = getContactIdByRawContactId(rawContactId1);
-        long contactId2 = getContactIdByRawContactId(rawContactId2);
+        final String encodedLookup = Uri.encode("aa%12%@!");
+        final String wrongEncodedLookup = Uri.encode("ab%12%@!");
+        final Uri baseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
+        final Uri wrongBaseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, wrongContactId);
+        final Uri lookupUri = ContentUris.withAppendedId(
+                Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, encodedLookup),
+                contactId);
+        final Uri lookupWithWrongIdUri = ContentUris.withAppendedId(
+                Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, encodedLookup),
+                wrongContactId);
+        final Uri dataUri = Uri.withAppendedPath(baseUri, Contacts.Data.CONTENT_DIRECTORY);
 
-        String lookupKey1 = getContactLookupByContactId(contactId1);
-        String lookupKey2 = getContactLookupByContactId(contactId2);
+        ContactQueries queries = new ContactQueries();
+        queries.fetchHeaderData(wrongBaseUri, wrongRawContactId, wrongEncodedLookup);
+        queries.fetchLookupAndId(lookupWithWrongIdUri, contactId, encodedLookup);
+        queries.fetchHeaderData(baseUri, rawContactId, encodedLookup);
+        queries.fetchSocial(dataUri, contactId);
+        queries.fetchRawContacts(contactId, dataId, rawContactId);
 
-        long[] fakeIds = new long[] { 0, rawContactId1, rawContactId2, contactId1, contactId2 };
+        ContactLoader.Result contact = assertLoadContact(lookupWithWrongIdUri);
 
-        for (long fakeContactId : fakeIds) {
-            Uri contactLookupUri1 = ContentUris.withAppendedId(
-                    Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey1), fakeContactId);
-            Uri contactLookupUri2 = ContentUris.withAppendedId(
-                    Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey2), fakeContactId);
+        assertEquals(contactId, contact.getId());
+        assertEquals(rawContactId, contact.getNameRawContactId());
+        assertEquals(DisplayNameSources.STRUCTURED_NAME, contact.getDisplayNameSource());
+        assertEquals(encodedLookup, contact.getLookupKey());
+        assertEquals(lookupUri, contact.getLookupUri());
+        assertEquals(1, contact.getEntities().size());
+        assertEquals(1, contact.getStatuses().size());
 
-            ContactLoader.Result contact1 = assertLoadContact(contactLookupUri1);
-            ContactLoader.Result contact2 = assertLoadContact(contactLookupUri2);
+        mContactsProvider.verify();
+    }
 
-            assertEquals(contactId1, contact1.getId());
-            assertEquals(contactId2, contact2.getId());
+    public void testLoadContactWithContactLookupWithIncorrectIdUri2() {
+        // Use lookup-style Uris that contain incorrect Contact-ID
+        // (we want to ensure that still the correct contact is chosen)
+        // In this test, the incorrect Id references no contact
+
+        final long contactId = 1;
+        final long wrongContactId = 2;
+        final long rawContactId = 11;
+        final long wrongRawContactId = 12;
+        final long dataId = 21;
+
+        final String encodedLookup = Uri.encode("aa%12%@!");
+        final String wrongEncodedLookup = Uri.encode("ab%12%@!");
+        final Uri baseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
+        final Uri wrongBaseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, wrongContactId);
+        final Uri lookupUri = ContentUris.withAppendedId(
+                Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, encodedLookup),
+                contactId);
+        final Uri lookupWithWrongIdUri = ContentUris.withAppendedId(
+                Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, encodedLookup),
+                wrongContactId);
+        final Uri dataUri = Uri.withAppendedPath(baseUri, Contacts.Data.CONTENT_DIRECTORY);
+
+        ContactQueries queries = new ContactQueries();
+        queries.fetchHeaderDataNoResult(wrongBaseUri);
+        queries.fetchLookupAndId(lookupWithWrongIdUri, contactId, encodedLookup);
+        queries.fetchHeaderData(baseUri, rawContactId, encodedLookup);
+        queries.fetchSocial(dataUri, contactId);
+        queries.fetchRawContacts(contactId, dataId, rawContactId);
+
+        ContactLoader.Result contact = assertLoadContact(lookupWithWrongIdUri);
+
+        assertEquals(contactId, contact.getId());
+        assertEquals(rawContactId, contact.getNameRawContactId());
+        assertEquals(DisplayNameSources.STRUCTURED_NAME, contact.getDisplayNameSource());
+        assertEquals(encodedLookup, contact.getLookupKey());
+        assertEquals(lookupUri, contact.getLookupUri());
+        assertEquals(1, contact.getEntities().size());
+        assertEquals(1, contact.getStatuses().size());
+
+        mContactsProvider.verify();
+    }
+
+    public void testLoadContactWithContactLookupWithIncorrectIdUri3() {
+        // Use lookup-style Uris that contain incorrect Contact-ID
+        // (we want to ensure that still the correct contact is chosen)
+        // In this test, the incorrect Id references no contact and the lookup
+        // key can also not be resolved
+
+        final long contactId = 1;
+        final long wrongContactId = 2;
+        final long rawContactId = 11;
+        final long wrongRawContactId = 12;
+        final long dataId = 21;
+
+        final String encodedLookup = Uri.encode("aa%12%@!");
+        final String wrongEncodedLookup = Uri.encode("ab%12%@!");
+        final Uri baseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
+        final Uri wrongBaseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, wrongContactId);
+        final Uri lookupUri = ContentUris.withAppendedId(
+                Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, encodedLookup),
+                contactId);
+        final Uri lookupWithWrongIdUri = ContentUris.withAppendedId(
+                Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, encodedLookup),
+                wrongContactId);
+        final Uri dataUri = Uri.withAppendedPath(baseUri, Contacts.Data.CONTENT_DIRECTORY);
+
+        ContactQueries queries = new ContactQueries();
+        queries.fetchHeaderDataNoResult(wrongBaseUri);
+        queries.fetchLookupAndIdNoResult(lookupWithWrongIdUri);
+
+        ContactLoader.Result contact = assertLoadContact(lookupWithWrongIdUri);
+
+        assertEquals(ContactLoader.Result.NOT_FOUND, contact);
+
+        mContactsProvider.verify();
+    }
+
+    private class ContactQueries {
+        private void fetchRawContacts(final long contactId, final long dataId,
+                final long rawContactId) {
+            mContactsProvider.expectQuery(RawContactsEntity.CONTENT_URI)
+                .withDefaultProjection(new String[] {
+                        RawContacts._ID, RawContacts.ACCOUNT_NAME, RawContacts.ACCOUNT_TYPE,
+                        RawContacts.DIRTY, RawContacts.VERSION, RawContacts.SOURCE_ID,
+                        RawContacts.SYNC1, RawContacts.SYNC2, RawContacts.SYNC3, RawContacts.SYNC4,
+                        RawContacts.DELETED, RawContacts.CONTACT_ID, RawContacts.STARRED,
+                        RawContacts.IS_RESTRICTED, RawContacts.NAME_VERIFIED,
+
+                        Entity.DATA_ID, Data.RES_PACKAGE, Data.MIMETYPE, Data.IS_PRIMARY,
+                        Data.IS_SUPER_PRIMARY, Data.DATA_VERSION,
+                        CommonDataKinds.GroupMembership.GROUP_SOURCE_ID,
+                        Data.DATA1, Data.DATA2, Data.DATA3, Data.DATA4, Data.DATA5,
+                        Data.DATA6, Data.DATA7, Data.DATA8, Data.DATA9, Data.DATA10,
+                        Data.DATA11, Data.DATA12, Data.DATA13, Data.DATA14, Data.DATA15,
+                        Data.SYNC1, Data.SYNC2, Data.SYNC3, Data.SYNC4
+                })
+                .withSelection(
+                        RawContacts.CONTACT_ID + "=?",
+                        new String[] { String.valueOf(contactId) } )
+                .returnRow(
+                        rawContactId, "mockAccountName", "mockAccountType",
+                        0, 1, "aa%12%@!",
+                        "", "", "", "",
+                        0, contactId, 0,
+                        0, 1,
+
+                        dataId, "", StructuredName.CONTENT_ITEM_TYPE, 1,
+                        1, 1,
+                        "mockGroupId",
+                        "dat1", "dat2", "dat3", "dat4", "dat5",
+                        "dat6", "dat7", "dat8", "dat9", "dat10",
+                        "dat11", "dat12", "dat13", "dat14", null,
+                        "syn1", "syn2", "syn3", "syn4");
+        }
+
+        private void fetchSocial(final Uri dataUri, final long expectedContactId) {
+            mContactsProvider.expectQuery(dataUri)
+                    .withProjection(
+                            Contacts._ID, StatusUpdates.STATUS, StatusUpdates.STATUS_RES_PACKAGE,
+                            StatusUpdates.STATUS_ICON, StatusUpdates.STATUS_LABEL,
+                            StatusUpdates.STATUS_TIMESTAMP, StatusUpdates.PRESENCE)
+                    .withSelection(
+                            StatusUpdates.PRESENCE +" IS NOT NULL OR " +
+                            StatusUpdates.STATUS + " IS NOT NULL",
+                            (String[]) null)
+                    .returnRow(
+                            expectedContactId, "This is a mock Status update", 0,
+                            1, 2,
+                            0, StatusUpdates.AVAILABLE);
+        }
+
+        private void fetchHeaderData(final Uri uri, final long expectedRawContactId,
+                final String expectedEncodedLookup) {
+            mContactsProvider.expectQuery(uri)
+                    .withProjection(
+                            Contacts.NAME_RAW_CONTACT_ID,
+                            Contacts.DISPLAY_NAME_SOURCE,
+                            Contacts.LOOKUP_KEY)
+                    .returnRow(
+                            expectedRawContactId,
+                            DisplayNameSources.STRUCTURED_NAME,
+                            expectedEncodedLookup);
+        }
+
+        private void fetchHeaderDataNoResult(final Uri uri) {
+            mContactsProvider.expectQuery(uri)
+                    .withProjection(
+                            Contacts.NAME_RAW_CONTACT_ID,
+                            Contacts.DISPLAY_NAME_SOURCE,
+                            Contacts.LOOKUP_KEY);
+        }
+
+        private void fetchLookupAndId(final Uri sourceUri, final long expectedContactId,
+                final String expectedEncodedLookup) {
+            mContactsProvider.expectQuery(sourceUri)
+                    .withProjection(Contacts.LOOKUP_KEY, Contacts._ID)
+                    .returnRow(expectedEncodedLookup, expectedContactId);
+        }
+
+        private void fetchLookupAndIdNoResult(final Uri sourceUri) {
+            mContactsProvider.expectQuery(sourceUri)
+                    .withProjection(Contacts.LOOKUP_KEY, Contacts._ID);
+        }
+
+        private void fetchContactIdAndLookupFromRawContactUri(final Uri rawContactUri,
+                final long expectedContactId, final String expectedEncodedLookup) {
+            // TODO: use a lighter query by joining rawcontacts with contacts in provider
+            // (See ContactContracts.java)
+            final Uri dataUri = Uri.withAppendedPath(rawContactUri, Data.CONTENT_DIRECTORY);
+            mContactsProvider.expectQuery(dataUri)
+                    .withProjection(RawContacts.CONTACT_ID, Contacts.LOOKUP_KEY)
+                    .returnRow(expectedContactId, expectedEncodedLookup);
         }
     }
 }
diff --git a/tests/src/com/android/contacts/ContactDetailTest.java b/tests/src/com/android/contacts/ContactDetailTest.java
new file mode 100644
index 0000000..16538e6
--- /dev/null
+++ b/tests/src/com/android/contacts/ContactDetailTest.java
@@ -0,0 +1,47 @@
+package com.android.contacts;
+
+import com.android.contacts.activities.ContactDetailActivity;
+import com.android.contacts.tests.mocks.ContactsMockContext;
+import com.android.contacts.tests.mocks.MockContentProvider;
+import com.android.contacts.views.detail.ContactLoader;
+
+import android.content.ContentUris;
+import android.content.Intent;
+import android.provider.ContactsContract.Contacts;
+import android.test.ActivityUnitTestCase;
+
+public class ContactDetailTest extends ActivityUnitTestCase<ContactDetailActivity> {
+    private ContactsMockContext mContext;
+    private MockContentProvider mContactsProvider;
+
+    public ContactDetailTest() {
+        super(ContactDetailActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        ContactLoader.setSynchronous(true);
+        mContext = new ContactsMockContext(getInstrumentation().getTargetContext());
+        mContactsProvider = mContext.getContactsProvider();
+        setActivityContext(mContext);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        ContactLoader.setSynchronous(false);
+    }
+
+//    public void testFoo() {
+//        // Use lookup-style Uris that also contain the Contact-ID
+//        //long rawContactId1 = mCreator.createRawContact("JohnDoe", "John", "Doe");
+//        //long contactId1 = mCreator.getContactIdByRawContactId(rawContactId1);
+//        //Uri contactUri1 = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId1);
+//        Intent intent = new Intent(Intent.ACTION_VIEW,
+//                ContentUris.withAppendedId(Contacts.CONTENT_URI, 123));
+//        startActivity(intent, null, null);
+//        ContactDetailActivity activity = getActivity();
+//        mContactsProvider.verify();
+//    }
+}
diff --git a/tests/src/com/android/contacts/tests/mocks/ContactsMockContext.java b/tests/src/com/android/contacts/tests/mocks/ContactsMockContext.java
index bd2010e..4697b83 100644
--- a/tests/src/com/android/contacts/tests/mocks/ContactsMockContext.java
+++ b/tests/src/com/android/contacts/tests/mocks/ContactsMockContext.java
@@ -55,4 +55,9 @@
     public MockContentProvider getSettingsProvider() {
         return mSettingsProvider;
     }
+
+    @Override
+    public Context getApplicationContext() {
+        return this;
+    }
 }
diff --git a/tests/src/com/android/contacts/tests/mocks/MockContentProvider.java b/tests/src/com/android/contacts/tests/mocks/MockContentProvider.java
index 63b134a..4a11eb0 100644
--- a/tests/src/com/android/contacts/tests/mocks/MockContentProvider.java
+++ b/tests/src/com/android/contacts/tests/mocks/MockContentProvider.java
@@ -193,7 +193,7 @@
         }
         if (selection != null) {
             sb.append(" selection: '").append(selection).append("'");
-            if (projection != null) {
+            if (selectionArgs != null) {
                 sb.append(Arrays.toString(selectionArgs));
             } else {
                 sb.append("[]");
@@ -204,4 +204,9 @@
         }
         return sb.toString();
     }
+
+    public void verify() {
+        Assert.assertTrue("Not all expected queries have been called: " +
+                mExpectedQueries, mExpectedQueries.isEmpty());
+    }
 }