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 fileapi::FileSystemContext
;
23 using fileapi::FileSystemOperationContext
;
24 using fileapi::FileSystemURL
;
29 class LoggingRecursiveOperation
: public fileapi::RecursiveOperationDelegate
{
35 POST_PROCESS_DIRECTORY
41 LoggingRecursiveOperation(FileSystemContext
* file_system_context
,
42 const FileSystemURL
& root
,
43 const StatusCallback
& callback
)
44 : fileapi::RecursiveOperationDelegate(file_system_context
),
49 virtual ~LoggingRecursiveOperation() {}
51 const std::vector
<LogEntry
>& log_entries() const { return log_entries_
; }
53 // RecursiveOperationDelegate overrides.
54 virtual void Run() OVERRIDE
{
58 virtual void RunRecursively() OVERRIDE
{
59 StartRecursiveOperation(root_
, callback_
);
62 virtual void ProcessFile(const FileSystemURL
& url
,
63 const StatusCallback
& callback
) OVERRIDE
{
64 RecordLogEntry(LogEntry::PROCESS_FILE
, url
);
65 operation_runner()->GetMetadata(
67 base::Bind(&LoggingRecursiveOperation::DidGetMetadata
,
68 weak_factory_
.GetWeakPtr(), callback
));
71 virtual void ProcessDirectory(const FileSystemURL
& url
,
72 const StatusCallback
& callback
) OVERRIDE
{
73 RecordLogEntry(LogEntry::PROCESS_DIRECTORY
, url
);
74 callback
.Run(base::File::FILE_OK
);
77 virtual void PostProcessDirectory(const FileSystemURL
& url
,
78 const StatusCallback
& callback
) OVERRIDE
{
79 RecordLogEntry(LogEntry::POST_PROCESS_DIRECTORY
, url
);
80 callback
.Run(base::File::FILE_OK
);
84 void RecordLogEntry(LogEntry::Type type
, const FileSystemURL
& url
) {
88 log_entries_
.push_back(entry
);
91 void DidGetMetadata(const StatusCallback
& callback
,
92 base::File::Error result
,
93 const base::File::Info
& file_info
) {
94 if (result
!= base::File::FILE_OK
) {
99 callback
.Run(file_info
.is_directory
?
100 base::File::FILE_ERROR_NOT_A_FILE
:
101 base::File::FILE_OK
);
105 StatusCallback callback_
;
106 std::vector
<LogEntry
> log_entries_
;
108 base::WeakPtrFactory
<LoggingRecursiveOperation
> weak_factory_
;
109 DISALLOW_COPY_AND_ASSIGN(LoggingRecursiveOperation
);
112 void ReportStatus(base::File::Error
* out_error
,
113 base::File::Error error
) {
118 // To test the Cancel() during operation, calls Cancel() of |operation|
119 // after |counter| times message posting.
120 void CallCancelLater(fileapi::RecursiveOperationDelegate
* operation
,
123 base::MessageLoopProxy::current()->PostTask(
125 base::Bind(&CallCancelLater
, base::Unretained(operation
), counter
- 1));
134 class RecursiveOperationDelegateTest
: public testing::Test
{
136 virtual void SetUp() OVERRIDE
{
137 EXPECT_TRUE(base_
.CreateUniqueTempDir());
138 sandbox_file_system_
.SetUp(base_
.path().AppendASCII("filesystem"));
141 virtual void TearDown() OVERRIDE
{
142 sandbox_file_system_
.TearDown();
145 scoped_ptr
<FileSystemOperationContext
> NewContext() {
146 FileSystemOperationContext
* context
=
147 sandbox_file_system_
.NewOperationContext();
148 // Grant enough quota for all test cases.
149 context
->set_allowed_bytes_growth(1000000);
150 return make_scoped_ptr(context
);
153 fileapi::FileSystemFileUtil
* file_util() {
154 return sandbox_file_system_
.file_util();
157 FileSystemURL
URLForPath(const std::string
& path
) const {
158 return sandbox_file_system_
.CreateURLFromUTF8(path
);
161 FileSystemURL
CreateFile(const std::string
& path
) {
162 FileSystemURL url
= URLForPath(path
);
163 bool created
= false;
164 EXPECT_EQ(base::File::FILE_OK
,
165 file_util()->EnsureFileExists(NewContext().get(),
167 EXPECT_TRUE(created
);
171 FileSystemURL
CreateDirectory(const std::string
& path
) {
172 FileSystemURL url
= URLForPath(path
);
173 EXPECT_EQ(base::File::FILE_OK
,
174 file_util()->CreateDirectory(NewContext().get(), url
,
175 false /* exclusive */, true));
180 base::MessageLoop message_loop_
;
182 // Common temp base for nondestructive uses.
183 base::ScopedTempDir base_
;
184 SandboxFileSystemTestHelper sandbox_file_system_
;
187 TEST_F(RecursiveOperationDelegateTest
, RootIsFile
) {
188 FileSystemURL
src_file(CreateFile("src"));
190 base::File::Error error
= base::File::FILE_ERROR_FAILED
;
191 scoped_ptr
<FileSystemOperationContext
> context
= NewContext();
192 scoped_ptr
<LoggingRecursiveOperation
> operation(
193 new LoggingRecursiveOperation(
194 context
->file_system_context(), src_file
,
195 base::Bind(&ReportStatus
, &error
)));
196 operation
->RunRecursively();
197 base::RunLoop().RunUntilIdle();
198 ASSERT_EQ(base::File::FILE_OK
, error
);
200 const std::vector
<LoggingRecursiveOperation::LogEntry
>& log_entries
=
201 operation
->log_entries();
202 ASSERT_EQ(1U, log_entries
.size());
203 const LoggingRecursiveOperation::LogEntry
& entry
= log_entries
[0];
204 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE
, entry
.type
);
205 EXPECT_EQ(src_file
, entry
.url
);
208 TEST_F(RecursiveOperationDelegateTest
, RootIsDirectory
) {
209 FileSystemURL
src_root(CreateDirectory("src"));
210 FileSystemURL
src_dir1(CreateDirectory("src/dir1"));
211 FileSystemURL
src_file1(CreateFile("src/file1"));
212 FileSystemURL
src_file2(CreateFile("src/dir1/file2"));
213 FileSystemURL
src_file3(CreateFile("src/dir1/file3"));
215 base::File::Error error
= base::File::FILE_ERROR_FAILED
;
216 scoped_ptr
<FileSystemOperationContext
> context
= NewContext();
217 scoped_ptr
<LoggingRecursiveOperation
> operation(
218 new LoggingRecursiveOperation(
219 context
->file_system_context(), src_root
,
220 base::Bind(&ReportStatus
, &error
)));
221 operation
->RunRecursively();
222 base::RunLoop().RunUntilIdle();
223 ASSERT_EQ(base::File::FILE_OK
, error
);
225 const std::vector
<LoggingRecursiveOperation::LogEntry
>& log_entries
=
226 operation
->log_entries();
227 ASSERT_EQ(8U, log_entries
.size());
229 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE
,
230 log_entries
[0].type
);
231 EXPECT_EQ(src_root
, log_entries
[0].url
);
233 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_DIRECTORY
,
234 log_entries
[1].type
);
235 EXPECT_EQ(src_root
, log_entries
[1].url
);
237 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE
,
238 log_entries
[2].type
);
239 EXPECT_EQ(src_file1
, log_entries
[2].url
);
241 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_DIRECTORY
,
242 log_entries
[3].type
);
243 EXPECT_EQ(src_dir1
, log_entries
[3].url
);
245 // The order of src/dir1/file2 and src/dir1/file3 depends on the file system
246 // implementation (can be swapped).
247 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE
,
248 log_entries
[4].type
);
249 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE
,
250 log_entries
[5].type
);
251 EXPECT_TRUE((src_file2
== log_entries
[4].url
&&
252 src_file3
== log_entries
[5].url
) ||
253 (src_file3
== log_entries
[4].url
&&
254 src_file2
== log_entries
[5].url
));
256 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::POST_PROCESS_DIRECTORY
,
257 log_entries
[6].type
);
258 EXPECT_EQ(src_dir1
, log_entries
[6].url
);
260 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::POST_PROCESS_DIRECTORY
,
261 log_entries
[7].type
);
262 EXPECT_EQ(src_root
, log_entries
[7].url
);
265 TEST_F(RecursiveOperationDelegateTest
, Cancel
) {
266 FileSystemURL
src_root(CreateDirectory("src"));
267 FileSystemURL
src_dir1(CreateDirectory("src/dir1"));
268 FileSystemURL
src_file1(CreateFile("src/file1"));
269 FileSystemURL
src_file2(CreateFile("src/dir1/file2"));
271 base::File::Error error
= base::File::FILE_ERROR_FAILED
;
272 scoped_ptr
<FileSystemOperationContext
> context
= NewContext();
273 scoped_ptr
<LoggingRecursiveOperation
> operation(
274 new LoggingRecursiveOperation(
275 context
->file_system_context(), src_root
,
276 base::Bind(&ReportStatus
, &error
)));
277 operation
->RunRecursively();
279 // Invoke Cancel(), after 5 times message posting.
280 CallCancelLater(operation
.get(), 5);
281 base::RunLoop().RunUntilIdle();
282 ASSERT_EQ(base::File::FILE_ERROR_ABORT
, error
);
285 } // namespace content