xref: /aosp_15_r20/external/cronet/base/android/junit/src/org/chromium/base/FileUtilsTest.java (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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