RawContactDeltaTests.java revision 0a49afa2ad697307cc04ef4cb86570574fa720f2
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
17package com.android.contacts.model;
18
19import android.content.ContentProviderOperation;
20import android.content.ContentValues;
21import android.content.Context;
22import android.os.Build;
23import android.os.Parcel;
24import android.provider.ContactsContract.CommonDataKinds.Phone;
25import android.provider.ContactsContract.Data;
26import android.provider.ContactsContract.RawContacts;
27import android.test.AndroidTestCase;
28import android.test.suitebuilder.annotation.LargeTest;
29
30import com.android.contacts.compat.CompatUtils;
31
32import com.google.common.collect.Lists;
33
34import 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
42public 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
53    public RawContactDeltaTests() {
54        super();
55    }
56
57    @Override
58    public void setUp() {
59        mContext = getContext();
60    }
61
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     */
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
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
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
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
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     */
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
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
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
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
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
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
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