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
17package com.android.mediaframeworktest.unit;
18import android.media.Metadata;
19import android.os.Parcel;
20import android.test.AndroidTestCase;
21import android.test.suitebuilder.annotation.SmallTest;
22import android.util.Log;
23
24import java.util.Calendar;
25import java.util.Date;
26
27/*
28 * Check the Java layer that parses serialized metadata in Parcel
29 * works as expected.
30 *
31 */
32
33public 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
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.
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.
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).
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.
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.
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
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
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
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
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
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
146    public void testRecordWithZeroPayload() throws Exception {
147        mParcel.writeInt(0);
148        adjustSize();
149        assertParseFail();
150    }
151
152    // A record cannot be empty.
153    @SmallTest
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
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
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
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
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
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
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
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
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
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
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
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
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
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.
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.
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.
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.
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.
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.
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.
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