1/* 2 * Copyright (C) 2011 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.providers.contacts; 18 19import android.database.sqlite.SQLiteDatabase; 20import android.test.MoreAsserts; 21import android.test.suitebuilder.annotation.LargeTest; 22import android.test.suitebuilder.annotation.SmallTest; 23 24import com.android.providers.contacts.ContactsDatabaseHelper.MimetypesColumns; 25import com.android.providers.contacts.ContactsDatabaseHelper.Tables; 26import com.google.android.collect.Sets; 27 28import java.util.HashSet; 29import java.util.Set; 30 31@SmallTest 32public class ContactsDatabaseHelperTest extends BaseContactsProvider2Test { 33 private ContactsDatabaseHelper mDbHelper; 34 private SQLiteDatabase mDb; 35 36 @Override 37 protected void setUp() throws Exception { 38 super.setUp(); 39 mDbHelper = getContactsProvider().getDatabaseHelper(getContext()); 40 mDb = mDbHelper.getWritableDatabase(); 41 } 42 43 public void testGetOrCreateAccountId() { 44 final AccountWithDataSet a1 = null; 45 final AccountWithDataSet a2 = new AccountWithDataSet("a", null, null); 46 final AccountWithDataSet a3 = new AccountWithDataSet(null, "b", null); 47 final AccountWithDataSet a4 = new AccountWithDataSet(null, null, "c"); 48 final AccountWithDataSet a5 = new AccountWithDataSet("a", "b", "c"); 49 50 // First, there's no accounts. getAccountIdOrNull() always returns null. 51 assertNull(mDbHelper.getAccountIdOrNull(a1)); 52 assertNull(mDbHelper.getAccountIdOrNull(a2)); 53 assertNull(mDbHelper.getAccountIdOrNull(a3)); 54 assertNull(mDbHelper.getAccountIdOrNull(a4)); 55 assertNull(mDbHelper.getAccountIdOrNull(a5)); 56 57 // getOrCreateAccountId should create accounts. 58 final long a1id = mDbHelper.getOrCreateAccountIdInTransaction(a1); 59 final long a2id = mDbHelper.getOrCreateAccountIdInTransaction(a2); 60 final long a3id = mDbHelper.getOrCreateAccountIdInTransaction(a3); 61 final long a4id = mDbHelper.getOrCreateAccountIdInTransaction(a4); 62 final long a5id = mDbHelper.getOrCreateAccountIdInTransaction(a5); 63 64 // The IDs should be all positive and unique. 65 assertTrue(a1id > 0); 66 assertTrue(a2id > 0); 67 assertTrue(a3id > 0); 68 assertTrue(a4id > 0); 69 assertTrue(a5id > 0); 70 71 final Set<Long> ids = Sets.newHashSet(); 72 ids.add(a1id); 73 ids.add(a2id); 74 ids.add(a3id); 75 ids.add(a4id); 76 ids.add(a5id); 77 assertEquals(5, ids.size()); 78 79 // Second call: This time getOrCreateAccountId will return the existing IDs. 80 assertEquals(a1id, mDbHelper.getOrCreateAccountIdInTransaction(a1)); 81 assertEquals(a2id, mDbHelper.getOrCreateAccountIdInTransaction(a2)); 82 assertEquals(a3id, mDbHelper.getOrCreateAccountIdInTransaction(a3)); 83 assertEquals(a4id, mDbHelper.getOrCreateAccountIdInTransaction(a4)); 84 assertEquals(a5id, mDbHelper.getOrCreateAccountIdInTransaction(a5)); 85 86 // Now getAccountIdOrNull() returns IDs too. 87 assertEquals((Long) a1id, mDbHelper.getAccountIdOrNull(a1)); 88 assertEquals((Long) a2id, mDbHelper.getAccountIdOrNull(a2)); 89 assertEquals((Long) a3id, mDbHelper.getAccountIdOrNull(a3)); 90 assertEquals((Long) a4id, mDbHelper.getAccountIdOrNull(a4)); 91 assertEquals((Long) a5id, mDbHelper.getAccountIdOrNull(a5)); 92 93 // null and AccountWithDataSet.NULL should be treated as the same thing. 94 assertEquals(a1id, mDbHelper.getOrCreateAccountIdInTransaction(AccountWithDataSet.LOCAL)); 95 assertEquals((Long) a1id, mDbHelper.getAccountIdOrNull(AccountWithDataSet.LOCAL)); 96 97 // Remove all accounts. 98 mDbHelper.getWritableDatabase().execSQL("delete from " + Tables.ACCOUNTS); 99 100 assertNull(mDbHelper.getAccountIdOrNull(AccountWithDataSet.LOCAL)); 101 assertNull(mDbHelper.getAccountIdOrNull(a1)); 102 assertNull(mDbHelper.getAccountIdOrNull(a2)); 103 assertNull(mDbHelper.getAccountIdOrNull(a3)); 104 assertNull(mDbHelper.getAccountIdOrNull(a4)); 105 assertNull(mDbHelper.getAccountIdOrNull(a5)); 106 107 // Logically same as a5, but physically different object. 108 final AccountWithDataSet a5b = new AccountWithDataSet("a", "b", "c"); 109 // a5 and a5b should have the same ID. 110 assertEquals( 111 mDbHelper.getOrCreateAccountIdInTransaction(a5), 112 mDbHelper.getOrCreateAccountIdInTransaction(a5b)); 113 } 114 115 /** 116 * Test for {@link ContactsDatabaseHelper#queryIdWithOneArg} and 117 * {@link ContactsDatabaseHelper#insertWithOneArgAndReturnId}. 118 */ 119 public void testQueryIdWithOneArg_insertWithOneArgAndReturnId() { 120 final String query = 121 "SELECT " + MimetypesColumns._ID + 122 " FROM " + Tables.MIMETYPES + 123 " WHERE " + MimetypesColumns.MIMETYPE + "=?"; 124 125 final String insert = 126 "INSERT INTO " + Tables.MIMETYPES + "(" 127 + MimetypesColumns.MIMETYPE + 128 ") VALUES (?)"; 129 130 // First, the table is empty. 131 assertEquals(-1, ContactsDatabaseHelper.queryIdWithOneArg(mDb, query, "value1")); 132 assertEquals(-1, ContactsDatabaseHelper.queryIdWithOneArg(mDb, query, "value2")); 133 134 // Insert one value. 135 final long id1 = ContactsDatabaseHelper.insertWithOneArgAndReturnId(mDb, insert, "value1"); 136 MoreAsserts.assertNotEqual(-1, id1); 137 138 assertEquals(id1, ContactsDatabaseHelper.queryIdWithOneArg(mDb, query, "value1")); 139 assertEquals(-1, ContactsDatabaseHelper.queryIdWithOneArg(mDb, query, "value2")); 140 141 142 // Insert one value. 143 final long id2 = ContactsDatabaseHelper.insertWithOneArgAndReturnId(mDb, insert, "value2"); 144 MoreAsserts.assertNotEqual(-1, id2); 145 146 assertEquals(id1, ContactsDatabaseHelper.queryIdWithOneArg(mDb, query, "value1")); 147 assertEquals(id2, ContactsDatabaseHelper.queryIdWithOneArg(mDb, query, "value2")); 148 149 // Insert the same value and cause a conflict. 150 assertEquals(-1, ContactsDatabaseHelper.insertWithOneArgAndReturnId(mDb, insert, "value2")); 151 } 152 153 /** 154 * Test for {@link ContactsDatabaseHelper#getPackageId(String)} and 155 * {@link ContactsDatabaseHelper#getMimeTypeId(String)}. 156 * 157 * We test them at the same time here, to make sure they're not mixing up the caches. 158 */ 159 public void testGetPackageId_getMimeTypeId() { 160 161 // Test for getPackageId. 162 final long packageId1 = mDbHelper.getPackageId("value1"); 163 final long packageId2 = mDbHelper.getPackageId("value2"); 164 final long packageId3 = mDbHelper.getPackageId("value3"); 165 166 // Make sure they're all different. 167 final HashSet<Long> set = new HashSet<>(); 168 set.add(packageId1); 169 set.add(packageId2); 170 set.add(packageId3); 171 172 assertEquals(3, set.size()); 173 174 // Test for getMimeTypeId. 175 final long mimetypeId1 = mDbHelper.getMimeTypeId("value1"); 176 final long mimetypeId2 = mDbHelper.getMimeTypeId("value2"); 177 final long mimetypeId3 = mDbHelper.getMimeTypeId("value3"); 178 179 // Make sure they're all different. 180 set.clear(); 181 set.add(mimetypeId1); 182 set.add(mimetypeId2); 183 set.add(mimetypeId3); 184 185 assertEquals(3, set.size()); 186 187 // Call with the same values and make sure they return the cached value. 188 final long packageId1b = mDbHelper.getPackageId("value1"); 189 final long mimetypeId1b = mDbHelper.getMimeTypeId("value1"); 190 191 assertEquals(packageId1, packageId1b); 192 assertEquals(mimetypeId1, mimetypeId1b); 193 194 // Make sure the caches are also updated. 195 assertEquals(packageId2, (long) mDbHelper.mPackageCache.get("value2")); 196 assertEquals(mimetypeId2, (long) mDbHelper.mMimetypeCache.get("value2")); 197 198 // Clear the cache, but they should still return the values, selecting from the database. 199 mDbHelper.mPackageCache.clear(); 200 mDbHelper.mMimetypeCache.clear(); 201 assertEquals(packageId1, mDbHelper.getPackageId("value1")); 202 assertEquals(mimetypeId1, mDbHelper.getMimeTypeId("value1")); 203 204 // Empty the table 205 mDb.execSQL("DELETE FROM " + Tables.MIMETYPES); 206 207 // We should still have the cached value. 208 assertEquals(mimetypeId1, mDbHelper.getMimeTypeId("value1")); 209 } 210 211 /** 212 * Try to cause conflicts in getMimeTypeId() by calling it from multiple threads with 213 * the current time as the argument and make sure it won't crash. 214 * 215 * We don't know from the test if there have actually been conflits, but if you look at 216 * logcat you'll see a lot of conflict warnings. 217 */ 218 @LargeTest 219 public void testGetMimeTypeId_conflict() { 220 221 final int NUM_THREADS = 4; 222 final int DURATION_SECONDS = 5; 223 224 final long finishTime = System.currentTimeMillis() + DURATION_SECONDS * 1000; 225 226 final Runnable r = new Runnable() { 227 @Override 228 public void run() { 229 for (;;) { 230 final long now = System.currentTimeMillis(); 231 if (now >= finishTime) { 232 return; 233 } 234 assertTrue(mDbHelper.getMimeTypeId(String.valueOf(now)) > 0); 235 } 236 } 237 }; 238 final Thread[] threads = new Thread[NUM_THREADS]; 239 for (int i = 0; i < threads.length; i++) { 240 threads[i] = new Thread(r); 241 threads[i].setDaemon(true); 242 } 243 for (int i = 0; i < threads.length; i++) { 244 threads[i].start(); 245 } 246 for (int i = 0; i < threads.length; i++) { 247 try { 248 threads[i].join(); 249 } catch (InterruptedException ignore) { 250 } 251 } 252 } 253} 254