MediaResourceGetterTest.java revision c5cede9ae108bb15f6b7a8aea21c7e1fefa2834c
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_File_Allowed_MntSdcard() { 340 final String path = "/mnt/sdcard/test"; 341 final String url = "file://" + path; 342 mFakeMRG.mFileExists = true; 343 assertTrue(mFakeMRG.configure(mMockContext, url, "", null)); 344 assertEquals(path, mFakeMRG.mPath); 345 assertNull(mFakeMRG.mUri); 346 assertNull(mFakeMRG.mHeaders); 347 } 348 349 @SmallTest 350 public void testConfigure_File_Allowed_Sdcard() { 351 final String path = "/sdcard/test"; 352 final String url = "file://" + path; 353 mFakeMRG.mFileExists = true; 354 assertTrue(mFakeMRG.configure(mMockContext, url, "", null)); 355 assertEquals(path, mFakeMRG.mPath); 356 assertNull(mFakeMRG.mUri); 357 assertNull(mFakeMRG.mHeaders); 358 } 359 360 @SmallTest 361 public void testConfigure_File_Allowed_Sdcard_DoesntExist() { 362 final String path = "/sdcard/test"; 363 final String url = "file://" + path; 364 mFakeMRG.mFileExists = false; 365 assertFalse(mFakeMRG.configure(mMockContext, url, "", null)); 366 assertNull(mFakeMRG.mPath); 367 } 368 369 @SmallTest 370 public void testConfigure_File_Allowed_ExternalStorage() { 371 final String path = sExternalStorageDirectory + "/test"; 372 final String url = "file://" + path; 373 mFakeMRG.mFileExists = true; 374 assertTrue(mFakeMRG.configure(mMockContext, url, "", null)); 375 assertEquals(path, mFakeMRG.mPath); 376 assertNull(mFakeMRG.mUri); 377 assertNull(mFakeMRG.mHeaders); 378 } 379 380 @SmallTest 381 public void testConfigure_File_Disallowed_Innocent() { 382 final String path = "/malicious/path"; 383 final String url = "file://" + path; 384 mFakeMRG.mFileExists = true; 385 assertFalse(mFakeMRG.configure(mMockContext, url, "", null)); 386 assertNull(mFakeMRG.mPath); 387 } 388 389 @SmallTest 390 public void testConfigure_File_Disallowed_Malicious() { 391 final String path = "/mnt/sdcard/../../data"; 392 final String url = "file://" + path; 393 mFakeMRG.mFileExists = true; 394 assertFalse(mFakeMRG.configure(mMockContext, url, "", null)); 395 assertNull(mFakeMRG.mPath); 396 } 397 398 @SmallTest 399 public void testConfigure_File_Allowed_Exception() { 400 final String path = "/mnt/sdcard/test"; 401 final String url = "file://" + path; 402 mFakeMRG.mFileExists = true; 403 mFakeMRG.mThrowExceptionInConfigure = true; 404 assertFalse(mFakeMRG.configure(mMockContext, url, "", null)); 405 assertNull(mFakeMRG.mPath); 406 } 407 408 @SmallTest 409 public void testExtract_NoMetadata() { 410 mFakeMRG.mFileExists = true; 411 assertEquals(sEmptyMetadata, mFakeMRG.extract( 412 mMockContext, TEST_FILE_URL, null, null)); 413 assertEquals("configured successfully when we shouldn't have", 414 TEST_FILE_PATH, mFakeMRG.mPath); // tricky 415 } 416 417 @SmallTest 418 public void testExtract_WithMetadata_ValidDuration() { 419 mFakeMRG.mFileExists = true; 420 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "1"); 421 final MediaMetadata expected = new MediaMetadata(1, 0, 0, true); 422 assertEquals(expected, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null)); 423 } 424 425 @SmallTest 426 public void testExtract_WithMetadata_InvalidDuration() { 427 mFakeMRG.mFileExists = true; 428 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "i am not an integer"); 429 assertEquals(sEmptyMetadata, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null)); 430 } 431 432 @SmallTest 433 public void testExtract_WithMetadata_ValidDuration_HasVideo_NoWidth_NoHeight() { 434 mFakeMRG.mFileExists = true; 435 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "1"); 436 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO, "yes"); 437 assertEquals(sEmptyMetadata, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null)); 438 } 439 440 @SmallTest 441 public void testExtract_WithMetadata_ValidDuration_HasVideo_ValidWidth_NoHeight() { 442 mFakeMRG.mFileExists = true; 443 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "1"); 444 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO, "yes"); 445 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH, "1"); 446 assertEquals(sEmptyMetadata, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null)); 447 } 448 449 @SmallTest 450 public void testExtract_WithMetadata_ValidDuration_HasVideo_InvalidWidth_NoHeight() { 451 mFakeMRG.mFileExists = true; 452 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "1"); 453 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO, "yes"); 454 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH, "i am not an integer"); 455 assertEquals(sEmptyMetadata, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null)); 456 } 457 458 @SmallTest 459 public void testExtract_WithMetadata_ValidDuration_HasVideo_ValidWidth_ValidHeight() { 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, "2"); 464 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT, "3"); 465 final MediaMetadata expected = new MediaMetadata(1, 2, 3, true); 466 assertEquals(expected, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null)); 467 } 468 469 @SmallTest 470 public void testExtract_WithMetadata_ValidDuration_HasVideo_ValidWidth_InvalidHeight() { 471 mFakeMRG.mFileExists = true; 472 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "1"); 473 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO, "yes"); 474 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH, "1"); 475 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT, "i am not an integer"); 476 assertEquals(sEmptyMetadata, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null)); 477 } 478 479 @SmallTest 480 public void testExtract_WithMetadata_ValidDuration_ExceptionInExtract() { 481 mFakeMRG.mFileExists = true; 482 mFakeMRG.mThrowExceptionInExtract = true; 483 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "1"); 484 assertEquals(sEmptyMetadata, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null)); 485 } 486 487 @SmallTest 488 public void testAndroidDeviceOk_BadModel_BadVersion() { 489 assertFalse(MediaResourceGetter.androidDeviceOk( 490 "GT-I9100", android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1)); 491 } 492 493 @SmallTest 494 public void testAndroidDeviceOk_BadModel_GoodVersion() { 495 assertTrue(MediaResourceGetter.androidDeviceOk( 496 "GT-I9100", android.os.Build.VERSION_CODES.JELLY_BEAN)); 497 } 498 499 @SmallTest 500 public void testAndroidDeviceOk_GoodModel_AnyVersion() { 501 assertTrue(MediaResourceGetter.androidDeviceOk( 502 "Happy Device", android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH)); 503 } 504}