1/* 2 * Copyright (C) 2016 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 */ 16package com.android.contacts.model; 17 18import android.content.ContentProvider; 19import android.content.ContentResolver; 20import android.database.Cursor; 21import android.database.MatrixCursor; 22import android.net.Uri; 23import android.os.CancellationSignal; 24import android.provider.ContactsContract; 25import android.provider.ContactsContract.RawContacts; 26import android.support.annotation.Nullable; 27import android.test.AndroidTestCase; 28import android.test.mock.MockContentResolver; 29import android.test.suitebuilder.annotation.SmallTest; 30 31import com.android.contacts.test.mocks.MockContentProvider; 32import com.android.contacts.tests.FakeDeviceAccountTypeFactory; 33import com.android.contacts.util.DeviceLocalAccountTypeFactory; 34 35import java.util.Arrays; 36import java.util.Collections; 37import java.util.HashMap; 38import java.util.HashSet; 39import java.util.List; 40import java.util.Map; 41 42@SmallTest 43public class Cp2DeviceLocalAccountLocatorTests extends AndroidTestCase { 44 45 // Basic smoke test that just checks that it doesn't throw when loading from CP2. We don't 46 // care what CP2 actually contains for this. 47 public void testShouldNotCrash() { 48 final DeviceLocalAccountLocator sut = new Cp2DeviceLocalAccountLocator( 49 getContext().getContentResolver(), 50 new DeviceLocalAccountTypeFactory.Default(getContext()), 51 Collections.<String>emptySet()); 52 sut.getDeviceLocalAccounts(); 53 // We didn't throw so it passed 54 } 55 56 public void test_getDeviceLocalAccounts_returnsEmptyListWhenQueryReturnsNull() { 57 final DeviceLocalAccountLocator sut = createWithQueryResult(null); 58 assertTrue(sut.getDeviceLocalAccounts().isEmpty()); 59 } 60 61 public void test_getDeviceLocalAccounts_returnsEmptyListWhenNoRawContactsHaveDeviceType() { 62 final DeviceLocalAccountLocator sut = createWithQueryResult(queryResult( 63 "user", "com.example", 64 "user", "com.example", 65 "user", "com.example")); 66 assertTrue(sut.getDeviceLocalAccounts().isEmpty()); 67 } 68 69 public void test_getDeviceLocalAccounts_returnsListWithItemForNullAccount() { 70 final DeviceLocalAccountLocator sut = createWithQueryResult(queryResult( 71 "user", "com.example", 72 null, null, 73 "user", "com.example", 74 null, null)); 75 76 assertEquals(1, sut.getDeviceLocalAccounts().size()); 77 } 78 79 public void test_getDeviceLocalAccounts_containsItemForEachDeviceAccount() { 80 final DeviceLocalAccountTypeFactory stubFactory = new FakeDeviceAccountTypeFactory() 81 .withDeviceTypes(null, "vnd.sec.contact.phone") 82 .withSimTypes("vnd.sec.contact.sim"); 83 final DeviceLocalAccountLocator sut = createLocator(queryResult( 84 "user", "com.example", 85 "user", "com.example", 86 "phone_account", "vnd.sec.contact.phone", 87 null, null, 88 "phone_account", "vnd.sec.contact.phone", 89 "user", "com.example", 90 null, null, 91 "sim_account", "vnd.sec.contact.sim", 92 "sim_account_2", "vnd.sec.contact.sim" 93 ), stubFactory); 94 95 96 assertEquals(4, sut.getDeviceLocalAccounts().size()); 97 } 98 99 public void test_getDeviceLocalAccounts_doesNotContainItemsForKnownAccountTypes() { 100 final Cp2DeviceLocalAccountLocator sut = new Cp2DeviceLocalAccountLocator( 101 getContext().getContentResolver(), new FakeDeviceAccountTypeFactory(), 102 new HashSet<>(Arrays.asList("com.example", "com.example.1"))); 103 104 assertTrue("Selection should filter known accounts", 105 sut.getSelection().contains("NOT IN (?,?)")); 106 107 final List<String> args = Arrays.asList(sut.getSelectionArgs()); 108 assertEquals(2, args.size()); 109 assertTrue("Selection args is missing an expected value", args.contains("com.example")); 110 assertTrue("Selection args is missing an expected value", args.contains("com.example.1")); 111 } 112 113 public void test_getDeviceLocalAccounts_includesAccountsFromSettings() { 114 final DeviceLocalAccountTypeFactory stubFactory = new FakeDeviceAccountTypeFactory() 115 .withDeviceTypes(null, "vnd.sec.contact.phone") 116 .withSimTypes("vnd.sec.contact.sim"); 117 final DeviceLocalAccountLocator sut = createLocator(new FakeContactsProvider() 118 .withQueryResult(ContactsContract.Settings.CONTENT_URI, queryResult( 119 "phone_account", "vnd.sec.contact.phone", 120 "sim_account", "vnd.sec.contact.sim" 121 )), stubFactory); 122 123 assertEquals(2, sut.getDeviceLocalAccounts().size()); 124 } 125 126 public void test_getDeviceLocalAccounts_includesAccountsFromGroups() { 127 final DeviceLocalAccountTypeFactory stubFactory = new FakeDeviceAccountTypeFactory() 128 .withDeviceTypes(null, "vnd.sec.contact.phone") 129 .withSimTypes("vnd.sec.contact.sim"); 130 final DeviceLocalAccountLocator sut = createLocator(new FakeContactsProvider() 131 .withQueryResult(ContactsContract.Groups.CONTENT_URI, queryResult( 132 "phone_account", "vnd.sec.contact.phone", 133 "sim_account", "vnd.sec.contact.sim" 134 )), stubFactory); 135 136 assertEquals(2, sut.getDeviceLocalAccounts().size()); 137 } 138 139 private DeviceLocalAccountLocator createWithQueryResult( 140 Cursor cursor) { 141 return createLocator(cursor, new DeviceLocalAccountTypeFactory.Default(mContext)); 142 } 143 144 private DeviceLocalAccountLocator createLocator(ContentProvider contactsProvider, 145 DeviceLocalAccountTypeFactory localAccountTypeFactory) { 146 final DeviceLocalAccountLocator locator = new Cp2DeviceLocalAccountLocator( 147 createContentResolverWithProvider(contactsProvider), 148 localAccountTypeFactory, Collections.<String>emptySet()); 149 return locator; 150 } 151 152 private DeviceLocalAccountLocator createLocator(Cursor cursor, 153 DeviceLocalAccountTypeFactory localAccountTypeFactory) { 154 final DeviceLocalAccountLocator locator = new Cp2DeviceLocalAccountLocator( 155 createStubResolverWithContentQueryResult(cursor), 156 localAccountTypeFactory, 157 Collections.<String>emptySet()); 158 return locator; 159 } 160 161 private ContentResolver createContentResolverWithProvider(ContentProvider contactsProvider) { 162 final MockContentResolver resolver = new MockContentResolver(); 163 resolver.addProvider(ContactsContract.AUTHORITY, contactsProvider); 164 return resolver; 165 } 166 167 private ContentResolver createStubResolverWithContentQueryResult(Cursor cursor) { 168 final MockContentResolver resolver = new MockContentResolver(); 169 resolver.addProvider(ContactsContract.AUTHORITY, new FakeContactsProvider() 170 .withDefaultQueryResult(cursor)); 171 return resolver; 172 } 173 174 private Cursor queryResult(String... nameTypePairs) { 175 final MatrixCursor cursor = new MatrixCursor(new String[] 176 { RawContacts.ACCOUNT_NAME, RawContacts.ACCOUNT_TYPE, RawContacts.DATA_SET }); 177 for (int i = 0; i < nameTypePairs.length; i+=2) { 178 cursor.newRow().add(nameTypePairs[i]).add(nameTypePairs[i+1]) 179 .add(null); 180 } 181 return cursor; 182 } 183 184 private static class FakeContactsProvider extends MockContentProvider { 185 public Cursor mNextQueryResult; 186 public Map<Uri, Cursor> mNextResultMapping = new HashMap<>(); 187 188 public FakeContactsProvider() {} 189 190 public FakeContactsProvider withDefaultQueryResult(Cursor cursor) { 191 mNextQueryResult = cursor; 192 return this; 193 } 194 195 public FakeContactsProvider withQueryResult(Uri uri, Cursor cursor) { 196 mNextResultMapping.put(uri, cursor); 197 return this; 198 } 199 200 @Override 201 public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, 202 String sortOrder) { 203 return query(uri, projection, selection, selectionArgs, sortOrder, null); 204 } 205 206 @Nullable 207 @Override 208 public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, 209 String sortOrder, CancellationSignal cancellationSignal) { 210 final Cursor result = mNextResultMapping.get(uri); 211 if (result == null) { 212 return mNextQueryResult; 213 } else { 214 return result; 215 } 216 } 217 } 218} 219