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.
9 #include "base/files/file_util.h"
10 #include "base/files/scoped_temp_dir.h"
11 #include "base/format_macros.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/message_loop/message_loop_proxy.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/time/time.h"
16 #include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h"
17 #include "chrome/browser/media_galleries/fileapi/native_media_file_util.h"
18 #include "content/public/test/mock_special_storage_policy.h"
19 #include "content/public/test/test_browser_thread.h"
20 #include "content/public/test/test_file_system_options.h"
21 #include "storage/browser/fileapi/external_mount_points.h"
22 #include "storage/browser/fileapi/file_system_backend.h"
23 #include "storage/browser/fileapi/file_system_context.h"
24 #include "storage/browser/fileapi/file_system_operation_runner.h"
25 #include "storage/browser/fileapi/file_system_url.h"
26 #include "storage/browser/fileapi/isolated_context.h"
27 #include "storage/browser/fileapi/native_file_util.h"
28 #include "testing/gtest/include/gtest/gtest.h"
30 #define FPL(x) FILE_PATH_LITERAL(x)
32 using storage::FileSystemOperation
;
33 using storage::FileSystemURL
;
37 typedef FileSystemOperation::FileEntryList FileEntryList
;
39 struct FilteringTestCase
{
40 const base::FilePath::CharType
* path
;
47 const FilteringTestCase kFilteringTestCases
[] = {
48 // Directory should always be visible.
49 { FPL("hoge"), true, true, false, NULL
},
50 { FPL("fuga.jpg"), true, true, false, NULL
},
51 { FPL("piyo.txt"), true, true, false, NULL
},
52 { FPL("moga.cod"), true, true, false, NULL
},
54 // File should be visible if it's a supported media file.
55 // File without extension.
56 { FPL("foo"), false, false, false, "abc" },
57 // Supported media file.
58 { FPL("bar.jpg"), false, true, true, "\xFF\xD8\xFF" },
59 // Unsupported masquerading file.
60 { FPL("sna.jpg"), false, true, false, "abc" },
62 { FPL("baz.txt"), false, false, false, "abc" },
63 // Unsupported media file.
64 { FPL("foobar.cod"), false, false, false, "abc" },
67 void ExpectEqHelper(const std::string
& test_name
,
68 base::File::Error expected
,
69 base::File::Error actual
) {
70 EXPECT_EQ(expected
, actual
) << test_name
;
73 void ExpectMetadataEqHelper(const std::string
& test_name
,
74 base::File::Error expected
,
75 bool expected_is_directory
,
76 base::File::Error actual
,
77 const base::File::Info
& file_info
) {
78 EXPECT_EQ(expected
, actual
) << test_name
;
79 if (actual
== base::File::FILE_OK
)
80 EXPECT_EQ(expected_is_directory
, file_info
.is_directory
) << test_name
;
83 void DidReadDirectory(std::set
<base::FilePath::StringType
>* content
,
85 base::File::Error error
,
86 const FileEntryList
& file_list
,
88 EXPECT_TRUE(!*completed
);
89 *completed
= !has_more
;
90 for (FileEntryList::const_iterator itr
= file_list
.begin();
91 itr
!= file_list
.end(); ++itr
)
92 EXPECT_TRUE(content
->insert(itr
->name
).second
);
95 void PopulateDirectoryWithTestCases(const base::FilePath
& dir
,
96 const FilteringTestCase
* test_cases
,
98 for (size_t i
= 0; i
< n
; ++i
) {
99 base::FilePath path
= dir
.Append(test_cases
[i
].path
);
100 if (test_cases
[i
].is_directory
) {
101 ASSERT_TRUE(base::CreateDirectory(path
));
103 ASSERT_TRUE(test_cases
[i
].content
!= NULL
);
104 int len
= strlen(test_cases
[i
].content
);
105 ASSERT_EQ(len
, base::WriteFile(path
, test_cases
[i
].content
, len
));
112 class NativeMediaFileUtilTest
: public testing::Test
{
114 NativeMediaFileUtilTest()
115 : io_thread_(content::BrowserThread::IO
, &message_loop_
) {
118 void SetUp() override
{
119 ASSERT_TRUE(data_dir_
.CreateUniqueTempDir());
120 ASSERT_TRUE(base::CreateDirectory(root_path()));
122 scoped_refptr
<storage::SpecialStoragePolicy
> storage_policy
=
123 new content::MockSpecialStoragePolicy();
125 ScopedVector
<storage::FileSystemBackend
> additional_providers
;
126 additional_providers
.push_back(new MediaFileSystemBackend(
127 data_dir_
.path(), base::MessageLoopProxy::current().get()));
129 file_system_context_
= new storage::FileSystemContext(
130 base::MessageLoopProxy::current().get(),
131 base::MessageLoopProxy::current().get(),
132 storage::ExternalMountPoints::CreateRefCounted().get(),
133 storage_policy
.get(),
135 additional_providers
.Pass(),
136 std::vector
<storage::URLRequestAutoMountHandler
>(),
138 content::CreateAllowFileAccessOptions());
140 filesystem_id_
= isolated_context()->RegisterFileSystemForPath(
141 storage::kFileSystemTypeNativeMedia
, std::string(), root_path(), NULL
);
143 isolated_context()->AddReference(filesystem_id_
);
146 void TearDown() override
{
147 isolated_context()->RemoveReference(filesystem_id_
);
148 file_system_context_
= NULL
;
152 storage::FileSystemContext
* file_system_context() {
153 return file_system_context_
.get();
156 FileSystemURL
CreateURL(const base::FilePath::CharType
* test_case_path
) {
157 return file_system_context_
->CreateCrackedFileSystemURL(
159 storage::kFileSystemTypeIsolated
,
160 GetVirtualPath(test_case_path
));
163 storage::IsolatedContext
* isolated_context() {
164 return storage::IsolatedContext::GetInstance();
167 base::FilePath
root_path() {
168 return data_dir_
.path().Append(FPL("Media Directory"));
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
));
179 return GURL("http://example.com");
182 storage::FileSystemType
type() { return storage::kFileSystemTypeNativeMedia
; }
184 storage::FileSystemOperationRunner
* operation_runner() {
185 return file_system_context_
->operation_runner();
189 base::MessageLoop message_loop_
;
190 content::TestBrowserThread io_thread_
;
192 base::ScopedTempDir data_dir_
;
193 scoped_refptr
<storage::FileSystemContext
> file_system_context_
;
195 std::string filesystem_id_
;
197 DISALLOW_COPY_AND_ASSIGN(NativeMediaFileUtilTest
);
200 TEST_F(NativeMediaFileUtilTest
, DirectoryExistsAndFileExistsFiltering
) {
201 PopulateDirectoryWithTestCases(root_path(),
203 arraysize(kFilteringTestCases
));
205 for (size_t i
= 0; i
< arraysize(kFilteringTestCases
); ++i
) {
206 FileSystemURL url
= CreateURL(kFilteringTestCases
[i
].path
);
208 base::File::Error expectation
=
209 kFilteringTestCases
[i
].visible
?
210 base::File::FILE_OK
:
211 base::File::FILE_ERROR_NOT_FOUND
;
213 std::string test_name
=
214 base::StringPrintf("DirectoryExistsAndFileExistsFiltering %" PRIuS
, i
);
215 if (kFilteringTestCases
[i
].is_directory
) {
216 operation_runner()->DirectoryExists(
217 url
, base::Bind(&ExpectEqHelper
, test_name
, expectation
));
219 operation_runner()->FileExists(
220 url
, base::Bind(&ExpectEqHelper
, test_name
, expectation
));
222 base::MessageLoop::current()->RunUntilIdle();
226 TEST_F(NativeMediaFileUtilTest
, ReadDirectoryFiltering
) {
227 PopulateDirectoryWithTestCases(root_path(),
229 arraysize(kFilteringTestCases
));
231 std::set
<base::FilePath::StringType
> content
;
232 FileSystemURL url
= CreateURL(FPL(""));
233 bool completed
= false;
234 operation_runner()->ReadDirectory(
235 url
, base::Bind(&DidReadDirectory
, &content
, &completed
));
236 base::MessageLoop::current()->RunUntilIdle();
237 EXPECT_TRUE(completed
);
238 EXPECT_EQ(6u, content
.size());
240 for (size_t i
= 0; i
< arraysize(kFilteringTestCases
); ++i
) {
241 base::FilePath::StringType name
=
242 base::FilePath(kFilteringTestCases
[i
].path
).BaseName().value();
243 std::set
<base::FilePath::StringType
>::const_iterator found
=
245 EXPECT_EQ(kFilteringTestCases
[i
].visible
, found
!= content
.end());
249 TEST_F(NativeMediaFileUtilTest
, CreateDirectoryFiltering
) {
250 // Run the loop twice. The second loop attempts to create directories that are
251 // pre-existing. Though the result should be the same.
252 for (int loop_count
= 0; loop_count
< 2; ++loop_count
) {
253 for (size_t i
= 0; i
< arraysize(kFilteringTestCases
); ++i
) {
254 if (kFilteringTestCases
[i
].is_directory
) {
255 FileSystemURL root_url
= CreateURL(FPL(""));
256 FileSystemURL url
= CreateURL(kFilteringTestCases
[i
].path
);
258 std::string test_name
= base::StringPrintf(
259 "CreateFileAndCreateDirectoryFiltering run %d, test %" PRIuS
,
261 base::File::Error expectation
=
262 kFilteringTestCases
[i
].visible
?
263 base::File::FILE_OK
:
264 base::File::FILE_ERROR_SECURITY
;
265 operation_runner()->CreateDirectory(
267 base::Bind(&ExpectEqHelper
, test_name
, expectation
));
269 base::MessageLoop::current()->RunUntilIdle();
274 TEST_F(NativeMediaFileUtilTest
, CopySourceFiltering
) {
275 base::FilePath dest_path
= root_path().AppendASCII("dest");
276 FileSystemURL dest_url
= CreateURL(FPL("dest"));
278 // Run the loop twice. The first run has no source files. The second run does.
279 for (int loop_count
= 0; loop_count
< 2; ++loop_count
) {
280 if (loop_count
== 1) {
281 PopulateDirectoryWithTestCases(root_path(),
283 arraysize(kFilteringTestCases
));
285 for (size_t i
= 0; i
< arraysize(kFilteringTestCases
); ++i
) {
286 // Always start with an empty destination directory.
287 // Copying to a non-empty destination directory is an invalid operation.
288 ASSERT_TRUE(base::DeleteFile(dest_path
, true));
289 ASSERT_TRUE(base::CreateDirectory(dest_path
));
291 FileSystemURL root_url
= CreateURL(FPL(""));
292 FileSystemURL url
= CreateURL(kFilteringTestCases
[i
].path
);
294 std::string test_name
= base::StringPrintf(
295 "CopySourceFiltering run %d test %" PRIuS
, loop_count
, i
);
296 base::File::Error expectation
= base::File::FILE_OK
;
297 if (loop_count
== 0 || !kFilteringTestCases
[i
].visible
) {
298 // If the source does not exist or is not visible.
299 expectation
= base::File::FILE_ERROR_NOT_FOUND
;
300 } else if (!kFilteringTestCases
[i
].is_directory
) {
301 // Cannot copy a visible file to a directory.
302 expectation
= base::File::FILE_ERROR_INVALID_OPERATION
;
304 operation_runner()->Copy(
307 storage::FileSystemOperation::OPTION_NONE
,
308 storage::FileSystemOperationRunner::CopyProgressCallback(),
309 base::Bind(&ExpectEqHelper
, test_name
, expectation
));
310 base::MessageLoop::current()->RunUntilIdle();
315 TEST_F(NativeMediaFileUtilTest
, CopyDestFiltering
) {
316 // Run the loop twice. The first run has no destination files.
317 // The second run does.
318 for (int loop_count
= 0; loop_count
< 2; ++loop_count
) {
319 if (loop_count
== 1) {
320 // Reset the test directory between the two loops to remove old
321 // directories and create new ones that should pre-exist.
322 ASSERT_TRUE(base::DeleteFile(root_path(), true));
323 ASSERT_TRUE(base::CreateDirectory(root_path()));
324 PopulateDirectoryWithTestCases(root_path(),
326 arraysize(kFilteringTestCases
));
329 // Always create a dummy source data file.
330 base::FilePath src_path
= root_path().AppendASCII("foo.jpg");
331 FileSystemURL src_url
= CreateURL(FPL("foo.jpg"));
332 static const char kDummyData
[] = "dummy";
333 ASSERT_TRUE(base::WriteFile(src_path
, kDummyData
, strlen(kDummyData
)));
335 for (size_t i
= 0; i
< arraysize(kFilteringTestCases
); ++i
) {
336 if (loop_count
== 0 && kFilteringTestCases
[i
].is_directory
) {
337 // These directories do not exist in this case, so Copy() will not
338 // treat them as directories. Thus invalidating these test cases.
341 FileSystemURL root_url
= CreateURL(FPL(""));
342 FileSystemURL url
= CreateURL(kFilteringTestCases
[i
].path
);
344 std::string test_name
= base::StringPrintf(
345 "CopyDestFiltering run %d test %" PRIuS
, loop_count
, i
);
346 base::File::Error expectation
;
347 if (loop_count
== 0) {
348 // The destination path is a file here. The directory case has been
350 // If the destination path does not exist and is not visible, then
351 // creating it would be a security violation.
353 kFilteringTestCases
[i
].visible
?
354 base::File::FILE_OK
:
355 base::File::FILE_ERROR_SECURITY
;
357 if (!kFilteringTestCases
[i
].visible
) {
358 // If the destination path exist and is not visible, then to the copy
359 // operation, it looks like the file needs to be created, which is a
360 // security violation.
361 expectation
= base::File::FILE_ERROR_SECURITY
;
362 } else if (kFilteringTestCases
[i
].is_directory
) {
363 // Cannot copy a file to a directory.
364 expectation
= base::File::FILE_ERROR_INVALID_OPERATION
;
366 // Copying from a file to a visible file that exists is ok.
367 expectation
= base::File::FILE_OK
;
370 operation_runner()->Copy(
373 storage::FileSystemOperation::OPTION_NONE
,
374 storage::FileSystemOperationRunner::CopyProgressCallback(),
375 base::Bind(&ExpectEqHelper
, test_name
, expectation
));
376 base::MessageLoop::current()->RunUntilIdle();
381 TEST_F(NativeMediaFileUtilTest
, MoveSourceFiltering
) {
382 base::FilePath dest_path
= root_path().AppendASCII("dest");
383 FileSystemURL dest_url
= CreateURL(FPL("dest"));
385 // Run the loop twice. The first run has no source files. The second run does.
386 for (int loop_count
= 0; loop_count
< 2; ++loop_count
) {
387 if (loop_count
== 1) {
388 PopulateDirectoryWithTestCases(root_path(),
390 arraysize(kFilteringTestCases
));
392 for (size_t i
= 0; i
< arraysize(kFilteringTestCases
); ++i
) {
393 // Always start with an empty destination directory.
394 // Moving to a non-empty destination directory is an invalid operation.
395 ASSERT_TRUE(base::DeleteFile(dest_path
, true));
396 ASSERT_TRUE(base::CreateDirectory(dest_path
));
398 FileSystemURL root_url
= CreateURL(FPL(""));
399 FileSystemURL url
= CreateURL(kFilteringTestCases
[i
].path
);
401 std::string test_name
= base::StringPrintf(
402 "MoveSourceFiltering run %d test %" PRIuS
, loop_count
, i
);
403 base::File::Error expectation
= base::File::FILE_OK
;
404 if (loop_count
== 0 || !kFilteringTestCases
[i
].visible
) {
405 // If the source does not exist or is not visible.
406 expectation
= base::File::FILE_ERROR_NOT_FOUND
;
407 } else if (!kFilteringTestCases
[i
].is_directory
) {
408 // Cannot move a visible file to a directory.
409 expectation
= base::File::FILE_ERROR_INVALID_OPERATION
;
411 operation_runner()->Move(
414 storage::FileSystemOperation::OPTION_NONE
,
415 base::Bind(&ExpectEqHelper
, test_name
, expectation
));
416 base::MessageLoop::current()->RunUntilIdle();
421 TEST_F(NativeMediaFileUtilTest
, MoveDestFiltering
) {
422 // Run the loop twice. The first run has no destination files.
423 // The second run does.
424 for (int loop_count
= 0; loop_count
< 2; ++loop_count
) {
425 if (loop_count
== 1) {
426 // Reset the test directory between the two loops to remove old
427 // directories and create new ones that should pre-exist.
428 ASSERT_TRUE(base::DeleteFile(root_path(), true));
429 ASSERT_TRUE(base::CreateDirectory(root_path()));
430 PopulateDirectoryWithTestCases(root_path(),
432 arraysize(kFilteringTestCases
));
435 for (size_t i
= 0; i
< arraysize(kFilteringTestCases
); ++i
) {
436 if (loop_count
== 0 && kFilteringTestCases
[i
].is_directory
) {
437 // These directories do not exist in this case, so Copy() will not
438 // treat them as directories. Thus invalidating these test cases.
442 // Create the source file for every test case because it might get moved.
443 base::FilePath src_path
= root_path().AppendASCII("foo.jpg");
444 FileSystemURL src_url
= CreateURL(FPL("foo.jpg"));
445 static const char kDummyData
[] = "dummy";
447 base::WriteFile(src_path
, kDummyData
, strlen(kDummyData
)));
449 FileSystemURL root_url
= CreateURL(FPL(""));
450 FileSystemURL url
= CreateURL(kFilteringTestCases
[i
].path
);
452 std::string test_name
= base::StringPrintf(
453 "MoveDestFiltering run %d test %" PRIuS
, loop_count
, i
);
454 base::File::Error expectation
;
455 if (loop_count
== 0) {
456 // The destination path is a file here. The directory case has been
458 // If the destination path does not exist and is not visible, then
459 // creating it would be a security violation.
461 kFilteringTestCases
[i
].visible
?
462 base::File::FILE_OK
:
463 base::File::FILE_ERROR_SECURITY
;
465 if (!kFilteringTestCases
[i
].visible
) {
466 // If the destination path exist and is not visible, then to the move
467 // operation, it looks like the file needs to be created, which is a
468 // security violation.
469 expectation
= base::File::FILE_ERROR_SECURITY
;
470 } else if (kFilteringTestCases
[i
].is_directory
) {
471 // Cannot move a file to a directory.
472 expectation
= base::File::FILE_ERROR_INVALID_OPERATION
;
474 // Moving from a file to a visible file that exists is ok.
475 expectation
= base::File::FILE_OK
;
478 operation_runner()->Move(
481 storage::FileSystemOperation::OPTION_NONE
,
482 base::Bind(&ExpectEqHelper
, test_name
, expectation
));
483 base::MessageLoop::current()->RunUntilIdle();
488 TEST_F(NativeMediaFileUtilTest
, GetMetadataFiltering
) {
489 // Run the loop twice. The first run has no files. The second run does.
490 for (int loop_count
= 0; loop_count
< 2; ++loop_count
) {
491 if (loop_count
== 1) {
492 PopulateDirectoryWithTestCases(root_path(),
494 arraysize(kFilteringTestCases
));
496 for (size_t i
= 0; i
< arraysize(kFilteringTestCases
); ++i
) {
497 FileSystemURL root_url
= CreateURL(FPL(""));
498 FileSystemURL url
= CreateURL(kFilteringTestCases
[i
].path
);
500 std::string test_name
= base::StringPrintf(
501 "GetMetadataFiltering run %d test %" PRIuS
, loop_count
, i
);
502 base::File::Error expectation
= base::File::FILE_OK
;
503 if (loop_count
== 0 || !kFilteringTestCases
[i
].visible
) {
504 // Cannot get metadata from files that do not exist or are not visible.
505 expectation
= base::File::FILE_ERROR_NOT_FOUND
;
507 operation_runner()->GetMetadata(
509 base::Bind(&ExpectMetadataEqHelper
,
512 kFilteringTestCases
[i
].is_directory
));
513 base::MessageLoop::current()->RunUntilIdle();
518 TEST_F(NativeMediaFileUtilTest
, RemoveFileFiltering
) {
519 // Run the loop twice. The first run has no files. The second run does.
520 for (int loop_count
= 0; loop_count
< 2; ++loop_count
) {
521 if (loop_count
== 1) {
522 PopulateDirectoryWithTestCases(root_path(),
524 arraysize(kFilteringTestCases
));
526 for (size_t i
= 0; i
< arraysize(kFilteringTestCases
); ++i
) {
527 FileSystemURL root_url
= CreateURL(FPL(""));
528 FileSystemURL url
= CreateURL(kFilteringTestCases
[i
].path
);
530 std::string test_name
= base::StringPrintf(
531 "RemoveFiltering run %d test %" PRIuS
, loop_count
, i
);
532 base::File::Error expectation
= base::File::FILE_OK
;
533 if (loop_count
== 0 || !kFilteringTestCases
[i
].visible
) {
534 // Cannot remove files that do not exist or are not visible.
535 expectation
= base::File::FILE_ERROR_NOT_FOUND
;
536 } else if (kFilteringTestCases
[i
].is_directory
) {
537 expectation
= base::File::FILE_ERROR_NOT_A_FILE
;
539 operation_runner()->RemoveFile(
540 url
, base::Bind(&ExpectEqHelper
, test_name
, expectation
));
541 base::MessageLoop::current()->RunUntilIdle();
546 void CreateSnapshotCallback(
547 base::File::Error
* error
,
548 base::File::Error result
,
549 const base::File::Info
&,
550 const base::FilePath
&,
551 const scoped_refptr
<storage::ShareableFileReference
>&) {
555 TEST_F(NativeMediaFileUtilTest
, CreateSnapshot
) {
556 PopulateDirectoryWithTestCases(root_path(),
558 arraysize(kFilteringTestCases
));
559 for (size_t i
= 0; i
< arraysize(kFilteringTestCases
); ++i
) {
560 if (kFilteringTestCases
[i
].is_directory
||
561 !kFilteringTestCases
[i
].visible
) {
564 FileSystemURL root_url
= CreateURL(FPL(""));
565 FileSystemURL url
= CreateURL(kFilteringTestCases
[i
].path
);
566 base::File::Error expected_error
, error
;
567 if (kFilteringTestCases
[i
].media_file
)
568 expected_error
= base::File::FILE_OK
;
570 expected_error
= base::File::FILE_ERROR_SECURITY
;
571 error
= base::File::FILE_ERROR_FAILED
;
572 operation_runner()->CreateSnapshotFile(url
,
573 base::Bind(CreateSnapshotCallback
, &error
));
574 base::MessageLoop::current()->RunUntilIdle();
575 ASSERT_EQ(expected_error
, error
);