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 "webkit/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/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"
22 using storage::FileSystemContext
;
23 using storage::FileSystemOperationContext
;
24 using storage::FileSystemURL
;
29 class LoggingRecursiveOperation
: public storage::RecursiveOperationDelegate
{
35 POST_PROCESS_DIRECTORY
41 LoggingRecursiveOperation(FileSystemContext
* file_system_context
,
42 const FileSystemURL
& root
,
43 const StatusCallback
& callback
)
44 : storage::RecursiveOperationDelegate(file_system_context
),
47 weak_factory_(this) {}
48 virtual ~LoggingRecursiveOperation() {}
50 const std::vector
<LogEntry
>& log_entries() const { return log_entries_
; }
52 // RecursiveOperationDelegate overrides.
53 virtual void Run() OVERRIDE
{
57 virtual void RunRecursively() OVERRIDE
{
58 StartRecursiveOperation(root_
, callback_
);
61 virtual void ProcessFile(const FileSystemURL
& url
,
62 const StatusCallback
& callback
) OVERRIDE
{
63 RecordLogEntry(LogEntry::PROCESS_FILE
, url
);
64 operation_runner()->GetMetadata(
66 base::Bind(&LoggingRecursiveOperation::DidGetMetadata
,
67 weak_factory_
.GetWeakPtr(), 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
);
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
);
83 void RecordLogEntry(LogEntry::Type type
, const FileSystemURL
& url
) {
87 log_entries_
.push_back(entry
);
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
) {
98 callback
.Run(file_info
.is_directory
?
99 base::File::FILE_ERROR_NOT_A_FILE
:
100 base::File::FILE_OK
);
104 StatusCallback callback_
;
105 std::vector
<LogEntry
> log_entries_
;
107 base::WeakPtrFactory
<LoggingRecursiveOperation
> weak_factory_
;
108 DISALLOW_COPY_AND_ASSIGN(LoggingRecursiveOperation
);
111 void ReportStatus(base::File::Error
* out_error
,
112 base::File::Error error
) {
117 // To test the Cancel() during operation, calls Cancel() of |operation|
118 // after |counter| times message posting.
119 void CallCancelLater(storage::RecursiveOperationDelegate
* operation
,
122 base::MessageLoopProxy::current()->PostTask(
124 base::Bind(&CallCancelLater
, base::Unretained(operation
), counter
- 1));
133 class RecursiveOperationDelegateTest
: public testing::Test
{
135 virtual void SetUp() OVERRIDE
{
136 EXPECT_TRUE(base_
.CreateUniqueTempDir());
137 sandbox_file_system_
.SetUp(base_
.path().AppendASCII("filesystem"));
140 virtual void TearDown() OVERRIDE
{
141 sandbox_file_system_
.TearDown();
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
);
152 storage::FileSystemFileUtil
* file_util() {
153 return sandbox_file_system_
.file_util();
156 FileSystemURL
URLForPath(const std::string
& path
) const {
157 return sandbox_file_system_
.CreateURLFromUTF8(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(),
166 EXPECT_TRUE(created
);
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));
179 base::MessageLoop message_loop_
;
181 // Common temp base for nondestructive uses.
182 base::ScopedTempDir base_
;
183 SandboxFileSystemTestHelper sandbox_file_system_
;
186 TEST_F(RecursiveOperationDelegateTest
, RootIsFile
) {
187 FileSystemURL
src_file(CreateFile("src"));
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
);
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
);
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"));
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
);
224 const std::vector
<LoggingRecursiveOperation::LogEntry
>& log_entries
=
225 operation
->log_entries();
226 ASSERT_EQ(8U, log_entries
.size());
228 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE
,
229 log_entries
[0].type
);
230 EXPECT_EQ(src_root
, log_entries
[0].url
);
232 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_DIRECTORY
,
233 log_entries
[1].type
);
234 EXPECT_EQ(src_root
, log_entries
[1].url
);
236 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE
,
237 log_entries
[2].type
);
238 EXPECT_EQ(src_file1
, log_entries
[2].url
);
240 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_DIRECTORY
,
241 log_entries
[3].type
);
242 EXPECT_EQ(src_dir1
, log_entries
[3].url
);
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
));
255 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::POST_PROCESS_DIRECTORY
,
256 log_entries
[6].type
);
257 EXPECT_EQ(src_dir1
, log_entries
[6].url
);
259 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::POST_PROCESS_DIRECTORY
,
260 log_entries
[7].type
);
261 EXPECT_EQ(src_root
, log_entries
[7].url
);
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"));
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();
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
);
284 } // namespace content