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 com.android.contacts.model.EntityDelta;
     20 import com.android.contacts.model.EntityDelta.ValuesDelta;
     21 import com.google.android.collect.Lists;
     22 
     23 import static android.content.ContentProviderOperation.TYPE_INSERT;
     24 import static android.content.ContentProviderOperation.TYPE_UPDATE;
     25 import static android.content.ContentProviderOperation.TYPE_DELETE;
     26 import static android.content.ContentProviderOperation.TYPE_ASSERT;
     27 
     28 import android.content.ContentProviderOperation;
     29 import android.content.ContentValues;
     30 import android.content.Entity;
     31 import android.content.ContentProviderOperation.Builder;
     32 import android.os.Parcel;
     33 import android.provider.ContactsContract.Data;
     34 import android.provider.ContactsContract.RawContacts;
     35 import android.provider.ContactsContract.CommonDataKinds.Phone;
     36 import android.test.AndroidTestCase;
     37 import android.test.suitebuilder.annotation.LargeTest;
     38 
     39 import java.util.ArrayList;
     40 
     41 /**
     42  * Tests for {@link EntityDelta} and {@link ValuesDelta}. These tests
     43  * focus on passing changes across {@link Parcel}, and verifying that they
     44  * correctly build expected "diff" operations.
     45  */
     46 @LargeTest
     47 public class EntityDeltaTests extends AndroidTestCase {
     48     public static final String TAG = "EntityDeltaTests";
     49 
     50     public static final long TEST_CONTACT_ID = 12;
     51     public static final long TEST_PHONE_ID = 24;
     52 
     53     public static final String TEST_PHONE_NUMBER_1 = "218-555-1111";
     54     public static final String TEST_PHONE_NUMBER_2 = "218-555-2222";
     55 
     56     public static final String TEST_ACCOUNT_NAME = "TEST";
     57 
     58     public EntityDeltaTests() {
     59         super();
     60     }
     61 
     62     @Override
     63     public void setUp() {
     64         mContext = getContext();
     65     }
     66 
     67     public static Entity getEntity(long contactId, long phoneId) {
     68         // Build an existing contact read from database
     69         final ContentValues contact = new ContentValues();
     70         contact.put(RawContacts.VERSION, 43);
     71         contact.put(RawContacts._ID, contactId);
     72 
     73         final ContentValues phone = new ContentValues();
     74         phone.put(Data._ID, phoneId);
     75         phone.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
     76         phone.put(Phone.NUMBER, TEST_PHONE_NUMBER_1);
     77         phone.put(Phone.TYPE, Phone.TYPE_HOME);
     78 
     79         final Entity before = new Entity(contact);
     80         before.addSubValue(Data.CONTENT_URI, phone);
     81         return before;
     82     }
     83 
     84     /**
     85      * Test that {@link EntityDelta#mergeAfter(EntityDelta)} correctly passes
     86      * any changes through the {@link Parcel} object. This enforces that
     87      * {@link EntityDelta} should be identical when serialized against the same
     88      * "before" {@link Entity}.
     89      */
     90     public void testParcelChangesNone() {
     91         final Entity before = getEntity(TEST_CONTACT_ID, TEST_PHONE_ID);
     92         final EntityDelta source = EntityDelta.fromBefore(before);
     93         final EntityDelta dest = EntityDelta.fromBefore(before);
     94 
     95         // Merge modified values and assert they match
     96         final EntityDelta merged = EntityDelta.mergeAfter(dest, source);
     97         assertEquals("Unexpected change when merging", source, merged);
     98     }
     99 
    100     public void testParcelChangesInsert() {
    101         final Entity before = getEntity(TEST_CONTACT_ID, TEST_PHONE_ID);
    102         final EntityDelta source = EntityDelta.fromBefore(before);
    103         final EntityDelta dest = EntityDelta.fromBefore(before);
    104 
    105         // Add a new row and pass across parcel, should be same
    106         final ContentValues phone = new ContentValues();
    107         phone.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
    108         phone.put(Phone.NUMBER, TEST_PHONE_NUMBER_2);
    109         phone.put(Phone.TYPE, Phone.TYPE_WORK);
    110         source.addEntry(ValuesDelta.fromAfter(phone));
    111 
    112         // Merge modified values and assert they match
    113         final EntityDelta merged = EntityDelta.mergeAfter(dest, source);
    114         assertEquals("Unexpected change when merging", source, merged);
    115     }
    116 
    117     public void testParcelChangesUpdate() {
    118         // Update existing row and pass across parcel, should be same
    119         final Entity before = getEntity(TEST_CONTACT_ID, TEST_PHONE_ID);
    120         final EntityDelta source = EntityDelta.fromBefore(before);
    121         final EntityDelta dest = EntityDelta.fromBefore(before);
    122 
    123         final ValuesDelta child = source.getEntry(TEST_PHONE_ID);
    124         child.put(Phone.NUMBER, TEST_PHONE_NUMBER_2);
    125 
    126         // Merge modified values and assert they match
    127         final EntityDelta merged = EntityDelta.mergeAfter(dest, source);
    128         assertEquals("Unexpected change when merging", source, merged);
    129     }
    130 
    131     public void testParcelChangesDelete() {
    132         // Delete a row and pass across parcel, should be same
    133         final Entity before = getEntity(TEST_CONTACT_ID, TEST_PHONE_ID);
    134         final EntityDelta source = EntityDelta.fromBefore(before);
    135         final EntityDelta dest = EntityDelta.fromBefore(before);
    136 
    137         final ValuesDelta child = source.getEntry(TEST_PHONE_ID);
    138         child.markDeleted();
    139 
    140         // Merge modified values and assert they match
    141         final EntityDelta merged = EntityDelta.mergeAfter(dest, source);
    142         assertEquals("Unexpected change when merging", source, merged);
    143     }
    144 
    145     /**
    146      * Test that {@link ValuesDelta#buildDiff(android.net.Uri)} is correctly
    147      * built for insert, update, and delete cases. Note this only tests behavior
    148      * for individual {@link Data} rows.
    149      */
    150     public void testValuesDiffNone() {
    151         final ContentValues before = new ContentValues();
    152         before.put(Data._ID, TEST_PHONE_ID);
    153         before.put(Phone.NUMBER, TEST_PHONE_NUMBER_1);
    154 
    155         final ValuesDelta values = ValuesDelta.fromBefore(before);
    156 
    157         // None action shouldn't produce a builder
    158         final Builder builder = values.buildDiff(Data.CONTENT_URI);
    159         assertNull("None action produced a builder", builder);
    160     }
    161 
    162     public void testValuesDiffInsert() {
    163         final ContentValues after = new ContentValues();
    164         after.put(Phone.NUMBER, TEST_PHONE_NUMBER_2);
    165 
    166         final ValuesDelta values = ValuesDelta.fromAfter(after);
    167 
    168         // Should produce an insert action
    169         final Builder builder = values.buildDiff(Data.CONTENT_URI);
    170         final int type = builder.build().getType();
    171         assertEquals("Didn't produce insert action", TYPE_INSERT, type);
    172     }
    173 
    174     public void testValuesDiffUpdate() {
    175         final ContentValues before = new ContentValues();
    176         before.put(Data._ID, TEST_PHONE_ID);
    177         before.put(Phone.NUMBER, TEST_PHONE_NUMBER_1);
    178 
    179         final ValuesDelta values = ValuesDelta.fromBefore(before);
    180         values.put(Phone.NUMBER, TEST_PHONE_NUMBER_2);
    181 
    182         // Should produce an update action
    183         final Builder builder = values.buildDiff(Data.CONTENT_URI);
    184         final int type = builder.build().getType();
    185         assertEquals("Didn't produce update action", TYPE_UPDATE, type);
    186     }
    187 
    188     public void testValuesDiffDelete() {
    189         final ContentValues before = new ContentValues();
    190         before.put(Data._ID, TEST_PHONE_ID);
    191         before.put(Phone.NUMBER, TEST_PHONE_NUMBER_1);
    192 
    193         final ValuesDelta values = ValuesDelta.fromBefore(before);
    194         values.markDeleted();
    195 
    196         // Should produce a delete action
    197         final Builder builder = values.buildDiff(Data.CONTENT_URI);
    198         final int type = builder.build().getType();
    199         assertEquals("Didn't produce delete action", TYPE_DELETE, type);
    200     }
    201 
    202     /**
    203      * Test that {@link EntityDelta#buildDiff(ArrayList)} is correctly built for
    204      * insert, update, and delete cases. This only tests a subset of possible
    205      * {@link Data} row changes.
    206      */
    207     public void testEntityDiffNone() {
    208         final Entity before = getEntity(TEST_CONTACT_ID, TEST_PHONE_ID);
    209         final EntityDelta source = EntityDelta.fromBefore(before);
    210 
    211         // Assert that writing unchanged produces few operations
    212         final ArrayList<ContentProviderOperation> diff = Lists.newArrayList();
    213         source.buildDiff(diff);
    214 
    215         assertTrue("Created changes when none needed", (diff.size() == 0));
    216     }
    217 
    218     public void testEntityDiffNoneInsert() {
    219         final Entity before = getEntity(TEST_CONTACT_ID, TEST_PHONE_ID);
    220         final EntityDelta source = EntityDelta.fromBefore(before);
    221 
    222         // Insert a new phone number
    223         final ContentValues phone = new ContentValues();
    224         phone.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
    225         phone.put(Phone.NUMBER, TEST_PHONE_NUMBER_2);
    226         phone.put(Phone.TYPE, Phone.TYPE_WORK);
    227         source.addEntry(ValuesDelta.fromAfter(phone));
    228 
    229         // Assert two operations: insert Data row and enforce version
    230         final ArrayList<ContentProviderOperation> diff = Lists.newArrayList();
    231         source.buildAssert(diff);
    232         source.buildDiff(diff);
    233         assertEquals("Unexpected operations", 4, diff.size());
    234         {
    235             final ContentProviderOperation oper = diff.get(0);
    236             assertEquals("Expected version enforcement", TYPE_ASSERT, oper.getType());
    237         }
    238         {
    239             final ContentProviderOperation oper = diff.get(1);
    240             assertEquals("Expected aggregation mode change", TYPE_UPDATE, oper.getType());
    241             assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri());
    242         }
    243         {
    244             final ContentProviderOperation oper = diff.get(2);
    245             assertEquals("Incorrect type", TYPE_INSERT, oper.getType());
    246             assertEquals("Incorrect target", Data.CONTENT_URI, oper.getUri());
    247         }
    248         {
    249             final ContentProviderOperation oper = diff.get(3);
    250             assertEquals("Expected aggregation mode change", TYPE_UPDATE, oper.getType());
    251             assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri());
    252         }
    253     }
    254 
    255     public void testEntityDiffUpdateInsert() {
    256         final Entity before = getEntity(TEST_CONTACT_ID, TEST_PHONE_ID);
    257         final EntityDelta source = EntityDelta.fromBefore(before);
    258 
    259         // Update parent contact values
    260         source.getValues().put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
    261 
    262         // Insert a new phone number
    263         final ContentValues phone = new ContentValues();
    264         phone.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
    265         phone.put(Phone.NUMBER, TEST_PHONE_NUMBER_2);
    266         phone.put(Phone.TYPE, Phone.TYPE_WORK);
    267         source.addEntry(ValuesDelta.fromAfter(phone));
    268 
    269         // Assert three operations: update Contact, insert Data row, enforce version
    270         final ArrayList<ContentProviderOperation> diff = Lists.newArrayList();
    271         source.buildAssert(diff);
    272         source.buildDiff(diff);
    273         assertEquals("Unexpected operations", 5, diff.size());
    274         {
    275             final ContentProviderOperation oper = diff.get(0);
    276             assertEquals("Expected version enforcement", TYPE_ASSERT, oper.getType());
    277         }
    278         {
    279             final ContentProviderOperation oper = diff.get(1);
    280             assertEquals("Expected aggregation mode change", TYPE_UPDATE, oper.getType());
    281             assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri());
    282         }
    283         {
    284             final ContentProviderOperation oper = diff.get(2);
    285             assertEquals("Incorrect type", TYPE_UPDATE, oper.getType());
    286             assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri());
    287         }
    288         {
    289             final ContentProviderOperation oper = diff.get(3);
    290             assertEquals("Incorrect type", TYPE_INSERT, oper.getType());
    291             assertEquals("Incorrect target", Data.CONTENT_URI, oper.getUri());
    292         }
    293         {
    294             final ContentProviderOperation oper = diff.get(4);
    295             assertEquals("Expected aggregation mode change", TYPE_UPDATE, oper.getType());
    296             assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri());
    297         }
    298     }
    299 
    300     public void testEntityDiffNoneUpdate() {
    301         final Entity before = getEntity(TEST_CONTACT_ID, TEST_PHONE_ID);
    302         final EntityDelta source = EntityDelta.fromBefore(before);
    303 
    304         // Update existing phone number
    305         final ValuesDelta child = source.getEntry(TEST_PHONE_ID);
    306         child.put(Phone.NUMBER, TEST_PHONE_NUMBER_2);
    307 
    308         // Assert that version is enforced
    309         final ArrayList<ContentProviderOperation> diff = Lists.newArrayList();
    310         source.buildAssert(diff);
    311         source.buildDiff(diff);
    312         assertEquals("Unexpected operations", 4, diff.size());
    313         {
    314             final ContentProviderOperation oper = diff.get(0);
    315             assertEquals("Expected version enforcement", TYPE_ASSERT, oper.getType());
    316         }
    317         {
    318             final ContentProviderOperation oper = diff.get(1);
    319             assertEquals("Expected aggregation mode change", TYPE_UPDATE, oper.getType());
    320             assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri());
    321         }
    322         {
    323             final ContentProviderOperation oper = diff.get(2);
    324             assertEquals("Incorrect type", TYPE_UPDATE, oper.getType());
    325             assertEquals("Incorrect target", Data.CONTENT_URI, oper.getUri());
    326         }
    327         {
    328             final ContentProviderOperation oper = diff.get(3);
    329             assertEquals("Expected aggregation mode change", TYPE_UPDATE, oper.getType());
    330             assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri());
    331         }
    332     }
    333 
    334     public void testEntityDiffDelete() {
    335         final Entity before = getEntity(TEST_CONTACT_ID, TEST_PHONE_ID);
    336         final EntityDelta source = EntityDelta.fromBefore(before);
    337 
    338         // Delete entire entity
    339         source.getValues().markDeleted();
    340 
    341         // Assert two operations: delete Contact and enforce version
    342         final ArrayList<ContentProviderOperation> diff = Lists.newArrayList();
    343         source.buildAssert(diff);
    344         source.buildDiff(diff);
    345         assertEquals("Unexpected operations", 2, diff.size());
    346         {
    347             final ContentProviderOperation oper = diff.get(0);
    348             assertEquals("Expected version enforcement", TYPE_ASSERT, oper.getType());
    349         }
    350         {
    351             final ContentProviderOperation oper = diff.get(1);
    352             assertEquals("Incorrect type", TYPE_DELETE, oper.getType());
    353             assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri());
    354         }
    355     }
    356 
    357     public void testEntityDiffInsert() {
    358         // Insert a RawContact
    359         final ContentValues after = new ContentValues();
    360         after.put(RawContacts.ACCOUNT_NAME, TEST_ACCOUNT_NAME);
    361         after.put(RawContacts.SEND_TO_VOICEMAIL, 1);
    362 
    363         final ValuesDelta values = ValuesDelta.fromAfter(after);
    364         final EntityDelta source = new EntityDelta(values);
    365 
    366         // Assert two operations: delete Contact and enforce version
    367         final ArrayList<ContentProviderOperation> diff = Lists.newArrayList();
    368         source.buildAssert(diff);
    369         source.buildDiff(diff);
    370         assertEquals("Unexpected operations", 2, diff.size());
    371         {
    372             final ContentProviderOperation oper = diff.get(0);
    373             assertEquals("Incorrect type", TYPE_INSERT, oper.getType());
    374             assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri());
    375         }
    376     }
    377 
    378     public void testEntityDiffInsertInsert() {
    379         // Insert a RawContact
    380         final ContentValues after = new ContentValues();
    381         after.put(RawContacts.ACCOUNT_NAME, TEST_ACCOUNT_NAME);
    382         after.put(RawContacts.SEND_TO_VOICEMAIL, 1);
    383 
    384         final ValuesDelta values = ValuesDelta.fromAfter(after);
    385         final EntityDelta source = new EntityDelta(values);
    386 
    387         // Insert a new phone number
    388         final ContentValues phone = new ContentValues();
    389         phone.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
    390         phone.put(Phone.NUMBER, TEST_PHONE_NUMBER_2);
    391         phone.put(Phone.TYPE, Phone.TYPE_WORK);
    392         source.addEntry(ValuesDelta.fromAfter(phone));
    393 
    394         // Assert two operations: delete Contact and enforce version
    395         final ArrayList<ContentProviderOperation> diff = Lists.newArrayList();
    396         source.buildAssert(diff);
    397         source.buildDiff(diff);
    398         assertEquals("Unexpected operations", 3, diff.size());
    399         {
    400             final ContentProviderOperation oper = diff.get(0);
    401             assertEquals("Incorrect type", TYPE_INSERT, oper.getType());
    402             assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri());
    403         }
    404         {
    405             final ContentProviderOperation oper = diff.get(1);
    406             assertEquals("Incorrect type", TYPE_INSERT, oper.getType());
    407             assertEquals("Incorrect target", Data.CONTENT_URI, oper.getUri());
    408 
    409         }
    410     }
    411 }
    412