1 // Copyright (c) 2012 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 
5 #include "chrome/browser/media_galleries/fileapi/native_media_file_util.h"
6 
7 #include <stddef.h>
8 #include <set>
9 #include <string>
10 #include <utility>
11 #include <vector>
12 
13 #include "base/bind.h"
14 #include "base/files/file_util.h"
15 #include "base/files/scoped_temp_dir.h"
16 #include "base/format_macros.h"
17 #include "base/memory/scoped_refptr.h"
18 #include "base/stl_util.h"
19 #include "base/strings/stringprintf.h"
20 #include "base/threading/sequenced_task_runner_handle.h"
21 #include "base/time/time.h"
22 #include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h"
23 #include "content/public/browser/browser_task_traits.h"
24 #include "content/public/browser/browser_thread.h"
25 #include "content/public/test/browser_task_environment.h"
26 #include "content/public/test/test_utils.h"
27 #include "storage/browser/blob/shareable_file_reference.h"
28 #include "storage/browser/file_system/external_mount_points.h"
29 #include "storage/browser/file_system/file_system_backend.h"
30 #include "storage/browser/file_system/file_system_context.h"
31 #include "storage/browser/file_system/file_system_operation_runner.h"
32 #include "storage/browser/file_system/file_system_url.h"
33 #include "storage/browser/file_system/isolated_context.h"
34 #include "storage/browser/file_system/native_file_util.h"
35 #include "storage/browser/test/mock_special_storage_policy.h"
36 #include "storage/browser/test/test_file_system_options.h"
37 #include "testing/gtest/include/gtest/gtest.h"
38 #include "url/origin.h"
39 
40 #define FPL(x) FILE_PATH_LITERAL(x)
41 
42 using storage::FileSystemOperation;
43 using storage::FileSystemURL;
44 
45 namespace {
46 
47 typedef FileSystemOperation::FileEntryList FileEntryList;
48 
49 struct FilteringTestCase {
50   const base::FilePath::CharType* path;
51   bool is_directory;
52   bool visible;
53   bool media_file;
54   const char* content;
55 };
56 
57 const FilteringTestCase kFilteringTestCases[] = {
58     // Directory should always be visible.
59     {FPL("hoge"), true, true, false, nullptr},
60     {FPL("fuga.jpg"), true, true, false, nullptr},
61     {FPL("piyo.txt"), true, true, false, nullptr},
62     {FPL("moga.cod"), true, true, false, nullptr},
63 
64     // File should be visible if it's a supported media file.
65     // File without extension.
66     {FPL("foo"), false, false, false, "abc"},
67     // Supported media file.
68     {FPL("bar.jpg"), false, true, true, "\xFF\xD8\xFF"},
69     // Unsupported masquerading file.
70     {FPL("sna.jpg"), false, true, false, "abc"},
71     // Non-media file.
72     {FPL("baz.txt"), false, false, false, "abc"},
73     // Unsupported media file.
74     {FPL("foobar.cod"), false, false, false, "abc"},
75 };
76 
ExpectEqHelper(const std::string & test_name,base::File::Error expected,base::File::Error actual)77 void ExpectEqHelper(const std::string& test_name,
78                     base::File::Error expected,
79                     base::File::Error actual) {
80   EXPECT_EQ(expected, actual) << test_name;
81 }
82 
ExpectMetadataEqHelper(const std::string & test_name,base::File::Error expected,bool expected_is_directory,base::File::Error actual,const base::File::Info & file_info)83 void ExpectMetadataEqHelper(const std::string& test_name,
84                             base::File::Error expected,
85                             bool expected_is_directory,
86                             base::File::Error actual,
87                             const base::File::Info& file_info) {
88   EXPECT_EQ(expected, actual) << test_name;
89   if (actual == base::File::FILE_OK)
90     EXPECT_EQ(expected_is_directory, file_info.is_directory) << test_name;
91 }
92 
DidReadDirectory(std::set<base::FilePath::StringType> * content,bool * completed,base::File::Error error,FileEntryList file_list,bool has_more)93 void DidReadDirectory(std::set<base::FilePath::StringType>* content,
94                       bool* completed,
95                       base::File::Error error,
96                       FileEntryList file_list,
97                       bool has_more) {
98   EXPECT_TRUE(!*completed);
99   *completed = !has_more;
100   for (const auto& entry : file_list)
101     EXPECT_TRUE(content->insert(entry.name.value()).second);
102 }
103 
PopulateDirectoryWithTestCases(const base::FilePath & dir,const FilteringTestCase * test_cases,size_t n)104 void PopulateDirectoryWithTestCases(const base::FilePath& dir,
105                                     const FilteringTestCase* test_cases,
106                                     size_t n) {
107   for (size_t i = 0; i < n; ++i) {
108     base::FilePath path = dir.Append(test_cases[i].path);
109     if (test_cases[i].is_directory) {
110       ASSERT_TRUE(base::CreateDirectory(path));
111     } else {
112       ASSERT_TRUE(test_cases[i].content);
113       ASSERT_TRUE(base::WriteFile(path, test_cases[i].content));
114     }
115   }
116 }
117 
118 }  // namespace
119 
120 class NativeMediaFileUtilTest : public testing::Test {
121  public:
122   NativeMediaFileUtilTest() = default;
123 
SetUp()124   void SetUp() override {
125     ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
126     ASSERT_TRUE(base::CreateDirectory(root_path()));
127 
128     auto storage_policy =
129         base::MakeRefCounted<storage::MockSpecialStoragePolicy>();
130 
131     std::vector<std::unique_ptr<storage::FileSystemBackend>>
132         additional_providers;
133     additional_providers.push_back(
134         std::make_unique<MediaFileSystemBackend>(data_dir_.GetPath()));
135 
136     file_system_context_ = base::MakeRefCounted<storage::FileSystemContext>(
137         content::GetIOThreadTaskRunner({}).get(),
138         base::SequencedTaskRunnerHandle::Get().get(),
139         storage::ExternalMountPoints::CreateRefCounted().get(),
140         storage_policy.get(), nullptr, std::move(additional_providers),
141         std::vector<storage::URLRequestAutoMountHandler>(), data_dir_.GetPath(),
142         storage::CreateAllowFileAccessOptions());
143 
144     filesystem_ = isolated_context()->RegisterFileSystemForPath(
145         storage::kFileSystemTypeNativeMedia, std::string(), root_path(),
146         nullptr);
147     filesystem_id_ = filesystem_.id();
148   }
149 
TearDown()150   void TearDown() override { file_system_context_.reset(); }
151 
152  protected:
file_system_context()153   storage::FileSystemContext* file_system_context() {
154     return file_system_context_.get();
155   }
156 
CreateURL(const base::FilePath::CharType * test_case_path)157   FileSystemURL CreateURL(const base::FilePath::CharType* test_case_path) {
158     return file_system_context_->CreateCrackedFileSystemURL(
159         url::Origin::Create(origin()), storage::kFileSystemTypeIsolated,
160         GetVirtualPath(test_case_path));
161   }
162 
isolated_context()163   storage::IsolatedContext* isolated_context() {
164     return storage::IsolatedContext::GetInstance();
165   }
166 
root_path()167   base::FilePath root_path() {
168     return data_dir_.GetPath().Append(FPL("Media Directory"));
169   }
170 
GetVirtualPath(const base::FilePath::CharType * test_case_path)171   base::FilePath GetVirtualPath(
172       const base::FilePath::CharType* test_case_path) {
173     return base::FilePath::FromUTF8Unsafe(filesystem_id_)
174         .Append(FPL("Media Directory"))
175         .Append(base::FilePath(test_case_path));
176   }
177 
origin()178   GURL origin() { return GURL("http://example.com"); }
179 
type()180   storage::FileSystemType type() { return storage::kFileSystemTypeNativeMedia; }
181 
operation_runner()182   storage::FileSystemOperationRunner* operation_runner() {
183     return file_system_context_->operation_runner();
184   }
185 
186   content::BrowserTaskEnvironment task_environment_;
187 
188  private:
189   base::ScopedTempDir data_dir_;
190   scoped_refptr<storage::FileSystemContext> file_system_context_;
191 
192   std::string filesystem_id_;
193   storage::IsolatedContext::ScopedFSHandle filesystem_;
194 
195   DISALLOW_COPY_AND_ASSIGN(NativeMediaFileUtilTest);
196 };
197 
TEST_F(NativeMediaFileUtilTest,DirectoryExistsAndFileExistsFiltering)198 TEST_F(NativeMediaFileUtilTest, DirectoryExistsAndFileExistsFiltering) {
199   PopulateDirectoryWithTestCases(root_path(), kFilteringTestCases,
200                                  base::size(kFilteringTestCases));
201 
202   for (size_t i = 0; i < base::size(kFilteringTestCases); ++i) {
203     FileSystemURL url = CreateURL(kFilteringTestCases[i].path);
204 
205     base::File::Error expectation = kFilteringTestCases[i].visible
206                                         ? base::File::FILE_OK
207                                         : base::File::FILE_ERROR_NOT_FOUND;
208 
209     std::string test_name =
210         base::StringPrintf("DirectoryExistsAndFileExistsFiltering %" PRIuS, i);
211     if (kFilteringTestCases[i].is_directory) {
212       operation_runner()->DirectoryExists(
213           url, base::BindOnce(&ExpectEqHelper, test_name, expectation));
214     } else {
215       operation_runner()->FileExists(
216           url, base::BindOnce(&ExpectEqHelper, test_name, expectation));
217     }
218     content::RunAllTasksUntilIdle();
219   }
220 }
221 
TEST_F(NativeMediaFileUtilTest,ReadDirectoryFiltering)222 TEST_F(NativeMediaFileUtilTest, ReadDirectoryFiltering) {
223   PopulateDirectoryWithTestCases(root_path(), kFilteringTestCases,
224                                  base::size(kFilteringTestCases));
225 
226   std::set<base::FilePath::StringType> content;
227   FileSystemURL url = CreateURL(FPL(""));
228   bool completed = false;
229   operation_runner()->ReadDirectory(
230       url, base::BindRepeating(&DidReadDirectory, &content, &completed));
231   content::RunAllTasksUntilIdle();
232   EXPECT_TRUE(completed);
233   EXPECT_EQ(6u, content.size());
234 
235   for (size_t i = 0; i < base::size(kFilteringTestCases); ++i) {
236     base::FilePath::StringType name =
237         base::FilePath(kFilteringTestCases[i].path).BaseName().value();
238     auto found = content.find(name);
239     EXPECT_EQ(kFilteringTestCases[i].visible, found != content.end());
240   }
241 }
242 
TEST_F(NativeMediaFileUtilTest,CreateDirectoryFiltering)243 TEST_F(NativeMediaFileUtilTest, CreateDirectoryFiltering) {
244   // Run the loop twice. The second loop attempts to create directories that are
245   // pre-existing. Though the result should be the same.
246   for (int loop_count = 0; loop_count < 2; ++loop_count) {
247     for (size_t i = 0; i < base::size(kFilteringTestCases); ++i) {
248       if (kFilteringTestCases[i].is_directory) {
249         FileSystemURL root_url = CreateURL(FPL(""));
250         FileSystemURL url = CreateURL(kFilteringTestCases[i].path);
251 
252         std::string test_name = base::StringPrintf(
253             "CreateFileAndCreateDirectoryFiltering run %d, test %" PRIuS,
254             loop_count, i);
255         base::File::Error expectation = kFilteringTestCases[i].visible
256                                             ? base::File::FILE_OK
257                                             : base::File::FILE_ERROR_SECURITY;
258         operation_runner()->CreateDirectory(
259             url, false, false,
260             base::BindOnce(&ExpectEqHelper, test_name, expectation));
261       }
262       content::RunAllTasksUntilIdle();
263     }
264   }
265 }
266 
TEST_F(NativeMediaFileUtilTest,CopySourceFiltering)267 TEST_F(NativeMediaFileUtilTest, CopySourceFiltering) {
268   base::FilePath dest_path = root_path().AppendASCII("dest");
269   FileSystemURL dest_url = CreateURL(FPL("dest"));
270 
271   // Run the loop twice. The first run has no source files. The second run does.
272   for (int loop_count = 0; loop_count < 2; ++loop_count) {
273     if (loop_count == 1) {
274       PopulateDirectoryWithTestCases(root_path(), kFilteringTestCases,
275                                      base::size(kFilteringTestCases));
276     }
277     for (size_t i = 0; i < base::size(kFilteringTestCases); ++i) {
278       // Always start with an empty destination directory.
279       // Copying to a non-empty destination directory is an invalid operation.
280       ASSERT_TRUE(base::DeletePathRecursively(dest_path));
281       ASSERT_TRUE(base::CreateDirectory(dest_path));
282 
283       FileSystemURL root_url = CreateURL(FPL(""));
284       FileSystemURL url = CreateURL(kFilteringTestCases[i].path);
285 
286       std::string test_name = base::StringPrintf(
287           "CopySourceFiltering run %d test %" PRIuS, loop_count, i);
288       base::File::Error expectation = base::File::FILE_OK;
289       if (loop_count == 0 || !kFilteringTestCases[i].visible) {
290         // If the source does not exist or is not visible.
291         expectation = base::File::FILE_ERROR_NOT_FOUND;
292       } else if (!kFilteringTestCases[i].is_directory) {
293         // Cannot copy a visible file to a directory.
294         expectation = base::File::FILE_ERROR_INVALID_OPERATION;
295       }
296       operation_runner()->Copy(
297           url, dest_url, storage::FileSystemOperation::OPTION_NONE,
298           storage::FileSystemOperation::ERROR_BEHAVIOR_ABORT,
299           storage::FileSystemOperationRunner::CopyProgressCallback(),
300           base::BindOnce(&ExpectEqHelper, test_name, expectation));
301       content::RunAllTasksUntilIdle();
302     }
303   }
304 }
305 
TEST_F(NativeMediaFileUtilTest,CopyDestFiltering)306 TEST_F(NativeMediaFileUtilTest, CopyDestFiltering) {
307   // Run the loop twice. The first run has no destination files.
308   // The second run does.
309   for (int loop_count = 0; loop_count < 2; ++loop_count) {
310     if (loop_count == 1) {
311       // Reset the test directory between the two loops to remove old
312       // directories and create new ones that should pre-exist.
313       ASSERT_TRUE(base::DeletePathRecursively(root_path()));
314       ASSERT_TRUE(base::CreateDirectory(root_path()));
315       PopulateDirectoryWithTestCases(root_path(), kFilteringTestCases,
316                                      base::size(kFilteringTestCases));
317     }
318 
319     // Always create a dummy source data file.
320     base::FilePath src_path = root_path().AppendASCII("foo.jpg");
321     FileSystemURL src_url = CreateURL(FPL("foo.jpg"));
322     static const char kDummyData[] = "dummy";
323     ASSERT_TRUE(base::WriteFile(src_path, kDummyData));
324 
325     for (size_t i = 0; i < base::size(kFilteringTestCases); ++i) {
326       if (loop_count == 0 && kFilteringTestCases[i].is_directory) {
327         // These directories do not exist in this case, so Copy() will not
328         // treat them as directories. Thus invalidating these test cases.
329         continue;
330       }
331       FileSystemURL root_url = CreateURL(FPL(""));
332       FileSystemURL url = CreateURL(kFilteringTestCases[i].path);
333 
334       std::string test_name = base::StringPrintf(
335           "CopyDestFiltering run %d test %" PRIuS, loop_count, i);
336       base::File::Error expectation;
337       if (loop_count == 0) {
338         // The destination path is a file here. The directory case has been
339         // handled above.
340         // If the destination path does not exist and is not visible, then
341         // creating it would be a security violation.
342         expectation = kFilteringTestCases[i].visible
343                           ? base::File::FILE_OK
344                           : base::File::FILE_ERROR_SECURITY;
345       } else {
346         if (!kFilteringTestCases[i].visible) {
347           // If the destination path exist and is not visible, then to the copy
348           // operation, it looks like the file needs to be created, which is a
349           // security violation.
350           expectation = base::File::FILE_ERROR_SECURITY;
351         } else if (kFilteringTestCases[i].is_directory) {
352           // Cannot copy a file to a directory.
353           expectation = base::File::FILE_ERROR_INVALID_OPERATION;
354         } else {
355           // Copying from a file to a visible file that exists is ok.
356           expectation = base::File::FILE_OK;
357         }
358       }
359       operation_runner()->Copy(
360           src_url, url, storage::FileSystemOperation::OPTION_NONE,
361           storage::FileSystemOperation::ERROR_BEHAVIOR_ABORT,
362           storage::FileSystemOperationRunner::CopyProgressCallback(),
363           base::BindOnce(&ExpectEqHelper, test_name, expectation));
364       content::RunAllTasksUntilIdle();
365     }
366   }
367 }
368 
TEST_F(NativeMediaFileUtilTest,MoveSourceFiltering)369 TEST_F(NativeMediaFileUtilTest, MoveSourceFiltering) {
370   base::FilePath dest_path = root_path().AppendASCII("dest");
371   FileSystemURL dest_url = CreateURL(FPL("dest"));
372 
373   // Run the loop twice. The first run has no source files. The second run does.
374   for (int loop_count = 0; loop_count < 2; ++loop_count) {
375     if (loop_count == 1) {
376       PopulateDirectoryWithTestCases(root_path(), kFilteringTestCases,
377                                      base::size(kFilteringTestCases));
378     }
379     for (size_t i = 0; i < base::size(kFilteringTestCases); ++i) {
380       // Always start with an empty destination directory.
381       // Moving to a non-empty destination directory is an invalid operation.
382       ASSERT_TRUE(base::DeletePathRecursively(dest_path));
383       ASSERT_TRUE(base::CreateDirectory(dest_path));
384 
385       FileSystemURL root_url = CreateURL(FPL(""));
386       FileSystemURL url = CreateURL(kFilteringTestCases[i].path);
387 
388       std::string test_name = base::StringPrintf(
389           "MoveSourceFiltering run %d test %" PRIuS, loop_count, i);
390       base::File::Error expectation = base::File::FILE_OK;
391       if (loop_count == 0 || !kFilteringTestCases[i].visible) {
392         // If the source does not exist or is not visible.
393         expectation = base::File::FILE_ERROR_NOT_FOUND;
394       } else if (!kFilteringTestCases[i].is_directory) {
395         // Cannot move a visible file to a directory.
396         expectation = base::File::FILE_ERROR_INVALID_OPERATION;
397       }
398       operation_runner()->Move(
399           url, dest_url, storage::FileSystemOperation::OPTION_NONE,
400           base::BindOnce(&ExpectEqHelper, test_name, expectation));
401       content::RunAllTasksUntilIdle();
402     }
403   }
404 }
405 
TEST_F(NativeMediaFileUtilTest,MoveDestFiltering)406 TEST_F(NativeMediaFileUtilTest, MoveDestFiltering) {
407   // Run the loop twice. The first run has no destination files.
408   // The second run does.
409   for (int loop_count = 0; loop_count < 2; ++loop_count) {
410     if (loop_count == 1) {
411       // Reset the test directory between the two loops to remove old
412       // directories and create new ones that should pre-exist.
413       ASSERT_TRUE(base::DeletePathRecursively(root_path()));
414       ASSERT_TRUE(base::CreateDirectory(root_path()));
415       PopulateDirectoryWithTestCases(root_path(), kFilteringTestCases,
416                                      base::size(kFilteringTestCases));
417     }
418 
419     for (size_t i = 0; i < base::size(kFilteringTestCases); ++i) {
420       if (loop_count == 0 && kFilteringTestCases[i].is_directory) {
421         // These directories do not exist in this case, so Copy() will not
422         // treat them as directories. Thus invalidating these test cases.
423         continue;
424       }
425 
426       // Create the source file for every test case because it might get moved.
427       base::FilePath src_path = root_path().AppendASCII("foo.jpg");
428       FileSystemURL src_url = CreateURL(FPL("foo.jpg"));
429       static const char kDummyData[] = "dummy";
430       ASSERT_TRUE(base::WriteFile(src_path, kDummyData));
431 
432       FileSystemURL root_url = CreateURL(FPL(""));
433       FileSystemURL url = CreateURL(kFilteringTestCases[i].path);
434 
435       std::string test_name = base::StringPrintf(
436           "MoveDestFiltering run %d test %" PRIuS, loop_count, i);
437       base::File::Error expectation;
438       if (loop_count == 0) {
439         // The destination path is a file here. The directory case has been
440         // handled above.
441         // If the destination path does not exist and is not visible, then
442         // creating it would be a security violation.
443         expectation = kFilteringTestCases[i].visible
444                           ? base::File::FILE_OK
445                           : base::File::FILE_ERROR_SECURITY;
446       } else {
447         if (!kFilteringTestCases[i].visible) {
448           // If the destination path exist and is not visible, then to the move
449           // operation, it looks like the file needs to be created, which is a
450           // security violation.
451           expectation = base::File::FILE_ERROR_SECURITY;
452         } else if (kFilteringTestCases[i].is_directory) {
453           // Cannot move a file to a directory.
454           expectation = base::File::FILE_ERROR_INVALID_OPERATION;
455         } else {
456           // Moving from a file to a visible file that exists is ok.
457           expectation = base::File::FILE_OK;
458         }
459       }
460       operation_runner()->Move(
461           src_url, url, storage::FileSystemOperation::OPTION_NONE,
462           base::BindOnce(&ExpectEqHelper, test_name, expectation));
463       content::RunAllTasksUntilIdle();
464     }
465   }
466 }
467 
TEST_F(NativeMediaFileUtilTest,GetMetadataFiltering)468 TEST_F(NativeMediaFileUtilTest, GetMetadataFiltering) {
469   // Run the loop twice. The first run has no files. The second run does.
470   for (int loop_count = 0; loop_count < 2; ++loop_count) {
471     if (loop_count == 1) {
472       PopulateDirectoryWithTestCases(root_path(), kFilteringTestCases,
473                                      base::size(kFilteringTestCases));
474     }
475     for (size_t i = 0; i < base::size(kFilteringTestCases); ++i) {
476       FileSystemURL root_url = CreateURL(FPL(""));
477       FileSystemURL url = CreateURL(kFilteringTestCases[i].path);
478 
479       std::string test_name = base::StringPrintf(
480           "GetMetadataFiltering run %d test %" PRIuS, loop_count, i);
481       base::File::Error expectation = base::File::FILE_OK;
482       if (loop_count == 0 || !kFilteringTestCases[i].visible) {
483         // Cannot get metadata from files that do not exist or are not visible.
484         expectation = base::File::FILE_ERROR_NOT_FOUND;
485       }
486       operation_runner()->GetMetadata(
487           url, storage::FileSystemOperation::GET_METADATA_FIELD_IS_DIRECTORY,
488           base::BindOnce(&ExpectMetadataEqHelper, test_name, expectation,
489                          kFilteringTestCases[i].is_directory));
490       content::RunAllTasksUntilIdle();
491     }
492   }
493 }
494 
TEST_F(NativeMediaFileUtilTest,RemoveFileFiltering)495 TEST_F(NativeMediaFileUtilTest, RemoveFileFiltering) {
496   // Run the loop twice. The first run has no files. The second run does.
497   for (int loop_count = 0; loop_count < 2; ++loop_count) {
498     if (loop_count == 1) {
499       PopulateDirectoryWithTestCases(root_path(), kFilteringTestCases,
500                                      base::size(kFilteringTestCases));
501     }
502     for (size_t i = 0; i < base::size(kFilteringTestCases); ++i) {
503       FileSystemURL root_url = CreateURL(FPL(""));
504       FileSystemURL url = CreateURL(kFilteringTestCases[i].path);
505 
506       std::string test_name = base::StringPrintf(
507           "RemoveFiltering run %d test %" PRIuS, loop_count, i);
508       base::File::Error expectation = base::File::FILE_OK;
509       if (loop_count == 0 || !kFilteringTestCases[i].visible) {
510         // Cannot remove files that do not exist or are not visible.
511         expectation = base::File::FILE_ERROR_NOT_FOUND;
512       } else if (kFilteringTestCases[i].is_directory) {
513         expectation = base::File::FILE_ERROR_NOT_A_FILE;
514       }
515       operation_runner()->RemoveFile(
516           url, base::BindOnce(&ExpectEqHelper, test_name, expectation));
517       content::RunAllTasksUntilIdle();
518     }
519   }
520 }
521 
CreateSnapshotCallback(base::File::Error * error,base::File::Error result,const base::File::Info &,const base::FilePath &,scoped_refptr<storage::ShareableFileReference>)522 void CreateSnapshotCallback(base::File::Error* error,
523                             base::File::Error result,
524                             const base::File::Info&,
525                             const base::FilePath&,
526                             scoped_refptr<storage::ShareableFileReference>) {
527   *error = result;
528 }
529 
TEST_F(NativeMediaFileUtilTest,CreateSnapshot)530 TEST_F(NativeMediaFileUtilTest, CreateSnapshot) {
531   PopulateDirectoryWithTestCases(root_path(), kFilteringTestCases,
532                                  base::size(kFilteringTestCases));
533   for (size_t i = 0; i < base::size(kFilteringTestCases); ++i) {
534     if (kFilteringTestCases[i].is_directory ||
535         !kFilteringTestCases[i].visible) {
536       continue;
537     }
538     FileSystemURL root_url = CreateURL(FPL(""));
539     FileSystemURL url = CreateURL(kFilteringTestCases[i].path);
540     base::File::Error expected_error, error;
541     if (kFilteringTestCases[i].media_file)
542       expected_error = base::File::FILE_OK;
543     else
544       expected_error = base::File::FILE_ERROR_SECURITY;
545     error = base::File::FILE_ERROR_FAILED;
546     operation_runner()->CreateSnapshotFile(
547         url, base::BindOnce(CreateSnapshotCallback, &error));
548     content::RunAllTasksUntilIdle();
549     ASSERT_EQ(expected_error, error);
550   }
551 }
552