blob: fa716c77aacf4ae7fa83e76480a09152089c7228 [file] [log] [blame]
/*
* Copyright (C) 2009 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 com.android.contacts;
import com.android.contacts.model.EntityDelta;
import com.android.contacts.model.EntityDelta.ValuesDelta;
import com.google.android.collect.Lists;
import static android.content.ContentProviderOperation.TYPE_INSERT;
import static android.content.ContentProviderOperation.TYPE_UPDATE;
import static android.content.ContentProviderOperation.TYPE_DELETE;
import static android.content.ContentProviderOperation.TYPE_ASSERT;
import android.content.ContentProviderOperation;
import android.content.ContentValues;
import android.content.Entity;
import android.content.ContentProviderOperation.Builder;
import android.os.Parcel;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.RawContacts;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.LargeTest;
import java.util.ArrayList;
/**
* Tests for {@link EntityDelta} and {@link ValuesDelta}. These tests
* focus on passing changes across {@link Parcel}, and verifying that they
* correctly build expected "diff" operations.
*/
@LargeTest
public class EntityDeltaTests extends AndroidTestCase {
public static final String TAG = "EntityDeltaTests";
public static final long TEST_CONTACT_ID = 12;
public static final long TEST_PHONE_ID = 24;
public static final String TEST_PHONE_NUMBER_1 = "218-555-1111";
public static final String TEST_PHONE_NUMBER_2 = "218-555-2222";
public static final String TEST_ACCOUNT_NAME = "TEST";
public EntityDeltaTests() {
super();
}
@Override
public void setUp() {
mContext = getContext();
}
public static Entity getEntity(long contactId, long phoneId) {
// Build an existing contact read from database
final ContentValues contact = new ContentValues();
contact.put(RawContacts.VERSION, 43);
contact.put(RawContacts._ID, contactId);
final ContentValues phone = new ContentValues();
phone.put(Data._ID, phoneId);
phone.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
phone.put(Phone.NUMBER, TEST_PHONE_NUMBER_1);
phone.put(Phone.TYPE, Phone.TYPE_HOME);
final Entity before = new Entity(contact);
before.addSubValue(Data.CONTENT_URI, phone);
return before;
}
/**
* Test that {@link EntityDelta#mergeAfter(EntityDelta)} correctly passes
* any changes through the {@link Parcel} object. This enforces that
* {@link EntityDelta} should be identical when serialized against the same
* "before" {@link Entity}.
*/
public void testParcelChangesNone() {
final Entity before = getEntity(TEST_CONTACT_ID, TEST_PHONE_ID);
final EntityDelta source = EntityDelta.fromBefore(before);
final EntityDelta dest = EntityDelta.fromBefore(before);
// Merge modified values and assert they match
final EntityDelta merged = EntityDelta.mergeAfter(dest, source);
assertEquals("Unexpected change when merging", source, merged);
}
public void testParcelChangesInsert() {
final Entity before = getEntity(TEST_CONTACT_ID, TEST_PHONE_ID);
final EntityDelta source = EntityDelta.fromBefore(before);
final EntityDelta dest = EntityDelta.fromBefore(before);
// Add a new row and pass across parcel, should be same
final ContentValues phone = new ContentValues();
phone.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
phone.put(Phone.NUMBER, TEST_PHONE_NUMBER_2);
phone.put(Phone.TYPE, Phone.TYPE_WORK);
source.addEntry(ValuesDelta.fromAfter(phone));
// Merge modified values and assert they match
final EntityDelta merged = EntityDelta.mergeAfter(dest, source);
assertEquals("Unexpected change when merging", source, merged);
}
public void testParcelChangesUpdate() {
// Update existing row and pass across parcel, should be same
final Entity before = getEntity(TEST_CONTACT_ID, TEST_PHONE_ID);
final EntityDelta source = EntityDelta.fromBefore(before);
final EntityDelta dest = EntityDelta.fromBefore(before);
final ValuesDelta child = source.getEntry(TEST_PHONE_ID);
child.put(Phone.NUMBER, TEST_PHONE_NUMBER_2);
// Merge modified values and assert they match
final EntityDelta merged = EntityDelta.mergeAfter(dest, source);
assertEquals("Unexpected change when merging", source, merged);
}
public void testParcelChangesDelete() {
// Delete a row and pass across parcel, should be same
final Entity before = getEntity(TEST_CONTACT_ID, TEST_PHONE_ID);
final EntityDelta source = EntityDelta.fromBefore(before);
final EntityDelta dest = EntityDelta.fromBefore(before);
final ValuesDelta child = source.getEntry(TEST_PHONE_ID);
child.markDeleted();
// Merge modified values and assert they match
final EntityDelta merged = EntityDelta.mergeAfter(dest, source);
assertEquals("Unexpected change when merging", source, merged);
}
/**
* Test that {@link ValuesDelta#buildDiff(android.net.Uri)} is correctly
* built for insert, update, and delete cases. Note this only tests behavior
* for individual {@link Data} rows.
*/
public void testValuesDiffNone() {
final ContentValues before = new ContentValues();
before.put(Data._ID, TEST_PHONE_ID);
before.put(Phone.NUMBER, TEST_PHONE_NUMBER_1);
final ValuesDelta values = ValuesDelta.fromBefore(before);
// None action shouldn't produce a builder
final Builder builder = values.buildDiff(Data.CONTENT_URI);
assertNull("None action produced a builder", builder);
}
public void testValuesDiffInsert() {
final ContentValues after = new ContentValues();
after.put(Phone.NUMBER, TEST_PHONE_NUMBER_2);
final ValuesDelta values = ValuesDelta.fromAfter(after);
// Should produce an insert action
final Builder builder = values.buildDiff(Data.CONTENT_URI);
final int type = builder.build().getType();
assertEquals("Didn't produce insert action", TYPE_INSERT, type);
}
public void testValuesDiffUpdate() {
final ContentValues before = new ContentValues();
before.put(Data._ID, TEST_PHONE_ID);
before.put(Phone.NUMBER, TEST_PHONE_NUMBER_1);
final ValuesDelta values = ValuesDelta.fromBefore(before);
values.put(Phone.NUMBER, TEST_PHONE_NUMBER_2);
// Should produce an update action
final Builder builder = values.buildDiff(Data.CONTENT_URI);
final int type = builder.build().getType();
assertEquals("Didn't produce update action", TYPE_UPDATE, type);
}
public void testValuesDiffDelete() {
final ContentValues before = new ContentValues();
before.put(Data._ID, TEST_PHONE_ID);
before.put(Phone.NUMBER, TEST_PHONE_NUMBER_1);
final ValuesDelta values = ValuesDelta.fromBefore(before);
values.markDeleted();
// Should produce a delete action
final Builder builder = values.buildDiff(Data.CONTENT_URI);
final int type = builder.build().getType();
assertEquals("Didn't produce delete action", TYPE_DELETE, type);
}
/**
* Test that {@link EntityDelta#buildDiff(ArrayList)} is correctly built for
* insert, update, and delete cases. This only tests a subset of possible
* {@link Data} row changes.
*/
public void testEntityDiffNone() {
final Entity before = getEntity(TEST_CONTACT_ID, TEST_PHONE_ID);
final EntityDelta source = EntityDelta.fromBefore(before);
// Assert that writing unchanged produces few operations
final ArrayList<ContentProviderOperation> diff = Lists.newArrayList();
source.buildDiff(diff);
assertTrue("Created changes when none needed", (diff.size() == 0));
}
public void testEntityDiffNoneInsert() {
final Entity before = getEntity(TEST_CONTACT_ID, TEST_PHONE_ID);
final EntityDelta source = EntityDelta.fromBefore(before);
// Insert a new phone number
final ContentValues phone = new ContentValues();
phone.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
phone.put(Phone.NUMBER, TEST_PHONE_NUMBER_2);
phone.put(Phone.TYPE, Phone.TYPE_WORK);
source.addEntry(ValuesDelta.fromAfter(phone));
// Assert two operations: insert Data row and enforce version
final ArrayList<ContentProviderOperation> diff = Lists.newArrayList();
source.buildAssert(diff);
source.buildDiff(diff);
assertEquals("Unexpected operations", 4, diff.size());
{
final ContentProviderOperation oper = diff.get(0);
assertEquals("Expected version enforcement", TYPE_ASSERT, oper.getType());
}
{
final ContentProviderOperation oper = diff.get(1);
assertEquals("Expected aggregation mode change", TYPE_UPDATE, oper.getType());
assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri());
}
{
final ContentProviderOperation oper = diff.get(2);
assertEquals("Incorrect type", TYPE_INSERT, oper.getType());
assertEquals("Incorrect target", Data.CONTENT_URI, oper.getUri());
}
{
final ContentProviderOperation oper = diff.get(3);
assertEquals("Expected aggregation mode change", TYPE_UPDATE, oper.getType());
assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri());
}
}
public void testEntityDiffUpdateInsert() {
final Entity before = getEntity(TEST_CONTACT_ID, TEST_PHONE_ID);
final EntityDelta source = EntityDelta.fromBefore(before);
// Update parent contact values
source.getValues().put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
// Insert a new phone number
final ContentValues phone = new ContentValues();
phone.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
phone.put(Phone.NUMBER, TEST_PHONE_NUMBER_2);
phone.put(Phone.TYPE, Phone.TYPE_WORK);
source.addEntry(ValuesDelta.fromAfter(phone));
// Assert three operations: update Contact, insert Data row, enforce version
final ArrayList<ContentProviderOperation> diff = Lists.newArrayList();
source.buildAssert(diff);
source.buildDiff(diff);
assertEquals("Unexpected operations", 5, diff.size());
{
final ContentProviderOperation oper = diff.get(0);
assertEquals("Expected version enforcement", TYPE_ASSERT, oper.getType());
}
{
final ContentProviderOperation oper = diff.get(1);
assertEquals("Expected aggregation mode change", TYPE_UPDATE, oper.getType());
assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri());
}
{
final ContentProviderOperation oper = diff.get(2);
assertEquals("Incorrect type", TYPE_UPDATE, oper.getType());
assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri());
}
{
final ContentProviderOperation oper = diff.get(3);
assertEquals("Incorrect type", TYPE_INSERT, oper.getType());
assertEquals("Incorrect target", Data.CONTENT_URI, oper.getUri());
}
{
final ContentProviderOperation oper = diff.get(4);
assertEquals("Expected aggregation mode change", TYPE_UPDATE, oper.getType());
assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri());
}
}
public void testEntityDiffNoneUpdate() {
final Entity before = getEntity(TEST_CONTACT_ID, TEST_PHONE_ID);
final EntityDelta source = EntityDelta.fromBefore(before);
// Update existing phone number
final ValuesDelta child = source.getEntry(TEST_PHONE_ID);
child.put(Phone.NUMBER, TEST_PHONE_NUMBER_2);
// Assert that version is enforced
final ArrayList<ContentProviderOperation> diff = Lists.newArrayList();
source.buildAssert(diff);
source.buildDiff(diff);
assertEquals("Unexpected operations", 4, diff.size());
{
final ContentProviderOperation oper = diff.get(0);
assertEquals("Expected version enforcement", TYPE_ASSERT, oper.getType());
}
{
final ContentProviderOperation oper = diff.get(1);
assertEquals("Expected aggregation mode change", TYPE_UPDATE, oper.getType());
assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri());
}
{
final ContentProviderOperation oper = diff.get(2);
assertEquals("Incorrect type", TYPE_UPDATE, oper.getType());
assertEquals("Incorrect target", Data.CONTENT_URI, oper.getUri());
}
{
final ContentProviderOperation oper = diff.get(3);
assertEquals("Expected aggregation mode change", TYPE_UPDATE, oper.getType());
assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri());
}
}
public void testEntityDiffDelete() {
final Entity before = getEntity(TEST_CONTACT_ID, TEST_PHONE_ID);
final EntityDelta source = EntityDelta.fromBefore(before);
// Delete entire entity
source.getValues().markDeleted();
// Assert two operations: delete Contact and enforce version
final ArrayList<ContentProviderOperation> diff = Lists.newArrayList();
source.buildAssert(diff);
source.buildDiff(diff);
assertEquals("Unexpected operations", 2, diff.size());
{
final ContentProviderOperation oper = diff.get(0);
assertEquals("Expected version enforcement", TYPE_ASSERT, oper.getType());
}
{
final ContentProviderOperation oper = diff.get(1);
assertEquals("Incorrect type", TYPE_DELETE, oper.getType());
assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri());
}
}
public void testEntityDiffInsert() {
// Insert a RawContact
final ContentValues after = new ContentValues();
after.put(RawContacts.ACCOUNT_NAME, TEST_ACCOUNT_NAME);
after.put(RawContacts.SEND_TO_VOICEMAIL, 1);
final ValuesDelta values = ValuesDelta.fromAfter(after);
final EntityDelta source = new EntityDelta(values);
// Assert two operations: delete Contact and enforce version
final ArrayList<ContentProviderOperation> diff = Lists.newArrayList();
source.buildAssert(diff);
source.buildDiff(diff);
assertEquals("Unexpected operations", 2, diff.size());
{
final ContentProviderOperation oper = diff.get(0);
assertEquals("Incorrect type", TYPE_INSERT, oper.getType());
assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri());
}
}
public void testEntityDiffInsertInsert() {
// Insert a RawContact
final ContentValues after = new ContentValues();
after.put(RawContacts.ACCOUNT_NAME, TEST_ACCOUNT_NAME);
after.put(RawContacts.SEND_TO_VOICEMAIL, 1);
final ValuesDelta values = ValuesDelta.fromAfter(after);
final EntityDelta source = new EntityDelta(values);
// Insert a new phone number
final ContentValues phone = new ContentValues();
phone.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
phone.put(Phone.NUMBER, TEST_PHONE_NUMBER_2);
phone.put(Phone.TYPE, Phone.TYPE_WORK);
source.addEntry(ValuesDelta.fromAfter(phone));
// Assert two operations: delete Contact and enforce version
final ArrayList<ContentProviderOperation> diff = Lists.newArrayList();
source.buildAssert(diff);
source.buildDiff(diff);
assertEquals("Unexpected operations", 3, diff.size());
{
final ContentProviderOperation oper = diff.get(0);
assertEquals("Incorrect type", TYPE_INSERT, oper.getType());
assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri());
}
{
final ContentProviderOperation oper = diff.get(1);
assertEquals("Incorrect type", TYPE_INSERT, oper.getType());
assertEquals("Incorrect target", Data.CONTENT_URI, oper.getUri());
}
}
}