OLD | NEW |
| (Empty) |
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 "testing/gtest/include/gtest/gtest.h" | |
17 #include "webkit/browser/fileapi/file_system_file_util.h" | |
18 #include "webkit/browser/fileapi/file_system_operation.h" | |
19 #include "webkit/browser/fileapi/file_system_operation_runner.h" | |
20 #include "webkit/browser/fileapi/sandbox_file_system_test_helper.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 | |
OLD | NEW |