/*
* 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();
}
}
}