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