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/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/test_browser_thread.h"
19 #include "content/public/test/test_file_system_options.h"
20 #include "testing/gtest/include/gtest/gtest.h"
21 #include "webkit/browser/fileapi/external_mount_points.h"
22 #include "webkit/browser/fileapi/file_system_backend.h"
23 #include "webkit/browser/fileapi/file_system_context.h"
24 #include "webkit/browser/fileapi/file_system_operation_runner.h"
25 #include "webkit/browser/fileapi/file_system_url.h"
26 #include "webkit/browser/fileapi/isolated_context.h"
27 #include "webkit/browser/fileapi/native_file_util.h"
28 #include "webkit/browser/quota/mock_special_storage_policy.h"
30 #define FPL(x) FILE_PATH_LITERAL(x)
32 using fileapi::FileSystemOperation;
33 using fileapi::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::PlatformFileError expected,
69 base::PlatformFileError actual) {
70 EXPECT_EQ(expected, actual) << test_name;
73 void ExpectMetadataEqHelper(const std::string& test_name,
74 base::PlatformFileError expected,
75 bool expected_is_directory,
76 base::PlatformFileError actual,
77 const base::PlatformFileInfo& file_info) {
78 EXPECT_EQ(expected, actual) << test_name;
79 if (actual == base::PLATFORM_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::PlatformFileError 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(file_util::CreateDirectory(path));
103 ASSERT_TRUE(test_cases[i].content != NULL);
104 int len = strlen(test_cases[i].content);
105 ASSERT_EQ(len, file_util::WriteFile(path, test_cases[i].content, len));
112 class NativeMediaFileUtilTest : public testing::Test {
114 NativeMediaFileUtilTest()
115 : io_thread_(content::BrowserThread::IO, &message_loop_) {
118 virtual void SetUp() {
119 ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
120 ASSERT_TRUE(file_util::CreateDirectory(root_path()));
122 scoped_refptr<quota::SpecialStoragePolicy> storage_policy =
123 new quota::MockSpecialStoragePolicy();
125 ScopedVector<fileapi::FileSystemBackend> additional_providers;
126 additional_providers.push_back(new MediaFileSystemBackend(
127 data_dir_.path(), base::MessageLoopProxy::current().get()));
129 file_system_context_ = new fileapi::FileSystemContext(
130 base::MessageLoopProxy::current().get(),
131 base::MessageLoopProxy::current().get(),
132 fileapi::ExternalMountPoints::CreateRefCounted().get(),
133 storage_policy.get(),
135 additional_providers.Pass(),
137 fileapi::CreateAllowFileAccessOptions());
139 filesystem_id_ = isolated_context()->RegisterFileSystemForPath(
140 fileapi::kFileSystemTypeNativeMedia, root_path(), NULL);
142 isolated_context()->AddReference(filesystem_id_);
145 virtual void TearDown() {
146 isolated_context()->RemoveReference(filesystem_id_);
147 file_system_context_ = NULL;
151 fileapi::FileSystemContext* file_system_context() {
152 return file_system_context_.get();
155 FileSystemURL CreateURL(const base::FilePath::CharType* test_case_path) {
156 return file_system_context_->CreateCrackedFileSystemURL(
158 fileapi::kFileSystemTypeIsolated,
159 GetVirtualPath(test_case_path));
162 fileapi::IsolatedContext* isolated_context() {
163 return fileapi::IsolatedContext::GetInstance();
166 base::FilePath root_path() {
167 return data_dir_.path().Append(FPL("Media Directory"));
170 base::FilePath GetVirtualPath(
171 const base::FilePath::CharType* test_case_path) {
172 return base::FilePath::FromUTF8Unsafe(filesystem_id_).
173 Append(FPL("Media Directory")).
174 Append(base::FilePath(test_case_path));
178 return GURL("http://example.com");
181 fileapi::FileSystemType type() {
182 return fileapi::kFileSystemTypeNativeMedia;
185 fileapi::FileSystemOperationRunner* operation_runner() {
186 return file_system_context_->operation_runner();
190 base::MessageLoop message_loop_;
191 content::TestBrowserThread io_thread_;
193 base::ScopedTempDir data_dir_;
194 scoped_refptr<fileapi::FileSystemContext> file_system_context_;
196 std::string filesystem_id_;
198 DISALLOW_COPY_AND_ASSIGN(NativeMediaFileUtilTest);
201 TEST_F(NativeMediaFileUtilTest, DirectoryExistsAndFileExistsFiltering) {
202 PopulateDirectoryWithTestCases(root_path(),
204 arraysize(kFilteringTestCases));
206 for (size_t i = 0; i < arraysize(kFilteringTestCases); ++i) {
207 FileSystemURL url = CreateURL(kFilteringTestCases[i].path);
209 base::PlatformFileError expectation =
210 kFilteringTestCases[i].visible ?
211 base::PLATFORM_FILE_OK :
212 base::PLATFORM_FILE_ERROR_NOT_FOUND;
214 std::string test_name =
215 base::StringPrintf("DirectoryExistsAndFileExistsFiltering %" PRIuS, i);
216 if (kFilteringTestCases[i].is_directory) {
217 operation_runner()->DirectoryExists(
218 url, base::Bind(&ExpectEqHelper, test_name, expectation));
220 operation_runner()->FileExists(
221 url, base::Bind(&ExpectEqHelper, test_name, expectation));
223 base::MessageLoop::current()->RunUntilIdle();
227 TEST_F(NativeMediaFileUtilTest, ReadDirectoryFiltering) {
228 PopulateDirectoryWithTestCases(root_path(),
230 arraysize(kFilteringTestCases));
232 std::set<base::FilePath::StringType> content;
233 FileSystemURL url = CreateURL(FPL(""));
234 bool completed = false;
235 operation_runner()->ReadDirectory(
236 url, base::Bind(&DidReadDirectory, &content, &completed));
237 base::MessageLoop::current()->RunUntilIdle();
238 EXPECT_TRUE(completed);
239 EXPECT_EQ(6u, content.size());
241 for (size_t i = 0; i < arraysize(kFilteringTestCases); ++i) {
242 base::FilePath::StringType name =
243 base::FilePath(kFilteringTestCases[i].path).BaseName().value();
244 std::set<base::FilePath::StringType>::const_iterator found =
246 EXPECT_EQ(kFilteringTestCases[i].visible, found != content.end());
250 TEST_F(NativeMediaFileUtilTest, CreateDirectoryFiltering) {
251 // Run the loop twice. The second loop attempts to create directories that are
252 // pre-existing. Though the result should be the same.
253 for (int loop_count = 0; loop_count < 2; ++loop_count) {
254 for (size_t i = 0; i < arraysize(kFilteringTestCases); ++i) {
255 if (kFilteringTestCases[i].is_directory) {
256 FileSystemURL root_url = CreateURL(FPL(""));
257 FileSystemURL url = CreateURL(kFilteringTestCases[i].path);
259 std::string test_name = base::StringPrintf(
260 "CreateFileAndCreateDirectoryFiltering run %d, test %" PRIuS,
262 base::PlatformFileError expectation =
263 kFilteringTestCases[i].visible ?
264 base::PLATFORM_FILE_OK :
265 base::PLATFORM_FILE_ERROR_SECURITY;
266 operation_runner()->CreateDirectory(
268 base::Bind(&ExpectEqHelper, test_name, expectation));
270 base::MessageLoop::current()->RunUntilIdle();
275 TEST_F(NativeMediaFileUtilTest, CopySourceFiltering) {
276 base::FilePath dest_path = root_path().AppendASCII("dest");
277 FileSystemURL dest_url = CreateURL(FPL("dest"));
279 // Run the loop twice. The first run has no source files. The second run does.
280 for (int loop_count = 0; loop_count < 2; ++loop_count) {
281 if (loop_count == 1) {
282 PopulateDirectoryWithTestCases(root_path(),
284 arraysize(kFilteringTestCases));
286 for (size_t i = 0; i < arraysize(kFilteringTestCases); ++i) {
287 // Always start with an empty destination directory.
288 // Copying to a non-empty destination directory is an invalid operation.
289 ASSERT_TRUE(base::DeleteFile(dest_path, true));
290 ASSERT_TRUE(file_util::CreateDirectory(dest_path));
292 FileSystemURL root_url = CreateURL(FPL(""));
293 FileSystemURL url = CreateURL(kFilteringTestCases[i].path);
295 std::string test_name = base::StringPrintf(
296 "CopySourceFiltering run %d test %" PRIuS, loop_count, i);
297 base::PlatformFileError expectation = base::PLATFORM_FILE_OK;
298 if (loop_count == 0 || !kFilteringTestCases[i].visible) {
299 // If the source does not exist or is not visible.
300 expectation = base::PLATFORM_FILE_ERROR_NOT_FOUND;
301 } else if (!kFilteringTestCases[i].is_directory) {
302 // Cannot copy a visible file to a directory.
303 expectation = base::PLATFORM_FILE_ERROR_INVALID_OPERATION;
305 operation_runner()->Copy(
307 fileapi::FileSystemOperation::OPTION_NONE,
308 fileapi::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(file_util::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(file_util::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::PlatformFileError 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::PLATFORM_FILE_OK :
355 base::PLATFORM_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::PLATFORM_FILE_ERROR_SECURITY;
362 } else if (kFilteringTestCases[i].is_directory) {
363 // Cannot copy a file to a directory.
364 expectation = base::PLATFORM_FILE_ERROR_INVALID_OPERATION;
366 // Copying from a file to a visible file that exists is ok.
367 expectation = base::PLATFORM_FILE_OK;
370 operation_runner()->Copy(
372 fileapi::FileSystemOperation::OPTION_NONE,
373 fileapi::FileSystemOperationRunner::CopyProgressCallback(),
374 base::Bind(&ExpectEqHelper, test_name, expectation));
375 base::MessageLoop::current()->RunUntilIdle();
380 TEST_F(NativeMediaFileUtilTest, MoveSourceFiltering) {
381 base::FilePath dest_path = root_path().AppendASCII("dest");
382 FileSystemURL dest_url = CreateURL(FPL("dest"));
384 // Run the loop twice. The first run has no source files. The second run does.
385 for (int loop_count = 0; loop_count < 2; ++loop_count) {
386 if (loop_count == 1) {
387 PopulateDirectoryWithTestCases(root_path(),
389 arraysize(kFilteringTestCases));
391 for (size_t i = 0; i < arraysize(kFilteringTestCases); ++i) {
392 // Always start with an empty destination directory.
393 // Moving to a non-empty destination directory is an invalid operation.
394 ASSERT_TRUE(base::DeleteFile(dest_path, true));
395 ASSERT_TRUE(file_util::CreateDirectory(dest_path));
397 FileSystemURL root_url = CreateURL(FPL(""));
398 FileSystemURL url = CreateURL(kFilteringTestCases[i].path);
400 std::string test_name = base::StringPrintf(
401 "MoveSourceFiltering run %d test %" PRIuS, loop_count, i);
402 base::PlatformFileError expectation = base::PLATFORM_FILE_OK;
403 if (loop_count == 0 || !kFilteringTestCases[i].visible) {
404 // If the source does not exist or is not visible.
405 expectation = base::PLATFORM_FILE_ERROR_NOT_FOUND;
406 } else if (!kFilteringTestCases[i].is_directory) {
407 // Cannot move a visible file to a directory.
408 expectation = base::PLATFORM_FILE_ERROR_INVALID_OPERATION;
410 operation_runner()->Move(
411 url, dest_url, fileapi::FileSystemOperation::OPTION_NONE,
412 base::Bind(&ExpectEqHelper, test_name, expectation));
413 base::MessageLoop::current()->RunUntilIdle();
418 TEST_F(NativeMediaFileUtilTest, MoveDestFiltering) {
419 // Run the loop twice. The first run has no destination files.
420 // The second run does.
421 for (int loop_count = 0; loop_count < 2; ++loop_count) {
422 if (loop_count == 1) {
423 // Reset the test directory between the two loops to remove old
424 // directories and create new ones that should pre-exist.
425 ASSERT_TRUE(base::DeleteFile(root_path(), true));
426 ASSERT_TRUE(file_util::CreateDirectory(root_path()));
427 PopulateDirectoryWithTestCases(root_path(),
429 arraysize(kFilteringTestCases));
432 for (size_t i = 0; i < arraysize(kFilteringTestCases); ++i) {
433 if (loop_count == 0 && kFilteringTestCases[i].is_directory) {
434 // These directories do not exist in this case, so Copy() will not
435 // treat them as directories. Thus invalidating these test cases.
439 // Create the source file for every test case because it might get moved.
440 base::FilePath src_path = root_path().AppendASCII("foo.jpg");
441 FileSystemURL src_url = CreateURL(FPL("foo.jpg"));
442 static const char kDummyData[] = "dummy";
444 file_util::WriteFile(src_path, kDummyData, strlen(kDummyData)));
446 FileSystemURL root_url = CreateURL(FPL(""));
447 FileSystemURL url = CreateURL(kFilteringTestCases[i].path);
449 std::string test_name = base::StringPrintf(
450 "MoveDestFiltering run %d test %" PRIuS, loop_count, i);
451 base::PlatformFileError expectation;
452 if (loop_count == 0) {
453 // The destination path is a file here. The directory case has been
455 // If the destination path does not exist and is not visible, then
456 // creating it would be a security violation.
458 kFilteringTestCases[i].visible ?
459 base::PLATFORM_FILE_OK :
460 base::PLATFORM_FILE_ERROR_SECURITY;
462 if (!kFilteringTestCases[i].visible) {
463 // If the destination path exist and is not visible, then to the move
464 // operation, it looks like the file needs to be created, which is a
465 // security violation.
466 expectation = base::PLATFORM_FILE_ERROR_SECURITY;
467 } else if (kFilteringTestCases[i].is_directory) {
468 // Cannot move a file to a directory.
469 expectation = base::PLATFORM_FILE_ERROR_INVALID_OPERATION;
471 // Moving from a file to a visible file that exists is ok.
472 expectation = base::PLATFORM_FILE_OK;
475 operation_runner()->Move(
476 src_url, url, fileapi::FileSystemOperation::OPTION_NONE,
477 base::Bind(&ExpectEqHelper, test_name, expectation));
478 base::MessageLoop::current()->RunUntilIdle();
483 TEST_F(NativeMediaFileUtilTest, GetMetadataFiltering) {
484 // Run the loop twice. The first run has no files. The second run does.
485 for (int loop_count = 0; loop_count < 2; ++loop_count) {
486 if (loop_count == 1) {
487 PopulateDirectoryWithTestCases(root_path(),
489 arraysize(kFilteringTestCases));
491 for (size_t i = 0; i < arraysize(kFilteringTestCases); ++i) {
492 FileSystemURL root_url = CreateURL(FPL(""));
493 FileSystemURL url = CreateURL(kFilteringTestCases[i].path);
495 std::string test_name = base::StringPrintf(
496 "GetMetadataFiltering run %d test %" PRIuS, loop_count, i);
497 base::PlatformFileError expectation = base::PLATFORM_FILE_OK;
498 if (loop_count == 0 || !kFilteringTestCases[i].visible) {
499 // Cannot get metadata from files that do not exist or are not visible.
500 expectation = base::PLATFORM_FILE_ERROR_NOT_FOUND;
502 operation_runner()->GetMetadata(
504 base::Bind(&ExpectMetadataEqHelper,
507 kFilteringTestCases[i].is_directory));
508 base::MessageLoop::current()->RunUntilIdle();
513 void CreateSnapshotCallback(base::PlatformFileError* error,
514 base::PlatformFileError result, const base::PlatformFileInfo&,
515 const base::FilePath&,
516 const scoped_refptr<webkit_blob::ShareableFileReference>&) {
520 TEST_F(NativeMediaFileUtilTest, CreateSnapshot) {
521 PopulateDirectoryWithTestCases(root_path(),
523 arraysize(kFilteringTestCases));
524 for (size_t i = 0; i < arraysize(kFilteringTestCases); ++i) {
525 if (kFilteringTestCases[i].is_directory ||
526 !kFilteringTestCases[i].visible) {
529 FileSystemURL root_url = CreateURL(FPL(""));
530 FileSystemURL url = CreateURL(kFilteringTestCases[i].path);
531 base::PlatformFileError expected_error, error;
532 if (kFilteringTestCases[i].media_file)
533 expected_error = base::PLATFORM_FILE_OK;
535 expected_error = base::PLATFORM_FILE_ERROR_SECURITY;
536 error = base::PLATFORM_FILE_ERROR_FAILED;
537 operation_runner()->CreateSnapshotFile(url,
538 base::Bind(CreateSnapshotCallback, &error));
539 base::MessageLoop::current()->RunUntilIdle();
540 ASSERT_EQ(expected_error, error);