• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
RawContactDeltaTests()57     public RawContactDeltaTests() {
58         super();
59     }
60 
61     @Override
setUp()62     public void setUp() {
63         mContext = getContext();
64     }
65 
getRawContact(Context context, long contactId, long phoneId)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      */
testParcelChangesNone()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 
testParcelChangesInsert()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 
testParcelChangesUpdate()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 
testParcelChangesDelete()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 
testValuesDiffDelete()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      */
testEntityDiffNone()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 
testEntityDiffNoneInsert()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 
testEntityDiffUpdateInsert()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 
testEntityDiffNoneUpdate()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 
testEntityDiffDelete()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 
testEntityDiffInsert()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 
testEntityDiffInsertInsert()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