• 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 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