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.
5 #include "storage/browser/fileapi/recursive_operation_delegate.h"
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/location.h"
14 #include "base/run_loop.h"
15 #include "base/single_thread_task_runner.h"
16 #include "base/thread_task_runner_handle.h"
17 #include "content/public/test/sandbox_file_system_test_helper.h"
18 #include "storage/browser/fileapi/file_system_file_util.h"
19 #include "storage/browser/fileapi/file_system_operation.h"
20 #include "storage/browser/fileapi/file_system_operation_runner.h"
21 #include "testing/gtest/include/gtest/gtest.h"
23 using storage::FileSystemContext
;
24 using storage::FileSystemOperationContext
;
25 using storage::FileSystemURL
;
30 class LoggingRecursiveOperation
: public storage::RecursiveOperationDelegate
{
36 POST_PROCESS_DIRECTORY
42 LoggingRecursiveOperation(FileSystemContext
* file_system_context
,
43 const FileSystemURL
& root
,
44 const StatusCallback
& callback
)
45 : storage::RecursiveOperationDelegate(file_system_context
),
48 weak_factory_(this) {}
49 ~LoggingRecursiveOperation() override
{}
51 const std::vector
<LogEntry
>& log_entries() const { return log_entries_
; }
53 // RecursiveOperationDelegate overrides.
54 void Run() override
{ NOTREACHED(); }
56 void RunRecursively() override
{
57 StartRecursiveOperation(
58 root_
, storage::FileSystemOperation::ERROR_BEHAVIOR_ABORT
, callback_
);
61 void RunRecursivelyWithIgnoringError() {
62 StartRecursiveOperation(
63 root_
, storage::FileSystemOperation::ERROR_BEHAVIOR_SKIP
, callback_
);
66 void ProcessFile(const FileSystemURL
& url
,
67 const StatusCallback
& callback
) override
{
68 RecordLogEntry(LogEntry::PROCESS_FILE
, url
);
70 if (error_url_
.is_valid() && error_url_
== url
) {
71 callback
.Run(base::File::FILE_ERROR_FAILED
);
75 operation_runner()->GetMetadata(
77 base::Bind(&LoggingRecursiveOperation::DidGetMetadata
,
78 weak_factory_
.GetWeakPtr(), callback
));
81 void ProcessDirectory(const FileSystemURL
& url
,
82 const StatusCallback
& callback
) override
{
83 RecordLogEntry(LogEntry::PROCESS_DIRECTORY
, url
);
84 callback
.Run(base::File::FILE_OK
);
87 void PostProcessDirectory(const FileSystemURL
& url
,
88 const StatusCallback
& callback
) override
{
89 RecordLogEntry(LogEntry::POST_PROCESS_DIRECTORY
, url
);
90 callback
.Run(base::File::FILE_OK
);
93 void SetEntryToFail(const FileSystemURL
& url
) { error_url_
= url
; }
96 void RecordLogEntry(LogEntry::Type type
, const FileSystemURL
& url
) {
100 log_entries_
.push_back(entry
);
103 void DidGetMetadata(const StatusCallback
& callback
,
104 base::File::Error result
,
105 const base::File::Info
& file_info
) {
106 if (result
!= base::File::FILE_OK
) {
107 callback
.Run(result
);
111 callback
.Run(file_info
.is_directory
?
112 base::File::FILE_ERROR_NOT_A_FILE
:
113 base::File::FILE_OK
);
117 StatusCallback callback_
;
118 std::vector
<LogEntry
> log_entries_
;
119 FileSystemURL error_url_
;
121 base::WeakPtrFactory
<LoggingRecursiveOperation
> weak_factory_
;
122 DISALLOW_COPY_AND_ASSIGN(LoggingRecursiveOperation
);
125 void ReportStatus(base::File::Error
* out_error
,
126 base::File::Error error
) {
131 // To test the Cancel() during operation, calls Cancel() of |operation|
132 // after |counter| times message posting.
133 void CallCancelLater(storage::RecursiveOperationDelegate
* operation
,
136 base::ThreadTaskRunnerHandle::Get()->PostTask(
138 base::Bind(&CallCancelLater
, base::Unretained(operation
), counter
- 1));
147 class RecursiveOperationDelegateTest
: public testing::Test
{
149 void SetUp() override
{
150 EXPECT_TRUE(base_
.CreateUniqueTempDir());
151 sandbox_file_system_
.SetUp(base_
.path().AppendASCII("filesystem"));
154 void TearDown() override
{ sandbox_file_system_
.TearDown(); }
156 scoped_ptr
<FileSystemOperationContext
> NewContext() {
157 FileSystemOperationContext
* context
=
158 sandbox_file_system_
.NewOperationContext();
159 // Grant enough quota for all test cases.
160 context
->set_allowed_bytes_growth(1000000);
161 return make_scoped_ptr(context
);
164 storage::FileSystemFileUtil
* file_util() {
165 return sandbox_file_system_
.file_util();
168 FileSystemURL
URLForPath(const std::string
& path
) const {
169 return sandbox_file_system_
.CreateURLFromUTF8(path
);
172 FileSystemURL
CreateFile(const std::string
& path
) {
173 FileSystemURL url
= URLForPath(path
);
174 bool created
= false;
175 EXPECT_EQ(base::File::FILE_OK
,
176 file_util()->EnsureFileExists(NewContext().get(),
178 EXPECT_TRUE(created
);
182 FileSystemURL
CreateDirectory(const std::string
& path
) {
183 FileSystemURL url
= URLForPath(path
);
184 EXPECT_EQ(base::File::FILE_OK
,
185 file_util()->CreateDirectory(NewContext().get(), url
,
186 false /* exclusive */, true));
191 base::MessageLoop message_loop_
;
193 // Common temp base for nondestructive uses.
194 base::ScopedTempDir base_
;
195 SandboxFileSystemTestHelper sandbox_file_system_
;
198 TEST_F(RecursiveOperationDelegateTest
, RootIsFile
) {
199 FileSystemURL
src_file(CreateFile("src"));
201 base::File::Error error
= base::File::FILE_ERROR_FAILED
;
202 scoped_ptr
<FileSystemOperationContext
> context
= NewContext();
203 scoped_ptr
<LoggingRecursiveOperation
> operation(
204 new LoggingRecursiveOperation(
205 context
->file_system_context(), src_file
,
206 base::Bind(&ReportStatus
, &error
)));
207 operation
->RunRecursively();
208 base::RunLoop().RunUntilIdle();
209 ASSERT_EQ(base::File::FILE_OK
, error
);
211 const std::vector
<LoggingRecursiveOperation::LogEntry
>& log_entries
=
212 operation
->log_entries();
213 ASSERT_EQ(1U, log_entries
.size());
214 const LoggingRecursiveOperation::LogEntry
& entry
= log_entries
[0];
215 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE
, entry
.type
);
216 EXPECT_EQ(src_file
, entry
.url
);
219 TEST_F(RecursiveOperationDelegateTest
, RootIsDirectory
) {
220 FileSystemURL
src_root(CreateDirectory("src"));
221 FileSystemURL
src_dir1(CreateDirectory("src/dir1"));
222 FileSystemURL
src_file1(CreateFile("src/file1"));
223 FileSystemURL
src_file2(CreateFile("src/dir1/file2"));
224 FileSystemURL
src_file3(CreateFile("src/dir1/file3"));
226 base::File::Error error
= base::File::FILE_ERROR_FAILED
;
227 scoped_ptr
<FileSystemOperationContext
> context
= NewContext();
228 scoped_ptr
<LoggingRecursiveOperation
> operation(
229 new LoggingRecursiveOperation(
230 context
->file_system_context(), src_root
,
231 base::Bind(&ReportStatus
, &error
)));
232 operation
->RunRecursively();
233 base::RunLoop().RunUntilIdle();
234 ASSERT_EQ(base::File::FILE_OK
, error
);
236 const std::vector
<LoggingRecursiveOperation::LogEntry
>& log_entries
=
237 operation
->log_entries();
238 ASSERT_EQ(8U, log_entries
.size());
240 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE
,
241 log_entries
[0].type
);
242 EXPECT_EQ(src_root
, log_entries
[0].url
);
244 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_DIRECTORY
,
245 log_entries
[1].type
);
246 EXPECT_EQ(src_root
, log_entries
[1].url
);
248 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE
,
249 log_entries
[2].type
);
250 EXPECT_EQ(src_file1
, log_entries
[2].url
);
252 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_DIRECTORY
,
253 log_entries
[3].type
);
254 EXPECT_EQ(src_dir1
, log_entries
[3].url
);
256 // The order of src/dir1/file2 and src/dir1/file3 depends on the file system
257 // implementation (can be swapped).
258 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE
,
259 log_entries
[4].type
);
260 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE
,
261 log_entries
[5].type
);
262 EXPECT_TRUE((src_file2
== log_entries
[4].url
&&
263 src_file3
== log_entries
[5].url
) ||
264 (src_file3
== log_entries
[4].url
&&
265 src_file2
== log_entries
[5].url
));
267 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::POST_PROCESS_DIRECTORY
,
268 log_entries
[6].type
);
269 EXPECT_EQ(src_dir1
, log_entries
[6].url
);
271 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::POST_PROCESS_DIRECTORY
,
272 log_entries
[7].type
);
273 EXPECT_EQ(src_root
, log_entries
[7].url
);
276 TEST_F(RecursiveOperationDelegateTest
, Cancel
) {
277 FileSystemURL
src_root(CreateDirectory("src"));
278 FileSystemURL
src_dir1(CreateDirectory("src/dir1"));
279 FileSystemURL
src_file1(CreateFile("src/file1"));
280 FileSystemURL
src_file2(CreateFile("src/dir1/file2"));
282 base::File::Error error
= base::File::FILE_ERROR_FAILED
;
283 scoped_ptr
<FileSystemOperationContext
> context
= NewContext();
284 scoped_ptr
<LoggingRecursiveOperation
> operation(
285 new LoggingRecursiveOperation(
286 context
->file_system_context(), src_root
,
287 base::Bind(&ReportStatus
, &error
)));
288 operation
->RunRecursively();
290 // Invoke Cancel(), after 5 times message posting.
291 CallCancelLater(operation
.get(), 5);
292 base::RunLoop().RunUntilIdle();
293 ASSERT_EQ(base::File::FILE_ERROR_ABORT
, error
);
296 TEST_F(RecursiveOperationDelegateTest
, AbortWithError
) {
297 FileSystemURL
src_root(CreateDirectory("src"));
298 FileSystemURL
src_dir1(CreateDirectory("src/dir1"));
299 FileSystemURL
src_file1(CreateFile("src/file1"));
300 FileSystemURL
src_file2(CreateFile("src/dir1/file2"));
301 FileSystemURL
src_file3(CreateFile("src/dir1/file3"));
303 base::File::Error error
= base::File::FILE_ERROR_FAILED
;
304 scoped_ptr
<FileSystemOperationContext
> context
= NewContext();
305 scoped_ptr
<LoggingRecursiveOperation
> operation(
306 new LoggingRecursiveOperation(context
->file_system_context(), src_root
,
307 base::Bind(&ReportStatus
, &error
)));
308 operation
->SetEntryToFail(src_file1
);
309 operation
->RunRecursively();
310 base::RunLoop().RunUntilIdle();
312 ASSERT_EQ(base::File::FILE_ERROR_FAILED
, error
);
314 // Confirm that operation has been aborted in the middle.
315 const std::vector
<LoggingRecursiveOperation::LogEntry
>& log_entries
=
316 operation
->log_entries();
317 ASSERT_EQ(3U, log_entries
.size());
319 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE
,
320 log_entries
[0].type
);
321 EXPECT_EQ(src_root
, log_entries
[0].url
);
323 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_DIRECTORY
,
324 log_entries
[1].type
);
325 EXPECT_EQ(src_root
, log_entries
[1].url
);
327 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE
,
328 log_entries
[2].type
);
329 EXPECT_EQ(src_file1
, log_entries
[2].url
);
332 TEST_F(RecursiveOperationDelegateTest
, ContinueWithError
) {
333 FileSystemURL
src_root(CreateDirectory("src"));
334 FileSystemURL
src_dir1(CreateDirectory("src/dir1"));
335 FileSystemURL
src_file1(CreateFile("src/file1"));
336 FileSystemURL
src_file2(CreateFile("src/dir1/file2"));
337 FileSystemURL
src_file3(CreateFile("src/dir1/file3"));
339 base::File::Error error
= base::File::FILE_ERROR_FAILED
;
340 scoped_ptr
<FileSystemOperationContext
> context
= NewContext();
341 scoped_ptr
<LoggingRecursiveOperation
> operation(
342 new LoggingRecursiveOperation(context
->file_system_context(), src_root
,
343 base::Bind(&ReportStatus
, &error
)));
344 operation
->SetEntryToFail(src_file1
);
345 operation
->RunRecursivelyWithIgnoringError();
346 base::RunLoop().RunUntilIdle();
348 // Error code should be base::File::FILE_ERROR_FAILED.
349 ASSERT_EQ(base::File::FILE_ERROR_FAILED
, error
);
351 // Confirm that operation continues after the error.
352 const std::vector
<LoggingRecursiveOperation::LogEntry
>& log_entries
=
353 operation
->log_entries();
354 ASSERT_EQ(8U, log_entries
.size());
356 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE
,
357 log_entries
[0].type
);
358 EXPECT_EQ(src_root
, log_entries
[0].url
);
360 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_DIRECTORY
,
361 log_entries
[1].type
);
362 EXPECT_EQ(src_root
, log_entries
[1].url
);
364 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE
,
365 log_entries
[2].type
);
366 EXPECT_EQ(src_file1
, log_entries
[2].url
);
368 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_DIRECTORY
,
369 log_entries
[3].type
);
370 EXPECT_EQ(src_dir1
, log_entries
[3].url
);
372 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE
,
373 log_entries
[4].type
);
374 EXPECT_EQ(src_file3
, log_entries
[4].url
);
376 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE
,
377 log_entries
[5].type
);
378 EXPECT_EQ(src_file2
, log_entries
[5].url
);
380 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::POST_PROCESS_DIRECTORY
,
381 log_entries
[6].type
);
382 EXPECT_EQ(src_dir1
, log_entries
[6].url
);
384 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::POST_PROCESS_DIRECTORY
,
385 log_entries
[7].type
);
386 EXPECT_EQ(src_root
, log_entries
[7].url
);
389 } // namespace content