blob: fba4794cfa1560264e91907f440e2337b5099a56 [file] [log] [blame]
// Copyright 2015 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 <stdint.h>
#include <string>
#include <utility>
#include "base/bind.h"
#include "base/callback_forward.h"
#include "base/run_loop.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chrome/browser/extensions/app_data_migrator.h"
#include "chrome/browser/extensions/extension_special_storage_policy.h"
#include "chrome/test/base/testing_profile.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/indexed_db_context.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "content/public/test/test_utils.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_builder.h"
#include "extensions/common/manifest.h"
#include "storage/browser/fileapi/file_system_context.h"
#include "storage/browser/fileapi/file_system_operation_runner.h"
#include "storage/browser/fileapi/file_system_url.h"
#include "storage/browser/test/mock_blob_url_request_context.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
std::unique_ptr<TestingProfile> GetTestingProfile() {
TestingProfile::Builder profile_builder;
return profile_builder.Build();
}
}
namespace extensions {
class AppDataMigratorTest : public testing::Test {
public:
AppDataMigratorTest()
: thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {}
void SetUp() override {
profile_ = GetTestingProfile();
registry_ = ExtensionRegistry::Get(profile_.get());
migrator_ = std::unique_ptr<AppDataMigrator>(
new AppDataMigrator(profile_.get(), registry_));
default_partition_ =
content::BrowserContext::GetDefaultStoragePartition(profile_.get());
idb_context_ = default_partition_->GetIndexedDBContext();
default_fs_context_ = default_partition_->GetFileSystemContext();
url_request_context_ = std::unique_ptr<content::MockBlobURLRequestContext>(
new content::MockBlobURLRequestContext());
}
void TearDown() override {}
protected:
content::TestBrowserThreadBundle thread_bundle_;
std::unique_ptr<TestingProfile> profile_;
std::unique_ptr<AppDataMigrator> migrator_;
content::StoragePartition* default_partition_;
ExtensionRegistry* registry_;
storage::FileSystemContext* default_fs_context_;
content::IndexedDBContext* idb_context_;
std::unique_ptr<content::MockBlobURLRequestContext> url_request_context_;
};
scoped_refptr<const Extension> GetTestExtension(bool platform_app) {
scoped_refptr<const Extension> app;
if (platform_app) {
app = ExtensionBuilder()
.SetManifest(
DictionaryBuilder()
.Set("name", "test app")
.Set("version", "1")
.Set("app", DictionaryBuilder()
.Set("background",
DictionaryBuilder()
.Set("scripts",
ListBuilder()
.Append("background.js")
.Build())
.Build())
.Build())
.Set("permissions",
ListBuilder().Append("unlimitedStorage").Build())
.Build())
.Build();
} else {
app = ExtensionBuilder()
.SetManifest(
DictionaryBuilder()
.Set("name", "test app")
.Set("version", "1")
.Set("app", DictionaryBuilder()
.Set("launch",
DictionaryBuilder()
.Set("local_path", "index.html")
.Build())
.Build())
.Set("permissions",
ListBuilder().Append("unlimitedStorage").Build())
.Build())
.Build();
}
return app;
}
void MigrationCallback() {
}
void DidWrite(base::File::Error status, int64_t bytes, bool complete) {
base::RunLoop::QuitCurrentWhenIdleDeprecated();
}
void DidCreate(base::File::Error status) {
}
void DidOpenFileSystem(const GURL& root,
const std::string& name,
base::File::Error result) {
}
void OpenFileSystems(storage::FileSystemContext* fs_context,
GURL extension_url) {
fs_context->OpenFileSystem(extension_url, storage::kFileSystemTypeTemporary,
storage::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
base::BindOnce(&DidOpenFileSystem));
fs_context->OpenFileSystem(extension_url, storage::kFileSystemTypePersistent,
storage::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
base::BindOnce(&DidOpenFileSystem));
content::RunAllTasksUntilIdle();
}
void GenerateTestFiles(content::MockBlobURLRequestContext* url_request_context,
const Extension* ext,
storage::FileSystemContext* fs_context,
Profile* profile) {
profile->GetExtensionSpecialStoragePolicy()->GrantRightsForExtension(ext,
profile);
base::FilePath path(FILE_PATH_LITERAL("test.txt"));
GURL extension_url =
extensions::Extension::GetBaseURLFromExtensionId(ext->id());
OpenFileSystems(fs_context, extension_url);
storage::FileSystemURL fs_temp_url = fs_context->CreateCrackedFileSystemURL(
extension_url, storage::kFileSystemTypeTemporary, path);
storage::FileSystemURL fs_persistent_url =
fs_context->CreateCrackedFileSystemURL(
extension_url, storage::kFileSystemTypePersistent, path);
content::ScopedTextBlob blob1(*url_request_context, "blob-id:success1",
"Hello, world!\n");
fs_context->operation_runner()->CreateFile(fs_temp_url, false,
base::Bind(&DidCreate));
fs_context->operation_runner()->CreateFile(fs_persistent_url, false,
base::Bind(&DidCreate));
content::RunAllTasksUntilIdle();
fs_context->operation_runner()->Write(fs_temp_url, blob1.GetBlobDataHandle(),
0, base::BindRepeating(&DidWrite));
content::RunAllTasksUntilIdle();
fs_context->operation_runner()->Write(fs_persistent_url,
blob1.GetBlobDataHandle(), 0,
base::BindRepeating(&DidWrite));
content::RunAllTasksUntilIdle();
}
void VerifyFileContents(base::File file, base::OnceClosure on_close_callback) {
ASSERT_EQ(14, file.GetLength());
std::unique_ptr<char[]> buffer(new char[15]);
file.Read(0, buffer.get(), 14);
buffer.get()[14] = 0;
std::string expected = "Hello, world!\n";
std::string actual = buffer.get();
EXPECT_EQ(expected, actual);
file.Close();
if (!on_close_callback.is_null())
std::move(on_close_callback).Run();
base::RunLoop::QuitCurrentWhenIdleDeprecated();
}
void VerifyTestFilesMigrated(content::StoragePartition* new_partition,
const Extension* new_ext) {
GURL extension_url =
extensions::Extension::GetBaseURLFromExtensionId(new_ext->id());
storage::FileSystemContext* new_fs_context =
new_partition->GetFileSystemContext();
OpenFileSystems(new_fs_context, extension_url);
base::FilePath path(FILE_PATH_LITERAL("test.txt"));
storage::FileSystemURL fs_temp_url =
new_fs_context->CreateCrackedFileSystemURL(
extension_url, storage::kFileSystemTypeTemporary, path);
storage::FileSystemURL fs_persistent_url =
new_fs_context->CreateCrackedFileSystemURL(
extension_url, storage::kFileSystemTypePersistent, path);
new_fs_context->operation_runner()->OpenFile(
fs_temp_url, base::File::FLAG_READ | base::File::FLAG_OPEN,
base::Bind(&VerifyFileContents));
content::RunAllTasksUntilIdle();
new_fs_context->operation_runner()->OpenFile(
fs_persistent_url, base::File::FLAG_READ | base::File::FLAG_OPEN,
base::Bind(&VerifyFileContents));
content::RunAllTasksUntilIdle();
}
TEST_F(AppDataMigratorTest, ShouldMigrate) {
scoped_refptr<const Extension> old_ext = GetTestExtension(false);
scoped_refptr<const Extension> new_ext = GetTestExtension(true);
EXPECT_TRUE(AppDataMigrator::NeedsMigration(old_ext.get(), new_ext.get()));
}
TEST_F(AppDataMigratorTest, ShouldNotMigratePlatformApp) {
scoped_refptr<const Extension> old_ext = GetTestExtension(true);
scoped_refptr<const Extension> new_ext = GetTestExtension(true);
EXPECT_FALSE(AppDataMigrator::NeedsMigration(old_ext.get(), new_ext.get()));
}
TEST_F(AppDataMigratorTest, ShouldNotMigrateLegacyApp) {
scoped_refptr<const Extension> old_ext = GetTestExtension(false);
scoped_refptr<const Extension> new_ext = GetTestExtension(false);
EXPECT_FALSE(AppDataMigrator::NeedsMigration(old_ext.get(), new_ext.get()));
}
TEST_F(AppDataMigratorTest, NoOpMigration) {
scoped_refptr<const Extension> old_ext = GetTestExtension(false);
scoped_refptr<const Extension> new_ext = GetTestExtension(true);
// Nothing to migrate. Basically this should just not cause an error
migrator_->DoMigrationAndReply(old_ext.get(), new_ext.get(),
base::Bind(&MigrationCallback));
}
// crbug.com/747589
TEST_F(AppDataMigratorTest, DISABLED_FileSystemMigration) {
scoped_refptr<const Extension> old_ext = GetTestExtension(false);
scoped_refptr<const Extension> new_ext = GetTestExtension(true);
GenerateTestFiles(url_request_context_.get(), old_ext.get(),
default_fs_context_, profile_.get());
migrator_->DoMigrationAndReply(old_ext.get(), new_ext.get(),
base::Bind(&MigrationCallback));
content::RunAllTasksUntilIdle();
registry_->AddEnabled(new_ext);
GURL extension_url =
extensions::Extension::GetBaseURLFromExtensionId(new_ext->id());
content::StoragePartition* new_partition =
content::BrowserContext::GetStoragePartitionForSite(profile_.get(),
extension_url);
ASSERT_NE(new_partition->GetPath(), default_partition_->GetPath());
VerifyTestFilesMigrated(new_partition, new_ext.get());
// Clean up.
content::RunAllTasksUntilIdle();
}
} // namespace extensions