MediaResourceGetterTest.java revision cedac228d2dd51db4b79ea1e72c7f249408ee061
1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5package org.chromium.content.browser;
6
7import android.content.Context;
8import android.content.pm.PackageManager;
9import android.media.MediaMetadataRetriever;
10import android.net.ConnectivityManager;
11import android.test.InstrumentationTestCase;
12import android.test.mock.MockContext;
13import android.test.suitebuilder.annotation.SmallTest;
14import android.util.SparseArray;
15
16import org.chromium.content.browser.MediaResourceGetter.MediaMetadata;
17
18import java.io.File;
19import java.util.Collections;
20import java.util.HashMap;
21import java.util.Map;
22
23/**
24 * Tests for MediaResourceGetter.
25 */
26public class MediaResourceGetterTest extends InstrumentationTestCase {
27    private static final String TEST_HTTP_URL = "http://example.com";
28    private static final String TEST_USER_AGENT = // Anyhting, really
29            "Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 " +
30            "(KHTML, like Gecko) Chrome/32.0.1667.0 Safari/537.36";
31    private static final String TEST_FILE_PATH = "/mnt/sdcard/test";
32    private static final String TEST_FILE_URL = "file://" + TEST_FILE_PATH;
33    private static final String TEST_COOKIES = "yum yum yum!";
34    private static final MediaMetadata sEmptyMetadata = new MediaMetadata(0, 0, 0, false);
35    private static final String sExternalStorageDirectory = "/test_external_storage";
36
37    private static final Map<String,String> sHeadersCookieOnly;
38    private static final Map<String,String> sHeadersCookieAndUA;
39    private static final Map<String,String> sHeadersUAOnly;
40    static {
41        Map<String,String> headers = new HashMap<String, String>();
42        headers.put("Cookie", TEST_COOKIES);
43        sHeadersCookieOnly = Collections.unmodifiableMap(headers);
44
45        headers = new HashMap<String, String>();
46        headers.put("User-Agent", TEST_USER_AGENT);
47        sHeadersUAOnly = Collections.unmodifiableMap(headers);
48
49        headers = new HashMap<String, String>();
50        headers.putAll(sHeadersCookieOnly);
51        headers.putAll(sHeadersUAOnly);
52        sHeadersCookieAndUA = Collections.unmodifiableMap(headers);
53    }
54
55    private static class FakeFile extends File {
56        final boolean mExists;
57
58        public FakeFile(String path, boolean exists) {
59            super(path);
60            mExists = exists;
61        }
62
63        @Override
64        public int hashCode() {
65            final int prime = 31;
66            int result = super.hashCode();
67            result = prime * result + (mExists ? 1231 : 1237);
68            return result;
69        }
70
71        @Override
72        public boolean equals(Object obj) {
73            if (this == obj)
74                return true;
75            if (!super.equals(obj))
76                return false;
77            if (getClass() != obj.getClass())
78                return false;
79            FakeFile other = (FakeFile) obj;
80            if (mExists != other.mExists)
81                return false;
82            return true;
83        }
84
85        @Override
86        public boolean exists() {
87            return mExists;
88        }
89    }
90
91    /**
92     * Helper class that fakes functionality we cannot perform without real
93     * media resources. The class being faked here has been architected
94     * carefully to allow most of the functionality to be tested. What remains
95     * here is overrides of trivial functionality.
96     */
97    private static class FakeMediaResourceGetter extends MediaResourceGetter {
98        // Read these back in tests to ensure proper values passed through
99        String mUri = null;
100        Map<String,String> mHeaders = null;
101        String mPath = null;
102
103        // Write these before tests to configure functionality
104        SparseArray<String> mMetadata = null;
105        Integer mNetworkType = null;
106        boolean mThrowExceptionInConfigure = false;
107        boolean mThrowExceptionInExtract = false;
108        boolean mFileExists = false;
109
110        // Can't use a real MediaMetadataRetriever as we have no media
111        @Override
112        public void configure(String uri, Map<String, String> headers) {
113            if (mThrowExceptionInConfigure) {
114                throw new RuntimeException("test exception");
115            }
116            mUri = uri;
117            mHeaders = headers;
118        }
119
120        // Can't use a real MediaMetadataRetriever as we have no media
121        @Override
122        public void configure(String path) {
123            if (mThrowExceptionInConfigure) {
124                throw new RuntimeException("test exception");
125            }
126            mPath = path;
127        }
128
129        // Can't use a real MediaMetadataRetriever as we have no media
130        @Override
131        public String extractMetadata(int key) {
132            if (mThrowExceptionInExtract) {
133                throw new RuntimeException("test exception");
134            }
135            if (mMetadata == null) {
136                return null;
137            }
138            return mMetadata.get(key);
139        }
140
141        // Can't use a real NetworkInfo object because there is no way to
142        // mock the ConnectivityManager in Android.
143        @Override
144        Integer getNetworkType(Context context) {
145            return mNetworkType;
146        }
147
148        // Can't use Environment.getExternalStorageDirectory because it could
149        // be inconsistent depending upon the state of the real or emulated
150        // device upon which we are testing.
151        @Override
152        String getExternalStorageDirectory() {
153            return sExternalStorageDirectory;
154        }
155
156        // Can't use regular File because we need to control the return from
157        // File.exists() on arbitrary paths.
158        @Override
159        File uriToFile(String path) {
160            FakeFile result = new FakeFile(path, mFileExists);
161            return result;
162        }
163
164        /**
165         * Convenience method to bind a metadata value to a key.
166         * @param key the key
167         * @param value the value
168         */
169        void bind(int key, String value) {
170            if (mMetadata == null) {
171                mMetadata = new SparseArray<String>();
172            }
173            mMetadata.put(key, value);
174        }
175
176    }
177
178    /**
179     * Helper class to control the result of permission checks.
180     */
181    private static class InternalMockContext extends MockContext {
182        boolean allowPermission = false;
183        @Override
184        public int checkCallingOrSelfPermission(String permission) {
185            assertEquals(android.Manifest.permission.ACCESS_NETWORK_STATE,
186                    permission);
187            return allowPermission ? PackageManager.PERMISSION_GRANTED :
188                PackageManager.PERMISSION_DENIED;
189        }
190    }
191
192    // Our test objects.
193    private FakeMediaResourceGetter mFakeMRG;
194    private InternalMockContext mMockContext;
195
196    @Override
197    protected void setUp() throws Exception {
198        super.setUp();
199        mFakeMRG = new FakeMediaResourceGetter();
200        mMockContext = new InternalMockContext();
201    }
202
203    @SmallTest
204    public void testMediaMetadataEquals() {
205        assertEquals(sEmptyMetadata, sEmptyMetadata);
206        assertEquals(sEmptyMetadata, new MediaMetadata(0, 0, 0, false));
207        assertFalse(sEmptyMetadata.equals(null));
208        assertFalse(sEmptyMetadata.equals("test"));
209        assertFalse(sEmptyMetadata.equals(new MediaMetadata(1, 0, 0, false)));
210        assertFalse(sEmptyMetadata.equals(new MediaMetadata(0, 1, 0, false)));
211        assertFalse(sEmptyMetadata.equals(new MediaMetadata(0, 0, 1, false)));
212        assertFalse(sEmptyMetadata.equals(new MediaMetadata(0, 0, 0, true)));
213    }
214
215    @SmallTest
216    public void testMediaMetadataHashCode() {
217        assertEquals(sEmptyMetadata.hashCode(), sEmptyMetadata.hashCode());
218        assertEquals(sEmptyMetadata.hashCode(), new MediaMetadata(0, 0, 0, false).hashCode());
219        assertFalse(sEmptyMetadata.hashCode() == new MediaMetadata(1, 0, 0, false).hashCode());
220        assertFalse(sEmptyMetadata.hashCode() == new MediaMetadata(0, 1, 0, false).hashCode());
221        assertFalse(sEmptyMetadata.hashCode() == new MediaMetadata(0, 0, 1, false).hashCode());
222        assertFalse(sEmptyMetadata.hashCode() == new MediaMetadata(0, 0, 0, true).hashCode());
223    }
224
225    @SmallTest
226    public void testMediaMetadataGetters() {
227        MediaMetadata data = new MediaMetadata(1, 2, 3, true);
228        assertEquals(1, data.getDurationInMilliseconds());
229        assertEquals(2, data.getWidth());
230        assertEquals(3, data.getHeight());
231        assertTrue(data.isSuccess());
232
233        // Validate no overlap of test values with defaults
234        data = new MediaMetadata(4, 5, 6, false);
235        assertEquals(4, data.getDurationInMilliseconds());
236        assertEquals(5, data.getWidth());
237        assertEquals(6, data.getHeight());
238        assertFalse(data.isSuccess());
239    }
240
241    @SmallTest
242    public void testConfigure_Net_NoPermissions() {
243        mMockContext.allowPermission = false;
244        assertFalse(mFakeMRG.configure(mMockContext, TEST_HTTP_URL,
245                                       TEST_COOKIES, TEST_USER_AGENT));
246    }
247
248    @SmallTest
249    public void testConfigure_Net_NoActiveNetwork() {
250        mMockContext.allowPermission = true;
251        mFakeMRG.mNetworkType = null;
252        assertFalse(mFakeMRG.configure(mMockContext, TEST_HTTP_URL,
253                                       TEST_COOKIES, TEST_USER_AGENT));
254    }
255
256    @SmallTest
257    public void testConfigure_Net_Disallowed_Mobile() {
258        mMockContext.allowPermission = true;
259        mFakeMRG.mNetworkType = ConnectivityManager.TYPE_MOBILE;
260        assertFalse(mFakeMRG.configure(mMockContext, TEST_HTTP_URL,
261                                       TEST_COOKIES, TEST_USER_AGENT));
262    }
263
264    @SmallTest
265    public void testConfigure_Net_Disallowed_Wimax() {
266        mMockContext.allowPermission = true;
267        mFakeMRG.mNetworkType = ConnectivityManager.TYPE_WIMAX;
268        assertFalse(mFakeMRG.configure(mMockContext, TEST_HTTP_URL,
269                                       TEST_COOKIES, TEST_USER_AGENT));
270    }
271
272    @SmallTest
273    public void testConfigure_Net_Allowed_Ethernet_Cookies_NoUA() {
274        mMockContext.allowPermission = true;
275        mFakeMRG.mNetworkType = ConnectivityManager.TYPE_ETHERNET;
276        assertTrue(mFakeMRG.configure(mMockContext, TEST_HTTP_URL,
277                                      TEST_COOKIES, null));
278        assertEquals(TEST_HTTP_URL, mFakeMRG.mUri);
279        assertEquals(sHeadersCookieOnly, mFakeMRG.mHeaders);
280        assertNull(mFakeMRG.mPath);
281    }
282
283    @SmallTest
284    public void testConfigure_Net_Allowed_Wifi_Cookies_NoUA() {
285        mMockContext.allowPermission = true;
286        mFakeMRG.mNetworkType = ConnectivityManager.TYPE_WIFI;
287        assertTrue(mFakeMRG.configure(mMockContext, TEST_HTTP_URL,
288                                      TEST_COOKIES, null));
289        assertEquals(TEST_HTTP_URL, mFakeMRG.mUri);
290        assertEquals(sHeadersCookieOnly, mFakeMRG.mHeaders);
291        assertNull(mFakeMRG.mPath);
292    }
293
294    @SmallTest
295    public void testConfigure_Net_Allowed_Ethernet_NoCookies_NoUA() {
296        mMockContext.allowPermission = true;
297        mFakeMRG.mNetworkType = ConnectivityManager.TYPE_ETHERNET;
298        assertTrue(mFakeMRG.configure(mMockContext, TEST_HTTP_URL,
299                                      "", null));
300        assertEquals(TEST_HTTP_URL, mFakeMRG.mUri);
301        assertEquals(Collections.emptyMap(), mFakeMRG.mHeaders);
302        assertNull(mFakeMRG.mPath);
303    }
304
305    @SmallTest
306    public void testConfigure_Net_Allowed_Ethernet_Cookies_WithUA() {
307        mMockContext.allowPermission = true;
308        mFakeMRG.mNetworkType = ConnectivityManager.TYPE_ETHERNET;
309        assertTrue(mFakeMRG.configure(mMockContext, TEST_HTTP_URL,
310                                      TEST_COOKIES, TEST_USER_AGENT));
311        assertEquals(TEST_HTTP_URL, mFakeMRG.mUri);
312        assertEquals(sHeadersCookieAndUA, mFakeMRG.mHeaders);
313        assertNull(mFakeMRG.mPath);
314    }
315
316    @SmallTest
317    public void testConfigure_Net_Allowed_Ethernet_NoCookies_WithUA() {
318        mMockContext.allowPermission = true;
319        mFakeMRG.mNetworkType = ConnectivityManager.TYPE_ETHERNET;
320        assertTrue(mFakeMRG.configure(mMockContext, TEST_HTTP_URL,
321                                      "", TEST_USER_AGENT));
322        assertEquals(TEST_HTTP_URL, mFakeMRG.mUri);
323        assertEquals(sHeadersUAOnly, mFakeMRG.mHeaders);
324        assertNull(mFakeMRG.mPath);
325    }
326
327    @SmallTest
328    public void testConfigure_Net_Allowed_Ethernet_Exception() {
329        mMockContext.allowPermission = true;
330        mFakeMRG.mThrowExceptionInConfigure = true;
331        mFakeMRG.mNetworkType = ConnectivityManager.TYPE_ETHERNET;
332        assertFalse(mFakeMRG.configure(mMockContext, TEST_HTTP_URL,
333                                       "", TEST_USER_AGENT));
334        assertNull(mFakeMRG.mUri);
335        assertNull(mFakeMRG.mHeaders);
336    }
337
338    @SmallTest
339    public void testConfigure_Net_Allowed_LocalHost_WithNoNetwork() {
340        String[] localHostUrls = {
341            "http://LocalHost",
342            "https://127.0.0.1/",
343            "http://[::1]:8888/",
344        };
345        mMockContext.allowPermission = true;
346        mFakeMRG.mNetworkType = null;
347        for (String localHostUrl : localHostUrls) {
348            assertTrue(mFakeMRG.configure(mMockContext, localHostUrl,
349                                          TEST_COOKIES, TEST_USER_AGENT));
350            assertEquals(localHostUrl, mFakeMRG.mUri);
351            assertEquals(sHeadersCookieAndUA, mFakeMRG.mHeaders);
352            assertNull(mFakeMRG.mPath);
353        }
354    }
355
356    @SmallTest
357    public void testConfigure_File_Allowed_MntSdcard() {
358        final String path = "/mnt/sdcard/test";
359        final String url = "file://" + path;
360        mFakeMRG.mFileExists = true;
361        assertTrue(mFakeMRG.configure(mMockContext, url, "", null));
362        assertEquals(path, mFakeMRG.mPath);
363        assertNull(mFakeMRG.mUri);
364        assertNull(mFakeMRG.mHeaders);
365    }
366
367    @SmallTest
368    public void testConfigure_File_Allowed_Sdcard() {
369        final String path = "/sdcard/test";
370        final String url = "file://" + path;
371        mFakeMRG.mFileExists = true;
372        assertTrue(mFakeMRG.configure(mMockContext, url, "", null));
373        assertEquals(path, mFakeMRG.mPath);
374        assertNull(mFakeMRG.mUri);
375        assertNull(mFakeMRG.mHeaders);
376    }
377
378    @SmallTest
379    public void testConfigure_File_Allowed_Sdcard_DoesntExist() {
380        final String path = "/sdcard/test";
381        final String url = "file://" + path;
382        mFakeMRG.mFileExists = false;
383        assertFalse(mFakeMRG.configure(mMockContext, url, "", null));
384        assertNull(mFakeMRG.mPath);
385    }
386
387    @SmallTest
388    public void testConfigure_File_Allowed_ExternalStorage() {
389        final String path = sExternalStorageDirectory + "/test";
390        final String url = "file://" + path;
391        mFakeMRG.mFileExists = true;
392        assertTrue(mFakeMRG.configure(mMockContext, url, "", null));
393        assertEquals(path, mFakeMRG.mPath);
394        assertNull(mFakeMRG.mUri);
395        assertNull(mFakeMRG.mHeaders);
396    }
397
398    @SmallTest
399    public void testConfigure_File_Disallowed_Innocent() {
400        final String path = "/malicious/path";
401        final String url = "file://" + path;
402        mFakeMRG.mFileExists = true;
403        assertFalse(mFakeMRG.configure(mMockContext, url, "", null));
404        assertNull(mFakeMRG.mPath);
405    }
406
407    @SmallTest
408    public void testConfigure_File_Disallowed_Malicious() {
409        final String path = "/mnt/sdcard/../../data";
410        final String url = "file://" + path;
411        mFakeMRG.mFileExists = true;
412        assertFalse(mFakeMRG.configure(mMockContext, url, "", null));
413        assertNull(mFakeMRG.mPath);
414    }
415
416    @SmallTest
417    public void testConfigure_File_Allowed_Exception() {
418        final String path = "/mnt/sdcard/test";
419        final String url = "file://" + path;
420        mFakeMRG.mFileExists = true;
421        mFakeMRG.mThrowExceptionInConfigure = true;
422        assertFalse(mFakeMRG.configure(mMockContext, url, "", null));
423        assertNull(mFakeMRG.mPath);
424    }
425
426    @SmallTest
427    public void testExtract_NoMetadata() {
428        mFakeMRG.mFileExists = true;
429        assertEquals(sEmptyMetadata, mFakeMRG.extract(
430                mMockContext, TEST_FILE_URL, null, null));
431        assertEquals("configured successfully when we shouldn't have",
432                     TEST_FILE_PATH, mFakeMRG.mPath); // tricky
433    }
434
435    @SmallTest
436    public void testExtract_WithMetadata_ValidDuration() {
437        mFakeMRG.mFileExists = true;
438        mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "1");
439        final MediaMetadata expected = new MediaMetadata(1, 0, 0, true);
440        assertEquals(expected, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null));
441    }
442
443    @SmallTest
444    public void testExtract_WithMetadata_InvalidDuration() {
445        mFakeMRG.mFileExists = true;
446        mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "i am not an integer");
447        assertEquals(sEmptyMetadata, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null));
448    }
449
450    @SmallTest
451    public void testExtract_WithMetadata_ValidDuration_HasVideo_NoWidth_NoHeight() {
452        mFakeMRG.mFileExists = true;
453        mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "1");
454        mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO, "yes");
455        assertEquals(sEmptyMetadata, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null));
456    }
457
458    @SmallTest
459    public void testExtract_WithMetadata_ValidDuration_HasVideo_ValidWidth_NoHeight() {
460        mFakeMRG.mFileExists = true;
461        mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "1");
462        mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO, "yes");
463        mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH, "1");
464        assertEquals(sEmptyMetadata, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null));
465    }
466
467    @SmallTest
468    public void testExtract_WithMetadata_ValidDuration_HasVideo_InvalidWidth_NoHeight() {
469        mFakeMRG.mFileExists = true;
470        mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "1");
471        mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO, "yes");
472        mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH, "i am not an integer");
473        assertEquals(sEmptyMetadata, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null));
474    }
475
476    @SmallTest
477    public void testExtract_WithMetadata_ValidDuration_HasVideo_ValidWidth_ValidHeight() {
478        mFakeMRG.mFileExists = true;
479        mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "1");
480        mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO, "yes");
481        mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH, "2");
482        mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT, "3");
483        final MediaMetadata expected = new MediaMetadata(1, 2, 3, true);
484        assertEquals(expected, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null));
485    }
486
487    @SmallTest
488    public void testExtract_WithMetadata_ValidDuration_HasVideo_ValidWidth_InvalidHeight() {
489        mFakeMRG.mFileExists = true;
490        mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "1");
491        mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO, "yes");
492        mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH, "1");
493        mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT, "i am not an integer");
494        assertEquals(sEmptyMetadata, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null));
495    }
496
497    @SmallTest
498    public void testExtract_WithMetadata_ValidDuration_ExceptionInExtract() {
499        mFakeMRG.mFileExists = true;
500        mFakeMRG.mThrowExceptionInExtract = true;
501        mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "1");
502        assertEquals(sEmptyMetadata, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null));
503    }
504
505    @SmallTest
506    public void testAndroidDeviceOk_BadModel_BadVersion() {
507        assertFalse(MediaResourceGetter.androidDeviceOk(
508                "GT-I9100", android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1));
509    }
510
511    @SmallTest
512    public void testAndroidDeviceOk_BadModel_GoodVersion() {
513        assertTrue(MediaResourceGetter.androidDeviceOk(
514                "GT-I9100", android.os.Build.VERSION_CODES.JELLY_BEAN));
515    }
516
517    @SmallTest
518    public void testAndroidDeviceOk_GoodModel_AnyVersion() {
519        assertTrue(MediaResourceGetter.androidDeviceOk(
520                "Happy Device", android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH));
521    }
522}
523