| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <set> |
| #include <string> |
| |
| #include "base/bind.h" |
| #include "base/file_util.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/format_macros.h" |
| #include "base/message_loop/message_loop.h" |
| #include "base/message_loop/message_loop_proxy.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/time/time.h" |
| #include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h" |
| #include "chrome/browser/media_galleries/fileapi/native_media_file_util.h" |
| #include "content/public/test/test_browser_thread.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "webkit/browser/fileapi/external_mount_points.h" |
| #include "webkit/browser/fileapi/file_system_backend.h" |
| #include "webkit/browser/fileapi/file_system_context.h" |
| #include "webkit/browser/fileapi/file_system_operation_runner.h" |
| #include "webkit/browser/fileapi/file_system_url.h" |
| #include "webkit/browser/fileapi/isolated_context.h" |
| #include "webkit/browser/fileapi/mock_file_system_options.h" |
| #include "webkit/browser/fileapi/native_file_util.h" |
| #include "webkit/browser/quota/mock_special_storage_policy.h" |
| |
| #define FPL(x) FILE_PATH_LITERAL(x) |
| |
| using fileapi::FileSystemOperation; |
| using fileapi::FileSystemURL; |
| |
| namespace chrome { |
| |
| namespace { |
| |
| typedef FileSystemOperation::FileEntryList FileEntryList; |
| |
| struct FilteringTestCase { |
| const base::FilePath::CharType* path; |
| bool is_directory; |
| bool visible; |
| bool media_file; |
| const char* content; |
| }; |
| |
| const FilteringTestCase kFilteringTestCases[] = { |
| // Directory should always be visible. |
| { FPL("hoge"), true, true, false, NULL }, |
| { FPL("fuga.jpg"), true, true, false, NULL }, |
| { FPL("piyo.txt"), true, true, false, NULL }, |
| { FPL("moga.cod"), true, true, false, NULL }, |
| |
| // File should be visible if it's a supported media file. |
| // File without extension. |
| { FPL("foo"), false, false, false, "abc" }, |
| // Supported media file. |
| { FPL("bar.jpg"), false, true, true, "\xFF\xD8\xFF" }, |
| // Unsupported masquerading file. |
| { FPL("sna.jpg"), false, true, false, "abc" }, |
| // Non-media file. |
| { FPL("baz.txt"), false, false, false, "abc" }, |
| // Unsupported media file. |
| { FPL("foobar.cod"), false, false, false, "abc" }, |
| }; |
| |
| void ExpectEqHelper(const std::string& test_name, |
| base::PlatformFileError expected, |
| base::PlatformFileError actual) { |
| EXPECT_EQ(expected, actual) << test_name; |
| } |
| |
| void ExpectMetadataEqHelper(const std::string& test_name, |
| base::PlatformFileError expected, |
| bool expected_is_directory, |
| base::PlatformFileError actual, |
| const base::PlatformFileInfo& file_info) { |
| EXPECT_EQ(expected, actual) << test_name; |
| if (actual == base::PLATFORM_FILE_OK) |
| EXPECT_EQ(expected_is_directory, file_info.is_directory) << test_name; |
| } |
| |
| void DidReadDirectory(std::set<base::FilePath::StringType>* content, |
| bool* completed, |
| base::PlatformFileError error, |
| const FileEntryList& file_list, |
| bool has_more) { |
| EXPECT_TRUE(!*completed); |
| *completed = !has_more; |
| for (FileEntryList::const_iterator itr = file_list.begin(); |
| itr != file_list.end(); ++itr) |
| EXPECT_TRUE(content->insert(itr->name).second); |
| } |
| |
| void PopulateDirectoryWithTestCases(const base::FilePath& dir, |
| const FilteringTestCase* test_cases, |
| size_t n) { |
| for (size_t i = 0; i < n; ++i) { |
| base::FilePath path = dir.Append(test_cases[i].path); |
| if (test_cases[i].is_directory) { |
| ASSERT_TRUE(file_util::CreateDirectory(path)); |
| } else { |
| ASSERT_TRUE(test_cases[i].content != NULL); |
| int len = strlen(test_cases[i].content); |
| ASSERT_EQ(len, file_util::WriteFile(path, test_cases[i].content, len)); |
| } |
| } |
| } |
| |
| } // namespace |
| |
| class NativeMediaFileUtilTest : public testing::Test { |
| public: |
| NativeMediaFileUtilTest() |
| : io_thread_(content::BrowserThread::IO, &message_loop_) { |
| } |
| |
| virtual void SetUp() { |
| ASSERT_TRUE(data_dir_.CreateUniqueTempDir()); |
| ASSERT_TRUE(file_util::CreateDirectory(root_path())); |
| |
| scoped_refptr<quota::SpecialStoragePolicy> storage_policy = |
| new quota::MockSpecialStoragePolicy(); |
| |
| ScopedVector<fileapi::FileSystemBackend> additional_providers; |
| additional_providers.push_back(new MediaFileSystemBackend( |
| data_dir_.path(), base::MessageLoopProxy::current().get())); |
| |
| file_system_context_ = new fileapi::FileSystemContext( |
| base::MessageLoopProxy::current().get(), |
| base::MessageLoopProxy::current().get(), |
| fileapi::ExternalMountPoints::CreateRefCounted().get(), |
| storage_policy.get(), |
| NULL, |
| additional_providers.Pass(), |
| data_dir_.path(), |
| fileapi::CreateAllowFileAccessOptions()); |
| |
| filesystem_id_ = isolated_context()->RegisterFileSystemForPath( |
| fileapi::kFileSystemTypeNativeMedia, root_path(), NULL); |
| |
| isolated_context()->AddReference(filesystem_id_); |
| } |
| |
| virtual void TearDown() { |
| isolated_context()->RemoveReference(filesystem_id_); |
| file_system_context_ = NULL; |
| } |
| |
| protected: |
| fileapi::FileSystemContext* file_system_context() { |
| return file_system_context_.get(); |
| } |
| |
| FileSystemURL CreateURL(const base::FilePath::CharType* test_case_path) { |
| return file_system_context_->CreateCrackedFileSystemURL( |
| origin(), |
| fileapi::kFileSystemTypeIsolated, |
| GetVirtualPath(test_case_path)); |
| } |
| |
| fileapi::IsolatedContext* isolated_context() { |
| return fileapi::IsolatedContext::GetInstance(); |
| } |
| |
| base::FilePath root_path() { |
| return data_dir_.path().Append(FPL("Media Directory")); |
| } |
| |
| base::FilePath GetVirtualPath( |
| const base::FilePath::CharType* test_case_path) { |
| return base::FilePath::FromUTF8Unsafe(filesystem_id_). |
| Append(FPL("Media Directory")). |
| Append(base::FilePath(test_case_path)); |
| } |
| |
| GURL origin() { |
| return GURL("http://example.com"); |
| } |
| |
| fileapi::FileSystemType type() { |
| return fileapi::kFileSystemTypeNativeMedia; |
| } |
| |
| fileapi::FileSystemOperationRunner* operation_runner() { |
| return file_system_context_->operation_runner(); |
| } |
| |
| private: |
| base::MessageLoop message_loop_; |
| content::TestBrowserThread io_thread_; |
| |
| base::ScopedTempDir data_dir_; |
| scoped_refptr<fileapi::FileSystemContext> file_system_context_; |
| |
| std::string filesystem_id_; |
| |
| DISALLOW_COPY_AND_ASSIGN(NativeMediaFileUtilTest); |
| }; |
| |
| TEST_F(NativeMediaFileUtilTest, DirectoryExistsAndFileExistsFiltering) { |
| PopulateDirectoryWithTestCases(root_path(), |
| kFilteringTestCases, |
| arraysize(kFilteringTestCases)); |
| |
| for (size_t i = 0; i < arraysize(kFilteringTestCases); ++i) { |
| FileSystemURL url = CreateURL(kFilteringTestCases[i].path); |
| |
| base::PlatformFileError expectation = |
| kFilteringTestCases[i].visible ? |
| base::PLATFORM_FILE_OK : |
| base::PLATFORM_FILE_ERROR_NOT_FOUND; |
| |
| std::string test_name = |
| base::StringPrintf("DirectoryExistsAndFileExistsFiltering %" PRIuS, i); |
| if (kFilteringTestCases[i].is_directory) { |
| operation_runner()->DirectoryExists( |
| url, base::Bind(&ExpectEqHelper, test_name, expectation)); |
| } else { |
| operation_runner()->FileExists( |
| url, base::Bind(&ExpectEqHelper, test_name, expectation)); |
| } |
| base::MessageLoop::current()->RunUntilIdle(); |
| } |
| } |
| |
| TEST_F(NativeMediaFileUtilTest, ReadDirectoryFiltering) { |
| PopulateDirectoryWithTestCases(root_path(), |
| kFilteringTestCases, |
| arraysize(kFilteringTestCases)); |
| |
| std::set<base::FilePath::StringType> content; |
| FileSystemURL url = CreateURL(FPL("")); |
| bool completed = false; |
| operation_runner()->ReadDirectory( |
| url, base::Bind(&DidReadDirectory, &content, &completed)); |
| base::MessageLoop::current()->RunUntilIdle(); |
| EXPECT_TRUE(completed); |
| EXPECT_EQ(6u, content.size()); |
| |
| for (size_t i = 0; i < arraysize(kFilteringTestCases); ++i) { |
| base::FilePath::StringType name = |
| base::FilePath(kFilteringTestCases[i].path).BaseName().value(); |
| std::set<base::FilePath::StringType>::const_iterator found = |
| content.find(name); |
| EXPECT_EQ(kFilteringTestCases[i].visible, found != content.end()); |
| } |
| } |
| |
| TEST_F(NativeMediaFileUtilTest, CreateDirectoryFiltering) { |
| // Run the loop twice. The second loop attempts to create directories that are |
| // pre-existing. Though the result should be the same. |
| for (int loop_count = 0; loop_count < 2; ++loop_count) { |
| for (size_t i = 0; i < arraysize(kFilteringTestCases); ++i) { |
| if (kFilteringTestCases[i].is_directory) { |
| FileSystemURL root_url = CreateURL(FPL("")); |
| FileSystemURL url = CreateURL(kFilteringTestCases[i].path); |
| |
| std::string test_name = base::StringPrintf( |
| "CreateFileAndCreateDirectoryFiltering run %d, test %" PRIuS, |
| loop_count, i); |
| base::PlatformFileError expectation = |
| kFilteringTestCases[i].visible ? |
| base::PLATFORM_FILE_OK : |
| base::PLATFORM_FILE_ERROR_SECURITY; |
| operation_runner()->CreateDirectory( |
| url, false, false, |
| base::Bind(&ExpectEqHelper, test_name, expectation)); |
| } |
| base::MessageLoop::current()->RunUntilIdle(); |
| } |
| } |
| } |
| |
| TEST_F(NativeMediaFileUtilTest, CopySourceFiltering) { |
| base::FilePath dest_path = root_path().AppendASCII("dest"); |
| FileSystemURL dest_url = CreateURL(FPL("dest")); |
| |
| // Run the loop twice. The first run has no source files. The second run does. |
| for (int loop_count = 0; loop_count < 2; ++loop_count) { |
| if (loop_count == 1) { |
| PopulateDirectoryWithTestCases(root_path(), |
| kFilteringTestCases, |
| arraysize(kFilteringTestCases)); |
| } |
| for (size_t i = 0; i < arraysize(kFilteringTestCases); ++i) { |
| // Always start with an empty destination directory. |
| // Copying to a non-empty destination directory is an invalid operation. |
| ASSERT_TRUE(base::DeleteFile(dest_path, true)); |
| ASSERT_TRUE(file_util::CreateDirectory(dest_path)); |
| |
| FileSystemURL root_url = CreateURL(FPL("")); |
| FileSystemURL url = CreateURL(kFilteringTestCases[i].path); |
| |
| std::string test_name = base::StringPrintf( |
| "CopySourceFiltering run %d test %" PRIuS, loop_count, i); |
| base::PlatformFileError expectation = base::PLATFORM_FILE_OK; |
| if (loop_count == 0 || !kFilteringTestCases[i].visible) { |
| // If the source does not exist or is not visible. |
| expectation = base::PLATFORM_FILE_ERROR_NOT_FOUND; |
| } else if (!kFilteringTestCases[i].is_directory) { |
| // Cannot copy a visible file to a directory. |
| expectation = base::PLATFORM_FILE_ERROR_INVALID_OPERATION; |
| } |
| operation_runner()->Copy( |
| url, dest_url, |
| fileapi::FileSystemOperationRunner::CopyProgressCallback(), |
| base::Bind(&ExpectEqHelper, test_name, expectation)); |
| base::MessageLoop::current()->RunUntilIdle(); |
| } |
| } |
| } |
| |
| TEST_F(NativeMediaFileUtilTest, CopyDestFiltering) { |
| // Run the loop twice. The first run has no destination files. |
| // The second run does. |
| for (int loop_count = 0; loop_count < 2; ++loop_count) { |
| if (loop_count == 1) { |
| // Reset the test directory between the two loops to remove old |
| // directories and create new ones that should pre-exist. |
| ASSERT_TRUE(base::DeleteFile(root_path(), true)); |
| ASSERT_TRUE(file_util::CreateDirectory(root_path())); |
| PopulateDirectoryWithTestCases(root_path(), |
| kFilteringTestCases, |
| arraysize(kFilteringTestCases)); |
| } |
| |
| // Always create a dummy source data file. |
| base::FilePath src_path = root_path().AppendASCII("foo.jpg"); |
| FileSystemURL src_url = CreateURL(FPL("foo.jpg")); |
| static const char kDummyData[] = "dummy"; |
| ASSERT_TRUE(file_util::WriteFile(src_path, kDummyData, strlen(kDummyData))); |
| |
| for (size_t i = 0; i < arraysize(kFilteringTestCases); ++i) { |
| if (loop_count == 0 && kFilteringTestCases[i].is_directory) { |
| // These directories do not exist in this case, so Copy() will not |
| // treat them as directories. Thus invalidating these test cases. |
| continue; |
| } |
| FileSystemURL root_url = CreateURL(FPL("")); |
| FileSystemURL url = CreateURL(kFilteringTestCases[i].path); |
| |
| std::string test_name = base::StringPrintf( |
| "CopyDestFiltering run %d test %" PRIuS, loop_count, i); |
| base::PlatformFileError expectation; |
| if (loop_count == 0) { |
| // The destination path is a file here. The directory case has been |
| // handled above. |
| // If the destination path does not exist and is not visible, then |
| // creating it would be a security violation. |
| expectation = |
| kFilteringTestCases[i].visible ? |
| base::PLATFORM_FILE_OK : |
| base::PLATFORM_FILE_ERROR_SECURITY; |
| } else { |
| if (!kFilteringTestCases[i].visible) { |
| // If the destination path exist and is not visible, then to the copy |
| // operation, it looks like the file needs to be created, which is a |
| // security violation. |
| expectation = base::PLATFORM_FILE_ERROR_SECURITY; |
| } else if (kFilteringTestCases[i].is_directory) { |
| // Cannot copy a file to a directory. |
| expectation = base::PLATFORM_FILE_ERROR_INVALID_OPERATION; |
| } else { |
| // Copying from a file to a visible file that exists is ok. |
| expectation = base::PLATFORM_FILE_OK; |
| } |
| } |
| operation_runner()->Copy( |
| src_url, url, |
| fileapi::FileSystemOperationRunner::CopyProgressCallback(), |
| base::Bind(&ExpectEqHelper, test_name, expectation)); |
| base::MessageLoop::current()->RunUntilIdle(); |
| } |
| } |
| } |
| |
| TEST_F(NativeMediaFileUtilTest, MoveSourceFiltering) { |
| base::FilePath dest_path = root_path().AppendASCII("dest"); |
| FileSystemURL dest_url = CreateURL(FPL("dest")); |
| |
| // Run the loop twice. The first run has no source files. The second run does. |
| for (int loop_count = 0; loop_count < 2; ++loop_count) { |
| if (loop_count == 1) { |
| PopulateDirectoryWithTestCases(root_path(), |
| kFilteringTestCases, |
| arraysize(kFilteringTestCases)); |
| } |
| for (size_t i = 0; i < arraysize(kFilteringTestCases); ++i) { |
| // Always start with an empty destination directory. |
| // Moving to a non-empty destination directory is an invalid operation. |
| ASSERT_TRUE(base::DeleteFile(dest_path, true)); |
| ASSERT_TRUE(file_util::CreateDirectory(dest_path)); |
| |
| FileSystemURL root_url = CreateURL(FPL("")); |
| FileSystemURL url = CreateURL(kFilteringTestCases[i].path); |
| |
| std::string test_name = base::StringPrintf( |
| "MoveSourceFiltering run %d test %" PRIuS, loop_count, i); |
| base::PlatformFileError expectation = base::PLATFORM_FILE_OK; |
| if (loop_count == 0 || !kFilteringTestCases[i].visible) { |
| // If the source does not exist or is not visible. |
| expectation = base::PLATFORM_FILE_ERROR_NOT_FOUND; |
| } else if (!kFilteringTestCases[i].is_directory) { |
| // Cannot move a visible file to a directory. |
| expectation = base::PLATFORM_FILE_ERROR_INVALID_OPERATION; |
| } |
| operation_runner()->Move( |
| url, dest_url, base::Bind(&ExpectEqHelper, test_name, expectation)); |
| base::MessageLoop::current()->RunUntilIdle(); |
| } |
| } |
| } |
| |
| TEST_F(NativeMediaFileUtilTest, MoveDestFiltering) { |
| // Run the loop twice. The first run has no destination files. |
| // The second run does. |
| for (int loop_count = 0; loop_count < 2; ++loop_count) { |
| if (loop_count == 1) { |
| // Reset the test directory between the two loops to remove old |
| // directories and create new ones that should pre-exist. |
| ASSERT_TRUE(base::DeleteFile(root_path(), true)); |
| ASSERT_TRUE(file_util::CreateDirectory(root_path())); |
| PopulateDirectoryWithTestCases(root_path(), |
| kFilteringTestCases, |
| arraysize(kFilteringTestCases)); |
| } |
| |
| for (size_t i = 0; i < arraysize(kFilteringTestCases); ++i) { |
| if (loop_count == 0 && kFilteringTestCases[i].is_directory) { |
| // These directories do not exist in this case, so Copy() will not |
| // treat them as directories. Thus invalidating these test cases. |
| continue; |
| } |
| |
| // Create the source file for every test case because it might get moved. |
| base::FilePath src_path = root_path().AppendASCII("foo.jpg"); |
| FileSystemURL src_url = CreateURL(FPL("foo.jpg")); |
| static const char kDummyData[] = "dummy"; |
| ASSERT_TRUE( |
| file_util::WriteFile(src_path, kDummyData, strlen(kDummyData))); |
| |
| FileSystemURL root_url = CreateURL(FPL("")); |
| FileSystemURL url = CreateURL(kFilteringTestCases[i].path); |
| |
| std::string test_name = base::StringPrintf( |
| "MoveDestFiltering run %d test %" PRIuS, loop_count, i); |
| base::PlatformFileError expectation; |
| if (loop_count == 0) { |
| // The destination path is a file here. The directory case has been |
| // handled above. |
| // If the destination path does not exist and is not visible, then |
| // creating it would be a security violation. |
| expectation = |
| kFilteringTestCases[i].visible ? |
| base::PLATFORM_FILE_OK : |
| base::PLATFORM_FILE_ERROR_SECURITY; |
| } else { |
| if (!kFilteringTestCases[i].visible) { |
| // If the destination path exist and is not visible, then to the move |
| // operation, it looks like the file needs to be created, which is a |
| // security violation. |
| expectation = base::PLATFORM_FILE_ERROR_SECURITY; |
| } else if (kFilteringTestCases[i].is_directory) { |
| // Cannot move a file to a directory. |
| expectation = base::PLATFORM_FILE_ERROR_INVALID_OPERATION; |
| } else { |
| // Moving from a file to a visible file that exists is ok. |
| expectation = base::PLATFORM_FILE_OK; |
| } |
| } |
| operation_runner()->Move( |
| src_url, url, base::Bind(&ExpectEqHelper, test_name, expectation)); |
| base::MessageLoop::current()->RunUntilIdle(); |
| } |
| } |
| } |
| |
| TEST_F(NativeMediaFileUtilTest, GetMetadataFiltering) { |
| // Run the loop twice. The first run has no files. The second run does. |
| for (int loop_count = 0; loop_count < 2; ++loop_count) { |
| if (loop_count == 1) { |
| PopulateDirectoryWithTestCases(root_path(), |
| kFilteringTestCases, |
| arraysize(kFilteringTestCases)); |
| } |
| for (size_t i = 0; i < arraysize(kFilteringTestCases); ++i) { |
| FileSystemURL root_url = CreateURL(FPL("")); |
| FileSystemURL url = CreateURL(kFilteringTestCases[i].path); |
| |
| std::string test_name = base::StringPrintf( |
| "GetMetadataFiltering run %d test %" PRIuS, loop_count, i); |
| base::PlatformFileError expectation = base::PLATFORM_FILE_OK; |
| if (loop_count == 0 || !kFilteringTestCases[i].visible) { |
| // Cannot get metadata from files that do not exist or are not visible. |
| expectation = base::PLATFORM_FILE_ERROR_NOT_FOUND; |
| } |
| operation_runner()->GetMetadata( |
| url, |
| base::Bind(&ExpectMetadataEqHelper, |
| test_name, |
| expectation, |
| kFilteringTestCases[i].is_directory)); |
| base::MessageLoop::current()->RunUntilIdle(); |
| } |
| } |
| } |
| |
| void CreateSnapshotCallback(base::PlatformFileError* error, |
| base::PlatformFileError result, const base::PlatformFileInfo&, |
| const base::FilePath&, |
| const scoped_refptr<webkit_blob::ShareableFileReference>&) { |
| *error = result; |
| } |
| |
| TEST_F(NativeMediaFileUtilTest, CreateSnapshot) { |
| PopulateDirectoryWithTestCases(root_path(), |
| kFilteringTestCases, |
| arraysize(kFilteringTestCases)); |
| for (size_t i = 0; i < arraysize(kFilteringTestCases); ++i) { |
| if (kFilteringTestCases[i].is_directory || |
| !kFilteringTestCases[i].visible) { |
| continue; |
| } |
| FileSystemURL root_url = CreateURL(FPL("")); |
| FileSystemURL url = CreateURL(kFilteringTestCases[i].path); |
| base::PlatformFileError expected_error, error; |
| if (kFilteringTestCases[i].media_file) |
| expected_error = base::PLATFORM_FILE_OK; |
| else |
| expected_error = base::PLATFORM_FILE_ERROR_SECURITY; |
| error = base::PLATFORM_FILE_ERROR_FAILED; |
| operation_runner()->CreateSnapshotFile(url, |
| base::Bind(CreateSnapshotCallback, &error)); |
| base::MessageLoop::current()->RunUntilIdle(); |
| ASSERT_EQ(expected_error, error); |
| } |
| } |
| |
| } // namespace chrome |