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