summaryrefslogtreecommitdiffstats
path: root/media/tests/MediaFrameworkTest
diff options
context:
space:
mode:
authorIgor Murashkin <iam@google.com>2014-04-22 15:05:50 -0700
committerIgor Murashkin <iam@google.com>2014-05-13 15:28:24 -0700
commit3c40a046cf0ea7b6af01ec93e5276eccb3234bfe (patch)
treeb78afeb203d25c370995da98601eca721fbcd7e6 /media/tests/MediaFrameworkTest
parentbee74c2b479153bb6a4b9e03f068658042a5fdfc (diff)
downloadframeworks_base-3c40a046cf0ea7b6af01ec93e5276eccb3234bfe.zip
frameworks_base-3c40a046cf0ea7b6af01ec93e5276eccb3234bfe.tar.gz
frameworks_base-3c40a046cf0ea7b6af01ec93e5276eccb3234bfe.tar.bz2
camera2: Update native<->managed camera metadata marshalers
* Improve existing marshalers: - each managed/native type combination can be queried marshal support - marshalers can recursively call other marshalers for nested types - support marshaling/unmarshaling generic classes by using super type tokens * Add new marshalers for: - ColorSpaceTransform - MeteringRectangle - Parcelable - Range<T> - ReprocessFormatsMap - RggbChannelVector - SizeF - StreamConfiguration - StreamConfigurationDuration Batteries included; so are unit tests. Bug: 14628001 Change-Id: I38d3e646ccfb3953898cd6f750c33e4097328482
Diffstat (limited to 'media/tests/MediaFrameworkTest')
-rw-r--r--media/tests/MediaFrameworkTest/Android.mk2
-rw-r--r--media/tests/MediaFrameworkTest/AndroidManifest.xml4
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java1
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ByteArrayHelpers.java253
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java451
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraUtilsTypeReferenceTest.java223
6 files changed, 881 insertions, 53 deletions
diff --git a/media/tests/MediaFrameworkTest/Android.mk b/media/tests/MediaFrameworkTest/Android.mk
index 1e6b2e7..42da48d 100644
--- a/media/tests/MediaFrameworkTest/Android.mk
+++ b/media/tests/MediaFrameworkTest/Android.mk
@@ -7,7 +7,7 @@ LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := easymocklib mockito-target
+LOCAL_STATIC_JAVA_LIBRARIES := easymocklib mockito-target core-tests android-support-test
LOCAL_PACKAGE_NAME := mediaframeworktest
diff --git a/media/tests/MediaFrameworkTest/AndroidManifest.xml b/media/tests/MediaFrameworkTest/AndroidManifest.xml
index 91ee2c6..c62199f 100644
--- a/media/tests/MediaFrameworkTest/AndroidManifest.xml
+++ b/media/tests/MediaFrameworkTest/AndroidManifest.xml
@@ -76,4 +76,8 @@
android:label="MediaFramework integration tests InstrumentationRunner">
</instrumentation>
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.mediaframeworktest"
+ android:label="media framework tests"/>
+
</manifest>
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java
index 64b12b7..11d9070 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java
@@ -63,6 +63,7 @@ public class MediaFrameworkUnitTestRunner extends InstrumentationTestRunner {
suite.addTestSuite(CameraUtilsRuntimeExceptionTest.class);
suite.addTestSuite(CameraUtilsUncheckedThrowTest.class);
suite.addTestSuite(CameraUtilsBinderDecoratorTest.class);
+ suite.addTestSuite(CameraUtilsTypeReferenceTest.class);
suite.addTestSuite(CameraMetadataTest.class);
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ByteArrayHelpers.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ByteArrayHelpers.java
new file mode 100644
index 0000000..6f31480
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ByteArrayHelpers.java
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2014 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.mediaframeworktest.unit;
+
+import android.util.Log;
+
+import java.lang.reflect.Array;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Arrays;
+
+public class ByteArrayHelpers {
+
+ private static final String TAG = "ByteArrayHelpers";
+ private static boolean VERBOSE = false;
+
+ /**
+ * Convert an array of byte primitives to a {@code byte[]} using native endian order.
+ *
+ * <p>This function is a pass-through; it's here only to provide overloads for
+ * every single type of primitive arrays.
+ *
+ * @param array array of primitives
+ * @return array
+ */
+ public static byte[] toByteArray(byte[] array) {
+ return array;
+ }
+
+ /**
+ * Convert an array of shorts to a {@code byte[]} using native endian order.
+ *
+ * @param array array of shorts
+ * @return array converted into byte array using native endian order
+ */
+ public static byte[] toByteArray(short[] array) {
+ return toByteArray(array, Short.SIZE);
+ }
+
+ /**
+ * Convert an array of chars to a {@code byte[]} using native endian order.
+ *
+ * @param array array of chars
+ * @return array converted into byte array using native endian order
+ */
+ public static byte[] toByteArray(char[] array) {
+ return toByteArray(array, Character.SIZE);
+ }
+ /**
+ * Convert an array of ints to a {@code byte[]} using native endian order.
+ *
+ * @param array array of ints
+ * @return array converted into byte array using native endian order
+ */
+ public static byte[] toByteArray(int[] array) {
+ return toByteArray(array, Integer.SIZE);
+ }
+ /**
+ * Convert an array of longs to a {@code byte[]} using native endian order.
+ *
+ * @param array array of longs
+ * @return array converted into byte array using native endian order
+ */
+ public static byte[] toByteArray(long[] array) {
+ return toByteArray(array, Long.SIZE);
+ }
+ /**
+ * Convert an array of floats to a {@code byte[]} using native endian order.
+ *
+ * @param array array of floats
+ * @return array converted into byte array using native endian order
+ */
+ public static byte[] toByteArray(float[] array) {
+ return toByteArray(array, Float.SIZE);
+ }
+ /**
+ * Convert an array of doubles to a {@code byte[]} using native endian order.
+ *
+ * @param array array of doubles
+ * @return array converted into byte array using native endian order
+ */
+ public static byte[] toByteArray(double[] array) {
+ return toByteArray(array, Double.SIZE);
+ }
+
+ /**
+ * Convert an array of primitives to a {@code byte[]} using native endian order.
+ *
+ * <p>Arguments other than arrays are not supported. The array component must be primitive,
+ * the wrapper class is not allowed (e.g. {@code int[]} is ok, but {@code Integer[]} is not.</p>
+ *
+ * @param array array of primitives
+ * @return array converted into byte array using native endian order
+ *
+ * @throws IllegalArgumentException if {@code array} was not an array of primitives
+ */
+ public static <T> byte[] toByteArray(T array) {
+ @SuppressWarnings("unchecked")
+ Class<T> klass = (Class<T>) array.getClass();
+
+ if (!klass.isArray()) {
+ throw new IllegalArgumentException("array class must be an array");
+ }
+
+ Class<?> componentClass = klass.getComponentType();
+
+ if (!componentClass.isPrimitive()) {
+ throw new IllegalArgumentException("array's component must be a primitive");
+ }
+
+ int sizeInBits;
+ if (klass == int.class) {
+ sizeInBits = Integer.SIZE;
+ } else if (klass == float.class) {
+ sizeInBits = Float.SIZE;
+ } else if (klass == double.class) {
+ sizeInBits = Double.SIZE;
+ } else if (klass == short.class) {
+ sizeInBits = Short.SIZE;
+ } else if (klass == char.class) {
+ sizeInBits = Character.SIZE;
+ } else if (klass == long.class) {
+ sizeInBits = Long.SIZE;
+ } else if (klass == byte.class) {
+ sizeInBits = Byte.SIZE;
+ } else {
+ throw new AssertionError();
+ }
+
+ return toByteArray(array, sizeInBits);
+ }
+
+ /**
+ * Convert a variadic list of {@code Number}s into a byte array using native endian order.
+ *
+ * <p>Each {@link Number} must be an instance of a primitive wrapper class
+ * (e.g. {@link Integer} is OK, since it wraps {@code int}, but {@code BigInteger} is not.</p>
+ *
+ * @param numbers variadic list of numeric values
+ * @return array converted into byte array using native endian order
+ *
+ * @throws IllegalArgumentException
+ * if {@code numbers} contained a class that wasn't a primitive wrapper
+ */
+ public static byte[] toByteArray(Number... numbers) {
+ if (numbers.length == 0) {
+ throw new IllegalArgumentException("too few numbers");
+ }
+
+ if (VERBOSE) Log.v(TAG, "toByteArray - input: " + Arrays.toString(numbers));
+
+ // Have a large enough capacity to fit in every number as a double
+ ByteBuffer byteBuffer = ByteBuffer.allocate(numbers.length * (Double.SIZE / Byte.SIZE))
+ .order(ByteOrder.nativeOrder());
+
+ for (int i = 0; i < numbers.length; ++i) {
+ Number value = numbers[i];
+ Class<? extends Number> klass = value.getClass();
+
+ if (VERBOSE) Log.v(TAG, "toByteArray - number " + i + ", class " + klass);
+
+ if (klass == Integer.class) {
+ byteBuffer.putInt((Integer)value);
+ } else if (klass == Float.class) {
+ byteBuffer.putFloat((Float)value);
+ } else if (klass == Double.class) {
+ byteBuffer.putDouble((Double)value);
+ } else if (klass == Short.class) {
+ byteBuffer.putShort((Short)value);
+ } else if (klass == Long.class) {
+ byteBuffer.putLong((Long)value);
+ } else if (klass == Byte.class) {
+ byteBuffer.put((Byte)value);
+ } else {
+ throw new IllegalArgumentException(
+ "number class invalid; must be wrapper around primitive class");
+ }
+ }
+
+ if (VERBOSE) Log.v(TAG, "toByteArray - end of loop");
+
+ // Each number written is at least 1 byte, so the position should be at least length
+ if (numbers.length != 0 && byteBuffer.position() < numbers.length) {
+ throw new AssertionError(String.format(
+ "Had %d numbers, but byte buffer position was only %d",
+ numbers.length, byteBuffer.position()));
+ }
+
+ byteBuffer.flip();
+
+ byte[] bytes = new byte[byteBuffer.limit()];
+ byteBuffer.get(bytes);
+
+ if (VERBOSE) Log.v(TAG, "toByteArray - output: " + Arrays.toString(bytes));
+
+ return bytes;
+ }
+
+ private static <T> byte[] toByteArray(T array, int sizeOfTBits) {
+ @SuppressWarnings("unchecked")
+ Class<T> klass = (Class<T>) array.getClass();
+
+ if (!klass.isArray()) {
+ throw new IllegalArgumentException("array class must be an array");
+ }
+
+ int sizeOfT = sizeOfTBits / Byte.SIZE;
+ int byteLength = Array.getLength(array) * sizeOfT;
+
+ if (klass == byte[].class) {
+ // Always return a copy
+ return Arrays.copyOf((byte[])array, byteLength);
+ }
+
+ ByteBuffer byteBuffer = ByteBuffer.allocate(byteLength).order(ByteOrder.nativeOrder());
+
+ if (klass == int[].class) {
+ byteBuffer.asIntBuffer().put((int[])array);
+ } else if (klass == float[].class) {
+ byteBuffer.asFloatBuffer().put((float[])array);
+ } else if (klass == double[].class) {
+ byteBuffer.asDoubleBuffer().put((double[])array);
+ } else if (klass == short[].class) {
+ byteBuffer.asShortBuffer().put((short[])array);
+ } else if (klass == char[].class) {
+ byteBuffer.asCharBuffer().put((char[])array);
+ } else if (klass == long[].class) {
+ byteBuffer.asLongBuffer().put((long[])array);
+ } else {
+ throw new IllegalArgumentException("array class invalid; must be a primitive array");
+ }
+
+ return byteBuffer.array();
+ }
+
+ private ByteArrayHelpers() {
+ throw new AssertionError();
+ }
+}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
index 45df065..b28733a 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
@@ -16,18 +16,30 @@
package com.android.mediaframeworktest.unit;
-import android.os.Parcel;
import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Range;
+import android.util.SizeF;
+import android.graphics.ImageFormat;
import android.graphics.Point;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.ColorSpaceTransform;
import android.hardware.camera2.Face;
+import android.hardware.camera2.MeteringRectangle;
import android.hardware.camera2.Rational;
+import android.hardware.camera2.ReprocessFormatsMap;
+import android.hardware.camera2.RggbChannelVector;
import android.hardware.camera2.Size;
+import android.hardware.camera2.StreamConfiguration;
+import android.hardware.camera2.StreamConfigurationDuration;
import android.hardware.camera2.impl.CameraMetadataNative;
+import android.hardware.camera2.marshal.impl.MarshalQueryableEnum;
+import android.hardware.camera2.utils.TypeReference;
import static android.hardware.camera2.impl.CameraMetadataNative.*;
+import static com.android.mediaframeworktest.unit.ByteArrayHelpers.*;
import java.lang.reflect.Array;
import java.nio.ByteBuffer;
@@ -43,7 +55,6 @@ import java.nio.ByteOrder;
public class CameraMetadataTest extends junit.framework.TestCase {
CameraMetadataNative mMetadata;
- Parcel mParcel;
// Sections
static final int ANDROID_COLOR_CORRECTION = 0;
@@ -64,15 +75,11 @@ public class CameraMetadataTest extends junit.framework.TestCase {
@Override
public void setUp() {
mMetadata = new CameraMetadataNative();
- mParcel = Parcel.obtain();
}
@Override
public void tearDown() throws Exception {
mMetadata = null;
-
- mParcel.recycle();
- mParcel = null;
}
@SmallTest
@@ -130,10 +137,14 @@ public class CameraMetadataTest extends junit.framework.TestCase {
@SmallTest
public void testGetTypeFromTag() {
- assertEquals(TYPE_BYTE, CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_MODE));
- assertEquals(TYPE_RATIONAL, CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_TRANSFORM));
- assertEquals(TYPE_FLOAT, CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_GAINS));
- assertEquals(TYPE_BYTE, CameraMetadataNative.getNativeType(ANDROID_CONTROL_AE_ANTIBANDING_MODE));
+ assertEquals(TYPE_BYTE,
+ CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_MODE));
+ assertEquals(TYPE_RATIONAL,
+ CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_TRANSFORM));
+ assertEquals(TYPE_FLOAT,
+ CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_GAINS));
+ assertEquals(TYPE_BYTE,
+ CameraMetadataNative.getNativeType(ANDROID_CONTROL_AE_ANTIBANDING_MODE));
assertEquals(TYPE_INT32,
CameraMetadataNative.getNativeType(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION));
@@ -215,35 +226,163 @@ public class CameraMetadataTest extends junit.framework.TestCase {
assertEquals(1, mMetadata.getEntryCount());
}
+ /**
+ * Format an array into a string with the {@code badIndex} highlighted with {@code **}.
+ *
+ * <p>Numbers are printed as hexadecimal values.</p>
+ *
+ * <p>Example: {@code "[hello, **world**]"} for a {@code string[]},
+ * or a {@code "[**0xFF**, 0xFF]"} for a {@code int[]}.</p>
+ */
+ private static <T> String formatArray(T array, int badIndex) {
+ StringBuilder builder = new StringBuilder();
+
+ builder.append("[");
+
+ int len = Array.getLength(array);
+ for (int i = 0; i < len; ++i) {
+
+ Object elem = Array.get(array, i);
+
+ if (i == badIndex) {
+ builder.append("**");
+ }
+
+ if (elem instanceof Number) {
+ builder.append(String.format("%x", elem));
+ } else {
+ builder.append(elem);
+ }
+
+ if (i == badIndex) {
+ builder.append("**");
+ }
+
+ if (i != len - 1) {
+ builder.append(", ");
+ }
+ }
+
+ builder.append("]");
+
+ return builder.toString();
+ }
+
private static <T> void assertArrayEquals(T expected, T actual) {
- assertEquals(Array.getLength(expected), Array.getLength(actual));
+ if (!expected.getClass().isArray() || !actual.getClass().isArray()) {
+ throw new IllegalArgumentException("expected, actual must both be arrays");
+ }
+
+ assertEquals("Array lengths must be equal",
+ Array.getLength(expected), Array.getLength(actual));
int len = Array.getLength(expected);
for (int i = 0; i < len; ++i) {
- assertEquals(Array.get(expected, i), Array.get(actual, i));
+
+ Object expectedElement = Array.get(expected, i);
+ Object actualElement = Array.get(actual, i);
+
+ if (!expectedElement.equals(actualElement)) {
+ fail(String.format(
+ "element %d in array was not equal (expected %s, actual %s). "
+ + "Arrays were: (expected %s, actual %s).",
+ i, expectedElement, actualElement,
+ formatArray(expected, i),
+ formatArray(actual, i)));
+ }
}
}
- private <T> void checkKeyGetAndSet(String keyStr, Class<T> type, T value) {
- assertFalse("Use checkKeyGetAndSetArray to compare array Keys", type.isArray());
-
- Key<T> key = new Key<T>(keyStr, type);
+ private <T> void checkKeyGetAndSet(String keyStr, TypeReference<T> typeToken, T expected,
+ boolean reuse) {
+ Key<T> key = new Key<T>(keyStr, typeToken);
assertNull(mMetadata.get(key));
mMetadata.set(key, null);
assertNull(mMetadata.get(key));
- mMetadata.set(key, value);
+ mMetadata.set(key, expected);
T actual = mMetadata.get(key);
- assertEquals(value, actual);
+
+ if (typeToken.getRawType().isArray()) {
+ assertArrayEquals(expected, actual);
+ } else {
+ assertEquals(expected, actual);
+ }
+
+ if (reuse) {
+ // reset the key incase we want to use it again
+ mMetadata.set(key, null);
+ }
+ }
+
+ private <T> void checkKeyGetAndSet(String keyStr, TypeReference<T> typeToken, T expected) {
+ checkKeyGetAndSet(keyStr, typeToken, expected, /*reuse*/false);
}
- private <T> void checkKeyGetAndSetArray(String keyStr, Class<T> type, T value) {
- assertTrue(type.isArray());
+ private <T> void checkKeyGetAndSet(String keyStr, Class<T> type, T expected) {
+ checkKeyGetAndSet(keyStr, TypeReference.createSpecializedTypeReference(type), expected);
+ }
+
+ /**
+ * Ensure that the data survives a marshal/unmarshal round-trip;
+ * it must also be equal to the {@code expectedNative} byte array.
+ *
+ * <p>As a side-effect, the metadata value corresponding to the key is now set to
+ * {@code expected}.</p>
+ *
+ * @return key created with {@code keyName} and {@code T}
+ */
+ private <T> Key<T> checkKeyMarshal(String keyName, TypeReference<T> typeReference,
+ T expected, byte[] expectedNative) {
+ Key<T> key = new Key<T>(keyName, typeReference);
- Key<T> key = new Key<T>(keyStr, type);
+ mMetadata.set(key, null);
assertNull(mMetadata.get(key));
- mMetadata.set(key, value);
- assertArrayEquals(value, mMetadata.get(key));
+
+ // Write managed value -> make sure native bytes are what we expect
+ mMetadata.set(key, expected);
+
+ byte[] actualValues = mMetadata.readValues(key.getTag());
+ assertArrayEquals(expectedNative, actualValues);
+
+ // Write managed value -> make sure read-out managed value is what we expect
+ T actual = mMetadata.get(key);
+
+ if (typeReference.getRawType().isArray()) {
+ assertArrayEquals(expected, actual);
+ } else {
+ assertEquals(expected, actual);
+ }
+
+ // Write native bytes -> make sure read-out managed value is what we expect
+ mMetadata.writeValues(key.getTag(), expectedNative);
+ actual = mMetadata.get(key);
+
+ if (typeReference.getRawType().isArray()) {
+ assertArrayEquals(expected, actual);
+ } else {
+ assertEquals(expected, actual);
+ }
+
+ return key;
+ }
+
+ /**
+ * Ensure that the data survives a marshal/unmarshal round-trip;
+ * it must also be equal to the {@code expectedNative} byte array.
+ *
+ * <p>As a side-effect,
+ * the metadata value corresponding to the key is now set to {@code expected}.</p>
+ *
+ * @return key created with {@code keyName} and {@code T}
+ */
+ private <T> Key<T> checkKeyMarshal(String keyName, T expected, byte[] expectedNative) {
+ @SuppressWarnings("unchecked")
+ Class<T> expectedClass = (Class<T>) expected.getClass();
+ return checkKeyMarshal(keyName,
+ TypeReference.createSpecializedTypeReference(expectedClass),
+ expected,
+ expectedNative);
}
@SmallTest
@@ -280,36 +419,36 @@ public class CameraMetadataTest extends junit.framework.TestCase {
@SmallTest
public void testReadWritePrimitiveArray() {
// int32 (n)
- checkKeyGetAndSetArray("android.sensor.info.sensitivityRange", int[].class,
+ checkKeyGetAndSet("android.sensor.info.sensitivityRange", int[].class,
new int[] {
0xC0FFEE, 0xDEADF00D
});
// byte (n)
- checkKeyGetAndSetArray("android.statistics.faceScores", byte[].class, new byte[] {
+ checkKeyGetAndSet("android.statistics.faceScores", byte[].class, new byte[] {
1, 2, 3, 4
});
// int64 (n)
- checkKeyGetAndSetArray("android.scaler.availableProcessedMinDurations", long[].class,
+ checkKeyGetAndSet("android.scaler.availableProcessedMinDurations", long[].class,
new long[] {
0xABCD12345678FFFFL, 0x1234ABCD5678FFFFL, 0xFFFF12345678ABCDL
});
// float (n)
- checkKeyGetAndSetArray("android.lens.info.availableApertures", float[].class,
+ checkKeyGetAndSet("android.lens.info.availableApertures", float[].class,
new float[] {
Float.MAX_VALUE, Float.MIN_NORMAL, Float.MIN_VALUE
});
// double (n) -- in particular double x 3
- checkKeyGetAndSetArray("android.jpeg.gpsCoordinates", double[].class,
+ checkKeyGetAndSet("android.jpeg.gpsCoordinates", double[].class,
new double[] {
Double.MAX_VALUE, Double.MIN_NORMAL, Double.MIN_VALUE
});
// rational (n) -- in particular rational x 9
- checkKeyGetAndSetArray("android.sensor.calibrationTransform1", Rational[].class,
+ checkKeyGetAndSet("android.sensor.calibrationTransform1", Rational[].class,
new Rational[] {
new Rational(1, 2), new Rational(3, 4), new Rational(5, 6),
new Rational(7, 8), new Rational(9, 10), new Rational(10, 11),
@@ -321,13 +460,12 @@ public class CameraMetadataTest extends junit.framework.TestCase {
*/
// bool (n) -- with TYPE_BYTE
- checkKeyGetAndSetArray("android.control.aeLock", boolean[].class, new boolean[] {
+ checkKeyGetAndSet("android.control.aeLock", boolean[].class, new boolean[] {
true, false, true
});
-
// integer (n) -- with TYPE_BYTE
- checkKeyGetAndSetArray("android.control.aeAvailableModes", int[].class, new int[] {
+ checkKeyGetAndSet("android.control.aeAvailableModes", int[].class, new int[] {
1, 2, 3, 4
});
}
@@ -345,7 +483,6 @@ public class CameraMetadataTest extends junit.framework.TestCase {
AUTO
}
- // TODO: special values for the enum.
private enum AvailableFormat {
RAW_SENSOR,
YV12,
@@ -366,7 +503,7 @@ public class CameraMetadataTest extends junit.framework.TestCase {
AeAntibandingMode.AUTO);
// byte (n)
- checkKeyGetAndSetArray("android.control.aeAvailableAntibandingModes",
+ checkKeyGetAndSet("android.control.aeAvailableAntibandingModes",
AeAntibandingMode[].class, new AeAntibandingMode[] {
AeAntibandingMode.OFF, AeAntibandingMode._50HZ, AeAntibandingMode._60HZ,
AeAntibandingMode.AUTO
@@ -376,7 +513,7 @@ public class CameraMetadataTest extends junit.framework.TestCase {
* Stranger cases that don't use byte enums
*/
// int (n)
- checkKeyGetAndSetArray("android.scaler.availableFormats", AvailableFormat[].class,
+ checkKeyGetAndSet("android.scaler.availableFormats", AvailableFormat[].class,
new AvailableFormat[] {
AvailableFormat.RAW_SENSOR,
AvailableFormat.YV12,
@@ -389,7 +526,7 @@ public class CameraMetadataTest extends junit.framework.TestCase {
@SmallTest
public void testReadWriteEnumWithCustomValues() {
- CameraMetadataNative.registerEnumValues(AeAntibandingMode.class, new int[] {
+ MarshalQueryableEnum.registerEnumValues(AeAntibandingMode.class, new int[] {
0,
10,
20,
@@ -401,15 +538,12 @@ public class CameraMetadataTest extends junit.framework.TestCase {
AeAntibandingMode.AUTO);
// byte (n)
- checkKeyGetAndSetArray("android.control.aeAvailableAntibandingModes",
+ checkKeyGetAndSet("android.control.aeAvailableAntibandingModes",
AeAntibandingMode[].class, new AeAntibandingMode[] {
AeAntibandingMode.OFF, AeAntibandingMode._50HZ, AeAntibandingMode._60HZ,
AeAntibandingMode.AUTO
});
- Key<AeAntibandingMode[]> aeAntibandingModeKey =
- new Key<AeAntibandingMode[]>("android.control.aeAvailableAntibandingModes",
- AeAntibandingMode[].class);
byte[] aeAntibandingModeValues = mMetadata.readValues(CameraMetadataNative
.getTag("android.control.aeAvailableAntibandingModes"));
byte[] expectedValues = new byte[] { 0, 10, 20, 30 };
@@ -420,7 +554,7 @@ public class CameraMetadataTest extends junit.framework.TestCase {
* Stranger cases that don't use byte enums
*/
// int (n)
- CameraMetadataNative.registerEnumValues(AvailableFormat.class, new int[] {
+ MarshalQueryableEnum.registerEnumValues(AvailableFormat.class, new int[] {
0x20,
0x32315659,
0x11,
@@ -429,7 +563,7 @@ public class CameraMetadataTest extends junit.framework.TestCase {
0x21,
});
- checkKeyGetAndSetArray("android.scaler.availableFormats", AvailableFormat[].class,
+ checkKeyGetAndSet("android.scaler.availableFormats", AvailableFormat[].class,
new AvailableFormat[] {
AvailableFormat.RAW_SENSOR,
AvailableFormat.YV12,
@@ -466,7 +600,7 @@ public class CameraMetadataTest extends junit.framework.TestCase {
checkKeyGetAndSet("android.jpeg.thumbnailSize", Size.class, new Size(123, 456));
// int32 x 2 x n
- checkKeyGetAndSetArray("android.scaler.availableJpegSizes", Size[].class, new Size[] {
+ checkKeyGetAndSet("android.scaler.availableJpegSizes", Size[].class, new Size[] {
new Size(123, 456),
new Size(0xDEAD, 0xF00D),
new Size(0xF00, 0xB00)
@@ -474,16 +608,217 @@ public class CameraMetadataTest extends junit.framework.TestCase {
}
@SmallTest
- public void testReadWriteRectangle() {
+ public void testReadWriteRggbChannelVector() {
// int32 x n
- checkKeyGetAndSet("android.scaler.cropRegion", Rect.class, new Rect(10, 11, 1280, 1024));
+ checkKeyMarshal("android.colorCorrection.gains",
+ new RggbChannelVector(1.0f, 2.1f, 3.2f, 4.5f),
+ toByteArray(1.0f, 2.1f, 3.2f, 4.5f));
+
+ // int32 x 2 x n [pretend; actual is not array]
+ checkKeyMarshal("android.colorCorrection.gains",
+ new RggbChannelVector[] {
+ new RggbChannelVector(1.0f, 2.0f, 3.0f, 4.0f),
+ new RggbChannelVector(9.0f, 8.0f, 7.0f, 6.0f),
+ new RggbChannelVector(1.3f, 5.5f, 2.4f, 6.7f),
+ }, toByteArray(
+ 1.0f, 2.0f, 3.0f, 4.0f,
+ 9.0f, 8.0f, 7.0f, 6.0f,
+ 1.3f, 5.5f, 2.4f, 6.7f
+ ));
+ }
+
+ @SmallTest
+ public void testReadWriteSizeF() {
+ // int32 x n
+ checkKeyMarshal("android.sensor.info.physicalSize",
+ new SizeF(123f, 456f),
+ toByteArray(123f, 456f));
// int32 x 2 x n
- checkKeyGetAndSetArray("android.statistics.faceRectangles", Rect[].class, new Rect[] {
+ checkKeyMarshal("android.sensor.info.physicalSize",
+ new SizeF[] {
+ new SizeF(123f, 456f),
+ new SizeF(1.234f, 4.567f),
+ new SizeF(999.0f, 555.0f)
+ },
+ toByteArray(
+ 123f, 456f,
+ 1.234f, 4.567f,
+ 999.0f, 555.0f)
+ );
+ }
+
+ @SmallTest
+ public void testReadWriteRectangle() {
+ // int32 x n
+ checkKeyMarshal("android.scaler.cropRegion",
+ // x1, y1, x2, y2
+ new Rect(10, 11, 1280, 1024),
+ // x, y, width, height
+ toByteArray(10, 11, 1280 - 10, 1024 - 11));
+
+ // int32 x 2 x n [actually not array, but we pretend it is]
+ checkKeyMarshal("android.scaler.cropRegion", new Rect[] {
new Rect(110, 111, 11280, 11024),
new Rect(210, 111, 21280, 21024),
new Rect(310, 111, 31280, 31024)
- });
+ }, toByteArray(
+ 110, 111, 11280 - 110, 11024 - 111,
+ 210, 111, 21280 - 210, 21024 - 111,
+ 310, 111, 31280 - 310, 31024 - 111
+ ));
+ }
+
+ @SmallTest
+ public void testReadWriteMeteringRectangle() {
+ // int32 x 5 x area_count [but we pretend it's a single element]
+ checkKeyMarshal("android.control.aeRegions",
+ new MeteringRectangle(/*x*/1, /*y*/2, /*width*/100, /*height*/200, /*weight*/5),
+ /* xmin, ymin, xmax, ymax, weight */
+ toByteArray(1, 2, 1 + 100, 2 + 200, 5));
+
+ // int32 x 5 x area_count
+ checkKeyMarshal("android.control.afRegions",
+ new MeteringRectangle[] {
+ new MeteringRectangle(/*x*/5, /*y*/6, /*width*/123, /*height*/456, /*weight*/7),
+ new MeteringRectangle(/*x*/7, /*y*/8, /*width*/456, /*height*/999, /*weight*/6),
+ new MeteringRectangle(/*x*/1, /*y*/2, /*width*/100, /*height*/200, /*weight*/5)
+ },
+ toByteArray(
+ 5, 6, 5 + 123, 6 + 456, 7,
+ 7, 8, 7 + 456, 8 + 999, 6,
+ 1, 2, 1 + 100, 2 + 200, 5
+ ));
+ }
+
+ @SmallTest
+ public void testReadWriteColorSpaceTransform() {
+ // rational x 3 x 3
+ checkKeyMarshal("android.colorCorrection.transform",
+ new ColorSpaceTransform(new Rational[] {
+ new Rational(1, 2), new Rational(3, 4), new Rational(5, 6),
+ new Rational(7, 8), new Rational(8, 9), new Rational(10, 11),
+ new Rational(1, 5), new Rational(2, 8), new Rational(3, 9),
+ }),
+ toByteArray(
+ 1, 2, 3, 4, 5, 6,
+ 7, 8, 8, 9, 10, 11,
+ 1, 5, 2, 8, 3, 9));
+ }
+
+ @SmallTest
+ public void testReadWritePoint() {
+ // int32 x 2 [actually 'x n' but pretend it's a single value for now]
+ checkKeyMarshal("android.statistics.hotPixelMap",
+ new Point(1, 2),
+ toByteArray(1, 2));
+
+ // int32 x 2 x samples
+ checkKeyMarshal("android.statistics.hotPixelMap",
+ new Point[] {
+ new Point(1, 2),
+ new Point(3, 4),
+ new Point(5, 6),
+ new Point(7, 8),
+ },
+ toByteArray(
+ 1, 2,
+ 3, 4,
+ 5, 6,
+ 7, 8)
+ );
+ }
+
+ @SmallTest
+ public void testReadWritePointF() {
+ // float x 2 [actually 'x samples' but pretend it's a single value for now]
+ checkKeyMarshal(
+ "android.sensor.profileToneCurve",
+ new PointF(1.0f, 2.0f),
+ toByteArray(1.0f, 2.0f));
+
+ // float x 2 x samples
+ checkKeyMarshal("android.sensor.profileToneCurve",
+ new PointF[] {
+ new PointF(1.0f, 2.0f),
+ new PointF(3.0f, 4.0f),
+ new PointF(5.0f, 6.0f),
+ new PointF(7.0f, 8.0f),
+ },
+ toByteArray(
+ 1.0f, 2.0f,
+ 3.0f, 4.0f,
+ 5.0f, 6.0f,
+ 7.0f, 8.0f));
+ }
+
+ @SmallTest
+ public void testReadWriteRange() {
+ // int32 x 2
+ checkKeyMarshal("android.control.aeTargetFpsRange",
+ new TypeReference<Range<Integer>>() {{ }},
+ Range.create(123, 456),
+ toByteArray(123, 456));
+
+ // int64 x 2
+ checkKeyMarshal("android.sensor.info.exposureTimeRange",
+ new TypeReference<Range<Long>>() {{ }},
+ Range.create(123L, 456L),
+ toByteArray(123L, 456L));
+ }
+
+ @SmallTest
+ public void testReadWriteStreamConfiguration() {
+ // int32 x 4 x n
+ checkKeyMarshal("android.scaler.availableStreamConfigurations",
+ new StreamConfiguration[] {
+ new StreamConfiguration(ImageFormat.YUV_420_888, 640, 480, /*input*/false),
+ new StreamConfiguration(ImageFormat.RGB_565, 320, 240, /*input*/true),
+ },
+ toByteArray(
+ ImageFormat.YUV_420_888, 640, 480, /*input*/0,
+ ImageFormat.RGB_565, 320, 240, /*input*/1)
+ );
+ }
+
+ @SmallTest
+ public void testReadWriteStreamConfigurationDuration() {
+ // Avoid sign extending ints when converting to a long
+ final long MASK_UNSIGNED_INT = 0x00000000ffffffffL;
+
+ // int64 x 4 x n
+ checkKeyMarshal("android.scaler.availableMinFrameDurations",
+ new StreamConfigurationDuration[] {
+ new StreamConfigurationDuration(
+ ImageFormat.YUV_420_888, 640, 480, /*duration*/123L),
+ new StreamConfigurationDuration(
+ ImageFormat.RGB_565, 320, 240, /*duration*/345L),
+ },
+ toByteArray(
+ ImageFormat.YUV_420_888 & MASK_UNSIGNED_INT, 640L, 480L, /*duration*/123L,
+ ImageFormat.RGB_565 & MASK_UNSIGNED_INT, 320L, 240L, /*duration*/345L)
+ );
+ }
+
+
+ @SmallTest
+ public void testReadWriteReprocessFormatsMap() {
+
+ final int RAW_OPAQUE = 0x24;
+ final int RAW16 = ImageFormat.RAW_SENSOR;
+ final int YUV_420_888 = ImageFormat.YUV_420_888;
+ final int BLOB = 0x21;
+
+ int[] contents = new int[] {
+ RAW_OPAQUE, 3, RAW16, YUV_420_888, BLOB,
+ RAW16, 2, YUV_420_888, BLOB,
+ };
+
+ // int32 x n
+ checkKeyMarshal("android.scaler.availableInputOutputFormatsMap",
+ new ReprocessFormatsMap(contents),
+ toByteArray(contents)
+ );
}
@SmallTest
@@ -525,10 +860,6 @@ public class CameraMetadataTest extends junit.framework.TestCase {
assertArrayEquals(gpsBytes, actualBytes2);
}
- <T> void compareGeneric(T expected, T actual) {
- assertEquals(expected, actual);
- }
-
@SmallTest
public void testReadWriteOverride() {
//
@@ -691,7 +1022,7 @@ public class CameraMetadataTest extends junit.framework.TestCase {
*/
private <T> void validateArrayMetadataReadWriteOverride(Key<T> key, T writeValues,
T readValues, int tag) {
- Class<T> type = key.getType();
+ Class<?> type = writeValues.getClass();
if (!type.isArray()) {
throw new IllegalArgumentException("This function expects an key with array type");
} else if (type != int[].class && type != long[].class) {
@@ -737,4 +1068,20 @@ public class CameraMetadataTest extends junit.framework.TestCase {
assertNotNull(key.getName() + " result shouldn't be null", result);
assertArrayEquals(writeValues, result);
}
+
+ // TODO: move somewhere else
+ @SmallTest
+ public void testToByteArray() {
+ assertArrayEquals(new byte[] { 5, 0, 0, 0, 6, 0, 0, 0 },
+ toByteArray(5, 6));
+ assertArrayEquals(new byte[] { 5, 0, 6, 0, },
+ toByteArray((short)5, (short)6));
+ assertArrayEquals(new byte[] { (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
+ (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,},
+ toByteArray(~0, ~0));
+
+ assertArrayEquals(new byte[] { (byte)0xAB, (byte)0xFF, 0, 0,
+ 0x0D, (byte)0xF0, (byte)0xAD, (byte)0xDE },
+ toByteArray(0xFFAB, 0xDEADF00D));
+ }
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraUtilsTypeReferenceTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraUtilsTypeReferenceTest.java
new file mode 100644
index 0000000..1b765ea
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraUtilsTypeReferenceTest.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2014 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.mediaframeworktest.unit;
+
+import static android.hardware.camera2.utils.TypeReference.*;
+
+import android.hardware.camera2.utils.TypeReference;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+
+import java.lang.reflect.Type;
+import java.util.List;
+
+public class CameraUtilsTypeReferenceTest extends junit.framework.TestCase {
+ private static final String TAG = CameraUtilsTypeReferenceTest.class.getSimpleName();
+ private static final boolean VERBOSE = false;
+
+ private class RegularClass {}
+ private class SubClass extends RegularClass {}
+
+ private class GenericClass<T> {}
+ private class SubGenericClass<T> extends GenericClass<T> {}
+
+ private class SpecificClass extends GenericClass<Integer> {}
+
+ private interface RegularInterface {}
+ private interface GenericInterface<T> {}
+ private interface GenericInterface2<T> {}
+
+ private class ImplementsRegularInterface implements RegularInterface {}
+ private class ImplementsGenericInterface<T> implements GenericInterface<T> {}
+ private class Implements2GenericInterface<T>
+ implements GenericInterface<Integer>, GenericInterface2<T> {}
+
+ private class GenericOuterClass<T> {
+ class GenericInnerClass {
+ @SuppressWarnings("unused")
+ T field;
+ }
+ }
+
+ private static void assertContainsTypeVariable(Type type) {
+ assertTrue(type.toString() + " was expected to have a type variable, but it didn't",
+ containsTypeVariable(type));
+ }
+
+ private static void assertLacksTypeVariable(Type type) {
+ assertFalse(type.toString() + " was expected to *not* have a type variable, but it did",
+ containsTypeVariable(type));
+ }
+
+ /*
+ * Only test classes and interfaces. Other types are not tested (e.g. fields, methods, etc).
+ */
+
+ @SmallTest
+ public void testLacksTypeVariables() {
+ assertLacksTypeVariable(RegularClass.class);
+ assertLacksTypeVariable(SubClass.class);
+ assertLacksTypeVariable(SpecificClass.class);
+
+ assertLacksTypeVariable(RegularInterface.class);
+ assertLacksTypeVariable(ImplementsRegularInterface.class);
+ }
+
+ @SmallTest
+ public void testContainsTypeVariables() {
+ assertContainsTypeVariable(GenericClass.class);
+ assertContainsTypeVariable(SubGenericClass.class);
+
+ assertContainsTypeVariable(GenericInterface.class);
+ assertContainsTypeVariable(ImplementsGenericInterface.class);
+ assertContainsTypeVariable(Implements2GenericInterface.class);
+
+ assertContainsTypeVariable(GenericOuterClass.class);
+ assertContainsTypeVariable(GenericOuterClass.GenericInnerClass.class);
+ }
+
+ /**
+ * This should always throw an IllegalArgumentException since the
+ * type reference to {@code T} will contain a type variable (also {@code T}).
+ *
+ * @throws IllegalArgumentException unconditionally
+ */
+ private static <T> TypeReference<T> createTypeRefWithTypeVar() {
+ return new TypeReference<T>() {{ }};
+ }
+
+ @SmallTest
+ public void testTypeReferences() {
+ TypeReference<Integer> typeRefInt = new TypeReference<Integer>() {{ }};
+ TypeReference<Integer> typeRefInt2 = new TypeReference<Integer>() {{ }};
+
+ assertEquals(typeRefInt, typeRefInt2);
+ assertEquals("The type ref's captured type should be the Integer class",
+ Integer.class, typeRefInt.getType());
+
+ TypeReference<Float> typeRefFloat = new TypeReference<Float>() {{ }};
+ assertFalse("Integer/Float type references must not be equal",
+ typeRefInt.equals(typeRefFloat));
+ assertEquals("The type ref's captured type should be the Float class",
+ Float.class, typeRefFloat.getType());
+
+ try {
+ TypeReference<Integer> typeRefTypeVar = createTypeRefWithTypeVar();
+ fail("Expected a type reference with type variables to fail");
+ // Unreachable. Make the warning about an unused variable go away.
+ assertFalse(typeRefTypeVar.equals(typeRefInt));
+ } catch (IllegalArgumentException e) {
+ // OK. Expected behavior
+ }
+ }
+
+ // Compare the raw type against rawClass
+ private static <T> void assertRawTypeEquals(TypeReference<T> typeRef, Class<?> rawClass) {
+ assertEquals("Expected the raw type from " + typeRef + " to match the class " + rawClass,
+ rawClass, typeRef.getRawType());
+ }
+
+ // Compare the normal type against the klass
+ private static <T> void assertTypeReferenceEquals(TypeReference<T> typeRef, Class<?> klass) {
+ assertEquals("Expected the type from " + typeRef + " to match the class " + klass,
+ klass, typeRef.getType());
+ }
+
+ @SmallTest
+ public void testRawTypes() {
+ TypeReference<Integer> intToken = new TypeReference<Integer>() {{ }};
+ assertRawTypeEquals(intToken, Integer.class);
+
+ TypeReference<List<Integer>> listToken = new TypeReference<List<Integer>>() {{ }};
+ assertRawTypeEquals(listToken, List.class);
+
+ TypeReference<List<List<Integer>>> listListToken =
+ new TypeReference<List<List<Integer>>>() {{ }};
+ assertRawTypeEquals(listListToken, List.class);
+
+ TypeReference<int[]> intArrayToken = new TypeReference<int[]>() {{ }};
+ assertRawTypeEquals(intArrayToken, int[].class);
+
+ TypeReference<Integer[]> integerArrayToken = new TypeReference<Integer[]>() {{ }};
+ assertRawTypeEquals(integerArrayToken, Integer[].class);
+
+ TypeReference<List<Integer>[]> listArrayToken = new TypeReference<List<Integer>[]>() {{ }};
+ assertRawTypeEquals(listArrayToken, List[].class);
+ }
+
+ private class IntTokenOne extends TypeReference<Integer> {}
+ private class IntTokenTwo extends TypeReference<Integer> {}
+
+ private class IntArrayToken1 extends TypeReference<Integer[]> {}
+ private class IntArrayToken2 extends TypeReference<Integer[]> {}
+
+ private class IntListToken1 extends TypeReference<List<Integer>> {}
+ private class IntListToken2 extends TypeReference<List<Integer>> {}
+
+ private class IntListArrayToken1 extends TypeReference<List<Integer>[]> {}
+ private class IntListArrayToken2 extends TypeReference<List<Integer>[]> {}
+
+
+ // FIXME: Equality will fail: b/14590652
+ @SmallTest
+ public void testEquals() {
+ // Not an array. component type should be null.
+ TypeReference<Integer> intToken = new TypeReference<Integer>() {{ }};
+ assertEquals(intToken, intToken);
+ assertEquals(intToken, new TypeReference<Integer>() {{ }});
+
+ assertEquals(intToken, new IntTokenOne());
+ assertEquals(intToken, new IntTokenTwo());
+ assertEquals(new IntTokenOne(), new IntTokenTwo());
+
+ assertEquals(new IntArrayToken1(), new IntArrayToken2());
+ assertEquals(new IntListToken1(), new IntListToken2());
+ assertEquals(new IntListArrayToken1(), new IntListArrayToken2());
+ }
+
+ @SmallTest
+ public void testComponentType() {
+ // Not an array. component type should be null.
+ TypeReference<Integer> intToken = new TypeReference<Integer>() {{ }};
+ assertNull(intToken.getComponentType());
+
+ TypeReference<List<Integer>> listToken = new TypeReference<List<Integer>>() {{ }};
+ assertNull(listToken.getComponentType());
+
+ TypeReference<List<List<Integer>>> listListToken =
+ new TypeReference<List<List<Integer>>>() {{ }};
+ assertNull(listListToken.getComponentType());
+
+ // Check arrays. Component types should be what we expect.
+ TypeReference<int[]> intArrayToken = new TypeReference<int[]>() {{ }};
+ assertTypeReferenceEquals(intArrayToken.getComponentType(), int.class);
+
+ TypeReference<Integer[]> integerArrayToken = new TypeReference<Integer[]>() {{ }};
+ assertTypeReferenceEquals(integerArrayToken.getComponentType(), Integer.class);
+
+ assertEquals(new IntArrayToken1().getComponentType(),
+ new IntArrayToken2().getComponentType());
+
+ assertEquals(new IntListArrayToken1().getComponentType(),
+ new IntListArrayToken2().getComponentType());
+
+ // FIXME: Equality will fail: b/14590652
+ TypeReference<List<Integer>[]> listArrayToken = new TypeReference<List<Integer>[]>() {{ }};
+ assertEquals(listToken, listArrayToken.getComponentType());
+ }
+}