1/*
2 * Copyright (C) 2015 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.providers.contacts;
18
19import android.content.ContentProviderOperation;
20import android.content.ContentUris;
21import android.content.ContentValues;
22import android.database.Cursor;
23import android.net.Uri;
24import android.provider.ContactsContract.MetadataSync;
25import android.provider.ContactsContract.MetadataSyncState;
26import android.provider.ContactsContract.RawContacts;
27import android.test.MoreAsserts;
28import android.test.suitebuilder.annotation.MediumTest;
29import com.android.providers.contacts.ContactsDatabaseHelper.MetadataSyncColumns;
30import com.android.providers.contacts.testutil.RawContactUtil;
31import com.google.android.collect.Lists;
32
33import java.util.ArrayList;
34import java.util.Arrays;
35import java.util.HashSet;
36import java.util.Set;
37
38/**
39 * Unit tests for {@link com.android.providers.contacts.ContactMetadataProvider}.
40 * <p/>
41 * Run the test like this:
42 * <code>
43 * adb shell am instrument -e class com.android.providers.contacts.ContactMetadataProviderTest -w \
44 * com.android.providers.contacts.tests/android.test.InstrumentationTestRunner
45 * </code>
46 */
47@MediumTest
48public class ContactMetadataProviderTest extends BaseContactsProvider2Test {
49    private static String TEST_ACCOUNT_TYPE1 = "test_account_type1";
50    private static String TEST_ACCOUNT_NAME1 = "test_account_name1";
51    private static String TEST_DATA_SET1 = "plus";
52    private static String TEST_BACKUP_ID1 = "1001";
53    private static String TEST_DATA1 = "{\n" +
54            "  \"unique_contact_id\": {\n" +
55            "    \"account_type\": \"CUSTOM_ACCOUNT\",\n" +
56            "    \"custom_account_type\": " + TEST_ACCOUNT_TYPE1 + ",\n" +
57            "    \"account_name\": " + TEST_ACCOUNT_NAME1 + ",\n" +
58            "    \"contact_id\": " + TEST_BACKUP_ID1 + ",\n" +
59            "    \"data_set\": \"GOOGLE_PLUS\"\n" +
60            "  },\n" +
61            "  \"contact_prefs\": {\n" +
62            "    \"send_to_voicemail\": true,\n" +
63            "    \"starred\": true,\n" +
64            "    \"pinned\": 2\n" +
65            "  }\n" +
66            "  }";
67    private static byte[] TEST_SYNC_STATE1 = "sync state1".getBytes();
68    private static String TEST_ACCOUNT_TYPE2 = "test_account_type2";
69    private static String TEST_ACCOUNT_NAME2 = "test_account_name2";
70    private static String TEST_DATA_SET2 = null;
71    private static String TEST_BACKUP_ID2 = "1002";
72    private static String TEST_DATA2 =  "{\n" +
73            "  \"unique_contact_id\": {\n" +
74            "    \"account_type\": \"CUSTOM_ACCOUNT\",\n" +
75            "    \"custom_account_type\": " + TEST_ACCOUNT_TYPE2 + ",\n" +
76            "    \"account_name\": " + TEST_ACCOUNT_NAME2 + ",\n" +
77            "    \"contact_id\": " + TEST_BACKUP_ID2 + ",\n" +
78            "    \"data_set\": \"GOOGLE_PLUS\"\n" +
79            "  },\n" +
80            "  \"contact_prefs\": {\n" +
81            "    \"send_to_voicemail\": true,\n" +
82            "    \"starred\": true,\n" +
83            "    \"pinned\": 2\n" +
84            "  }\n" +
85            "  }";
86    private static byte[] TEST_SYNC_STATE2 = "sync state2".getBytes();
87    private static String SELECTION_BY_TEST_ACCOUNT1 = MetadataSync.ACCOUNT_NAME + "='" +
88            TEST_ACCOUNT_NAME1 + "' AND " + MetadataSync.ACCOUNT_TYPE + "='" + TEST_ACCOUNT_TYPE1 +
89            "' AND " + MetadataSync.DATA_SET + "='" + TEST_DATA_SET1 + "'";
90
91    private static String SELECTION_BY_TEST_ACCOUNT2 = MetadataSync.ACCOUNT_NAME + "='" +
92            TEST_ACCOUNT_NAME2 + "' AND " + MetadataSync.ACCOUNT_TYPE + "='" + TEST_ACCOUNT_TYPE2 +
93            "' AND " + MetadataSync.DATA_SET + "='" + TEST_DATA_SET2 + "'";
94
95    private ContactMetadataProvider mContactMetadataProvider;
96    private ContentValues defaultValues;
97
98    @Override
99    protected void setUp() throws Exception {
100        super.setUp();
101        mContactMetadataProvider = addProvider(
102                ContactMetadataProviderTestable.class, MetadataSync.METADATA_AUTHORITY);
103        // Reset the dbHelper to be the one ContactsProvider2 is using. Before this, two providers
104        // are using different dbHelpers.
105        mContactMetadataProvider.setDatabaseHelper(((SynchronousContactsProvider2)
106                mActor.provider).getDatabaseHelper());
107        setupData();
108    }
109
110    public void testInsertWithInvalidUri() {
111        try {
112            mResolver.insert(Uri.withAppendedPath(MetadataSync.METADATA_AUTHORITY_URI,
113                    "metadata"), getDefaultValues());
114            fail("the insert was expected to fail, but it succeeded");
115        } catch (IllegalArgumentException e) {
116            // this was expected
117        }
118    }
119
120    public void testUpdateWithInvalidUri() {
121        try {
122            mResolver.update(Uri.withAppendedPath(MetadataSync.METADATA_AUTHORITY_URI,
123                    "metadata"), getDefaultValues(), null, null);
124            fail("the update was expected to fail, but it succeeded");
125        } catch (IllegalArgumentException e) {
126            // this was expected
127        }
128    }
129
130    public void testGetMetadataByAccount() {
131        Cursor c = mResolver.query(MetadataSync.CONTENT_URI, null, SELECTION_BY_TEST_ACCOUNT1,
132                null, null);
133        assertEquals(1, c.getCount());
134
135        ContentValues expectedValues = defaultValues;
136        expectedValues.remove(MetadataSyncColumns.ACCOUNT_ID);
137        c.moveToFirst();
138        assertCursorValues(c, expectedValues);
139        c.close();
140    }
141
142    public void testFailOnInsertMetadataForSameAccountIdAndBackupId() {
143        // Insert a new metadata with same account and backupId as defaultValues should fail.
144        String newData = "{\n" +
145                "  \"unique_contact_id\": {\n" +
146                "    \"account_type\": \"CUSTOM_ACCOUNT\",\n" +
147                "    \"custom_account_type\": " + TEST_ACCOUNT_TYPE1 + ",\n" +
148                "    \"account_name\": " + TEST_ACCOUNT_NAME1 + ",\n" +
149                "    \"contact_id\": " + TEST_BACKUP_ID1 + ",\n" +
150                "    \"data_set\": \"GOOGLE_PLUS\"\n" +
151                "  },\n" +
152                "  \"contact_prefs\": {\n" +
153                "    \"send_to_voicemail\": false,\n" +
154                "    \"starred\": false,\n" +
155                "    \"pinned\": 1\n" +
156                "  }\n" +
157                "  }";
158
159        ContentValues  newValues =  new ContentValues();
160        newValues.put(MetadataSync.ACCOUNT_NAME, TEST_ACCOUNT_NAME1);
161        newValues.put(MetadataSync.ACCOUNT_TYPE, TEST_ACCOUNT_TYPE1);
162        newValues.put(MetadataSync.DATA_SET, TEST_DATA_SET1);
163        newValues.put(MetadataSync.RAW_CONTACT_BACKUP_ID, TEST_BACKUP_ID1);
164        newValues.put(MetadataSync.DATA, newData);
165        newValues.put(MetadataSync.DELETED, 0);
166        try {
167            mResolver.insert(MetadataSync.CONTENT_URI, newValues);
168        } catch (Exception e) {
169            // Expected.
170        }
171    }
172
173    public void testInsertAndUpdateMetadataSync() {
174        // Create a raw contact with backupId.
175        String backupId = "backupId10001";
176        long rawContactId = RawContactUtil.createRawContactWithAccountDataSet(
177                mResolver, TEST_ACCOUNT_NAME1, TEST_ACCOUNT_TYPE1, TEST_DATA_SET1);
178        Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
179        ContentValues values = new ContentValues();
180        values.put(RawContacts.BACKUP_ID, backupId);
181        assertEquals(1, mResolver.update(rawContactUri, values, null, null));
182
183        assertStoredValue(rawContactUri, RawContacts._ID, rawContactId);
184        assertStoredValue(rawContactUri, RawContacts.ACCOUNT_TYPE, TEST_ACCOUNT_TYPE1);
185        assertStoredValue(rawContactUri, RawContacts.ACCOUNT_NAME, TEST_ACCOUNT_NAME1);
186        assertStoredValue(rawContactUri, RawContacts.BACKUP_ID, backupId);
187        assertStoredValue(rawContactUri, RawContacts.DATA_SET, TEST_DATA_SET1);
188
189        String deleted = "0";
190        String insertJson = "{\n" +
191                "  \"unique_contact_id\": {\n" +
192                "    \"account_type\": \"CUSTOM_ACCOUNT\",\n" +
193                "    \"custom_account_type\": " + TEST_ACCOUNT_TYPE1 + ",\n" +
194                "    \"account_name\": " + TEST_ACCOUNT_NAME1 + ",\n" +
195                "    \"contact_id\": " + backupId + ",\n" +
196                "    \"data_set\": \"GOOGLE_PLUS\"\n" +
197                "  },\n" +
198                "  \"contact_prefs\": {\n" +
199                "    \"send_to_voicemail\": true,\n" +
200                "    \"starred\": true,\n" +
201                "    \"pinned\": 2\n" +
202                "  }\n" +
203                "  }";
204
205        // Insert to MetadataSync table.
206        ContentValues insertedValues = new ContentValues();
207        insertedValues.put(MetadataSync.RAW_CONTACT_BACKUP_ID, backupId);
208        insertedValues.put(MetadataSync.ACCOUNT_TYPE, TEST_ACCOUNT_TYPE1);
209        insertedValues.put(MetadataSync.ACCOUNT_NAME, TEST_ACCOUNT_NAME1);
210        insertedValues.put(MetadataSync.DATA_SET, TEST_DATA_SET1);
211        insertedValues.put(MetadataSync.DATA, insertJson);
212        insertedValues.put(MetadataSync.DELETED, deleted);
213        Uri metadataUri = mResolver.insert(MetadataSync.CONTENT_URI, insertedValues);
214
215        long metadataId = ContentUris.parseId(metadataUri);
216        assertEquals(true, metadataId > 0);
217
218        // Check if RawContact table is updated  after inserting metadata.
219        assertStoredValue(rawContactUri, RawContacts.ACCOUNT_TYPE, TEST_ACCOUNT_TYPE1);
220        assertStoredValue(rawContactUri, RawContacts.ACCOUNT_NAME, TEST_ACCOUNT_NAME1);
221        assertStoredValue(rawContactUri, RawContacts.BACKUP_ID, backupId);
222        assertStoredValue(rawContactUri, RawContacts.DATA_SET, TEST_DATA_SET1);
223        assertStoredValue(rawContactUri, RawContacts.SEND_TO_VOICEMAIL, "1");
224        assertStoredValue(rawContactUri, RawContacts.STARRED, "1");
225        assertStoredValue(rawContactUri, RawContacts.PINNED, "2");
226
227        // Update the MetadataSync table.
228        String updatedJson = "{\n" +
229                "  \"unique_contact_id\": {\n" +
230                "    \"account_type\": \"CUSTOM_ACCOUNT\",\n" +
231                "    \"custom_account_type\": " + TEST_ACCOUNT_TYPE1 + ",\n" +
232                "    \"account_name\": " + TEST_ACCOUNT_NAME1 + ",\n" +
233                "    \"contact_id\": " + backupId + ",\n" +
234                "    \"data_set\": \"GOOGLE_PLUS\"\n" +
235                "  },\n" +
236                "  \"contact_prefs\": {\n" +
237                "    \"send_to_voicemail\": false,\n" +
238                "    \"starred\": false,\n" +
239                "    \"pinned\": 1\n" +
240                "  }\n" +
241                "  }";
242        ContentValues updatedValues = new ContentValues();
243        updatedValues.put(MetadataSync.RAW_CONTACT_BACKUP_ID, backupId);
244        updatedValues.put(MetadataSync.ACCOUNT_TYPE, TEST_ACCOUNT_TYPE1);
245        updatedValues.put(MetadataSync.ACCOUNT_NAME, TEST_ACCOUNT_NAME1);
246        updatedValues.put(MetadataSync.DATA_SET, TEST_DATA_SET1);
247        updatedValues.put(MetadataSync.DATA, updatedJson);
248        updatedValues.put(MetadataSync.DELETED, deleted);
249        mResolver.insert(MetadataSync.CONTENT_URI, updatedValues);
250
251        // Check if the insert (actually update) is correct.
252        assertStoredValue(rawContactUri, RawContacts.ACCOUNT_TYPE, TEST_ACCOUNT_TYPE1);
253        assertStoredValue(rawContactUri, RawContacts.ACCOUNT_NAME, TEST_ACCOUNT_NAME1);
254        assertStoredValue(rawContactUri, RawContacts.DATA_SET, TEST_DATA_SET1);
255        assertStoredValue(rawContactUri, RawContacts.SEND_TO_VOICEMAIL, "0");
256        assertStoredValue(rawContactUri, RawContacts.STARRED, "0");
257        assertStoredValue(rawContactUri, RawContacts.PINNED, "1");
258    }
259
260    public void testInsertMetadata() {
261        String backupId = "newBackupId";
262        String deleted = "0";
263        String insertJson = "{\n" +
264                "  \"unique_contact_id\": {\n" +
265                "    \"account_type\": \"CUSTOM_ACCOUNT\",\n" +
266                "    \"custom_account_type\": " + TEST_ACCOUNT_TYPE1 + ",\n" +
267                "    \"account_name\": " + TEST_ACCOUNT_NAME1 + ",\n" +
268                "    \"contact_id\": " + backupId + ",\n" +
269                "    \"data_set\": \"GOOGLE_PLUS\"\n" +
270                "  },\n" +
271                "  \"contact_prefs\": {\n" +
272                "    \"send_to_voicemail\": true,\n" +
273                "    \"starred\": true,\n" +
274                "    \"pinned\": 2\n" +
275                "  }\n" +
276                "  }";
277
278        // Insert to MetadataSync table.
279        ContentValues insertedValues = new ContentValues();
280        insertedValues.put(MetadataSync.RAW_CONTACT_BACKUP_ID, backupId);
281        insertedValues.put(MetadataSync.ACCOUNT_TYPE, TEST_ACCOUNT_TYPE1);
282        insertedValues.put(MetadataSync.ACCOUNT_NAME, TEST_ACCOUNT_NAME1);
283        insertedValues.put(MetadataSync.DATA_SET, TEST_DATA_SET1);
284        insertedValues.put(MetadataSync.DATA, insertJson);
285        insertedValues.put(MetadataSync.DELETED, deleted);
286        Uri metadataUri = mResolver.insert(MetadataSync.CONTENT_URI, insertedValues);
287
288        long metadataId = ContentUris.parseId(metadataUri);
289        assertEquals(true, metadataId > 0);
290    }
291
292    public void testFailUpdateDeletedMetadata() {
293        String backupId = "backupId001";
294        String newData = "{\n" +
295                "  \"unique_contact_id\": {\n" +
296                "    \"account_type\": \"CUSTOM_ACCOUNT\",\n" +
297                "    \"custom_account_type\": " + TEST_ACCOUNT_TYPE1 + ",\n" +
298                "    \"account_name\": " + TEST_ACCOUNT_NAME1 + ",\n" +
299                "    \"contact_id\": " + backupId + ",\n" +
300                "    \"data_set\": \"GOOGLE_PLUS\"\n" +
301                "  },\n" +
302                "  \"contact_prefs\": {\n" +
303                "    \"send_to_voicemail\": false,\n" +
304                "    \"starred\": false,\n" +
305                "    \"pinned\": 1\n" +
306                "  }\n" +
307                "  }";
308
309        ContentValues  newValues =  new ContentValues();
310        newValues.put(MetadataSync.ACCOUNT_NAME, TEST_ACCOUNT_NAME1);
311        newValues.put(MetadataSync.ACCOUNT_TYPE, TEST_ACCOUNT_TYPE1);
312        newValues.put(MetadataSync.DATA_SET, TEST_DATA_SET1);
313        newValues.put(MetadataSync.RAW_CONTACT_BACKUP_ID, backupId);
314        newValues.put(MetadataSync.DATA, newData);
315        newValues.put(MetadataSync.DELETED, 1);
316
317        try {
318            mResolver.insert(MetadataSync.CONTENT_URI, newValues);
319            fail("the update was expected to fail, but it succeeded");
320        } catch (IllegalArgumentException e) {
321            // Expected
322        }
323    }
324
325    public void testInsertWithNullData() {
326        ContentValues  newValues =  new ContentValues();
327        String data = null;
328        String backupId = "backupId002";
329        newValues.put(MetadataSync.ACCOUNT_NAME, TEST_ACCOUNT_NAME1);
330        newValues.put(MetadataSync.ACCOUNT_TYPE, TEST_ACCOUNT_TYPE1);
331        newValues.put(MetadataSync.DATA_SET, TEST_DATA_SET1);
332        newValues.put(MetadataSync.RAW_CONTACT_BACKUP_ID, backupId);
333        newValues.put(MetadataSync.DATA, data);
334        newValues.put(MetadataSync.DELETED, 0);
335
336        try {
337            mResolver.insert(MetadataSync.CONTENT_URI, newValues);
338        } catch (IllegalArgumentException e) {
339            // Expected.
340        }
341    }
342
343    public void testDeleteMetadata() {
344        //insert another metadata for TEST_ACCOUNT
345        insertMetadata(TEST_ACCOUNT_NAME1, TEST_ACCOUNT_TYPE1, TEST_DATA_SET1, "2", TEST_DATA1, 0);
346        Cursor c = mResolver.query(MetadataSync.CONTENT_URI, null, SELECTION_BY_TEST_ACCOUNT1,
347                null, null);
348        assertEquals(2, c.getCount());
349        int numOfDeletion = mResolver.delete(MetadataSync.CONTENT_URI, SELECTION_BY_TEST_ACCOUNT1,
350                null);
351        assertEquals(2, numOfDeletion);
352        c = mResolver.query(MetadataSync.CONTENT_URI, null, SELECTION_BY_TEST_ACCOUNT1,
353                null, null);
354        assertEquals(0, c.getCount());
355    }
356
357    public void testBulkInsert() {
358        Cursor c = mResolver.query(MetadataSync.CONTENT_URI, new String[]{MetadataSync._ID},
359                SELECTION_BY_TEST_ACCOUNT1, null, null);
360        assertEquals(1, c.getCount());
361
362        ContentValues values1 = getMetadataContentValues(
363                TEST_ACCOUNT_NAME1, TEST_ACCOUNT_TYPE1, TEST_DATA_SET1, "123", TEST_DATA1, 0);
364        ContentValues values2 = getMetadataContentValues(
365                TEST_ACCOUNT_NAME1, TEST_ACCOUNT_TYPE1, TEST_DATA_SET1, "456", TEST_DATA1, 0);
366        ContentValues[] values = new ContentValues[] {values1, values2};
367
368        mResolver.bulkInsert(MetadataSync.CONTENT_URI, values);
369        c = mResolver.query(MetadataSync.CONTENT_URI, new String[] {MetadataSync._ID},
370                SELECTION_BY_TEST_ACCOUNT1, null, null);
371        assertEquals(3, c.getCount());
372    }
373
374    public void testBatchOperations() throws Exception {
375        // Two mentadata_sync entries in the beginning, one for TEST_ACCOUNT1 and another for
376        // TEST_ACCOUNT2
377        Cursor c = mResolver.query(MetadataSync.CONTENT_URI, new String[] {MetadataSync._ID},
378                null, null, null);
379        assertEquals(2, c.getCount());
380
381        String updatedData = "{\n" +
382                "  \"unique_contact_id\": {\n" +
383                "    \"account_type\": \"CUSTOM_ACCOUNT\",\n" +
384                "    \"custom_account_type\": " + TEST_ACCOUNT_TYPE1 + ",\n" +
385                "    \"account_name\": " + TEST_ACCOUNT_NAME1 + ",\n" +
386                "    \"contact_id\": " + TEST_BACKUP_ID1 + ",\n" +
387                "    \"data_set\": \"GOOGLE_PLUS\"\n" +
388                "  },\n" +
389                "  \"contact_prefs\": {\n" +
390                "    \"send_to_voicemail\": true,\n" +
391                "    \"starred\": false,\n" +
392                "    \"pinned\": 5\n" +
393                "  }\n" +
394                "  }";
395
396        String newBackupId = "2222";
397        String newData = "{\n" +
398                "  \"unique_contact_id\": {\n" +
399                "    \"account_type\": \"CUSTOM_ACCOUNT\",\n" +
400                "    \"custom_account_type\": " + TEST_ACCOUNT_TYPE1 + ",\n" +
401                "    \"account_name\": " + TEST_ACCOUNT_NAME1 + ",\n" +
402                "    \"contact_id\": " + newBackupId + ",\n" +
403                "    \"data_set\": \"GOOGLE_PLUS\"\n" +
404                "  },\n" +
405                "  \"contact_prefs\": {\n" +
406                "    \"send_to_voicemail\": true,\n" +
407                "    \"starred\": false,\n" +
408                "    \"pinned\": 5\n" +
409                "  }\n" +
410                "  }";
411
412        ArrayList<ContentProviderOperation> ops = Lists.newArrayList();
413        ops.add(ContentProviderOperation.newInsert(MetadataSync.CONTENT_URI)
414                .withValue(MetadataSync.ACCOUNT_NAME, TEST_ACCOUNT_NAME1)
415                .withValue(MetadataSync.ACCOUNT_TYPE, TEST_ACCOUNT_TYPE1)
416                .withValue(MetadataSync.DATA_SET, TEST_DATA_SET1)
417                .withValue(MetadataSync.RAW_CONTACT_BACKUP_ID, TEST_BACKUP_ID1)
418                .withValue(MetadataSync.DATA, updatedData)
419                .withValue(MetadataSync.DELETED, 0)
420                .build());
421
422        ops.add(ContentProviderOperation.newInsert(MetadataSync.CONTENT_URI)
423                .withValue(MetadataSync.ACCOUNT_NAME, TEST_ACCOUNT_NAME1)
424                .withValue(MetadataSync.ACCOUNT_TYPE, TEST_ACCOUNT_TYPE1)
425                .withValue(MetadataSync.DATA_SET, TEST_DATA_SET1)
426                .withValue(MetadataSync.RAW_CONTACT_BACKUP_ID, newBackupId)
427                .withValue(MetadataSync.DATA, newData)
428                .withValue(MetadataSync.DELETED, 0)
429                .build());
430
431        ops.add(ContentProviderOperation.newDelete(MetadataSync.CONTENT_URI)
432                .withSelection(SELECTION_BY_TEST_ACCOUNT2, null)
433                .build());
434
435        // Batch three operations: update the metadata_entry of TEST_ACCOUNT1; insert one new
436        // metadata_entry for TEST_ACCOUNT1; delete metadata_entry of TEST_ACCOUNT2
437        mResolver.applyBatch(MetadataSync.METADATA_AUTHORITY, ops);
438
439        // After the batch operations, there should be two metadata_entry for TEST_ACCOUNT1 with
440        // new data value and no metadata_entry for TEST_ACCOUNT2.
441        c = mResolver.query(MetadataSync.CONTENT_URI, new String[] {MetadataSync.DATA},
442                SELECTION_BY_TEST_ACCOUNT1, null, null);
443        assertEquals(2, c.getCount());
444        Set<String> actualData = new HashSet<>();
445        while (c.moveToNext()) {
446            actualData.add(c.getString(0));
447        }
448        c.close();
449        MoreAsserts.assertContentsInAnyOrder(actualData, updatedData, newData);
450
451        c = mResolver.query(MetadataSync.CONTENT_URI, new String[] {MetadataSync._ID},
452                SELECTION_BY_TEST_ACCOUNT2, null, null);
453        assertEquals(0, c.getCount());
454    }
455
456    public void testQueryMetadataSyncState() {
457        String selection = MetadataSyncState.ACCOUNT_NAME + "=?1 AND " +
458                MetadataSyncState.ACCOUNT_TYPE + "=?2 AND " + MetadataSyncState.DATA_SET + "=?3";
459        final String[] args = new String[]{TEST_ACCOUNT_NAME1, TEST_ACCOUNT_TYPE1, TEST_DATA_SET1};
460        final String[] projection = new String[]{MetadataSyncState.STATE};
461        Cursor c = mResolver.query(MetadataSyncState.CONTENT_URI, projection, selection, args,
462                null);
463        assertEquals(1, c.getCount());
464        c.moveToFirst();
465        assertTrue(Arrays.equals(TEST_SYNC_STATE1, c.getBlob(0)));
466        c.close();
467    }
468
469    public void testUpdateMetadataSyncState() {
470        mResolver.update(MetadataSyncState.CONTENT_URI, getSyncStateValues(TEST_ACCOUNT_NAME1,
471                TEST_ACCOUNT_TYPE1, TEST_DATA_SET1, TEST_SYNC_STATE2), null, null);
472        String selection = MetadataSyncState.ACCOUNT_NAME + "=?1 AND " +
473                MetadataSyncState.ACCOUNT_TYPE + "=?2 AND " + MetadataSyncState.DATA_SET + "=?3";
474        final String[] args = new String[]{TEST_ACCOUNT_NAME1, TEST_ACCOUNT_TYPE1, TEST_DATA_SET1};
475        final String[] projection =  new String[] {MetadataSyncState.STATE};
476        Cursor c = mResolver.query(MetadataSyncState.CONTENT_URI, projection, selection, args,
477                null);
478
479        assertEquals(1, c.getCount());
480        c.moveToFirst();
481        assertTrue(Arrays.equals(TEST_SYNC_STATE2, c.getBlob(0)));
482        c.close();
483    }
484
485    public void testUpdateMetadataSyncState_NonExisting() {
486        // Delete the existing one first.
487        String selection = MetadataSyncState.ACCOUNT_NAME + "=?1 AND " +
488                MetadataSyncState.ACCOUNT_TYPE + "=?2 AND " + MetadataSyncState.DATA_SET + "=?3";
489        final String[] args = new String[]{TEST_ACCOUNT_NAME1, TEST_ACCOUNT_TYPE1, TEST_DATA_SET1};
490
491        mResolver.delete(MetadataSyncState.CONTENT_URI, selection, args);
492
493        mResolver.update(MetadataSyncState.CONTENT_URI, getSyncStateValues(TEST_ACCOUNT_NAME1,
494                TEST_ACCOUNT_TYPE1, TEST_DATA_SET1, TEST_SYNC_STATE2), null, null);
495        final String[] projection =  new String[] {MetadataSyncState.STATE};
496        Cursor c = mResolver.query(MetadataSyncState.CONTENT_URI, projection, selection, args,
497                null);
498
499        assertEquals(1, c.getCount());
500        c.moveToFirst();
501        assertTrue(Arrays.equals(TEST_SYNC_STATE2, c.getBlob(0)));
502        c.close();
503    }
504
505    public void testDeleteMetadataSyncState() {
506        String selection = MetadataSyncState.ACCOUNT_NAME + "=?1 AND " +
507                MetadataSyncState.ACCOUNT_TYPE + "=?2 AND " + MetadataSyncState.DATA_SET + "=?3";
508        final String[] args = new String[]{TEST_ACCOUNT_NAME1, TEST_ACCOUNT_TYPE1, TEST_DATA_SET1};
509        final String[] projection = new String[]{MetadataSyncState.STATE};
510        Cursor c = mResolver.query(MetadataSyncState.CONTENT_URI, projection, selection, args,
511                null);
512        assertEquals(1, c.getCount());
513        c.close();
514
515        mResolver.delete(MetadataSyncState.CONTENT_URI, selection, args);
516        c = mResolver.query(MetadataSyncState.CONTENT_URI, projection, selection, args,
517                null);
518        assertEquals(0, c.getCount());
519        c.close();
520    }
521
522    private void setupData() {
523        long rawContactId1 = RawContactUtil.createRawContactWithAccountDataSet(
524                mResolver, TEST_ACCOUNT_NAME1, TEST_ACCOUNT_TYPE1, TEST_DATA_SET1);
525        createAccount(TEST_ACCOUNT_NAME1, TEST_ACCOUNT_TYPE1, TEST_DATA_SET1);
526        insertMetadata(getDefaultValues());
527        insertMetadataSyncState(TEST_ACCOUNT_NAME1, TEST_ACCOUNT_TYPE1, TEST_DATA_SET1,
528                TEST_SYNC_STATE1);
529
530        // Insert another entry for another account
531        createAccount(TEST_ACCOUNT_NAME2, TEST_ACCOUNT_TYPE2, TEST_DATA_SET2);
532        insertMetadata(TEST_ACCOUNT_NAME2, TEST_ACCOUNT_TYPE2, TEST_DATA_SET2, TEST_BACKUP_ID2,
533                TEST_DATA2, 0);
534        insertMetadataSyncState(TEST_ACCOUNT_NAME2, TEST_ACCOUNT_TYPE2, TEST_DATA_SET2,
535                TEST_SYNC_STATE2);
536    }
537
538    private ContentValues getDefaultValues() {
539        defaultValues = new ContentValues();
540        defaultValues.put(MetadataSync.ACCOUNT_NAME, TEST_ACCOUNT_NAME1);
541        defaultValues.put(MetadataSync.ACCOUNT_TYPE, TEST_ACCOUNT_TYPE1);
542        defaultValues.put(MetadataSync.DATA_SET, TEST_DATA_SET1);
543        defaultValues.put(MetadataSync.RAW_CONTACT_BACKUP_ID, TEST_BACKUP_ID1);
544        defaultValues.put(MetadataSync.DATA, TEST_DATA1);
545        defaultValues.put(MetadataSync.DELETED, 0);
546        return defaultValues;
547    }
548
549    private long insertMetadata(String accountName, String accountType, String dataSet,
550            String backupId, String data, int deleted) {
551        return insertMetadata(getMetadataContentValues(
552                accountName, accountType, dataSet, backupId, data, deleted));
553    }
554
555    private ContentValues getMetadataContentValues(String accountName, String accountType,
556            String dataSet, String backupId, String data, int deleted) {
557        ContentValues values = new ContentValues();
558        values.put(MetadataSync.ACCOUNT_NAME, accountName);
559        values.put(MetadataSync.ACCOUNT_TYPE, accountType);
560        values.put(MetadataSync.DATA_SET, dataSet);
561        values.put(MetadataSync.RAW_CONTACT_BACKUP_ID, backupId);
562        values.put(MetadataSync.DATA, data);
563        values.put(MetadataSync.DELETED, deleted);
564        return values;
565    }
566
567    private long insertMetadata(ContentValues values) {
568        return ContentUris.parseId(mResolver.insert(MetadataSync.CONTENT_URI, values));
569    }
570
571    private long insertMetadataSyncState(String accountName, String accountType,
572            String dataSet, byte[] state) {
573        return ContentUris.parseId(mResolver.insert(MetadataSyncState.CONTENT_URI,
574                getSyncStateValues(accountName, accountType, dataSet, state)));
575    }
576
577    private ContentValues getSyncStateValues(String accountName, String accountType,
578            String dataSet, byte[] state) {
579        ContentValues values = new ContentValues();
580        values.put(MetadataSyncState.ACCOUNT_NAME, accountName);
581        values.put(MetadataSyncState.ACCOUNT_TYPE, accountType);
582        values.put(MetadataSyncState.DATA_SET, dataSet);
583        values.put(MetadataSyncState.STATE, state);
584        return values;
585    }
586}
587