summaryrefslogtreecommitdiffstats
path: root/tests/src/com/android/gallery3d/exif
diff options
context:
space:
mode:
Diffstat (limited to 'tests/src/com/android/gallery3d/exif')
-rw-r--r--tests/src/com/android/gallery3d/exif/ExifDataTest.java154
-rw-r--r--tests/src/com/android/gallery3d/exif/ExifInterfaceTest.java533
-rw-r--r--tests/src/com/android/gallery3d/exif/ExifModifierTest.java184
-rw-r--r--tests/src/com/android/gallery3d/exif/ExifOutputStreamTest.java198
-rw-r--r--tests/src/com/android/gallery3d/exif/ExifParserTest.java243
-rw-r--r--tests/src/com/android/gallery3d/exif/ExifReaderTest.java165
-rw-r--r--tests/src/com/android/gallery3d/exif/ExifTagTest.java219
-rw-r--r--tests/src/com/android/gallery3d/exif/ExifTestRunner.java135
-rw-r--r--tests/src/com/android/gallery3d/exif/ExifXmlDataTestCase.java108
-rw-r--r--tests/src/com/android/gallery3d/exif/ExifXmlReader.java125
-rw-r--r--tests/src/com/android/gallery3d/exif/Util.java195
11 files changed, 2259 insertions, 0 deletions
diff --git a/tests/src/com/android/gallery3d/exif/ExifDataTest.java b/tests/src/com/android/gallery3d/exif/ExifDataTest.java
new file mode 100644
index 000000000..142cc6bf6
--- /dev/null
+++ b/tests/src/com/android/gallery3d/exif/ExifDataTest.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2012 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.gallery3d.exif;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import junit.framework.TestCase;
+import java.nio.ByteOrder;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class ExifDataTest extends TestCase {
+ Map<Integer, ExifTag> mTestTags;
+ ExifInterface mInterface;
+ private ExifTag mVersionTag;
+ private ExifTag mGpsVersionTag;
+ private ExifTag mModelTag;
+ private ExifTag mDateTimeTag;
+ private ExifTag mCompressionTag;
+ private ExifTag mThumbnailFormatTag;
+ private ExifTag mLongitudeTag;
+ private ExifTag mShutterTag;
+ private ExifTag mInteropIndex;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mInterface = new ExifInterface();
+
+ // TYPE_UNDEFINED with 4 components
+ mVersionTag = mInterface.buildTag(ExifInterface.TAG_EXIF_VERSION, new byte[] {
+ 5, 4, 3, 2
+ });
+ // TYPE_UNSIGNED_BYTE with 4 components
+ mGpsVersionTag = mInterface.buildTag(ExifInterface.TAG_GPS_VERSION_ID, new byte[] {
+ 6, 7, 8, 9
+ });
+ // TYPE ASCII with arbitrary length
+ mModelTag = mInterface.buildTag(ExifInterface.TAG_MODEL, "helloworld");
+ // TYPE_ASCII with 20 components
+ mDateTimeTag = mInterface.buildTag(ExifInterface.TAG_DATE_TIME, "2013:02:11 20:20:20");
+ // TYPE_UNSIGNED_SHORT with 1 components
+ mCompressionTag = mInterface.buildTag(ExifInterface.TAG_COMPRESSION, 100);
+ // TYPE_UNSIGNED_LONG with 1 components
+ mThumbnailFormatTag =
+ mInterface.buildTag(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT, 100);
+ // TYPE_UNSIGNED_RATIONAL with 3 components
+ mLongitudeTag = mInterface.buildTag(ExifInterface.TAG_GPS_LONGITUDE, new Rational[] {
+ new Rational(2, 2), new Rational(11, 11),
+ new Rational(102, 102)
+ });
+ // TYPE_RATIONAL with 1 components
+ mShutterTag = mInterface
+ .buildTag(ExifInterface.TAG_SHUTTER_SPEED_VALUE, new Rational(4, 6));
+ // TYPE_ASCII with arbitrary length
+ mInteropIndex = mInterface.buildTag(ExifInterface.TAG_INTEROPERABILITY_INDEX, "foo");
+
+ mTestTags = new HashMap<Integer, ExifTag>();
+
+ mTestTags.put(ExifInterface.TAG_EXIF_VERSION, mVersionTag);
+ mTestTags.put(ExifInterface.TAG_GPS_VERSION_ID, mGpsVersionTag);
+ mTestTags.put(ExifInterface.TAG_MODEL, mModelTag);
+ mTestTags.put(ExifInterface.TAG_DATE_TIME, mDateTimeTag);
+ mTestTags.put(ExifInterface.TAG_COMPRESSION, mCompressionTag);
+ mTestTags.put(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT, mThumbnailFormatTag);
+ mTestTags.put(ExifInterface.TAG_GPS_LONGITUDE, mLongitudeTag);
+ mTestTags.put(ExifInterface.TAG_SHUTTER_SPEED_VALUE, mShutterTag);
+ mTestTags.put(ExifInterface.TAG_INTEROPERABILITY_INDEX, mInteropIndex);
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ super.tearDown();
+ mInterface = null;
+ mTestTags = null;
+ }
+
+ @SmallTest
+ public void testAddTag() {
+ ExifData exifData = new ExifData(ByteOrder.BIG_ENDIAN);
+
+ // Add all test tags
+ for (ExifTag t : mTestTags.values()) {
+ assertTrue(exifData.addTag(t) == null);
+ }
+
+ // Make sure no initial thumbnails
+ assertFalse(exifData.hasCompressedThumbnail());
+ assertFalse(exifData.hasUncompressedStrip());
+
+ // Check that we can set thumbnails
+ exifData.setStripBytes(3, new byte[] {
+ 1, 2, 3, 4, 5
+ });
+ assertTrue(exifData.hasUncompressedStrip());
+ exifData.setCompressedThumbnail(new byte[] {
+ 1
+ });
+ assertTrue(exifData.hasCompressedThumbnail());
+
+ // Check that we can clear thumbnails
+ exifData.clearThumbnailAndStrips();
+ assertFalse(exifData.hasCompressedThumbnail());
+ assertFalse(exifData.hasUncompressedStrip());
+
+ // Make sure ifds exist
+ for (int i : IfdData.getIfds()) {
+ assertTrue(exifData.getIfdData(i) != null);
+ }
+
+ // Get all test tags
+ List<ExifTag> allTags = exifData.getAllTags();
+ assertTrue(allTags != null);
+
+ // Make sure all test tags are in data
+ for (ExifTag t : mTestTags.values()) {
+ boolean check = false;
+ for (ExifTag i : allTags) {
+ if (t.equals(i)) {
+ check = true;
+ break;
+ }
+ }
+ assertTrue(check);
+ }
+
+ // Check if getting tags for a tid works
+ List<ExifTag> tidTags = exifData.getAllTagsForTagId(ExifInterface
+ .getTrueTagKey(ExifInterface.TAG_SHUTTER_SPEED_VALUE));
+ assertTrue(tidTags.size() == 1);
+ assertTrue(tidTags.get(0).equals(mShutterTag));
+
+ // Check if getting tags for an ifd works
+ List<ExifTag> ifdTags = exifData.getAllTagsForIfd(IfdId.TYPE_IFD_INTEROPERABILITY);
+ assertTrue(ifdTags.size() == 1);
+ assertTrue(ifdTags.get(0).equals(mInteropIndex));
+
+ }
+}
diff --git a/tests/src/com/android/gallery3d/exif/ExifInterfaceTest.java b/tests/src/com/android/gallery3d/exif/ExifInterfaceTest.java
new file mode 100644
index 000000000..01b2a323e
--- /dev/null
+++ b/tests/src/com/android/gallery3d/exif/ExifInterfaceTest.java
@@ -0,0 +1,533 @@
+/*
+ * 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.gallery3d.exif;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+
+import android.test.suitebuilder.annotation.MediumTest;
+
+import java.io.ByteArrayInputStream;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class ExifInterfaceTest extends ExifXmlDataTestCase {
+
+ private File mTmpFile;
+ private List<Map<Short, List<String>>> mGroundTruth;
+ private ExifInterface mInterface;
+ private ExifTag mVersionTag;
+ private ExifTag mGpsVersionTag;
+ private ExifTag mModelTag;
+ private ExifTag mDateTimeTag;
+ private ExifTag mCompressionTag;
+ private ExifTag mThumbnailFormatTag;
+ private ExifTag mLongitudeTag;
+ private ExifTag mShutterTag;
+ Map<Integer, ExifTag> mTestTags;
+ Map<Integer, Integer> mTagDefinitions;
+
+ public ExifInterfaceTest(int imageRes, int xmlRes) {
+ super(imageRes, xmlRes);
+ }
+
+ public ExifInterfaceTest(String imagePath, String xmlPath) {
+ super(imagePath, xmlPath);
+ }
+
+ @MediumTest
+ public void testInterface() throws Exception {
+
+ InputStream imageInputStream = null;
+ try {
+ // Basic checks
+
+ // Check if bitmap is valid
+ byte[] imgData = Util.readToByteArray(getImageInputStream());
+ imageInputStream = new ByteArrayInputStream(imgData);
+ checkBitmap(imageInputStream);
+
+ // Check defines
+ int tag = ExifInterface.defineTag(1, (short) 0x0100);
+ assertTrue(getImageTitle(), tag == 0x00010100);
+ int tagDef = mInterface.getTagDefinition((short) 0x0100, IfdId.TYPE_IFD_0);
+ assertTrue(getImageTitle(), tagDef == 0x03040001);
+ int[] allowed = ExifInterface.getAllowedIfdsFromInfo(mInterface.getTagInfo().get(
+ ExifInterface.TAG_IMAGE_WIDTH));
+ assertTrue(getImageTitle(), allowed.length == 2 && allowed[0] == IfdId.TYPE_IFD_0
+ && allowed[1] == IfdId.TYPE_IFD_1);
+
+ // Check if there are any initial tags
+ assertTrue(getImageTitle(), mInterface.getAllTags() == null);
+
+ // ///////// Basic read/write testing
+
+ // Make sure we can read
+ imageInputStream = new ByteArrayInputStream(imgData);
+ mInterface.readExif(imageInputStream);
+
+ // Check tags against ground truth
+ checkTagsAgainstXml(mInterface.getAllTags());
+
+ // Make sure clearing Exif works
+ mInterface.clearExif();
+ assertTrue(getImageTitle(), mInterface.getAllTags() == null);
+
+ // Make sure setting tags works
+ mInterface.setTags(mTestTags.values());
+ checkTagsAgainstHash(mInterface.getAllTags(), mTestTags);
+
+ // Try writing over bitmap exif
+ ByteArrayOutputStream imgModified = new ByteArrayOutputStream();
+ mInterface.writeExif(imgData, imgModified);
+
+ // Check if bitmap is valid
+ byte[] imgData2 = imgModified.toByteArray();
+ imageInputStream = new ByteArrayInputStream(imgData2);
+ checkBitmap(imageInputStream);
+
+ // Make sure we get the same tags out
+ imageInputStream = new ByteArrayInputStream(imgData2);
+ mInterface.readExif(imageInputStream);
+ checkTagsAgainstHash(mInterface.getAllTags(), mTestTags);
+
+ // Reread original image
+ imageInputStream = new ByteArrayInputStream(imgData);
+ mInterface.readExif(imageInputStream);
+
+ // Write out with original exif
+ imgModified = new ByteArrayOutputStream();
+ mInterface.writeExif(imgData2, imgModified);
+
+ // Read back in exif and check tags
+ imgData2 = imgModified.toByteArray();
+ imageInputStream = new ByteArrayInputStream(imgData2);
+ mInterface.readExif(imageInputStream);
+ checkTagsAgainstXml(mInterface.getAllTags());
+
+ // Check if bitmap is valid
+ imageInputStream = new ByteArrayInputStream(imgData2);
+ checkBitmap(imageInputStream);
+
+ } catch (Exception e) {
+ throw new Exception(getImageTitle(), e);
+ } finally {
+ Util.closeSilently(imageInputStream);
+ }
+ }
+
+ @MediumTest
+ public void testInterfaceModify() throws Exception {
+
+ // TODO: This test is dependent on galaxy_nexus jpeg/xml file.
+ InputStream imageInputStream = null;
+ try {
+ // Check if bitmap is valid
+ byte[] imgData = Util.readToByteArray(getImageInputStream());
+ imageInputStream = new ByteArrayInputStream(imgData);
+ checkBitmap(imageInputStream);
+
+ // ///////// Exif modifier testing.
+
+ // Read exif and write to temp file
+ imageInputStream = new ByteArrayInputStream(imgData);
+ mInterface.readExif(imageInputStream);
+ mInterface.writeExif(imgData, mTmpFile.getPath());
+
+ // Check if bitmap is valid
+ imageInputStream = new FileInputStream(mTmpFile);
+ checkBitmap(imageInputStream);
+
+ // Create some tags to overwrite with
+ ArrayList<ExifTag> tags = new ArrayList<ExifTag>();
+ tags.add(mInterface.buildTag(ExifInterface.TAG_ORIENTATION,
+ ExifInterface.Orientation.RIGHT_TOP));
+ tags.add(mInterface.buildTag(ExifInterface.TAG_USER_COMMENT, "goooooooooooooooooogle"));
+
+ // Attempt to rewrite tags
+ assertTrue(getImageTitle(), mInterface.rewriteExif(mTmpFile.getPath(), tags));
+
+ imageInputStream.close();
+ // Check if bitmap is valid
+ imageInputStream = new FileInputStream(mTmpFile);
+ checkBitmap(imageInputStream);
+
+ // Read tags and check against xml
+ mInterface.readExif(mTmpFile.getPath());
+ for (ExifTag t : mInterface.getAllTags()) {
+ short tid = t.getTagId();
+ if (tid != ExifInterface.getTrueTagKey(ExifInterface.TAG_ORIENTATION)
+ && tid != ExifInterface.getTrueTagKey(ExifInterface.TAG_USER_COMMENT)) {
+ checkTagAgainstXml(t);
+ }
+ }
+ assertTrue(getImageTitle(), mInterface.getTagIntValue(ExifInterface.TAG_ORIENTATION)
+ .shortValue() == ExifInterface.Orientation.RIGHT_TOP);
+ String valString = mInterface.getTagStringValue(ExifInterface.TAG_USER_COMMENT);
+ assertTrue(getImageTitle(), valString.equals("goooooooooooooooooogle"));
+
+ // Test forced modify
+
+ // Create some tags to overwrite with
+ tags = new ArrayList<ExifTag>();
+ tags.add(mInterface.buildTag(ExifInterface.TAG_SOFTWARE, "magic super photomaker pro"));
+ tags.add(mInterface.buildTag(ExifInterface.TAG_USER_COMMENT, "noodles"));
+ tags.add(mInterface.buildTag(ExifInterface.TAG_ORIENTATION,
+ ExifInterface.Orientation.TOP_LEFT));
+
+ // Force rewrite tags
+ mInterface.forceRewriteExif(mTmpFile.getPath(), tags);
+
+ imageInputStream.close();
+ // Check if bitmap is valid
+ imageInputStream = new FileInputStream(mTmpFile);
+ checkBitmap(imageInputStream);
+
+ // Read tags and check against xml
+ mInterface.readExif(mTmpFile.getPath());
+ for (ExifTag t : mInterface.getAllTags()) {
+ short tid = t.getTagId();
+ if (!ExifInterface.isOffsetTag(tid)
+ && tid != ExifInterface.getTrueTagKey(ExifInterface.TAG_SOFTWARE)
+ && tid != ExifInterface.getTrueTagKey(ExifInterface.TAG_USER_COMMENT)) {
+ checkTagAgainstXml(t);
+ }
+ }
+ valString = mInterface.getTagStringValue(ExifInterface.TAG_SOFTWARE);
+ String compareString = "magic super photomaker pro\0";
+ assertTrue(getImageTitle(), valString.equals(compareString));
+ valString = mInterface.getTagStringValue(ExifInterface.TAG_USER_COMMENT);
+ assertTrue(getImageTitle(), valString.equals("noodles"));
+
+ } catch (Exception e) {
+ throw new Exception(getImageTitle(), e);
+ } finally {
+ Util.closeSilently(imageInputStream);
+ }
+ }
+
+ @MediumTest
+ public void testInterfaceDefines() throws Exception {
+
+ InputStream imageInputStream = null;
+ try {
+ // Check if bitmap is valid
+ byte[] imgData = Util.readToByteArray(getImageInputStream());
+ imageInputStream = new ByteArrayInputStream(imgData);
+ checkBitmap(imageInputStream);
+
+ // Set some tags.
+ mInterface.setTags(mTestTags.values());
+
+ // Check tag definitions against default
+ for (Integer i : mTestTags.keySet()) {
+ int check = mTagDefinitions.get(i).intValue();
+ int actual = mInterface.getTagInfo().get(i);
+ assertTrue(check == actual);
+ }
+
+ // Check defines
+ int tag1 = ExifInterface.defineTag(IfdId.TYPE_IFD_1, (short) 42);
+ int tag2 = ExifInterface.defineTag(IfdId.TYPE_IFD_INTEROPERABILITY, (short) 43);
+ assertTrue(tag1 == 0x0001002a);
+ assertTrue(tag2 == 0x0003002b);
+
+ // Define some non-standard tags
+ assertTrue(mInterface.setTagDefinition((short) 42, IfdId.TYPE_IFD_1,
+ ExifTag.TYPE_UNSIGNED_BYTE, (short) 16, new int[] {
+ IfdId.TYPE_IFD_1
+ }) == tag1);
+ assertTrue(mInterface.getTagInfo().get(tag1) == 0x02010010);
+ assertTrue(mInterface.setTagDefinition((short) 43, IfdId.TYPE_IFD_INTEROPERABILITY,
+ ExifTag.TYPE_ASCII, (short) 5, new int[] {
+ IfdId.TYPE_IFD_GPS, IfdId.TYPE_IFD_INTEROPERABILITY
+ }) == tag2);
+ assertTrue(mInterface.getTagInfo().get(tag2) == 0x18020005);
+
+ // Make sure these don't work
+ assertTrue(mInterface.setTagDefinition((short) 42, IfdId.TYPE_IFD_1,
+ ExifTag.TYPE_UNSIGNED_BYTE, (short) 16, new int[] {
+ IfdId.TYPE_IFD_0
+ }) == ExifInterface.TAG_NULL);
+ assertTrue(mInterface.setTagDefinition((short) 42, IfdId.TYPE_IFD_1, (short) 0,
+ (short) 16, new int[] {
+ IfdId.TYPE_IFD_1
+ }) == ExifInterface.TAG_NULL);
+ assertTrue(mInterface.setTagDefinition((short) 42, 5, ExifTag.TYPE_UNSIGNED_BYTE,
+ (short) 16, new int[] {
+ 5
+ }) == ExifInterface.TAG_NULL);
+ assertTrue(mInterface.setTagDefinition((short) 42, IfdId.TYPE_IFD_1,
+ ExifTag.TYPE_UNSIGNED_BYTE, (short) 16, new int[] {
+ -1
+ }) == ExifInterface.TAG_NULL);
+ assertTrue(mInterface.setTagDefinition((short) 43, IfdId.TYPE_IFD_GPS,
+ ExifTag.TYPE_ASCII, (short) 5, new int[] {
+ IfdId.TYPE_IFD_GPS
+ }) == ExifInterface.TAG_NULL);
+ assertTrue(mInterface.setTagDefinition((short) 43, IfdId.TYPE_IFD_0,
+ ExifTag.TYPE_ASCII, (short) 5, new int[] {
+ IfdId.TYPE_IFD_0, IfdId.TYPE_IFD_GPS
+ }) == ExifInterface.TAG_NULL);
+
+ // Set some tags
+ mInterface.setTags(mTestTags.values());
+ checkTagsAgainstHash(mInterface.getAllTags(), mTestTags);
+
+ // Make some tags using new defines
+ ExifTag defTag0 = mInterface.buildTag(tag1, new byte[] {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
+ });
+ assertTrue(defTag0 != null);
+ ExifTag defTag1 = mInterface.buildTag(tag2, "hihi");
+ assertTrue(defTag1 != null);
+ ExifTag defTag2 = mInterface.buildTag(tag2, IfdId.TYPE_IFD_GPS, "byte");
+ assertTrue(defTag2 != null);
+
+ // Make sure these don't work
+ ExifTag badTag = mInterface.buildTag(tag1, new byte[] {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
+ });
+ assertTrue(badTag == null);
+ badTag = mInterface.buildTag(tag1, IfdId.TYPE_IFD_0, new byte[] {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
+ });
+ assertTrue(badTag == null);
+ badTag = mInterface.buildTag(0x0002002a, new byte[] {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
+ });
+ assertTrue(badTag == null);
+ badTag = mInterface.buildTag(tag2, new byte[] {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17
+ });
+ assertTrue(badTag == null);
+
+ // Set the tags
+ assertTrue(mInterface.setTag(defTag0) == null);
+ assertTrue(mInterface.setTag(defTag1) == null);
+ assertTrue(mInterface.setTag(defTag2) == null);
+ assertTrue(mInterface.setTag(defTag0).equals(defTag0));
+ assertTrue(mInterface.setTag(null) == null);
+ assertTrue(mInterface.setTagValue(tag2, "yoyo") == true);
+ assertTrue(mInterface.setTagValue(tag2, "yaaarggg") == false);
+ assertTrue(mInterface.getTagStringValue(tag2).equals("yoyo\0"));
+
+ // Try writing over bitmap exif
+ ByteArrayOutputStream imgModified = new ByteArrayOutputStream();
+ mInterface.writeExif(imgData, imgModified);
+
+ // Check if bitmap is valid
+ byte[] imgData2 = imgModified.toByteArray();
+ imageInputStream = new ByteArrayInputStream(imgData2);
+ checkBitmap(imageInputStream);
+
+ // Read back in the tags
+ mInterface.readExif(imgData2);
+
+ // Check tags
+ for (ExifTag t : mInterface.getAllTags()) {
+ int tid = t.getTagId();
+ if (tid != ExifInterface.getTrueTagKey(tag1)
+ && tid != ExifInterface.getTrueTagKey(tag2)) {
+ checkTagAgainstHash(t, mTestTags);
+ }
+ }
+ assertTrue(Arrays.equals(mInterface.getTagByteValues(tag1), new byte[] {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
+ }));
+ assertTrue(mInterface.getTagStringValue(tag2).equals("yoyo\0"));
+ assertTrue(mInterface.getTagStringValue(tag2, IfdId.TYPE_IFD_GPS).equals("byte\0"));
+
+ } catch (Exception e) {
+ throw new Exception(getImageTitle(), e);
+ } finally {
+ Util.closeSilently(imageInputStream);
+ }
+ }
+
+ @MediumTest
+ public void testInterfaceThumbnails() throws Exception {
+
+ InputStream imageInputStream = null;
+ try {
+ // Check if bitmap is valid
+ byte[] imgData = Util.readToByteArray(getImageInputStream());
+ imageInputStream = new ByteArrayInputStream(imgData);
+ checkBitmap(imageInputStream);
+
+ // Check thumbnails
+ mInterface.readExif(imgData);
+ Bitmap bmap = mInterface.getThumbnailBitmap();
+ assertTrue(getImageTitle(), bmap != null);
+
+ // Make a new thumbnail and set it
+ BitmapFactory.Options opts = new BitmapFactory.Options();
+ opts.inSampleSize = 16;
+ Bitmap thumb = BitmapFactory.decodeByteArray(imgData, 0, imgData.length, opts);
+ assertTrue(getImageTitle(), thumb != null);
+ assertTrue(getImageTitle(), mInterface.setCompressedThumbnail(thumb) == true);
+
+ // Write out image
+ ByteArrayOutputStream outData = new ByteArrayOutputStream();
+ mInterface.writeExif(imgData, outData);
+
+ // Make sure bitmap is still valid
+ byte[] imgData2 = outData.toByteArray();
+ imageInputStream = new ByteArrayInputStream(imgData2);
+ checkBitmap(imageInputStream);
+
+ // Read in bitmap and make sure thumbnail is still valid
+ mInterface.readExif(imgData2);
+ bmap = mInterface.getThumbnailBitmap();
+ assertTrue(getImageTitle(), bmap != null);
+
+ } catch (Exception e) {
+ throw new Exception(getImageTitle(), e);
+ } finally {
+ Util.closeSilently(imageInputStream);
+ }
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ mTmpFile = File.createTempFile("exif_test", ".jpg");
+ mGroundTruth = ExifXmlReader.readXml(getXmlParser());
+
+ mInterface = new ExifInterface();
+
+ // TYPE_UNDEFINED with 4 components
+ mVersionTag = mInterface.buildTag(ExifInterface.TAG_EXIF_VERSION, new byte[] {
+ 5, 4, 3, 2
+ });
+ // TYPE_UNSIGNED_BYTE with 4 components
+ mGpsVersionTag = mInterface.buildTag(ExifInterface.TAG_GPS_VERSION_ID, new byte[] {
+ 6, 7, 8, 9
+ });
+ // TYPE ASCII with arbitary length
+ mModelTag = mInterface.buildTag(ExifInterface.TAG_MODEL, "helloworld");
+ // TYPE_ASCII with 20 components
+ mDateTimeTag = mInterface.buildTag(ExifInterface.TAG_DATE_TIME, "2013:02:11 20:20:20");
+ // TYPE_UNSIGNED_SHORT with 1 components
+ mCompressionTag = mInterface.buildTag(ExifInterface.TAG_COMPRESSION, 100);
+ // TYPE_UNSIGNED_LONG with 1 components
+ mThumbnailFormatTag =
+ mInterface.buildTag(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT, 100);
+ // TYPE_UNSIGNED_RATIONAL with 3 components
+ mLongitudeTag = mInterface.buildTag(ExifInterface.TAG_GPS_LONGITUDE, new Rational[] {
+ new Rational(2, 2), new Rational(11, 11),
+ new Rational(102, 102)
+ });
+ // TYPE_RATIONAL with 1 components
+ mShutterTag = mInterface
+ .buildTag(ExifInterface.TAG_SHUTTER_SPEED_VALUE, new Rational(4, 6));
+
+ mTestTags = new HashMap<Integer, ExifTag>();
+
+ mTestTags.put(ExifInterface.TAG_EXIF_VERSION, mVersionTag);
+ mTestTags.put(ExifInterface.TAG_GPS_VERSION_ID, mGpsVersionTag);
+ mTestTags.put(ExifInterface.TAG_MODEL, mModelTag);
+ mTestTags.put(ExifInterface.TAG_DATE_TIME, mDateTimeTag);
+ mTestTags.put(ExifInterface.TAG_COMPRESSION, mCompressionTag);
+ mTestTags.put(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT, mThumbnailFormatTag);
+ mTestTags.put(ExifInterface.TAG_GPS_LONGITUDE, mLongitudeTag);
+ mTestTags.put(ExifInterface.TAG_SHUTTER_SPEED_VALUE, mShutterTag);
+
+ mTagDefinitions = new HashMap<Integer, Integer>();
+ mTagDefinitions.put(ExifInterface.TAG_EXIF_VERSION, 0x04070004);
+ mTagDefinitions.put(ExifInterface.TAG_GPS_VERSION_ID, 0x10010004);
+ mTagDefinitions.put(ExifInterface.TAG_MODEL, 0x03020000);
+ mTagDefinitions.put(ExifInterface.TAG_DATE_TIME, 0x03020014);
+ mTagDefinitions.put(ExifInterface.TAG_COMPRESSION, 0x03030001);
+ mTagDefinitions.put(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT, 0x02040001);
+ mTagDefinitions.put(ExifInterface.TAG_GPS_LONGITUDE, 0x100a0003);
+ mTagDefinitions.put(ExifInterface.TAG_SHUTTER_SPEED_VALUE, 0x040a0001);
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ super.tearDown();
+ mTmpFile.delete();
+ }
+
+ // Helper functions
+
+ private void checkTagAgainstXml(ExifTag tag) {
+ List<String> truth = mGroundTruth.get(tag.getIfd()).get(tag.getTagId());
+
+ if (truth == null) {
+ fail(String.format("Unknown Tag %02x", tag.getTagId()) + ", " + getImageTitle());
+ }
+
+ // No value from exiftool.
+ if (truth.contains(null))
+ return;
+
+ String dataString = Util.tagValueToString(tag).trim();
+ assertTrue(String.format("Tag %02x", tag.getTagId()) + ", " + getImageTitle()
+ + ": " + dataString,
+ truth.contains(dataString));
+ }
+
+ private void checkTagsAgainstXml(List<ExifTag> tags) {
+ for (ExifTag t : tags) {
+ checkTagAgainstXml(t);
+ }
+ }
+
+ private void checkTagAgainstHash(ExifTag tag, Map<Integer, ExifTag> testTags) {
+ int tagdef = mInterface.getTagDefinitionForTag(tag);
+ assertTrue(getImageTitle(), tagdef != ExifInterface.TAG_NULL);
+ ExifTag t = testTags.get(tagdef);
+ // Ignore offset tags & other special tags
+ if (!ExifInterface.sBannedDefines.contains(tag.getTagId())) {
+ assertTrue(getImageTitle(), t != null);
+ } else {
+ return;
+ }
+ if (t == tag)
+ return;
+ assertTrue(getImageTitle(), tag.equals(t));
+ assertTrue(getImageTitle(), tag.getDataType() == t.getDataType());
+ assertTrue(getImageTitle(), tag.getTagId() == t.getTagId());
+ assertTrue(getImageTitle(), tag.getIfd() == t.getIfd());
+ assertTrue(getImageTitle(), tag.getComponentCount() == t.getComponentCount());
+ }
+
+ private void checkTagsAgainstHash(List<ExifTag> tags, Map<Integer, ExifTag> testTags) {
+ for (ExifTag t : tags) {
+ checkTagAgainstHash(t, testTags);
+ }
+ }
+
+ private void checkBitmap(InputStream inputStream) throws IOException {
+ Bitmap bmp = BitmapFactory.decodeStream(inputStream);
+ assertTrue(getImageTitle(), bmp != null);
+ }
+
+}
diff --git a/tests/src/com/android/gallery3d/exif/ExifModifierTest.java b/tests/src/com/android/gallery3d/exif/ExifModifierTest.java
new file mode 100644
index 000000000..96f405ef6
--- /dev/null
+++ b/tests/src/com/android/gallery3d/exif/ExifModifierTest.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2012 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.gallery3d.exif;
+
+import android.test.suitebuilder.annotation.MediumTest;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.RandomAccessFile;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel.MapMode;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class ExifModifierTest extends ExifXmlDataTestCase {
+
+ private File mTmpFile;
+ private List<Map<Short, List<String>>> mGroundTruth;
+ private ExifInterface mInterface;
+ private Map<Short, ExifTag> mTestTags;
+ ExifTag mVersionTag;
+ ExifTag mGpsVersionTag;
+ ExifTag mModelTag;
+ ExifTag mDateTimeTag;
+ ExifTag mCompressionTag;
+ ExifTag mThumbnailFormatTag;
+ ExifTag mLongitudeTag;
+ ExifTag mShutterTag;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ mGroundTruth = ExifXmlReader.readXml(getXmlParser());
+ mTmpFile = File.createTempFile("exif_test", ".jpg");
+ FileOutputStream os = null;
+ InputStream is = getImageInputStream();
+ try {
+ os = new FileOutputStream(mTmpFile);
+ byte[] buf = new byte[1024];
+ int n;
+ while ((n = is.read(buf)) > 0) {
+ os.write(buf, 0, n);
+ }
+ } finally {
+ Util.closeSilently(os);
+ }
+
+ // TYPE_UNDEFINED with 4 components
+ mVersionTag = mInterface.buildTag(ExifInterface.TAG_EXIF_VERSION, new byte[] {
+ 1, 2, 3, 4
+ });
+ // TYPE_UNSIGNED_BYTE with 4 components
+ mGpsVersionTag = mInterface.buildTag(ExifInterface.TAG_GPS_VERSION_ID, new byte[] {
+ 4, 3, 2, 1
+ });
+ // TYPE ASCII with arbitary length
+ mModelTag = mInterface.buildTag(ExifInterface.TAG_MODEL, "end-of-the-world");
+ // TYPE_ASCII with 20 components
+ mDateTimeTag = mInterface.buildTag(ExifInterface.TAG_DATE_TIME, "2012:12:31 23:59:59");
+ // TYPE_UNSIGNED_SHORT with 1 components
+ mCompressionTag = mInterface.buildTag(ExifInterface.TAG_COMPRESSION, 100);
+ // TYPE_UNSIGNED_LONG with 1 components
+ mThumbnailFormatTag =
+ mInterface.buildTag(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT, 100);
+ // TYPE_UNSIGNED_RATIONAL with 3 components
+ mLongitudeTag = mInterface.buildTag(ExifInterface.TAG_GPS_LONGITUDE, new Rational[] {
+ new Rational(1, 1), new Rational(10, 10),
+ new Rational(100, 100)
+ });
+ // TYPE_RATIONAL with 1 components
+ mShutterTag = mInterface
+ .buildTag(ExifInterface.TAG_SHUTTER_SPEED_VALUE, new Rational(1, 1));
+
+ mTestTags = new HashMap<Short, ExifTag>();
+
+ mTestTags.put(mVersionTag.getTagId(), mVersionTag);
+ mTestTags.put(mGpsVersionTag.getTagId(), mGpsVersionTag);
+ mTestTags.put(mModelTag.getTagId(), mModelTag);
+ mTestTags.put(mDateTimeTag.getTagId(), mDateTimeTag);
+ mTestTags.put(mCompressionTag.getTagId(), mCompressionTag);
+ mTestTags.put(mThumbnailFormatTag.getTagId(), mThumbnailFormatTag);
+ mTestTags.put(mLongitudeTag.getTagId(), mLongitudeTag);
+ mTestTags.put(mShutterTag.getTagId(), mShutterTag);
+ }
+
+ public ExifModifierTest(int imageRes, int xmlRes) {
+ super(imageRes, xmlRes);
+ mInterface = new ExifInterface();
+ }
+
+ public ExifModifierTest(String imagePath, String xmlPath) {
+ super(imagePath, xmlPath);
+ mInterface = new ExifInterface();
+ }
+
+ @MediumTest
+ public void testModify() throws Exception {
+ Map<Short, Boolean> results = new HashMap<Short, Boolean>();
+
+ RandomAccessFile file = null;
+ try {
+ file = new RandomAccessFile(mTmpFile, "rw");
+ MappedByteBuffer buf = file.getChannel().map(MapMode.READ_WRITE, 0, file.length());
+ for (ExifTag tag : mTestTags.values()) {
+ ExifModifier modifier = new ExifModifier(buf, mInterface);
+ modifier.modifyTag(tag);
+ boolean result = modifier.commit();
+ results.put(tag.getTagId(), result);
+ buf.force();
+ buf.position(0);
+
+ if (!result) {
+ List<String> value = mGroundTruth.get(tag.getIfd()).get(tag.getTagId());
+ assertTrue(String.format("Tag %x, ", tag.getTagId()) + getImageTitle(),
+ value == null || tag.getTagId() == ExifInterface.TAG_MODEL);
+ }
+ }
+ } finally {
+ Util.closeSilently(file);
+ }
+
+ // Parse the new file and check the result
+ InputStream is = null;
+ try {
+ is = new FileInputStream(mTmpFile);
+ ExifData data = new ExifReader(mInterface).read(is);
+ for (int i = 0; i < IfdId.TYPE_IFD_COUNT; i++) {
+ checkIfd(data.getIfdData(i), mGroundTruth.get(i), results);
+ }
+ } finally {
+ Util.closeSilently(is);
+ }
+
+ }
+
+ private void checkIfd(IfdData ifd, Map<Short, List<String>> ifdValue,
+ Map<Short, Boolean> results) {
+ if (ifd == null) {
+ assertEquals(getImageTitle(), 0, ifdValue.size());
+ return;
+ }
+ ExifTag[] tags = ifd.getAllTags();
+ for (ExifTag tag : tags) {
+ List<String> truth = ifdValue.get(tag.getTagId());
+ assertNotNull(String.format("Tag %x, ", tag.getTagId()) + getImageTitle(), truth);
+ if (truth.contains(null)) {
+ continue;
+ }
+
+ ExifTag newTag = mTestTags.get(tag.getTagId());
+ if (newTag != null
+ && results.get(tag.getTagId())) {
+ assertEquals(String.format("Tag %x, ", tag.getTagId()) + getImageTitle(),
+ Util.tagValueToString(newTag), Util.tagValueToString(tag));
+ } else {
+ assertTrue(String.format("Tag %x, ", tag.getTagId()) + getImageTitle(),
+ truth.contains(Util.tagValueToString(tag).trim()));
+ }
+ }
+ assertEquals(getImageTitle(), ifdValue.size(), tags.length);
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ super.tearDown();
+ mTmpFile.delete();
+ }
+}
diff --git a/tests/src/com/android/gallery3d/exif/ExifOutputStreamTest.java b/tests/src/com/android/gallery3d/exif/ExifOutputStreamTest.java
new file mode 100644
index 000000000..151bdbc99
--- /dev/null
+++ b/tests/src/com/android/gallery3d/exif/ExifOutputStreamTest.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2012 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.gallery3d.exif;
+
+import android.test.suitebuilder.annotation.MediumTest;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.util.Log;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class ExifOutputStreamTest extends ExifXmlDataTestCase {
+
+ private File mTmpFile;
+
+ private ExifInterface mInterface;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ mTmpFile = File.createTempFile("exif_test", ".jpg");
+ }
+
+ public ExifOutputStreamTest(int imgRes, int xmlRes) {
+ super(imgRes, xmlRes);
+ mInterface = new ExifInterface();
+ }
+
+ public ExifOutputStreamTest(String imgPath, String xmlPath) {
+ super(imgPath, xmlPath);
+ mInterface = new ExifInterface();
+ }
+
+ @MediumTest
+ public void testExifOutputStream() throws Exception {
+ InputStream imageInputStream = null;
+ InputStream exifInputStream = null;
+ FileInputStream reDecodeInputStream = null;
+ FileInputStream reParseInputStream = null;
+
+ InputStream dangerInputStream = null;
+ OutputStream dangerOutputStream = null;
+ try {
+ try {
+ byte[] imgData = Util.readToByteArray(getImageInputStream());
+ imageInputStream = new ByteArrayInputStream(imgData);
+ exifInputStream = new ByteArrayInputStream(imgData);
+
+ // Read the image data
+ Bitmap bmp = BitmapFactory.decodeStream(imageInputStream);
+ // The image is invalid
+ if (bmp == null) {
+ return;
+ }
+
+ // Read exif data
+ ExifData exifData = new ExifReader(mInterface).read(exifInputStream);
+
+ // Encode the image with the exif data
+ FileOutputStream outputStream = new FileOutputStream(mTmpFile);
+ ExifOutputStream exifOutputStream = new ExifOutputStream(outputStream, mInterface);
+ exifOutputStream.setExifData(exifData);
+ bmp.compress(Bitmap.CompressFormat.JPEG, 90, exifOutputStream);
+ exifOutputStream.close();
+ exifOutputStream = null;
+
+ // Re-decode the temp file and check the data.
+ reDecodeInputStream = new FileInputStream(mTmpFile);
+ Bitmap decodedBmp = BitmapFactory.decodeStream(reDecodeInputStream);
+ assertNotNull(getImageTitle(), decodedBmp);
+ reDecodeInputStream.close();
+
+ // Re-parse the temp file the check EXIF tag
+ reParseInputStream = new FileInputStream(mTmpFile);
+ ExifData reExifData = new ExifReader(mInterface).read(reParseInputStream);
+ assertEquals(getImageTitle(), exifData, reExifData);
+ reParseInputStream.close();
+
+ // Try writing exif to file with existing exif.
+ dangerOutputStream = (OutputStream) new FileOutputStream(mTmpFile);
+ exifOutputStream = new ExifOutputStream(dangerOutputStream, mInterface);
+ exifOutputStream.setExifData(exifData);
+ exifOutputStream.write(imgData);
+ // exifOutputStream.write(strippedImgData);
+ exifOutputStream.close();
+ exifOutputStream = null;
+
+ // Make sure it still can be parsed into a bitmap.
+ dangerInputStream = (InputStream) new FileInputStream(mTmpFile);
+ decodedBmp = null;
+ decodedBmp = BitmapFactory.decodeStream(dangerInputStream);
+ assertNotNull(getImageTitle(), decodedBmp);
+ dangerInputStream.close();
+ dangerInputStream = null;
+
+ // Make sure exif is still well-formatted.
+ dangerInputStream = (InputStream) new FileInputStream(mTmpFile);
+ reExifData = null;
+ reExifData = new ExifReader(mInterface).read(dangerInputStream);
+ assertEquals(getImageTitle(), exifData, reExifData);
+ dangerInputStream.close();
+ dangerInputStream = null;
+
+ } finally {
+ Util.closeSilently(imageInputStream);
+ Util.closeSilently(exifInputStream);
+ Util.closeSilently(reDecodeInputStream);
+ Util.closeSilently(reParseInputStream);
+
+ Util.closeSilently(dangerInputStream);
+ Util.closeSilently(dangerOutputStream);
+ }
+ } catch (Exception e) {
+ throw new Exception(getImageTitle(), e);
+ }
+ }
+
+ @MediumTest
+ public void testOutputSpeed() throws Exception {
+ final String LOGTAG = "testOutputSpeed";
+ InputStream imageInputStream = null;
+ OutputStream imageOutputStream = null;
+ try {
+ try {
+ imageInputStream = getImageInputStream();
+ // Read the image data
+ Bitmap bmp = BitmapFactory.decodeStream(imageInputStream);
+ // The image is invalid
+ if (bmp == null) {
+ return;
+ }
+ imageInputStream.close();
+ int nLoops = 20;
+ long totalReadDuration = 0;
+ long totalWriteDuration = 0;
+ for (int i = 0; i < nLoops; i++) {
+ imageInputStream = reopenFileStream();
+ // Read exif data
+ long startTime = System.nanoTime();
+ ExifData exifData = new ExifReader(mInterface).read(imageInputStream);
+ long endTime = System.nanoTime();
+ long duration = endTime - startTime;
+ totalReadDuration += duration;
+ Log.v(LOGTAG, " read time: " + duration);
+ imageInputStream.close();
+
+ // Encode the image with the exif data
+ imageOutputStream = (OutputStream) new FileOutputStream(mTmpFile);
+ ExifOutputStream exifOutputStream = new ExifOutputStream(imageOutputStream,
+ mInterface);
+ exifOutputStream.setExifData(exifData);
+ startTime = System.nanoTime();
+ bmp.compress(Bitmap.CompressFormat.JPEG, 90, exifOutputStream);
+ endTime = System.nanoTime();
+ duration = endTime - startTime;
+ totalWriteDuration += duration;
+ Log.v(LOGTAG, " write time: " + duration);
+ exifOutputStream.close();
+ }
+ Log.v(LOGTAG, "======================= normal");
+ Log.v(LOGTAG, "avg read time: " + totalReadDuration / nLoops);
+ Log.v(LOGTAG, "avg write time: " + totalWriteDuration / nLoops);
+ Log.v(LOGTAG, "=======================");
+ } finally {
+ Util.closeSilently(imageInputStream);
+ Util.closeSilently(imageOutputStream);
+ }
+ } catch (Exception e) {
+ throw new Exception(getImageTitle(), e);
+ }
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ super.tearDown();
+ mTmpFile.delete();
+ }
+}
diff --git a/tests/src/com/android/gallery3d/exif/ExifParserTest.java b/tests/src/com/android/gallery3d/exif/ExifParserTest.java
new file mode 100644
index 000000000..247ea022b
--- /dev/null
+++ b/tests/src/com/android/gallery3d/exif/ExifParserTest.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2012 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.gallery3d.exif;
+
+import android.test.suitebuilder.annotation.MediumTest;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+
+import java.util.List;
+import java.util.Map;
+
+public class ExifParserTest extends ExifXmlDataTestCase {
+ private static final String TAG = "ExifParserTest";
+
+ private ExifInterface mInterface;
+
+ public ExifParserTest(int imgRes, int xmlRes) {
+ super(imgRes, xmlRes);
+ mInterface = new ExifInterface();
+ }
+
+ public ExifParserTest(String imgPath, String xmlPath) {
+ super(imgPath, xmlPath);
+ mInterface = new ExifInterface();
+ }
+
+ private List<Map<Short, List<String>>> mGroundTruth;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ mGroundTruth = ExifXmlReader.readXml(getXmlParser());
+ }
+
+ @MediumTest
+ public void testParse() throws Exception {
+ try {
+ ExifParser parser = ExifParser.parse(getImageInputStream(), mInterface);
+ int event = parser.next();
+ while (event != ExifParser.EVENT_END) {
+ switch (event) {
+ case ExifParser.EVENT_START_OF_IFD:
+ break;
+ case ExifParser.EVENT_NEW_TAG:
+ ExifTag tag = parser.getTag();
+ if (!tag.hasValue()) {
+ parser.registerForTagValue(tag);
+ } else {
+ checkTag(tag);
+ }
+ break;
+ case ExifParser.EVENT_VALUE_OF_REGISTERED_TAG:
+ tag = parser.getTag();
+ if (tag.getDataType() == ExifTag.TYPE_UNDEFINED) {
+ byte[] buf = new byte[tag.getComponentCount()];
+ parser.read(buf);
+ assertTrue(TAG, tag.setValue(buf));
+ }
+ checkTag(tag);
+ break;
+ }
+ event = parser.next();
+ }
+ } catch (Exception e) {
+ throw new Exception(getImageTitle(), e);
+ }
+ }
+
+ private void checkTag(ExifTag tag) {
+ List<String> truth = mGroundTruth.get(tag.getIfd()).get(tag.getTagId());
+
+ if (truth == null) {
+ fail(String.format("Unknown Tag %02x", tag.getTagId()) + ", " + getImageTitle());
+ }
+
+ // No value from exiftool.
+ if (truth.contains(null)) {
+ return;
+ }
+
+ String dataString = Util.tagValueToString(tag).trim();
+ assertTrue(String.format("Tag %02x", tag.getTagId()) + ", " + getImageTitle()
+ + ": " + dataString,
+ truth.contains(dataString));
+ }
+
+ private void parseOneIfd(int ifd, int options) throws Exception {
+ try {
+ Map<Short, List<String>> expectedResult = mGroundTruth.get(ifd);
+ int numOfTag = 0;
+ ExifParser parser = ExifParser.parse(getImageInputStream(), options, mInterface);
+ int event = parser.next();
+ while (event != ExifParser.EVENT_END) {
+ switch (event) {
+ case ExifParser.EVENT_START_OF_IFD:
+ assertEquals(getImageTitle(), ifd, parser.getCurrentIfd());
+ break;
+ case ExifParser.EVENT_NEW_TAG:
+ ExifTag tag = parser.getTag();
+ numOfTag++;
+ if (tag.hasValue()) {
+ checkTag(tag);
+ } else {
+ parser.registerForTagValue(tag);
+ }
+ break;
+ case ExifParser.EVENT_VALUE_OF_REGISTERED_TAG:
+ tag = parser.getTag();
+ if (tag.getDataType() == ExifTag.TYPE_UNDEFINED) {
+ byte[] buf = new byte[tag.getComponentCount()];
+ parser.read(buf);
+ tag.setValue(buf);
+ }
+ checkTag(tag);
+ break;
+ case ExifParser.EVENT_COMPRESSED_IMAGE:
+ case ExifParser.EVENT_UNCOMPRESSED_STRIP:
+ fail("Invalid Event type: " + event + ", " + getImageTitle());
+ break;
+ }
+ event = parser.next();
+ }
+ assertEquals(getImageTitle(), ExifXmlReader.getTrueTagNumber(expectedResult), numOfTag);
+ } catch (Exception e) {
+ throw new Exception(getImageTitle(), e);
+ }
+ }
+
+ @MediumTest
+ public void testOnlyExifIfd() throws Exception {
+ parseOneIfd(IfdId.TYPE_IFD_EXIF, ExifParser.OPTION_IFD_EXIF);
+ }
+
+ @MediumTest
+ public void testOnlyIfd0() throws Exception {
+ parseOneIfd(IfdId.TYPE_IFD_0, ExifParser.OPTION_IFD_0);
+ }
+
+ @MediumTest
+ public void testOnlyIfd1() throws Exception {
+ parseOneIfd(IfdId.TYPE_IFD_1, ExifParser.OPTION_IFD_1);
+ }
+
+ @MediumTest
+ public void testOnlyInteroperabilityIfd() throws Exception {
+ parseOneIfd(IfdId.TYPE_IFD_INTEROPERABILITY, ExifParser.OPTION_IFD_INTEROPERABILITY);
+ }
+
+ @MediumTest
+ public void testOnlyReadSomeTag() throws Exception {
+ // Do not do this test if there is no model tag.
+ if (mGroundTruth.get(IfdId.TYPE_IFD_0).get(ExifInterface.TAG_MODEL) == null) {
+ return;
+ }
+
+ try {
+ ExifParser parser = ExifParser.parse(getImageInputStream(), ExifParser.OPTION_IFD_0,
+ mInterface);
+ int event = parser.next();
+ boolean isTagFound = false;
+ while (event != ExifParser.EVENT_END) {
+ switch (event) {
+ case ExifParser.EVENT_START_OF_IFD:
+ assertEquals(getImageTitle(), IfdId.TYPE_IFD_0, parser.getCurrentIfd());
+ break;
+ case ExifParser.EVENT_NEW_TAG:
+ ExifTag tag = parser.getTag();
+ if (tag.getTagId() == ExifInterface.TAG_MODEL) {
+ if (tag.hasValue()) {
+ isTagFound = true;
+ checkTag(tag);
+ } else {
+ parser.registerForTagValue(tag);
+ }
+ parser.skipRemainingTagsInCurrentIfd();
+ }
+ break;
+ case ExifParser.EVENT_VALUE_OF_REGISTERED_TAG:
+ tag = parser.getTag();
+ assertEquals(getImageTitle(), ExifInterface.TAG_MODEL, tag.getTagId());
+ checkTag(tag);
+ isTagFound = true;
+ break;
+ }
+ event = parser.next();
+ }
+ assertTrue(getImageTitle(), isTagFound);
+ } catch (Exception e) {
+ throw new Exception(getImageTitle(), e);
+ }
+ }
+
+ @MediumTest
+ public void testReadThumbnail() throws Exception {
+ try {
+ ExifParser parser = ExifParser.parse(getImageInputStream(),
+ ExifParser.OPTION_IFD_1 | ExifParser.OPTION_THUMBNAIL, mInterface);
+
+ int event = parser.next();
+ Bitmap bmp = null;
+ boolean mIsContainCompressedImage = false;
+ while (event != ExifParser.EVENT_END) {
+ switch (event) {
+ case ExifParser.EVENT_NEW_TAG:
+ ExifTag tag = parser.getTag();
+ if (tag.getTagId() == ExifInterface.TAG_COMPRESSION) {
+ if (tag.getValueAt(0) == ExifInterface.Compression.JPEG) {
+ mIsContainCompressedImage = true;
+ }
+ }
+ break;
+ case ExifParser.EVENT_COMPRESSED_IMAGE:
+ int imageSize = parser.getCompressedImageSize();
+ byte buf[] = new byte[imageSize];
+ parser.read(buf);
+ bmp = BitmapFactory.decodeByteArray(buf, 0, imageSize);
+ break;
+ }
+ event = parser.next();
+ }
+ if (mIsContainCompressedImage) {
+ assertNotNull(getImageTitle(), bmp);
+ }
+ } catch (Exception e) {
+ throw new Exception(getImageTitle(), e);
+ }
+ }
+}
diff --git a/tests/src/com/android/gallery3d/exif/ExifReaderTest.java b/tests/src/com/android/gallery3d/exif/ExifReaderTest.java
new file mode 100644
index 000000000..4b5c02941
--- /dev/null
+++ b/tests/src/com/android/gallery3d/exif/ExifReaderTest.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2012 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.gallery3d.exif;
+
+import android.test.suitebuilder.annotation.MediumTest;
+
+import android.graphics.BitmapFactory;
+
+import java.util.List;
+import java.util.Map;
+
+public class ExifReaderTest extends ExifXmlDataTestCase {
+ private static final String TAG = "ExifReaderTest";
+
+ private ExifInterface mInterface;
+ private List<Map<Short, List<String>>> mGroundTruth;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ mGroundTruth = ExifXmlReader.readXml(getXmlParser());
+ }
+
+ public ExifReaderTest(int imgRes, int xmlRes) {
+ super(imgRes, xmlRes);
+ mInterface = new ExifInterface();
+ }
+
+ public ExifReaderTest(String imgPath, String xmlPath) {
+ super(imgPath, xmlPath);
+ mInterface = new ExifInterface();
+ }
+
+ @MediumTest
+ public void testRead() throws Exception {
+ try {
+ ExifReader reader = new ExifReader(mInterface);
+ ExifData exifData = reader.read(getImageInputStream());
+ for (int i = 0; i < IfdId.TYPE_IFD_COUNT; i++) {
+ checkIfd(exifData.getIfdData(i), mGroundTruth.get(i));
+ }
+ checkThumbnail(exifData);
+ } catch (Exception e) {
+ throw new Exception(getImageTitle(), e);
+ }
+ }
+
+ private void checkThumbnail(ExifData exifData) {
+ Map<Short, List<String>> ifd1Truth = mGroundTruth.get(IfdId.TYPE_IFD_1);
+
+ List<String> typeTagValue = ifd1Truth.get(ExifInterface.TAG_COMPRESSION);
+ if (typeTagValue == null)
+ return;
+
+ IfdData ifd1 = exifData.getIfdData(IfdId.TYPE_IFD_1);
+ if (ifd1 == null)
+ fail(getImageTitle() + ": failed to find IFD1");
+
+ String typeTagTruth = typeTagValue.get(0);
+
+ int type = (int) ifd1.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_COMPRESSION))
+ .getValueAt(0);
+
+ if (String.valueOf(ExifInterface.Compression.JPEG).equals(typeTagTruth)) {
+ assertTrue(getImageTitle(), type == ExifInterface.Compression.JPEG);
+ assertTrue(getImageTitle(), exifData.hasCompressedThumbnail());
+ byte[] thumbnail = exifData.getCompressedThumbnail();
+ assertTrue(getImageTitle(),
+ BitmapFactory.decodeByteArray(thumbnail, 0, thumbnail.length) != null);
+ } else if (String.valueOf(ExifInterface.Compression.UNCOMPRESSION).equals(typeTagTruth)) {
+ assertTrue(getImageTitle(), type == ExifInterface.Compression.UNCOMPRESSION);
+ // Try to check the strip count with the formula provided by EXIF spec.
+ int planarType = ExifInterface.PlanarConfiguration.CHUNKY;
+ ExifTag planarTag = ifd1.getTag(ExifInterface
+ .getTrueTagKey(ExifInterface.TAG_PLANAR_CONFIGURATION));
+ if (planarTag != null) {
+ planarType = (int) planarTag.getValueAt(0);
+ }
+
+ if (!ifd1Truth.containsKey(ExifInterface.TAG_IMAGE_LENGTH) ||
+ !ifd1Truth.containsKey(ExifInterface.TAG_ROWS_PER_STRIP)) {
+ return;
+ }
+
+ ExifTag heightTag = ifd1.getTag(ExifInterface
+ .getTrueTagKey(ExifInterface.TAG_IMAGE_LENGTH));
+ ExifTag rowPerStripTag = ifd1.getTag(ExifInterface
+ .getTrueTagKey(ExifInterface.TAG_ROWS_PER_STRIP));
+
+ // Fail the test if required tags are missing
+ if (heightTag == null || rowPerStripTag == null) {
+ fail(getImageTitle());
+ }
+
+ int imageLength = (int) heightTag.getValueAt(0);
+ int rowsPerStrip = (int) rowPerStripTag.getValueAt(0);
+ int stripCount = ifd1.getTag(
+ ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_OFFSETS))
+ .getComponentCount();
+
+ if (planarType == ExifInterface.PlanarConfiguration.CHUNKY) {
+ assertTrue(getImageTitle(),
+ stripCount == (imageLength + rowsPerStrip - 1) / rowsPerStrip);
+ } else {
+ if (!ifd1Truth.containsKey(ExifInterface.TAG_SAMPLES_PER_PIXEL)) {
+ return;
+ }
+ ExifTag samplePerPixelTag = ifd1.getTag(ExifInterface
+ .getTrueTagKey(ExifInterface.TAG_SAMPLES_PER_PIXEL));
+ int samplePerPixel = (int) samplePerPixelTag.getValueAt(0);
+ assertTrue(getImageTitle(),
+ stripCount ==
+ (imageLength + rowsPerStrip - 1) / rowsPerStrip * samplePerPixel);
+ }
+
+ if (!ifd1Truth.containsKey(ExifInterface.TAG_STRIP_BYTE_COUNTS)) {
+ return;
+ }
+ ExifTag byteCountTag = ifd1.getTag(ExifInterface
+ .getTrueTagKey(ExifInterface.TAG_STRIP_BYTE_COUNTS));
+ short byteCountDataType = byteCountTag.getDataType();
+ for (int i = 0; i < stripCount; i++) {
+ if (byteCountDataType == ExifTag.TYPE_UNSIGNED_SHORT) {
+ assertEquals(getImageTitle(),
+ byteCountTag.getValueAt(i), exifData.getStrip(i).length);
+ } else {
+ assertEquals(getImageTitle(),
+ byteCountTag.getValueAt(i), exifData.getStrip(i).length);
+ }
+ }
+ }
+ }
+
+ private void checkIfd(IfdData ifd, Map<Short, List<String>> ifdValue) {
+ if (ifd == null) {
+ assertEquals(getImageTitle(), 0, ifdValue.size());
+ return;
+ }
+ ExifTag[] tags = ifd.getAllTags();
+ for (ExifTag tag : tags) {
+ List<String> truth = ifdValue.get(tag.getTagId());
+ assertNotNull(String.format("Tag %x, ", tag.getTagId()) + getImageTitle(), truth);
+ if (truth.contains(null)) {
+ continue;
+ }
+ assertTrue(String.format("Tag %x, ", tag.getTagId()) + getImageTitle(),
+ truth.contains(Util.tagValueToString(tag).trim()));
+ }
+ assertEquals(getImageTitle(), ifdValue.size(), tags.length);
+ }
+}
diff --git a/tests/src/com/android/gallery3d/exif/ExifTagTest.java b/tests/src/com/android/gallery3d/exif/ExifTagTest.java
new file mode 100644
index 000000000..e6a41ecfa
--- /dev/null
+++ b/tests/src/com/android/gallery3d/exif/ExifTagTest.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2012 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.gallery3d.exif;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import junit.framework.TestCase;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class ExifTagTest extends TestCase {
+
+ private static long MAX_UNSIGNED_LONG = (1L << 32) - 1;
+ private static int MAX_LONG = Integer.MAX_VALUE;
+ private static int MIN_LONG = Integer.MIN_VALUE;
+
+ Map<Integer, ExifTag> mTestTags;
+ ExifInterface mInterface;
+ private ExifTag mVersionTag;
+ private ExifTag mGpsVersionTag;
+ private ExifTag mModelTag;
+ private ExifTag mDateTimeTag;
+ private ExifTag mCompressionTag;
+ private ExifTag mThumbnailFormatTag;
+ private ExifTag mLongitudeTag;
+ private ExifTag mShutterTag;
+ private ExifTag mInteropIndex;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ mInterface = new ExifInterface();
+
+ // TYPE_UNDEFINED with 4 components
+ mVersionTag = mInterface.buildTag(ExifInterface.TAG_EXIF_VERSION, new byte[] {
+ 5, 4, 3, 2
+ });
+ // TYPE_UNSIGNED_BYTE with 4 components
+ mGpsVersionTag = mInterface.buildTag(ExifInterface.TAG_GPS_VERSION_ID, new byte[] {
+ 6, 7, 8, 9
+ });
+ // TYPE ASCII with arbitrary length
+ mModelTag = mInterface.buildTag(ExifInterface.TAG_MODEL, "helloworld");
+ // TYPE_ASCII with 20 components
+ mDateTimeTag = mInterface.buildTag(ExifInterface.TAG_DATE_TIME, "2013:02:11 20:20:20");
+ // TYPE_UNSIGNED_SHORT with 1 components
+ mCompressionTag = mInterface.buildTag(ExifInterface.TAG_COMPRESSION, 100);
+ // TYPE_UNSIGNED_LONG with 1 components
+ mThumbnailFormatTag =
+ mInterface.buildTag(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT, 100);
+ // TYPE_UNSIGNED_RATIONAL with 3 components
+ mLongitudeTag = mInterface.buildTag(ExifInterface.TAG_GPS_LONGITUDE, new Rational[] {
+ new Rational(2, 2), new Rational(11, 11),
+ new Rational(102, 102)
+ });
+ // TYPE_RATIONAL with 1 components
+ mShutterTag = mInterface
+ .buildTag(ExifInterface.TAG_SHUTTER_SPEED_VALUE, new Rational(4, 6));
+ // TYPE_ASCII with arbitrary length
+ mInteropIndex = mInterface.buildTag(ExifInterface.TAG_INTEROPERABILITY_INDEX, "foo");
+
+ mTestTags = new HashMap<Integer, ExifTag>();
+
+ mTestTags.put(ExifInterface.TAG_EXIF_VERSION, mVersionTag);
+ mTestTags.put(ExifInterface.TAG_GPS_VERSION_ID, mGpsVersionTag);
+ mTestTags.put(ExifInterface.TAG_MODEL, mModelTag);
+ mTestTags.put(ExifInterface.TAG_DATE_TIME, mDateTimeTag);
+ mTestTags.put(ExifInterface.TAG_COMPRESSION, mCompressionTag);
+ mTestTags.put(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT, mThumbnailFormatTag);
+ mTestTags.put(ExifInterface.TAG_GPS_LONGITUDE, mLongitudeTag);
+ mTestTags.put(ExifInterface.TAG_SHUTTER_SPEED_VALUE, mShutterTag);
+ mTestTags.put(ExifInterface.TAG_INTEROPERABILITY_INDEX, mInteropIndex);
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ super.tearDown();
+ mInterface = null;
+ mTestTags = null;
+ }
+
+ @SmallTest
+ public void testValueType() {
+ for (ExifTag tag : mTestTags.values()) {
+ assertTrue(tag != null);
+ int count = tag.getComponentCount();
+ int intBuf[] = new int[count];
+ long longBuf[] = new long[count];
+ byte byteBuf[] = new byte[count];
+ Rational rationalBuf[] = new Rational[count];
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < count; i++) {
+ intBuf[i] = 0;
+ longBuf[i] = 0;
+ byteBuf[i] = 0;
+ rationalBuf[i] = new Rational(0, 0);
+ // The string size should equal to component count - 1
+ if (i != count - 1) {
+ sb.append("*");
+ } else {
+ sb.append("\0");
+ }
+ }
+ String strBuf = sb.toString();
+
+ checkTypeByte(tag, byteBuf);
+ checkTypeAscii(tag, strBuf);
+ checkTypeUnsignedShort(tag, intBuf);
+ checkTypeUnsignedLong(tag, intBuf, longBuf);
+ checkTypeLong(tag, intBuf);
+ checkTypeRational(tag, rationalBuf);
+ checkTypeUnsignedRational(tag, rationalBuf);
+ }
+ }
+
+ private void checkTypeByte(ExifTag tag, byte[] buf) {
+ short type = tag.getDataType();
+ assertFalse("\nTag: " + tag.toString(), tag.setValue(buf)
+ ^ (type == ExifTag.TYPE_UNDEFINED || type == ExifTag.TYPE_UNSIGNED_BYTE));
+ }
+
+ private void checkTypeAscii(ExifTag tag, String str) {
+ short type = tag.getDataType();
+ assertFalse("\nTag: " + tag.toString(), tag.setValue(str)
+ ^ (type == ExifTag.TYPE_ASCII || type == ExifTag.TYPE_UNDEFINED));
+ }
+
+ private void checkTypeUnsignedShort(ExifTag tag, int[] intBuf) {
+ short type = tag.getDataType();
+ assertFalse("\nTag: " + tag.toString(),
+ tag.setValue(intBuf)
+ ^ (type == ExifTag.TYPE_UNSIGNED_SHORT
+ || type == ExifTag.TYPE_UNSIGNED_LONG
+ || type == ExifTag.TYPE_LONG));
+ }
+
+ private void checkTypeUnsignedLong(ExifTag tag, int[] intBuf, long[] longBuf) {
+
+ // Test value only for unsigned long.
+ int count = intBuf.length;
+ intBuf[count - 1] = MAX_LONG;
+ tag.setValue(intBuf);
+ longBuf[count - 1] = MAX_UNSIGNED_LONG;
+
+ assertFalse("\nTag: " + tag.toString(), tag.setValue(longBuf)
+ ^ (tag.getDataType() == ExifTag.TYPE_UNSIGNED_LONG));
+
+ intBuf[count - 1] = 0;
+ // Test invalid value for all type.
+ longBuf[count - 1] = MAX_UNSIGNED_LONG + 1;
+ assertFalse(tag.setValue(longBuf));
+ longBuf[count - 1] = 0;
+ }
+
+ private void checkTypeLong(ExifTag tag, int[] intBuf) {
+ int count = intBuf.length;
+ intBuf[count - 1] = MAX_LONG;
+ tag.setValue(intBuf);
+ intBuf[count - 1] = MIN_LONG;
+
+ assertFalse("\nTag: " + tag.toString(), tag.setValue(intBuf)
+ ^ (tag.getDataType() == ExifTag.TYPE_LONG));
+ intBuf[count - 1] = 0;
+ }
+
+ private void checkTypeRational(ExifTag tag, Rational rationalBuf[]) {
+ int count = rationalBuf.length;
+ Rational r = rationalBuf[count - 1];
+ rationalBuf[count - 1] = new Rational(MAX_LONG, MIN_LONG);
+
+ assertFalse("\nTag: " + tag.toString(), tag.setValue(rationalBuf)
+ ^ (tag.getDataType() == ExifTag.TYPE_RATIONAL));
+
+ if (tag.getDataType() == ExifTag.TYPE_RATIONAL) {
+ // check overflow
+
+ rationalBuf[count - 1] = new Rational(MAX_LONG + 1L, MIN_LONG);
+ assertFalse(tag.setValue(rationalBuf));
+
+ rationalBuf[count - 1] = new Rational(MAX_LONG, MIN_LONG - 1L);
+ assertFalse(tag.setValue(rationalBuf));
+ }
+ rationalBuf[count - 1] = r;
+ }
+
+ private void checkTypeUnsignedRational(ExifTag tag, Rational rationalBuf[]) {
+ int count = rationalBuf.length;
+ Rational r = rationalBuf[count - 1];
+ rationalBuf[count - 1] = new Rational(MAX_UNSIGNED_LONG, MAX_UNSIGNED_LONG);
+
+ assertFalse("\nTag: " + tag.toString(), tag.setValue(rationalBuf)
+ ^ (tag.getDataType() == ExifTag.TYPE_UNSIGNED_RATIONAL));
+
+ if (tag.getDataType() == ExifTag.TYPE_UNSIGNED_RATIONAL) {
+ // check overflow
+ rationalBuf[count - 1] = new Rational(MAX_UNSIGNED_LONG + 1, 0);
+ assertFalse(tag.setValue(rationalBuf));
+
+ rationalBuf[count - 1] = new Rational(MAX_UNSIGNED_LONG, -1);
+ assertFalse(tag.setValue(rationalBuf));
+ }
+ rationalBuf[count - 1] = r;
+ }
+}
diff --git a/tests/src/com/android/gallery3d/exif/ExifTestRunner.java b/tests/src/com/android/gallery3d/exif/ExifTestRunner.java
new file mode 100644
index 000000000..162baea29
--- /dev/null
+++ b/tests/src/com/android/gallery3d/exif/ExifTestRunner.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2012 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.gallery3d.exif;
+
+import android.content.Context;
+import android.os.Environment;
+import android.test.InstrumentationTestRunner;
+import android.test.InstrumentationTestSuite;
+import android.util.Log;
+
+import com.android.gallery3d.tests.R;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import java.io.File;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ExifTestRunner extends InstrumentationTestRunner {
+ private static final String TAG = "ExifTestRunner";
+
+ private static final int[] IMG_RESOURCE = {
+ R.raw.galaxy_nexus
+ };
+
+ private static final int[] EXIF_DATA_RESOURCE = {
+ R.xml.galaxy_nexus
+ };
+
+ private static List<String> mTestImgPath = new ArrayList<String>();
+ private static List<String> mTestXmlPath = new ArrayList<String>();
+
+ @Override
+ public TestSuite getAllTests() {
+ getTestImagePath();
+ TestSuite suite = new InstrumentationTestSuite(this);
+ suite.addTestSuite(ExifDataTest.class);
+ suite.addTestSuite(ExifTagTest.class);
+ addAllTestsFromExifTestCase(ExifParserTest.class, suite);
+ addAllTestsFromExifTestCase(ExifReaderTest.class, suite);
+ addAllTestsFromExifTestCase(ExifOutputStreamTest.class, suite);
+ addAllTestsFromExifTestCase(ExifModifierTest.class, suite);
+ addAllTestsFromExifTestCase(ExifInterfaceTest.class, suite);
+ return suite;
+ }
+
+ private void getTestImagePath() {
+ Context context = getContext();
+ File imgDir = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
+ File xmlDir = new File(context.getExternalFilesDir(null).getPath(), "Xml");
+
+ if (imgDir != null && xmlDir != null) {
+ String[] imgs = imgDir.list();
+ if (imgs == null) {
+ return;
+ }
+ for (String imgName : imgs) {
+ String xmlName = imgName.substring(0, imgName.lastIndexOf('.')) + ".xml";
+ File xmlFile = new File(xmlDir, xmlName);
+ if (xmlFile.exists()) {
+ mTestImgPath.add(new File(imgDir, imgName).getAbsolutePath());
+ mTestXmlPath.add(xmlFile.getAbsolutePath());
+ }
+ }
+ }
+ }
+
+ private void addAllTestsFromExifTestCase(Class<? extends ExifXmlDataTestCase> testClass,
+ TestSuite suite) {
+ for (Method method : testClass.getDeclaredMethods()) {
+ if (method.getName().startsWith("test") && method.getParameterTypes().length == 0) {
+ for (int i = 0; i < IMG_RESOURCE.length; i++) {
+ TestCase test;
+ try {
+ test = testClass.getDeclaredConstructor(int.class, int.class).
+ newInstance(IMG_RESOURCE[i], EXIF_DATA_RESOURCE[i]);
+ test.setName(method.getName());
+ 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);
+ } catch (InvocationTargetException e) {
+ Log.e(TAG, "Failed to create test case", e);
+ } catch (NoSuchMethodException e) {
+ Log.e(TAG, "Failed to create test case", e);
+ }
+ }
+ for (int i = 0, n = mTestImgPath.size(); i < n; i++) {
+ TestCase test;
+ try {
+ test = testClass.getDeclaredConstructor(String.class, String.class).
+ newInstance(mTestImgPath.get(i), mTestXmlPath.get(i));
+ test.setName(method.getName());
+ 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);
+ } catch (InvocationTargetException e) {
+ Log.e(TAG, "Failed to create test case", e);
+ } catch (NoSuchMethodException e) {
+ Log.e(TAG, "Failed to create test case", e);
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public ClassLoader getLoader() {
+ return ExifTestRunner.class.getClassLoader();
+ }
+}
diff --git a/tests/src/com/android/gallery3d/exif/ExifXmlDataTestCase.java b/tests/src/com/android/gallery3d/exif/ExifXmlDataTestCase.java
new file mode 100644
index 000000000..da860208b
--- /dev/null
+++ b/tests/src/com/android/gallery3d/exif/ExifXmlDataTestCase.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2012 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.gallery3d.exif;
+
+import android.content.res.Resources;
+import android.test.InstrumentationTestCase;
+import android.util.Xml;
+
+import org.xmlpull.v1.XmlPullParser;
+
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+public class ExifXmlDataTestCase extends InstrumentationTestCase {
+
+ private static final String RES_ID_TITLE = "Resource ID: %x";
+
+ private InputStream mImageInputStream;
+ private InputStream mXmlInputStream;
+ private XmlPullParser mXmlParser;
+ private final String mImagePath;
+ private final String mXmlPath;
+ private final int mImageResourceId;
+ private final int mXmlResourceId;
+
+ public ExifXmlDataTestCase(int imageRes, int xmlRes) {
+ mImagePath = null;
+ mXmlPath = null;
+ mImageResourceId = imageRes;
+ mXmlResourceId = xmlRes;
+ }
+
+ public ExifXmlDataTestCase(String imagePath, String xmlPath) {
+ mImagePath = imagePath;
+ mXmlPath = xmlPath;
+ mImageResourceId = 0;
+ mXmlResourceId = 0;
+ }
+
+ protected InputStream getImageInputStream() {
+ return mImageInputStream;
+ }
+
+ protected XmlPullParser getXmlParser() {
+ return mXmlParser;
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ try {
+ if (mImagePath != null) {
+ mImageInputStream = new FileInputStream(mImagePath);
+ mXmlInputStream = new FileInputStream(mXmlPath);
+ mXmlParser = Xml.newPullParser();
+ mXmlParser.setInput(new InputStreamReader(mXmlInputStream));
+ } else {
+ Resources res = getInstrumentation().getContext().getResources();
+ mImageInputStream = res.openRawResource(mImageResourceId);
+ mXmlParser = res.getXml(mXmlResourceId);
+ }
+ } catch (Exception e) {
+ throw new Exception(getImageTitle(), e);
+ }
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ Util.closeSilently(mImageInputStream);
+ Util.closeSilently(mXmlInputStream);
+ mXmlParser = null;
+ }
+
+ protected String getImageTitle() {
+ if (mImagePath != null) {
+ return mImagePath;
+ } else {
+ return String.format(RES_ID_TITLE, mImageResourceId);
+ }
+ }
+
+ protected InputStream reopenFileStream() throws Exception {
+ try {
+ if (mImagePath != null) {
+ return new FileInputStream(mImagePath);
+ } else {
+ Resources res = getInstrumentation().getContext().getResources();
+ return res.openRawResource(mImageResourceId);
+ }
+ } catch (Exception e) {
+ throw new Exception(getImageTitle(), e);
+ }
+ }
+}
diff --git a/tests/src/com/android/gallery3d/exif/ExifXmlReader.java b/tests/src/com/android/gallery3d/exif/ExifXmlReader.java
new file mode 100644
index 000000000..12e9cf77e
--- /dev/null
+++ b/tests/src/com/android/gallery3d/exif/ExifXmlReader.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2012 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.gallery3d.exif;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class ExifXmlReader {
+ private static final String TAG_EXIF = "exif";
+ private static final String TAG_TAG = "tag";
+
+ private static final String IFD0 = "IFD0";
+ private static final String EXIF_IFD = "ExifIFD";
+ private static final String GPS_IFD = "GPS";
+ private static final String IFD1 = "IFD1";
+ private static final String INTEROP_IFD = "InteropIFD";
+
+ private static final String ATTR_ID = "id";
+ private static final String ATTR_IFD = "ifd";
+
+ private static final String NO_VALUE = "NO_VALUE";
+
+ /**
+ * This function read the ground truth XML.
+ *
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ static public List<Map<Short, List<String>>> readXml(XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+
+ List<Map<Short, List<String>>> exifData =
+ new ArrayList<Map<Short, List<String>>>(IfdId.TYPE_IFD_COUNT);
+ for (int i = 0; i < IfdId.TYPE_IFD_COUNT; i++) {
+ exifData.add(new HashMap<Short, List<String>>());
+ }
+
+ while (parser.next() != XmlPullParser.END_DOCUMENT) {
+ if (parser.getEventType() == XmlPullParser.START_TAG) {
+ break;
+ }
+ }
+ parser.require(XmlPullParser.START_TAG, null, TAG_EXIF);
+
+ while (parser.next() != XmlPullParser.END_TAG) {
+ if (parser.getEventType() != XmlPullParser.START_TAG) {
+ continue;
+ }
+
+ parser.require(XmlPullParser.START_TAG, null, TAG_TAG);
+
+ int ifdId = getIfdIdFromString(parser.getAttributeValue(null, ATTR_IFD));
+ short id = Integer.decode(parser.getAttributeValue(null, ATTR_ID)).shortValue();
+
+ String value = "";
+ if (parser.next() == XmlPullParser.TEXT) {
+ value = parser.getText();
+ parser.next();
+ }
+
+ if (ifdId < 0) {
+ // TODO: the MarkerNote segment.
+ } else {
+ List<String> tagData = exifData.get(ifdId).get(id);
+ if (tagData == null) {
+ tagData = new ArrayList<String>();
+ exifData.get(ifdId).put(id, tagData);
+ }
+ if (NO_VALUE.equals(value)) {
+ tagData.add(null);
+ } else {
+ tagData.add(value.trim());
+ }
+ }
+
+ parser.require(XmlPullParser.END_TAG, null, null);
+ }
+ return exifData;
+ }
+
+ static private int getIfdIdFromString(String prefix) {
+ if (IFD0.equals(prefix)) {
+ return IfdId.TYPE_IFD_0;
+ } else if (EXIF_IFD.equals(prefix)) {
+ return IfdId.TYPE_IFD_EXIF;
+ } else if (GPS_IFD.equals(prefix)) {
+ return IfdId.TYPE_IFD_GPS;
+ } else if (IFD1.equals(prefix)) {
+ return IfdId.TYPE_IFD_1;
+ } else if (INTEROP_IFD.equals(prefix)) {
+ return IfdId.TYPE_IFD_INTEROPERABILITY;
+ } else {
+ assert (false);
+ return -1;
+ }
+ }
+
+ static public int getTrueTagNumber(Map<Short, List<String>> ifdData) {
+ int size = 0;
+ for (List<String> tag : ifdData.values()) {
+ size += tag.size();
+ }
+ return size;
+ }
+}
diff --git a/tests/src/com/android/gallery3d/exif/Util.java b/tests/src/com/android/gallery3d/exif/Util.java
new file mode 100644
index 000000000..15de00714
--- /dev/null
+++ b/tests/src/com/android/gallery3d/exif/Util.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2012 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.gallery3d.exif;
+
+import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+
+class Util {
+ public static boolean equals(Object a, Object b) {
+ return (a == b) || (a == null ? false : a.equals(b));
+ }
+
+ public static void closeSilently(Closeable c) {
+ if (c == null)
+ return;
+ try {
+ c.close();
+ } catch (Throwable t) {
+ // do nothing
+ }
+ }
+
+ public static byte[] readToByteArray(InputStream is) throws IOException {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ int len;
+ byte[] buf = new byte[1024];
+ while ((len = is.read(buf)) > -1) {
+ bos.write(buf, 0, len);
+ }
+ bos.flush();
+ return bos.toByteArray();
+ }
+
+ /**
+ * Tags that are not defined in the spec.
+ */
+ static final short TAG_XP_TITLE = (short) 0x9c9b;
+ static final short TAG_XP_COMMENT = (short) 0x9c9c;
+ static final short TAG_XP_AUTHOR = (short) 0x9c9d;
+ static final short TAG_XP_KEYWORDS = (short) 0x9c9e;
+ static final short TAG_XP_SUBJECT = (short) 0x9c9f;
+
+ private static String tagUndefinedTypeValueToString(ExifTag tag) {
+ StringBuilder sbuilder = new StringBuilder();
+ byte[] buf = new byte[tag.getComponentCount()];
+ tag.getBytes(buf);
+ short tagId = tag.getTagId();
+ if (tagId == ExifInterface.getTrueTagKey(ExifInterface.TAG_COMPONENTS_CONFIGURATION)) {
+ for (int i = 0, n = tag.getComponentCount(); i < n; i++) {
+ if (i != 0) {
+ sbuilder.append(" ");
+ }
+ sbuilder.append(buf[i]);
+ }
+ } else {
+ if (buf.length == 1) {
+ sbuilder.append(buf[0]);
+ } else {
+ for (int i = 0, n = buf.length; i < n; i++) {
+ byte code = buf[i];
+ if (code == 0) {
+ continue;
+ }
+ if (code > 31 && code < 127) {
+ sbuilder.append((char) code);
+ } else {
+ sbuilder.append('.');
+ }
+ }
+ }
+ }
+ return sbuilder.toString();
+ }
+
+ /**
+ * Returns a string representation of the value of this tag.
+ */
+ public static String tagValueToString(ExifTag tag) {
+ StringBuilder sbuilder = new StringBuilder();
+ short id = tag.getTagId();
+ switch (tag.getDataType()) {
+ case ExifTag.TYPE_UNDEFINED:
+ sbuilder.append(tagUndefinedTypeValueToString(tag));
+ break;
+ case ExifTag.TYPE_UNSIGNED_BYTE:
+ if (id == ExifInterface.TAG_MAKER_NOTE || id == TAG_XP_TITLE ||
+ id == TAG_XP_COMMENT || id == TAG_XP_AUTHOR ||
+ id == TAG_XP_KEYWORDS || id == TAG_XP_SUBJECT) {
+ sbuilder.append(tagUndefinedTypeValueToString(tag));
+ } else {
+ byte[] buf = new byte[tag.getComponentCount()];
+ tag.getBytes(buf);
+ for (int i = 0, n = tag.getComponentCount(); i < n; i++) {
+ if (i != 0)
+ sbuilder.append(" ");
+ sbuilder.append(buf[i]);
+ }
+ }
+ break;
+ case ExifTag.TYPE_ASCII:
+ byte[] buf = tag.getStringByte();
+ for (int i = 0, n = buf.length; i < n; i++) {
+ byte code = buf[i];
+ if (code == 0) {
+ // Treat some tag as undefined type data.
+ if (id == ExifInterface.TAG_COPYRIGHT
+ || id == ExifInterface.TAG_GPS_DATE_STAMP) {
+ continue;
+ } else {
+ break;
+ }
+ }
+ if (code > 31 && code < 127) {
+ sbuilder.append((char) code);
+ } else {
+ sbuilder.append('.');
+ }
+ }
+ break;
+ case ExifTag.TYPE_UNSIGNED_LONG:
+ for (int i = 0, n = tag.getComponentCount(); i < n; i++) {
+ if (i != 0) {
+ sbuilder.append(" ");
+ }
+ sbuilder.append(tag.getValueAt(i));
+ }
+ break;
+ case ExifTag.TYPE_RATIONAL:
+ case ExifTag.TYPE_UNSIGNED_RATIONAL:
+ for (int i = 0, n = tag.getComponentCount(); i < n; i++) {
+ Rational r = tag.getRational(i);
+ if (i != 0) {
+ sbuilder.append(" ");
+ }
+ sbuilder.append(r.getNumerator()).append("/").append(r.getDenominator());
+ }
+ break;
+ case ExifTag.TYPE_UNSIGNED_SHORT:
+ for (int i = 0, n = tag.getComponentCount(); i < n; i++) {
+ if (i != 0) {
+ sbuilder.append(" ");
+ }
+ sbuilder.append((int) tag.getValueAt(i));
+ }
+ break;
+ case ExifTag.TYPE_LONG:
+ for (int i = 0, n = tag.getComponentCount(); i < n; i++) {
+ if (i != 0) {
+ sbuilder.append(" ");
+ }
+ sbuilder.append((int) tag.getValueAt(i));
+ }
+ break;
+ }
+ return sbuilder.toString();
+ }
+
+ public static String valueToString(Object obj) {
+ if (obj instanceof int[]) {
+ return Arrays.toString((int[]) obj);
+ } else if (obj instanceof Integer[]) {
+ return Arrays.toString((Integer[]) obj);
+ } else if (obj instanceof long[]) {
+ return Arrays.toString((long[]) obj);
+ } else if (obj instanceof Long[]) {
+ return Arrays.toString((Long[]) obj);
+ } else if (obj instanceof Rational) {
+ return ((Rational) obj).toString();
+ } else if (obj instanceof Rational[]) {
+ return Arrays.toString((Rational[]) obj);
+ } else if (obj instanceof byte[]) {
+ return Arrays.toString((byte[]) obj);
+ } else if (obj != null) {
+ return obj.toString();
+ }
+ return "";
+ }
+}