Home | History | Annotate | Download | only in contacts
      1 /*
      2  * Copyright (C) 2009 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.contacts;
     18 
     19 import static android.content.ContentProviderOperation.TYPE_ASSERT;
     20 import static android.content.ContentProviderOperation.TYPE_DELETE;
     21 import static android.content.ContentProviderOperation.TYPE_INSERT;
     22 import static android.content.ContentProviderOperation.TYPE_UPDATE;
     23 
     24 import android.content.ContentProviderOperation;
     25 import android.content.ContentProviderOperation.Builder;
     26 import android.content.ContentValues;
     27 import android.content.Context;
     28 import android.os.Parcel;
     29 import android.provider.ContactsContract.CommonDataKinds.Phone;
     30 import android.provider.ContactsContract.Data;
     31 import android.provider.ContactsContract.RawContacts;
     32 import android.test.AndroidTestCase;
     33 import android.test.suitebuilder.annotation.LargeTest;
     34 
     35 import com.android.contacts.model.RawContact;
     36 import com.android.contacts.model.RawContactDelta;
     37 import com.android.contacts.common.model.ValuesDelta;
     38 import com.google.common.collect.Lists;
     39 
     40 import java.util.ArrayList;
     41 
     42 /**
     43  * Tests for {@link RawContactDelta} and {@link ValuesDelta}. These tests
     44  * focus on passing changes across {@link Parcel}, and verifying that they
     45  * correctly build expected "diff" operations.
     46  */
     47 @LargeTest
     48 public class RawContactDeltaTests extends AndroidTestCase {
     49     public static final String TAG = "EntityDeltaTests";
     50 
     51     public static final long TEST_CONTACT_ID = 12;
     52     public static final long TEST_PHONE_ID = 24;
     53 
     54     public static final String TEST_PHONE_NUMBER_1 = "218-555-1111";
     55     public static final String TEST_PHONE_NUMBER_2 = "218-555-2222";
     56 
     57     public static final String TEST_ACCOUNT_NAME = "TEST";
     58 
     59     public RawContactDeltaTests() {
     60         super();
     61     }
     62 
     63     @Override
     64     public void setUp() {
     65         mContext = getContext();
     66     }
     67 
     68     public static RawContact getRawContact(Context context, long contactId, long phoneId) {
     69         // Build an existing contact read from database
     70         final ContentValues contact = new ContentValues();
     71         contact.put(RawContacts.VERSION, 43);
     72         contact.put(RawContacts._ID, contactId);
     73 
     74         final ContentValues phone = new ContentValues();
     75         phone.put(Data._ID, phoneId);
     76         phone.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
     77         phone.put(Phone.NUMBER, TEST_PHONE_NUMBER_1);
     78         phone.put(Phone.TYPE, Phone.TYPE_HOME);
     79 
     80         final RawContact before = new RawContact(contact);
     81         before.addDataItemValues(phone);
     82         return before;
     83     }
     84 
     85     /**
     86      * Test that {@link RawContactDelta#mergeAfter(RawContactDelta)} correctly passes
     87      * any changes through the {@link Parcel} object. This enforces that
     88      * {@link RawContactDelta} should be identical when serialized against the same
     89      * "before" {@link RawContact}.
     90      */
     91     public void testParcelChangesNone() {
     92         final RawContact before = getRawContact(mContext, TEST_CONTACT_ID, TEST_PHONE_ID);
     93         final RawContactDelta source = RawContactDelta.fromBefore(before);
     94         final RawContactDelta dest = RawContactDelta.fromBefore(before);
     95 
     96         // Merge modified values and assert they match
     97         final RawContactDelta merged = RawContactDelta.mergeAfter(dest, source);
     98         assertEquals("Unexpected change when merging", source, merged);
     99     }
    100 
    101     public void testParcelChangesInsert() {
    102         final RawContact before = getRawContact(mContext, TEST_CONTACT_ID, TEST_PHONE_ID);
    103         final RawContactDelta source = RawContactDelta.fromBefore(before);
    104         final RawContactDelta dest = RawContactDelta.fromBefore(before);
    105 
    106         // Add a new row and pass across parcel, should be same
    107         final ContentValues phone = new ContentValues();
    108         phone.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
    109         phone.put(Phone.NUMBER, TEST_PHONE_NUMBER_2);
    110         phone.put(Phone.TYPE, Phone.TYPE_WORK);
    111         source.addEntry(ValuesDelta.fromAfter(phone));
    112 
    113         // Merge modified values and assert they match
    114         final RawContactDelta merged = RawContactDelta.mergeAfter(dest, source);
    115         assertEquals("Unexpected change when merging", source, merged);
    116     }
    117 
    118     public void testParcelChangesUpdate() {
    119         // Update existing row and pass across parcel, should be same
    120         final RawContact before = getRawContact(mContext, TEST_CONTACT_ID, TEST_PHONE_ID);
    121         final RawContactDelta source = RawContactDelta.fromBefore(before);
    122         final RawContactDelta dest = RawContactDelta.fromBefore(before);
    123 
    124         final ValuesDelta child = source.getEntry(TEST_PHONE_ID);
    125         child.put(Phone.NUMBER, TEST_PHONE_NUMBER_2);
    126 
    127         // Merge modified values and assert they match
    128         final RawContactDelta merged = RawContactDelta.mergeAfter(dest, source);
    129         assertEquals("Unexpected change when merging", source, merged);
    130     }
    131 
    132     public void testParcelChangesDelete() {
    133         // Delete a row and pass across parcel, should be same
    134         final RawContact before = getRawContact(mContext, TEST_CONTACT_ID, TEST_PHONE_ID);
    135         final RawContactDelta source = RawContactDelta.fromBefore(before);
    136         final RawContactDelta dest = RawContactDelta.fromBefore(before);
    137 
    138         final ValuesDelta child = source.getEntry(TEST_PHONE_ID);
    139         child.markDeleted();
    140 
    141         // Merge modified values and assert they match
    142         final RawContactDelta merged = RawContactDelta.mergeAfter(dest, source);
    143         assertEquals("Unexpected change when merging", source, merged);
    144     }
    145 
    146     public void testValuesDiffDelete() {
    147         final ContentValues before = new ContentValues();
    148         before.put(Data._ID, TEST_PHONE_ID);
    149         before.put(Phone.NUMBER, TEST_PHONE_NUMBER_1);
    150 
    151         final ValuesDelta values = ValuesDelta.fromBefore(before);
    152         values.markDeleted();
    153 
    154         // Should produce a delete action
    155         final Builder builder = values.buildDiff(Data.CONTENT_URI);
    156         final int type = builder.build().getType();
    157         assertEquals("Didn't produce delete action", TYPE_DELETE, type);
    158     }
    159 
    160     /**
    161      * Test that {@link RawContactDelta#buildDiff(ArrayList)} is correctly built for
    162      * insert, update, and delete cases. This only tests a subset of possible
    163      * {@link Data} row changes.
    164      */
    165     public void testEntityDiffNone() {
    166         final RawContact before = getRawContact(mContext, TEST_CONTACT_ID, TEST_PHONE_ID);
    167         final RawContactDelta source = RawContactDelta.fromBefore(before);
    168 
    169         // Assert that writing unchanged produces few operations
    170         final ArrayList<ContentProviderOperation> diff = Lists.newArrayList();
    171         source.buildDiff(diff);
    172 
    173         assertTrue("Created changes when none needed", (diff.size() == 0));
    174     }
    175 
    176     public void testEntityDiffNoneInsert() {
    177         final RawContact before = getRawContact(mContext, TEST_CONTACT_ID, TEST_PHONE_ID);
    178         final RawContactDelta source = RawContactDelta.fromBefore(before);
    179 
    180         // Insert a new phone number
    181         final ContentValues phone = new ContentValues();
    182         phone.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
    183         phone.put(Phone.NUMBER, TEST_PHONE_NUMBER_2);
    184         phone.put(Phone.TYPE, Phone.TYPE_WORK);
    185         source.addEntry(ValuesDelta.fromAfter(phone));
    186 
    187         // Assert two operations: insert Data row and enforce version
    188         final ArrayList<ContentProviderOperation> diff = Lists.newArrayList();
    189         source.buildAssert(diff);
    190         source.buildDiff(diff);
    191         assertEquals("Unexpected operations", 4, diff.size());
    192         {
    193             final ContentProviderOperation oper = diff.get(0);
    194             assertEquals("Expected version enforcement", TYPE_ASSERT, oper.getType());
    195         }
    196         {
    197             final ContentProviderOperation oper = diff.get(1);
    198             assertEquals("Expected aggregation mode change", TYPE_UPDATE, oper.getType());
    199             assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri());
    200         }
    201         {
    202             final ContentProviderOperation oper = diff.get(2);
    203             assertEquals("Incorrect type", TYPE_INSERT, oper.getType());
    204             assertEquals("Incorrect target", Data.CONTENT_URI, oper.getUri());
    205         }
    206         {
    207             final ContentProviderOperation oper = diff.get(3);
    208             assertEquals("Expected aggregation mode change", TYPE_UPDATE, oper.getType());
    209             assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri());
    210         }
    211     }
    212 
    213     public void testEntityDiffUpdateInsert() {
    214         final RawContact before = getRawContact(mContext, TEST_CONTACT_ID, TEST_PHONE_ID);
    215         final RawContactDelta source = RawContactDelta.fromBefore(before);
    216 
    217         // Update parent contact values
    218         source.getValues().put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
    219 
    220         // Insert a new phone number
    221         final ContentValues phone = new ContentValues();
    222         phone.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
    223         phone.put(Phone.NUMBER, TEST_PHONE_NUMBER_2);
    224         phone.put(Phone.TYPE, Phone.TYPE_WORK);
    225         source.addEntry(ValuesDelta.fromAfter(phone));
    226 
    227         // Assert three operations: update Contact, insert Data row, enforce version
    228         final ArrayList<ContentProviderOperation> diff = Lists.newArrayList();
    229         source.buildAssert(diff);
    230         source.buildDiff(diff);
    231         assertEquals("Unexpected operations", 5, diff.size());
    232         {
    233             final ContentProviderOperation oper = diff.get(0);
    234             assertEquals("Expected version enforcement", TYPE_ASSERT, oper.getType());
    235         }
    236         {
    237             final ContentProviderOperation oper = diff.get(1);
    238             assertEquals("Expected aggregation mode change", TYPE_UPDATE, oper.getType());
    239             assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri());
    240         }
    241         {
    242             final ContentProviderOperation oper = diff.get(2);
    243             assertEquals("Incorrect type", TYPE_UPDATE, oper.getType());
    244             assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri());
    245         }
    246         {
    247             final ContentProviderOperation oper = diff.get(3);
    248             assertEquals("Incorrect type", TYPE_INSERT, oper.getType());
    249             assertEquals("Incorrect target", Data.CONTENT_URI, oper.getUri());
    250         }
    251         {
    252             final ContentProviderOperation oper = diff.get(4);
    253             assertEquals("Expected aggregation mode change", TYPE_UPDATE, oper.getType());
    254             assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri());
    255         }
    256     }
    257 
    258     public void testEntityDiffNoneUpdate() {
    259         final RawContact before = getRawContact(mContext, TEST_CONTACT_ID, TEST_PHONE_ID);
    260         final RawContactDelta source = RawContactDelta.fromBefore(before);
    261 
    262         // Update existing phone number
    263         final ValuesDelta child = source.getEntry(TEST_PHONE_ID);
    264         child.put(Phone.NUMBER, TEST_PHONE_NUMBER_2);
    265 
    266         // Assert that version is enforced
    267         final ArrayList<ContentProviderOperation> diff = Lists.newArrayList();
    268         source.buildAssert(diff);
    269         source.buildDiff(diff);
    270         assertEquals("Unexpected operations", 4, diff.size());
    271         {
    272             final ContentProviderOperation oper = diff.get(0);
    273             assertEquals("Expected version enforcement", TYPE_ASSERT, oper.getType());
    274         }
    275         {
    276             final ContentProviderOperation oper = diff.get(1);
    277             assertEquals("Expected aggregation mode change", TYPE_UPDATE, oper.getType());
    278             assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri());
    279         }
    280         {
    281             final ContentProviderOperation oper = diff.get(2);
    282             assertEquals("Incorrect type", TYPE_UPDATE, oper.getType());
    283             assertEquals("Incorrect target", Data.CONTENT_URI, oper.getUri());
    284         }
    285         {
    286             final ContentProviderOperation oper = diff.get(3);
    287             assertEquals("Expected aggregation mode change", TYPE_UPDATE, oper.getType());
    288             assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri());
    289         }
    290     }
    291 
    292     public void testEntityDiffDelete() {
    293         final RawContact before = getRawContact(mContext, TEST_CONTACT_ID, TEST_PHONE_ID);
    294         final RawContactDelta source = RawContactDelta.fromBefore(before);
    295 
    296         // Delete entire entity
    297         source.getValues().markDeleted();
    298 
    299         // Assert two operations: delete Contact and enforce version
    300         final ArrayList<ContentProviderOperation> diff = Lists.newArrayList();
    301         source.buildAssert(diff);
    302         source.buildDiff(diff);
    303         assertEquals("Unexpected operations", 2, diff.size());
    304         {
    305             final ContentProviderOperation oper = diff.get(0);
    306             assertEquals("Expected version enforcement", TYPE_ASSERT, oper.getType());
    307         }
    308         {
    309             final ContentProviderOperation oper = diff.get(1);
    310             assertEquals("Incorrect type", TYPE_DELETE, oper.getType());
    311             assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri());
    312         }
    313     }
    314 
    315     public void testEntityDiffInsert() {
    316         // Insert a RawContact
    317         final ContentValues after = new ContentValues();
    318         after.put(RawContacts.ACCOUNT_NAME, TEST_ACCOUNT_NAME);
    319         after.put(RawContacts.SEND_TO_VOICEMAIL, 1);
    320 
    321         final ValuesDelta values = ValuesDelta.fromAfter(after);
    322         final RawContactDelta source = new RawContactDelta(values);
    323 
    324         // Assert two operations: delete Contact and enforce version
    325         final ArrayList<ContentProviderOperation> diff = Lists.newArrayList();
    326         source.buildAssert(diff);
    327         source.buildDiff(diff);
    328         assertEquals("Unexpected operations", 2, diff.size());
    329         {
    330             final ContentProviderOperation oper = diff.get(0);
    331             assertEquals("Incorrect type", TYPE_INSERT, oper.getType());
    332             assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri());
    333         }
    334     }
    335 
    336     public void testEntityDiffInsertInsert() {
    337         // Insert a RawContact
    338         final ContentValues after = new ContentValues();
    339         after.put(RawContacts.ACCOUNT_NAME, TEST_ACCOUNT_NAME);
    340         after.put(RawContacts.SEND_TO_VOICEMAIL, 1);
    341 
    342         final ValuesDelta values = ValuesDelta.fromAfter(after);
    343         final RawContactDelta source = new RawContactDelta(values);
    344 
    345         // Insert a new phone number
    346         final ContentValues phone = new ContentValues();
    347         phone.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
    348         phone.put(Phone.NUMBER, TEST_PHONE_NUMBER_2);
    349         phone.put(Phone.TYPE, Phone.TYPE_WORK);
    350         source.addEntry(ValuesDelta.fromAfter(phone));
    351 
    352         // Assert two operations: delete Contact and enforce version
    353         final ArrayList<ContentProviderOperation> diff = Lists.newArrayList();
    354         source.buildAssert(diff);
    355         source.buildDiff(diff);
    356         assertEquals("Unexpected operations", 3, diff.size());
    357         {
    358             final ContentProviderOperation oper = diff.get(0);
    359             assertEquals("Incorrect type", TYPE_INSERT, oper.getType());
    360             assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri());
    361         }
    362         {
    363             final ContentProviderOperation oper = diff.get(1);
    364             assertEquals("Incorrect type", TYPE_INSERT, oper.getType());
    365             assertEquals("Incorrect target", Data.CONTENT_URI, oper.getUri());
    366 
    367         }
    368     }
    369 }
    370