diff options
author | ryanackley <ryanackley@gmail.com> | 2015-01-27 15:12:14 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-01-27 23:13:42 +0000 |
commit | 48bedbd2773df847e5dbf1c01fb409da4636d542 (patch) | |
tree | 84a0424f2dd8c553984ff742f5886f7bd5337e92 /chrome/browser/extensions/app_data_migrator_unittest.cc | |
parent | d23af8f121147bdced97b13fa7518b19050447d6 (diff) | |
download | chromium_src-48bedbd2773df847e5dbf1c01fb409da4636d542.zip chromium_src-48bedbd2773df847e5dbf1c01fb409da4636d542.tar.gz chromium_src-48bedbd2773df847e5dbf1c01fb409da4636d542.tar.bz2 |
Migrates legacy packaged app data when it's upgraded to a platform app
Currently, data stored by a legacy packaged app in the HTML5 filesystem
and IndexedDB is lost when it gets migrated to the new type of Chrome App
(internally referred to as "platform apps"). This is because Chrome stores
extension and legacy packaged app data in the general storage partition and
it stores platform app data in a it's own isolated storage partition.
When a legacy packaged app is upgraded to the new type of app, a new
isolated storage partition is created but the data gets left behind
in the general storage partition.
This change fixes this by performing a migration of the data to the new
storage partition at the time of the upgrade. It only migrates IndexedDB
and HTML5 filesystem because these are the only permanent data storage
options available to platform apps. Platform apps are prohibited from
using DOMStorage and WebSQL. Data stored by chrome.storage APIs is
separate from the storage partitions.
BUG=302577
Review URL: https://codereview.chromium.org/671873004
Cr-Commit-Position: refs/heads/master@{#313394}
Diffstat (limited to 'chrome/browser/extensions/app_data_migrator_unittest.cc')
-rw-r--r-- | chrome/browser/extensions/app_data_migrator_unittest.cc | 273 |
1 files changed, 273 insertions, 0 deletions
diff --git a/chrome/browser/extensions/app_data_migrator_unittest.cc b/chrome/browser/extensions/app_data_migrator_unittest.cc new file mode 100644 index 0000000..c369710 --- /dev/null +++ b/chrome/browser/extensions/app_data_migrator_unittest.cc @@ -0,0 +1,273 @@ +// 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 <string> + +#include "base/callback_forward.h" +#include "base/message_loop/message_loop.h" +#include "base/threading/sequenced_worker_pool.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/mock_blob_url_request_context.h" +#include "content/public/test/test_browser_thread_bundle.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 "testing/gtest/include/gtest/gtest.h" + +namespace { +scoped_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_ = scoped_ptr<AppDataMigrator>( + new AppDataMigrator(profile_.get(), registry_)); + + default_partition_ = + content::BrowserContext::GetDefaultStoragePartition(profile_.get()); + + idb_context_ = default_partition_->GetIndexedDBContext(); + idb_context_->SetTaskRunnerForTesting( + base::MessageLoop::current()->message_loop_proxy().get()); + + default_fs_context_ = default_partition_->GetFileSystemContext(); + + url_request_context_ = scoped_ptr<content::MockBlobURLRequestContext>( + new content::MockBlobURLRequestContext(default_fs_context_)); + } + + void TearDown() override {} + + protected: + content::TestBrowserThreadBundle thread_bundle_; + scoped_ptr<TestingProfile> profile_; + scoped_ptr<AppDataMigrator> migrator_; + content::StoragePartition* default_partition_; + ExtensionRegistry* registry_; + storage::FileSystemContext* default_fs_context_; + content::IndexedDBContext* idb_context_; + scoped_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")))) + .Set("permissions", + ListBuilder().Append("unlimitedStorage"))) + .Build(); + } else { + app = ExtensionBuilder() + .SetManifest(DictionaryBuilder() + .Set("name", "test app") + .Set("version", "1") + .Set("app", DictionaryBuilder().Set( + "launch", + DictionaryBuilder().Set( + "local_path", "index.html"))) + .Set("permissions", + ListBuilder().Append("unlimitedStorage"))) + .Build(); + } + return app; +} + +void MigrationCallback() { +} + +void DidWrite(base::File::Error status, int64 bytes, bool complete) { + base::MessageLoop::current()->Quit(); +} + +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::Bind(&DidOpenFileSystem)); + + fs_context->OpenFileSystem(extension_url, storage::kFileSystemTypePersistent, + storage::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT, + base::Bind(&DidOpenFileSystem)); + base::MessageLoop::current()->RunUntilIdle(); +} + +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)); + base::MessageLoop::current()->RunUntilIdle(); + + fs_context->operation_runner()->Write(url_request_context, fs_temp_url, + blob1.GetBlobDataHandle(), 0, + base::Bind(&DidWrite)); + base::MessageLoop::current()->Run(); + fs_context->operation_runner()->Write(url_request_context, fs_persistent_url, + blob1.GetBlobDataHandle(), 0, + base::Bind(&DidWrite)); + base::MessageLoop::current()->Run(); +} + +void VerifyFileContents(base::File file, + const base::Closure& on_close_callback) { + ASSERT_EQ(14, file.GetLength()); + scoped_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()) + on_close_callback.Run(); + base::MessageLoop::current()->Quit(); +} + +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)); + base::MessageLoop::current()->Run(); + new_fs_context->operation_runner()->OpenFile( + fs_persistent_url, base::File::FLAG_READ | base::File::FLAG_OPEN, + base::Bind(&VerifyFileContents)); + base::MessageLoop::current()->Run(); +} + +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)); +} + +TEST_F(AppDataMigratorTest, 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)); + + base::MessageLoop::current()->RunUntilIdle(); + + 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()); +} + +} // namespace extensions |