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"
25 class LoggingRecursiveOperation
: public RecursiveOperationDelegate
{
31 POST_PROCESS_DIRECTORY
37 LoggingRecursiveOperation(FileSystemContext
* file_system_context
,
38 const FileSystemURL
& root
,
39 const StatusCallback
& callback
)
40 : RecursiveOperationDelegate(file_system_context
),
45 virtual ~LoggingRecursiveOperation() {}
47 const std::vector
<LogEntry
>& log_entries() const { return log_entries_
; }
49 // RecursiveOperationDelegate overrides.
50 virtual void Run() OVERRIDE
{
54 virtual void RunRecursively() OVERRIDE
{
55 StartRecursiveOperation(root_
, callback_
);
58 virtual void ProcessFile(const FileSystemURL
& url
,
59 const StatusCallback
& callback
) OVERRIDE
{
60 RecordLogEntry(LogEntry::PROCESS_FILE
, url
);
61 operation_runner()->GetMetadata(
63 base::Bind(&LoggingRecursiveOperation::DidGetMetadata
,
64 weak_factory_
.GetWeakPtr(), callback
));
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
);
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
);
80 void RecordLogEntry(LogEntry::Type type
, const FileSystemURL
& url
) {
84 log_entries_
.push_back(entry
);
87 void DidGetMetadata(const StatusCallback
& callback
,
88 base::PlatformFileError result
,
89 const base::PlatformFileInfo
& file_info
) {
90 if (result
!= base::PLATFORM_FILE_OK
) {
95 callback
.Run(file_info
.is_directory
?
96 base::PLATFORM_FILE_ERROR_NOT_A_FILE
:
97 base::PLATFORM_FILE_OK
);
101 StatusCallback callback_
;
102 std::vector
<LogEntry
> log_entries_
;
104 base::WeakPtrFactory
<LoggingRecursiveOperation
> weak_factory_
;
105 DISALLOW_COPY_AND_ASSIGN(LoggingRecursiveOperation
);
108 void ReportStatus(base::PlatformFileError
* out_error
,
109 base::PlatformFileError error
) {
114 // To test the Cancel() during operation, calls Cancel() of |operation|
115 // after |counter| times message posting.
116 void CallCancelLater(RecursiveOperationDelegate
* operation
, int counter
) {
118 base::MessageLoopProxy::current()->PostTask(
120 base::Bind(&CallCancelLater
, base::Unretained(operation
), counter
- 1));
129 class RecursiveOperationDelegateTest
: public testing::Test
{
131 virtual void SetUp() OVERRIDE
{
132 EXPECT_TRUE(base_
.CreateUniqueTempDir());
133 sandbox_file_system_
.SetUp(base_
.path().AppendASCII("filesystem"));
136 virtual void TearDown() OVERRIDE
{
137 sandbox_file_system_
.TearDown();
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
);
148 FileSystemFileUtil
* file_util() {
149 return sandbox_file_system_
.file_util();
152 FileSystemURL
URLForPath(const std::string
& path
) const {
153 return sandbox_file_system_
.CreateURLFromUTF8(path
);
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(),
162 EXPECT_TRUE(created
);
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));
175 base::MessageLoop message_loop_
;
177 // Common temp base for nondestructive uses.
178 base::ScopedTempDir base_
;
179 SandboxFileSystemTestHelper sandbox_file_system_
;
182 TEST_F(RecursiveOperationDelegateTest
, RootIsFile
) {
183 FileSystemURL
src_file(CreateFile("src"));
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
);
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
);
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"));
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
);
220 const std::vector
<LoggingRecursiveOperation::LogEntry
>& log_entries
=
221 operation
->log_entries();
222 ASSERT_EQ(8U, log_entries
.size());
224 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE
,
225 log_entries
[0].type
);
226 EXPECT_EQ(src_root
, log_entries
[0].url
);
228 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_DIRECTORY
,
229 log_entries
[1].type
);
230 EXPECT_EQ(src_root
, log_entries
[1].url
);
232 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE
,
233 log_entries
[2].type
);
234 EXPECT_EQ(src_file1
, log_entries
[2].url
);
236 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_DIRECTORY
,
237 log_entries
[3].type
);
238 EXPECT_EQ(src_dir1
, log_entries
[3].url
);
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
));
251 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::POST_PROCESS_DIRECTORY
,
252 log_entries
[6].type
);
253 EXPECT_EQ(src_dir1
, log_entries
[6].url
);
255 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::POST_PROCESS_DIRECTORY
,
256 log_entries
[7].type
);
257 EXPECT_EQ(src_root
, log_entries
[7].url
);
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"));
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();
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
);
280 } // namespace fileapi