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