/*
* Copyright (C) 2009 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.media.Metadata;
import android.os.Parcel;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
import java.util.Calendar;
import java.util.Date;
/*
* Check the Java layer that parses serialized metadata in Parcel
* works as expected.
*
*/
public class MediaPlayerMetadataParserTest extends AndroidTestCase {
private static final String TAG = "MediaPlayerMetadataTest";
private static final int kMarker = 0x4d455441; // 'M' 'E' 'T' 'A'
private static final int kHeaderSize = 8;
private Metadata mMetadata = null;
private Parcel mParcel = null;
@Override
protected void setUp() throws Exception {
super.setUp();
mMetadata = new Metadata();
mParcel = Parcel.obtain();
resetParcel();
}
// Check parsing of the parcel fails. Make sure the parser rewind
// the parcel properly.
private void assertParseFail() throws Exception {
mParcel.setDataPosition(0);
assertFalse(mMetadata.parse(mParcel));
assertEquals(0, mParcel.dataPosition());
}
// Check parsing of the parcel is successful.
private void assertParse() throws Exception {
mParcel.setDataPosition(0);
assertTrue(mMetadata.parse(mParcel));
}
// Write the number of bytes from the start of the parcel to the
// current position at the beginning of the parcel (offset 0).
private void adjustSize() {
adjustSize(0);
}
// Write the number of bytes from the offset to the current
// position at position pointed by offset.
private void adjustSize(int offset) {
final int pos = mParcel.dataPosition();
mParcel.setDataPosition(offset);
mParcel.writeInt(pos - offset);
mParcel.setDataPosition(pos);
}
// Rewind the parcel and insert the header.
private void resetParcel() {
mParcel.setDataPosition(0);
// Most tests will use a properly formed parcel with a size
// and the meta marker so we add them by default.
mParcel.writeInt(-1); // Placeholder for the size
mParcel.writeInt(kMarker);
}
// ----------------------------------------------------------------------
// START OF THE TESTS
// There should be at least 8 bytes in the parcel, 4 for the size
// and 4 for the 'M' 'E' 'T' 'A' marker.
@SmallTest
public void testMissingSizeAndMarker() throws Exception {
for (int i = 0; i < kHeaderSize; ++i) {
mParcel.setDataPosition(0);
mParcel.setDataSize(i);
assertEquals(i, mParcel.dataAvail());
assertParseFail();
}
}
// There should be at least 'size' bytes in the parcel.
@SmallTest
public void testMissingData() throws Exception {
final int size = 20;
mParcel.writeInt(size);
mParcel.setDataSize(size - 1);
assertParseFail();
}
// Empty parcel is fine
@SmallTest
public void testEmptyIsOk() throws Exception {
adjustSize();
assertParse();
}
// ----------------------------------------------------------------------
// RECORDS
// ----------------------------------------------------------------------
// A record header should be at least 12 bytes long
@SmallTest
public void testRecordMissingId() throws Exception {
mParcel.writeInt(13); // record length
// misses metadata id and metadata type.
adjustSize();
assertParseFail();
}
@SmallTest
public void testRecordMissingType() throws Exception {
mParcel.writeInt(13); // record length lies
mParcel.writeInt(Metadata.TITLE);
// misses metadata type
adjustSize();
assertParseFail();
}
@SmallTest
public void testRecordWithZeroPayload() throws Exception {
mParcel.writeInt(0);
adjustSize();
assertParseFail();
}
// A record cannot be empty.
@SmallTest
public void testRecordMissingPayload() throws Exception {
mParcel.writeInt(12);
mParcel.writeInt(Metadata.TITLE);
mParcel.writeInt(Metadata.STRING_VAL);
// misses payload
adjustSize();
assertParseFail();
}
// Check records can be found.
@SmallTest
public void testRecordsFound() throws Exception {
writeStringRecord(Metadata.TITLE, "a title");
writeStringRecord(Metadata.GENRE, "comedy");
writeStringRecord(Metadata.firstCustomId(), "custom");
adjustSize();
assertParse();
assertTrue(mMetadata.has(Metadata.TITLE));
assertTrue(mMetadata.has(Metadata.GENRE));
assertTrue(mMetadata.has(Metadata.firstCustomId()));
assertFalse(mMetadata.has(Metadata.DRM_CRIPPLED));
assertEquals(3, mMetadata.keySet().size());
}
// Detects bad metadata type
@SmallTest
public void testBadMetadataType() throws Exception {
final int start = mParcel.dataPosition();
mParcel.writeInt(-1); // Placeholder for the length
mParcel.writeInt(Metadata.TITLE);
mParcel.writeInt(0); // Invalid type.
mParcel.writeString("dummy");
adjustSize(start);
adjustSize();
assertParseFail();
}
// Check a Metadata instance can be reused, i.e the parse method
// wipes out the existing states/keys.
@SmallTest
public void testParseClearState() throws Exception {
writeStringRecord(Metadata.TITLE, "a title");
writeStringRecord(Metadata.GENRE, "comedy");
writeStringRecord(Metadata.firstCustomId(), "custom");
adjustSize();
assertParse();
resetParcel();
writeStringRecord(Metadata.MIME_TYPE, "audio/mpg");
adjustSize();
assertParse();
// Only the mime type metadata should be present.
assertEquals(1, mMetadata.keySet().size());
assertTrue(mMetadata.has(Metadata.MIME_TYPE));
assertFalse(mMetadata.has(Metadata.TITLE));
assertFalse(mMetadata.has(Metadata.GENRE));
assertFalse(mMetadata.has(Metadata.firstCustomId()));
}
// ----------------------------------------------------------------------
// GETTERS
// ----------------------------------------------------------------------
// getString
@SmallTest
public void testGetString() throws Exception {
writeStringRecord(Metadata.TITLE, "a title");
writeStringRecord(Metadata.GENRE, "comedy");
adjustSize();
assertParse();
assertEquals("a title", mMetadata.getString(Metadata.TITLE));
assertEquals("comedy", mMetadata.getString(Metadata.GENRE));
}
// get an empty string.
@SmallTest
public void testGetEmptyString() throws Exception {
writeStringRecord(Metadata.TITLE, "");
adjustSize();
assertParse();
assertEquals("", mMetadata.getString(Metadata.TITLE));
}
// get a string when a NULL value was in the parcel
@SmallTest
public void testGetNullString() throws Exception {
writeStringRecord(Metadata.TITLE, null);
adjustSize();
assertParse();
assertEquals(null, mMetadata.getString(Metadata.TITLE));
}
// get a string when an integer is actually present
@SmallTest
public void testWrongType() throws Exception {
writeIntRecord(Metadata.DURATION, 5);
adjustSize();
assertParse();
try {
mMetadata.getString(Metadata.DURATION);
} catch (IllegalStateException ise) {
return;
}
fail("Exception was not thrown");
}
// getInt
@SmallTest
public void testGetInt() throws Exception {
writeIntRecord(Metadata.CD_TRACK_NUM, 1);
adjustSize();
assertParse();
assertEquals(1, mMetadata.getInt(Metadata.CD_TRACK_NUM));
}
// getBoolean
@SmallTest
public void testGetBoolean() throws Exception {
writeBooleanRecord(Metadata.PAUSE_AVAILABLE, true);
writeBooleanRecord(Metadata.SEEK_AVAILABLE, true);
writeBooleanRecord(Metadata.SEEK_BACKWARD_AVAILABLE, true);
writeBooleanRecord(Metadata.SEEK_FORWARD_AVAILABLE, true);
adjustSize();
assertParse();
assertEquals(true, mMetadata.getBoolean(Metadata.PAUSE_AVAILABLE));
assertEquals(true, mMetadata.getBoolean(Metadata.SEEK_AVAILABLE));
assertEquals(true, mMetadata.getBoolean(Metadata.SEEK_BACKWARD_AVAILABLE));
assertEquals(true, mMetadata.getBoolean(Metadata.SEEK_FORWARD_AVAILABLE));
}
// getLong
@SmallTest
public void testGetLong() throws Exception {
writeLongRecord(Metadata.DURATION, 1L);
adjustSize();
assertParse();
assertEquals(1L, mMetadata.getLong(Metadata.DURATION));
}
// getDouble
@SmallTest
public void testGetDouble() throws Exception {
writeDoubleRecord(Metadata.VIDEO_FRAME_RATE, 29.97);
adjustSize();
assertParse();
assertEquals(29.97, mMetadata.getDouble(Metadata.VIDEO_FRAME_RATE));
}
// getByteArray
@SmallTest
public void testGetByteArray() throws Exception {
byte data[] = new byte[]{1,2,3,4,5};
writeByteArrayRecord(Metadata.ALBUM_ART, data);
adjustSize();
assertParse();
byte res[] = mMetadata.getByteArray(Metadata.ALBUM_ART);
for (int i = 0; i < data.length; ++i) {
assertEquals(data[i], res[i]);
}
}
// getDate
@SmallTest
public void testGetDate() throws Exception {
writeDateRecord(Metadata.DATE, 0, "PST");
adjustSize();
assertParse();
assertEquals(new Date(0), mMetadata.getDate(Metadata.DATE));
}
// ----------------------------------------------------------------------
// HELPERS TO APPEND RECORDS
// ----------------------------------------------------------------------
// Insert a string record at the current position.
private void writeStringRecord(int metadataId, String val) {
final int start = mParcel.dataPosition();
mParcel.writeInt(-1); // Placeholder for the length
mParcel.writeInt(metadataId);
mParcel.writeInt(Metadata.STRING_VAL);
mParcel.writeString(val);
adjustSize(start);
}
// Insert an int record at the current position.
private void writeIntRecord(int metadataId, int val) {
final int start = mParcel.dataPosition();
mParcel.writeInt(-1); // Placeholder for the length
mParcel.writeInt(metadataId);
mParcel.writeInt(Metadata.INTEGER_VAL);
mParcel.writeInt(val);
adjustSize(start);
}
// Insert a boolean record at the current position.
private void writeBooleanRecord(int metadataId, boolean val) {
final int start = mParcel.dataPosition();
mParcel.writeInt(-1); // Placeholder for the length
mParcel.writeInt(metadataId);
mParcel.writeInt(Metadata.BOOLEAN_VAL);
mParcel.writeInt(val ? 1 : 0);
adjustSize(start);
}
// Insert a Long record at the current position.
private void writeLongRecord(int metadataId, long val) {
final int start = mParcel.dataPosition();
mParcel.writeInt(-1); // Placeholder for the length
mParcel.writeInt(metadataId);
mParcel.writeInt(Metadata.LONG_VAL);
mParcel.writeLong(val);
adjustSize(start);
}
// Insert a Double record at the current position.
private void writeDoubleRecord(int metadataId, double val) {
final int start = mParcel.dataPosition();
mParcel.writeInt(-1); // Placeholder for the length
mParcel.writeInt(metadataId);
mParcel.writeInt(Metadata.DOUBLE_VAL);
mParcel.writeDouble(val);
adjustSize(start);
}
// Insert a ByteArray record at the current position.
private void writeByteArrayRecord(int metadataId, byte[] val) {
final int start = mParcel.dataPosition();
mParcel.writeInt(-1); // Placeholder for the length
mParcel.writeInt(metadataId);
mParcel.writeInt(Metadata.BYTE_ARRAY_VAL);
mParcel.writeByteArray(val);
adjustSize(start);
}
// Insert a Date record at the current position.
private void writeDateRecord(int metadataId, long time, String tz) {
final int start = mParcel.dataPosition();
mParcel.writeInt(-1); // Placeholder for the length
mParcel.writeInt(metadataId);
mParcel.writeInt(Metadata.DATE_VAL);
mParcel.writeLong(time);
mParcel.writeString(tz);
adjustSize(start);
}
}