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