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