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