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/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 "storage/browser/fileapi/file_system_file_util.h"
18 #include "storage/browser/fileapi/file_system_operation.h"
19 #include "storage/browser/fileapi/file_system_operation_runner.h"
20 #include "testing/gtest/include/gtest/gtest.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 ~LoggingRecursiveOperation() override
{}
50 const std::vector
<LogEntry
>& log_entries() const { return log_entries_
; }
52 // RecursiveOperationDelegate overrides.
53 void Run() override
{ NOTREACHED(); }
55 void RunRecursively() override
{ StartRecursiveOperation(root_
, callback_
); }
57 void ProcessFile(const FileSystemURL
& url
,
58 const StatusCallback
& callback
) override
{
59 RecordLogEntry(LogEntry::PROCESS_FILE
, url
);
60 operation_runner()->GetMetadata(
62 base::Bind(&LoggingRecursiveOperation::DidGetMetadata
,
63 weak_factory_
.GetWeakPtr(), callback
));
66 void ProcessDirectory(const FileSystemURL
& url
,
67 const StatusCallback
& callback
) override
{
68 RecordLogEntry(LogEntry::PROCESS_DIRECTORY
, url
);
69 callback
.Run(base::File::FILE_OK
);
72 void PostProcessDirectory(const FileSystemURL
& url
,
73 const StatusCallback
& callback
) override
{
74 RecordLogEntry(LogEntry::POST_PROCESS_DIRECTORY
, url
);
75 callback
.Run(base::File::FILE_OK
);
79 void RecordLogEntry(LogEntry::Type type
, const FileSystemURL
& url
) {
83 log_entries_
.push_back(entry
);
86 void DidGetMetadata(const StatusCallback
& callback
,
87 base::File::Error result
,
88 const base::File::Info
& file_info
) {
89 if (result
!= base::File::FILE_OK
) {
94 callback
.Run(file_info
.is_directory
?
95 base::File::FILE_ERROR_NOT_A_FILE
:
100 StatusCallback callback_
;
101 std::vector
<LogEntry
> log_entries_
;
103 base::WeakPtrFactory
<LoggingRecursiveOperation
> weak_factory_
;
104 DISALLOW_COPY_AND_ASSIGN(LoggingRecursiveOperation
);
107 void ReportStatus(base::File::Error
* out_error
,
108 base::File::Error error
) {
113 // To test the Cancel() during operation, calls Cancel() of |operation|
114 // after |counter| times message posting.
115 void CallCancelLater(storage::RecursiveOperationDelegate
* operation
,
118 base::MessageLoopProxy::current()->PostTask(
120 base::Bind(&CallCancelLater
, base::Unretained(operation
), counter
- 1));
129 class RecursiveOperationDelegateTest
: public testing::Test
{
131 void SetUp() override
{
132 EXPECT_TRUE(base_
.CreateUniqueTempDir());
133 sandbox_file_system_
.SetUp(base_
.path().AppendASCII("filesystem"));
136 void TearDown() override
{ sandbox_file_system_
.TearDown(); }
138 scoped_ptr
<FileSystemOperationContext
> NewContext() {
139 FileSystemOperationContext
* context
=
140 sandbox_file_system_
.NewOperationContext();
141 // Grant enough quota for all test cases.
142 context
->set_allowed_bytes_growth(1000000);
143 return make_scoped_ptr(context
);
146 storage::FileSystemFileUtil
* file_util() {
147 return sandbox_file_system_
.file_util();
150 FileSystemURL
URLForPath(const std::string
& path
) const {
151 return sandbox_file_system_
.CreateURLFromUTF8(path
);
154 FileSystemURL
CreateFile(const std::string
& path
) {
155 FileSystemURL url
= URLForPath(path
);
156 bool created
= false;
157 EXPECT_EQ(base::File::FILE_OK
,
158 file_util()->EnsureFileExists(NewContext().get(),
160 EXPECT_TRUE(created
);
164 FileSystemURL
CreateDirectory(const std::string
& path
) {
165 FileSystemURL url
= URLForPath(path
);
166 EXPECT_EQ(base::File::FILE_OK
,
167 file_util()->CreateDirectory(NewContext().get(), url
,
168 false /* exclusive */, true));
173 base::MessageLoop message_loop_
;
175 // Common temp base for nondestructive uses.
176 base::ScopedTempDir base_
;
177 SandboxFileSystemTestHelper sandbox_file_system_
;
180 TEST_F(RecursiveOperationDelegateTest
, RootIsFile
) {
181 FileSystemURL
src_file(CreateFile("src"));
183 base::File::Error error
= base::File::FILE_ERROR_FAILED
;
184 scoped_ptr
<FileSystemOperationContext
> context
= NewContext();
185 scoped_ptr
<LoggingRecursiveOperation
> operation(
186 new LoggingRecursiveOperation(
187 context
->file_system_context(), src_file
,
188 base::Bind(&ReportStatus
, &error
)));
189 operation
->RunRecursively();
190 base::RunLoop().RunUntilIdle();
191 ASSERT_EQ(base::File::FILE_OK
, error
);
193 const std::vector
<LoggingRecursiveOperation::LogEntry
>& log_entries
=
194 operation
->log_entries();
195 ASSERT_EQ(1U, log_entries
.size());
196 const LoggingRecursiveOperation::LogEntry
& entry
= log_entries
[0];
197 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE
, entry
.type
);
198 EXPECT_EQ(src_file
, entry
.url
);
201 TEST_F(RecursiveOperationDelegateTest
, RootIsDirectory
) {
202 FileSystemURL
src_root(CreateDirectory("src"));
203 FileSystemURL
src_dir1(CreateDirectory("src/dir1"));
204 FileSystemURL
src_file1(CreateFile("src/file1"));
205 FileSystemURL
src_file2(CreateFile("src/dir1/file2"));
206 FileSystemURL
src_file3(CreateFile("src/dir1/file3"));
208 base::File::Error error
= base::File::FILE_ERROR_FAILED
;
209 scoped_ptr
<FileSystemOperationContext
> context
= NewContext();
210 scoped_ptr
<LoggingRecursiveOperation
> operation(
211 new LoggingRecursiveOperation(
212 context
->file_system_context(), src_root
,
213 base::Bind(&ReportStatus
, &error
)));
214 operation
->RunRecursively();
215 base::RunLoop().RunUntilIdle();
216 ASSERT_EQ(base::File::FILE_OK
, error
);
218 const std::vector
<LoggingRecursiveOperation::LogEntry
>& log_entries
=
219 operation
->log_entries();
220 ASSERT_EQ(8U, log_entries
.size());
222 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE
,
223 log_entries
[0].type
);
224 EXPECT_EQ(src_root
, log_entries
[0].url
);
226 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_DIRECTORY
,
227 log_entries
[1].type
);
228 EXPECT_EQ(src_root
, log_entries
[1].url
);
230 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE
,
231 log_entries
[2].type
);
232 EXPECT_EQ(src_file1
, log_entries
[2].url
);
234 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_DIRECTORY
,
235 log_entries
[3].type
);
236 EXPECT_EQ(src_dir1
, log_entries
[3].url
);
238 // The order of src/dir1/file2 and src/dir1/file3 depends on the file system
239 // implementation (can be swapped).
240 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE
,
241 log_entries
[4].type
);
242 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::PROCESS_FILE
,
243 log_entries
[5].type
);
244 EXPECT_TRUE((src_file2
== log_entries
[4].url
&&
245 src_file3
== log_entries
[5].url
) ||
246 (src_file3
== log_entries
[4].url
&&
247 src_file2
== log_entries
[5].url
));
249 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::POST_PROCESS_DIRECTORY
,
250 log_entries
[6].type
);
251 EXPECT_EQ(src_dir1
, log_entries
[6].url
);
253 EXPECT_EQ(LoggingRecursiveOperation::LogEntry::POST_PROCESS_DIRECTORY
,
254 log_entries
[7].type
);
255 EXPECT_EQ(src_root
, log_entries
[7].url
);
258 TEST_F(RecursiveOperationDelegateTest
, Cancel
) {
259 FileSystemURL
src_root(CreateDirectory("src"));
260 FileSystemURL
src_dir1(CreateDirectory("src/dir1"));
261 FileSystemURL
src_file1(CreateFile("src/file1"));
262 FileSystemURL
src_file2(CreateFile("src/dir1/file2"));
264 base::File::Error error
= base::File::FILE_ERROR_FAILED
;
265 scoped_ptr
<FileSystemOperationContext
> context
= NewContext();
266 scoped_ptr
<LoggingRecursiveOperation
> operation(
267 new LoggingRecursiveOperation(
268 context
->file_system_context(), src_root
,
269 base::Bind(&ReportStatus
, &error
)));
270 operation
->RunRecursively();
272 // Invoke Cancel(), after 5 times message posting.
273 CallCancelLater(operation
.get(), 5);
274 base::RunLoop().RunUntilIdle();
275 ASSERT_EQ(base::File::FILE_ERROR_ABORT
, error
);
278 } // namespace content