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.
7 #include "base/files/scoped_temp_dir.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/memory/weak_ptr.h"
10 #include "base/run_loop.h"
11 #include "base/thread_task_runner_handle.h"
12 #include "content/browser/fileapi/mock_file_change_observer.h"
13 #include "content/browser/quota/mock_quota_manager.h"
14 #include "content/public/test/mock_blob_url_request_context.h"
15 #include "content/public/test/test_file_system_backend.h"
16 #include "content/public/test/test_file_system_context.h"
17 #include "net/url_request/url_request.h"
18 #include "net/url_request/url_request_context.h"
19 #include "net/url_request/url_request_job.h"
20 #include "net/url_request/url_request_job_factory_impl.h"
21 #include "storage/browser/blob/blob_storage_context.h"
22 #include "storage/browser/blob/blob_url_request_job.h"
23 #include "storage/browser/fileapi/file_system_context.h"
24 #include "storage/browser/fileapi/file_system_file_util.h"
25 #include "storage/browser/fileapi/file_system_operation_context.h"
26 #include "storage/browser/fileapi/file_system_operation_runner.h"
27 #include "storage/browser/fileapi/local_file_util.h"
28 #include "storage/common/fileapi/file_system_util.h"
29 #include "testing/gtest/include/gtest/gtest.h"
32 using storage::FileSystemOperation
;
33 using storage::FileSystemOperationRunner
;
34 using storage::FileSystemURL
;
35 using content::MockBlobURLRequestContext
;
36 using content::ScopedTextBlob
;
42 const GURL
kOrigin("http://example.com");
43 const storage::FileSystemType kFileSystemType
= storage::kFileSystemTypeTest
;
45 void AssertStatusEq(base::File::Error expected
,
46 base::File::Error actual
) {
47 ASSERT_EQ(expected
, actual
);
52 class FileSystemOperationImplWriteTest
53 : public testing::Test
{
55 FileSystemOperationImplWriteTest()
56 : status_(base::File::FILE_OK
),
57 cancel_status_(base::File::FILE_ERROR_FAILED
),
62 storage::MockFileChangeObserver::CreateList(&change_observer_
);
65 void SetUp() override
{
66 ASSERT_TRUE(dir_
.CreateUniqueTempDir());
69 new MockQuotaManager(false /* is_incognito */, dir_
.path(),
70 base::ThreadTaskRunnerHandle::Get().get(),
71 base::ThreadTaskRunnerHandle::Get().get(),
72 NULL
/* special storage policy */);
73 virtual_path_
= base::FilePath(FILE_PATH_LITERAL("temporary file"));
75 file_system_context_
= CreateFileSystemContextForTesting(
76 quota_manager_
->proxy(), dir_
.path());
77 url_request_context_
.reset(
78 new MockBlobURLRequestContext(file_system_context_
.get()));
80 file_system_context_
->operation_runner()->CreateFile(
81 URLForPath(virtual_path_
), true /* exclusive */,
82 base::Bind(&AssertStatusEq
, base::File::FILE_OK
));
84 static_cast<TestFileSystemBackend
*>(
85 file_system_context_
->GetFileSystemBackend(kFileSystemType
))
86 ->AddFileChangeObserver(change_observer());
89 void TearDown() override
{
90 quota_manager_
= NULL
;
91 file_system_context_
= NULL
;
92 base::RunLoop().RunUntilIdle();
95 base::File::Error
status() const { return status_
; }
96 base::File::Error
cancel_status() const { return cancel_status_
; }
97 void add_bytes_written(int64 bytes
, bool complete
) {
98 bytes_written_
+= bytes
;
99 EXPECT_FALSE(complete_
);
100 complete_
= complete
;
102 int64
bytes_written() const { return bytes_written_
; }
103 bool complete() const { return complete_
; }
106 const storage::ChangeObserverList
& change_observers() const {
107 return change_observers_
;
110 storage::MockFileChangeObserver
* change_observer() {
111 return &change_observer_
;
114 FileSystemURL
URLForPath(const base::FilePath
& path
) const {
115 return file_system_context_
->CreateCrackedFileSystemURL(
116 kOrigin
, kFileSystemType
, path
);
119 // Callback function for recording test results.
120 FileSystemOperation::WriteCallback
RecordWriteCallback() {
121 return base::Bind(&FileSystemOperationImplWriteTest::DidWrite
,
122 weak_factory_
.GetWeakPtr());
125 FileSystemOperation::StatusCallback
RecordCancelCallback() {
126 return base::Bind(&FileSystemOperationImplWriteTest::DidCancel
,
127 weak_factory_
.GetWeakPtr());
130 void DidWrite(base::File::Error status
, int64 bytes
, bool complete
) {
131 if (status
== base::File::FILE_OK
) {
132 add_bytes_written(bytes
, complete
);
134 base::MessageLoop::current()->Quit();
136 EXPECT_FALSE(complete_
);
137 EXPECT_EQ(status_
, base::File::FILE_OK
);
140 if (base::MessageLoop::current()->is_running())
141 base::MessageLoop::current()->Quit();
145 void DidCancel(base::File::Error status
) {
146 cancel_status_
= status
;
149 const MockBlobURLRequestContext
& url_request_context() const {
150 return *url_request_context_
;
153 scoped_refptr
<storage::FileSystemContext
> file_system_context_
;
154 scoped_refptr
<MockQuotaManager
> quota_manager_
;
156 base::MessageLoopForIO loop_
;
158 base::ScopedTempDir dir_
;
159 base::FilePath virtual_path_
;
161 // For post-operation status.
162 base::File::Error status_
;
163 base::File::Error cancel_status_
;
164 int64 bytes_written_
;
167 scoped_ptr
<MockBlobURLRequestContext
> url_request_context_
;
169 storage::MockFileChangeObserver change_observer_
;
170 storage::ChangeObserverList change_observers_
;
172 base::WeakPtrFactory
<FileSystemOperationImplWriteTest
> weak_factory_
;
174 DISALLOW_COPY_AND_ASSIGN(FileSystemOperationImplWriteTest
);
177 TEST_F(FileSystemOperationImplWriteTest
, TestWriteSuccess
) {
178 ScopedTextBlob
blob(url_request_context(),
181 file_system_context_
->operation_runner()->Write(
182 &url_request_context(), URLForPath(virtual_path_
),
183 blob
.GetBlobDataHandle(),
184 0, RecordWriteCallback());
185 base::MessageLoop::current()->Run();
187 EXPECT_EQ(14, bytes_written());
188 EXPECT_EQ(base::File::FILE_OK
, status());
189 EXPECT_TRUE(complete());
191 EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count());
194 TEST_F(FileSystemOperationImplWriteTest
, TestWriteZero
) {
195 ScopedTextBlob
blob(url_request_context(), "blob_id:zero", "");
196 file_system_context_
->operation_runner()->Write(
197 &url_request_context(), URLForPath(virtual_path_
),
198 blob
.GetBlobDataHandle(), 0, RecordWriteCallback());
199 base::MessageLoop::current()->Run();
201 EXPECT_EQ(0, bytes_written());
202 EXPECT_EQ(base::File::FILE_OK
, status());
203 EXPECT_TRUE(complete());
205 EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count());
209 TEST_F(FileSystemOperationImplWriteTest
, TestWriteInvalidBlobUrl
) {
210 scoped_ptr
<storage::BlobDataHandle
> null_handle
;
211 file_system_context_
->operation_runner()->Write(
212 &url_request_context(), URLForPath(virtual_path_
),
213 null_handle
.Pass(), 0, RecordWriteCallback());
214 base::MessageLoop::current()->Run();
216 EXPECT_EQ(0, bytes_written());
217 EXPECT_EQ(base::File::FILE_ERROR_FAILED
, status());
218 EXPECT_TRUE(complete());
220 EXPECT_EQ(0, change_observer()->get_and_reset_modify_file_count());
223 TEST_F(FileSystemOperationImplWriteTest
, TestWriteInvalidFile
) {
224 ScopedTextBlob
blob(url_request_context(), "blob_id:writeinvalidfile",
225 "It\'ll not be written.");
226 file_system_context_
->operation_runner()->Write(
227 &url_request_context(),
228 URLForPath(base::FilePath(FILE_PATH_LITERAL("nonexist"))),
229 blob
.GetBlobDataHandle(), 0, RecordWriteCallback());
230 base::MessageLoop::current()->Run();
232 EXPECT_EQ(0, bytes_written());
233 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND
, status());
234 EXPECT_TRUE(complete());
236 EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count());
239 TEST_F(FileSystemOperationImplWriteTest
, TestWriteDir
) {
240 base::FilePath
virtual_dir_path(FILE_PATH_LITERAL("d"));
241 file_system_context_
->operation_runner()->CreateDirectory(
242 URLForPath(virtual_dir_path
),
243 true /* exclusive */, false /* recursive */,
244 base::Bind(&AssertStatusEq
, base::File::FILE_OK
));
246 ScopedTextBlob
blob(url_request_context(), "blob:writedir",
247 "It\'ll not be written, too.");
248 file_system_context_
->operation_runner()->Write(
249 &url_request_context(), URLForPath(virtual_dir_path
),
250 blob
.GetBlobDataHandle(), 0, RecordWriteCallback());
251 base::MessageLoop::current()->Run();
253 EXPECT_EQ(0, bytes_written());
254 // TODO(kinuko): This error code is platform- or fileutil- dependent
255 // right now. Make it return File::FILE_ERROR_NOT_A_FILE in every case.
256 EXPECT_TRUE(status() == base::File::FILE_ERROR_NOT_A_FILE
||
257 status() == base::File::FILE_ERROR_ACCESS_DENIED
||
258 status() == base::File::FILE_ERROR_FAILED
);
259 EXPECT_TRUE(complete());
261 EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count());
264 TEST_F(FileSystemOperationImplWriteTest
, TestWriteFailureByQuota
) {
265 ScopedTextBlob
blob(url_request_context(), "blob:success",
267 quota_manager_
->SetQuota(
268 kOrigin
, FileSystemTypeToQuotaStorageType(kFileSystemType
), 10);
269 file_system_context_
->operation_runner()->Write(
270 &url_request_context(), URLForPath(virtual_path_
),
271 blob
.GetBlobDataHandle(), 0, RecordWriteCallback());
272 base::MessageLoop::current()->Run();
274 EXPECT_EQ(10, bytes_written());
275 EXPECT_EQ(base::File::FILE_ERROR_NO_SPACE
, status());
276 EXPECT_TRUE(complete());
278 EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count());
281 TEST_F(FileSystemOperationImplWriteTest
, TestImmediateCancelSuccessfulWrite
) {
282 ScopedTextBlob
blob(url_request_context(), "blob:success",
284 FileSystemOperationRunner::OperationID id
=
285 file_system_context_
->operation_runner()->Write(
286 &url_request_context(), URLForPath(virtual_path_
),
287 blob
.GetBlobDataHandle(), 0, RecordWriteCallback());
288 file_system_context_
->operation_runner()->Cancel(id
, RecordCancelCallback());
289 // We use RunAllPendings() instead of Run() here, because we won't dispatch
290 // callbacks after Cancel() is issued (so no chance to Quit) nor do we need
291 // to run another write cycle.
292 base::RunLoop().RunUntilIdle();
294 // Issued Cancel() before receiving any response from Write(),
295 // so nothing should have happen.
296 EXPECT_EQ(0, bytes_written());
297 EXPECT_EQ(base::File::FILE_ERROR_ABORT
, status());
298 EXPECT_EQ(base::File::FILE_OK
, cancel_status());
299 EXPECT_TRUE(complete());
301 EXPECT_EQ(0, change_observer()->get_and_reset_modify_file_count());
304 TEST_F(FileSystemOperationImplWriteTest
, TestImmediateCancelFailingWrite
) {
305 ScopedTextBlob
blob(url_request_context(), "blob:writeinvalidfile",
306 "It\'ll not be written.");
307 FileSystemOperationRunner::OperationID id
=
308 file_system_context_
->operation_runner()->Write(
309 &url_request_context(),
310 URLForPath(base::FilePath(FILE_PATH_LITERAL("nonexist"))),
311 blob
.GetBlobDataHandle(), 0, RecordWriteCallback());
312 file_system_context_
->operation_runner()->Cancel(id
, RecordCancelCallback());
313 // We use RunAllPendings() instead of Run() here, because we won't dispatch
314 // callbacks after Cancel() is issued (so no chance to Quit) nor do we need
315 // to run another write cycle.
316 base::RunLoop().RunUntilIdle();
318 // Issued Cancel() before receiving any response from Write(),
319 // so nothing should have happen.
320 EXPECT_EQ(0, bytes_written());
321 EXPECT_EQ(base::File::FILE_ERROR_ABORT
, status());
322 EXPECT_EQ(base::File::FILE_OK
, cancel_status());
323 EXPECT_TRUE(complete());
325 EXPECT_EQ(0, change_observer()->get_and_reset_modify_file_count());
328 // TODO(ericu,dmikurube,kinuko): Add more tests for cancel cases.
330 } // namespace content