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 17package com.android.contacts; 18 19import static android.content.ContentProviderOperation.TYPE_ASSERT; 20import static android.content.ContentProviderOperation.TYPE_DELETE; 21import static android.content.ContentProviderOperation.TYPE_INSERT; 22import static android.content.ContentProviderOperation.TYPE_UPDATE; 23 24import android.content.ContentProviderOperation; 25import android.content.ContentProviderOperation.Builder; 26import android.content.ContentValues; 27import android.content.Context; 28import android.os.Parcel; 29import android.provider.ContactsContract.CommonDataKinds.Phone; 30import android.provider.ContactsContract.Data; 31import android.provider.ContactsContract.RawContacts; 32import android.test.AndroidTestCase; 33import android.test.suitebuilder.annotation.LargeTest; 34 35import com.android.contacts.model.RawContact; 36import com.android.contacts.model.RawContactDelta; 37import com.android.contacts.common.model.ValuesDelta; 38import com.google.common.collect.Lists; 39 40import 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 48public 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