/* * 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 static com.android.providers.contacts.ContactsActor.PACKAGE_GREY; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.provider.ContactsContract; import android.provider.ContactsContract.PhotoFiles; import android.test.suitebuilder.annotation.MediumTest; import com.android.providers.contacts.ContactsDatabaseHelper.Tables; import com.android.providers.contacts.tests.R; 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; /** * Tests for {@link PhotoStore}. */ @MediumTest 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(newPhotoProcessor(photo, false))); } public void testStore200Photo() throws IOException { // As 200 is below the full photo size, we don't want to see it upscaled runStorageTestForResource(R.drawable.earth_200, 200, 200); } public void testStoreNonSquare300x200Photo() throws IOException { // The longer side should be downscaled to the target size runStorageTestForResource(R.drawable.earth_300x200, 256, 170); } public void testStoreNonSquare300x200PhotoWithCrop() throws IOException { // As 300x200 is below the full photo size, we don't want to see it upscaled // This one is not square, so we expect the longer side to be cropped runStorageTestForResourceWithCrop(R.drawable.earth_300x200, 200, 200); } public void testStoreNonSquare600x400PhotoWithCrop() throws IOException { // As 600x400 is above the full photo size, we expect the picture to be cropped and then // scaled runStorageTestForResourceWithCrop(R.drawable.earth_600x400, 256, 256); } public void testStoreMediumPhoto() throws IOException { // Source Image is 256x256 runStorageTestForResource(R.drawable.earth_normal, 256, 256); } public void testStoreLargePhoto() throws IOException { // Source image is 512x512 runStorageTestForResource(R.drawable.earth_large, 256, 256); } public void testStoreHugePhoto() throws IOException { // Source image is 1024x1024 runStorageTestForResource(R.drawable.earth_huge, 256, 256); } /** * 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, int expectedWidth, int expectedHeight) throws IOException { byte[] photo = loadPhotoFromResource(resourceId, PhotoSize.ORIGINAL); long photoFileId = mPhotoStore.insert(newPhotoProcessor(photo, false)); assertTrue(photoFileId != 0); File storedFile = new File(mPhotoStore.get(photoFileId).path); assertTrue(storedFile.exists()); byte[] actualStoredVersion = readInputStreamFully(new FileInputStream(storedFile)); byte[] expectedStoredVersion = loadPhotoFromResource(resourceId, PhotoSize.DISPLAY_PHOTO); EvenMoreAsserts.assertImageRawData(getContext(), expectedStoredVersion, actualStoredVersion); Cursor c = mDb.query(Tables.PHOTO_FILES, new String[]{PhotoFiles.WIDTH, PhotoFiles.HEIGHT, PhotoFiles.FILESIZE}, PhotoFiles._ID + "=?", new String[]{String.valueOf(photoFileId)}, null, null, null); try { assertEquals(1, c.getCount()); c.moveToFirst(); assertEquals(expectedWidth + "/" + expectedHeight, c.getInt(0) + "/" + c.getInt(1)); assertEquals(expectedStoredVersion.length, c.getInt(2)); } finally { c.close(); } assertEquals(expectedStoredVersion.length, mPhotoStore.getTotalSize()); } public void runStorageTestForResourceWithCrop(int resourceId, int expectedWidth, int expectedHeight) throws IOException { byte[] photo = loadPhotoFromResource(resourceId, PhotoSize.ORIGINAL); long photoFileId = mPhotoStore.insert(newPhotoProcessor(photo, true)); assertTrue(photoFileId != 0); 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(expectedWidth + "/" + expectedHeight, c.getInt(0) + "/" + c.getInt(1)); } finally { c.close(); } } public void testRemoveEntry() throws IOException { byte[] photo = loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.ORIGINAL); long photoFileId = mPhotoStore.insert(newPhotoProcessor(photo, false)); 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 photoFileIds = new HashSet(); Map resourceIdToPhotoMap = new HashMap(); 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 = 123456789; Set photoFileIdsInUse = new HashSet(); photoFileIdsInUse.add(resourceIdToPhotoMap.get(R.drawable.earth_large)); photoFileIdsInUse.add(resourceIdToPhotoMap.get(R.drawable.earth_huge)); photoFileIdsInUse.add(bogusPhotoFileId); Set 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(); } } }