| /* |
| * Copyright (C) 2016 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.contacts.database; |
| |
| import static android.os.Build.VERSION_CODES; |
| |
| import static com.android.contacts.tests.ContactsMatchers.DataCursor.hasEmail; |
| import static com.android.contacts.tests.ContactsMatchers.DataCursor.hasName; |
| import static com.android.contacts.tests.ContactsMatchers.DataCursor.hasPhone; |
| import static com.android.contacts.tests.ContactsMatchers.isSimContactWithNameAndPhone; |
| |
| import static org.hamcrest.Matchers.allOf; |
| import static org.hamcrest.Matchers.equalTo; |
| import static org.junit.Assert.assertThat; |
| import static org.junit.Assert.assertTrue; |
| import static org.mockito.Mockito.mock; |
| import static org.mockito.Mockito.when; |
| |
| import android.content.ContentProviderOperation; |
| import android.content.ContentResolver; |
| import android.content.Context; |
| import android.content.OperationApplicationException; |
| import android.database.Cursor; |
| import android.os.RemoteException; |
| import android.provider.ContactsContract; |
| import android.provider.ContactsContract.CommonDataKinds.Email; |
| import android.provider.ContactsContract.CommonDataKinds.Phone; |
| import android.provider.ContactsContract.CommonDataKinds.StructuredName; |
| import android.provider.ContactsContract.Data; |
| import android.support.annotation.RequiresApi; |
| import android.support.test.InstrumentationRegistry; |
| import android.support.test.filters.LargeTest; |
| import android.support.test.filters.SdkSuppress; |
| import android.support.test.filters.SmallTest; |
| import android.support.test.filters.Suppress; |
| import android.support.test.runner.AndroidJUnit4; |
| import android.test.mock.MockContentResolver; |
| import android.test.mock.MockContext; |
| |
| import com.android.contacts.model.SimCard; |
| import com.android.contacts.model.SimContact; |
| import com.android.contacts.model.account.AccountWithDataSet; |
| import com.android.contacts.test.mocks.MockContentProvider; |
| import com.android.contacts.tests.AccountsTestHelper; |
| import com.android.contacts.tests.ContactsMatchers; |
| import com.android.contacts.tests.SimContactsTestHelper; |
| import com.android.contacts.tests.StringableCursor; |
| |
| import com.google.common.collect.ImmutableMap; |
| import com.google.common.collect.ImmutableSet; |
| |
| import org.hamcrest.Matchers; |
| import org.junit.After; |
| import org.junit.AfterClass; |
| import org.junit.Before; |
| import org.junit.BeforeClass; |
| import org.junit.Test; |
| import org.junit.experimental.runners.Enclosed; |
| import org.junit.runner.RunWith; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Map; |
| import java.util.Random; |
| import java.util.Set; |
| |
| @RunWith(Enclosed.class) |
| public class SimContactDaoTests { |
| |
| // Some random area codes for generating realistic US phones when |
| // generating fake data for the SIM contacts or CP2 |
| private static final String[] AREA_CODES = |
| {"360", "509", "416", "831", "212", "208"}; |
| private static final Random sRandom = new Random(); |
| |
| // Approximate maximum number of contacts that can be stored on a SIM card for testing |
| // boundary cases |
| public static final int MAX_SIM_CONTACTS = 600; |
| |
| // On pre-M addAccountExplicitly (which we call via AccountsTestHelper) causes a |
| // SecurityException to be thrown unless we add AUTHENTICATE_ACCOUNTS permission to the app |
| // manifest. Instead of adding the extra permission just for tests we'll just only run them |
| // on M or newer |
| @SdkSuppress(minSdkVersion = VERSION_CODES.M) |
| // Lollipop MR1 is required for removeAccountExplicitly |
| @RequiresApi(api = VERSION_CODES.LOLLIPOP_MR1) |
| @LargeTest |
| @RunWith(AndroidJUnit4.class) |
| public static class ImportIntegrationTest { |
| private AccountWithDataSet mAccount; |
| private AccountsTestHelper mAccountsHelper; |
| private ContentResolver mResolver; |
| |
| @Before |
| public void setUp() throws Exception { |
| mAccountsHelper = new AccountsTestHelper(); |
| mAccount = mAccountsHelper.addTestAccount(); |
| mResolver = getContext().getContentResolver(); |
| } |
| |
| @After |
| public void tearDown() throws Exception { |
| mAccountsHelper.cleanup(); |
| } |
| |
| @Test |
| public void importFromSim() throws Exception { |
| final SimContactDao sut = SimContactDao.create(getContext()); |
| |
| sut.importContacts(Arrays.asList( |
| new SimContact(1, "Test One", "15095550101"), |
| new SimContact(2, "Test Two", "15095550102"), |
| new SimContact(3, "Test Three", "15095550103", new String[] { |
| "user@example.com", "user2@example.com" |
| }) |
| ), mAccount); |
| |
| Cursor cursor = queryContactWithName("Test One"); |
| assertThat(cursor, ContactsMatchers.hasCount(2)); |
| assertThat(cursor, hasName("Test One")); |
| assertThat(cursor, hasPhone("15095550101")); |
| cursor.close(); |
| |
| cursor = queryContactWithName("Test Two"); |
| assertThat(cursor, ContactsMatchers.hasCount(2)); |
| assertThat(cursor, hasName("Test Two")); |
| assertThat(cursor, hasPhone("15095550102")); |
| cursor.close(); |
| |
| cursor = queryContactWithName("Test Three"); |
| assertThat(cursor, ContactsMatchers.hasCount(4)); |
| assertThat(cursor, hasName("Test Three")); |
| assertThat(cursor, hasPhone("15095550103")); |
| assertThat(cursor, allOf(hasEmail("user@example.com"), hasEmail("user2@example.com"))); |
| cursor.close(); |
| } |
| |
| @Test |
| public void importContactWhichOnlyHasName() throws Exception { |
| final SimContactDao sut = SimContactDao.create(getContext()); |
| |
| sut.importContacts(Arrays.asList( |
| new SimContact(1, "Test importJustName", null, null) |
| ), mAccount); |
| |
| Cursor cursor = queryAllDataInAccount(); |
| |
| assertThat(cursor, ContactsMatchers.hasCount(1)); |
| assertThat(cursor, hasName("Test importJustName")); |
| cursor.close(); |
| } |
| |
| @Test |
| public void importContactWhichOnlyHasPhone() throws Exception { |
| final SimContactDao sut = SimContactDao.create(getContext()); |
| |
| sut.importContacts(Arrays.asList( |
| new SimContact(1, null, "15095550111", null) |
| ), mAccount); |
| |
| Cursor cursor = queryAllDataInAccount(); |
| |
| assertThat(cursor, ContactsMatchers.hasCount(1)); |
| assertThat(cursor, hasPhone("15095550111")); |
| cursor.close(); |
| } |
| |
| @Test |
| public void ignoresEmptyContacts() throws Exception { |
| final SimContactDao sut = SimContactDao.create(getContext()); |
| |
| // This probably isn't possible but we'll test it to demonstrate expected behavior and |
| // just in case it does occur |
| sut.importContacts(Arrays.asList( |
| new SimContact(1, null, null, null), |
| new SimContact(2, null, null, null), |
| new SimContact(3, null, null, null), |
| new SimContact(4, "Not null", null, null) |
| ), mAccount); |
| |
| final Cursor contactsCursor = queryAllRawContactsInAccount(); |
| assertThat(contactsCursor, ContactsMatchers.hasCount(1)); |
| contactsCursor.close(); |
| |
| final Cursor dataCursor = queryAllDataInAccount(); |
| assertThat(dataCursor, ContactsMatchers.hasCount(1)); |
| |
| dataCursor.close(); |
| } |
| |
| /** |
| * Tests importing a large number of contacts |
| * |
| * Make sure that {@link android.os.TransactionTooLargeException} is not thrown |
| */ |
| @Test |
| public void largeImport() throws Exception { |
| final SimContactDao sut = SimContactDao.create(getContext()); |
| |
| final List<SimContact> contacts = new ArrayList<>(); |
| |
| for (int i = 0; i < MAX_SIM_CONTACTS; i++) { |
| contacts.add(new SimContact(i + 1, "Contact " + (i + 1), randomPhone(), |
| new String[] { randomEmail("contact" + (i + 1) + "_")})); |
| } |
| |
| sut.importContacts(contacts, mAccount); |
| |
| final Cursor contactsCursor = queryAllRawContactsInAccount(); |
| assertThat(contactsCursor, ContactsMatchers.hasCount(MAX_SIM_CONTACTS)); |
| contactsCursor.close(); |
| |
| final Cursor dataCursor = queryAllDataInAccount(); |
| // Each contact has one data row for each of name, phone and email |
| assertThat(dataCursor, ContactsMatchers.hasCount(MAX_SIM_CONTACTS * 3)); |
| |
| dataCursor.close(); |
| } |
| |
| private Cursor queryAllRawContactsInAccount() { |
| return new StringableCursor(mResolver.query(ContactsContract.RawContacts.CONTENT_URI, |
| null, ContactsContract.RawContacts.ACCOUNT_NAME + "=? AND " + |
| ContactsContract.RawContacts.ACCOUNT_TYPE+ "=?", |
| new String[] { |
| mAccount.name, |
| mAccount.type |
| }, null)); |
| } |
| |
| private Cursor queryAllDataInAccount() { |
| return new StringableCursor(mResolver.query(Data.CONTENT_URI, null, |
| ContactsContract.RawContacts.ACCOUNT_NAME + "=? AND " + |
| ContactsContract.RawContacts.ACCOUNT_TYPE+ "=?", |
| new String[] { |
| mAccount.name, |
| mAccount.type |
| }, null)); |
| } |
| |
| private Cursor queryContactWithName(String name) { |
| return new StringableCursor(mResolver.query(Data.CONTENT_URI, null, |
| ContactsContract.RawContacts.ACCOUNT_NAME + "=? AND " + |
| ContactsContract.RawContacts.ACCOUNT_TYPE+ "=? AND " + |
| Data.DISPLAY_NAME + "=?", |
| new String[] { |
| mAccount.name, |
| mAccount.type, |
| name |
| }, null)); |
| } |
| } |
| |
| /** |
| * Tests for {@link SimContactDao#findAccountsOfExistingSimContacts(List)} |
| * |
| * These are integration tests that query CP2 so that the SQL will be validated in addition |
| * to the detection algorithm |
| */ |
| @SdkSuppress(minSdkVersion = VERSION_CODES.M) |
| // Lollipop MR1 is required for removeAccountExplicitly |
| @RequiresApi(api = VERSION_CODES.LOLLIPOP_MR1) |
| @LargeTest |
| @RunWith(AndroidJUnit4.class) |
| public static class FindAccountsIntegrationTests { |
| |
| private Context mContext; |
| private AccountsTestHelper mAccountHelper; |
| private List<AccountWithDataSet> mAccounts; |
| // We need to generate something distinct to prevent flakiness on devices that may not |
| // start with an empty CP2 DB |
| private String mNameSuffix; |
| |
| private static AccountWithDataSet sSeedAccount; |
| |
| @BeforeClass |
| public static void setUpClass() throws Exception { |
| final AccountsTestHelper helper = new AccountsTestHelper( |
| InstrumentationRegistry.getContext()); |
| sSeedAccount = helper.addTestAccount(helper.generateAccountName("seedAccount")); |
| |
| seedCp2(); |
| } |
| |
| @AfterClass |
| public static void tearDownClass() { |
| final AccountsTestHelper helper = new AccountsTestHelper( |
| InstrumentationRegistry.getContext()); |
| helper.removeTestAccount(sSeedAccount); |
| sSeedAccount = null; |
| } |
| |
| @Before |
| public void setUp() throws Exception { |
| mContext = InstrumentationRegistry.getTargetContext(); |
| mAccountHelper = new AccountsTestHelper(InstrumentationRegistry.getContext()); |
| mAccounts = new ArrayList<>(); |
| mNameSuffix = getClass().getSimpleName() + "At" + System.nanoTime(); |
| |
| seedCp2(); |
| } |
| |
| @After |
| public void tearDown() { |
| for (AccountWithDataSet account : mAccounts) { |
| mAccountHelper.removeTestAccount(account); |
| } |
| } |
| |
| @Test |
| public void returnsEmptyMapWhenNoMatchingContactsExist() { |
| mAccounts.add(mAccountHelper.addTestAccount()); |
| |
| final SimContactDao sut = createDao(); |
| |
| final List<SimContact> contacts = Arrays.asList( |
| new SimContact(1, "Name 1 " + mNameSuffix, "5550101"), |
| new SimContact(2, "Name 2 " + mNameSuffix, "5550102"), |
| new SimContact(3, "Name 3 " + mNameSuffix, "5550103"), |
| new SimContact(4, "Name 4 " + mNameSuffix, "5550104")); |
| |
| final Map<AccountWithDataSet, Set<SimContact>> existing = sut |
| .findAccountsOfExistingSimContacts(contacts); |
| |
| assertTrue(existing.isEmpty()); |
| } |
| |
| @Test |
| public void hasAccountWithMatchingContactsWhenSingleMatchingContactExists() |
| throws Exception { |
| final SimContactDao sut = createDao(); |
| |
| final AccountWithDataSet account = mAccountHelper.addTestAccount( |
| mAccountHelper.generateAccountName("primary_")); |
| mAccounts.add(account); |
| |
| final SimContact existing1 = |
| new SimContact(2, "Exists 2 " + mNameSuffix, "5550102"); |
| final SimContact existing2 = |
| new SimContact(4, "Exists 4 " + mNameSuffix, "5550104"); |
| |
| final List<SimContact> contacts = Arrays.asList( |
| new SimContact(1, "Missing 1 " + mNameSuffix, "5550101"), |
| new SimContact(existing1), |
| new SimContact(3, "Missing 3 " + mNameSuffix, "5550103"), |
| new SimContact(existing2)); |
| |
| sut.importContacts(Arrays.asList( |
| new SimContact(existing1), |
| new SimContact(existing2) |
| ), account); |
| |
| |
| final Map<AccountWithDataSet, Set<SimContact>> existing = sut |
| .findAccountsOfExistingSimContacts(contacts); |
| |
| assertThat(existing.size(), equalTo(1)); |
| assertThat(existing.get(account), |
| Matchers.<Set<SimContact>>equalTo(ImmutableSet.of(existing1, existing2))); |
| } |
| |
| @Test |
| public void hasMultipleAccountsWhenMultipleMatchingContactsExist() throws Exception { |
| final SimContactDao sut = createDao(); |
| |
| final AccountWithDataSet account1 = mAccountHelper.addTestAccount( |
| mAccountHelper.generateAccountName("account1_")); |
| mAccounts.add(account1); |
| final AccountWithDataSet account2 = mAccountHelper.addTestAccount( |
| mAccountHelper.generateAccountName("account2_")); |
| mAccounts.add(account2); |
| |
| final SimContact existsInBoth = |
| new SimContact(2, "Exists Both " + mNameSuffix, "5550102"); |
| final SimContact existsInAccount1 = |
| new SimContact(4, "Exists 1 " + mNameSuffix, "5550104"); |
| final SimContact existsInAccount2 = |
| new SimContact(5, "Exists 2 " + mNameSuffix, "5550105"); |
| |
| final List<SimContact> contacts = Arrays.asList( |
| new SimContact(1, "Missing 1 " + mNameSuffix, "5550101"), |
| new SimContact(existsInBoth), |
| new SimContact(3, "Missing 3 " + mNameSuffix, "5550103"), |
| new SimContact(existsInAccount1), |
| new SimContact(existsInAccount2)); |
| |
| sut.importContacts(Arrays.asList( |
| new SimContact(existsInBoth), |
| new SimContact(existsInAccount1) |
| ), account1); |
| |
| sut.importContacts(Arrays.asList( |
| new SimContact(existsInBoth), |
| new SimContact(existsInAccount2) |
| ), account2); |
| |
| |
| final Map<AccountWithDataSet, Set<SimContact>> existing = sut |
| .findAccountsOfExistingSimContacts(contacts); |
| |
| assertThat(existing.size(), equalTo(2)); |
| assertThat(existing, Matchers.<Map<AccountWithDataSet, Set<SimContact>>>equalTo( |
| ImmutableMap.<AccountWithDataSet, Set<SimContact>>of( |
| account1, ImmutableSet.of(existsInBoth, existsInAccount1), |
| account2, ImmutableSet.of(existsInBoth, existsInAccount2)))); |
| } |
| |
| @Test |
| public void matchesByNameIfSimContactHasNoPhone() throws Exception { |
| final SimContactDao sut = createDao(); |
| |
| final AccountWithDataSet account = mAccountHelper.addTestAccount( |
| mAccountHelper.generateAccountName("account_")); |
| mAccounts.add(account); |
| |
| final SimContact noPhone = new SimContact(1, "Nophone " + mNameSuffix, null); |
| final SimContact otherExisting = new SimContact( |
| 5, "Exists 1 " + mNameSuffix, "5550105"); |
| |
| final List<SimContact> contacts = Arrays.asList( |
| new SimContact(noPhone), |
| new SimContact(2, "Name 2 " + mNameSuffix, "5550102"), |
| new SimContact(3, "Name 3 " + mNameSuffix, "5550103"), |
| new SimContact(4, "Name 4 " + mNameSuffix, "5550104"), |
| new SimContact(otherExisting)); |
| |
| sut.importContacts(Arrays.asList( |
| new SimContact(noPhone), |
| new SimContact(otherExisting) |
| ), account); |
| |
| final Map<AccountWithDataSet, Set<SimContact>> existing = sut |
| .findAccountsOfExistingSimContacts(contacts); |
| |
| assertThat(existing.size(), equalTo(1)); |
| assertThat(existing.get(account), Matchers.<Set<SimContact>>equalTo( |
| ImmutableSet.of(noPhone, otherExisting))); |
| } |
| |
| @Test |
| public void largeNumberOfSimContacts() throws Exception { |
| final SimContactDao sut = createDao(); |
| |
| final List<SimContact> contacts = new ArrayList<>(); |
| for (int i = 0; i < MAX_SIM_CONTACTS; i++) { |
| contacts.add(new SimContact( |
| i + 1, "Contact " + (i + 1) + " " + mNameSuffix, randomPhone())); |
| } |
| // The work has to be split into batches to avoid hitting SQL query parameter limits |
| // so test contacts that will be at boundary points |
| final SimContact imported1 = contacts.get(0); |
| final SimContact imported2 = contacts.get(99); |
| final SimContact imported3 = contacts.get(100); |
| final SimContact imported4 = contacts.get(101); |
| final SimContact imported5 = contacts.get(MAX_SIM_CONTACTS - 1); |
| |
| final AccountWithDataSet account = mAccountHelper.addTestAccount( |
| mAccountHelper.generateAccountName("account_")); |
| mAccounts.add(account); |
| |
| sut.importContacts(Arrays.asList(imported1, imported2, imported3, imported4, imported5), |
| account); |
| |
| mAccounts.add(account); |
| |
| final Map<AccountWithDataSet, Set<SimContact>> existing = sut |
| .findAccountsOfExistingSimContacts(contacts); |
| |
| assertThat(existing.size(), equalTo(1)); |
| assertThat(existing.get(account), Matchers.<Set<SimContact>>equalTo( |
| ImmutableSet.of(imported1, imported2, imported3, imported4, imported5))); |
| |
| } |
| |
| private SimContactDao createDao() { |
| return SimContactDao.create(mContext); |
| } |
| |
| /** |
| * Adds a bunch of random contact data to CP2 to make the test environment more realistic |
| */ |
| private static void seedCp2() throws RemoteException, OperationApplicationException { |
| |
| final ArrayList<ContentProviderOperation> ops = new ArrayList<>(); |
| |
| appendCreateContact("John Smith", sSeedAccount, ops); |
| appendCreateContact("Marcus Seed", sSeedAccount, ops); |
| appendCreateContact("Gary Seed", sSeedAccount, ops); |
| appendCreateContact("Michael Seed", sSeedAccount, ops); |
| appendCreateContact("Isaac Seed", sSeedAccount, ops); |
| appendCreateContact("Sean Seed", sSeedAccount, ops); |
| appendCreateContact("Nate Seed", sSeedAccount, ops); |
| appendCreateContact("Andrey Seed", sSeedAccount, ops); |
| appendCreateContact("Cody Seed", sSeedAccount, ops); |
| appendCreateContact("John Seed", sSeedAccount, ops); |
| appendCreateContact("Alex Seed", sSeedAccount, ops); |
| |
| InstrumentationRegistry.getTargetContext() |
| .getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops); |
| } |
| |
| private static void appendCreateContact(String name, AccountWithDataSet account, |
| ArrayList<ContentProviderOperation> ops) { |
| final int emailCount = sRandom.nextInt(10); |
| final int phoneCount = sRandom.nextInt(5); |
| |
| final List<String> phones = new ArrayList<>(); |
| for (int i = 0; i < phoneCount; i++) { |
| phones.add(randomPhone()); |
| } |
| final List<String> emails = new ArrayList<>(); |
| for (int i = 0; i < emailCount; i++) { |
| emails.add(randomEmail(name)); |
| } |
| appendCreateContact(name, phones, emails, account, ops); |
| } |
| |
| |
| private static void appendCreateContact(String name, List<String> phoneNumbers, |
| List<String> emails, AccountWithDataSet account, List<ContentProviderOperation> ops) { |
| int index = ops.size(); |
| |
| ops.add(account.newRawContactOperation()); |
| ops.add(insertIntoData(name, StructuredName.CONTENT_ITEM_TYPE, index)); |
| for (String phone : phoneNumbers) { |
| ops.add(insertIntoData(phone, Phone.CONTENT_ITEM_TYPE, Phone.TYPE_MOBILE, index)); |
| } |
| for (String email : emails) { |
| ops.add(insertIntoData(email, Email.CONTENT_ITEM_TYPE, Email.TYPE_HOME, index)); |
| } |
| } |
| |
| private static ContentProviderOperation insertIntoData(String value, String mimeType, |
| int idBackReference) { |
| return ContentProviderOperation.newInsert(Data.CONTENT_URI) |
| .withValue(Data.DATA1, value) |
| .withValue(Data.MIMETYPE, mimeType) |
| .withValueBackReference(Data.RAW_CONTACT_ID, idBackReference).build(); |
| } |
| |
| private static ContentProviderOperation insertIntoData(String value, String mimeType, |
| int type, int idBackReference) { |
| return ContentProviderOperation.newInsert(Data.CONTENT_URI) |
| .withValue(Data.DATA1, value) |
| .withValue(ContactsContract.Data.DATA2, type) |
| .withValue(Data.MIMETYPE, mimeType) |
| .withValueBackReference(Data.RAW_CONTACT_ID, idBackReference).build(); |
| } |
| } |
| |
| /** |
| * Tests for {@link SimContactDao#loadContactsForSim(SimCard)} |
| * |
| * These are unit tests that verify that {@link SimContact}s are created correctly from |
| * the cursors that are returned by queries to the IccProvider |
| */ |
| @SmallTest |
| @RunWith(AndroidJUnit4.class) |
| public static class LoadContactsUnitTests { |
| |
| private MockContentProvider mMockIccProvider; |
| private Context mContext; |
| |
| @Before |
| public void setUp() { |
| mContext = mock(MockContext.class); |
| final MockContentResolver mockResolver = new MockContentResolver(); |
| mMockIccProvider = new MockContentProvider(); |
| mockResolver.addProvider("icc", mMockIccProvider); |
| when(mContext.getContentResolver()).thenReturn(mockResolver); |
| } |
| |
| |
| @Test |
| public void createsContactsFromCursor() { |
| mMockIccProvider.expect(MockContentProvider.Query.forAnyUri()) |
| .withDefaultProjection( |
| SimContactDaoImpl._ID, SimContactDaoImpl.NAME, |
| SimContactDaoImpl.NUMBER, SimContactDaoImpl.EMAILS) |
| .withAnyProjection() |
| .withAnySelection() |
| .withAnySortOrder() |
| .returnRow(1, "Name One", "5550101", null) |
| .returnRow(2, "Name Two", "5550102", null) |
| .returnRow(3, "Name Three", null, null) |
| .returnRow(4, null, "5550104", null) |
| .returnRow(5, "Name Five", "5550105", |
| "five@example.com,nf@example.com,name.five@example.com") |
| .returnRow(6, "Name Six", "5550106", "thesix@example.com"); |
| |
| final SimContactDao sut = SimContactDao.create(mContext); |
| final List<SimContact> contacts = sut |
| .loadContactsForSim(new SimCard("123", "carrier", "sim", null, "us")); |
| |
| assertThat(contacts, equalTo( |
| Arrays.asList( |
| new SimContact(1, "Name One", "5550101", null), |
| new SimContact(2, "Name Two", "5550102", null), |
| new SimContact(3, "Name Three", null, null), |
| new SimContact(4, null, "5550104", null), |
| new SimContact(5, "Name Five", "5550105", new String[] { |
| "five@example.com", "nf@example.com", "name.five@example.com" |
| }), |
| new SimContact(6, "Name Six", "5550106", new String[] { |
| "thesix@example.com" |
| }) |
| ))); |
| } |
| |
| @Test |
| public void excludesEmptyContactsFromResult() { |
| mMockIccProvider.expect(MockContentProvider.Query.forAnyUri()) |
| .withDefaultProjection( |
| SimContactDaoImpl._ID, SimContactDaoImpl.NAME, |
| SimContactDaoImpl.NUMBER, SimContactDaoImpl.EMAILS) |
| .withAnyProjection() |
| .withAnySelection() |
| .withAnySortOrder() |
| .returnRow(1, "Non Empty1", "5550101", null) |
| .returnRow(2, "", "", "") |
| .returnRow(3, "Non Empty2", null, null) |
| .returnRow(4, null, null, null) |
| .returnRow(5, "", null, null) |
| .returnRow(6, null, "5550102", null) |
| .returnRow(7, null, null, "user@example.com"); |
| |
| final SimContactDao sut = SimContactDao.create(mContext); |
| final List<SimContact> contacts = sut |
| .loadContactsForSim(new SimCard("123", "carrier", "sim", null, "us")); |
| |
| assertThat(contacts, equalTo( |
| Arrays.asList( |
| new SimContact(1, "Non Empty1", "5550101", null), |
| new SimContact(3, "Non Empty2", null, null), |
| new SimContact(6, null, "5550102", null), |
| new SimContact(7, null, null, new String[] { "user@example.com" }) |
| ))); |
| } |
| |
| @Test |
| public void usesSimCardSubscriptionIdIfAvailable() { |
| mMockIccProvider.expectQuery(SimContactDaoImpl.ICC_CONTENT_URI.buildUpon() |
| .appendPath("subId").appendPath("2").build()) |
| .withDefaultProjection( |
| SimContactDaoImpl._ID, SimContactDaoImpl.NAME, |
| SimContactDaoImpl.NUMBER, SimContactDaoImpl.EMAILS) |
| .withAnyProjection() |
| .withAnySelection() |
| .withAnySortOrder() |
| .returnEmptyCursor(); |
| |
| final SimContactDao sut = SimContactDao.create(mContext); |
| sut.loadContactsForSim(new SimCard("123", 2, "carrier", "sim", null, "us")); |
| mMockIccProvider.verify(); |
| } |
| |
| @Test |
| public void omitsSimCardSubscriptionIdIfUnavailable() { |
| mMockIccProvider.expectQuery(SimContactDaoImpl.ICC_CONTENT_URI) |
| .withDefaultProjection( |
| SimContactDaoImpl._ID, SimContactDaoImpl.NAME, |
| SimContactDaoImpl.NUMBER, SimContactDaoImpl.EMAILS) |
| .withAnyProjection() |
| .withAnySelection() |
| .withAnySortOrder() |
| .returnEmptyCursor(); |
| |
| final SimContactDao sut = SimContactDao.create(mContext); |
| sut.loadContactsForSim(new SimCard("123", SimCard.NO_SUBSCRIPTION_ID, |
| "carrier", "sim", null, "us")); |
| mMockIccProvider.verify(); |
| } |
| |
| @Test |
| public void returnsEmptyListForEmptyCursor() { |
| mMockIccProvider.expect(MockContentProvider.Query.forAnyUri()) |
| .withDefaultProjection( |
| SimContactDaoImpl._ID, SimContactDaoImpl.NAME, |
| SimContactDaoImpl.NUMBER, SimContactDaoImpl.EMAILS) |
| .withAnyProjection() |
| .withAnySelection() |
| .withAnySortOrder() |
| .returnEmptyCursor(); |
| |
| final SimContactDao sut = SimContactDao.create(mContext); |
| List<SimContact> result = sut |
| .loadContactsForSim(new SimCard("123", "carrier", "sim", null, "us")); |
| assertTrue(result.isEmpty()); |
| } |
| } |
| |
| @LargeTest |
| // suppressed because failed assumptions are reported as test failures by the build server |
| @Suppress |
| @RunWith(AndroidJUnit4.class) |
| public static class LoadContactsIntegrationTest { |
| private SimContactsTestHelper mSimTestHelper; |
| private ArrayList<ContentProviderOperation> mSimSnapshot; |
| |
| @Before |
| public void setUp() throws Exception { |
| mSimTestHelper = new SimContactsTestHelper(); |
| |
| mSimTestHelper.assumeSimWritable(); |
| if (!mSimTestHelper.isSimWritable()) return; |
| |
| mSimSnapshot = mSimTestHelper.captureRestoreSnapshot(); |
| mSimTestHelper.deleteAllSimContacts(); |
| } |
| |
| @After |
| public void tearDown() throws Exception { |
| mSimTestHelper.restore(mSimSnapshot); |
| } |
| |
| @Test |
| public void readFromSim() { |
| mSimTestHelper.addSimContact("Test Simone", "15095550101"); |
| mSimTestHelper.addSimContact("Test Simtwo", "15095550102"); |
| mSimTestHelper.addSimContact("Test Simthree", "15095550103"); |
| |
| final SimContactDao sut = SimContactDao.create(getContext()); |
| final SimCard sim = sut.getSimCards().get(0); |
| final ArrayList<SimContact> contacts = sut.loadContactsForSim(sim); |
| |
| assertThat(contacts.get(0), isSimContactWithNameAndPhone("Test Simone", "15095550101")); |
| assertThat(contacts.get(1), isSimContactWithNameAndPhone("Test Simtwo", "15095550102")); |
| assertThat(contacts.get(2), |
| isSimContactWithNameAndPhone("Test Simthree", "15095550103")); |
| } |
| } |
| |
| private static String randomPhone() { |
| return String.format(Locale.US, "1%s55501%02d", |
| AREA_CODES[sRandom.nextInt(AREA_CODES.length)], |
| sRandom.nextInt(100)); |
| } |
| |
| private static String randomEmail(String name) { |
| return String.format("%s%d@example.com", name.replace(" ", ".").toLowerCase(Locale.US), |
| 1000 + sRandom.nextInt(1000)); |
| } |
| |
| |
| static Context getContext() { |
| return InstrumentationRegistry.getTargetContext(); |
| } |
| } |