Merge "Seeding CTS tests for ContactsContract." into eclair
diff --git a/tests/tests/provider/src/android/provider/cts/ContactsContract_ContactsTest.java b/tests/tests/provider/src/android/provider/cts/ContactsContract_ContactsTest.java
new file mode 100644
index 0000000..b9be9cf
--- /dev/null
+++ b/tests/tests/provider/src/android/provider/cts/ContactsContract_ContactsTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.provider.cts;
+
+import dalvik.annotation.TestLevel;
+import dalvik.annotation.TestTargetClass;
+import dalvik.annotation.TestTargetNew;
+
+import android.content.ContentResolver;
+import android.content.IContentProvider;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.Contacts;
+import android.provider.cts.ContactsContract_TestDataBuilder.TestContact;
+import android.provider.cts.ContactsContract_TestDataBuilder.TestRawContact;
+import android.test.InstrumentationTestCase;
+
+@TestTargetClass(ContactsContract.Contacts.class)
+public class ContactsContract_ContactsTest extends InstrumentationTestCase {
+    private ContentResolver mContentResolver;
+    private ContactsContract_TestDataBuilder mBuilder;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mContentResolver = getInstrumentation().getTargetContext().getContentResolver();
+        IContentProvider provider = mContentResolver.acquireProvider(ContactsContract.AUTHORITY);
+        mBuilder = new ContactsContract_TestDataBuilder(provider);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        mBuilder.cleanup();
+    }
+
+    @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            notes = "Test markAsContacted(ContentResolver resolver, long contactId)",
+            method = "markAsContacted",
+            args = {android.content.ContentResolver.class, long.class}
+    )
+    public void testMarkAsContacted() throws Exception {
+        TestRawContact rawContact = mBuilder.newRawContact().insert().load();
+        TestContact contact = rawContact.getContact().load();
+        long oldLastContacted = contact.getLong(Contacts.LAST_TIME_CONTACTED);
+
+        Contacts.markAsContacted(mContentResolver, contact.getId());
+        contact.load(); // Reload
+
+        long lastContacted = contact.getLong(Contacts.LAST_TIME_CONTACTED);
+        assertTrue(oldLastContacted < lastContacted);
+        oldLastContacted = lastContacted;
+
+        Contacts.markAsContacted(mContentResolver, contact.getId());
+        contact.load();
+
+        lastContacted = contact.getLong(Contacts.LAST_TIME_CONTACTED);
+        assertTrue(oldLastContacted < lastContacted);
+    }
+}
+
diff --git a/tests/tests/provider/src/android/provider/cts/ContactsContract_GroupMembershipTest.java b/tests/tests/provider/src/android/provider/cts/ContactsContract_GroupMembershipTest.java
new file mode 100644
index 0000000..86e40a9
--- /dev/null
+++ b/tests/tests/provider/src/android/provider/cts/ContactsContract_GroupMembershipTest.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.provider.cts;
+
+import dalvik.annotation.TestLevel;
+import dalvik.annotation.TestTargetClass;
+import dalvik.annotation.TestTargetNew;
+import dalvik.annotation.TestTargets;
+
+import android.content.ContentResolver;
+import android.content.IContentProvider;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.Groups;
+import android.provider.ContactsContract.RawContacts;
+import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
+import android.provider.cts.ContactsContract_TestDataBuilder.TestData;
+import android.provider.cts.ContactsContract_TestDataBuilder.TestGroup;
+import android.provider.cts.ContactsContract_TestDataBuilder.TestRawContact;
+import android.test.InstrumentationTestCase;
+
+@TestTargetClass(GroupMembership.class)
+public class ContactsContract_GroupMembershipTest extends InstrumentationTestCase {
+    private ContactsContract_TestDataBuilder mBuilder;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        ContentResolver contentResolver =
+                getInstrumentation().getTargetContext().getContentResolver();
+        IContentProvider provider = contentResolver.acquireProvider(ContactsContract.AUTHORITY);
+        mBuilder = new ContactsContract_TestDataBuilder(provider);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        mBuilder.cleanup();
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.PARTIAL_COMPLETE,
+            notes = "Tests INSERT operation for group membership using group row ID"
+        )
+    })
+    public void testAddGroupMembershipWithGroupRowId() throws Exception {
+        TestRawContact rawContact = mBuilder.newRawContact().insert();
+        TestGroup group = mBuilder.newGroup().insert();
+        TestData groupMembership = rawContact.newDataRow(GroupMembership.CONTENT_ITEM_TYPE)
+                .with(GroupMembership.GROUP_ROW_ID, group.getId())
+                .insert();
+
+        groupMembership.load();
+        groupMembership.assertColumn(GroupMembership.RAW_CONTACT_ID, rawContact.getId());
+        groupMembership.assertColumn(GroupMembership.GROUP_ROW_ID, group.getId());
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.PARTIAL_COMPLETE,
+            notes = "Tests INSERT operation for group membership using group source ID"
+        )
+    })
+    public void testAddGroupMembershipWithGroupSourceId() throws Exception {
+        TestRawContact rawContact = mBuilder.newRawContact()
+                .with(RawContacts.ACCOUNT_TYPE, "test_type")
+                .with(RawContacts.ACCOUNT_NAME, "test_name")
+                .insert();
+        TestGroup group = mBuilder.newGroup()
+                .with(Groups.SOURCE_ID, "test_source_id")
+                .with(Groups.ACCOUNT_TYPE, "test_type")
+                .with(Groups.ACCOUNT_NAME, "test_name")
+                .insert();
+        TestData groupMembership = rawContact.newDataRow(GroupMembership.CONTENT_ITEM_TYPE)
+                .with(GroupMembership.GROUP_SOURCE_ID, "test_source_id")
+                .insert();
+
+        groupMembership.load();
+        groupMembership.assertColumn(GroupMembership.RAW_CONTACT_ID, rawContact.getId());
+        groupMembership.assertColumn(GroupMembership.GROUP_SOURCE_ID, "test_source_id");
+        groupMembership.assertColumn(GroupMembership.GROUP_ROW_ID, group.getId());
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.PARTIAL_COMPLETE,
+            notes = "Tests INSERT operation for group membership using an unknown group source ID"
+        )
+    })
+    public void testAddGroupMembershipWithUnknownGroupSourceId() throws Exception {
+        TestRawContact rawContact = mBuilder.newRawContact()
+                .with(RawContacts.ACCOUNT_TYPE, "test_type")
+                .with(RawContacts.ACCOUNT_NAME, "test_name")
+                .insert();
+        TestData groupMembership = rawContact.newDataRow(GroupMembership.CONTENT_ITEM_TYPE)
+                .with(GroupMembership.GROUP_SOURCE_ID, "test_source_id")
+                .insert();
+        TestGroup group = mBuilder.newGroup()
+                .with(Groups.ACCOUNT_TYPE, "test_type")
+                .with(Groups.ACCOUNT_NAME, "test_name")
+                .with(Groups.SOURCE_ID, "test_source_id")
+                .with(Groups.DELETED, 0)
+                .loadUsingValues();
+
+        groupMembership.load();
+        groupMembership.assertColumn(GroupMembership.RAW_CONTACT_ID, rawContact.getId());
+        groupMembership.assertColumn(GroupMembership.GROUP_SOURCE_ID, "test_source_id");
+        groupMembership.assertColumn(GroupMembership.GROUP_ROW_ID, group.getId());
+
+        group.deletePermanently();
+    }
+}
+
diff --git a/tests/tests/provider/src/android/provider/cts/ContactsContract_RawContactsTest.java b/tests/tests/provider/src/android/provider/cts/ContactsContract_RawContactsTest.java
new file mode 100644
index 0000000..c22f00f
--- /dev/null
+++ b/tests/tests/provider/src/android/provider/cts/ContactsContract_RawContactsTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.provider.cts;
+
+import dalvik.annotation.TestLevel;
+import dalvik.annotation.TestTargetClass;
+import dalvik.annotation.TestTargetNew;
+
+import android.content.ContentResolver;
+import android.content.IContentProvider;
+import android.net.Uri;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.RawContacts;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.provider.cts.ContactsContract_TestDataBuilder.TestContact;
+import android.provider.cts.ContactsContract_TestDataBuilder.TestRawContact;
+import android.test.InstrumentationTestCase;
+
+@TestTargetClass(ContactsContract.RawContacts.class)
+public class ContactsContract_RawContactsTest extends InstrumentationTestCase {
+    private ContentResolver mResolver;
+    private ContactsContract_TestDataBuilder mBuilder;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mResolver = getInstrumentation().getTargetContext().getContentResolver();
+        IContentProvider provider = mResolver.acquireProvider(ContactsContract.AUTHORITY);
+        mBuilder = new ContactsContract_TestDataBuilder(provider);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        mBuilder.cleanup();
+    }
+
+    @TestTargetNew(
+            level = TestLevel.PARTIAL_COMPLETE,
+            notes = "Test getContactLookupUri(ContentResolver resolver, Uri rawContactUri) " +
+                    "using source id",
+            method = "getContactLookupUri",
+            args = {android.content.ContentResolver.class, Uri.class}
+    )
+    public void testGetLookupUriBySourceId() throws Exception {
+        TestRawContact rawContact = mBuilder.newRawContact()
+                .with(RawContacts.ACCOUNT_TYPE, "test_type")
+                .with(RawContacts.ACCOUNT_NAME, "test_name")
+                .with(RawContacts.SOURCE_ID, "source_id")
+                .insert();
+
+        // TODO remove this. The method under test is currently broken: it will not
+        // work without at least one data row in the raw contact.
+        rawContact.newDataRow(StructuredName.CONTENT_ITEM_TYPE)
+                .with(StructuredName.DISPLAY_NAME, "test name")
+                .insert();
+
+        Uri lookupUri = RawContacts.getContactLookupUri(mResolver, rawContact.getUri());
+        assertNotNull("Could not produce a lookup URI", lookupUri);
+
+        TestContact lookupContact = mBuilder.newContact().setUri(lookupUri).load();
+        assertEquals("Lookup URI matched the wrong contact",
+                lookupContact.getId(), rawContact.load().getContactId());
+    }
+
+    @TestTargetNew(
+            level = TestLevel.PARTIAL_COMPLETE,
+            notes = "Test getContactLookupUri(ContentResolver resolver, Uri rawContactUri) " +
+                    "using display name",
+            method = "getContactLookupUri",
+            args = {android.content.ContentResolver.class, Uri.class}
+    )
+    public void testGetLookupUriByDisplayName() throws Exception {
+        TestRawContact rawContact = mBuilder.newRawContact()
+                .with(RawContacts.ACCOUNT_TYPE, "test_type")
+                .with(RawContacts.ACCOUNT_NAME, "test_name")
+                .insert();
+        rawContact.newDataRow(StructuredName.CONTENT_ITEM_TYPE)
+                .with(StructuredName.DISPLAY_NAME, "test name")
+                .insert();
+
+        Uri lookupUri = RawContacts.getContactLookupUri(mResolver, rawContact.getUri());
+        assertNotNull("Could not produce a lookup URI", lookupUri);
+
+        TestContact lookupContact = mBuilder.newContact().setUri(lookupUri).load();
+        assertEquals("Lookup URI matched the wrong contact",
+                lookupContact.getId(), rawContact.load().getContactId());
+    }
+}
+
diff --git a/tests/tests/provider/src/android/provider/cts/ContactsContract_TestDataBuilder.java b/tests/tests/provider/src/android/provider/cts/ContactsContract_TestDataBuilder.java
new file mode 100644
index 0000000..ab59082
--- /dev/null
+++ b/tests/tests/provider/src/android/provider/cts/ContactsContract_TestDataBuilder.java
@@ -0,0 +1,323 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.provider.cts;
+
+import com.google.android.collect.Lists;
+import com.google.android.collect.Sets;
+
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.IContentProvider;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.BaseColumns;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.Groups;
+import android.provider.ContactsContract.RawContacts;
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import junit.framework.Assert;
+
+/**
+ * A test data builder for ContactsContract tests.
+ */
+public class ContactsContract_TestDataBuilder {
+    private IContentProvider mProvider;
+    private ArrayList<Builder<?>> mCreatedRows = Lists.newArrayList();
+    private HashSet<Builder<?>> mLoadedRows = Sets.newHashSet();
+
+    private interface IdQuery {
+        String[] COLUMNS = new String[] {
+            BaseColumns._ID,
+        };
+
+        int _ID = 0;
+    }
+
+    public abstract class  Builder<T extends Builder<?>> extends Assert {
+        protected ContentValues mValues = new ContentValues();
+        private Uri mUri;
+        private long mId = -1;
+        private Cursor mCursor;
+
+        protected abstract Uri getContentUri();
+
+        public long getId() throws Exception {
+            if (mId != -1) {
+                return mId;
+            }
+
+            assertNotNull("Row has not be inserted or loaded yet", mUri);
+
+            Cursor cursor = mProvider.query(mUri, IdQuery.COLUMNS, null, null, null);
+            if (cursor != null) {
+                try {
+                    cursor.moveToFirst();
+                    mId = cursor.getInt(IdQuery._ID);
+                } finally {
+                    cursor.close();
+                }
+
+            }
+            assertTrue("Could not obtain _ID for URI: " + mUri, mId != -1);
+            return mId;
+        }
+
+        @SuppressWarnings("unchecked")
+        public T setUri(Uri uri) {
+            mUri = uri;
+            return (T)this;
+        }
+
+        public Uri getUri() {
+            if (mUri == null) {
+                assertTrue("Neither URI nor ID has been specified", mId != -1);
+                mUri = ContentUris.withAppendedId(getContentUri(), mId);
+            }
+            return mUri;
+        }
+
+        @SuppressWarnings("unchecked")
+        public T with(String key, String value) {
+            mValues.put(key, value);
+            return (T)this;
+        }
+
+        @SuppressWarnings("unchecked")
+        public T with(String key, long value) {
+            mValues.put(key, value);
+            return (T)this;
+        }
+
+        @SuppressWarnings("unchecked")
+        public T insert() throws Exception {
+            insertDependent();
+            mCreatedRows.add(this);
+            return (T)this;
+        }
+
+        @SuppressWarnings("unchecked")
+        public T insertDependent() throws Exception {
+            mUri = mProvider.insert(getContentUri(), mValues);
+            assertNotNull("Could not insert a row in " + getContentUri(), mUri);
+            mId = ContentUris.parseId(mUri);
+            return (T)this;
+        }
+
+        public void delete() throws Exception {
+            int count = mProvider.delete(getUri(), null, null);
+            assertEquals("Wrong number of rows deleted for " + getUri(), 1, count);
+        }
+
+        public void deletePermanently() throws Exception {
+            Uri uri = getUri().buildUpon()
+                    .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
+                    .build();
+            int count = mProvider.delete(uri, null, null);
+            assertEquals("Wrong number of rows deleted for " + uri, 1, count);
+            mCreatedRows.remove(this);
+        }
+
+        @SuppressWarnings("unchecked")
+        public T load() throws Exception {
+            close();
+            mLoadedRows.add(this);
+
+            mCursor = mProvider.query(getUri(), null, null, null, null);
+            if (mCursor == null || !mCursor.moveToFirst()) {
+                fail("No data rows for URI: " + getUri());
+            }
+            return (T)this;
+        }
+
+        @SuppressWarnings("unchecked")
+        public T loadUsingValues() throws Exception {
+            close();
+            mLoadedRows.add(this);
+
+            StringBuilder selection = new StringBuilder();
+            ArrayList<String> selectionArgs = Lists.newArrayList();
+            Set<Map.Entry<String, Object>> entries = mValues.valueSet();
+            for (Map.Entry<String, Object> entry : entries) {
+                String column = entry.getKey();
+                Object value = entry.getValue();
+
+                if (selection.length() != 0) {
+                    selection.append(" AND ");
+                }
+
+                selection.append(column);
+                if (value == null) {
+                    selection.append(" NOT NULL");
+                } else {
+                    selection.append("=?");
+                    selectionArgs.add(value.toString());
+                }
+            }
+            mCursor = mProvider.query(getContentUri(), null,
+                    selection.toString(),
+                    selectionArgs.toArray(new String[0]), null);
+            if (mCursor == null || !mCursor.moveToFirst()) {
+                fail("No data rows for " + getContentUri() + "[" + mValues.toString() + "]");
+            }
+            mId = mCursor.getLong(getColumnIndex(BaseColumns._ID));
+            return (T)this;
+        }
+
+        public long getLong(String columnName) {
+            return mCursor.getLong(mCursor.getColumnIndex(columnName));
+        }
+
+        public void assertColumn(String columnName, long value) {
+            assertEquals(value, mCursor.getLong(getColumnIndex(columnName)));
+        }
+
+        public void assertColumn(String columnName, String value) {
+            assertEquals(value, mCursor.getString(getColumnIndex(columnName)));
+        }
+
+        private int getColumnIndex(String columnName) {
+            int index = mCursor.getColumnIndex(columnName);
+            assertTrue("No such column: " + columnName +
+                    ". Available columns: " + TextUtils.join(", ", mCursor.getColumnNames()),
+                    index != -1);
+            return index;
+        }
+
+        public void close() {
+            if (mCursor != null) {
+                mCursor.close();
+                mCursor = null;
+            }
+            mLoadedRows.remove(this);
+        }
+
+    }
+
+    public class TestRawContact extends Builder<TestRawContact> {
+
+        private TestContact mContact;
+
+        @Override
+        protected Uri getContentUri() {
+            return RawContacts.CONTENT_URI;
+        }
+
+        public TestData newDataRow(String mimeType) {
+            return new TestData(this, mimeType);
+        }
+
+        public long getContactId() {
+            return getLong(RawContacts.CONTACT_ID);
+        }
+
+        public TestContact getContact() throws Exception {
+            if (mContact == null) {
+                mContact = new NestedTestContact(this);
+            }
+            return mContact;
+        }
+    }
+
+    public class TestContact extends Builder<TestContact> {
+
+        @Override
+        protected Uri getContentUri() {
+            return Contacts.CONTENT_URI;
+        }
+    }
+
+    private class NestedTestContact extends TestContact {
+        private final TestRawContact mRawContact;
+
+        public NestedTestContact(TestRawContact rawContact) {
+            mRawContact = rawContact;
+        }
+
+        @Override
+        public long getId() throws Exception {
+            return mRawContact.getContactId();
+        }
+
+        @Override
+        public TestContact load() throws Exception {
+            return with(Contacts._ID, mRawContact.getContactId()).loadUsingValues();
+        }
+    }
+
+    public class TestGroup extends Builder<TestGroup> {
+
+        @Override
+        protected Uri getContentUri() {
+            return Groups.CONTENT_URI;
+        }
+    }
+
+    public class TestData extends Builder<TestData> {
+        private final TestRawContact mRawContact;
+
+        public TestData(TestRawContact rawContact, String mimeType) {
+            this.mRawContact = rawContact;
+            mValues.put(Data.MIMETYPE, mimeType);
+        }
+
+        @Override
+        protected Uri getContentUri() {
+            return Data.CONTENT_URI;
+        }
+
+        @Override
+        public TestData insert() throws Exception {
+            mValues.put(Data.RAW_CONTACT_ID, mRawContact.getId());
+            return super.insertDependent();
+        }
+    }
+
+    public ContactsContract_TestDataBuilder(IContentProvider provider) {
+        this.mProvider = provider;
+    }
+
+    public TestRawContact newRawContact() {
+        return new TestRawContact();
+    }
+
+    public TestContact newContact() {
+        return new TestContact();
+    }
+
+    public TestGroup newGroup() {
+        return new TestGroup();
+    }
+
+    public void cleanup() throws Exception {
+        for (Builder<?> builder : new ArrayList<Builder<?>>(mCreatedRows)) {
+            builder.deletePermanently();
+        }
+
+        for (Builder<?> builder : new HashSet<Builder<?>>(mLoadedRows)) {
+            builder.close();
+        }
+    }
+
+}