// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "webkit/fileapi/sandbox_mount_point_provider.h" #include #include #include "base/basictypes.h" #include "base/bind.h" #include "base/file_util.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" #include "base/message_loop.h" #include "base/message_loop_proxy.h" #include "base/platform_file.h" #include "base/scoped_temp_dir.h" #include "googleurl/src/gurl.h" #include "testing/gtest/include/gtest/gtest.h" #include "webkit/fileapi/file_system_context.h" #include "webkit/fileapi/file_system_operation_context.h" #include "webkit/fileapi/file_system_path_manager.h" #include "webkit/fileapi/file_system_util.h" #include "webkit/quota/mock_special_storage_policy.h" namespace fileapi { class MockFileSystemPathManager : public FileSystemPathManager { public: explicit MockFileSystemPathManager(const FilePath& profile_path) : FileSystemPathManager(base::MessageLoopProxy::current(), profile_path, NULL, false, true) {} }; class SandboxMountPointProviderOriginEnumeratorTest : public testing::Test { public: void SetUp() { ASSERT_TRUE(data_dir_.CreateUniqueTempDir()); path_manager_.reset(new MockFileSystemPathManager(data_dir_.path())); } SandboxMountPointProvider::OriginEnumerator* CreateEnumerator() const { return path_manager_->sandbox_provider()->CreateOriginEnumerator(); } protected: void CreateOriginTypeDirectory(const GURL& origin, fileapi::FileSystemType type) { FilePath target = path_manager_->sandbox_provider()-> GetBaseDirectoryForOriginAndType(origin, type, true); ASSERT_TRUE(!target.empty()); ASSERT_TRUE(file_util::DirectoryExists(target)); } ScopedTempDir data_dir_; scoped_ptr path_manager_; }; TEST_F(SandboxMountPointProviderOriginEnumeratorTest, Empty) { scoped_ptr enumerator( CreateEnumerator()); ASSERT_TRUE(enumerator->Next().is_empty()); } TEST_F(SandboxMountPointProviderOriginEnumeratorTest, EnumerateOrigins) { const char* temporary_origins[] = { "http://www.bar.com/", "http://www.foo.com/", "http://www.foo.com:1/", "http://www.example.com:8080/", "http://www.google.com:80/", }; const char* persistent_origins[] = { "http://www.bar.com/", "http://www.foo.com:8080/", "http://www.foo.com:80/", }; size_t temporary_size = ARRAYSIZE_UNSAFE(temporary_origins); size_t persistent_size = ARRAYSIZE_UNSAFE(persistent_origins); std::set temporary_set, persistent_set; for (size_t i = 0; i < temporary_size; ++i) { CreateOriginTypeDirectory(GURL(temporary_origins[i]), fileapi::kFileSystemTypeTemporary); temporary_set.insert(GURL(temporary_origins[i])); } for (size_t i = 0; i < persistent_size; ++i) { CreateOriginTypeDirectory(GURL(persistent_origins[i]), kFileSystemTypePersistent); persistent_set.insert(GURL(persistent_origins[i])); } scoped_ptr enumerator( CreateEnumerator()); size_t temporary_actual_size = 0; size_t persistent_actual_size = 0; GURL current; while (!(current = enumerator->Next()).is_empty()) { SCOPED_TRACE(testing::Message() << "EnumerateOrigin " << current.spec()); if (enumerator->HasFileSystemType(kFileSystemTypeTemporary)) { ASSERT_TRUE(temporary_set.find(current) != temporary_set.end()); ++temporary_actual_size; } if (enumerator->HasFileSystemType(kFileSystemTypePersistent)) { ASSERT_TRUE(persistent_set.find(current) != persistent_set.end()); ++persistent_actual_size; } } EXPECT_EQ(temporary_size, temporary_actual_size); EXPECT_EQ(persistent_size, persistent_actual_size); } namespace { struct MigrationTestCaseRecord { GURL origin; bool has_temporary; bool has_persistent; }; const MigrationTestCaseRecord kMigrationTestRecords[] = { { GURL("http://www.example.com"), true, false }, { GURL("http://example.com"), false, true }, { GURL("http://www.google.com"), false, true }, { GURL("http://www.another.origin.com"), true, true }, { GURL("http://www.yet.another.origin.com"), true, true }, { GURL("file:///"), false, true }, }; } // anonymous namespace class SandboxMountPointProviderMigrationTest : public testing::Test { public: SandboxMountPointProviderMigrationTest() : ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { } void SetUp() { ASSERT_TRUE(data_dir_.CreateUniqueTempDir()); path_manager_ = new MockFileSystemPathManager(data_dir_.path()); scoped_refptr special_storage_policy = new quota::MockSpecialStoragePolicy; special_storage_policy->SetAllUnlimited(true); file_system_context_ = new FileSystemContext( base::MessageLoopProxy::current(), base::MessageLoopProxy::current(), special_storage_policy, NULL, data_dir_.path(), false, // incognito true, // allow_file_access_from_files path_manager_); } FileSystemPathManager* path_manager() { return path_manager_; } SandboxMountPointProvider* sandbox_provider() { return path_manager()->sandbox_provider(); } FileSystemFileUtil* file_util() { return sandbox_provider()->GetFileUtil(); } void OnGetRootPath(bool success, const FilePath& unused, const std::string& unused_also) { EXPECT_FALSE(success); // We told it not to create. } FileSystemPathManager::GetRootPathCallback GetRootPathCallback() { return base::Bind(&SandboxMountPointProviderMigrationTest::OnGetRootPath, weak_factory_.GetWeakPtr()); } void EnsureFileExists(const FilePath& path) { bool created = false; PlatformFileError error_code = base::PLATFORM_FILE_OK; PlatformFile handle = base::CreatePlatformFile( path, base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_READ, &created, &error_code); ASSERT_EQ(base::PLATFORM_FILE_OK, error_code); ASSERT_TRUE(created); ASSERT_NE(base::kInvalidPlatformFileValue, handle); base::ClosePlatformFile(handle); } void CreateDataInDir(const FilePath& root, const std::string& seed) { ASSERT_TRUE(file_util::CreateDirectory( root.AppendASCII(seed))); ASSERT_TRUE(file_util::CreateDirectory( root.AppendASCII(seed).AppendASCII(seed))); ASSERT_TRUE(file_util::CreateDirectory( root.AppendASCII(seed).AppendASCII("d 0"))); ASSERT_TRUE(file_util::CreateDirectory( root.AppendASCII(seed).AppendASCII("d 1"))); EnsureFileExists(root.AppendASCII("file 0")); EnsureFileExists( root.AppendASCII(seed).AppendASCII("d 0").AppendASCII("file 1")); EnsureFileExists( root.AppendASCII(seed).AppendASCII("d 0").AppendASCII("file 2")); } FileSystemOperationContext* NewContext(const GURL& origin_url, fileapi::FileSystemType type) { FileSystemOperationContext* context = new FileSystemOperationContext( file_system_context_, file_util()); context->set_src_origin_url(origin_url); context->set_src_type(type); return context; } std::string URLAndTypeToSeedString(const GURL& origin_url, fileapi::FileSystemType type) { return GetOriginIdentifierFromURL(origin_url) + FileSystemPathManager::GetFileSystemTypeString(type); } void ValidateDataInNewFileSystem( const GURL& origin_url, fileapi::FileSystemType type) { scoped_ptr context; FilePath seed = FilePath().AppendASCII( URLAndTypeToSeedString(origin_url, type)); context.reset(NewContext(origin_url, type)); EXPECT_TRUE(file_util()->DirectoryExists( context.get(), seed)); context.reset(NewContext(origin_url, type)); EXPECT_TRUE(file_util()->DirectoryExists( context.get(), seed.Append(seed))); context.reset(NewContext(origin_url, type)); EXPECT_TRUE(file_util()->DirectoryExists( context.get(), seed.AppendASCII("d 0"))); context.reset(NewContext(origin_url, type)); EXPECT_TRUE(file_util()->DirectoryExists( context.get(), seed.AppendASCII("d 1"))); context.reset(NewContext(origin_url, type)); EXPECT_TRUE(file_util()->PathExists( context.get(), FilePath().AppendASCII("file 0"))); context.reset(NewContext(origin_url, type)); EXPECT_FALSE(file_util()->DirectoryExists( context.get(), seed.AppendASCII("file 0"))); context.reset(NewContext(origin_url, type)); EXPECT_TRUE(file_util()->PathExists( context.get(), seed.AppendASCII("d 0").AppendASCII("file 1"))); context.reset(NewContext(origin_url, type)); EXPECT_FALSE(file_util()->DirectoryExists( context.get(), seed.AppendASCII("d 0").AppendASCII("file 1"))); context.reset(NewContext(origin_url, type)); EXPECT_TRUE(file_util()->PathExists( context.get(), seed.AppendASCII("d 0").AppendASCII("file 2"))); context.reset(NewContext(origin_url, type)); EXPECT_FALSE(file_util()->DirectoryExists( context.get(), seed.AppendASCII("d 0").AppendASCII("file 2"))); } void RunMigrationTest(int method) { for (size_t i = 0; i < arraysize(kMigrationTestRecords); ++i) { const MigrationTestCaseRecord& test_case = kMigrationTestRecords[i]; const GURL& origin_url = test_case.origin; ASSERT_TRUE(test_case.has_temporary || test_case.has_persistent); if (test_case.has_temporary) { FilePath root = sandbox_provider()->OldCreateFileSystemRootPath( origin_url, kFileSystemTypeTemporary); ASSERT_FALSE(root.empty()); CreateDataInDir(root, URLAndTypeToSeedString(origin_url, kFileSystemTypeTemporary)); } if (test_case.has_persistent) { FilePath root = sandbox_provider()->OldCreateFileSystemRootPath( origin_url, kFileSystemTypePersistent); ASSERT_FALSE(root.empty()); CreateDataInDir(root, URLAndTypeToSeedString(origin_url, kFileSystemTypePersistent)); } } const GURL origin_url("http://not.in.the.test.cases"); fileapi::FileSystemType type = kFileSystemTypeTemporary; bool create = false; std::set origins; std::string host = "the host with the most"; int64 delta = 0; // We want to make sure that all the public methods of // SandboxMountPointProvider which might access the filesystem will cause a // migration if one is needed. switch (method) { case 0: sandbox_provider()->ValidateFileSystemRootAndGetURL( origin_url, type, create, GetRootPathCallback()); MessageLoop::current()->RunAllPending(); break; case 1: sandbox_provider()->ValidateFileSystemRootAndGetPathOnFileThread( origin_url, type, FilePath(), create); break; case 2: sandbox_provider()->GetBaseDirectoryForOriginAndType( origin_url, type, create); break; case 3: sandbox_provider()->DeleteOriginDataOnFileThread( NULL, origin_url, type); break; case 4: sandbox_provider()->GetOriginsForTypeOnFileThread( type, &origins); break; case 5: sandbox_provider()->GetOriginsForHostOnFileThread( type, host, &origins); break; case 6: sandbox_provider()->GetOriginUsageOnFileThread( origin_url, type); break; case 7: // This case has to use an origin that already exists in the // migrated data. sandbox_provider()->UpdateOriginUsageOnFileThread( NULL, kMigrationTestRecords[0].origin, fileapi::kFileSystemTypeTemporary, delta); break; case 8: // This case has to use a filesystem that already exists in the // migrated data. sandbox_provider()->StartUpdateOriginOnFileThread( kMigrationTestRecords[0].origin, fileapi::kFileSystemTypeTemporary); break; case 9: // This case has to use a filesystem that already exists in the // migrated data. sandbox_provider()->EndUpdateOriginOnFileThread( kMigrationTestRecords[0].origin, fileapi::kFileSystemTypeTemporary); break; default: FAIL(); break; } for (size_t i = 0; i < arraysize(kMigrationTestRecords); ++i) { const MigrationTestCaseRecord& test_case = kMigrationTestRecords[i]; const GURL& origin_url = test_case.origin; ASSERT_TRUE(test_case.has_temporary || test_case.has_persistent); if (test_case.has_temporary) ValidateDataInNewFileSystem(origin_url, kFileSystemTypeTemporary); if (test_case.has_persistent) ValidateDataInNewFileSystem(origin_url, kFileSystemTypePersistent); } } protected: ScopedTempDir data_dir_; FileSystemPathManager* path_manager_; scoped_refptr file_system_context_; base::WeakPtrFactory weak_factory_; }; TEST_F(SandboxMountPointProviderMigrationTest, TestMigrateViaMethod0) { RunMigrationTest(0); } TEST_F(SandboxMountPointProviderMigrationTest, TestMigrateViaMethod1) { RunMigrationTest(1); } TEST_F(SandboxMountPointProviderMigrationTest, TestMigrateViaMethod2) { RunMigrationTest(2); } TEST_F(SandboxMountPointProviderMigrationTest, TestMigrateViaMethod3) { RunMigrationTest(3); } TEST_F(SandboxMountPointProviderMigrationTest, TestMigrateViaMethod4) { RunMigrationTest(4); } TEST_F(SandboxMountPointProviderMigrationTest, TestMigrateViaMethod5) { RunMigrationTest(5); } TEST_F(SandboxMountPointProviderMigrationTest, TestMigrateViaMethod6) { RunMigrationTest(6); } TEST_F(SandboxMountPointProviderMigrationTest, TestMigrateViaMethod7) { RunMigrationTest(7); } TEST_F(SandboxMountPointProviderMigrationTest, TestMigrateViaMethod8) { RunMigrationTest(8); } TEST_F(SandboxMountPointProviderMigrationTest, TestMigrateViaMethod9) { RunMigrationTest(9); } } // namespace fileapi