summaryrefslogtreecommitdiffstats
path: root/tests/src/com/android/photos
diff options
context:
space:
mode:
Diffstat (limited to 'tests/src/com/android/photos')
-rw-r--r--tests/src/com/android/photos/data/DataTestRunner.java46
-rw-r--r--tests/src/com/android/photos/data/MediaCacheTest.java388
-rw-r--r--tests/src/com/android/photos/data/PhotoDatabaseTest.java229
-rw-r--r--tests/src/com/android/photos/data/PhotoDatabaseUtils.java135
-rw-r--r--tests/src/com/android/photos/data/PhotoProviderTest.java391
-rw-r--r--tests/src/com/android/photos/data/TestHelper.java53
6 files changed, 1242 insertions, 0 deletions
diff --git a/tests/src/com/android/photos/data/DataTestRunner.java b/tests/src/com/android/photos/data/DataTestRunner.java
new file mode 100644
index 000000000..10618d67d
--- /dev/null
+++ b/tests/src/com/android/photos/data/DataTestRunner.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2013 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.photos.data;
+
+import android.test.InstrumentationTestRunner;
+import android.test.InstrumentationTestSuite;
+
+import com.android.photos.data.TestHelper.TestInitialization;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+public class DataTestRunner extends InstrumentationTestRunner {
+ @Override
+ public TestSuite getAllTests() {
+ TestSuite suite = new InstrumentationTestSuite(this);
+ suite.addTestSuite(PhotoDatabaseTest.class);
+ suite.addTestSuite(PhotoProviderTest.class);
+ TestHelper.addTests(MediaCacheTest.class, suite, new TestInitialization() {
+ @Override
+ public void initialize(TestCase testCase) {
+ MediaCacheTest test = (MediaCacheTest) testCase;
+ test.setLocalContext(getContext());
+ }
+ });
+ return suite;
+ }
+
+ @Override
+ public ClassLoader getLoader() {
+ return DataTestRunner.class.getClassLoader();
+ }
+}
diff --git a/tests/src/com/android/photos/data/MediaCacheTest.java b/tests/src/com/android/photos/data/MediaCacheTest.java
new file mode 100644
index 000000000..9e7112807
--- /dev/null
+++ b/tests/src/com/android/photos/data/MediaCacheTest.java
@@ -0,0 +1,388 @@
+/*
+ * Copyright (C) 2013 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.photos.data;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.os.Environment;
+import android.os.SystemClock;
+import android.test.ProviderTestCase2;
+
+import com.android.gallery3d.tests.R;
+import com.android.photos.data.MediaCache.ImageReady;
+import com.android.photos.data.MediaCache.OriginalReady;
+import com.android.photos.data.MediaRetriever.MediaSize;
+import com.android.photos.data.PhotoProvider.Photos;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class MediaCacheTest extends ProviderTestCase2<PhotoProvider> {
+ @SuppressWarnings("unused")
+ private static final String TAG = MediaCacheTest.class.getSimpleName();
+
+ private File mDir;
+ private File mImage;
+ private File mCacheDir;
+ private Resources mResources;
+ private MediaCache mMediaCache;
+ private ReadyCollector mReady;
+
+ public static final long MAX_WAIT = 2000;
+
+ private static class ReadyCollector implements ImageReady, OriginalReady {
+ public File mOriginalFile;
+ public InputStream mInputStream;
+
+ @Override
+ public synchronized void originalReady(File originalFile) {
+ mOriginalFile = originalFile;
+ notifyAll();
+ }
+
+ @Override
+ public synchronized void imageReady(InputStream bitmapInputStream) {
+ mInputStream = bitmapInputStream;
+ notifyAll();
+ }
+
+ public synchronized boolean waitForNotification() {
+ long endWait = SystemClock.uptimeMillis() + MAX_WAIT;
+
+ try {
+ while (mInputStream == null && mOriginalFile == null
+ && SystemClock.uptimeMillis() < endWait) {
+ wait(endWait - SystemClock.uptimeMillis());
+ }
+ } catch (InterruptedException e) {
+ }
+ return mInputStream != null || mOriginalFile != null;
+ }
+ }
+
+ private static class DummyMediaRetriever implements MediaRetriever {
+ private boolean mNullUri = false;
+ @Override
+ public File getLocalFile(Uri contentUri) {
+ return null;
+ }
+
+ @Override
+ public MediaSize getFastImageSize(Uri contentUri, MediaSize size) {
+ return null;
+ }
+
+ @Override
+ public byte[] getTemporaryImage(Uri contentUri, MediaSize temporarySize) {
+ return null;
+ }
+
+ @Override
+ public boolean getMedia(Uri contentUri, MediaSize imageSize, File tempFile) {
+ return false;
+ }
+
+ @Override
+ public Uri normalizeUri(Uri contentUri, MediaSize size) {
+ if (mNullUri) {
+ return null;
+ } else {
+ return contentUri;
+ }
+ }
+
+ @Override
+ public MediaSize normalizeMediaSize(Uri contentUri, MediaSize size) {
+ return size;
+ }
+
+ public void setNullUri() {
+ mNullUri = true;
+ }
+ };
+
+ public MediaCacheTest() {
+ super(PhotoProvider.class, PhotoProvider.AUTHORITY);
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mReady = new ReadyCollector();
+ File externalDir = Environment.getExternalStorageDirectory();
+ mDir = new File(externalDir, "test");
+ mDir.mkdirs();
+ mCacheDir = new File(externalDir, "test_cache");
+ mImage = new File(mDir, "original.jpg");
+ MediaCache.initialize(getMockContext());
+ MediaCache.getInstance().setCacheDir(mCacheDir);
+ mMediaCache = MediaCache.getInstance();
+ mMediaCache.addRetriever("file", "", new FileRetriever());
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ super.tearDown();
+ mMediaCache.clearCacheDir();
+ MediaCache.shutdown();
+ mMediaCache = null;
+ mImage.delete();
+ mDir.delete();
+ mCacheDir.delete();
+ }
+
+ public void setLocalContext(Context context) {
+ mResources = context.getResources();
+ }
+
+ public void testRetrieveOriginal() throws IOException {
+ copyResourceToFile(R.raw.galaxy_nexus, mImage.getPath());
+ Uri uri = Uri.fromFile(mImage);
+ mMediaCache.retrieveOriginal(uri, mReady, null);
+ assertTrue(mReady.waitForNotification());
+ assertNull(mReady.mInputStream);
+ assertEquals(mImage, mReady.mOriginalFile);
+ }
+
+ public void testRetrievePreview() throws IOException {
+ copyResourceToFile(R.raw.galaxy_nexus, mImage.getPath());
+ Uri uri = Uri.fromFile(mImage);
+ mMediaCache.retrievePreview(uri, mReady, null);
+ assertTrue(mReady.waitForNotification());
+ assertNotNull(mReady.mInputStream);
+ assertNull(mReady.mOriginalFile);
+ Bitmap bitmap = BitmapFactory.decodeStream(mReady.mInputStream);
+ mReady.mInputStream.close();
+ assertNotNull(bitmap);
+ Bitmap original = BitmapFactory.decodeFile(mImage.getPath());
+ assertTrue(bitmap.getWidth() < original.getWidth());
+ assertTrue(bitmap.getHeight() < original.getHeight());
+ int maxDimension = Math.max(bitmap.getWidth(), bitmap.getHeight());
+ int targetSize = MediaCacheUtils.getTargetSize(MediaSize.Preview);
+ assertTrue(maxDimension >= targetSize);
+ assertTrue(maxDimension < (targetSize * 2));
+ }
+
+ public void testRetrieveExifThumb() throws IOException {
+ copyResourceToFile(R.raw.galaxy_nexus, mImage.getPath());
+ Uri uri = Uri.fromFile(mImage);
+ ReadyCollector done = new ReadyCollector();
+ mMediaCache.retrieveThumbnail(uri, done, mReady);
+ assertTrue(mReady.waitForNotification());
+ assertNotNull(mReady.mInputStream);
+ assertNull(mReady.mOriginalFile);
+ Bitmap bitmap = BitmapFactory.decodeStream(mReady.mInputStream);
+ mReady.mInputStream.close();
+ assertTrue(done.waitForNotification());
+ assertNotNull(done.mInputStream);
+ done.mInputStream.close();
+ assertNotNull(bitmap);
+ assertEquals(320, bitmap.getWidth());
+ assertEquals(240, bitmap.getHeight());
+ }
+
+ public void testRetrieveThumb() throws IOException {
+ copyResourceToFile(R.raw.galaxy_nexus, mImage.getPath());
+ Uri uri = Uri.fromFile(mImage);
+ long downsampleStart = SystemClock.uptimeMillis();
+ mMediaCache.retrieveThumbnail(uri, mReady, null);
+ assertTrue(mReady.waitForNotification());
+ long downsampleEnd = SystemClock.uptimeMillis();
+ assertNotNull(mReady.mInputStream);
+ assertNull(mReady.mOriginalFile);
+ Bitmap bitmap = BitmapFactory.decodeStream(mReady.mInputStream);
+ mReady.mInputStream.close();
+ assertNotNull(bitmap);
+ Bitmap original = BitmapFactory.decodeFile(mImage.getPath());
+ assertTrue(bitmap.getWidth() < original.getWidth());
+ assertTrue(bitmap.getHeight() < original.getHeight());
+ int maxDimension = Math.max(bitmap.getWidth(), bitmap.getHeight());
+ int targetSize = MediaCacheUtils.getTargetSize(MediaSize.Thumbnail);
+ assertTrue(maxDimension >= targetSize);
+ assertTrue(maxDimension < (targetSize * 2));
+
+ // Retrieve cached thumb.
+ mReady = new ReadyCollector();
+ long start = SystemClock.uptimeMillis();
+ mMediaCache.retrieveThumbnail(uri, mReady, null);
+ assertTrue(mReady.waitForNotification());
+ mReady.mInputStream.close();
+ long end = SystemClock.uptimeMillis();
+ // Already cached. Wait shorter time.
+ assertTrue((end - start) < (downsampleEnd - downsampleStart) / 2);
+ }
+
+ public void testGetVideo() throws IOException {
+ mImage = new File(mDir, "original.mp4");
+ copyResourceToFile(R.raw.android_lawn, mImage.getPath());
+ Uri uri = Uri.fromFile(mImage);
+
+ mMediaCache.retrieveOriginal(uri, mReady, null);
+ assertTrue(mReady.waitForNotification());
+ assertNull(mReady.mInputStream);
+ assertNotNull(mReady.mOriginalFile);
+
+ mReady = new ReadyCollector();
+ mMediaCache.retrievePreview(uri, mReady, null);
+ assertTrue(mReady.waitForNotification());
+ assertNotNull(mReady.mInputStream);
+ assertNull(mReady.mOriginalFile);
+ Bitmap bitmap = BitmapFactory.decodeStream(mReady.mInputStream);
+ mReady.mInputStream.close();
+ int maxDimension = Math.max(bitmap.getWidth(), bitmap.getHeight());
+ int targetSize = MediaCacheUtils.getTargetSize(MediaSize.Preview);
+ assertTrue(maxDimension >= targetSize);
+ assertTrue(maxDimension < (targetSize * 2));
+
+ mReady = new ReadyCollector();
+ mMediaCache.retrieveThumbnail(uri, mReady, null);
+ assertTrue(mReady.waitForNotification());
+ assertNotNull(mReady.mInputStream);
+ assertNull(mReady.mOriginalFile);
+ bitmap = BitmapFactory.decodeStream(mReady.mInputStream);
+ mReady.mInputStream.close();
+ maxDimension = Math.max(bitmap.getWidth(), bitmap.getHeight());
+ targetSize = MediaCacheUtils.getTargetSize(MediaSize.Thumbnail);
+ assertTrue(maxDimension >= targetSize);
+ assertTrue(maxDimension < (targetSize * 2));
+ }
+
+ public void testFastImage() throws IOException {
+ copyResourceToFile(R.raw.galaxy_nexus, mImage.getPath());
+ Uri uri = Uri.fromFile(mImage);
+ mMediaCache.retrieveThumbnail(uri, mReady, null);
+ mReady.waitForNotification();
+ mReady.mInputStream.close();
+
+ mMediaCache.retrieveOriginal(uri, mReady, null);
+ assertTrue(mReady.waitForNotification());
+ assertNotNull(mReady.mInputStream);
+ mReady.mInputStream.close();
+ }
+
+ public void testBadRetriever() {
+ Uri uri = Photos.CONTENT_URI;
+ try {
+ mMediaCache.retrieveOriginal(uri, mReady, null);
+ fail("Expected exception");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+ }
+
+ public void testInsertIntoCache() throws IOException {
+ // FileRetriever inserts into the cache opportunistically with Videos
+ mImage = new File(mDir, "original.mp4");
+ copyResourceToFile(R.raw.android_lawn, mImage.getPath());
+ Uri uri = Uri.fromFile(mImage);
+
+ mMediaCache.retrieveThumbnail(uri, mReady, null);
+ assertTrue(mReady.waitForNotification());
+ mReady.mInputStream.close();
+ assertNotNull(mMediaCache.getCachedFile(uri, MediaSize.Preview));
+ }
+
+ public void testBadNormalizedUri() {
+ DummyMediaRetriever retriever = new DummyMediaRetriever();
+ Uri uri = Uri.fromParts("http", "world", "morestuff");
+ mMediaCache.addRetriever(uri.getScheme(), uri.getAuthority(), retriever);
+ retriever.setNullUri();
+ try {
+ mMediaCache.retrieveOriginal(uri, mReady, null);
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+ }
+
+ public void testClearOldCache() throws IOException {
+ copyResourceToFile(R.raw.galaxy_nexus, mImage.getPath());
+ Uri uri = Uri.fromFile(mImage);
+ mMediaCache.retrievePreview(uri, mReady, null);
+ assertTrue(mReady.waitForNotification());
+ mReady.mInputStream.close();
+ mMediaCache.setMaxCacheSize(mMediaCache.getCachedFile(uri, MediaSize.Preview).length());
+ assertNotNull(mMediaCache.getCachedFile(uri, MediaSize.Preview));
+
+ mReady = new ReadyCollector();
+ // This should kick the preview image out of the cache.
+ mMediaCache.retrieveThumbnail(uri, mReady, null);
+ assertTrue(mReady.waitForNotification());
+ mReady.mInputStream.close();
+ assertNull(mMediaCache.getCachedFile(uri, MediaSize.Preview));
+ assertNotNull(mMediaCache.getCachedFile(uri, MediaSize.Thumbnail));
+ }
+
+ public void testClearLargeInCache() throws IOException {
+ copyResourceToFile(R.raw.galaxy_nexus, mImage.getPath());
+ Uri imageUri = Uri.fromFile(mImage);
+ mMediaCache.retrieveThumbnail(imageUri, mReady, null);
+ assertTrue(mReady.waitForNotification());
+ mReady.mInputStream.close();
+ assertNotNull(mMediaCache.getCachedFile(imageUri, MediaSize.Thumbnail));
+ long thumbSize = mMediaCache.getCachedFile(imageUri, MediaSize.Thumbnail).length();
+ mMediaCache.setMaxCacheSize(thumbSize * 10);
+
+ for (int i = 0; i < 9; i++) {
+ File tempImage = new File(mDir, "image" + i + ".jpg");
+ mImage.renameTo(tempImage);
+ Uri tempImageUri = Uri.fromFile(tempImage);
+ mReady = new ReadyCollector();
+ mMediaCache.retrieveThumbnail(tempImageUri, mReady, null);
+ assertTrue(mReady.waitForNotification());
+ mReady.mInputStream.close();
+ tempImage.renameTo(mImage);
+ }
+ assertNotNull(mMediaCache.getCachedFile(imageUri, MediaSize.Thumbnail));
+
+ for (int i = 0; i < 9; i++) {
+ File tempImage = new File(mDir, "image" + i + ".jpg");
+ mImage.renameTo(tempImage);
+ Uri tempImageUri = Uri.fromFile(tempImage);
+ mReady = new ReadyCollector();
+ mMediaCache.retrievePreview(tempImageUri, mReady, null);
+ assertTrue(mReady.waitForNotification());
+ mReady.mInputStream.close();
+ tempImage.renameTo(mImage);
+ }
+ assertNotNull(mMediaCache.getCachedFile(imageUri, MediaSize.Thumbnail));
+ Uri oldestUri = Uri.fromFile(new File(mDir, "image0.jpg"));
+ assertNull(mMediaCache.getCachedFile(oldestUri, MediaSize.Thumbnail));
+ }
+
+ private void copyResourceToFile(int resourceId, String path) throws IOException {
+ File outputDir = new File(path).getParentFile();
+ outputDir.mkdirs();
+
+ InputStream in = mResources.openRawResource(resourceId);
+ FileOutputStream out = new FileOutputStream(path);
+ byte[] buffer = new byte[1000];
+ int bytesRead;
+
+ while ((bytesRead = in.read(buffer)) >= 0) {
+ out.write(buffer, 0, bytesRead);
+ }
+
+ in.close();
+ out.close();
+ }
+}
diff --git a/tests/src/com/android/photos/data/PhotoDatabaseTest.java b/tests/src/com/android/photos/data/PhotoDatabaseTest.java
new file mode 100644
index 000000000..e7c168947
--- /dev/null
+++ b/tests/src/com/android/photos/data/PhotoDatabaseTest.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2013 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.photos.data;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.database.DatabaseUtils;
+import android.database.sqlite.SQLiteDatabase;
+import android.test.InstrumentationTestCase;
+
+import com.android.photos.data.PhotoProvider.Accounts;
+import com.android.photos.data.PhotoProvider.Albums;
+import com.android.photos.data.PhotoProvider.Metadata;
+import com.android.photos.data.PhotoProvider.Photos;
+
+import java.io.File;
+import java.io.IOException;
+
+public class PhotoDatabaseTest extends InstrumentationTestCase {
+
+ private PhotoDatabase mDBHelper;
+ private static final String DB_NAME = "dummy.db";
+ private static final long PARENT_ID1 = 100;
+ private static final long PARENT_ID2 = 101;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ Context context = getInstrumentation().getTargetContext();
+ context.deleteDatabase(DB_NAME);
+ mDBHelper = new PhotoDatabase(context, DB_NAME);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ mDBHelper.close();
+ mDBHelper = null;
+ Context context = getInstrumentation().getTargetContext();
+ context.deleteDatabase(DB_NAME);
+ super.tearDown();
+ }
+
+ public void testCreateDatabase() throws IOException {
+ Context context = getInstrumentation().getTargetContext();
+ File dbFile = context.getDatabasePath(DB_NAME);
+ SQLiteDatabase db = getReadableDB();
+ db.beginTransaction();
+ db.endTransaction();
+ assertTrue(dbFile.exists());
+ }
+
+ public void testTables() {
+ validateTable(Metadata.TABLE, PhotoDatabaseUtils.PROJECTION_METADATA);
+ validateTable(Albums.TABLE, PhotoDatabaseUtils.PROJECTION_ALBUMS);
+ validateTable(Photos.TABLE, PhotoDatabaseUtils.PROJECTION_PHOTOS);
+ }
+
+ public void testAlbumsConstraints() {
+ SQLiteDatabase db = getWritableDB();
+ db.beginTransaction();
+ try {
+ long accountId = 100;
+ // Test NOT NULL constraint on name
+ assertFalse(PhotoDatabaseUtils.insertAlbum(db, null, null, Albums.VISIBILITY_PRIVATE,
+ accountId));
+
+ // test NOT NULL constraint on privacy
+ assertFalse(PhotoDatabaseUtils.insertAlbum(db, null, "hello", null, accountId));
+
+ // test NOT NULL constraint on account_id
+ assertFalse(PhotoDatabaseUtils.insertAlbum(db, null, "hello",
+ Albums.VISIBILITY_PRIVATE, null));
+
+ // Normal insert
+ assertTrue(PhotoDatabaseUtils.insertAlbum(db, PARENT_ID1, "hello",
+ Albums.VISIBILITY_PRIVATE, accountId));
+
+ long albumId = PhotoDatabaseUtils.queryAlbumIdFromParentId(db, PARENT_ID1);
+
+ // Assign a valid child
+ assertTrue(PhotoDatabaseUtils.insertAlbum(db, PARENT_ID2, "hello",
+ Albums.VISIBILITY_PRIVATE, accountId));
+
+ long otherAlbumId = PhotoDatabaseUtils.queryAlbumIdFromParentId(db, PARENT_ID2);
+ assertNotSame(albumId, otherAlbumId);
+
+ // This is a valid child of another album.
+ assertTrue(PhotoDatabaseUtils.insertAlbum(db, otherAlbumId, "hello",
+ Albums.VISIBILITY_PRIVATE, accountId));
+
+ // This isn't allowed due to uniqueness constraint (parent_id/name)
+ assertFalse(PhotoDatabaseUtils.insertAlbum(db, otherAlbumId, "hello",
+ Albums.VISIBILITY_PRIVATE, accountId));
+ } finally {
+ db.endTransaction();
+ }
+ }
+
+ public void testPhotosConstraints() {
+ SQLiteDatabase db = getWritableDB();
+ db.beginTransaction();
+ try {
+ int width = 100;
+ int height = 100;
+ long dateTaken = System.currentTimeMillis();
+ String mimeType = "test/test";
+ long accountId = 100;
+
+ // Test NOT NULL mime-type
+ assertFalse(PhotoDatabaseUtils.insertPhoto(db, width, height, dateTaken, null, null,
+ accountId));
+
+ // Test NOT NULL width
+ assertFalse(PhotoDatabaseUtils.insertPhoto(db, null, height, dateTaken, null, mimeType,
+ accountId));
+
+ // Test NOT NULL height
+ assertFalse(PhotoDatabaseUtils.insertPhoto(db, width, null, dateTaken, null, mimeType,
+ accountId));
+
+ // Test NOT NULL dateTaken
+ assertFalse(PhotoDatabaseUtils.insertPhoto(db, width, height, null, null, mimeType,
+ accountId));
+
+ // Test NOT NULL accountId
+ assertFalse(PhotoDatabaseUtils.insertPhoto(db, width, height, dateTaken, null,
+ mimeType, null));
+
+ // Test normal insert
+ assertTrue(PhotoDatabaseUtils.insertPhoto(db, width, height, dateTaken, null, mimeType,
+ accountId));
+ } finally {
+ db.endTransaction();
+ }
+ }
+
+ public void testMetadataConstraints() {
+ SQLiteDatabase db = getWritableDB();
+ db.beginTransaction();
+ try {
+ final String mimeType = "test/test";
+ PhotoDatabaseUtils.insertPhoto(db, 100, 100, 100L, PARENT_ID1, mimeType, 100L);
+ long photoId = PhotoDatabaseUtils.queryPhotoIdFromAlbumId(db, PARENT_ID1);
+
+ // Test NOT NULL PHOTO_ID constraint.
+ assertFalse(PhotoDatabaseUtils.insertMetadata(db, null, "foo", "bar"));
+
+ // Normal insert.
+ assertTrue(PhotoDatabaseUtils.insertMetadata(db, photoId, "foo", "bar"));
+
+ // Test uniqueness constraint.
+ assertFalse(PhotoDatabaseUtils.insertMetadata(db, photoId, "foo", "baz"));
+ } finally {
+ db.endTransaction();
+ }
+ }
+
+ public void testAccountsConstraints() {
+ SQLiteDatabase db = getWritableDB();
+ db.beginTransaction();
+ try {
+ assertFalse(PhotoDatabaseUtils.insertAccount(db, null));
+ assertTrue(PhotoDatabaseUtils.insertAccount(db, "hello"));
+ assertTrue(PhotoDatabaseUtils.insertAccount(db, "hello"));
+ } finally {
+ db.endTransaction();
+ }
+ }
+
+ public void testUpgrade() {
+ SQLiteDatabase db = getWritableDB();
+ db.beginTransaction();
+ try {
+ assertTrue(PhotoDatabaseUtils.insertAccount(db, "Hello"));
+ assertTrue(PhotoDatabaseUtils.insertAlbum(db, PARENT_ID1, "hello",
+ Albums.VISIBILITY_PRIVATE, 100L));
+ final String mimeType = "test/test";
+ assertTrue(PhotoDatabaseUtils.insertPhoto(db, 100, 100, 100L, PARENT_ID1, mimeType,
+ 100L));
+ // Normal insert.
+ assertTrue(PhotoDatabaseUtils.insertMetadata(db, 100L, "foo", "bar"));
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ }
+ mDBHelper.close();
+ Context context = getInstrumentation().getTargetContext();
+ mDBHelper = new PhotoDatabase(context, DB_NAME, PhotoDatabase.DB_VERSION + 1);
+ db = getReadableDB();
+ assertEquals(0, DatabaseUtils.queryNumEntries(db, Accounts.TABLE));
+ assertEquals(0, DatabaseUtils.queryNumEntries(db, Photos.TABLE));
+ assertEquals(0, DatabaseUtils.queryNumEntries(db, Albums.TABLE));
+ assertEquals(0, DatabaseUtils.queryNumEntries(db, Metadata.TABLE));
+ }
+
+ private SQLiteDatabase getReadableDB() {
+ return mDBHelper.getReadableDatabase();
+ }
+
+ private SQLiteDatabase getWritableDB() {
+ return mDBHelper.getWritableDatabase();
+ }
+
+ private void validateTable(String table, String[] projection) {
+ SQLiteDatabase db = getReadableDB();
+ Cursor cursor = db.query(table, projection, null, null, null, null, null);
+ assertNotNull(cursor);
+ assertEquals(cursor.getCount(), 0);
+ assertEquals(cursor.getColumnCount(), projection.length);
+ for (int i = 0; i < projection.length; i++) {
+ assertEquals(cursor.getColumnName(i), projection[i]);
+ }
+ }
+
+
+}
diff --git a/tests/src/com/android/photos/data/PhotoDatabaseUtils.java b/tests/src/com/android/photos/data/PhotoDatabaseUtils.java
new file mode 100644
index 000000000..f7a46d419
--- /dev/null
+++ b/tests/src/com/android/photos/data/PhotoDatabaseUtils.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2013 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.photos.data;
+
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+
+import com.android.photos.data.PhotoProvider.Accounts;
+import com.android.photos.data.PhotoProvider.Albums;
+import com.android.photos.data.PhotoProvider.Metadata;
+import com.android.photos.data.PhotoProvider.Photos;
+
+import junit.framework.AssertionFailedError;
+
+public class PhotoDatabaseUtils {
+ public static String[] PROJECTION_ALBUMS = {
+ Albums._ID,
+ Albums.ACCOUNT_ID,
+ Albums.PARENT_ID,
+ Albums.VISIBILITY,
+ Albums.LOCATION_STRING,
+ Albums.TITLE,
+ Albums.SUMMARY,
+ Albums.DATE_PUBLISHED,
+ Albums.DATE_MODIFIED,
+ };
+
+ public static String[] PROJECTION_METADATA = {
+ Metadata.PHOTO_ID,
+ Metadata.KEY,
+ Metadata.VALUE,
+ };
+
+ public static String[] PROJECTION_PHOTOS = {
+ Photos._ID,
+ Photos.ACCOUNT_ID,
+ Photos.WIDTH,
+ Photos.HEIGHT,
+ Photos.DATE_TAKEN,
+ Photos.ALBUM_ID,
+ Photos.MIME_TYPE,
+ Photos.TITLE,
+ Photos.DATE_MODIFIED,
+ Photos.ROTATION,
+ };
+
+ public static String[] PROJECTION_ACCOUNTS = {
+ Accounts._ID,
+ Accounts.ACCOUNT_NAME,
+ };
+
+ private static String SELECTION_ALBUM_PARENT_ID = Albums.PARENT_ID + " = ?";
+ private static String SELECTION_PHOTO_ALBUM_ID = Photos.ALBUM_ID + " = ?";
+ private static String SELECTION_ACCOUNT_ID = Accounts.ACCOUNT_NAME + " = ?";
+
+ public static long queryAlbumIdFromParentId(SQLiteDatabase db, long parentId) {
+ return queryId(db, Albums.TABLE, PROJECTION_ALBUMS, SELECTION_ALBUM_PARENT_ID, parentId);
+ }
+
+ public static long queryPhotoIdFromAlbumId(SQLiteDatabase db, long albumId) {
+ return queryId(db, Photos.TABLE, PROJECTION_PHOTOS, SELECTION_PHOTO_ALBUM_ID, albumId);
+ }
+
+ public static long queryAccountIdFromName(SQLiteDatabase db, String accountName) {
+ return queryId(db, Accounts.TABLE, PROJECTION_ACCOUNTS, SELECTION_ACCOUNT_ID, accountName);
+ }
+
+ public static long queryId(SQLiteDatabase db, String table, String[] projection,
+ String selection, Object parameter) {
+ String paramString = parameter == null ? null : parameter.toString();
+ String[] selectionArgs = {
+ paramString,
+ };
+ Cursor cursor = db.query(table, projection, selection, selectionArgs, null, null, null);
+ try {
+ if (cursor.getCount() != 1 || !cursor.moveToNext()) {
+ throw new AssertionFailedError("Couldn't find item in table");
+ }
+ long id = cursor.getLong(0);
+ return id;
+ } finally {
+ cursor.close();
+ }
+ }
+
+ public static boolean insertPhoto(SQLiteDatabase db, Integer width, Integer height,
+ Long dateTaken, Long albumId, String mimeType, Long accountId) {
+ ContentValues values = new ContentValues();
+ values.put(Photos.WIDTH, width);
+ values.put(Photos.HEIGHT, height);
+ values.put(Photos.DATE_TAKEN, dateTaken);
+ values.put(Photos.ALBUM_ID, albumId);
+ values.put(Photos.MIME_TYPE, mimeType);
+ values.put(Photos.ACCOUNT_ID, accountId);
+ return db.insert(Photos.TABLE, null, values) != -1;
+ }
+
+ public static boolean insertAlbum(SQLiteDatabase db, Long parentId, String title,
+ Integer privacy, Long accountId) {
+ ContentValues values = new ContentValues();
+ values.put(Albums.PARENT_ID, parentId);
+ values.put(Albums.TITLE, title);
+ values.put(Albums.VISIBILITY, privacy);
+ values.put(Albums.ACCOUNT_ID, accountId);
+ return db.insert(Albums.TABLE, null, values) != -1;
+ }
+
+ public static boolean insertMetadata(SQLiteDatabase db, Long photosId, String key, String value) {
+ ContentValues values = new ContentValues();
+ values.put(Metadata.PHOTO_ID, photosId);
+ values.put(Metadata.KEY, key);
+ values.put(Metadata.VALUE, value);
+ return db.insert(Metadata.TABLE, null, values) != -1;
+ }
+
+ public static boolean insertAccount(SQLiteDatabase db, String name) {
+ ContentValues values = new ContentValues();
+ values.put(Accounts.ACCOUNT_NAME, name);
+ return db.insert(Accounts.TABLE, null, values) != -1;
+ }
+}
diff --git a/tests/src/com/android/photos/data/PhotoProviderTest.java b/tests/src/com/android/photos/data/PhotoProviderTest.java
new file mode 100644
index 000000000..685946ef0
--- /dev/null
+++ b/tests/src/com/android/photos/data/PhotoProviderTest.java
@@ -0,0 +1,391 @@
+/*
+ * Copyright (C) 2013 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.photos.data;
+
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.OperationApplicationException;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.provider.BaseColumns;
+import android.test.ProviderTestCase2;
+
+import com.android.photos.data.PhotoProvider.Accounts;
+import com.android.photos.data.PhotoProvider.Albums;
+import com.android.photos.data.PhotoProvider.Metadata;
+import com.android.photos.data.PhotoProvider.Photos;
+
+import java.util.ArrayList;
+
+public class PhotoProviderTest extends ProviderTestCase2<PhotoProvider> {
+ @SuppressWarnings("unused")
+ private static final String TAG = PhotoProviderTest.class.getSimpleName();
+
+ private static final String MIME_TYPE = "test/test";
+ private static final String ALBUM_TITLE = "My Album";
+ private static final long ALBUM_PARENT_ID = 100;
+ private static final String META_KEY = "mykey";
+ private static final String META_VALUE = "myvalue";
+ private static final String ACCOUNT_NAME = "foo@bar.com";
+
+ private static final Uri NO_TABLE_URI = PhotoProvider.BASE_CONTENT_URI;
+ private static final Uri BAD_TABLE_URI = Uri.withAppendedPath(PhotoProvider.BASE_CONTENT_URI,
+ "bad_table");
+
+ private static final String WHERE_METADATA_PHOTOS_ID = Metadata.PHOTO_ID + " = ?";
+ private static final String WHERE_METADATA = Metadata.PHOTO_ID + " = ? AND " + Metadata.KEY
+ + " = ?";
+
+ private long mAlbumId;
+ private long mPhotoId;
+ private long mMetadataId;
+ private long mAccountId;
+
+ private SQLiteOpenHelper mDBHelper;
+ private ContentResolver mResolver;
+ private NotificationWatcher mNotifications = new NotificationWatcher();
+
+ public PhotoProviderTest() {
+ super(PhotoProvider.class, PhotoProvider.AUTHORITY);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mResolver = getMockContentResolver();
+ PhotoProvider provider = (PhotoProvider) getProvider();
+ provider.setMockNotification(mNotifications);
+ mDBHelper = provider.getDatabaseHelper();
+ SQLiteDatabase db = mDBHelper.getWritableDatabase();
+ db.beginTransaction();
+ try {
+ PhotoDatabaseUtils.insertAccount(db, ACCOUNT_NAME);
+ mAccountId = PhotoDatabaseUtils.queryAccountIdFromName(db, ACCOUNT_NAME);
+ PhotoDatabaseUtils.insertAlbum(db, ALBUM_PARENT_ID, ALBUM_TITLE,
+ Albums.VISIBILITY_PRIVATE, mAccountId);
+ mAlbumId = PhotoDatabaseUtils.queryAlbumIdFromParentId(db, ALBUM_PARENT_ID);
+ PhotoDatabaseUtils.insertPhoto(db, 100, 100, System.currentTimeMillis(), mAlbumId,
+ MIME_TYPE, mAccountId);
+ mPhotoId = PhotoDatabaseUtils.queryPhotoIdFromAlbumId(db, mAlbumId);
+ PhotoDatabaseUtils.insertMetadata(db, mPhotoId, META_KEY, META_VALUE);
+ String[] projection = {
+ BaseColumns._ID,
+ };
+ Cursor cursor = db.query(Metadata.TABLE, projection, null, null, null, null, null);
+ cursor.moveToNext();
+ mMetadataId = cursor.getLong(0);
+ cursor.close();
+ db.setTransactionSuccessful();
+ mNotifications.reset();
+ } finally {
+ db.endTransaction();
+ }
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ mDBHelper.close();
+ mDBHelper = null;
+ super.tearDown();
+ getMockContext().deleteDatabase(PhotoProvider.DB_NAME);
+ }
+
+ public void testDelete() {
+ try {
+ mResolver.delete(NO_TABLE_URI, null, null);
+ fail("Exeption should be thrown when no table given");
+ } catch (Exception e) {
+ // expected exception
+ }
+ try {
+ mResolver.delete(BAD_TABLE_URI, null, null);
+ fail("Exeption should be thrown when deleting from a table that doesn't exist");
+ } catch (Exception e) {
+ // expected exception
+ }
+
+ String[] selectionArgs = {
+ String.valueOf(mPhotoId)
+ };
+ // Delete some metadata
+ assertEquals(1,
+ mResolver.delete(Metadata.CONTENT_URI, WHERE_METADATA_PHOTOS_ID, selectionArgs));
+ Uri photoUri = ContentUris.withAppendedId(Photos.CONTENT_URI, mPhotoId);
+ assertEquals(1, mResolver.delete(photoUri, null, null));
+ Uri albumUri = ContentUris.withAppendedId(Albums.CONTENT_URI, mAlbumId);
+ assertEquals(1, mResolver.delete(albumUri, null, null));
+ // now delete something that isn't there
+ assertEquals(0, mResolver.delete(photoUri, null, null));
+ }
+
+ public void testDeleteMetadataId() {
+ Uri metadataUri = ContentUris.withAppendedId(Metadata.CONTENT_URI, mMetadataId);
+ assertEquals(1, mResolver.delete(metadataUri, null, null));
+ Cursor cursor = mResolver.query(Metadata.CONTENT_URI, null, null, null, null);
+ assertEquals(0, cursor.getCount());
+ cursor.close();
+ }
+
+ // Delete the album and ensure that the photos referring to the album are
+ // deleted.
+ public void testDeleteAlbumCascade() {
+ Uri albumUri = ContentUris.withAppendedId(Albums.CONTENT_URI, mAlbumId);
+ mResolver.delete(albumUri, null, null);
+ assertTrue(mNotifications.isNotified(Photos.CONTENT_URI));
+ assertTrue(mNotifications.isNotified(Metadata.CONTENT_URI));
+ assertTrue(mNotifications.isNotified(albumUri));
+ assertEquals(3, mNotifications.notificationCount());
+ Cursor cursor = mResolver.query(Photos.CONTENT_URI, PhotoDatabaseUtils.PROJECTION_PHOTOS,
+ null, null, null);
+ assertEquals(0, cursor.getCount());
+ cursor.close();
+ }
+
+ // Delete all albums and ensure that photos in any album are deleted.
+ public void testDeleteAlbumCascade2() {
+ mResolver.delete(Albums.CONTENT_URI, null, null);
+ assertTrue(mNotifications.isNotified(Photos.CONTENT_URI));
+ assertTrue(mNotifications.isNotified(Metadata.CONTENT_URI));
+ assertTrue(mNotifications.isNotified(Albums.CONTENT_URI));
+ assertEquals(3, mNotifications.notificationCount());
+ Cursor cursor = mResolver.query(Photos.CONTENT_URI, PhotoDatabaseUtils.PROJECTION_PHOTOS,
+ null, null, null);
+ assertEquals(0, cursor.getCount());
+ cursor.close();
+ }
+
+ // Delete a photo and ensure that the metadata for that photo are deleted.
+ public void testDeletePhotoCascade() {
+ Uri photoUri = ContentUris.withAppendedId(Photos.CONTENT_URI, mPhotoId);
+ mResolver.delete(photoUri, null, null);
+ assertTrue(mNotifications.isNotified(photoUri));
+ assertTrue(mNotifications.isNotified(Metadata.CONTENT_URI));
+ assertEquals(2, mNotifications.notificationCount());
+ Cursor cursor = mResolver.query(Metadata.CONTENT_URI,
+ PhotoDatabaseUtils.PROJECTION_METADATA, null, null, null);
+ assertEquals(0, cursor.getCount());
+ cursor.close();
+ }
+
+ public void testDeleteAccountCascade() {
+ Uri accountUri = ContentUris.withAppendedId(Accounts.CONTENT_URI, mAccountId);
+ SQLiteDatabase db = mDBHelper.getWritableDatabase();
+ db.beginTransaction();
+ PhotoDatabaseUtils.insertPhoto(db, 100, 100, System.currentTimeMillis(), null,
+ "image/jpeg", mAccountId);
+ PhotoDatabaseUtils.insertPhoto(db, 100, 100, System.currentTimeMillis(), null,
+ "image/jpeg", 0L);
+ PhotoDatabaseUtils.insertAlbum(db, null, "title", Albums.VISIBILITY_PRIVATE, 10630L);
+ db.setTransactionSuccessful();
+ db.endTransaction();
+ // ensure all pictures are there:
+ Cursor cursor = mResolver.query(Photos.CONTENT_URI, null, null, null, null);
+ assertEquals(3, cursor.getCount());
+ cursor.close();
+ // delete the account
+ assertEquals(1, mResolver.delete(accountUri, null, null));
+ // now ensure that all associated photos were deleted
+ cursor = mResolver.query(Photos.CONTENT_URI, null, null, null, null);
+ assertEquals(1, cursor.getCount());
+ cursor.close();
+ // now ensure all associated albums were deleted.
+ cursor = mResolver.query(Albums.CONTENT_URI, null, null, null, null);
+ assertEquals(1, cursor.getCount());
+ cursor.close();
+ }
+
+ public void testGetType() {
+ // We don't return types for albums
+ assertNull(mResolver.getType(Albums.CONTENT_URI));
+
+ Uri noImage = ContentUris.withAppendedId(Photos.CONTENT_URI, mPhotoId + 1);
+ assertNull(mResolver.getType(noImage));
+
+ Uri image = ContentUris.withAppendedId(Photos.CONTENT_URI, mPhotoId);
+ assertEquals(MIME_TYPE, mResolver.getType(image));
+ }
+
+ public void testInsert() {
+ ContentValues values = new ContentValues();
+ values.put(Albums.TITLE, "add me");
+ values.put(Albums.VISIBILITY, Albums.VISIBILITY_PRIVATE);
+ values.put(Albums.ACCOUNT_ID, 100L);
+ values.put(Albums.DATE_MODIFIED, 100L);
+ values.put(Albums.DATE_PUBLISHED, 100L);
+ values.put(Albums.LOCATION_STRING, "Home");
+ values.put(Albums.TITLE, "hello world");
+ values.putNull(Albums.PARENT_ID);
+ values.put(Albums.SUMMARY, "Nothing much to say about this");
+ Uri insertedUri = mResolver.insert(Albums.CONTENT_URI, values);
+ assertNotNull(insertedUri);
+ Cursor cursor = mResolver.query(insertedUri, PhotoDatabaseUtils.PROJECTION_ALBUMS, null,
+ null, null);
+ assertNotNull(cursor);
+ assertEquals(1, cursor.getCount());
+ cursor.close();
+ }
+
+ public void testUpdate() {
+ ContentValues values = new ContentValues();
+ // Normal update -- use an album.
+ values.put(Albums.TITLE, "foo");
+ Uri albumUri = ContentUris.withAppendedId(Albums.CONTENT_URI, mAlbumId);
+ assertEquals(1, mResolver.update(albumUri, values, null, null));
+ String[] projection = {
+ Albums.TITLE,
+ };
+ Cursor cursor = mResolver.query(albumUri, projection, null, null, null);
+ assertEquals(1, cursor.getCount());
+ assertTrue(cursor.moveToNext());
+ assertEquals("foo", cursor.getString(0));
+ cursor.close();
+
+ // Update a row that doesn't exist.
+ Uri noAlbumUri = ContentUris.withAppendedId(Albums.CONTENT_URI, mAlbumId + 1);
+ values.put(Albums.TITLE, "bar");
+ assertEquals(0, mResolver.update(noAlbumUri, values, null, null));
+
+ // Update a metadata value that exists.
+ ContentValues metadata = new ContentValues();
+ metadata.put(Metadata.PHOTO_ID, mPhotoId);
+ metadata.put(Metadata.KEY, META_KEY);
+ metadata.put(Metadata.VALUE, "new value");
+ assertEquals(1, mResolver.update(Metadata.CONTENT_URI, metadata, null, null));
+
+ projection = new String[] {
+ Metadata.VALUE,
+ };
+
+ String[] selectionArgs = {
+ String.valueOf(mPhotoId), META_KEY,
+ };
+
+ cursor = mResolver.query(Metadata.CONTENT_URI, projection, WHERE_METADATA, selectionArgs,
+ null);
+ assertEquals(1, cursor.getCount());
+ assertTrue(cursor.moveToNext());
+ assertEquals("new value", cursor.getString(0));
+ cursor.close();
+
+ // Update a metadata value that doesn't exist.
+ metadata.put(Metadata.KEY, "other stuff");
+ assertEquals(1, mResolver.update(Metadata.CONTENT_URI, metadata, null, null));
+
+ selectionArgs[1] = "other stuff";
+ cursor = mResolver.query(Metadata.CONTENT_URI, projection, WHERE_METADATA, selectionArgs,
+ null);
+ assertEquals(1, cursor.getCount());
+ assertTrue(cursor.moveToNext());
+ assertEquals("new value", cursor.getString(0));
+ cursor.close();
+
+ // Remove a metadata value using update.
+ metadata.putNull(Metadata.VALUE);
+ assertEquals(1, mResolver.update(Metadata.CONTENT_URI, metadata, null, null));
+ cursor = mResolver.query(Metadata.CONTENT_URI, projection, WHERE_METADATA, selectionArgs,
+ null);
+ assertEquals(0, cursor.getCount());
+ cursor.close();
+ }
+
+ public void testQuery() {
+ // Query a photo that exists.
+ Cursor cursor = mResolver.query(Photos.CONTENT_URI, PhotoDatabaseUtils.PROJECTION_PHOTOS,
+ null, null, null);
+ assertNotNull(cursor);
+ assertEquals(1, cursor.getCount());
+ assertTrue(cursor.moveToNext());
+ assertEquals(mPhotoId, cursor.getLong(0));
+ cursor.close();
+
+ // Query a photo that doesn't exist.
+ Uri noPhotoUri = ContentUris.withAppendedId(Photos.CONTENT_URI, mPhotoId + 1);
+ cursor = mResolver.query(noPhotoUri, PhotoDatabaseUtils.PROJECTION_PHOTOS, null, null,
+ null);
+ assertNotNull(cursor);
+ assertEquals(0, cursor.getCount());
+ cursor.close();
+
+ // Query a photo that exists using selection arguments.
+ String[] selectionArgs = {
+ String.valueOf(mPhotoId),
+ };
+
+ cursor = mResolver.query(Photos.CONTENT_URI, PhotoDatabaseUtils.PROJECTION_PHOTOS,
+ Photos._ID + " = ?", selectionArgs, null);
+ assertNotNull(cursor);
+ assertEquals(1, cursor.getCount());
+ assertTrue(cursor.moveToNext());
+ assertEquals(mPhotoId, cursor.getLong(0));
+ cursor.close();
+ }
+
+ public void testUpdatePhotoNotification() {
+ Uri photoUri = ContentUris.withAppendedId(Photos.CONTENT_URI, mPhotoId);
+ ContentValues values = new ContentValues();
+ values.put(Photos.MIME_TYPE, "not-a/mime-type");
+ mResolver.update(photoUri, values, null, null);
+ assertTrue(mNotifications.isNotified(photoUri));
+ }
+
+ public void testUpdateMetadataNotification() {
+ ContentValues values = new ContentValues();
+ values.put(Metadata.PHOTO_ID, mPhotoId);
+ values.put(Metadata.KEY, META_KEY);
+ values.put(Metadata.VALUE, "hello world");
+ mResolver.update(Metadata.CONTENT_URI, values, null, null);
+ assertTrue(mNotifications.isNotified(Metadata.CONTENT_URI));
+ }
+
+ public void testBatchTransaction() throws RemoteException, OperationApplicationException {
+ ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>();
+ ContentProviderOperation.Builder insert = ContentProviderOperation
+ .newInsert(Photos.CONTENT_URI);
+ insert.withValue(Photos.WIDTH, 200L);
+ insert.withValue(Photos.HEIGHT, 100L);
+ insert.withValue(Photos.DATE_TAKEN, System.currentTimeMillis());
+ insert.withValue(Photos.ALBUM_ID, 1000L);
+ insert.withValue(Photos.MIME_TYPE, "image/jpg");
+ insert.withValue(Photos.ACCOUNT_ID, 1L);
+ operations.add(insert.build());
+ ContentProviderOperation.Builder update = ContentProviderOperation.newUpdate(Photos.CONTENT_URI);
+ update.withValue(Photos.DATE_MODIFIED, System.currentTimeMillis());
+ String[] whereArgs = {
+ "100",
+ };
+ String where = Photos.WIDTH + " = ?";
+ update.withSelection(where, whereArgs);
+ operations.add(update.build());
+ ContentProviderOperation.Builder delete = ContentProviderOperation
+ .newDelete(Photos.CONTENT_URI);
+ delete.withSelection(where, whereArgs);
+ operations.add(delete.build());
+ mResolver.applyBatch(PhotoProvider.AUTHORITY, operations);
+ assertEquals(3, mNotifications.notificationCount());
+ SQLiteDatabase db = mDBHelper.getReadableDatabase();
+ long id = PhotoDatabaseUtils.queryPhotoIdFromAlbumId(db, 1000L);
+ Uri uri = ContentUris.withAppendedId(Photos.CONTENT_URI, id);
+ assertTrue(mNotifications.isNotified(uri));
+ assertTrue(mNotifications.isNotified(Metadata.CONTENT_URI));
+ assertTrue(mNotifications.isNotified(Photos.CONTENT_URI));
+ }
+
+}
diff --git a/tests/src/com/android/photos/data/TestHelper.java b/tests/src/com/android/photos/data/TestHelper.java
new file mode 100644
index 000000000..338e160cf
--- /dev/null
+++ b/tests/src/com/android/photos/data/TestHelper.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2013 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.photos.data;
+
+import android.util.Log;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import java.lang.reflect.Method;
+
+public class TestHelper {
+ private static String TAG = TestHelper.class.getSimpleName();
+
+ public interface TestInitialization {
+ void initialize(TestCase testCase);
+ }
+
+ public static void addTests(Class<? extends TestCase> testClass, TestSuite suite,
+ TestInitialization initialization) {
+ for (Method method : testClass.getDeclaredMethods()) {
+ if (method.getName().startsWith("test") && method.getParameterTypes().length == 0) {
+ TestCase test;
+ try {
+ test = testClass.newInstance();
+ test.setName(method.getName());
+ initialization.initialize(test);
+ suite.addTest(test);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Failed to create test case", e);
+ } catch (InstantiationException e) {
+ Log.e(TAG, "Failed to create test case", e);
+ } catch (IllegalAccessException e) {
+ Log.e(TAG, "Failed to create test case", e);
+ }
+ }
+ }
+ }
+
+}