1 // Copyright 2020 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.base; 6 7 import static org.junit.Assert.assertEquals; 8 import static org.junit.Assert.assertNotNull; 9 import static org.junit.Assert.assertNull; 10 import static org.junit.Assert.assertTrue; 11 12 import android.content.ContentProvider; 13 import android.content.ContentValues; 14 import android.content.Context; 15 import android.content.pm.ProviderInfo; 16 import android.database.Cursor; 17 import android.graphics.Bitmap; 18 import android.graphics.BitmapFactory; 19 import android.net.Uri; 20 import android.os.ParcelFileDescriptor; 21 22 import org.junit.Before; 23 import org.junit.Ignore; 24 import org.junit.Rule; 25 import org.junit.Test; 26 import org.junit.rules.TemporaryFolder; 27 import org.junit.runner.RunWith; 28 import org.robolectric.Robolectric; 29 import org.robolectric.annotation.Config; 30 import org.robolectric.annotation.Implementation; 31 import org.robolectric.annotation.Implements; 32 33 import org.chromium.base.test.BaseRobolectricTestRunner; 34 35 import java.io.ByteArrayInputStream; 36 import java.io.ByteArrayOutputStream; 37 import java.io.File; 38 import java.io.FileDescriptor; 39 import java.io.FileInputStream; 40 import java.io.FileNotFoundException; 41 import java.io.FileOutputStream; 42 import java.io.IOException; 43 import java.io.InputStream; 44 import java.nio.file.FileVisitResult; 45 import java.nio.file.Files; 46 import java.nio.file.Path; 47 import java.nio.file.SimpleFileVisitor; 48 import java.nio.file.attribute.BasicFileAttributes; 49 import java.util.ArrayList; 50 import java.util.Arrays; 51 import java.util.Collections; 52 import java.util.HashMap; 53 import java.util.function.Function; 54 55 /** Unit tests for {@link Log}. */ 56 @RunWith(BaseRobolectricTestRunner.class) 57 @Config( 58 manifest = Config.NONE, 59 shadows = {FileUtilsTest.FakeShadowBitmapFactory.class}) 60 public class FileUtilsTest { 61 @Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder(); 62 63 private Context mContext; 64 65 @Before setUp()66 public void setUp() { 67 mContext = ContextUtils.getApplicationContext(); 68 } 69 70 /** 71 * Recursively lists all paths under a directory as relative paths, rendered as a string. 72 * 73 * @param rootDir The directory {@link Path}. 74 * @return A "; "-deliminated string of relative paths of all files stirctly under |rootDir|, 75 * lexicographically by path segments. Directories have "/" as suffix. 76 */ listAllPaths(Path rootDir)77 private String listAllPaths(Path rootDir) { 78 ArrayList<String> pathList = new ArrayList<String>(); 79 try { 80 Files.walkFileTree( 81 rootDir, 82 new SimpleFileVisitor<Path>() { 83 @Override 84 public FileVisitResult preVisitDirectory( 85 Path path, BasicFileAttributes attrs) throws IOException { 86 String relPathString = rootDir.relativize(path).toString(); 87 if (!relPathString.isEmpty()) { // Exclude |rootDir|. 88 pathList.add(relPathString + "/"); 89 } 90 return FileVisitResult.CONTINUE; 91 } 92 93 @Override 94 public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) 95 throws IOException { 96 pathList.add(rootDir.relativize(path).toString()); 97 return FileVisitResult.CONTINUE; 98 } 99 }); 100 } catch (IOException e) { 101 } 102 103 // Sort paths lexicographically by path segments. For example, "foo.bar/file" and "foo/sub" 104 // are treated as ["foo.bar", "file"] and ["foo", "sub"], then compared lexicographically 105 // element-by-element. Since "foo.bar" < "foo" (String comparison), so the order is 106 // "foo/sub" < "foo.bar/file". Instead of actually splitting the strings into lists, we 107 // simply replace '/' with |kSep| as ASCII character 1 for sorting... 108 final char kSep = (char) 1; 109 for (int i = 0; i < pathList.size(); ++i) { 110 pathList.set(i, pathList.get(i).replace('/', kSep)); 111 } 112 Collections.sort(pathList); 113 // Then restore '/'. 114 for (int i = 0; i < pathList.size(); ++i) { 115 pathList.set(i, pathList.get(i).replace(kSep, '/')); 116 } 117 return String.join("; ", pathList); 118 } 119 120 /** 121 * Helper to check the current list of temp files and directories matches expectation. 122 * 123 * @param expectedFileList A string representation of the expected list of temp files and 124 * directories. See listAllPaths() for format. 125 */ assertFileList(String expectedFileList)126 private void assertFileList(String expectedFileList) { 127 Path rootDir = temporaryFolder.getRoot().toPath(); 128 assertEquals(expectedFileList, listAllPaths(rootDir)); 129 } 130 131 /** 132 * Helper to get the absolute path strings of multiple temp paths created for testing. 133 * 134 * @param relPathnames Relative names of temp files or directories (does not need to exist). 135 */ getPathNames(String... relPathNames)136 private ArrayList<String> getPathNames(String... relPathNames) { 137 Path rootDir = temporaryFolder.getRoot().toPath(); 138 ArrayList<String> ret = new ArrayList<String>(); 139 for (String relPathName : relPathNames) { 140 ret.add(rootDir.resolve(relPathName).toString()); 141 } 142 return ret; 143 } 144 145 /** 146 * Helper to get the {@link File} object of a temp paths created for testing. 147 * 148 * @param relPathname The relative name of a temp file or directory (does not need to exist). 149 */ getFile(String relPathName)150 private File getFile(String relPathName) { 151 Path rootDir = temporaryFolder.getRoot().toPath(); 152 return rootDir.resolve(relPathName).toFile(); 153 } 154 155 /** 156 * Helper to create a mix of test files and directories. Can be called multiple times per test, 157 * but requires the temp file to be empty. 158 */ prepareMixedFilesTestCase()159 private void prepareMixedFilesTestCase() throws IOException { 160 assertFileList(""); 161 temporaryFolder.newFolder("a1"); 162 temporaryFolder.newFolder("a1", "b1"); 163 temporaryFolder.newFile("a1/b1/c"); 164 temporaryFolder.newFile("a1/b1/c2"); 165 temporaryFolder.newFolder("a1", "b2"); 166 temporaryFolder.newFolder("a1", "b2", "c"); 167 temporaryFolder.newFile("a1/b3"); 168 temporaryFolder.newFolder("a2"); 169 temporaryFolder.newFile("c"); 170 } 171 172 @Test testRecursivelyDeleteFileBasic()173 public void testRecursivelyDeleteFileBasic() throws IOException { 174 // Test file deletion. 175 temporaryFolder.newFile("some_File"); 176 temporaryFolder.newFile("some"); 177 temporaryFolder.newFile(".dot-config1"); 178 temporaryFolder.newFile("some_File.txt"); 179 assertFileList(".dot-config1; some; some_File; some_File.txt"); 180 assertTrue(FileUtils.recursivelyDeleteFile(getFile("some_File"), null)); 181 assertFileList(".dot-config1; some; some_File.txt"); 182 assertTrue(FileUtils.recursivelyDeleteFile(getFile("some"), null)); 183 assertFileList(".dot-config1; some_File.txt"); 184 assertTrue(FileUtils.recursivelyDeleteFile(getFile("ok_to_delete_nonexistent"), null)); 185 assertFileList(".dot-config1; some_File.txt"); 186 assertTrue(FileUtils.recursivelyDeleteFile(getFile(".dot-config1"), null)); 187 assertFileList("some_File.txt"); 188 assertTrue(FileUtils.recursivelyDeleteFile(getFile("some_File.txt"), null)); 189 assertFileList(""); 190 191 // Test directory deletion. 192 temporaryFolder.newFolder("some_Dir"); 193 temporaryFolder.newFolder("some"); 194 temporaryFolder.newFolder(".dot-dir2"); 195 temporaryFolder.newFolder("some_Dir.ext"); 196 assertFileList(".dot-dir2/; some/; some_Dir/; some_Dir.ext/"); 197 assertTrue(FileUtils.recursivelyDeleteFile(getFile("some_Dir"), null)); 198 assertFileList(".dot-dir2/; some/; some_Dir.ext/"); 199 assertTrue(FileUtils.recursivelyDeleteFile(getFile("some"), null)); 200 assertFileList(".dot-dir2/; some_Dir.ext/"); 201 assertTrue(FileUtils.recursivelyDeleteFile(getFile("ok/to/delete/nonexistent"), null)); 202 assertFileList(".dot-dir2/; some_Dir.ext/"); 203 assertTrue(FileUtils.recursivelyDeleteFile(getFile(".dot-dir2"), null)); 204 assertFileList("some_Dir.ext/"); 205 assertTrue(FileUtils.recursivelyDeleteFile(getFile("some_Dir.ext"), null)); 206 assertFileList(""); 207 208 // Test recursive deletion of mixed files and directories. 209 for (int i = 0; i < 2; ++i) { 210 Function<String, Boolean> canDelete = (i == 0) ? null : FileUtils.DELETE_ALL; 211 prepareMixedFilesTestCase(); 212 assertFileList("a1/; a1/b1/; a1/b1/c; a1/b1/c2; a1/b2/; a1/b2/c/; a1/b3; a2/; c"); 213 assertTrue(FileUtils.recursivelyDeleteFile(getFile("c"), canDelete)); 214 assertFileList("a1/; a1/b1/; a1/b1/c; a1/b1/c2; a1/b2/; a1/b2/c/; a1/b3; a2/"); 215 assertTrue(FileUtils.recursivelyDeleteFile(getFile("a1/b1"), canDelete)); 216 assertFileList("a1/; a1/b2/; a1/b2/c/; a1/b3; a2/"); 217 assertTrue(FileUtils.recursivelyDeleteFile(getFile("a1"), canDelete)); 218 assertFileList("a2/"); 219 assertTrue(FileUtils.recursivelyDeleteFile(getFile("a2"), canDelete)); 220 assertFileList(""); 221 } 222 } 223 224 // Enable or delete once https://crbug.com/1066733 is fixed. 225 @Ignore 226 @Test testRecursivelyDeleteFileWithCanDelete()227 public void testRecursivelyDeleteFileWithCanDelete() throws IOException { 228 Function<String, Boolean> canDeleteIfEndsWith1 = 229 (String filepath) -> { 230 return filepath.endsWith("1"); 231 }; 232 Function<String, Boolean> canDeleteIfEndsWith2 = 233 (String filepath) -> { 234 return filepath.endsWith("2"); 235 }; 236 237 prepareMixedFilesTestCase(); 238 assertFileList("a1/; a1/b1/; a1/b1/c; a1/b1/c2; a1/b2/; a1/b2/c/; a1/b3; a2/; c"); 239 assertTrue(FileUtils.recursivelyDeleteFile(getFile("a1"), canDeleteIfEndsWith1)); 240 assertFileList("a2/; c"); 241 assertTrue(FileUtils.recursivelyDeleteFile(getFile("a2"), canDeleteIfEndsWith1)); 242 assertFileList("a2/; c"); 243 assertTrue(FileUtils.recursivelyDeleteFile(getFile("a2"), canDeleteIfEndsWith2)); 244 assertFileList("c"); 245 assertTrue(FileUtils.recursivelyDeleteFile(getFile("a1"), null)); 246 assertFileList(""); 247 248 prepareMixedFilesTestCase(); 249 assertFileList("a1/; a1/b1/; a1/b1/c; a1/b1/c2; a1/b2/; a1/b2/c/; a1/b3; a2/; c"); 250 assertTrue(FileUtils.recursivelyDeleteFile(getFile("a1"), canDeleteIfEndsWith2)); 251 assertFileList("a1/; a1/b1/; a1/b1/c; a1/b1/c2; a1/b3; a2/; c"); 252 assertTrue(FileUtils.recursivelyDeleteFile(getFile("c"), canDeleteIfEndsWith2)); 253 assertFileList("a1/; a1/b1/; a1/b1/c; a1/b1/c2; a1/b3; a2/; c"); 254 assertTrue(FileUtils.recursivelyDeleteFile(getFile("c"), null)); 255 assertFileList("a1/; a1/b1/; a1/b1/c; a1/b1/c2; a1/b3; a2/"); 256 assertTrue(FileUtils.recursivelyDeleteFile(getFile("a2"), canDeleteIfEndsWith2)); 257 assertFileList("a1/; a1/b1/; a1/b1/c; a1/b1/c2; a1/b3"); 258 assertTrue(FileUtils.recursivelyDeleteFile(getFile("a1"), null)); 259 assertFileList(""); 260 } 261 262 @Test testBatchDeleteFiles()263 public void testBatchDeleteFiles() throws IOException { 264 // Batch delete files specified as path names. 265 prepareMixedFilesTestCase(); 266 assertFileList("a1/; a1/b1/; a1/b1/c; a1/b1/c2; a1/b2/; a1/b2/c/; a1/b3; a2/; c"); 267 FileUtils.batchDeleteFiles(getPathNames("a1/b1", "c", "nonexistent"), null); 268 assertFileList("a1/; a1/b2/; a1/b2/c/; a1/b3; a2/"); 269 // Note that "b2" is not "a1/b2". 270 FileUtils.batchDeleteFiles(getPathNames("b2", "a1/b2/c"), null); 271 assertFileList("a1/; a1/b2/; a1/b3; a2/"); 272 FileUtils.batchDeleteFiles(getPathNames("a1/b3", "a1", "a2", "a2", "a1", "a1/b2"), null); 273 assertFileList(""); 274 275 // Omit testing content URL deletion. 276 } 277 278 @Test testGetFileSize()279 public void testGetFileSize() throws IOException { 280 Function<byte[], Boolean> runCase = 281 (byte[] inputBytes) -> { 282 ByteArrayInputStream inputStream = new ByteArrayInputStream(inputBytes); 283 byte[] fileBytes; 284 long size; 285 try { 286 File tempFile = temporaryFolder.newFile(); 287 FileUtils.copyStreamToFile(inputStream, tempFile); 288 size = FileUtils.getFileSizeBytes(tempFile); 289 } catch (IOException e) { 290 return false; 291 } 292 return inputBytes.length == size; 293 }; 294 295 assertTrue(runCase.apply(new byte[] {})); 296 assertTrue(runCase.apply(new byte[] {3, 1, 4, 1, 5, 9, 2, 6, 5})); 297 assertTrue(runCase.apply("To be or not to be".getBytes())); 298 assertTrue(runCase.apply(createBigByteArray(131072))); // 1 << 17. 299 assertTrue(runCase.apply(createBigByteArray(119993))); // Prime. 300 } 301 302 /** 303 * Helper to create a byte array filled with arbitrary, non-repeating data. 304 * 305 * @param size Size of returned array. 306 */ createBigByteArray(int size)307 private byte[] createBigByteArray(int size) { 308 byte[] ret = new byte[size]; 309 for (int i = 0; i < size; ++i) { 310 int t = i ^ (i >> 8) ^ (i >> 16) ^ (i >> 24); // Prevents repeats. 311 ret[i] = (byte) (t & 0xFF); 312 } 313 return ret; 314 } 315 316 @Test testCopyStream()317 public void testCopyStream() { 318 Function<byte[], Boolean> runCase = 319 (byte[] inputBytes) -> { 320 ByteArrayInputStream inputStream = new ByteArrayInputStream(inputBytes); 321 ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 322 try { 323 FileUtils.copyStream(inputStream, outputStream); 324 } catch (IOException e) { 325 return false; 326 } 327 byte[] outputBytes = outputStream.toByteArray(); 328 return Arrays.equals(inputBytes, outputBytes); 329 }; 330 331 assertTrue(runCase.apply(new byte[] {})); 332 assertTrue(runCase.apply(new byte[] {3, 1, 4, 1, 5, 9, 2, 6, 5})); 333 assertTrue(runCase.apply("To be or not to be".getBytes())); 334 assertTrue(runCase.apply(createBigByteArray(131072))); // 1 << 17. 335 assertTrue(runCase.apply(createBigByteArray(119993))); // Prime. 336 } 337 338 @Test testCopyStreamToFile()339 public void testCopyStreamToFile() { 340 Function<byte[], Boolean> runCase = 341 (byte[] inputBytes) -> { 342 ByteArrayInputStream inputStream = new ByteArrayInputStream(inputBytes); 343 ByteArrayOutputStream verifyStream = new ByteArrayOutputStream(); 344 byte[] fileBytes; 345 try { 346 File tempFile = temporaryFolder.newFile(); 347 FileUtils.copyStreamToFile(inputStream, tempFile); 348 byte[] buffer = new byte[6543]; // Use weird size. 349 try (InputStream is = new FileInputStream(tempFile)) { 350 int amountRead; 351 while ((amountRead = is.read(buffer)) != -1) { 352 verifyStream.write(buffer, 0, amountRead); 353 } 354 } 355 } catch (IOException e) { 356 return false; 357 } 358 byte[] outputBytes = verifyStream.toByteArray(); 359 return Arrays.equals(inputBytes, outputBytes); 360 }; 361 362 assertTrue(runCase.apply(new byte[] {})); 363 assertTrue(runCase.apply(new byte[] {3, 1, 4, 1, 5, 9, 2, 6, 5})); 364 assertTrue(runCase.apply("To be or not to be".getBytes())); 365 assertTrue(runCase.apply(createBigByteArray(131072))); // 1 << 17. 366 assertTrue(runCase.apply(createBigByteArray(119993))); // Prime. 367 } 368 369 @Test testReadStream()370 public void testReadStream() { 371 Function<byte[], Boolean> runCase = 372 (byte[] inputBytes) -> { 373 ByteArrayInputStream inputStream = new ByteArrayInputStream(inputBytes); 374 byte[] verifyBytes; 375 try { 376 verifyBytes = FileUtils.readStream(inputStream); 377 } catch (IOException e) { 378 return false; 379 } 380 return Arrays.equals(inputBytes, verifyBytes); 381 }; 382 383 assertTrue(runCase.apply(new byte[] {})); 384 assertTrue(runCase.apply(new byte[] {3, 1, 4, 1, 5, 9, 2, 6, 5})); 385 assertTrue(runCase.apply("To be or not to be".getBytes())); 386 assertTrue(runCase.apply(createBigByteArray(131072))); // 1 << 17. 387 assertTrue(runCase.apply(createBigByteArray(119993))); // Prime. 388 } 389 390 @Test testGetUriForFileWithContentUri()391 public void testGetUriForFileWithContentUri() { 392 // ContentUriUtils needs to be initialized for "content://" URL to work. Use a fake 393 // version to avoid dealing with Android innards, and to provide consistent results. 394 ContentUriUtils.setFileProviderUtil( 395 new ContentUriUtils.FileProviderUtil() { 396 @Override 397 public Uri getContentUriFromFile(File file) { 398 Uri.Builder builder = new Uri.Builder(); 399 String fileString = file.toString(); 400 if (fileString.startsWith("/")) { 401 fileString = fileString.substring(1); 402 } 403 builder.scheme("content").authority("org.chromium.test"); 404 for (String path : fileString.split("/")) { 405 builder.appendPath(path); 406 } 407 return builder.build(); 408 } 409 }); 410 411 assertEquals( 412 "content://org.chromium.test/", FileUtils.getUriForFile(new File("/")).toString()); 413 assertEquals( 414 "content://org.chromium.test/foo.bar", 415 FileUtils.getUriForFile(new File("/foo.bar")).toString()); 416 assertEquals( 417 "content://org.chromium.test/path1/path2/filename.ext", 418 FileUtils.getUriForFile(new File("/path1/path2/filename.ext")).toString()); 419 assertEquals( 420 "content://org.chromium.test/../../..", 421 FileUtils.getUriForFile(new File("/../../..")).toString()); 422 } 423 424 @Test testGetUriForFileWithoutContentUri()425 public void testGetUriForFileWithoutContentUri() { 426 // Assumes contentUriUtils.setFileProviderUtil() is not called yet. 427 // Only test using absolute path. Otherwise cwd would be included into results. 428 assertEquals("file:///", FileUtils.getUriForFile(new File("/")).toString()); 429 assertEquals("file:///foo.bar", FileUtils.getUriForFile(new File("/foo.bar")).toString()); 430 assertEquals( 431 "file:///path1/path2/filename.ext", 432 FileUtils.getUriForFile(new File("/path1/path2/filename.ext")).toString()); 433 assertEquals("file:///../../..", FileUtils.getUriForFile(new File("/../../..")).toString()); 434 } 435 436 @Test testGetExtension()437 public void testGetExtension() { 438 assertEquals("txt", FileUtils.getExtension("foo.txt")); 439 assertEquals("txt", FileUtils.getExtension("fOo.TxT")); 440 assertEquals("", FileUtils.getExtension("")); 441 assertEquals("", FileUtils.getExtension("No_extension")); 442 assertEquals("foo_config", FileUtils.getExtension(".foo_conFIG")); 443 assertEquals("6", FileUtils.getExtension("a.1.2.3.4.5.6")); 444 assertEquals("a1z2_a8z9", FileUtils.getExtension("a....a1z2_A8Z9")); 445 assertEquals("", FileUtils.getExtension("dotAtEnd.")); 446 assertEquals("ext", FileUtils.getExtension("/Full/PATH/To/File.Ext")); 447 assertEquals("", FileUtils.getExtension("/Full.PATH/To.File/Extra")); 448 assertEquals("", FileUtils.getExtension("../../file")); 449 assertEquals("", FileUtils.getExtension("./etc/passwd")); 450 assertEquals("", FileUtils.getExtension("////////")); 451 assertEquals("", FileUtils.getExtension("........")); 452 assertEquals("", FileUtils.getExtension("././././")); 453 assertEquals("", FileUtils.getExtension("/./././.")); 454 } 455 456 /** 457 * This class replaces {@link ShadowBitmapFactory} so that decodeFileDescriptor() won't always 458 * return a valid {@link Image}. This enables testing error handing for attempting to load 459 * non-image files. A file is deemed valid if and only if it's non-empty. 460 */ 461 @Implements(BitmapFactory.class) 462 public static class FakeShadowBitmapFactory { 463 @Implementation decodeFileDescriptor(FileDescriptor fd)464 public static Bitmap decodeFileDescriptor(FileDescriptor fd) throws IOException { 465 FileInputStream inStream = new FileInputStream(fd); 466 if (inStream.read() == -1) { 467 return null; 468 } 469 return Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); 470 } 471 } 472 473 private static class TestContentProvider extends ContentProvider { 474 private HashMap<String, String> mUriToFilename; 475 TestContentProvider()476 public TestContentProvider() { 477 mUriToFilename = new HashMap<String, String>(); 478 } 479 insertForTest(String uriString, String filename)480 public void insertForTest(String uriString, String filename) { 481 mUriToFilename.put(uriString, filename); 482 } 483 484 @Override openFile(Uri uri, String mode)485 public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { 486 String uriString = uri.toString(); 487 if (mUriToFilename.containsKey(uriString)) { 488 String filename = mUriToFilename.get(uriString); 489 // Throws FileNotFoundException if |filename| is bogus. 490 return ParcelFileDescriptor.open( 491 new File(filename), ParcelFileDescriptor.MODE_READ_ONLY); 492 } 493 return null; 494 } 495 496 @Override onCreate()497 public boolean onCreate() { 498 return false; 499 } 500 501 @Override query( Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)502 public Cursor query( 503 Uri uri, 504 String[] projection, 505 String selection, 506 String[] selectionArgs, 507 String sortOrder) { 508 return null; 509 } 510 511 @Override getType(Uri uri)512 public String getType(Uri uri) { 513 return null; 514 } 515 516 @Override insert(Uri uri, ContentValues values)517 public Uri insert(Uri uri, ContentValues values) { 518 return null; 519 } 520 521 @Override delete(Uri uri, String selection, String[] selectionArgs)522 public int delete(Uri uri, String selection, String[] selectionArgs) { 523 return 0; 524 } 525 526 @Override update(Uri uri, ContentValues values, String selection, String[] selectionArgs)527 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { 528 return 0; 529 } 530 } 531 markFileAsValidImage(File outFile)532 public void markFileAsValidImage(File outFile) throws IOException { 533 FileOutputStream outStream = new FileOutputStream(outFile); 534 outStream.write("Non-empty file is assumed to be valid image.".getBytes()); 535 outStream.close(); 536 } 537 538 @Test testQueryBitmapFromContentProvider()539 public void testQueryBitmapFromContentProvider() throws IOException { 540 // Set up "org.chromium.test" provider. 541 ProviderInfo info = new ProviderInfo(); 542 info.authority = "org.chromium.test"; 543 TestContentProvider contentProvider = 544 Robolectric.buildContentProvider(TestContentProvider.class).create(info).get(); 545 546 // Fake valid image. Expect success. 547 File tempFile1 = temporaryFolder.newFile("temp1.png"); 548 markFileAsValidImage(tempFile1); 549 Uri validImageUri = Uri.parse("content://org.chromium.test/valid.png"); 550 contentProvider.insertForTest(validImageUri.toString(), tempFile1.toString()); 551 552 // File exists, but not a valid image (empty). Expect failure. 553 File tempFile2 = temporaryFolder.newFile("temp2.txt"); 554 Uri invalidImageUri = Uri.parse("content://org.chromium.test/invalid.txt"); 555 contentProvider.insertForTest(invalidImageUri.toString(), tempFile2.toString()); 556 557 // Uri exists, but file does not exist. Expect failure. 558 Uri bogusFileUri = Uri.parse("content://org.chromium.test/bogus-file.txt"); 559 contentProvider.insertForTest(bogusFileUri.toString(), "bogus-to-trigger-file-not-found"); 560 561 // Uri does not exist. Expect failure. 562 Uri nonExistentUri = Uri.parse("content://org.chromium.test/non-existent.txt"); 563 564 for (int i = 0; i < 2; ++i) { 565 assertNotNull(FileUtils.queryBitmapFromContentProvider(mContext, validImageUri)); 566 assertNull(FileUtils.queryBitmapFromContentProvider(mContext, invalidImageUri)); 567 assertNull(FileUtils.queryBitmapFromContentProvider(mContext, bogusFileUri)); 568 assertNull(FileUtils.queryBitmapFromContentProvider(mContext, nonExistentUri)); 569 } 570 } 571 } 572