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