blob: a6adc96909b1262c6abd5e1858ed448fa48257a9 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.content.browser;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.PackageManager;
import android.media.MediaMetadataRetriever;
import android.net.ConnectivityManager;
import android.test.InstrumentationTestCase;
import android.test.mock.MockContext;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.SparseArray;
import org.chromium.content.browser.MediaResourceGetter.MediaMetadata;
import java.io.File;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* Tests for MediaResourceGetter.
*/
@SuppressLint("SdCardPath")
public class MediaResourceGetterTest extends InstrumentationTestCase {
private static final String TEST_HTTP_URL = "http://example.com";
private static final String TEST_USER_AGENT = // Anything, really
"Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 " +
"(KHTML, like Gecko) Chrome/32.0.1667.0 Safari/537.36";
private static final String TEST_FILE_PATH = "/mnt/sdcard/test";
private static final String TEST_FILE_URL = "file://" + TEST_FILE_PATH;
private static final String TEST_COOKIES = "yum yum yum!";
private static final MediaMetadata sEmptyMetadata = new MediaMetadata(0, 0, 0, false);
private static final String sExternalStorageDirectory = "/test_external_storage";
private static final Map<String,String> sHeadersCookieOnly;
private static final Map<String,String> sHeadersCookieAndUA;
private static final Map<String,String> sHeadersUAOnly;
static {
Map<String,String> headers = new HashMap<String, String>();
headers.put("Cookie", TEST_COOKIES);
sHeadersCookieOnly = Collections.unmodifiableMap(headers);
headers = new HashMap<String, String>();
headers.put("User-Agent", TEST_USER_AGENT);
sHeadersUAOnly = Collections.unmodifiableMap(headers);
headers = new HashMap<String, String>();
headers.putAll(sHeadersCookieOnly);
headers.putAll(sHeadersUAOnly);
sHeadersCookieAndUA = Collections.unmodifiableMap(headers);
}
private static class FakeFile extends File {
final boolean mExists;
public FakeFile(String path, boolean exists) {
super(path);
mExists = exists;
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + (mExists ? 1231 : 1237);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (getClass() != obj.getClass())
return false;
FakeFile other = (FakeFile) obj;
if (mExists != other.mExists)
return false;
return true;
}
@Override
public boolean exists() {
return mExists;
}
}
/**
* Helper class that fakes functionality we cannot perform without real
* media resources. The class being faked here has been architected
* carefully to allow most of the functionality to be tested. What remains
* here is overrides of trivial functionality.
*/
private static class FakeMediaResourceGetter extends MediaResourceGetter {
// Read these back in tests to ensure proper values passed through
String mUri = null;
Map<String,String> mHeaders = null;
String mPath = null;
int mFd;
long mOffset;
long mLength;
// Write these before tests to configure functionality
SparseArray<String> mMetadata = null;
Integer mNetworkType = null;
boolean mThrowExceptionInConfigure = false;
boolean mThrowExceptionInExtract = false;
boolean mFileExists = false;
// Can't use a real MediaMetadataRetriever as we have no media
@Override
public void configure(int fd, long offset, long length) {
if (mThrowExceptionInConfigure) {
throw new RuntimeException("test exception");
}
mFd = fd;
mOffset = offset;
mLength = length;
}
// Can't use a real MediaMetadataRetriever as we have no media
@Override
public void configure(String uri, Map<String, String> headers) {
if (mThrowExceptionInConfigure) {
throw new RuntimeException("test exception");
}
mUri = uri;
mHeaders = headers;
}
// Can't use a real MediaMetadataRetriever as we have no media
@Override
public void configure(String path) {
if (mThrowExceptionInConfigure) {
throw new RuntimeException("test exception");
}
mPath = path;
}
// Can't use a real MediaMetadataRetriever as we have no media
@Override
public String extractMetadata(int key) {
if (mThrowExceptionInExtract) {
throw new RuntimeException("test exception");
}
if (mMetadata == null) {
return null;
}
return mMetadata.get(key);
}
// Can't use a real NetworkInfo object because there is no way to
// mock the ConnectivityManager in Android.
@Override
Integer getNetworkType(Context context) {
return mNetworkType;
}
// Can't use Environment.getExternalStorageDirectory because it could
// be inconsistent depending upon the state of the real or emulated
// device upon which we are testing.
@Override
String getExternalStorageDirectory() {
return sExternalStorageDirectory;
}
// Can't use regular File because we need to control the return from
// File.exists() on arbitrary paths.
@Override
File uriToFile(String path) {
FakeFile result = new FakeFile(path, mFileExists);
return result;
}
/**
* Convenience method to bind a metadata value to a key.
* @param key the key
* @param value the value
*/
void bind(int key, String value) {
if (mMetadata == null) {
mMetadata = new SparseArray<String>();
}
mMetadata.put(key, value);
}
}
/**
* Helper class to control the result of permission checks.
*/
private static class InternalMockContext extends MockContext {
boolean allowPermission = false;
@Override
public int checkCallingOrSelfPermission(String permission) {
assertEquals(android.Manifest.permission.ACCESS_NETWORK_STATE,
permission);
return allowPermission ? PackageManager.PERMISSION_GRANTED :
PackageManager.PERMISSION_DENIED;
}
}
// Our test objects.
private FakeMediaResourceGetter mFakeMRG;
private InternalMockContext mMockContext;
@Override
protected void setUp() throws Exception {
super.setUp();
mFakeMRG = new FakeMediaResourceGetter();
mMockContext = new InternalMockContext();
}
@SmallTest
public void testMediaMetadataEquals() {
assertEquals(sEmptyMetadata, sEmptyMetadata);
assertEquals(sEmptyMetadata, new MediaMetadata(0, 0, 0, false));
assertFalse(sEmptyMetadata.equals(null));
assertFalse(sEmptyMetadata.equals("test"));
assertFalse(sEmptyMetadata.equals(new MediaMetadata(1, 0, 0, false)));
assertFalse(sEmptyMetadata.equals(new MediaMetadata(0, 1, 0, false)));
assertFalse(sEmptyMetadata.equals(new MediaMetadata(0, 0, 1, false)));
assertFalse(sEmptyMetadata.equals(new MediaMetadata(0, 0, 0, true)));
}
@SmallTest
public void testMediaMetadataHashCode() {
assertEquals(sEmptyMetadata.hashCode(), sEmptyMetadata.hashCode());
assertEquals(sEmptyMetadata.hashCode(), new MediaMetadata(0, 0, 0, false).hashCode());
assertFalse(sEmptyMetadata.hashCode() == new MediaMetadata(1, 0, 0, false).hashCode());
assertFalse(sEmptyMetadata.hashCode() == new MediaMetadata(0, 1, 0, false).hashCode());
assertFalse(sEmptyMetadata.hashCode() == new MediaMetadata(0, 0, 1, false).hashCode());
assertFalse(sEmptyMetadata.hashCode() == new MediaMetadata(0, 0, 0, true).hashCode());
}
@SmallTest
public void testMediaMetadataGetters() {
MediaMetadata data = new MediaMetadata(1, 2, 3, true);
assertEquals(1, data.getDurationInMilliseconds());
assertEquals(2, data.getWidth());
assertEquals(3, data.getHeight());
assertTrue(data.isSuccess());
// Validate no overlap of test values with defaults
data = new MediaMetadata(4, 5, 6, false);
assertEquals(4, data.getDurationInMilliseconds());
assertEquals(5, data.getWidth());
assertEquals(6, data.getHeight());
assertFalse(data.isSuccess());
}
@SmallTest
public void testConfigure_Net_NoPermissions() {
mMockContext.allowPermission = false;
assertFalse(mFakeMRG.configure(mMockContext, TEST_HTTP_URL,
TEST_COOKIES, TEST_USER_AGENT));
}
@SmallTest
public void testConfigure_Net_NoActiveNetwork() {
mMockContext.allowPermission = true;
mFakeMRG.mNetworkType = null;
assertFalse(mFakeMRG.configure(mMockContext, TEST_HTTP_URL,
TEST_COOKIES, TEST_USER_AGENT));
}
@SmallTest
public void testConfigure_Net_Disallowed_Mobile() {
mMockContext.allowPermission = true;
mFakeMRG.mNetworkType = ConnectivityManager.TYPE_MOBILE;
assertFalse(mFakeMRG.configure(mMockContext, TEST_HTTP_URL,
TEST_COOKIES, TEST_USER_AGENT));
}
@SmallTest
public void testConfigure_Net_Disallowed_Wimax() {
mMockContext.allowPermission = true;
mFakeMRG.mNetworkType = ConnectivityManager.TYPE_WIMAX;
assertFalse(mFakeMRG.configure(mMockContext, TEST_HTTP_URL,
TEST_COOKIES, TEST_USER_AGENT));
}
@SmallTest
public void testConfigure_Net_Allowed_Ethernet_Cookies_NoUA() {
mMockContext.allowPermission = true;
mFakeMRG.mNetworkType = ConnectivityManager.TYPE_ETHERNET;
assertTrue(mFakeMRG.configure(mMockContext, TEST_HTTP_URL,
TEST_COOKIES, null));
assertEquals(TEST_HTTP_URL, mFakeMRG.mUri);
assertEquals(sHeadersCookieOnly, mFakeMRG.mHeaders);
assertNull(mFakeMRG.mPath);
}
@SmallTest
public void testConfigure_Net_Allowed_Wifi_Cookies_NoUA() {
mMockContext.allowPermission = true;
mFakeMRG.mNetworkType = ConnectivityManager.TYPE_WIFI;
assertTrue(mFakeMRG.configure(mMockContext, TEST_HTTP_URL,
TEST_COOKIES, null));
assertEquals(TEST_HTTP_URL, mFakeMRG.mUri);
assertEquals(sHeadersCookieOnly, mFakeMRG.mHeaders);
assertNull(mFakeMRG.mPath);
}
@SmallTest
public void testConfigure_Net_Allowed_Ethernet_NoCookies_NoUA() {
mMockContext.allowPermission = true;
mFakeMRG.mNetworkType = ConnectivityManager.TYPE_ETHERNET;
assertTrue(mFakeMRG.configure(mMockContext, TEST_HTTP_URL,
"", null));
assertEquals(TEST_HTTP_URL, mFakeMRG.mUri);
assertEquals(Collections.emptyMap(), mFakeMRG.mHeaders);
assertNull(mFakeMRG.mPath);
}
@SmallTest
public void testConfigure_Net_Allowed_Ethernet_Cookies_WithUA() {
mMockContext.allowPermission = true;
mFakeMRG.mNetworkType = ConnectivityManager.TYPE_ETHERNET;
assertTrue(mFakeMRG.configure(mMockContext, TEST_HTTP_URL,
TEST_COOKIES, TEST_USER_AGENT));
assertEquals(TEST_HTTP_URL, mFakeMRG.mUri);
assertEquals(sHeadersCookieAndUA, mFakeMRG.mHeaders);
assertNull(mFakeMRG.mPath);
}
@SmallTest
public void testConfigure_Net_Allowed_Ethernet_NoCookies_WithUA() {
mMockContext.allowPermission = true;
mFakeMRG.mNetworkType = ConnectivityManager.TYPE_ETHERNET;
assertTrue(mFakeMRG.configure(mMockContext, TEST_HTTP_URL,
"", TEST_USER_AGENT));
assertEquals(TEST_HTTP_URL, mFakeMRG.mUri);
assertEquals(sHeadersUAOnly, mFakeMRG.mHeaders);
assertNull(mFakeMRG.mPath);
}
@SmallTest
public void testConfigure_Net_Allowed_Ethernet_Exception() {
mMockContext.allowPermission = true;
mFakeMRG.mThrowExceptionInConfigure = true;
mFakeMRG.mNetworkType = ConnectivityManager.TYPE_ETHERNET;
assertFalse(mFakeMRG.configure(mMockContext, TEST_HTTP_URL,
"", TEST_USER_AGENT));
assertNull(mFakeMRG.mUri);
assertNull(mFakeMRG.mHeaders);
}
@SmallTest
public void testConfigure_Net_Allowed_LocalHost_WithNoNetwork() {
String[] localHostUrls = {
"http://LocalHost",
"https://127.0.0.1/",
"http://[::1]:8888/",
};
mMockContext.allowPermission = true;
mFakeMRG.mNetworkType = null;
for (String localHostUrl : localHostUrls) {
assertTrue(mFakeMRG.configure(mMockContext, localHostUrl,
TEST_COOKIES, TEST_USER_AGENT));
assertEquals(localHostUrl, mFakeMRG.mUri);
assertEquals(sHeadersCookieAndUA, mFakeMRG.mHeaders);
assertNull(mFakeMRG.mPath);
}
}
@SmallTest
public void testConfigure_File_Allowed_MntSdcard() {
final String path = "/mnt/sdcard/test";
final String url = "file://" + path;
mFakeMRG.mFileExists = true;
assertTrue(mFakeMRG.configure(mMockContext, url, "", null));
assertEquals(path, mFakeMRG.mPath);
assertNull(mFakeMRG.mUri);
assertNull(mFakeMRG.mHeaders);
}
@SmallTest
public void testConfigure_File_Allowed_Sdcard() {
final String path = "/sdcard/test";
final String url = "file://" + path;
mFakeMRG.mFileExists = true;
assertTrue(mFakeMRG.configure(mMockContext, url, "", null));
assertEquals(path, mFakeMRG.mPath);
assertNull(mFakeMRG.mUri);
assertNull(mFakeMRG.mHeaders);
}
@SmallTest
public void testConfigure_File_Allowed_Sdcard_DoesntExist() {
final String path = "/sdcard/test";
final String url = "file://" + path;
mFakeMRG.mFileExists = false;
assertFalse(mFakeMRG.configure(mMockContext, url, "", null));
assertNull(mFakeMRG.mPath);
}
@SmallTest
public void testConfigure_File_Allowed_ExternalStorage() {
final String path = sExternalStorageDirectory + "/test";
final String url = "file://" + path;
mFakeMRG.mFileExists = true;
assertTrue(mFakeMRG.configure(mMockContext, url, "", null));
assertEquals(path, mFakeMRG.mPath);
assertNull(mFakeMRG.mUri);
assertNull(mFakeMRG.mHeaders);
}
@SmallTest
public void testConfigure_File_Disallowed_Innocent() {
final String path = "/malicious/path";
final String url = "file://" + path;
mFakeMRG.mFileExists = true;
assertFalse(mFakeMRG.configure(mMockContext, url, "", null));
assertNull(mFakeMRG.mPath);
}
@SmallTest
public void testConfigure_File_Disallowed_Malicious() {
final String path = "/mnt/sdcard/../../data";
final String url = "file://" + path;
mFakeMRG.mFileExists = true;
assertFalse(mFakeMRG.configure(mMockContext, url, "", null));
assertNull(mFakeMRG.mPath);
}
@SmallTest
public void testConfigure_File_Allowed_Exception() {
final String path = "/mnt/sdcard/test";
final String url = "file://" + path;
mFakeMRG.mFileExists = true;
mFakeMRG.mThrowExceptionInConfigure = true;
assertFalse(mFakeMRG.configure(mMockContext, url, "", null));
assertNull(mFakeMRG.mPath);
}
@SmallTest
public void testExtract_NoMetadata() {
mFakeMRG.mFileExists = true;
assertEquals(sEmptyMetadata, mFakeMRG.extract(
mMockContext, TEST_FILE_URL, null, null));
assertEquals("configured successfully when we shouldn't have",
TEST_FILE_PATH, mFakeMRG.mPath); // tricky
}
@SmallTest
public void testExtract_WithMetadata_ValidDuration() {
mFakeMRG.mFileExists = true;
mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "1");
final MediaMetadata expected = new MediaMetadata(1, 0, 0, true);
assertEquals(expected, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null));
}
@SmallTest
public void testExtract_WithMetadata_InvalidDuration() {
mFakeMRG.mFileExists = true;
mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "i am not an integer");
assertEquals(sEmptyMetadata, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null));
}
@SmallTest
public void testExtract_WithMetadata_ValidDuration_HasVideo_NoWidth_NoHeight() {
mFakeMRG.mFileExists = true;
mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "1");
mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO, "yes");
assertEquals(sEmptyMetadata, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null));
}
@SmallTest
public void testExtract_WithMetadata_ValidDuration_HasVideo_ValidWidth_NoHeight() {
mFakeMRG.mFileExists = true;
mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "1");
mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO, "yes");
mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH, "1");
assertEquals(sEmptyMetadata, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null));
}
@SmallTest
public void testExtract_WithMetadata_ValidDuration_HasVideo_InvalidWidth_NoHeight() {
mFakeMRG.mFileExists = true;
mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "1");
mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO, "yes");
mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH, "i am not an integer");
assertEquals(sEmptyMetadata, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null));
}
@SmallTest
public void testExtract_WithMetadata_ValidDuration_HasVideo_ValidWidth_ValidHeight() {
mFakeMRG.mFileExists = true;
mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "1");
mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO, "yes");
mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH, "2");
mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT, "3");
final MediaMetadata expected = new MediaMetadata(1, 2, 3, true);
assertEquals(expected, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null));
}
@SmallTest
public void testExtract_WithMetadata_ValidDuration_HasVideo_ValidWidth_InvalidHeight() {
mFakeMRG.mFileExists = true;
mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "1");
mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO, "yes");
mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH, "1");
mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT, "i am not an integer");
assertEquals(sEmptyMetadata, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null));
}
@SmallTest
public void testExtract_WithMetadata_ValidDuration_ExceptionInExtract() {
mFakeMRG.mFileExists = true;
mFakeMRG.mThrowExceptionInExtract = true;
mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "1");
assertEquals(sEmptyMetadata, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null));
}
@SmallTest
public void testExtractFromFileDescriptor_ValidMetadata() {
mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "1");
mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO, "yes");
mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH, "2");
mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT, "3");
final MediaMetadata expected = new MediaMetadata(1, 2, 3, true);
int fd = 1234;
long offset = 1000;
long length = 9000;
assertEquals(expected, mFakeMRG.extract(fd, offset, length));
assertEquals(fd, mFakeMRG.mFd);
assertEquals(offset, mFakeMRG.mOffset);
assertEquals(length, mFakeMRG.mLength);
}
@SmallTest
public void testAndroidDeviceOk_BadModel_BadVersion() {
assertFalse(MediaResourceGetter.androidDeviceOk(
"GT-I9100", android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1));
}
@SmallTest
public void testAndroidDeviceOk_BadModel_GoodVersion() {
assertTrue(MediaResourceGetter.androidDeviceOk(
"GT-I9100", android.os.Build.VERSION_CODES.JELLY_BEAN));
}
@SmallTest
public void testAndroidDeviceOk_GoodModel_AnyVersion() {
assertTrue(MediaResourceGetter.androidDeviceOk(
"Happy Device", android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH));
}
}