1 // Copyright (c) 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 <map> 6 #include <queue> 7 8 #include "base/basictypes.h" 9 #include "base/bind.h" 10 #include "base/files/scoped_temp_dir.h" 11 #include "base/message_loop/message_loop.h" 12 #include "base/run_loop.h" 13 #include "base/stl_util.h" 14 #include "testing/gtest/include/gtest/gtest.h" 15 #include "webkit/browser/fileapi/async_file_test_helper.h" 16 #include "webkit/browser/fileapi/copy_or_move_file_validator.h" 17 #include "webkit/browser/fileapi/file_system_backend.h" 18 #include "webkit/browser/fileapi/file_system_context.h" 19 #include "webkit/browser/fileapi/file_system_operation.h" 20 #include "webkit/browser/fileapi/file_system_url.h" 21 #include "webkit/browser/fileapi/mock_file_system_context.h" 22 #include "webkit/browser/fileapi/test_file_set.h" 23 #include "webkit/browser/fileapi/test_file_system_backend.h" 24 #include "webkit/browser/quota/mock_quota_manager.h" 25 #include "webkit/browser/quota/quota_manager.h" 26 #include "webkit/common/fileapi/file_system_util.h" 27 28 namespace fileapi { 29 30 typedef FileSystemOperation::FileEntryList FileEntryList; 31 32 namespace { 33 34 void ExpectOk(const GURL& origin_url, 35 const std::string& name, 36 base::PlatformFileError error) { 37 ASSERT_EQ(base::PLATFORM_FILE_OK, error); 38 } 39 40 class TestValidatorFactory : public CopyOrMoveFileValidatorFactory { 41 public: 42 // A factory that creates validators that accept everything or nothing. 43 TestValidatorFactory() {} 44 virtual ~TestValidatorFactory() {} 45 46 virtual CopyOrMoveFileValidator* CreateCopyOrMoveFileValidator( 47 const FileSystemURL& /*src_url*/, 48 const base::FilePath& /*platform_path*/) OVERRIDE { 49 // Move arg management to TestValidator? 50 return new TestValidator(true, true, std::string("2")); 51 } 52 53 private: 54 class TestValidator : public CopyOrMoveFileValidator { 55 public: 56 explicit TestValidator(bool pre_copy_valid, 57 bool post_copy_valid, 58 const std::string& reject_string) 59 : result_(pre_copy_valid ? base::PLATFORM_FILE_OK 60 : base::PLATFORM_FILE_ERROR_SECURITY), 61 write_result_(post_copy_valid ? base::PLATFORM_FILE_OK 62 : base::PLATFORM_FILE_ERROR_SECURITY), 63 reject_string_(reject_string) { 64 } 65 virtual ~TestValidator() {} 66 67 virtual void StartPreWriteValidation( 68 const ResultCallback& result_callback) OVERRIDE { 69 // Post the result since a real validator must do work asynchronously. 70 base::MessageLoop::current()->PostTask( 71 FROM_HERE, base::Bind(result_callback, result_)); 72 } 73 74 virtual void StartPostWriteValidation( 75 const base::FilePath& dest_platform_path, 76 const ResultCallback& result_callback) OVERRIDE { 77 base::PlatformFileError result = write_result_; 78 std::string unsafe = dest_platform_path.BaseName().AsUTF8Unsafe(); 79 if (unsafe.find(reject_string_) != std::string::npos) { 80 result = base::PLATFORM_FILE_ERROR_SECURITY; 81 } 82 // Post the result since a real validator must do work asynchronously. 83 base::MessageLoop::current()->PostTask( 84 FROM_HERE, base::Bind(result_callback, result)); 85 } 86 87 private: 88 base::PlatformFileError result_; 89 base::PlatformFileError write_result_; 90 std::string reject_string_; 91 92 DISALLOW_COPY_AND_ASSIGN(TestValidator); 93 }; 94 }; 95 96 } // namespace 97 98 class CopyOrMoveOperationTestHelper { 99 public: 100 CopyOrMoveOperationTestHelper( 101 const GURL& origin, 102 FileSystemType src_type, 103 FileSystemType dest_type) 104 : origin_(origin), 105 src_type_(src_type), 106 dest_type_(dest_type) {} 107 108 ~CopyOrMoveOperationTestHelper() { 109 file_system_context_ = NULL; 110 quota_manager_proxy_->SimulateQuotaManagerDestroyed(); 111 quota_manager_ = NULL; 112 quota_manager_proxy_ = NULL; 113 base::MessageLoop::current()->RunUntilIdle(); 114 } 115 116 void SetUp() { 117 SetUp(true, true); 118 } 119 120 void SetUpNoValidator() { 121 SetUp(true, false); 122 } 123 124 void SetUp(bool require_copy_or_move_validator, 125 bool init_copy_or_move_validator) { 126 ASSERT_TRUE(base_.CreateUniqueTempDir()); 127 base::FilePath base_dir = base_.path(); 128 quota_manager_ = 129 new quota::MockQuotaManager(false /* is_incognito */, 130 base_dir, 131 base::MessageLoopProxy::current().get(), 132 base::MessageLoopProxy::current().get(), 133 NULL /* special storage policy */); 134 quota_manager_proxy_ = new quota::MockQuotaManagerProxy( 135 quota_manager_.get(), base::MessageLoopProxy::current().get()); 136 file_system_context_ = 137 CreateFileSystemContextForTesting(quota_manager_proxy_.get(), base_dir); 138 139 // Prepare the origin's root directory. 140 FileSystemBackend* backend = 141 file_system_context_->GetFileSystemBackend(src_type_); 142 backend->OpenFileSystem(origin_, src_type_, 143 OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT, 144 base::Bind(&ExpectOk)); 145 backend = file_system_context_->GetFileSystemBackend(dest_type_); 146 if (dest_type_ == kFileSystemTypeTest) { 147 TestFileSystemBackend* test_backend = 148 static_cast<TestFileSystemBackend*>(backend); 149 scoped_ptr<CopyOrMoveFileValidatorFactory> factory( 150 new TestValidatorFactory); 151 test_backend->set_require_copy_or_move_validator( 152 require_copy_or_move_validator); 153 if (init_copy_or_move_validator) 154 test_backend->InitializeCopyOrMoveFileValidatorFactory(factory.Pass()); 155 } 156 backend->OpenFileSystem(origin_, dest_type_, 157 OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT, 158 base::Bind(&ExpectOk)); 159 base::MessageLoop::current()->RunUntilIdle(); 160 161 // Grant relatively big quota initially. 162 quota_manager_->SetQuota(origin_, 163 FileSystemTypeToQuotaStorageType(src_type_), 164 1024 * 1024); 165 quota_manager_->SetQuota(origin_, 166 FileSystemTypeToQuotaStorageType(dest_type_), 167 1024 * 1024); 168 } 169 170 int64 GetSourceUsage() { 171 int64 usage = 0; 172 GetUsageAndQuota(src_type_, &usage, NULL); 173 return usage; 174 } 175 176 int64 GetDestUsage() { 177 int64 usage = 0; 178 GetUsageAndQuota(dest_type_, &usage, NULL); 179 return usage; 180 } 181 182 FileSystemURL SourceURL(const std::string& path) { 183 return file_system_context_->CreateCrackedFileSystemURL( 184 origin_, src_type_, base::FilePath::FromUTF8Unsafe(path)); 185 } 186 187 FileSystemURL DestURL(const std::string& path) { 188 return file_system_context_->CreateCrackedFileSystemURL( 189 origin_, dest_type_, base::FilePath::FromUTF8Unsafe(path)); 190 } 191 192 base::PlatformFileError Copy(const FileSystemURL& src, 193 const FileSystemURL& dest) { 194 return AsyncFileTestHelper::Copy(file_system_context_.get(), src, dest); 195 } 196 197 base::PlatformFileError Move(const FileSystemURL& src, 198 const FileSystemURL& dest) { 199 return AsyncFileTestHelper::Move(file_system_context_.get(), src, dest); 200 } 201 202 base::PlatformFileError SetUpTestCaseFiles( 203 const FileSystemURL& root, 204 const test::TestCaseRecord* const test_cases, 205 size_t test_case_size) { 206 base::PlatformFileError result = base::PLATFORM_FILE_ERROR_FAILED; 207 for (size_t i = 0; i < test_case_size; ++i) { 208 const test::TestCaseRecord& test_case = test_cases[i]; 209 FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL( 210 root.origin(), 211 root.mount_type(), 212 root.virtual_path().Append(test_case.path)); 213 if (test_case.is_directory) 214 result = CreateDirectory(url); 215 else 216 result = CreateFile(url, test_case.data_file_size); 217 EXPECT_EQ(base::PLATFORM_FILE_OK, result) << url.DebugString(); 218 if (result != base::PLATFORM_FILE_OK) 219 return result; 220 } 221 return result; 222 } 223 224 void VerifyTestCaseFiles( 225 const FileSystemURL& root, 226 const test::TestCaseRecord* const test_cases, 227 size_t test_case_size) { 228 std::map<base::FilePath, const test::TestCaseRecord*> test_case_map; 229 for (size_t i = 0; i < test_case_size; ++i) { 230 test_case_map[ 231 base::FilePath(test_cases[i].path).NormalizePathSeparators()] = 232 &test_cases[i]; 233 } 234 235 std::queue<FileSystemURL> directories; 236 FileEntryList entries; 237 directories.push(root); 238 while (!directories.empty()) { 239 FileSystemURL dir = directories.front(); 240 directories.pop(); 241 ASSERT_EQ(base::PLATFORM_FILE_OK, ReadDirectory(dir, &entries)); 242 for (size_t i = 0; i < entries.size(); ++i) { 243 FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL( 244 dir.origin(), 245 dir.mount_type(), 246 dir.virtual_path().Append(entries[i].name)); 247 base::FilePath relative; 248 root.virtual_path().AppendRelativePath(url.virtual_path(), &relative); 249 relative = relative.NormalizePathSeparators(); 250 ASSERT_TRUE(ContainsKey(test_case_map, relative)); 251 if (entries[i].is_directory) { 252 EXPECT_TRUE(test_case_map[relative]->is_directory); 253 directories.push(url); 254 } else { 255 EXPECT_FALSE(test_case_map[relative]->is_directory); 256 EXPECT_TRUE(FileExists(url, test_case_map[relative]->data_file_size)); 257 } 258 test_case_map.erase(relative); 259 } 260 } 261 EXPECT_TRUE(test_case_map.empty()); 262 std::map<base::FilePath, const test::TestCaseRecord*>::const_iterator it; 263 for (it = test_case_map.begin(); it != test_case_map.end(); ++it) { 264 LOG(ERROR) << "Extra entry: " << it->first.LossyDisplayName(); 265 } 266 } 267 268 base::PlatformFileError ReadDirectory(const FileSystemURL& url, 269 FileEntryList* entries) { 270 return AsyncFileTestHelper::ReadDirectory( 271 file_system_context_.get(), url, entries); 272 } 273 274 base::PlatformFileError CreateDirectory(const FileSystemURL& url) { 275 return AsyncFileTestHelper::CreateDirectory(file_system_context_.get(), 276 url); 277 } 278 279 base::PlatformFileError CreateFile(const FileSystemURL& url, size_t size) { 280 base::PlatformFileError result = 281 AsyncFileTestHelper::CreateFile(file_system_context_.get(), url); 282 if (result != base::PLATFORM_FILE_OK) 283 return result; 284 return AsyncFileTestHelper::TruncateFile( 285 file_system_context_.get(), url, size); 286 } 287 288 bool FileExists(const FileSystemURL& url, int64 expected_size) { 289 return AsyncFileTestHelper::FileExists( 290 file_system_context_.get(), url, expected_size); 291 } 292 293 bool DirectoryExists(const FileSystemURL& url) { 294 return AsyncFileTestHelper::DirectoryExists(file_system_context_.get(), 295 url); 296 } 297 298 private: 299 void GetUsageAndQuota(FileSystemType type, int64* usage, int64* quota) { 300 quota::QuotaStatusCode status = AsyncFileTestHelper::GetUsageAndQuota( 301 quota_manager_.get(), origin_, type, usage, quota); 302 ASSERT_EQ(quota::kQuotaStatusOk, status); 303 } 304 305 private: 306 base::ScopedTempDir base_; 307 308 const GURL origin_; 309 const FileSystemType src_type_; 310 const FileSystemType dest_type_; 311 312 base::MessageLoop message_loop_; 313 scoped_refptr<FileSystemContext> file_system_context_; 314 scoped_refptr<quota::MockQuotaManagerProxy> quota_manager_proxy_; 315 scoped_refptr<quota::MockQuotaManager> quota_manager_; 316 317 DISALLOW_COPY_AND_ASSIGN(CopyOrMoveOperationTestHelper); 318 }; 319 320 TEST(LocalFileSystemCopyOrMoveOperationTest, CopySingleFile) { 321 CopyOrMoveOperationTestHelper helper(GURL("http://foo"), 322 kFileSystemTypeTemporary, 323 kFileSystemTypePersistent); 324 helper.SetUp(); 325 326 FileSystemURL src = helper.SourceURL("a"); 327 FileSystemURL dest = helper.DestURL("b"); 328 int64 src_initial_usage = helper.GetSourceUsage(); 329 int64 dest_initial_usage = helper.GetDestUsage(); 330 331 // Set up a source file. 332 ASSERT_EQ(base::PLATFORM_FILE_OK, helper.CreateFile(src, 10)); 333 int64 src_increase = helper.GetSourceUsage() - src_initial_usage; 334 335 // Copy it. 336 ASSERT_EQ(base::PLATFORM_FILE_OK, helper.Copy(src, dest)); 337 338 // Verify. 339 ASSERT_TRUE(helper.FileExists(src, 10)); 340 ASSERT_TRUE(helper.FileExists(dest, 10)); 341 342 int64 src_new_usage = helper.GetSourceUsage(); 343 ASSERT_EQ(src_initial_usage + src_increase, src_new_usage); 344 345 int64 dest_increase = helper.GetDestUsage() - dest_initial_usage; 346 ASSERT_EQ(src_increase, dest_increase); 347 } 348 349 TEST(LocalFileSystemCopyOrMoveOperationTest, MoveSingleFile) { 350 CopyOrMoveOperationTestHelper helper(GURL("http://foo"), 351 kFileSystemTypeTemporary, 352 kFileSystemTypePersistent); 353 helper.SetUp(); 354 355 FileSystemURL src = helper.SourceURL("a"); 356 FileSystemURL dest = helper.DestURL("b"); 357 int64 src_initial_usage = helper.GetSourceUsage(); 358 int64 dest_initial_usage = helper.GetDestUsage(); 359 360 // Set up a source file. 361 ASSERT_EQ(base::PLATFORM_FILE_OK, helper.CreateFile(src, 10)); 362 int64 src_increase = helper.GetSourceUsage() - src_initial_usage; 363 364 // Move it. 365 ASSERT_EQ(base::PLATFORM_FILE_OK, helper.Move(src, dest)); 366 367 // Verify. 368 ASSERT_FALSE(helper.FileExists(src, AsyncFileTestHelper::kDontCheckSize)); 369 ASSERT_TRUE(helper.FileExists(dest, 10)); 370 371 int64 src_new_usage = helper.GetSourceUsage(); 372 ASSERT_EQ(src_initial_usage, src_new_usage); 373 374 int64 dest_increase = helper.GetDestUsage() - dest_initial_usage; 375 ASSERT_EQ(src_increase, dest_increase); 376 } 377 378 TEST(LocalFileSystemCopyOrMoveOperationTest, CopySingleDirectory) { 379 CopyOrMoveOperationTestHelper helper(GURL("http://foo"), 380 kFileSystemTypeTemporary, 381 kFileSystemTypePersistent); 382 helper.SetUp(); 383 384 FileSystemURL src = helper.SourceURL("a"); 385 FileSystemURL dest = helper.DestURL("b"); 386 int64 src_initial_usage = helper.GetSourceUsage(); 387 int64 dest_initial_usage = helper.GetDestUsage(); 388 389 // Set up a source directory. 390 ASSERT_EQ(base::PLATFORM_FILE_OK, helper.CreateDirectory(src)); 391 int64 src_increase = helper.GetSourceUsage() - src_initial_usage; 392 393 // Copy it. 394 ASSERT_EQ(base::PLATFORM_FILE_OK, helper.Copy(src, dest)); 395 396 // Verify. 397 ASSERT_TRUE(helper.DirectoryExists(src)); 398 ASSERT_TRUE(helper.DirectoryExists(dest)); 399 400 int64 src_new_usage = helper.GetSourceUsage(); 401 ASSERT_EQ(src_initial_usage + src_increase, src_new_usage); 402 403 int64 dest_increase = helper.GetDestUsage() - dest_initial_usage; 404 ASSERT_EQ(src_increase, dest_increase); 405 } 406 407 TEST(LocalFileSystemCopyOrMoveOperationTest, MoveSingleDirectory) { 408 CopyOrMoveOperationTestHelper helper(GURL("http://foo"), 409 kFileSystemTypeTemporary, 410 kFileSystemTypePersistent); 411 helper.SetUp(); 412 413 FileSystemURL src = helper.SourceURL("a"); 414 FileSystemURL dest = helper.DestURL("b"); 415 int64 src_initial_usage = helper.GetSourceUsage(); 416 int64 dest_initial_usage = helper.GetDestUsage(); 417 418 // Set up a source directory. 419 ASSERT_EQ(base::PLATFORM_FILE_OK, helper.CreateDirectory(src)); 420 int64 src_increase = helper.GetSourceUsage() - src_initial_usage; 421 422 // Move it. 423 ASSERT_EQ(base::PLATFORM_FILE_OK, helper.Move(src, dest)); 424 425 // Verify. 426 ASSERT_FALSE(helper.DirectoryExists(src)); 427 ASSERT_TRUE(helper.DirectoryExists(dest)); 428 429 int64 src_new_usage = helper.GetSourceUsage(); 430 ASSERT_EQ(src_initial_usage, src_new_usage); 431 432 int64 dest_increase = helper.GetDestUsage() - dest_initial_usage; 433 ASSERT_EQ(src_increase, dest_increase); 434 } 435 436 TEST(LocalFileSystemCopyOrMoveOperationTest, CopyDirectory) { 437 CopyOrMoveOperationTestHelper helper(GURL("http://foo"), 438 kFileSystemTypeTemporary, 439 kFileSystemTypePersistent); 440 helper.SetUp(); 441 442 FileSystemURL src = helper.SourceURL("a"); 443 FileSystemURL dest = helper.DestURL("b"); 444 int64 src_initial_usage = helper.GetSourceUsage(); 445 int64 dest_initial_usage = helper.GetDestUsage(); 446 447 // Set up a source directory. 448 ASSERT_EQ(base::PLATFORM_FILE_OK, helper.CreateDirectory(src)); 449 ASSERT_EQ(base::PLATFORM_FILE_OK, 450 helper.SetUpTestCaseFiles(src, 451 test::kRegularTestCases, 452 test::kRegularTestCaseSize)); 453 int64 src_increase = helper.GetSourceUsage() - src_initial_usage; 454 455 // Copy it. 456 ASSERT_EQ(base::PLATFORM_FILE_OK, helper.Copy(src, dest)); 457 458 // Verify. 459 ASSERT_TRUE(helper.DirectoryExists(src)); 460 ASSERT_TRUE(helper.DirectoryExists(dest)); 461 462 helper.VerifyTestCaseFiles(dest, 463 test::kRegularTestCases, 464 test::kRegularTestCaseSize); 465 466 int64 src_new_usage = helper.GetSourceUsage(); 467 ASSERT_EQ(src_initial_usage + src_increase, src_new_usage); 468 469 int64 dest_increase = helper.GetDestUsage() - dest_initial_usage; 470 ASSERT_EQ(src_increase, dest_increase); 471 } 472 473 TEST(LocalFileSystemCopyOrMoveOperationTest, MoveDirectory) { 474 CopyOrMoveOperationTestHelper helper(GURL("http://foo"), 475 kFileSystemTypeTemporary, 476 kFileSystemTypePersistent); 477 helper.SetUp(); 478 479 FileSystemURL src = helper.SourceURL("a"); 480 FileSystemURL dest = helper.DestURL("b"); 481 int64 src_initial_usage = helper.GetSourceUsage(); 482 int64 dest_initial_usage = helper.GetDestUsage(); 483 484 // Set up a source directory. 485 ASSERT_EQ(base::PLATFORM_FILE_OK, helper.CreateDirectory(src)); 486 ASSERT_EQ(base::PLATFORM_FILE_OK, 487 helper.SetUpTestCaseFiles(src, 488 test::kRegularTestCases, 489 test::kRegularTestCaseSize)); 490 int64 src_increase = helper.GetSourceUsage() - src_initial_usage; 491 492 // Move it. 493 ASSERT_EQ(base::PLATFORM_FILE_OK, helper.Move(src, dest)); 494 495 // Verify. 496 ASSERT_FALSE(helper.DirectoryExists(src)); 497 ASSERT_TRUE(helper.DirectoryExists(dest)); 498 499 helper.VerifyTestCaseFiles(dest, 500 test::kRegularTestCases, 501 test::kRegularTestCaseSize); 502 503 int64 src_new_usage = helper.GetSourceUsage(); 504 ASSERT_EQ(src_initial_usage, src_new_usage); 505 506 int64 dest_increase = helper.GetDestUsage() - dest_initial_usage; 507 ASSERT_EQ(src_increase, dest_increase); 508 } 509 510 TEST(LocalFileSystemCopyOrMoveOperationTest, 511 MoveDirectoryFailPostWriteValidation) { 512 CopyOrMoveOperationTestHelper helper(GURL("http://foo"), 513 kFileSystemTypeTemporary, 514 kFileSystemTypeTest); 515 helper.SetUp(); 516 517 FileSystemURL src = helper.SourceURL("a"); 518 FileSystemURL dest = helper.DestURL("b"); 519 520 // Set up a source directory. 521 ASSERT_EQ(base::PLATFORM_FILE_OK, helper.CreateDirectory(src)); 522 ASSERT_EQ(base::PLATFORM_FILE_OK, 523 helper.SetUpTestCaseFiles(src, 524 test::kRegularTestCases, 525 test::kRegularTestCaseSize)); 526 527 // Move it. 528 helper.Move(src, dest); 529 530 // Verify. 531 ASSERT_TRUE(helper.DirectoryExists(src)); 532 ASSERT_TRUE(helper.DirectoryExists(dest)); 533 534 test::TestCaseRecord kMoveDirResultCases[] = { 535 {false, FILE_PATH_LITERAL("file 0"), 38}, 536 {false, FILE_PATH_LITERAL("file 3"), 0}, 537 }; 538 539 helper.VerifyTestCaseFiles(dest, 540 kMoveDirResultCases, 541 arraysize(kMoveDirResultCases)); 542 } 543 544 TEST(LocalFileSystemCopyOrMoveOperationTest, CopySingleFileNoValidator) { 545 CopyOrMoveOperationTestHelper helper(GURL("http://foo"), 546 kFileSystemTypeTemporary, 547 kFileSystemTypeTest); 548 helper.SetUpNoValidator(); 549 550 FileSystemURL src = helper.SourceURL("a"); 551 FileSystemURL dest = helper.DestURL("b"); 552 553 // Set up a source file. 554 ASSERT_EQ(base::PLATFORM_FILE_OK, helper.CreateFile(src, 10)); 555 556 // The copy attempt should fail with a security error -- getting 557 // the factory returns a security error, and the copy operation must 558 // respect that. 559 ASSERT_EQ(base::PLATFORM_FILE_ERROR_SECURITY, helper.Copy(src, dest)); 560 } 561 562 } // namespace fileapi 563