• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
LoggingRecursiveOperation(FileSystemContext * file_system_context,const FileSystemURL & root,const StatusCallback & callback)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) {}
~LoggingRecursiveOperation()48   virtual ~LoggingRecursiveOperation() {}
49 
log_entries() const50   const std::vector<LogEntry>& log_entries() const { return log_entries_; }
51 
52   // RecursiveOperationDelegate overrides.
Run()53   virtual void Run() OVERRIDE {
54     NOTREACHED();
55   }
56 
RunRecursively()57   virtual void RunRecursively() OVERRIDE {
58     StartRecursiveOperation(root_, callback_);
59   }
60 
ProcessFile(const FileSystemURL & url,const StatusCallback & callback)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 
ProcessDirectory(const FileSystemURL & url,const StatusCallback & callback)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 
PostProcessDirectory(const FileSystemURL & url,const StatusCallback & callback)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:
RecordLogEntry(LogEntry::Type type,const FileSystemURL & url)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 
DidGetMetadata(const StatusCallback & callback,base::File::Error result,const base::File::Info & file_info)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 
ReportStatus(base::File::Error * out_error,base::File::Error error)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.
CallCancelLater(storage::RecursiveOperationDelegate * operation,int counter)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:
SetUp()135   virtual void SetUp() OVERRIDE {
136     EXPECT_TRUE(base_.CreateUniqueTempDir());
137     sandbox_file_system_.SetUp(base_.path().AppendASCII("filesystem"));
138   }
139 
TearDown()140   virtual void TearDown() OVERRIDE {
141     sandbox_file_system_.TearDown();
142   }
143 
NewContext()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 
file_util()152   storage::FileSystemFileUtil* file_util() {
153     return sandbox_file_system_.file_util();
154   }
155 
URLForPath(const std::string & path) const156   FileSystemURL URLForPath(const std::string& path) const {
157     return sandbox_file_system_.CreateURLFromUTF8(path);
158   }
159 
CreateFile(const std::string & path)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 
CreateDirectory(const std::string & path)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 
TEST_F(RecursiveOperationDelegateTest,RootIsFile)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 
TEST_F(RecursiveOperationDelegateTest,RootIsDirectory)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 
TEST_F(RecursiveOperationDelegateTest,Cancel)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