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 RawContactDeltaTests()53 public RawContactDeltaTests() { 54 super(); 55 } 56 57 @Override setUp()58 public void setUp() { 59 mContext = getContext(); 60 } 61 getRawContact(Context context, long contactId, long phoneId)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 */ testParcelChangesNone()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 testParcelChangesInsert()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 testParcelChangesUpdate()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 testParcelChangesDelete()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 testValuesDiffDelete()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 */ testEntityDiffNone()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 testEntityDiffNoneInsert()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 testEntityDiffUpdateInsert()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 testEntityDiffNoneUpdate()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 testEntityDiffDelete()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 testEntityDiffInsert()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 testEntityDiffInsertInsert()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