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.
5 package org.chromium.content.browser;
7 import android.annotation.SuppressLint;
8 import android.content.Context;
9 import android.content.pm.PackageManager;
10 import android.media.MediaMetadataRetriever;
11 import android.net.ConnectivityManager;
12 import android.test.InstrumentationTestCase;
13 import android.test.mock.MockContext;
14 import android.test.suitebuilder.annotation.SmallTest;
15 import android.util.SparseArray;
17 import org.chromium.content.browser.MediaResourceGetter.MediaMetadata;
20 import java.util.Collections;
21 import java.util.HashMap;
25 * Tests for MediaResourceGetter.
27 @SuppressLint("SdCardPath")
28 public class MediaResourceGetterTest extends InstrumentationTestCase {
29 private static final String TEST_HTTP_URL = "http://example.com";
30 private static final String TEST_USER_AGENT = // Anything, really
31 "Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 " +
32 "(KHTML, like Gecko) Chrome/32.0.1667.0 Safari/537.36";
33 private static final String TEST_FILE_PATH = "/mnt/sdcard/test";
34 private static final String TEST_FILE_URL = "file://" + TEST_FILE_PATH;
35 private static final String TEST_COOKIES = "yum yum yum!";
36 private static final MediaMetadata sEmptyMetadata = new MediaMetadata(0, 0, 0, false);
37 private static final String sExternalStorageDirectory = "/test_external_storage";
39 private static final Map<String,String> sHeadersCookieOnly;
40 private static final Map<String,String> sHeadersCookieAndUA;
41 private static final Map<String,String> sHeadersUAOnly;
43 Map<String,String> headers = new HashMap<String, String>();
44 headers.put("Cookie", TEST_COOKIES);
45 sHeadersCookieOnly = Collections.unmodifiableMap(headers);
47 headers = new HashMap<String, String>();
48 headers.put("User-Agent", TEST_USER_AGENT);
49 sHeadersUAOnly = Collections.unmodifiableMap(headers);
51 headers = new HashMap<String, String>();
52 headers.putAll(sHeadersCookieOnly);
53 headers.putAll(sHeadersUAOnly);
54 sHeadersCookieAndUA = Collections.unmodifiableMap(headers);
57 private static class FakeFile extends File {
58 final boolean mExists;
60 public FakeFile(String path, boolean exists) {
66 public int hashCode() {
68 int result = super.hashCode();
69 result = prime * result + (mExists ? 1231 : 1237);
74 public boolean equals(Object obj) {
77 if (!super.equals(obj))
79 if (getClass() != obj.getClass())
81 FakeFile other = (FakeFile) obj;
82 if (mExists != other.mExists)
88 public boolean exists() {
94 * Helper class that fakes functionality we cannot perform without real
95 * media resources. The class being faked here has been architected
96 * carefully to allow most of the functionality to be tested. What remains
97 * here is overrides of trivial functionality.
99 private static class FakeMediaResourceGetter extends MediaResourceGetter {
100 // Read these back in tests to ensure proper values passed through
102 Map<String,String> mHeaders = null;
108 // Write these before tests to configure functionality
109 SparseArray<String> mMetadata = null;
110 Integer mNetworkType = null;
111 boolean mThrowExceptionInConfigure = false;
112 boolean mThrowExceptionInExtract = false;
113 boolean mFileExists = false;
115 // Can't use a real MediaMetadataRetriever as we have no media
117 public void configure(int fd, long offset, long length) {
118 if (mThrowExceptionInConfigure) {
119 throw new RuntimeException("test exception");
126 // Can't use a real MediaMetadataRetriever as we have no media
128 public void configure(String uri, Map<String, String> headers) {
129 if (mThrowExceptionInConfigure) {
130 throw new RuntimeException("test exception");
136 // Can't use a real MediaMetadataRetriever as we have no media
138 public void configure(String path) {
139 if (mThrowExceptionInConfigure) {
140 throw new RuntimeException("test exception");
145 // Can't use a real MediaMetadataRetriever as we have no media
147 public String extractMetadata(int key) {
148 if (mThrowExceptionInExtract) {
149 throw new RuntimeException("test exception");
151 if (mMetadata == null) {
154 return mMetadata.get(key);
157 // Can't use a real NetworkInfo object because there is no way to
158 // mock the ConnectivityManager in Android.
160 Integer getNetworkType(Context context) {
164 // Can't use Environment.getExternalStorageDirectory because it could
165 // be inconsistent depending upon the state of the real or emulated
166 // device upon which we are testing.
168 String getExternalStorageDirectory() {
169 return sExternalStorageDirectory;
172 // Can't use regular File because we need to control the return from
173 // File.exists() on arbitrary paths.
175 File uriToFile(String path) {
176 FakeFile result = new FakeFile(path, mFileExists);
181 * Convenience method to bind a metadata value to a key.
183 * @param value the value
185 void bind(int key, String value) {
186 if (mMetadata == null) {
187 mMetadata = new SparseArray<String>();
189 mMetadata.put(key, value);
195 * Helper class to control the result of permission checks.
197 private static class InternalMockContext extends MockContext {
198 boolean allowPermission = false;
200 public int checkCallingOrSelfPermission(String permission) {
201 assertEquals(android.Manifest.permission.ACCESS_NETWORK_STATE,
203 return allowPermission ? PackageManager.PERMISSION_GRANTED :
204 PackageManager.PERMISSION_DENIED;
209 private FakeMediaResourceGetter mFakeMRG;
210 private InternalMockContext mMockContext;
213 protected void setUp() throws Exception {
215 mFakeMRG = new FakeMediaResourceGetter();
216 mMockContext = new InternalMockContext();
220 public void testMediaMetadataEquals() {
221 assertEquals(sEmptyMetadata, sEmptyMetadata);
222 assertEquals(sEmptyMetadata, new MediaMetadata(0, 0, 0, false));
223 assertFalse(sEmptyMetadata.equals(null));
224 assertFalse(sEmptyMetadata.equals("test"));
225 assertFalse(sEmptyMetadata.equals(new MediaMetadata(1, 0, 0, false)));
226 assertFalse(sEmptyMetadata.equals(new MediaMetadata(0, 1, 0, false)));
227 assertFalse(sEmptyMetadata.equals(new MediaMetadata(0, 0, 1, false)));
228 assertFalse(sEmptyMetadata.equals(new MediaMetadata(0, 0, 0, true)));
232 public void testMediaMetadataHashCode() {
233 assertEquals(sEmptyMetadata.hashCode(), sEmptyMetadata.hashCode());
234 assertEquals(sEmptyMetadata.hashCode(), new MediaMetadata(0, 0, 0, false).hashCode());
235 assertFalse(sEmptyMetadata.hashCode() == new MediaMetadata(1, 0, 0, false).hashCode());
236 assertFalse(sEmptyMetadata.hashCode() == new MediaMetadata(0, 1, 0, false).hashCode());
237 assertFalse(sEmptyMetadata.hashCode() == new MediaMetadata(0, 0, 1, false).hashCode());
238 assertFalse(sEmptyMetadata.hashCode() == new MediaMetadata(0, 0, 0, true).hashCode());
242 public void testMediaMetadataGetters() {
243 MediaMetadata data = new MediaMetadata(1, 2, 3, true);
244 assertEquals(1, data.getDurationInMilliseconds());
245 assertEquals(2, data.getWidth());
246 assertEquals(3, data.getHeight());
247 assertTrue(data.isSuccess());
249 // Validate no overlap of test values with defaults
250 data = new MediaMetadata(4, 5, 6, false);
251 assertEquals(4, data.getDurationInMilliseconds());
252 assertEquals(5, data.getWidth());
253 assertEquals(6, data.getHeight());
254 assertFalse(data.isSuccess());
258 public void testConfigure_Net_NoPermissions() {
259 mMockContext.allowPermission = false;
260 assertFalse(mFakeMRG.configure(mMockContext, TEST_HTTP_URL,
261 TEST_COOKIES, TEST_USER_AGENT));
265 public void testConfigure_Net_NoActiveNetwork() {
266 mMockContext.allowPermission = true;
267 mFakeMRG.mNetworkType = null;
268 assertFalse(mFakeMRG.configure(mMockContext, TEST_HTTP_URL,
269 TEST_COOKIES, TEST_USER_AGENT));
273 public void testConfigure_Net_Disallowed_Mobile() {
274 mMockContext.allowPermission = true;
275 mFakeMRG.mNetworkType = ConnectivityManager.TYPE_MOBILE;
276 assertFalse(mFakeMRG.configure(mMockContext, TEST_HTTP_URL,
277 TEST_COOKIES, TEST_USER_AGENT));
281 public void testConfigure_Net_Disallowed_Wimax() {
282 mMockContext.allowPermission = true;
283 mFakeMRG.mNetworkType = ConnectivityManager.TYPE_WIMAX;
284 assertFalse(mFakeMRG.configure(mMockContext, TEST_HTTP_URL,
285 TEST_COOKIES, TEST_USER_AGENT));
289 public void testConfigure_Net_Allowed_Ethernet_Cookies_NoUA() {
290 mMockContext.allowPermission = true;
291 mFakeMRG.mNetworkType = ConnectivityManager.TYPE_ETHERNET;
292 assertTrue(mFakeMRG.configure(mMockContext, TEST_HTTP_URL,
293 TEST_COOKIES, null));
294 assertEquals(TEST_HTTP_URL, mFakeMRG.mUri);
295 assertEquals(sHeadersCookieOnly, mFakeMRG.mHeaders);
296 assertNull(mFakeMRG.mPath);
300 public void testConfigure_Net_Allowed_Wifi_Cookies_NoUA() {
301 mMockContext.allowPermission = true;
302 mFakeMRG.mNetworkType = ConnectivityManager.TYPE_WIFI;
303 assertTrue(mFakeMRG.configure(mMockContext, TEST_HTTP_URL,
304 TEST_COOKIES, null));
305 assertEquals(TEST_HTTP_URL, mFakeMRG.mUri);
306 assertEquals(sHeadersCookieOnly, mFakeMRG.mHeaders);
307 assertNull(mFakeMRG.mPath);
311 public void testConfigure_Net_Allowed_Ethernet_NoCookies_NoUA() {
312 mMockContext.allowPermission = true;
313 mFakeMRG.mNetworkType = ConnectivityManager.TYPE_ETHERNET;
314 assertTrue(mFakeMRG.configure(mMockContext, TEST_HTTP_URL,
316 assertEquals(TEST_HTTP_URL, mFakeMRG.mUri);
317 assertEquals(Collections.emptyMap(), mFakeMRG.mHeaders);
318 assertNull(mFakeMRG.mPath);
322 public void testConfigure_Net_Allowed_Ethernet_Cookies_WithUA() {
323 mMockContext.allowPermission = true;
324 mFakeMRG.mNetworkType = ConnectivityManager.TYPE_ETHERNET;
325 assertTrue(mFakeMRG.configure(mMockContext, TEST_HTTP_URL,
326 TEST_COOKIES, TEST_USER_AGENT));
327 assertEquals(TEST_HTTP_URL, mFakeMRG.mUri);
328 assertEquals(sHeadersCookieAndUA, mFakeMRG.mHeaders);
329 assertNull(mFakeMRG.mPath);
333 public void testConfigure_Net_Allowed_Ethernet_NoCookies_WithUA() {
334 mMockContext.allowPermission = true;
335 mFakeMRG.mNetworkType = ConnectivityManager.TYPE_ETHERNET;
336 assertTrue(mFakeMRG.configure(mMockContext, TEST_HTTP_URL,
337 "", TEST_USER_AGENT));
338 assertEquals(TEST_HTTP_URL, mFakeMRG.mUri);
339 assertEquals(sHeadersUAOnly, mFakeMRG.mHeaders);
340 assertNull(mFakeMRG.mPath);
344 public void testConfigure_Net_Allowed_Ethernet_Exception() {
345 mMockContext.allowPermission = true;
346 mFakeMRG.mThrowExceptionInConfigure = true;
347 mFakeMRG.mNetworkType = ConnectivityManager.TYPE_ETHERNET;
348 assertFalse(mFakeMRG.configure(mMockContext, TEST_HTTP_URL,
349 "", TEST_USER_AGENT));
350 assertNull(mFakeMRG.mUri);
351 assertNull(mFakeMRG.mHeaders);
355 public void testConfigure_Net_Allowed_LocalHost_WithNoNetwork() {
356 String[] localHostUrls = {
358 "https://127.0.0.1/",
359 "http://[::1]:8888/",
361 mMockContext.allowPermission = true;
362 mFakeMRG.mNetworkType = null;
363 for (String localHostUrl : localHostUrls) {
364 assertTrue(mFakeMRG.configure(mMockContext, localHostUrl,
365 TEST_COOKIES, TEST_USER_AGENT));
366 assertEquals(localHostUrl, mFakeMRG.mUri);
367 assertEquals(sHeadersCookieAndUA, mFakeMRG.mHeaders);
368 assertNull(mFakeMRG.mPath);
373 public void testConfigure_File_Allowed_MntSdcard() {
374 final String path = "/mnt/sdcard/test";
375 final String url = "file://" + path;
376 mFakeMRG.mFileExists = true;
377 assertTrue(mFakeMRG.configure(mMockContext, url, "", null));
378 assertEquals(path, mFakeMRG.mPath);
379 assertNull(mFakeMRG.mUri);
380 assertNull(mFakeMRG.mHeaders);
384 public void testConfigure_File_Allowed_Sdcard() {
385 final String path = "/sdcard/test";
386 final String url = "file://" + path;
387 mFakeMRG.mFileExists = true;
388 assertTrue(mFakeMRG.configure(mMockContext, url, "", null));
389 assertEquals(path, mFakeMRG.mPath);
390 assertNull(mFakeMRG.mUri);
391 assertNull(mFakeMRG.mHeaders);
395 public void testConfigure_File_Allowed_Sdcard_DoesntExist() {
396 final String path = "/sdcard/test";
397 final String url = "file://" + path;
398 mFakeMRG.mFileExists = false;
399 assertFalse(mFakeMRG.configure(mMockContext, url, "", null));
400 assertNull(mFakeMRG.mPath);
404 public void testConfigure_File_Allowed_ExternalStorage() {
405 final String path = sExternalStorageDirectory + "/test";
406 final String url = "file://" + path;
407 mFakeMRG.mFileExists = true;
408 assertTrue(mFakeMRG.configure(mMockContext, url, "", null));
409 assertEquals(path, mFakeMRG.mPath);
410 assertNull(mFakeMRG.mUri);
411 assertNull(mFakeMRG.mHeaders);
415 public void testConfigure_File_Disallowed_Innocent() {
416 final String path = "/malicious/path";
417 final String url = "file://" + path;
418 mFakeMRG.mFileExists = true;
419 assertFalse(mFakeMRG.configure(mMockContext, url, "", null));
420 assertNull(mFakeMRG.mPath);
424 public void testConfigure_File_Disallowed_Malicious() {
425 final String path = "/mnt/sdcard/../../data";
426 final String url = "file://" + path;
427 mFakeMRG.mFileExists = true;
428 assertFalse(mFakeMRG.configure(mMockContext, url, "", null));
429 assertNull(mFakeMRG.mPath);
433 public void testConfigure_File_Allowed_Exception() {
434 final String path = "/mnt/sdcard/test";
435 final String url = "file://" + path;
436 mFakeMRG.mFileExists = true;
437 mFakeMRG.mThrowExceptionInConfigure = true;
438 assertFalse(mFakeMRG.configure(mMockContext, url, "", null));
439 assertNull(mFakeMRG.mPath);
443 public void testExtract_NoMetadata() {
444 mFakeMRG.mFileExists = true;
445 assertEquals(sEmptyMetadata, mFakeMRG.extract(
446 mMockContext, TEST_FILE_URL, null, null));
447 assertEquals("configured successfully when we shouldn't have",
448 TEST_FILE_PATH, mFakeMRG.mPath); // tricky
452 public void testExtract_WithMetadata_ValidDuration() {
453 mFakeMRG.mFileExists = true;
454 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "1");
455 final MediaMetadata expected = new MediaMetadata(1, 0, 0, true);
456 assertEquals(expected, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null));
460 public void testExtract_WithMetadata_InvalidDuration() {
461 mFakeMRG.mFileExists = true;
462 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "i am not an integer");
463 assertEquals(sEmptyMetadata, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null));
467 public void testExtract_WithMetadata_ValidDuration_HasVideo_NoWidth_NoHeight() {
468 mFakeMRG.mFileExists = true;
469 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "1");
470 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO, "yes");
471 assertEquals(sEmptyMetadata, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null));
475 public void testExtract_WithMetadata_ValidDuration_HasVideo_ValidWidth_NoHeight() {
476 mFakeMRG.mFileExists = true;
477 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "1");
478 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO, "yes");
479 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH, "1");
480 assertEquals(sEmptyMetadata, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null));
484 public void testExtract_WithMetadata_ValidDuration_HasVideo_InvalidWidth_NoHeight() {
485 mFakeMRG.mFileExists = true;
486 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "1");
487 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO, "yes");
488 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH, "i am not an integer");
489 assertEquals(sEmptyMetadata, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null));
493 public void testExtract_WithMetadata_ValidDuration_HasVideo_ValidWidth_ValidHeight() {
494 mFakeMRG.mFileExists = true;
495 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "1");
496 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO, "yes");
497 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH, "2");
498 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT, "3");
499 final MediaMetadata expected = new MediaMetadata(1, 2, 3, true);
500 assertEquals(expected, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null));
504 public void testExtract_WithMetadata_ValidDuration_HasVideo_ValidWidth_InvalidHeight() {
505 mFakeMRG.mFileExists = true;
506 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "1");
507 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO, "yes");
508 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH, "1");
509 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT, "i am not an integer");
510 assertEquals(sEmptyMetadata, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null));
514 public void testExtract_WithMetadata_ValidDuration_ExceptionInExtract() {
515 mFakeMRG.mFileExists = true;
516 mFakeMRG.mThrowExceptionInExtract = true;
517 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "1");
518 assertEquals(sEmptyMetadata, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null));
522 public void testExtractFromFileDescriptor_ValidMetadata() {
523 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "1");
524 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO, "yes");
525 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH, "2");
526 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT, "3");
527 final MediaMetadata expected = new MediaMetadata(1, 2, 3, true);
531 assertEquals(expected, mFakeMRG.extract(fd, offset, length));
532 assertEquals(fd, mFakeMRG.mFd);
533 assertEquals(offset, mFakeMRG.mOffset);
534 assertEquals(length, mFakeMRG.mLength);
538 public void testAndroidDeviceOk_BadModel_BadVersion() {
539 assertFalse(MediaResourceGetter.androidDeviceOk(
540 "GT-I9100", android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1));
544 public void testAndroidDeviceOk_BadModel_GoodVersion() {
545 assertTrue(MediaResourceGetter.androidDeviceOk(
546 "GT-I9100", android.os.Build.VERSION_CODES.JELLY_BEAN));
550 public void testAndroidDeviceOk_GoodModel_AnyVersion() {
551 assertTrue(MediaResourceGetter.androidDeviceOk(
552 "Happy Device", android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH));