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.model.RawContactDelta.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 RawContactDeltaTests()59 public RawContactDeltaTests() { 60 super(); 61 } 62 63 @Override setUp()64 public void setUp() { 65 mContext = getContext(); 66 } 67 getRawContact(Context context, long contactId, long phoneId)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(context, 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 */ testParcelChangesNone()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 testParcelChangesInsert()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 testParcelChangesUpdate()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 testParcelChangesDelete()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 /** 147 * Test that {@link ValuesDelta#buildDiff(android.net.Uri)} is correctly 148 * built for insert, update, and delete cases. Note this only tests behavior 149 * for individual {@link Data} rows. 150 */ testValuesDiffNone()151 public void testValuesDiffNone() { 152 final ContentValues before = new ContentValues(); 153 before.put(Data._ID, TEST_PHONE_ID); 154 before.put(Phone.NUMBER, TEST_PHONE_NUMBER_1); 155 156 final ValuesDelta values = ValuesDelta.fromBefore(before); 157 158 // None action shouldn't produce a builder 159 final Builder builder = values.buildDiff(Data.CONTENT_URI); 160 assertNull("None action produced a builder", builder); 161 } 162 testValuesDiffInsert()163 public void testValuesDiffInsert() { 164 final ContentValues after = new ContentValues(); 165 after.put(Phone.NUMBER, TEST_PHONE_NUMBER_2); 166 167 final ValuesDelta values = ValuesDelta.fromAfter(after); 168 169 // Should produce an insert action 170 final Builder builder = values.buildDiff(Data.CONTENT_URI); 171 final int type = builder.build().getType(); 172 assertEquals("Didn't produce insert action", TYPE_INSERT, type); 173 } 174 testValuesDiffUpdate()175 public void testValuesDiffUpdate() { 176 final ContentValues before = new ContentValues(); 177 before.put(Data._ID, TEST_PHONE_ID); 178 before.put(Phone.NUMBER, TEST_PHONE_NUMBER_1); 179 180 final ValuesDelta values = ValuesDelta.fromBefore(before); 181 values.put(Phone.NUMBER, TEST_PHONE_NUMBER_2); 182 183 // Should produce an update action 184 final Builder builder = values.buildDiff(Data.CONTENT_URI); 185 final int type = builder.build().getType(); 186 assertEquals("Didn't produce update action", TYPE_UPDATE, type); 187 } 188 testValuesDiffDelete()189 public void testValuesDiffDelete() { 190 final ContentValues before = new ContentValues(); 191 before.put(Data._ID, TEST_PHONE_ID); 192 before.put(Phone.NUMBER, TEST_PHONE_NUMBER_1); 193 194 final ValuesDelta values = ValuesDelta.fromBefore(before); 195 values.markDeleted(); 196 197 // Should produce a delete action 198 final Builder builder = values.buildDiff(Data.CONTENT_URI); 199 final int type = builder.build().getType(); 200 assertEquals("Didn't produce delete action", TYPE_DELETE, type); 201 } 202 203 /** 204 * Test that {@link RawContactDelta#buildDiff(ArrayList)} is correctly built for 205 * insert, update, and delete cases. This only tests a subset of possible 206 * {@link Data} row changes. 207 */ testEntityDiffNone()208 public void testEntityDiffNone() { 209 final RawContact before = getRawContact(mContext, TEST_CONTACT_ID, TEST_PHONE_ID); 210 final RawContactDelta source = RawContactDelta.fromBefore(before); 211 212 // Assert that writing unchanged produces few operations 213 final ArrayList<ContentProviderOperation> diff = Lists.newArrayList(); 214 source.buildDiff(diff); 215 216 assertTrue("Created changes when none needed", (diff.size() == 0)); 217 } 218 testEntityDiffNoneInsert()219 public void testEntityDiffNoneInsert() { 220 final RawContact before = getRawContact(mContext, TEST_CONTACT_ID, TEST_PHONE_ID); 221 final RawContactDelta source = RawContactDelta.fromBefore(before); 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 two operations: insert Data row and enforce version 231 final ArrayList<ContentProviderOperation> diff = Lists.newArrayList(); 232 source.buildAssert(diff); 233 source.buildDiff(diff); 234 assertEquals("Unexpected operations", 4, diff.size()); 235 { 236 final ContentProviderOperation oper = diff.get(0); 237 assertEquals("Expected version enforcement", TYPE_ASSERT, oper.getType()); 238 } 239 { 240 final ContentProviderOperation oper = diff.get(1); 241 assertEquals("Expected aggregation mode change", TYPE_UPDATE, oper.getType()); 242 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri()); 243 } 244 { 245 final ContentProviderOperation oper = diff.get(2); 246 assertEquals("Incorrect type", TYPE_INSERT, oper.getType()); 247 assertEquals("Incorrect target", Data.CONTENT_URI, oper.getUri()); 248 } 249 { 250 final ContentProviderOperation oper = diff.get(3); 251 assertEquals("Expected aggregation mode change", TYPE_UPDATE, oper.getType()); 252 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri()); 253 } 254 } 255 testEntityDiffUpdateInsert()256 public void testEntityDiffUpdateInsert() { 257 final RawContact before = getRawContact(mContext, TEST_CONTACT_ID, TEST_PHONE_ID); 258 final RawContactDelta source = RawContactDelta.fromBefore(before); 259 260 // Update parent contact values 261 source.getValues().put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED); 262 263 // Insert a new phone number 264 final ContentValues phone = new ContentValues(); 265 phone.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 266 phone.put(Phone.NUMBER, TEST_PHONE_NUMBER_2); 267 phone.put(Phone.TYPE, Phone.TYPE_WORK); 268 source.addEntry(ValuesDelta.fromAfter(phone)); 269 270 // Assert three operations: update Contact, insert Data row, enforce version 271 final ArrayList<ContentProviderOperation> diff = Lists.newArrayList(); 272 source.buildAssert(diff); 273 source.buildDiff(diff); 274 assertEquals("Unexpected operations", 5, diff.size()); 275 { 276 final ContentProviderOperation oper = diff.get(0); 277 assertEquals("Expected version enforcement", TYPE_ASSERT, oper.getType()); 278 } 279 { 280 final ContentProviderOperation oper = diff.get(1); 281 assertEquals("Expected aggregation mode change", TYPE_UPDATE, oper.getType()); 282 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri()); 283 } 284 { 285 final ContentProviderOperation oper = diff.get(2); 286 assertEquals("Incorrect type", TYPE_UPDATE, oper.getType()); 287 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri()); 288 } 289 { 290 final ContentProviderOperation oper = diff.get(3); 291 assertEquals("Incorrect type", TYPE_INSERT, oper.getType()); 292 assertEquals("Incorrect target", Data.CONTENT_URI, oper.getUri()); 293 } 294 { 295 final ContentProviderOperation oper = diff.get(4); 296 assertEquals("Expected aggregation mode change", TYPE_UPDATE, oper.getType()); 297 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri()); 298 } 299 } 300 testEntityDiffNoneUpdate()301 public void testEntityDiffNoneUpdate() { 302 final RawContact before = getRawContact(mContext, TEST_CONTACT_ID, TEST_PHONE_ID); 303 final RawContactDelta source = RawContactDelta.fromBefore(before); 304 305 // Update existing phone number 306 final ValuesDelta child = source.getEntry(TEST_PHONE_ID); 307 child.put(Phone.NUMBER, TEST_PHONE_NUMBER_2); 308 309 // Assert that version is enforced 310 final ArrayList<ContentProviderOperation> diff = Lists.newArrayList(); 311 source.buildAssert(diff); 312 source.buildDiff(diff); 313 assertEquals("Unexpected operations", 4, diff.size()); 314 { 315 final ContentProviderOperation oper = diff.get(0); 316 assertEquals("Expected version enforcement", TYPE_ASSERT, oper.getType()); 317 } 318 { 319 final ContentProviderOperation oper = diff.get(1); 320 assertEquals("Expected aggregation mode change", TYPE_UPDATE, oper.getType()); 321 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri()); 322 } 323 { 324 final ContentProviderOperation oper = diff.get(2); 325 assertEquals("Incorrect type", TYPE_UPDATE, oper.getType()); 326 assertEquals("Incorrect target", Data.CONTENT_URI, oper.getUri()); 327 } 328 { 329 final ContentProviderOperation oper = diff.get(3); 330 assertEquals("Expected aggregation mode change", TYPE_UPDATE, oper.getType()); 331 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri()); 332 } 333 } 334 testEntityDiffDelete()335 public void testEntityDiffDelete() { 336 final RawContact before = getRawContact(mContext, TEST_CONTACT_ID, TEST_PHONE_ID); 337 final RawContactDelta source = RawContactDelta.fromBefore(before); 338 339 // Delete entire entity 340 source.getValues().markDeleted(); 341 342 // Assert two operations: delete Contact and enforce version 343 final ArrayList<ContentProviderOperation> diff = Lists.newArrayList(); 344 source.buildAssert(diff); 345 source.buildDiff(diff); 346 assertEquals("Unexpected operations", 2, diff.size()); 347 { 348 final ContentProviderOperation oper = diff.get(0); 349 assertEquals("Expected version enforcement", TYPE_ASSERT, oper.getType()); 350 } 351 { 352 final ContentProviderOperation oper = diff.get(1); 353 assertEquals("Incorrect type", TYPE_DELETE, oper.getType()); 354 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri()); 355 } 356 } 357 testEntityDiffInsert()358 public void testEntityDiffInsert() { 359 // Insert a RawContact 360 final ContentValues after = new ContentValues(); 361 after.put(RawContacts.ACCOUNT_NAME, TEST_ACCOUNT_NAME); 362 after.put(RawContacts.SEND_TO_VOICEMAIL, 1); 363 364 final ValuesDelta values = ValuesDelta.fromAfter(after); 365 final RawContactDelta source = new RawContactDelta(values); 366 367 // Assert two operations: delete Contact and enforce version 368 final ArrayList<ContentProviderOperation> diff = Lists.newArrayList(); 369 source.buildAssert(diff); 370 source.buildDiff(diff); 371 assertEquals("Unexpected operations", 2, diff.size()); 372 { 373 final ContentProviderOperation oper = diff.get(0); 374 assertEquals("Incorrect type", TYPE_INSERT, oper.getType()); 375 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri()); 376 } 377 } 378 testEntityDiffInsertInsert()379 public void testEntityDiffInsertInsert() { 380 // Insert a RawContact 381 final ContentValues after = new ContentValues(); 382 after.put(RawContacts.ACCOUNT_NAME, TEST_ACCOUNT_NAME); 383 after.put(RawContacts.SEND_TO_VOICEMAIL, 1); 384 385 final ValuesDelta values = ValuesDelta.fromAfter(after); 386 final RawContactDelta source = new RawContactDelta(values); 387 388 // Insert a new phone number 389 final ContentValues phone = new ContentValues(); 390 phone.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 391 phone.put(Phone.NUMBER, TEST_PHONE_NUMBER_2); 392 phone.put(Phone.TYPE, Phone.TYPE_WORK); 393 source.addEntry(ValuesDelta.fromAfter(phone)); 394 395 // Assert two operations: delete Contact and enforce version 396 final ArrayList<ContentProviderOperation> diff = Lists.newArrayList(); 397 source.buildAssert(diff); 398 source.buildDiff(diff); 399 assertEquals("Unexpected operations", 3, diff.size()); 400 { 401 final ContentProviderOperation oper = diff.get(0); 402 assertEquals("Incorrect type", TYPE_INSERT, oper.getType()); 403 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri()); 404 } 405 { 406 final ContentProviderOperation oper = diff.get(1); 407 assertEquals("Incorrect type", TYPE_INSERT, oper.getType()); 408 assertEquals("Incorrect target", Data.CONTENT_URI, oper.getUri()); 409 410 } 411 } 412 } 413