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