| /* |
| * Copyright (C) 2020 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.ims.rcs.uce.eab; |
| |
| import static android.provider.ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.mockito.ArgumentMatchers.anyInt; |
| import static org.mockito.ArgumentMatchers.anyLong; |
| import static org.mockito.ArgumentMatchers.anyString; |
| import static org.mockito.Mockito.doReturn; |
| |
| import android.content.ContentProvider; |
| import android.content.ContentValues; |
| import android.content.SharedPreferences; |
| import android.content.pm.ProviderInfo; |
| import android.database.Cursor; |
| import android.database.MatrixCursor; |
| import android.net.Uri; |
| import android.provider.ContactsContract; |
| import android.test.mock.MockContentResolver; |
| |
| import androidx.annotation.NonNull; |
| import androidx.annotation.Nullable; |
| import androidx.test.rule.provider.ProviderTestRule; |
| |
| import com.android.ims.ImsTestBase; |
| |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Rule; |
| import org.junit.Test; |
| import org.mockito.Mock; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| |
| public class EabContactSyncControllerTest extends ImsTestBase { |
| private static final String TAG = "EabContactDataSyncServiceTest"; |
| |
| FakeContactProvider mFakeContactProvider = new FakeContactProvider(); |
| |
| @Rule |
| public ProviderTestRule mProviderTestRule = new ProviderTestRule.Builder( |
| EabProvider.class, EabProvider.AUTHORITY).build(); |
| |
| @Mock private SharedPreferences mSharedPreferences; |
| @Mock private SharedPreferences.Editor mSharedPreferencesEditor; |
| |
| @Before |
| public void setUp() throws Exception { |
| super.setUp(); |
| MockContentResolver mockContentResolver = |
| (MockContentResolver) mProviderTestRule.getResolver(); |
| ProviderInfo providerInfo = new ProviderInfo(); |
| providerInfo.authority = ContactsContract.AUTHORITY; |
| mFakeContactProvider.attachInfo(mContext, providerInfo); |
| mockContentResolver.addProvider(providerInfo.authority, mFakeContactProvider); |
| doReturn("com.android.phone.tests").when(mContext).getPackageName(); |
| |
| doReturn(mProviderTestRule.getResolver()).when(mContext).getContentResolver(); |
| |
| doReturn(mSharedPreferences).when(mContext).getSharedPreferences(anyString(), anyInt()); |
| doReturn(0L).when(mSharedPreferences).getLong(anyString(), anyInt()); |
| doReturn(mSharedPreferencesEditor).when(mSharedPreferences).edit(); |
| doReturn(mSharedPreferencesEditor).when(mSharedPreferencesEditor).putLong(anyString(), |
| anyLong()); |
| } |
| |
| @After |
| public void tearDown() throws Exception { |
| super.tearDown(); |
| mFakeContactProvider.clearData(); |
| mContext.getContentResolver().delete(EabProvider.CONTACT_URI, null, null); |
| } |
| |
| @Test |
| public void testContactDeletedCase() { |
| insertContactToEabProvider(1, 2, 3, "123456"); |
| insertDeletedContactToContactProvider(1, 1); |
| |
| new EabContactSyncController().syncContactToEabProvider(mContext); |
| |
| Cursor result = mProviderTestRule.getResolver().query( |
| EabProvider.CONTACT_URI, |
| null, |
| null, |
| null); |
| assertEquals(0, result.getCount()); |
| } |
| |
| @Test |
| public void testMultipleContactsDeletedCase() { |
| // Insert 3 contacts in EabProvider |
| insertContactToEabProvider(1, 1, 1, "123456"); |
| insertContactToEabProvider(2, 2, 2, "1234567"); |
| insertContactToEabProvider(3, 3, 3, "12345678"); |
| // Insert 2 deleted contacts |
| insertDeletedContactToContactProvider(1, 1); |
| insertDeletedContactToContactProvider(2, 1); |
| |
| new EabContactSyncController().syncContactToEabProvider(mContext); |
| |
| // Make sure only 1 contact in Eab DB |
| Cursor result = mProviderTestRule.getResolver().query( |
| EabProvider.CONTACT_URI, |
| null, |
| null, |
| null); |
| assertEquals(1, result.getCount()); |
| } |
| |
| @Test |
| public void testPhoneNumberDeletedCase() { |
| insertContactToEabProvider(1, 1, 2, "123456"); |
| insertContactToEabProvider(1, 1, 3, "1234567"); |
| insertContactToEabProvider(1, 1, 4, "12345678"); |
| // Delete phone number 12345678 |
| insertContactToContactProvider(1, 1, 2, "123456"); |
| insertContactToContactProvider(1, 1, 3, "1234567"); |
| |
| new EabContactSyncController().syncContactToEabProvider(mContext); |
| |
| Cursor result = mProviderTestRule.getResolver().query( |
| EabProvider.CONTACT_URI, |
| null, |
| null, |
| null); |
| assertEquals(2, result.getCount()); |
| } |
| |
| @Test |
| public void testPhoneNumberUpdatedCase() { |
| insertContactToEabProvider(1, 1, 2, "123456"); |
| insertContactToEabProvider(1, 1, 3, "1234567"); |
| insertContactToEabProvider(1, 1, 4, "12345678"); |
| // Update phone number to 1,2,3 |
| insertContactToContactProvider(1, 1, 2, "1"); |
| insertContactToContactProvider(1, 1, 3, "2"); |
| insertContactToContactProvider(1, 1, 4, "3"); |
| |
| new EabContactSyncController().syncContactToEabProvider(mContext); |
| |
| Cursor result = mProviderTestRule.getResolver().query( |
| EabProvider.CONTACT_URI, |
| null, |
| null, |
| null, |
| EabProvider.ContactColumns.DATA_ID); |
| result.moveToFirst(); |
| assertEquals(3, result.getCount()); |
| assertEquals("1", |
| result.getString(result.getColumnIndex(EabProvider.ContactColumns.PHONE_NUMBER))); |
| result.moveToNext(); |
| assertEquals("2", |
| result.getString(result.getColumnIndex(EabProvider.ContactColumns.PHONE_NUMBER))); |
| result.moveToNext(); |
| assertEquals("3", |
| result.getString(result.getColumnIndex(EabProvider.ContactColumns.PHONE_NUMBER))); |
| } |
| |
| private void insertDeletedContactToContactProvider(int contactId, int timestamp) { |
| ContentValues values = new ContentValues(); |
| values.put(ContactsContract.DeletedContacts.CONTACT_ID, contactId); |
| values.put(ContactsContract.DeletedContacts.CONTACT_DELETED_TIMESTAMP, timestamp); |
| mContext.getContentResolver().insert( |
| ContactsContract.DeletedContacts.CONTENT_URI, values); |
| } |
| |
| private void insertContactToContactProvider( |
| int contactId, int rawContactId, int dataId, String number) { |
| ContentValues values = new ContentValues(); |
| values.put(ContactsContract.Data._ID, dataId); |
| values.put(EabProvider.ContactColumns.CONTACT_ID, contactId); |
| values.put(ContactsContract.CommonDataKinds.Phone.RAW_CONTACT_ID, rawContactId); |
| values.put(ContactsContract.Data.MIMETYPE, CONTENT_ITEM_TYPE); |
| values.put(ContactsContract.CommonDataKinds.Phone.NUMBER, number); |
| values.put(ContactsContract.CommonDataKinds.Phone.CONTACT_LAST_UPDATED_TIMESTAMP, 1); |
| |
| mContext.getContentResolver().insert(ContactsContract.Data.CONTENT_URI, values); |
| } |
| |
| private void insertContactToEabProvider(int contactId, |
| int rawContactId, int dataId, String phoneNumber) { |
| ContentValues values = new ContentValues(); |
| values.put(EabProvider.ContactColumns.CONTACT_ID, contactId); |
| values.put(EabProvider.ContactColumns.RAW_CONTACT_ID, rawContactId); |
| values.put(EabProvider.ContactColumns.DATA_ID, dataId); |
| values.put(EabProvider.ContactColumns.PHONE_NUMBER, phoneNumber); |
| mContext.getContentResolver().insert(EabProvider.CONTACT_URI, values); |
| } |
| |
| /** |
| * Create a fake contact provider that store ContentValues in hashmap when invoke insert() |
| * and convert to cursor when invoke query() |
| */ |
| public static class FakeContactProvider extends ContentProvider { |
| private final HashMap<Uri, List<ContentValues>> mFakeProviderData = new HashMap<>(); |
| |
| public FakeContactProvider() { |
| } |
| |
| public void clearData() { |
| mFakeProviderData.clear(); |
| } |
| |
| @Override |
| public boolean onCreate() { |
| return true; |
| } |
| |
| @Override |
| public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, |
| String sortOrder) { |
| return convertContentValuesToCursor(mFakeProviderData.get(uri)); |
| } |
| |
| @Nullable |
| @Override |
| public String getType(@NonNull Uri uri) { |
| return null; |
| } |
| |
| @Nullable |
| @Override |
| public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) { |
| List<ContentValues> allDataList = |
| mFakeProviderData.computeIfAbsent(uri, k -> new ArrayList<>()); |
| allDataList.add(new ContentValues(values)); |
| return null; |
| } |
| |
| @Override |
| public int delete(Uri uri, String selection, String[] selectionArgs) { |
| return 0; |
| } |
| |
| @Override |
| public int update(@NonNull Uri uri, @Nullable ContentValues values, |
| @Nullable String selection, @Nullable String[] selectionArgs) { |
| return 0; |
| } |
| |
| private Cursor convertContentValuesToCursor(List<ContentValues> valuesList) { |
| if (valuesList != null) { |
| MatrixCursor result = |
| new MatrixCursor(valuesList.get(0).keySet().toArray(new String[0])); |
| for (ContentValues contentValue : valuesList) { |
| MatrixCursor.RowBuilder builder = result.newRow(); |
| for (String key : contentValue.keySet()) { |
| builder.add(key, contentValue.get(key)); |
| } |
| } |
| return result; |
| } else { |
| return new MatrixCursor(new String[0]); |
| } |
| } |
| } |
| } |