1 /*
2  * Copyright (C) 2009 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.mediaframeworktest.unit;
18 import android.media.Metadata;
19 import android.os.Parcel;
20 import android.test.AndroidTestCase;
21 import android.test.suitebuilder.annotation.SmallTest;
22 import android.util.Log;
23 
24 import java.util.Calendar;
25 import java.util.Date;
26 
27 /*
28  * Check the Java layer that parses serialized metadata in Parcel
29  * works as expected.
30  *
31  */
32 
33 public class MediaPlayerMetadataParserTest extends AndroidTestCase {
34     private static final String TAG = "MediaPlayerMetadataTest";
35     private static final int kMarker = 0x4d455441;  // 'M' 'E' 'T' 'A'
36     private static final int kHeaderSize = 8;
37 
38     private Metadata mMetadata = null;
39     private Parcel mParcel = null;
40 
41     @Override
setUp()42     protected void setUp() throws Exception {
43         super.setUp();
44         mMetadata = new Metadata();
45         mParcel = Parcel.obtain();
46 
47         resetParcel();
48     }
49 
50     // Check parsing of the parcel fails. Make sure the parser rewind
51     // the parcel properly.
assertParseFail()52     private void assertParseFail() throws Exception {
53         mParcel.setDataPosition(0);
54         assertFalse(mMetadata.parse(mParcel));
55         assertEquals(0, mParcel.dataPosition());
56     }
57 
58     // Check parsing of the parcel is successful.
assertParse()59     private void assertParse() throws Exception {
60         mParcel.setDataPosition(0);
61         assertTrue(mMetadata.parse(mParcel));
62     }
63 
64     // Write the number of bytes from the start of the parcel to the
65     // current position at the beginning of the parcel (offset 0).
adjustSize()66     private void adjustSize() {
67         adjustSize(0);
68     }
69 
70     // Write the number of bytes from the offset to the current
71     // position at position pointed by offset.
adjustSize(int offset)72     private void adjustSize(int offset) {
73         final int pos = mParcel.dataPosition();
74 
75         mParcel.setDataPosition(offset);
76         mParcel.writeInt(pos - offset);
77         mParcel.setDataPosition(pos);
78     }
79 
80     // Rewind the parcel and insert the header.
resetParcel()81     private void resetParcel() {
82         mParcel.setDataPosition(0);
83         // Most tests will use a properly formed parcel with a size
84         // and the meta marker so we add them by default.
85         mParcel.writeInt(-1);  // Placeholder for the size
86         mParcel.writeInt(kMarker);
87     }
88 
89     // ----------------------------------------------------------------------
90     // START OF THE TESTS
91 
92 
93     // There should be at least 8 bytes in the parcel, 4 for the size
94     // and 4 for the 'M' 'E' 'T' 'A' marker.
95     @SmallTest
testMissingSizeAndMarker()96     public void testMissingSizeAndMarker() throws Exception {
97         for (int i = 0; i < kHeaderSize; ++i) {
98             mParcel.setDataPosition(0);
99             mParcel.setDataSize(i);
100 
101             assertEquals(i, mParcel.dataAvail());
102             assertParseFail();
103         }
104     }
105 
106     // There should be at least 'size' bytes in the parcel.
107     @SmallTest
testMissingData()108     public void testMissingData() throws Exception {
109         final int size = 20;
110 
111         mParcel.writeInt(size);
112         mParcel.setDataSize(size - 1);
113         assertParseFail();
114     }
115 
116     // Empty parcel is fine
117     @SmallTest
testEmptyIsOk()118     public void testEmptyIsOk() throws Exception {
119         adjustSize();
120         assertParse();
121     }
122 
123     // ----------------------------------------------------------------------
124     // RECORDS
125     // ----------------------------------------------------------------------
126 
127     // A record header should be at least 12 bytes long
128     @SmallTest
testRecordMissingId()129     public void testRecordMissingId() throws Exception {
130         mParcel.writeInt(13); // record length
131         // misses metadata id and metadata type.
132         adjustSize();
133         assertParseFail();
134     }
135 
136     @SmallTest
testRecordMissingType()137     public void testRecordMissingType() throws Exception {
138         mParcel.writeInt(13); // record length lies
139         mParcel.writeInt(Metadata.TITLE);
140         // misses metadata type
141         adjustSize();
142         assertParseFail();
143     }
144 
145     @SmallTest
testRecordWithZeroPayload()146     public void testRecordWithZeroPayload() throws Exception {
147         mParcel.writeInt(0);
148         adjustSize();
149         assertParseFail();
150     }
151 
152     // A record cannot be empty.
153     @SmallTest
testRecordMissingPayload()154     public void testRecordMissingPayload() throws Exception {
155         mParcel.writeInt(12);
156         mParcel.writeInt(Metadata.TITLE);
157         mParcel.writeInt(Metadata.STRING_VAL);
158         // misses payload
159         adjustSize();
160         assertParseFail();
161     }
162 
163     // Check records can be found.
164     @SmallTest
testRecordsFound()165     public void testRecordsFound() throws Exception {
166         writeStringRecord(Metadata.TITLE, "a title");
167         writeStringRecord(Metadata.GENRE, "comedy");
168         writeStringRecord(Metadata.firstCustomId(), "custom");
169         adjustSize();
170         assertParse();
171         assertTrue(mMetadata.has(Metadata.TITLE));
172         assertTrue(mMetadata.has(Metadata.GENRE));
173         assertTrue(mMetadata.has(Metadata.firstCustomId()));
174         assertFalse(mMetadata.has(Metadata.DRM_CRIPPLED));
175         assertEquals(3, mMetadata.keySet().size());
176     }
177 
178     // Detects bad metadata type
179     @SmallTest
testBadMetadataType()180     public void testBadMetadataType() throws Exception {
181         final int start = mParcel.dataPosition();
182         mParcel.writeInt(-1);  // Placeholder for the length
183         mParcel.writeInt(Metadata.TITLE);
184         mParcel.writeInt(0);  // Invalid type.
185         mParcel.writeString("dummy");
186         adjustSize(start);
187 
188         adjustSize();
189         assertParseFail();
190     }
191 
192     // Check a Metadata instance can be reused, i.e the parse method
193     // wipes out the existing states/keys.
194     @SmallTest
testParseClearState()195     public void testParseClearState() throws Exception {
196         writeStringRecord(Metadata.TITLE, "a title");
197         writeStringRecord(Metadata.GENRE, "comedy");
198         writeStringRecord(Metadata.firstCustomId(), "custom");
199         adjustSize();
200         assertParse();
201 
202         resetParcel();
203         writeStringRecord(Metadata.MIME_TYPE, "audio/mpg");
204         adjustSize();
205         assertParse();
206 
207         // Only the mime type metadata should be present.
208         assertEquals(1, mMetadata.keySet().size());
209         assertTrue(mMetadata.has(Metadata.MIME_TYPE));
210 
211         assertFalse(mMetadata.has(Metadata.TITLE));
212         assertFalse(mMetadata.has(Metadata.GENRE));
213         assertFalse(mMetadata.has(Metadata.firstCustomId()));
214     }
215 
216     // ----------------------------------------------------------------------
217     // GETTERS
218     // ----------------------------------------------------------------------
219 
220     // getString
221     @SmallTest
testGetString()222     public void testGetString() throws Exception {
223         writeStringRecord(Metadata.TITLE, "a title");
224         writeStringRecord(Metadata.GENRE, "comedy");
225         adjustSize();
226         assertParse();
227 
228         assertEquals("a title", mMetadata.getString(Metadata.TITLE));
229         assertEquals("comedy", mMetadata.getString(Metadata.GENRE));
230     }
231 
232     // get an empty string.
233     @SmallTest
testGetEmptyString()234     public void testGetEmptyString() throws Exception {
235         writeStringRecord(Metadata.TITLE, "");
236         adjustSize();
237         assertParse();
238 
239         assertEquals("", mMetadata.getString(Metadata.TITLE));
240     }
241 
242     // get a string when a NULL value was in the parcel
243     @SmallTest
testGetNullString()244     public void testGetNullString() throws Exception {
245         writeStringRecord(Metadata.TITLE, null);
246         adjustSize();
247         assertParse();
248 
249         assertEquals(null, mMetadata.getString(Metadata.TITLE));
250     }
251 
252     // get a string when an integer is actually present
253     @SmallTest
testWrongType()254     public void testWrongType() throws Exception {
255         writeIntRecord(Metadata.DURATION, 5);
256         adjustSize();
257         assertParse();
258 
259         try {
260             mMetadata.getString(Metadata.DURATION);
261         } catch (IllegalStateException ise) {
262             return;
263         }
264         fail("Exception was not thrown");
265     }
266 
267     // getInt
268     @SmallTest
testGetInt()269     public void testGetInt() throws Exception {
270         writeIntRecord(Metadata.CD_TRACK_NUM, 1);
271         adjustSize();
272         assertParse();
273 
274         assertEquals(1, mMetadata.getInt(Metadata.CD_TRACK_NUM));
275     }
276 
277     // getBoolean
278     @SmallTest
testGetBoolean()279     public void testGetBoolean() throws Exception {
280         writeBooleanRecord(Metadata.PAUSE_AVAILABLE, true);
281         writeBooleanRecord(Metadata.SEEK_AVAILABLE, true);
282         writeBooleanRecord(Metadata.SEEK_BACKWARD_AVAILABLE, true);
283         writeBooleanRecord(Metadata.SEEK_FORWARD_AVAILABLE, true);
284         adjustSize();
285         assertParse();
286 
287         assertEquals(true, mMetadata.getBoolean(Metadata.PAUSE_AVAILABLE));
288         assertEquals(true, mMetadata.getBoolean(Metadata.SEEK_AVAILABLE));
289         assertEquals(true, mMetadata.getBoolean(Metadata.SEEK_BACKWARD_AVAILABLE));
290         assertEquals(true, mMetadata.getBoolean(Metadata.SEEK_FORWARD_AVAILABLE));
291     }
292 
293     // getLong
294     @SmallTest
testGetLong()295     public void testGetLong() throws Exception {
296         writeLongRecord(Metadata.DURATION, 1L);
297         adjustSize();
298         assertParse();
299 
300         assertEquals(1L, mMetadata.getLong(Metadata.DURATION));
301     }
302 
303     // getDouble
304     @SmallTest
testGetDouble()305     public void testGetDouble() throws Exception {
306         writeDoubleRecord(Metadata.VIDEO_FRAME_RATE, 29.97);
307         adjustSize();
308         assertParse();
309 
310         assertEquals(29.97, mMetadata.getDouble(Metadata.VIDEO_FRAME_RATE));
311     }
312 
313     // getByteArray
314     @SmallTest
testGetByteArray()315     public void testGetByteArray() throws Exception {
316         byte data[] = new byte[]{1,2,3,4,5};
317 
318         writeByteArrayRecord(Metadata.ALBUM_ART, data);
319         adjustSize();
320         assertParse();
321 
322         byte res[] = mMetadata.getByteArray(Metadata.ALBUM_ART);
323         for (int i = 0; i < data.length; ++i) {
324             assertEquals(data[i], res[i]);
325         }
326     }
327 
328     // getDate
329     @SmallTest
testGetDate()330     public void testGetDate() throws Exception {
331         writeDateRecord(Metadata.DATE, 0, "PST");
332         adjustSize();
333         assertParse();
334 
335         assertEquals(new Date(0), mMetadata.getDate(Metadata.DATE));
336     }
337 
338     // ----------------------------------------------------------------------
339     // HELPERS TO APPEND RECORDS
340     // ----------------------------------------------------------------------
341 
342     // Insert a string record at the current position.
writeStringRecord(int metadataId, String val)343     private void writeStringRecord(int metadataId, String val) {
344         final int start = mParcel.dataPosition();
345         mParcel.writeInt(-1);  // Placeholder for the length
346         mParcel.writeInt(metadataId);
347         mParcel.writeInt(Metadata.STRING_VAL);
348         mParcel.writeString(val);
349         adjustSize(start);
350     }
351 
352     // Insert an int record at the current position.
writeIntRecord(int metadataId, int val)353     private void writeIntRecord(int metadataId, int val) {
354         final int start = mParcel.dataPosition();
355         mParcel.writeInt(-1);  // Placeholder for the length
356         mParcel.writeInt(metadataId);
357         mParcel.writeInt(Metadata.INTEGER_VAL);
358         mParcel.writeInt(val);
359         adjustSize(start);
360     }
361 
362     // Insert a boolean record at the current position.
writeBooleanRecord(int metadataId, boolean val)363     private void writeBooleanRecord(int metadataId, boolean val) {
364         final int start = mParcel.dataPosition();
365         mParcel.writeInt(-1);  // Placeholder for the length
366         mParcel.writeInt(metadataId);
367         mParcel.writeInt(Metadata.BOOLEAN_VAL);
368         mParcel.writeInt(val ? 1 : 0);
369         adjustSize(start);
370     }
371 
372     // Insert a Long record at the current position.
writeLongRecord(int metadataId, long val)373     private void writeLongRecord(int metadataId, long val) {
374         final int start = mParcel.dataPosition();
375         mParcel.writeInt(-1);  // Placeholder for the length
376         mParcel.writeInt(metadataId);
377         mParcel.writeInt(Metadata.LONG_VAL);
378         mParcel.writeLong(val);
379         adjustSize(start);
380     }
381 
382     // Insert a Double record at the current position.
writeDoubleRecord(int metadataId, double val)383     private void writeDoubleRecord(int metadataId, double val) {
384         final int start = mParcel.dataPosition();
385         mParcel.writeInt(-1);  // Placeholder for the length
386         mParcel.writeInt(metadataId);
387         mParcel.writeInt(Metadata.DOUBLE_VAL);
388         mParcel.writeDouble(val);
389         adjustSize(start);
390     }
391 
392     // Insert a ByteArray record at the current position.
writeByteArrayRecord(int metadataId, byte[] val)393     private void writeByteArrayRecord(int metadataId, byte[] val) {
394         final int start = mParcel.dataPosition();
395         mParcel.writeInt(-1);  // Placeholder for the length
396         mParcel.writeInt(metadataId);
397         mParcel.writeInt(Metadata.BYTE_ARRAY_VAL);
398         mParcel.writeByteArray(val);
399         adjustSize(start);
400     }
401 
402     // Insert a Date record at the current position.
writeDateRecord(int metadataId, long time, String tz)403     private void writeDateRecord(int metadataId, long time, String tz) {
404         final int start = mParcel.dataPosition();
405         mParcel.writeInt(-1);  // Placeholder for the length
406         mParcel.writeInt(metadataId);
407         mParcel.writeInt(Metadata.DATE_VAL);
408         mParcel.writeLong(time);
409         mParcel.writeString(tz);
410         adjustSize(start);
411     }
412 }
413