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