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.util.ContactUtil;
34 
35 import org.junit.Assert;
36 
37 /**
38  * Utility class for testing ParticipantRefresh class for different scenarios.
39  */
40 @SmallTest
41 public class ParticipantRefreshTest extends BugleTestCase {
42     private FakeContext mContext;
43     FakeFactory mFakeFactory;
44 
45     @Override
setUp()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      */
addPhoneLookup(final String phone, final Object[][] lookupResult)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      */
addParticipant(final String normalizedDestination, final long contactId, final String name, final String photoUrl)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      */
verifyParticipant(final String normalizedDestination, final long contactId, final String name, final String photoUrl)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      */
testIncrementalRefreshNotResolvedSingleMatch()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      */
testIncrementalRefreshNotResolvedMultiMatch()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      */
testIncrementalRefreshResolvedSingleMatch()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      */
testFullRefreshResolvedSingleMatch()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      */
testIncrementalRefreshNotFound()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      */
testFullRefreshNotFound()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      */
testFullRefreshResolvedMultiMatch1()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      */
testFullRefreshResolvedMultiMatch2()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      */
testFullRefreshResolvedMultiMatch3()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      */
testFullRefreshResolvedBeforeButNotFoundNow()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