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