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
17package com.android.messaging.datamodel;
18
19import android.content.ContentProvider;
20import android.content.ContentValues;
21import android.database.Cursor;
22import android.net.Uri;
23import android.test.suitebuilder.annotation.SmallTest;
24import android.text.TextUtils;
25
26import com.android.messaging.BugleTestCase;
27import com.android.messaging.FakeContentProvider;
28import com.android.messaging.FakeContext;
29import com.android.messaging.FakeFactory;
30import com.android.messaging.datamodel.DatabaseHelper.ParticipantColumns;
31import com.android.messaging.datamodel.data.ParticipantData;
32import com.android.messaging.datamodel.data.ParticipantData.ParticipantsQuery;
33import com.android.messaging.util.ContactUtil;
34
35import org.junit.Assert;
36
37/**
38 * Utility class for testing ParticipantRefresh class for different scenarios.
39 */
40@SmallTest
41public class ParticipantRefreshTest extends BugleTestCase {
42    private FakeContext mContext;
43    FakeFactory mFakeFactory;
44
45    @Override
46    public void setUp() throws Exception {
47        super.setUp();
48
49        mContext = new FakeContext(getTestContext());
50
51        final ContentProvider provider = new MessagingContentProvider();
52        provider.attachInfo(mContext, null);
53        mContext.addContentProvider(MessagingContentProvider.AUTHORITY, provider);
54
55        final FakeDataModel fakeDataModel = new FakeDataModel(mContext);
56        mFakeFactory = FakeFactory.registerWithFakeContext(getTestContext(), mContext)
57                .withDataModel(fakeDataModel);
58    }
59
60    /**
61     * Add some phonelookup result into take PhoneLookup content provider. This will be
62     * used for doing phone lookup during participant refresh.
63     */
64    private void addPhoneLookup(final String phone, final Object[][] lookupResult) {
65        final Uri uri = ContactUtil.lookupPhone(mContext, phone).getUri();
66        final FakeContentProvider phoneLookup = new FakeContentProvider(mContext,
67                uri, false);
68        phoneLookup.addOverrideData(uri, null, null, ContactUtil.PhoneLookupQuery.PROJECTION,
69                lookupResult);
70        mFakeFactory.withProvider(uri, phoneLookup);
71    }
72
73    /**
74     * Add some participant to test database.
75     */
76    private void addParticipant(final String normalizedDestination, final long contactId,
77            final String name, final String photoUrl) {
78        final DatabaseWrapper db = DataModel.get().getDatabase();
79        final ContentValues values = new ContentValues();
80
81        values.put(ParticipantColumns.NORMALIZED_DESTINATION, normalizedDestination);
82        values.put(ParticipantColumns.CONTACT_ID, contactId);
83        values.put(ParticipantColumns.FULL_NAME, name);
84        values.put(ParticipantColumns.PROFILE_PHOTO_URI, photoUrl);
85
86        db.beginTransaction();
87        try {
88            db.insert(DatabaseHelper.PARTICIPANTS_TABLE, null, values);
89            db.setTransactionSuccessful();
90        } finally {
91            db.endTransaction();
92        }
93    }
94
95    /**
96     * Verify that participant in the database has expected contacdtId, name and photoUrl fields.
97     */
98    private void verifyParticipant(final String normalizedDestination, final long contactId,
99            final String name, final String photoUrl) {
100        final DatabaseWrapper db = DataModel.get().getDatabase();
101        db.beginTransaction();
102        try {
103            final String selection = ParticipantColumns.NORMALIZED_DESTINATION + "=?";
104            final String[] selectionArgs = new String[] { normalizedDestination };
105
106            final Cursor cursor = db.query(DatabaseHelper.PARTICIPANTS_TABLE,
107                    ParticipantsQuery.PROJECTION, selection, selectionArgs, null, null, null);
108
109            if (cursor == null || cursor.getCount() != 1) {
110                Assert.fail("Should have participants for:" + normalizedDestination);
111                return;
112            }
113
114            cursor.moveToFirst();
115            final int currentContactId = cursor.getInt(ParticipantsQuery.INDEX_CONTACT_ID);
116            final String currentName = cursor.getString(ParticipantsQuery.INDEX_FULL_NAME);
117            final String currentPhotoUrl =
118                    cursor.getString(ParticipantsQuery.INDEX_PROFILE_PHOTO_URI);
119            if (currentContactId != contactId) {
120                Assert.fail("Contact Id doesn't match. normalizedNumber=" + normalizedDestination +
121                        " expected=" + contactId + " actual=" + currentContactId);
122                return;
123            }
124
125            if (!TextUtils.equals(currentName, name)) {
126                Assert.fail("Name doesn't match. normalizedNumber=" + normalizedDestination +
127                        " expected=" + name + " actual=" + currentName);
128                return;
129            }
130
131            if (!TextUtils.equals(currentPhotoUrl, photoUrl)) {
132                Assert.fail("Contact Id doesn't match. normalizedNumber=" + normalizedDestination +
133                        " expected=" + photoUrl + " actual=" + currentPhotoUrl);
134                return;
135            }
136
137            db.setTransactionSuccessful();
138        } finally {
139            db.endTransaction();
140        }
141    }
142
143    /**
144     * Verify that incremental refresh will resolve previously not resolved participants.
145     */
146    public void testIncrementalRefreshNotResolvedSingleMatch() {
147        addParticipant("650-123-1233", ParticipantData.PARTICIPANT_CONTACT_ID_NOT_RESOLVED,
148                null, null);
149        addPhoneLookup("650-123-1233", new Object[][] {
150                { 1L, "John", "content://photo/john", "650-123-1233", null, null, null }
151        });
152
153        ParticipantRefresh.refreshParticipants(ParticipantRefresh.REFRESH_MODE_INCREMENTAL);
154        verifyParticipant("650-123-1233", 1, "John", "content://photo/john");
155    }
156
157    /**
158     * Verify that incremental refresh will resolve previously not resolved participants.
159     */
160    public void testIncrementalRefreshNotResolvedMultiMatch() {
161        addParticipant("650-123-1233", ParticipantData.PARTICIPANT_CONTACT_ID_NOT_RESOLVED,
162                null, null);
163        addPhoneLookup("650-123-1233", new Object[][] {
164                { 1L, "John", "content://photo/john", "650-123-1233", null, null, null },
165                { 2L, "Joe", "content://photo/joe", "650-123-1233", null, null, null }
166        });
167
168        ParticipantRefresh.refreshParticipants(ParticipantRefresh.REFRESH_MODE_INCREMENTAL);
169        verifyParticipant("650-123-1233", 1, "John", "content://photo/john");
170    }
171
172    /**
173     * Verify that incremental refresh will not touch already-resolved participants.
174     */
175    public void testIncrementalRefreshResolvedSingleMatch() {
176        addParticipant("650-123-1233", 1, "Joh", "content://photo/joh");
177        addPhoneLookup("650-123-1233", new Object[][] {
178                { 1L, "John", "content://photo/john", "650-123-1233", null, null, null }
179        });
180
181        ParticipantRefresh.refreshParticipants(ParticipantRefresh.REFRESH_MODE_INCREMENTAL);
182        verifyParticipant("650-123-1233", 1, "Joh", "content://photo/joh");
183    }
184
185    /**
186     * Verify that full refresh will correct already-resolved participants if needed
187     */
188    public void testFullRefreshResolvedSingleMatch() {
189        addParticipant("650-123-1233", 1, "Joh", "content://photo/joh");
190        addPhoneLookup("650-123-1233", new Object[][] {
191                { 1L, "John", "content://photo/john", "650-123-1233", null, null, null }
192        });
193
194        ParticipantRefresh.refreshParticipants(ParticipantRefresh.REFRESH_MODE_FULL);
195        verifyParticipant("650-123-1233", 1, "John", "content://photo/john");
196    }
197
198    /**
199     * Verify that incremental refresh will not touch participant that is marked as not found.
200     */
201    public void testIncrementalRefreshNotFound() {
202        addParticipant("650-123-1233", ParticipantData.PARTICIPANT_CONTACT_ID_NOT_FOUND,
203                null, null);
204        addPhoneLookup("650-123-1233", new Object[][] {
205                { 1L, "John", "content://photo/john", "650-123-1233", null, null, null }
206        });
207
208        ParticipantRefresh.refreshParticipants(ParticipantRefresh.REFRESH_MODE_INCREMENTAL);
209        verifyParticipant("650-123-1233", ParticipantData.PARTICIPANT_CONTACT_ID_NOT_FOUND,
210                null, null);
211    }
212
213    /**
214     * Verify that full refresh will resolve participant that is marked as not found.
215     */
216    public void testFullRefreshNotFound() {
217        addParticipant("650-123-1233", ParticipantData.PARTICIPANT_CONTACT_ID_NOT_FOUND,
218                null, null);
219        addPhoneLookup("650-123-1233", new Object[][] {
220                { 1L, "John", "content://photo/john", "650-123-1233", null, null, null }
221        });
222
223        ParticipantRefresh.refreshParticipants(ParticipantRefresh.REFRESH_MODE_FULL);
224        verifyParticipant("650-123-1233", 1, "John", "content://photo/john");
225    }
226
227    /**
228     * Verify that refresh take consideration of current contact_id when having multiple matches.
229     */
230    public void testFullRefreshResolvedMultiMatch1() {
231        addParticipant("650-123-1233", 1, "Joh", "content://photo/joh");
232        addPhoneLookup("650-123-1233", new Object[][] {
233                { 1L, "John", "content://photo/john", "650-123-1233", null, null, null },
234                { 2L, "Joe", "content://photo/joe", "650-123-1233", null, null, null }
235        });
236
237        ParticipantRefresh.refreshParticipants(ParticipantRefresh.REFRESH_MODE_FULL);
238        verifyParticipant("650-123-1233", 1, "John", "content://photo/john");
239    }
240
241    /**
242     * Verify that refresh take consideration of current contact_id when having multiple matches.
243     */
244    public void testFullRefreshResolvedMultiMatch2() {
245        addParticipant("650-123-1233", 2, "Joh", "content://photo/joh");
246        addPhoneLookup("650-123-1233", new Object[][] {
247                { 1L, "John", "content://photo/john", "650-123-1233", null, null, null },
248                { 2L, "Joe", "content://photo/joe", "650-123-1233", null, null, null }
249        });
250
251        ParticipantRefresh.refreshParticipants(ParticipantRefresh.REFRESH_MODE_FULL);
252        verifyParticipant("650-123-1233", 2, "Joe", "content://photo/joe");
253    }
254
255    /**
256     * Verify that refresh take first contact in case current contact_id no longer matches.
257     */
258    public void testFullRefreshResolvedMultiMatch3() {
259        addParticipant("650-123-1233", 3, "Joh", "content://photo/joh");
260        addPhoneLookup("650-123-1233", new Object[][] {
261                { 1L, "John", "content://photo/john", "650-123-1233", null, null, null },
262                { 2L, "Joe", "content://photo/joe", "650-123-1233", null, null, null }
263        });
264
265        ParticipantRefresh.refreshParticipants(ParticipantRefresh.REFRESH_MODE_FULL);
266        verifyParticipant("650-123-1233", 1, "John", "content://photo/john");
267    }
268
269    /**
270     * Verify that refresh take first contact in case current contact_id no longer matches.
271     */
272    public void testFullRefreshResolvedBeforeButNotFoundNow() {
273        addParticipant("650-123-1233", 3, "Joh", "content://photo/joh");
274        addPhoneLookup("650-123-1233", new Object[][] {});
275
276        ParticipantRefresh.refreshParticipants(ParticipantRefresh.REFRESH_MODE_FULL);
277        verifyParticipant("650-123-1233", ParticipantData.PARTICIPANT_CONTACT_ID_NOT_FOUND,
278                null, null);
279    }
280}
281