diff options
author | Igor Murashkin <iam@google.com> | 2014-04-22 15:05:50 -0700 |
---|---|---|
committer | Igor Murashkin <iam@google.com> | 2014-05-13 15:28:24 -0700 |
commit | 3c40a046cf0ea7b6af01ec93e5276eccb3234bfe (patch) | |
tree | b78afeb203d25c370995da98601eca721fbcd7e6 /media/tests/MediaFrameworkTest | |
parent | bee74c2b479153bb6a4b9e03f068658042a5fdfc (diff) | |
download | frameworks_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')
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()); + } +} |