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