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