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. 4 5#include <vector> 6 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/message_loop/message_loop_proxy.h" 11#include "base/run_loop.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/blob/blob_data.h" 29#include "storage/common/fileapi/file_system_util.h" 30#include "testing/gtest/include/gtest/gtest.h" 31#include "url/gurl.h" 32 33using storage::FileSystemOperation; 34using storage::FileSystemOperationRunner; 35using storage::FileSystemURL; 36using content::MockBlobURLRequestContext; 37using content::ScopedTextBlob; 38 39namespace content { 40 41namespace { 42 43const GURL kOrigin("http://example.com"); 44const storage::FileSystemType kFileSystemType = storage::kFileSystemTypeTest; 45 46void AssertStatusEq(base::File::Error expected, 47 base::File::Error actual) { 48 ASSERT_EQ(expected, actual); 49} 50 51} // namespace 52 53class FileSystemOperationImplWriteTest 54 : public testing::Test { 55 public: 56 FileSystemOperationImplWriteTest() 57 : status_(base::File::FILE_OK), 58 cancel_status_(base::File::FILE_ERROR_FAILED), 59 bytes_written_(0), 60 complete_(false), 61 weak_factory_(this) { 62 change_observers_ = 63 storage::MockFileChangeObserver::CreateList(&change_observer_); 64 } 65 66 virtual void SetUp() { 67 ASSERT_TRUE(dir_.CreateUniqueTempDir()); 68 69 quota_manager_ = 70 new MockQuotaManager(false /* is_incognito */, 71 dir_.path(), 72 base::MessageLoopProxy::current().get(), 73 base::MessageLoopProxy::current().get(), 74 NULL /* special storage policy */); 75 virtual_path_ = base::FilePath(FILE_PATH_LITERAL("temporary file")); 76 77 file_system_context_ = CreateFileSystemContextForTesting( 78 quota_manager_->proxy(), dir_.path()); 79 url_request_context_.reset( 80 new MockBlobURLRequestContext(file_system_context_.get())); 81 82 file_system_context_->operation_runner()->CreateFile( 83 URLForPath(virtual_path_), true /* exclusive */, 84 base::Bind(&AssertStatusEq, base::File::FILE_OK)); 85 86 static_cast<TestFileSystemBackend*>( 87 file_system_context_->GetFileSystemBackend(kFileSystemType)) 88 ->AddFileChangeObserver(change_observer()); 89 } 90 91 virtual void TearDown() { 92 quota_manager_ = NULL; 93 file_system_context_ = NULL; 94 base::RunLoop().RunUntilIdle(); 95 } 96 97 base::File::Error status() const { return status_; } 98 base::File::Error cancel_status() const { return cancel_status_; } 99 void add_bytes_written(int64 bytes, bool complete) { 100 bytes_written_ += bytes; 101 EXPECT_FALSE(complete_); 102 complete_ = complete; 103 } 104 int64 bytes_written() const { return bytes_written_; } 105 bool complete() const { return complete_; } 106 107 protected: 108 const storage::ChangeObserverList& change_observers() const { 109 return change_observers_; 110 } 111 112 storage::MockFileChangeObserver* change_observer() { 113 return &change_observer_; 114 } 115 116 FileSystemURL URLForPath(const base::FilePath& path) const { 117 return file_system_context_->CreateCrackedFileSystemURL( 118 kOrigin, kFileSystemType, path); 119 } 120 121 // Callback function for recording test results. 122 FileSystemOperation::WriteCallback RecordWriteCallback() { 123 return base::Bind(&FileSystemOperationImplWriteTest::DidWrite, 124 weak_factory_.GetWeakPtr()); 125 } 126 127 FileSystemOperation::StatusCallback RecordCancelCallback() { 128 return base::Bind(&FileSystemOperationImplWriteTest::DidCancel, 129 weak_factory_.GetWeakPtr()); 130 } 131 132 void DidWrite(base::File::Error status, int64 bytes, bool complete) { 133 if (status == base::File::FILE_OK) { 134 add_bytes_written(bytes, complete); 135 if (complete) 136 base::MessageLoop::current()->Quit(); 137 } else { 138 EXPECT_FALSE(complete_); 139 EXPECT_EQ(status_, base::File::FILE_OK); 140 complete_ = true; 141 status_ = status; 142 if (base::MessageLoop::current()->is_running()) 143 base::MessageLoop::current()->Quit(); 144 } 145 } 146 147 void DidCancel(base::File::Error status) { 148 cancel_status_ = status; 149 } 150 151 const MockBlobURLRequestContext& url_request_context() const { 152 return *url_request_context_; 153 } 154 155 scoped_refptr<storage::FileSystemContext> file_system_context_; 156 scoped_refptr<MockQuotaManager> quota_manager_; 157 158 base::MessageLoopForIO loop_; 159 160 base::ScopedTempDir dir_; 161 base::FilePath virtual_path_; 162 163 // For post-operation status. 164 base::File::Error status_; 165 base::File::Error cancel_status_; 166 int64 bytes_written_; 167 bool complete_; 168 169 scoped_ptr<MockBlobURLRequestContext> url_request_context_; 170 171 storage::MockFileChangeObserver change_observer_; 172 storage::ChangeObserverList change_observers_; 173 174 base::WeakPtrFactory<FileSystemOperationImplWriteTest> weak_factory_; 175 176 DISALLOW_COPY_AND_ASSIGN(FileSystemOperationImplWriteTest); 177}; 178 179TEST_F(FileSystemOperationImplWriteTest, TestWriteSuccess) { 180 ScopedTextBlob blob(url_request_context(), 181 "blob-id:success", 182 "Hello, world!\n"); 183 file_system_context_->operation_runner()->Write( 184 &url_request_context(), URLForPath(virtual_path_), 185 blob.GetBlobDataHandle(), 186 0, RecordWriteCallback()); 187 base::MessageLoop::current()->Run(); 188 189 EXPECT_EQ(14, bytes_written()); 190 EXPECT_EQ(base::File::FILE_OK, status()); 191 EXPECT_TRUE(complete()); 192 193 EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count()); 194} 195 196TEST_F(FileSystemOperationImplWriteTest, TestWriteZero) { 197 ScopedTextBlob blob(url_request_context(), "blob_id:zero", ""); 198 file_system_context_->operation_runner()->Write( 199 &url_request_context(), URLForPath(virtual_path_), 200 blob.GetBlobDataHandle(), 0, RecordWriteCallback()); 201 base::MessageLoop::current()->Run(); 202 203 EXPECT_EQ(0, bytes_written()); 204 EXPECT_EQ(base::File::FILE_OK, status()); 205 EXPECT_TRUE(complete()); 206 207 EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count()); 208} 209 210 211TEST_F(FileSystemOperationImplWriteTest, TestWriteInvalidBlobUrl) { 212 scoped_ptr<storage::BlobDataHandle> null_handle; 213 file_system_context_->operation_runner()->Write( 214 &url_request_context(), URLForPath(virtual_path_), 215 null_handle.Pass(), 0, RecordWriteCallback()); 216 base::MessageLoop::current()->Run(); 217 218 EXPECT_EQ(0, bytes_written()); 219 EXPECT_EQ(base::File::FILE_ERROR_FAILED, status()); 220 EXPECT_TRUE(complete()); 221 222 EXPECT_EQ(0, change_observer()->get_and_reset_modify_file_count()); 223} 224 225TEST_F(FileSystemOperationImplWriteTest, TestWriteInvalidFile) { 226 ScopedTextBlob blob(url_request_context(), "blob_id:writeinvalidfile", 227 "It\'ll not be written."); 228 file_system_context_->operation_runner()->Write( 229 &url_request_context(), 230 URLForPath(base::FilePath(FILE_PATH_LITERAL("nonexist"))), 231 blob.GetBlobDataHandle(), 0, RecordWriteCallback()); 232 base::MessageLoop::current()->Run(); 233 234 EXPECT_EQ(0, bytes_written()); 235 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, status()); 236 EXPECT_TRUE(complete()); 237 238 EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count()); 239} 240 241TEST_F(FileSystemOperationImplWriteTest, TestWriteDir) { 242 base::FilePath virtual_dir_path(FILE_PATH_LITERAL("d")); 243 file_system_context_->operation_runner()->CreateDirectory( 244 URLForPath(virtual_dir_path), 245 true /* exclusive */, false /* recursive */, 246 base::Bind(&AssertStatusEq, base::File::FILE_OK)); 247 248 ScopedTextBlob blob(url_request_context(), "blob:writedir", 249 "It\'ll not be written, too."); 250 file_system_context_->operation_runner()->Write( 251 &url_request_context(), URLForPath(virtual_dir_path), 252 blob.GetBlobDataHandle(), 0, RecordWriteCallback()); 253 base::MessageLoop::current()->Run(); 254 255 EXPECT_EQ(0, bytes_written()); 256 // TODO(kinuko): This error code is platform- or fileutil- dependent 257 // right now. Make it return File::FILE_ERROR_NOT_A_FILE in every case. 258 EXPECT_TRUE(status() == base::File::FILE_ERROR_NOT_A_FILE || 259 status() == base::File::FILE_ERROR_ACCESS_DENIED || 260 status() == base::File::FILE_ERROR_FAILED); 261 EXPECT_TRUE(complete()); 262 263 EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count()); 264} 265 266TEST_F(FileSystemOperationImplWriteTest, TestWriteFailureByQuota) { 267 ScopedTextBlob blob(url_request_context(), "blob:success", 268 "Hello, world!\n"); 269 quota_manager_->SetQuota( 270 kOrigin, FileSystemTypeToQuotaStorageType(kFileSystemType), 10); 271 file_system_context_->operation_runner()->Write( 272 &url_request_context(), URLForPath(virtual_path_), 273 blob.GetBlobDataHandle(), 0, RecordWriteCallback()); 274 base::MessageLoop::current()->Run(); 275 276 EXPECT_EQ(10, bytes_written()); 277 EXPECT_EQ(base::File::FILE_ERROR_NO_SPACE, status()); 278 EXPECT_TRUE(complete()); 279 280 EXPECT_EQ(1, change_observer()->get_and_reset_modify_file_count()); 281} 282 283TEST_F(FileSystemOperationImplWriteTest, TestImmediateCancelSuccessfulWrite) { 284 ScopedTextBlob blob(url_request_context(), "blob:success", 285 "Hello, world!\n"); 286 FileSystemOperationRunner::OperationID id = 287 file_system_context_->operation_runner()->Write( 288 &url_request_context(), URLForPath(virtual_path_), 289 blob.GetBlobDataHandle(), 0, RecordWriteCallback()); 290 file_system_context_->operation_runner()->Cancel(id, RecordCancelCallback()); 291 // We use RunAllPendings() instead of Run() here, because we won't dispatch 292 // callbacks after Cancel() is issued (so no chance to Quit) nor do we need 293 // to run another write cycle. 294 base::RunLoop().RunUntilIdle(); 295 296 // Issued Cancel() before receiving any response from Write(), 297 // so nothing should have happen. 298 EXPECT_EQ(0, bytes_written()); 299 EXPECT_EQ(base::File::FILE_ERROR_ABORT, status()); 300 EXPECT_EQ(base::File::FILE_OK, cancel_status()); 301 EXPECT_TRUE(complete()); 302 303 EXPECT_EQ(0, change_observer()->get_and_reset_modify_file_count()); 304} 305 306TEST_F(FileSystemOperationImplWriteTest, TestImmediateCancelFailingWrite) { 307 ScopedTextBlob blob(url_request_context(), "blob:writeinvalidfile", 308 "It\'ll not be written."); 309 FileSystemOperationRunner::OperationID id = 310 file_system_context_->operation_runner()->Write( 311 &url_request_context(), 312 URLForPath(base::FilePath(FILE_PATH_LITERAL("nonexist"))), 313 blob.GetBlobDataHandle(), 0, RecordWriteCallback()); 314 file_system_context_->operation_runner()->Cancel(id, RecordCancelCallback()); 315 // We use RunAllPendings() instead of Run() here, because we won't dispatch 316 // callbacks after Cancel() is issued (so no chance to Quit) nor do we need 317 // to run another write cycle. 318 base::RunLoop().RunUntilIdle(); 319 320 // Issued Cancel() before receiving any response from Write(), 321 // so nothing should have happen. 322 EXPECT_EQ(0, bytes_written()); 323 EXPECT_EQ(base::File::FILE_ERROR_ABORT, status()); 324 EXPECT_EQ(base::File::FILE_OK, cancel_status()); 325 EXPECT_TRUE(complete()); 326 327 EXPECT_EQ(0, change_observer()->get_and_reset_modify_file_count()); 328} 329 330// TODO(ericu,dmikurube,kinuko): Add more tests for cancel cases. 331 332} // namespace content 333