Home | History | Annotate | Download | only in fileapi
      1 // Copyright 2013 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 "webkit/browser/fileapi/recursive_operation_delegate.h"
      6 
      7 #include <vector>
      8 
      9 #include "base/basictypes.h"
     10 #include "base/bind.h"
     11 #include "base/callback.h"
     12 #include "base/files/scoped_temp_dir.h"
     13 #include "base/message_loop/message_loop.h"
     14 #include "base/message_loop/message_loop_proxy.h"
     15 #include "base/run_loop.h"
     16 #include "content/public/test/sandbox_file_system_test_helper.h"
     17 #include "testing/gtest/include/gtest/gtest.h"
     18 #include "webkit/browser/fileapi/file_system_file_util.h"
     19 #include "webkit/browser/fileapi/file_system_operation.h"
     20 #include "webkit/browser/fileapi/file_system_operation_runner.h"
     21 
     22 namespace fileapi {
     23 namespace {
     24 
     25 class LoggingRecursiveOperation : public RecursiveOperationDelegate {
     26  public:
     27   struct LogEntry {
     28     enum Type {
     29       PROCESS_FILE,
     30       PROCESS_DIRECTORY,
     31       POST_PROCESS_DIRECTORY
     32     };
     33     Type type;
     34     FileSystemURL url;
     35   };
     36 
     37   LoggingRecursiveOperation(FileSystemContext* file_system_context,
     38                             const FileSystemURL& root,
     39                             const StatusCallback& callback)
     40       : RecursiveOperationDelegate(file_system_context),
     41         root_(root),
     42         callback_(callback),
     43         weak_factory_(this) {
     44   }
     45   virtual ~LoggingRecursiveOperation() {}
     46 
     47   const std::vector<LogEntry>& log_entries() const { return log_entries_; }
     48 
     49   // RecursiveOperationDelegate overrides.
     50   virtual void Run() OVERRIDE {
     51     NOTREACHED();
     52   }
     53 
     54   virtual void RunRecursively() OVERRIDE {
     55     StartRecursiveOperation(root_, callback_);
     56   }
     57 
     58   virtual void ProcessFile(const FileSystemURL& url,
     59                            const StatusCallback& callback) OVERRIDE {
     60     RecordLogEntry(LogEntry::PROCESS_FILE, url);
     61     operation_runner()->GetMetadata(
     62         url,
     63         base::Bind(&LoggingRecursiveOperation::DidGetMetadata,
     64                    weak_factory_.GetWeakPtr(), callback));
     65   }
     66 
     67   virtual void ProcessDirectory(const FileSystemURL& url,
     68                                 const StatusCallback& callback) OVERRIDE {
     69     RecordLogEntry(LogEntry::PROCESS_DIRECTORY, url);
     70     callback.Run(base::PLATFORM_FILE_OK);
     71   }
     72 
     73   virtual void PostProcessDirectory(const FileSystemURL& url,
     74                                     const StatusCallback& callback) OVERRIDE {
     75     RecordLogEntry(LogEntry::POST_PROCESS_DIRECTORY, url);
     76     callback.Run(base::PLATFORM_FILE_OK);
     77   }
     78 
     79  private:
     80   void RecordLogEntry(LogEntry::Type type, const FileSystemURL& url) {
     81     LogEntry entry;
     82     entry.type = type;
     83     entry.url = url;
     84     log_entries_.push_back(entry);
     85   }
     86 
     87   void DidGetMetadata(const StatusCallback& callback,
     88                       base::PlatformFileError result,
     89                       const base::PlatformFileInfo& file_info) {
     90     if (result != base::PLATFORM_FILE_OK) {
     91       callback.Run(result);
     92       return;
     93     }
     94 
     95     callback.Run(file_info.is_directory ?
     96                  base::PLATFORM_FILE_ERROR_NOT_A_FILE :
     97                  base::PLATFORM_FILE_OK);
     98   }
     99 
    100   FileSystemURL root_;
    101   StatusCallback callback_;
    102   std::vector<LogEntry> log_entries_;
    103 
    104   base::WeakPtrFactory<LoggingRecursiveOperation> weak_factory_;
    105   DISALLOW_COPY_AND_ASSIGN(LoggingRecursiveOperation);
    106 };
    107 
    108 void ReportStatus(base::PlatformFileError* out_error,
    109                   base::PlatformFileError error) {
    110   DCHECK(out_error);
    111   *out_error = error;
    112 }
    113 
    114 // To test the Cancel() during operation, calls Cancel() of |operation|
    115 // after |counter| times message posting.
    116 void CallCancelLater(RecursiveOperationDelegate* operation, int counter) {
    117   if (counter > 0) {
    118     base::MessageLoopProxy::current()->PostTask(
    119         FROM_HERE,
    120         base::Bind(&CallCancelLater, base::Unretained(operation), counter - 1));
    121     return;
    122   }
    123 
    124   operation->Cancel();
    125 }
    126 
    127 }  // namespace
    128 
    129 class RecursiveOperationDelegateTest : public testing::Test {
    130  protected:
    131   virtual void SetUp() OVERRIDE {
    132     EXPECT_TRUE(base_.CreateUniqueTempDir());
    133     sandbox_file_system_.SetUp(base_.path().AppendASCII("filesystem"));
    134   }
    135 
    136   virtual void TearDown() OVERRIDE {
    137     sandbox_file_system_.TearDown();
    138   }
    139 
    140   scoped_ptr<FileSystemOperationContext> NewContext() {
    141     FileSystemOperationContext* context =
    142         sandbox_file_system_.NewOperationContext();
    143     // Grant enough quota for all test cases.
    144     context->set_allowed_bytes_growth(1000000);
    145     return make_scoped_ptr(context);
    146   }
    147 
    148   FileSystemFileUtil* file_util() {
    149     return sandbox_file_system_.file_util();
    150   }
    151 
    152   FileSystemURL URLForPath(const std::string& path) const {
    153     return sandbox_file_system_.CreateURLFromUTF8(path);
    154   }
    155 
    156   FileSystemURL CreateFile(const std::string& path) {
    157     FileSystemURL url = URLForPath(path);
    158     bool created = false;
    159     EXPECT_EQ(base::PLATFORM_FILE_OK,
    160               file_util()->EnsureFileExists(NewContext().get(),
    161                                             url, &created));
    162     EXPECT_TRUE(created);
    163     return url;
    164   }
    165 
    166   FileSystemURL CreateDirectory(const std::string& path) {
    167     FileSystemURL url = URLForPath(path);
    168     EXPECT_EQ(base::PLATFORM_FILE_OK,
    169               file_util()->CreateDirectory(NewContext().get(), url,
    170                                            false /* exclusive */, true));
    171     return url;
    172   }
    173 
    174  private:
    175   base::MessageLoop message_loop_;
    176 
    177   // Common temp base for nondestructive uses.
    178   base::ScopedTempDir base_;
    179   SandboxFileSystemTestHelper sandbox_file_system_;
    180 };
    181 
    182 TEST_F(RecursiveOperationDelegateTest, RootIsFile) {
    183   FileSystemURL src_file(CreateFile("src"));
    184 
    185   base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED;
    186   scoped_ptr<FileSystemOperationContext> context = NewContext();
    187   scoped_ptr<LoggingRecursiveOperation> operation(
    188       new LoggingRecursiveOperation(
    189           context->file_system_context(), src_file,
    190           base::Bind(&ReportStatus, &error)));
    191   operation->RunRecursively();
    192   base::RunLoop().RunUntilIdle();
    193   ASSERT_EQ(base::PLATFORM_FILE_OK, error);
    194 
    195   const std::vector<LoggingRecursiveOperation::LogEntry>& log_entries =
    196       operation->log_entries();
    197   ASSERT_EQ(1U, log_entries.size());
    198   const LoggingRecursiveOperation::LogEntry& entry = log_entries[0];
    199   EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE, entry.type);
    200   EXPECT_EQ(src_file, entry.url);
    201 }
    202 
    203 TEST_F(RecursiveOperationDelegateTest, RootIsDirectory) {
    204   FileSystemURL src_root(CreateDirectory("src"));
    205   FileSystemURL src_dir1(CreateDirectory("src/dir1"));
    206   FileSystemURL src_file1(CreateFile("src/file1"));
    207   FileSystemURL src_file2(CreateFile("src/dir1/file2"));
    208   FileSystemURL src_file3(CreateFile("src/dir1/file3"));
    209 
    210   base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED;
    211   scoped_ptr<FileSystemOperationContext> context = NewContext();
    212   scoped_ptr<LoggingRecursiveOperation> operation(
    213       new LoggingRecursiveOperation(
    214           context->file_system_context(), src_root,
    215           base::Bind(&ReportStatus, &error)));
    216   operation->RunRecursively();
    217   base::RunLoop().RunUntilIdle();
    218   ASSERT_EQ(base::PLATFORM_FILE_OK, error);
    219 
    220   const std::vector<LoggingRecursiveOperation::LogEntry>& log_entries =
    221       operation->log_entries();
    222   ASSERT_EQ(8U, log_entries.size());
    223 
    224   EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE,
    225             log_entries[0].type);
    226   EXPECT_EQ(src_root, log_entries[0].url);
    227 
    228   EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_DIRECTORY,
    229             log_entries[1].type);
    230   EXPECT_EQ(src_root, log_entries[1].url);
    231 
    232   EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE,
    233             log_entries[2].type);
    234   EXPECT_EQ(src_file1, log_entries[2].url);
    235 
    236   EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_DIRECTORY,
    237             log_entries[3].type);
    238   EXPECT_EQ(src_dir1, log_entries[3].url);
    239 
    240   // The order of src/dir1/file2 and src/dir1/file3 depends on the file system
    241   // implementation (can be swapped).
    242   EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE,
    243             log_entries[4].type);
    244   EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE,
    245             log_entries[5].type);
    246   EXPECT_TRUE((src_file2 == log_entries[4].url &&
    247                src_file3 == log_entries[5].url) ||
    248               (src_file3 == log_entries[4].url &&
    249                src_file2 == log_entries[5].url));
    250 
    251   EXPECT_EQ(LoggingRecursiveOperation::LogEntry::POST_PROCESS_DIRECTORY,
    252             log_entries[6].type);
    253   EXPECT_EQ(src_dir1, log_entries[6].url);
    254 
    255   EXPECT_EQ(LoggingRecursiveOperation::LogEntry::POST_PROCESS_DIRECTORY,
    256             log_entries[7].type);
    257   EXPECT_EQ(src_root, log_entries[7].url);
    258 }
    259 
    260 TEST_F(RecursiveOperationDelegateTest, Cancel) {
    261   FileSystemURL src_root(CreateDirectory("src"));
    262   FileSystemURL src_dir1(CreateDirectory("src/dir1"));
    263   FileSystemURL src_file1(CreateFile("src/file1"));
    264   FileSystemURL src_file2(CreateFile("src/dir1/file2"));
    265 
    266   base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED;
    267   scoped_ptr<FileSystemOperationContext> context = NewContext();
    268   scoped_ptr<LoggingRecursiveOperation> operation(
    269       new LoggingRecursiveOperation(
    270           context->file_system_context(), src_root,
    271           base::Bind(&ReportStatus, &error)));
    272   operation->RunRecursively();
    273 
    274   // Invoke Cancel(), after 5 times message posting.
    275   CallCancelLater(operation.get(), 5);
    276   base::RunLoop().RunUntilIdle();
    277   ASSERT_EQ(base::PLATFORM_FILE_ERROR_ABORT, error);
    278 }
    279 
    280 }  // namespace fileapi
    281