summaryrefslogtreecommitdiffstats
path: root/tests/src/com/android/providers
diff options
context:
space:
mode:
Diffstat (limited to 'tests/src/com/android/providers')
-rw-r--r--tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java43
-rw-r--r--tests/src/com/android/providers/contacts/ContactsActor.java7
-rw-r--r--tests/src/com/android/providers/contacts/ContactsProvider2Test.java359
-rw-r--r--tests/src/com/android/providers/contacts/LegacyContactImporterPerformanceTest.java10
-rw-r--r--tests/src/com/android/providers/contacts/LegacyContactsProviderTest.java10
-rw-r--r--tests/src/com/android/providers/contacts/PhotoLoadingTestCase.java106
-rw-r--r--tests/src/com/android/providers/contacts/PhotoStoreTest.java198
7 files changed, 684 insertions, 49 deletions
diff --git a/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java b/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java
index 50cd50e..ea99caf 100644
--- a/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java
+++ b/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java
@@ -16,8 +16,7 @@
package com.android.providers.contacts;
-import static com.android.providers.contacts.ContactsActor.PACKAGE_GREY;
-
+import com.google.android.collect.Maps;
import com.google.android.collect.Sets;
import android.accounts.Account;
@@ -50,7 +49,6 @@ import android.provider.ContactsContract.RawContacts;
import android.provider.ContactsContract.Settings;
import android.provider.ContactsContract.StatusUpdates;
import android.provider.ContactsContract.StreamItems;
-import android.provider.ContactsContract.StreamItemPhotos;
import android.test.AndroidTestCase;
import android.test.MoreAsserts;
import android.test.mock.MockContentResolver;
@@ -67,10 +65,12 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
+import static com.android.providers.contacts.ContactsActor.PACKAGE_GREY;
+
/**
* A common superclass for {@link ContactsProvider2}-related tests.
*/
-public abstract class BaseContactsProvider2Test extends AndroidTestCase {
+public abstract class BaseContactsProvider2Test extends PhotoLoadingTestCase {
protected static final String PACKAGE = "ContactsProvider2Test";
public static final String READ_ONLY_ACCOUNT_TYPE =
@@ -81,8 +81,6 @@ public abstract class BaseContactsProvider2Test extends AndroidTestCase {
protected Account mAccount = new Account("account1", "account type1");
protected Account mAccountTwo = new Account("account2", "account type2");
- private byte[] mTestPhoto;
-
protected final static Long NO_LONG = new Long(0);
protected final static String NO_STRING = new String("");
protected final static Account NO_ACCOUNT = new Account("a", "b");
@@ -363,6 +361,15 @@ public abstract class BaseContactsProvider2Test extends AndroidTestCase {
return resultUri;
}
+ protected Uri insertPhoto(long rawContactId, int resourceId) {
+ ContentValues values = new ContentValues();
+ values.put(Data.RAW_CONTACT_ID, rawContactId);
+ values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
+ values.put(Photo.PHOTO, loadPhotoFromResource(resourceId, PhotoSize.ORIGINAL));
+ Uri resultUri = mResolver.insert(Data.CONTENT_URI, values);
+ return resultUri;
+ }
+
protected Uri insertGroupMembership(long rawContactId, String sourceId) {
ContentValues values = new ContentValues();
values.put(Data.RAW_CONTACT_ID, rawContactId);
@@ -830,6 +837,10 @@ public abstract class BaseContactsProvider2Test extends AndroidTestCase {
return value;
}
+ protected Long getStoredLongValue(Uri uri, String column) {
+ return getStoredLongValue(uri, null, null, column);
+ }
+
protected void assertStoredValues(Uri rowUri, ContentValues expectedValues) {
assertStoredValues(rowUri, null, null, expectedValues);
}
@@ -1025,26 +1036,6 @@ public abstract class BaseContactsProvider2Test extends AndroidTestCase {
}
}
- protected byte[] loadTestPhoto() {
- if (mTestPhoto == null) {
- final Resources resources = getContext().getResources();
- InputStream is = resources
- .openRawResource(com.android.internal.R.drawable.ic_contact_picture);
- ByteArrayOutputStream os = new ByteArrayOutputStream();
- byte[] buffer = new byte[1000];
- int count;
- try {
- while ((count = is.read(buffer)) != -1) {
- os.write(buffer, 0, count);
- }
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- mTestPhoto = os.toByteArray();
- }
- return mTestPhoto;
- }
-
public static void dump(ContentResolver resolver, boolean aggregatedOnly) {
String[] projection = new String[] {
Contacts._ID,
diff --git a/tests/src/com/android/providers/contacts/ContactsActor.java b/tests/src/com/android/providers/contacts/ContactsActor.java
index f22943e..865c956 100644
--- a/tests/src/com/android/providers/contacts/ContactsActor.java
+++ b/tests/src/com/android/providers/contacts/ContactsActor.java
@@ -59,6 +59,7 @@ import android.test.mock.MockContext;
import android.test.mock.MockResources;
import android.util.TypedValue;
+import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Locale;
@@ -152,6 +153,12 @@ public class ContactsActor {
mProviderContext = new IsolatedContext(resolver, targetContextWrapper){
@Override
+ public File getFilesDir() {
+ // TODO: Need to figure out something more graceful than this.
+ return new File("/data/data/com.android.providers.contacts.tests/files");
+ }
+
+ @Override
public Object getSystemService(String name) {
if (Context.COUNTRY_DETECTOR.equals(name)) {
return mMockCountryDetector;
diff --git a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
index ab43158..aa19e4e 100644
--- a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
+++ b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
@@ -19,6 +19,7 @@ package com.android.providers.contacts;
import com.android.internal.util.ArrayUtils;
import com.android.providers.contacts.ContactsDatabaseHelper.AggregationExceptionColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns;
+import com.android.providers.contacts.tests.R;
import com.google.android.collect.Lists;
import android.accounts.Account;
@@ -47,6 +48,7 @@ import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.DataUsageFeedback;
import android.provider.ContactsContract.Directory;
import android.provider.ContactsContract.DisplayNameSources;
+import android.provider.ContactsContract.DisplayPhoto;
import android.provider.ContactsContract.FullNameStyle;
import android.provider.ContactsContract.Groups;
import android.provider.ContactsContract.PhoneLookup;
@@ -58,16 +60,18 @@ import android.provider.ContactsContract.RawContactsEntity;
import android.provider.ContactsContract.SearchSnippetColumns;
import android.provider.ContactsContract.Settings;
import android.provider.ContactsContract.StatusUpdates;
-import android.provider.ContactsContract.StreamItems;
import android.provider.ContactsContract.StreamItemPhotos;
+import android.provider.ContactsContract.StreamItems;
import android.provider.LiveFolders;
import android.provider.OpenableColumns;
import android.test.MoreAsserts;
import android.test.suitebuilder.annotation.LargeTest;
+import android.text.TextUtils;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Arrays;
@@ -104,6 +108,7 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test {
Contacts.STARRED,
Contacts.IN_VISIBLE_GROUP,
Contacts.PHOTO_ID,
+ Contacts.PHOTO_FILE_ID,
Contacts.PHOTO_URI,
Contacts.PHOTO_THUMBNAIL_URI,
Contacts.CUSTOM_RINGTONE,
@@ -138,6 +143,7 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test {
Contacts.STARRED,
Contacts.IN_VISIBLE_GROUP,
Contacts.PHOTO_ID,
+ Contacts.PHOTO_FILE_ID,
Contacts.PHOTO_URI,
Contacts.PHOTO_THUMBNAIL_URI,
Contacts.CUSTOM_RINGTONE,
@@ -246,6 +252,7 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test {
Contacts.STARRED,
Contacts.IN_VISIBLE_GROUP,
Contacts.PHOTO_ID,
+ Contacts.PHOTO_FILE_ID,
Contacts.PHOTO_URI,
Contacts.PHOTO_THUMBNAIL_URI,
Contacts.CUSTOM_RINGTONE,
@@ -314,6 +321,7 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test {
Contacts.STARRED,
Contacts.IN_VISIBLE_GROUP,
Contacts.PHOTO_ID,
+ Contacts.PHOTO_FILE_ID,
Contacts.PHOTO_URI,
Contacts.PHOTO_THUMBNAIL_URI,
Contacts.HAS_PHONE_NUMBER,
@@ -395,6 +403,7 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test {
Contacts.STARRED,
Contacts.IN_VISIBLE_GROUP,
Contacts.PHOTO_ID,
+ Contacts.PHOTO_FILE_ID,
Contacts.PHOTO_URI,
Contacts.PHOTO_THUMBNAIL_URI,
Contacts.CUSTOM_RINGTONE,
@@ -4012,28 +4021,29 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test {
Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
long rawContactId = ContentUris.parseId(rawContactUri);
insertStructuredName(rawContactId, "John", "Doe");
- Uri photoUri = insertPhoto(rawContactId);
-
- Uri twigUri = Uri.withAppendedPath(ContentUris.withAppendedId(Contacts.CONTENT_URI,
- queryContactId(rawContactId)), Contacts.Photo.CONTENT_DIRECTORY);
+ long dataId = ContentUris.parseId(insertPhoto(rawContactId, R.drawable.earth_normal));
+ long photoFileId = getStoredLongValue(Data.CONTENT_URI, Data._ID + "=?",
+ new String[]{String.valueOf(dataId)}, Photo.PHOTO_FILE_ID);
+ String photoUri = ContentUris.withAppendedId(DisplayPhoto.CONTENT_URI, photoFileId)
+ .toString();
assertStoredValue(
ContentUris.withAppendedId(Contacts.CONTENT_URI, queryContactId(rawContactId)),
- Contacts.PHOTO_URI, twigUri.toString());
-
- long twigId = Long.parseLong(getStoredValue(twigUri, Data._ID));
- assertEquals(ContentUris.parseId(photoUri), twigId);
+ Contacts.PHOTO_URI, photoUri);
}
public void testInputStreamForPhoto() throws Exception {
long rawContactId = createRawContact();
- Uri photoUri = insertPhoto(rawContactId);
- assertInputStreamContent(loadTestPhoto(), mResolver.openInputStream(photoUri));
+ long contactId = queryContactId(rawContactId);
+ Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
+ insertPhoto(rawContactId);
+ Uri photoUri = Uri.parse(getStoredValue(contactUri, Contacts.PHOTO_URI));
+ Uri photoThumbnailUri = Uri.parse(getStoredValue(contactUri, Contacts.PHOTO_THUMBNAIL_URI));
- Uri contactPhotoUri = Uri.withAppendedPath(
- ContentUris.withAppendedId(Contacts.CONTENT_URI, queryContactId(rawContactId)),
- Contacts.Photo.CONTENT_DIRECTORY);
- assertInputStreamContent(loadTestPhoto(), mResolver.openInputStream(contactPhotoUri));
+ assertInputStreamContent(loadTestPhoto(PhotoSize.DISPLAY_PHOTO),
+ mResolver.openInputStream(photoUri));
+ assertInputStreamContent(loadTestPhoto(PhotoSize.THUMBNAIL),
+ mResolver.openInputStream(photoThumbnailUri));
}
private static void assertInputStreamContent(byte[] expected, InputStream is)
@@ -4051,11 +4061,11 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test {
public void testSuperPrimaryPhoto() {
long rawContactId1 = createRawContact(new Account("a", "a"));
- Uri photoUri1 = insertPhoto(rawContactId1);
+ Uri photoUri1 = insertPhoto(rawContactId1, R.drawable.earth_normal);
long photoId1 = ContentUris.parseId(photoUri1);
long rawContactId2 = createRawContact(new Account("b", "b"));
- Uri photoUri2 = insertPhoto(rawContactId2);
+ Uri photoUri2 = insertPhoto(rawContactId2, R.drawable.earth_normal);
long photoId2 = ContentUris.parseId(photoUri2);
setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER,
@@ -4063,9 +4073,13 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test {
Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI,
queryContactId(rawContactId1));
+
+ long photoFileId1 = getStoredLongValue(Data.CONTENT_URI, Data._ID + "=?",
+ new String[]{String.valueOf(photoId1)}, Photo.PHOTO_FILE_ID);
+ String photoUri = ContentUris.withAppendedId(DisplayPhoto.CONTENT_URI, photoFileId1)
+ .toString();
assertStoredValue(contactUri, Contacts.PHOTO_ID, photoId1);
- assertStoredValue(contactUri, Contacts.PHOTO_URI,
- Uri.withAppendedPath(contactUri, Contacts.Photo.CONTENT_DIRECTORY));
+ assertStoredValue(contactUri, Contacts.PHOTO_URI, photoUri);
setAggregationException(AggregationExceptions.TYPE_KEEP_SEPARATE,
rawContactId1, rawContactId2);
@@ -4107,7 +4121,7 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test {
mResolver.update(dataUri, values, null, null);
assertNetworkNotified(true);
- long twigId = Long.parseLong(getStoredValue(twigUri, Data._ID));
+ long twigId = getStoredLongValue(twigUri, Data._ID);
assertEquals(photoId, twigId);
}
@@ -4142,10 +4156,313 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test {
Cursor storedPhoto = mResolver.query(dataUri, new String[] {Photo.PHOTO},
Data.MIMETYPE + "=?", new String[] {Photo.CONTENT_ITEM_TYPE}, null);
storedPhoto.moveToFirst();
- MoreAsserts.assertEquals(loadTestPhoto(), storedPhoto.getBlob(0));
+ MoreAsserts.assertEquals(loadTestPhoto(PhotoSize.THUMBNAIL), storedPhoto.getBlob(0));
storedPhoto.close();
}
+ public void testOpenDisplayPhotoForContactId() throws IOException {
+ long rawContactId = createRawContactWithName();
+ long contactId = queryContactId(rawContactId);
+ insertPhoto(rawContactId, R.drawable.earth_normal);
+ Uri photoUri = Contacts.CONTENT_URI.buildUpon()
+ .appendPath(String.valueOf(contactId))
+ .appendPath(Contacts.Photo.DISPLAY_PHOTO).build();
+ assertInputStreamContent(
+ loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.DISPLAY_PHOTO),
+ mResolver.openInputStream(photoUri));
+ }
+
+ public void testOpenDisplayPhotoForContactLookupKey() throws IOException {
+ long rawContactId = createRawContactWithName();
+ long contactId = queryContactId(rawContactId);
+ String lookupKey = queryLookupKey(contactId);
+ insertPhoto(rawContactId, R.drawable.earth_normal);
+ Uri photoUri = Contacts.CONTENT_LOOKUP_URI.buildUpon()
+ .appendPath(lookupKey)
+ .appendPath(Contacts.Photo.DISPLAY_PHOTO).build();
+ assertInputStreamContent(
+ loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.DISPLAY_PHOTO),
+ mResolver.openInputStream(photoUri));
+ }
+
+ public void testOpenDisplayPhotoForContactLookupKeyAndId() throws IOException {
+ long rawContactId = createRawContactWithName();
+ long contactId = queryContactId(rawContactId);
+ String lookupKey = queryLookupKey(contactId);
+ insertPhoto(rawContactId, R.drawable.earth_normal);
+ Uri photoUri = Contacts.CONTENT_LOOKUP_URI.buildUpon()
+ .appendPath(lookupKey)
+ .appendPath(String.valueOf(contactId))
+ .appendPath(Contacts.Photo.DISPLAY_PHOTO).build();
+ assertInputStreamContent(
+ loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.DISPLAY_PHOTO),
+ mResolver.openInputStream(photoUri));
+ }
+
+ public void testOpenDisplayPhotoForRawContactId() throws IOException {
+ long rawContactId = createRawContactWithName();
+ insertPhoto(rawContactId, R.drawable.earth_normal);
+ Uri photoUri = RawContacts.CONTENT_URI.buildUpon()
+ .appendPath(String.valueOf(rawContactId))
+ .appendPath(RawContacts.DisplayPhoto.CONTENT_DIRECTORY).build();
+ assertInputStreamContent(
+ loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.DISPLAY_PHOTO),
+ mResolver.openInputStream(photoUri));
+ }
+
+ public void testOpenDisplayPhotoByPhotoUri() throws IOException {
+ long rawContactId = createRawContactWithName();
+ long contactId = queryContactId(rawContactId);
+ insertPhoto(rawContactId, R.drawable.earth_normal);
+
+ // Get the photo URI out and check the content.
+ String photoUri = getStoredValue(
+ ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
+ Contacts.PHOTO_URI);
+ assertInputStreamContent(
+ loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.DISPLAY_PHOTO),
+ mResolver.openInputStream(Uri.parse(photoUri)));
+ }
+
+ public void testPhotoUriForDisplayPhoto() {
+ long rawContactId = createRawContactWithName();
+ long contactId = queryContactId(rawContactId);
+
+ // Photo being inserted is larger than a thumbnail, so it will be stored as a file.
+ long dataId = ContentUris.parseId(insertPhoto(rawContactId, R.drawable.earth_normal));
+ String photoFileId = getStoredValue(ContentUris.withAppendedId(Data.CONTENT_URI, dataId),
+ Photo.PHOTO_FILE_ID);
+ String photoUri = getStoredValue(
+ ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
+ Contacts.PHOTO_URI);
+
+ // Check that the photo URI differs from the thumbnail.
+ String thumbnailUri = getStoredValue(
+ ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
+ Contacts.PHOTO_THUMBNAIL_URI);
+ assertFalse(photoUri.equals(thumbnailUri));
+
+ // URI should be of the form display_photo/ID
+ assertEquals(Uri.withAppendedPath(DisplayPhoto.CONTENT_URI, photoFileId).toString(),
+ photoUri);
+ }
+
+ public void testPhotoUriForThumbnailPhoto() throws IOException {
+ long rawContactId = createRawContactWithName();
+ long contactId = queryContactId(rawContactId);
+
+ // Photo being inserted is a thumbnail, so it will only be stored in a BLOB. The photo URI
+ // will fall back to the thumbnail URI.
+ insertPhoto(rawContactId, R.drawable.earth_small);
+ String photoUri = getStoredValue(
+ ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
+ Contacts.PHOTO_URI);
+
+ // Check that the photo URI is equal to the thumbnail URI.
+ String thumbnailUri = getStoredValue(
+ ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
+ Contacts.PHOTO_THUMBNAIL_URI);
+ assertEquals(photoUri, thumbnailUri);
+
+ // URI should be of the form contacts/ID/photo
+ assertEquals(Uri.withAppendedPath(
+ ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
+ Contacts.Photo.CONTENT_DIRECTORY).toString(),
+ photoUri);
+
+ // Loading the photo URI content should get the thumbnail.
+ assertInputStreamContent(
+ loadPhotoFromResource(R.drawable.earth_small, PhotoSize.THUMBNAIL),
+ mResolver.openInputStream(Uri.parse(photoUri)));
+ }
+
+ public void testWriteNewPhotoToAssetFile() throws IOException {
+ long rawContactId = createRawContactWithName();
+ long contactId = queryContactId(rawContactId);
+
+ // Load in a huge photo.
+ byte[] originalPhoto = loadPhotoFromResource(R.drawable.earth_huge, PhotoSize.ORIGINAL);
+
+ // Write it out.
+ Uri writeablePhotoUri = RawContacts.CONTENT_URI.buildUpon()
+ .appendPath(String.valueOf(rawContactId))
+ .appendPath(RawContacts.DisplayPhoto.CONTENT_DIRECTORY).build();
+ OutputStream os = mResolver.openOutputStream(writeablePhotoUri, "rw");
+ try {
+ os.write(originalPhoto);
+ } finally {
+ os.close();
+ }
+
+ // Check that the display photo and thumbnail have been set.
+ String photoUri = getStoredValue(
+ ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), Contacts.PHOTO_URI);
+ assertFalse(TextUtils.isEmpty(photoUri));
+ String thumbnailUri = getStoredValue(
+ ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
+ Contacts.PHOTO_THUMBNAIL_URI);
+ assertFalse(TextUtils.isEmpty(thumbnailUri));
+ assertFalse(photoUri.equals(thumbnailUri));
+
+ // Check the content of the display photo and thumbnail.
+ assertInputStreamContent(
+ loadPhotoFromResource(R.drawable.earth_huge, PhotoSize.DISPLAY_PHOTO),
+ mResolver.openInputStream(Uri.parse(photoUri)));
+ assertInputStreamContent(
+ loadPhotoFromResource(R.drawable.earth_huge, PhotoSize.THUMBNAIL),
+ mResolver.openInputStream(Uri.parse(thumbnailUri)));
+ }
+
+ public void testWriteUpdatedPhotoToAssetFile() throws IOException {
+ long rawContactId = createRawContactWithName();
+ long contactId = queryContactId(rawContactId);
+
+ // Insert a large photo first.
+ insertPhoto(rawContactId, R.drawable.earth_large);
+ String largeEarthPhotoUri = getStoredValue(
+ ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), Contacts.PHOTO_URI);
+
+ // Load in a huge photo.
+ byte[] originalPhoto = loadPhotoFromResource(R.drawable.earth_huge, PhotoSize.ORIGINAL);
+
+ // Write it out.
+ Uri writeablePhotoUri = RawContacts.CONTENT_URI.buildUpon()
+ .appendPath(String.valueOf(rawContactId))
+ .appendPath(RawContacts.DisplayPhoto.CONTENT_DIRECTORY).build();
+ OutputStream os = mResolver.openOutputStream(writeablePhotoUri, "rw");
+ try {
+ os.write(originalPhoto);
+ } finally {
+ os.close();
+ }
+
+ // Check that the display photo URI has been modified.
+ String hugeEarthPhotoUri = getStoredValue(
+ ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), Contacts.PHOTO_URI);
+ assertFalse(hugeEarthPhotoUri.equals(largeEarthPhotoUri));
+
+ // Check the content of the display photo and thumbnail.
+ String hugeEarthThumbnailUri = getStoredValue(
+ ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
+ Contacts.PHOTO_THUMBNAIL_URI);
+ assertInputStreamContent(
+ loadPhotoFromResource(R.drawable.earth_huge, PhotoSize.DISPLAY_PHOTO),
+ mResolver.openInputStream(Uri.parse(hugeEarthPhotoUri)));
+ assertInputStreamContent(
+ loadPhotoFromResource(R.drawable.earth_huge, PhotoSize.THUMBNAIL),
+ mResolver.openInputStream(Uri.parse(hugeEarthThumbnailUri)));
+
+ }
+
+ public void testPhotoDimensionLimits() {
+ ContentValues values = new ContentValues();
+ values.put(DisplayPhoto.DISPLAY_MAX_DIM, 256);
+ values.put(DisplayPhoto.THUMBNAIL_MAX_DIM, 96);
+ assertStoredValues(DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI, values);
+ }
+
+ public void testPhotoStoreCleanup() throws IOException {
+ SynchronousContactsProvider2 provider = (SynchronousContactsProvider2) mActor.provider;
+
+ // Trigger an initial cleanup so another one won't happen while we're running this test.
+ provider.cleanupPhotoStore();
+
+ // Insert a couple of contacts with photos.
+ long rawContactId1 = createRawContactWithName();
+ long contactId1 = queryContactId(rawContactId1);
+ long dataId1 = ContentUris.parseId(insertPhoto(rawContactId1, R.drawable.earth_normal));
+ long photoFileId1 =
+ getStoredLongValue(ContentUris.withAppendedId(Data.CONTENT_URI, dataId1),
+ Photo.PHOTO_FILE_ID);
+
+ long rawContactId2 = createRawContactWithName();
+ long contactId2 = queryContactId(rawContactId2);
+ long dataId2 = ContentUris.parseId(insertPhoto(rawContactId2, R.drawable.earth_normal));
+ long photoFileId2 =
+ getStoredLongValue(ContentUris.withAppendedId(Data.CONTENT_URI, dataId2),
+ Photo.PHOTO_FILE_ID);
+
+ // Update the second raw contact with a different photo.
+ ContentValues values = new ContentValues();
+ values.put(Data.RAW_CONTACT_ID, rawContactId2);
+ values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
+ values.put(Photo.PHOTO, loadPhotoFromResource(R.drawable.earth_huge, PhotoSize.ORIGINAL));
+ assertEquals(1, mResolver.update(Data.CONTENT_URI, values, Data._ID + "=?",
+ new String[]{String.valueOf(dataId2)}));
+ long replacementPhotoFileId =
+ getStoredLongValue(ContentUris.withAppendedId(Data.CONTENT_URI, dataId2),
+ Photo.PHOTO_FILE_ID);
+
+ // Insert a third raw contact that has a bogus photo file ID.
+ long bogusFileId = 1234567;
+ long rawContactId3 = createRawContactWithName();
+ long contactId3 = queryContactId(rawContactId3);
+ values.clear();
+ values.put(Data.RAW_CONTACT_ID, rawContactId3);
+ values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
+ values.put(Photo.PHOTO, loadPhotoFromResource(R.drawable.earth_normal,
+ PhotoSize.THUMBNAIL));
+ values.put(Photo.PHOTO_FILE_ID, bogusFileId);
+ values.put(DataRowHandlerForPhoto.SKIP_PROCESSING_KEY, true);
+ mResolver.insert(Data.CONTENT_URI, values);
+
+ // Also insert a bogus photo that nobody is using.
+ PhotoStore photoStore = provider.getPhotoStore();
+ long bogusPhotoId = photoStore.insert(new PhotoProcessor(loadPhotoFromResource(
+ R.drawable.earth_huge, PhotoSize.ORIGINAL), 256, 96));
+
+ // Manually trigger another cleanup in the provider.
+ provider.cleanupPhotoStore();
+
+ // The following things should have happened.
+
+ // 1. Raw contact 1 and its photo remain unaffected.
+ assertEquals(photoFileId1, (long) getStoredLongValue(
+ ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId1),
+ Contacts.PHOTO_FILE_ID));
+
+ // 2. Raw contact 2 retains its new photo. The old one is deleted from the photo store.
+ assertEquals(replacementPhotoFileId, (long) getStoredLongValue(
+ ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId2),
+ Contacts.PHOTO_FILE_ID));
+ assertNull(photoStore.get(photoFileId2));
+
+ // 3. Raw contact 3 should have its photo file reference cleared.
+ assertNull(getStoredValue(
+ ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId3),
+ Contacts.PHOTO_FILE_ID));
+
+ // 4. The bogus photo that nobody was using should be cleared from the photo store.
+ assertNull(photoStore.get(bogusPhotoId));
+ }
+
+ public void testOverwritePhotoWithThumbnail() throws IOException {
+ long rawContactId = createRawContactWithName();
+ long contactId = queryContactId(rawContactId);
+ Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
+
+ // Write a regular-size photo.
+ long dataId = ContentUris.parseId(insertPhoto(rawContactId, R.drawable.earth_normal));
+ Long photoFileId = getStoredLongValue(contactUri, Contacts.PHOTO_FILE_ID);
+ assertTrue(photoFileId != null && photoFileId > 0);
+
+ // Now overwrite the photo with a thumbnail-sized photo.
+ ContentValues update = new ContentValues();
+ update.put(Photo.PHOTO, loadPhotoFromResource(R.drawable.earth_small, PhotoSize.ORIGINAL));
+ mResolver.update(ContentUris.withAppendedId(Data.CONTENT_URI, dataId), update, null, null);
+
+ // Photo file ID should have been nulled out, and the photo URI should be the same as the
+ // thumbnail URI.
+ assertNull(getStoredValue(contactUri, Contacts.PHOTO_FILE_ID));
+ String photoUri = getStoredValue(contactUri, Contacts.PHOTO_URI);
+ String thumbnailUri = getStoredValue(contactUri, Contacts.PHOTO_THUMBNAIL_URI);
+ assertEquals(photoUri, thumbnailUri);
+
+ // Retrieving the photo URI should get the thumbnail content.
+ assertInputStreamContent(loadPhotoFromResource(R.drawable.earth_small, PhotoSize.THUMBNAIL),
+ mResolver.openInputStream(Uri.parse(photoUri)));
+ }
+
public void testUpdateRawContactSetStarred() {
long rawContactId1 = createRawContactWithName();
Uri rawContactUri1 = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId1);
diff --git a/tests/src/com/android/providers/contacts/LegacyContactImporterPerformanceTest.java b/tests/src/com/android/providers/contacts/LegacyContactImporterPerformanceTest.java
index 7e4b39f..d78193b 100644
--- a/tests/src/com/android/providers/contacts/LegacyContactImporterPerformanceTest.java
+++ b/tests/src/com/android/providers/contacts/LegacyContactImporterPerformanceTest.java
@@ -32,6 +32,8 @@ import android.test.mock.MockContext;
import android.test.suitebuilder.annotation.MediumTest;
import android.util.Log;
+import java.io.File;
+
/**
* Performance test for {@link ContactAggregator}. Run the test like this:
* <code>
@@ -94,7 +96,13 @@ public class LegacyContactImporterPerformanceTest extends AndroidTestCase {
RenamingDelegatingContext targetContextWrapper = new RenamingDelegatingContext(context,
targetContext, "perf_imp.");
targetContextWrapper.makeExistingFilesAndDbsAccessible();
- IsolatedContext providerContext = new IsolatedContext(resolver, targetContextWrapper);
+ IsolatedContext providerContext = new IsolatedContext(resolver, targetContextWrapper) {
+ @Override
+ public File getFilesDir() {
+ // TODO: Need to figure out something more graceful than this.
+ return new File("/data/data/com.android.providers.contacts.tests/files");
+ }
+ };
SynchronousContactsProvider2 provider = new SynchronousContactsProvider2();
provider.setDataWipeEnabled(false);
provider.attachInfo(providerContext, null);
diff --git a/tests/src/com/android/providers/contacts/LegacyContactsProviderTest.java b/tests/src/com/android/providers/contacts/LegacyContactsProviderTest.java
index e034696..e515af2 100644
--- a/tests/src/com/android/providers/contacts/LegacyContactsProviderTest.java
+++ b/tests/src/com/android/providers/contacts/LegacyContactsProviderTest.java
@@ -16,6 +16,8 @@
package com.android.providers.contacts;
+import com.android.providers.contacts.tests.*;
+
import android.app.SearchManager;
import android.content.ContentProvider;
import android.content.ContentUris;
@@ -707,7 +709,10 @@ public class LegacyContactsProviderTest extends BaseContactsProvider2Test {
}
public void testPhotoUpdate() throws Exception {
- byte[] photo = loadTestPhoto();
+ byte[] photo = loadPhotoFromResource(
+ com.android.providers.contacts.tests.R.drawable.earth_small, PhotoSize.ORIGINAL);
+ byte[] thumbnailedPhoto = loadPhotoFromResource(
+ com.android.providers.contacts.tests.R.drawable.earth_small, PhotoSize.THUMBNAIL);
ContentValues values = new ContentValues();
Uri personUri = mResolver.insert(People.CONTENT_URI, values);
@@ -722,13 +727,16 @@ public class LegacyContactsProviderTest extends BaseContactsProvider2Test {
Uri photoUri = Uri.withAppendedPath(personUri, Photos.CONTENT_DIRECTORY);
mResolver.update(photoUri, values, null, null);
+ values.put(Photos.DATA, thumbnailedPhoto);
assertStoredValues(photoUri, values);
long photoId = Long.parseLong(getStoredValue(photoUri, Photos._ID));
values.put(Photos.LOCAL_VERSION, "11");
+ values.put(Photos.DATA, photo);
Uri contentUri = ContentUris.withAppendedId(Photos.CONTENT_URI, photoId);
mResolver.update(contentUri, values, null, null);
+ values.put(Photos.DATA, thumbnailedPhoto);
assertStoredValues(contentUri, values);
assertStoredValues(photoUri, values);
}
diff --git a/tests/src/com/android/providers/contacts/PhotoLoadingTestCase.java b/tests/src/com/android/providers/contacts/PhotoLoadingTestCase.java
new file mode 100644
index 0000000..285378c
--- /dev/null
+++ b/tests/src/com/android/providers/contacts/PhotoLoadingTestCase.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.providers.contacts;
+
+import com.google.android.collect.Maps;
+
+import android.content.res.Resources;
+import android.test.AndroidTestCase;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+
+/**
+ * Adds support for loading photo files easily from test resources.
+ */
+public class PhotoLoadingTestCase extends AndroidTestCase {
+
+ private Map<Integer, PhotoEntry> photoResourceCache = Maps.newHashMap();
+ protected static enum PhotoSize {
+ ORIGINAL,
+ DISPLAY_PHOTO,
+ THUMBNAIL
+ }
+
+ protected final class PhotoEntry {
+ Map<PhotoSize, byte[]> photoMap = Maps.newHashMap();
+ public PhotoEntry(byte[] original) {
+ try {
+ Resources resources = getContext().getResources();
+ PhotoProcessor processor = new PhotoProcessor(original,
+ resources.getInteger(R.integer.config_max_display_photo_dim),
+ resources.getInteger(R.integer.config_max_thumbnail_photo_dim));
+ photoMap.put(PhotoSize.ORIGINAL, original);
+ photoMap.put(PhotoSize.DISPLAY_PHOTO, processor.getDisplayPhotoBytes());
+ photoMap.put(PhotoSize.THUMBNAIL, processor.getThumbnailPhotoBytes());
+ } catch (IOException ignored) {
+ // Test is probably going to fail as a result anyway.
+ }
+ }
+
+ public byte[] getPhoto(PhotoSize size) {
+ return photoMap.get(size);
+ }
+ }
+
+ // The test photo will be loaded frequently in tests, so we'll just process it once.
+ private static PhotoEntry testPhotoEntry;
+
+
+ protected byte[] loadTestPhoto() {
+ int testPhotoId = com.android.providers.contacts.tests.R.drawable.ic_contact_picture;
+ if (testPhotoEntry == null) {
+ loadPhotoFromResource(testPhotoId, PhotoSize.ORIGINAL);
+ testPhotoEntry = photoResourceCache.get(testPhotoId);
+ }
+ return testPhotoEntry.getPhoto(PhotoSize.ORIGINAL);
+ }
+
+ protected byte[] loadTestPhoto(PhotoSize size) {
+ loadTestPhoto();
+ return testPhotoEntry.getPhoto(size);
+ }
+
+ protected byte[] loadPhotoFromResource(int resourceId, PhotoSize size) {
+ PhotoEntry entry = photoResourceCache.get(resourceId);
+ if (entry == null) {
+ final Resources resources = getTestContext().getResources();
+ InputStream is = resources.openRawResource(resourceId);
+ byte[] content = readInputStreamFully(is);
+ entry = new PhotoEntry(content);
+ photoResourceCache.put(resourceId, entry);
+ }
+ return entry.getPhoto(size);
+ }
+
+ protected byte[] readInputStreamFully(InputStream is) {
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ byte[] buffer = new byte[10000];
+ int count;
+ try {
+ while ((count = is.read(buffer)) != -1) {
+ os.write(buffer, 0, count);
+ }
+ is.close();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ return os.toByteArray();
+ }
+}
diff --git a/tests/src/com/android/providers/contacts/PhotoStoreTest.java b/tests/src/com/android/providers/contacts/PhotoStoreTest.java
new file mode 100644
index 0000000..9b7c50d
--- /dev/null
+++ b/tests/src/com/android/providers/contacts/PhotoStoreTest.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.providers.contacts;
+
+import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
+import com.android.providers.contacts.tests.R;
+
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.PhotoFiles;
+import android.test.mock.MockContentResolver;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import static com.android.providers.contacts.ContactsActor.PACKAGE_GREY;
+
+/**
+ * Tests for {@link PhotoStore}.
+ */
+@LargeTest
+public class PhotoStoreTest extends PhotoLoadingTestCase {
+
+ private ContactsActor mActor;
+ private SynchronousContactsProvider2 mProvider;
+ private SQLiteDatabase mDb;
+
+ // The object under test.
+ private PhotoStore mPhotoStore;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mActor = new ContactsActor(getContext(), PACKAGE_GREY, SynchronousContactsProvider2.class,
+ ContactsContract.AUTHORITY);
+ mProvider = ((SynchronousContactsProvider2) mActor.provider);
+ mPhotoStore = mProvider.getPhotoStore();
+ mProvider.wipeData();
+ mDb = mProvider.getDatabaseHelper(getContext()).getReadableDatabase();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ mPhotoStore.clear();
+ }
+
+ public void testStoreThumbnailPhoto() throws IOException {
+ byte[] photo = loadPhotoFromResource(R.drawable.earth_small, PhotoSize.ORIGINAL);
+
+ // Since the photo is already thumbnail-sized, no file will be stored.
+ assertEquals(0, mPhotoStore.insert(new PhotoProcessor(photo, 256, 96)));
+ }
+
+ public void testStoreMediumPhoto() throws IOException {
+ runStorageTestForResource(R.drawable.earth_normal);
+ }
+
+ public void testStoreLargePhoto() throws IOException {
+ runStorageTestForResource(R.drawable.earth_large);
+ }
+
+ public void testStoreHugePhoto() throws IOException {
+ runStorageTestForResource(R.drawable.earth_huge);
+ }
+
+ /**
+ * Runs the following steps:
+ * - Loads the given photo resource.
+ * - Inserts it into the photo store.
+ * - Checks that the photo has a photo file ID.
+ * - Loads the expected display photo for the resource.
+ * - Gets the photo entry from the photo store.
+ * - Loads the photo entry's file content from disk.
+ * - Compares the expected photo content to the disk content.
+ * - Queries the contacts provider for the photo file entry, checks for its
+ * existence, and matches it up against the expected metadata.
+ * - Checks that the total storage taken up by the photo store is equal to
+ * the size of the photo.
+ * @param resourceId The resource ID of the photo file to test.
+ */
+ public void runStorageTestForResource(int resourceId) throws IOException {
+ byte[] photo = loadPhotoFromResource(resourceId, PhotoSize.ORIGINAL);
+ long photoFileId = mPhotoStore.insert(new PhotoProcessor(photo, 256, 96));
+ assertTrue(photoFileId != 0);
+
+ byte[] expectedStoredVersion = loadPhotoFromResource(resourceId, PhotoSize.DISPLAY_PHOTO);
+ File storedFile = new File(mPhotoStore.get(photoFileId).path);
+ assertTrue(storedFile.exists());
+ byte[] storedVersion = readInputStreamFully(new FileInputStream(storedFile));
+ assertEquals(Hex.encodeHex(expectedStoredVersion, false),
+ Hex.encodeHex(storedVersion, false));
+
+ Cursor c = mDb.query(Tables.PHOTO_FILES,
+ new String[]{PhotoFiles.HEIGHT, PhotoFiles.WIDTH, PhotoFiles.FILESIZE},
+ PhotoFiles._ID + "=?", new String[]{String.valueOf(photoFileId)}, null, null, null);
+ try {
+ assertEquals(1, c.getCount());
+ c.moveToFirst();
+ assertEquals(256, c.getInt(0));
+ assertEquals(256, c.getInt(1));
+ assertEquals(expectedStoredVersion.length, c.getInt(2));
+ } finally {
+ c.close();
+ }
+
+ assertEquals(expectedStoredVersion.length, mPhotoStore.getTotalSize());
+ }
+
+ public void testRemoveEntry() throws IOException {
+ byte[] photo = loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.ORIGINAL);
+ long photoFileId = mPhotoStore.insert(new PhotoProcessor(photo, 256, 96));
+ PhotoStore.Entry entry = mPhotoStore.get(photoFileId);
+ assertTrue(new File(entry.path).exists());
+
+ mPhotoStore.remove(photoFileId);
+
+ // Check that the file has been deleted.
+ assertFalse(new File(entry.path).exists());
+
+ // Check that the database record has also been removed.
+ Cursor c = mDb.query(Tables.PHOTO_FILES, new String[]{PhotoFiles._ID},
+ PhotoFiles._ID + "=?", new String[]{String.valueOf(photoFileId)}, null, null, null);
+ try {
+ assertEquals(0, c.getCount());
+ } finally {
+ c.close();
+ }
+ }
+
+ public void testCleanup() throws IOException {
+ // Load some photos into the store.
+ Set<Long> photoFileIds = new HashSet<Long>();
+ Map<Integer, Long> resourceIdToPhotoMap = new HashMap<Integer, Long>();
+ int[] resourceIds = new int[] {
+ R.drawable.earth_normal, R.drawable.earth_large, R.drawable.earth_huge
+ };
+ for (int resourceId : resourceIds) {
+ long photoFileId = mPhotoStore.insert(
+ new PhotoProcessor(loadPhotoFromResource(resourceId, PhotoSize.ORIGINAL),
+ 256, 96));
+ resourceIdToPhotoMap.put(resourceId, photoFileId);
+ photoFileIds.add(photoFileId);
+ }
+ assertFalse(photoFileIds.contains(0L));
+ assertEquals(3, photoFileIds.size());
+
+ // Run cleanup with the indication that only the large and huge photos are in use, along
+ // with a bogus photo file ID that isn't in the photo store.
+ long bogusPhotoFileId = 42;
+ Set<Long> photoFileIdsInUse = new HashSet<Long>();
+ photoFileIdsInUse.add(resourceIdToPhotoMap.get(R.drawable.earth_large));
+ photoFileIdsInUse.add(resourceIdToPhotoMap.get(R.drawable.earth_huge));
+ photoFileIdsInUse.add(bogusPhotoFileId);
+
+ Set<Long> photoIdsToCleanup = mPhotoStore.cleanup(photoFileIdsInUse);
+
+ // The set of photo IDs to clean up should consist of the bogus photo file ID.
+ assertEquals(1, photoIdsToCleanup.size());
+ assertTrue(photoIdsToCleanup.contains(bogusPhotoFileId));
+
+ // The entry for the normal-sized photo should have been cleaned up, since it isn't being
+ // used.
+ long normalPhotoId = resourceIdToPhotoMap.get(R.drawable.earth_normal);
+ assertNull(mPhotoStore.get(normalPhotoId));
+
+ // Check that the database record has also been removed.
+ Cursor c = mDb.query(Tables.PHOTO_FILES, new String[]{PhotoFiles._ID},
+ PhotoFiles._ID + "=?", new String[]{String.valueOf(normalPhotoId)},
+ null, null, null);
+ try {
+ assertEquals(0, c.getCount());
+ } finally {
+ c.close();
+ }
+ }
+}