diff options
Diffstat (limited to 'chrome/browser/extensions/api/storage/settings_frontend_unittest.cc')
-rw-r--r-- | chrome/browser/extensions/api/storage/settings_frontend_unittest.cc | 288 |
1 files changed, 288 insertions, 0 deletions
diff --git a/chrome/browser/extensions/api/storage/settings_frontend_unittest.cc b/chrome/browser/extensions/api/storage/settings_frontend_unittest.cc new file mode 100644 index 0000000..07a473d --- /dev/null +++ b/chrome/browser/extensions/api/storage/settings_frontend_unittest.cc @@ -0,0 +1,288 @@ +// Copyright (c) 2012 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 "base/bind.h" +#include "base/file_util.h" +#include "base/files/scoped_temp_dir.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop.h" +#include "base/stringprintf.h" +#include "chrome/browser/extensions/api/storage/leveldb_settings_storage_factory.h" +#include "chrome/browser/extensions/api/storage/settings_frontend.h" +#include "chrome/browser/extensions/api/storage/settings_namespace.h" +#include "chrome/browser/extensions/api/storage/settings_test_util.h" +#include "chrome/browser/value_store/value_store.h" +#include "chrome/common/chrome_notification_types.h" +#include "content/public/test/test_browser_thread.h" +#include "testing/gtest/include/gtest/gtest.h" + +using content::BrowserThread; + +namespace extensions { + +namespace settings = settings_namespace; +namespace util = settings_test_util; + +namespace { + +// To save typing ValueStore::DEFAULTS everywhere. +const ValueStore::WriteOptions DEFAULTS = ValueStore::DEFAULTS; + +// Creates a kilobyte of data. +scoped_ptr<Value> CreateKilobyte() { + std::string kilobyte_string; + for (int i = 0; i < 1024; ++i) { + kilobyte_string += "a"; + } + return scoped_ptr<Value>(Value::CreateStringValue(kilobyte_string)); +} + +// Creates a megabyte of data. +scoped_ptr<Value> CreateMegabyte() { + ListValue* megabyte = new ListValue(); + for (int i = 0; i < 1000; ++i) { + megabyte->Append(CreateKilobyte().release()); + } + return scoped_ptr<Value>(megabyte); +} + +} + +class ExtensionSettingsFrontendTest : public testing::Test { + public: + ExtensionSettingsFrontendTest() + : storage_factory_(new util::ScopedSettingsStorageFactory()), + ui_thread_(BrowserThread::UI, MessageLoop::current()), + file_thread_(BrowserThread::FILE, MessageLoop::current()) {} + + virtual void SetUp() OVERRIDE { + ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); + profile_.reset(new util::MockProfile(temp_dir_.path())); + ResetFrontend(); + } + + virtual void TearDown() OVERRIDE { + frontend_.reset(); + profile_.reset(); + // Execute any pending deletion tasks. + message_loop_.RunUntilIdle(); + } + + protected: + void ResetFrontend() { + storage_factory_->Reset(new LeveldbSettingsStorageFactory()); + frontend_.reset( + SettingsFrontend::Create(storage_factory_.get(), profile_.get())); + } + + base::ScopedTempDir temp_dir_; + scoped_ptr<util::MockProfile> profile_; + scoped_ptr<SettingsFrontend> frontend_; + scoped_refptr<util::ScopedSettingsStorageFactory> storage_factory_; + + private: + MessageLoop message_loop_; + content::TestBrowserThread ui_thread_; + content::TestBrowserThread file_thread_; +}; + +// Get a semblance of coverage for both extension and app settings by +// alternating in each test. +// TODO(kalman): explicitly test the two interact correctly. + +TEST_F(ExtensionSettingsFrontendTest, SettingsPreservedAcrossReconstruction) { + const std::string id = "ext"; + ExtensionServiceInterface* esi = + extensions::ExtensionSystem::Get(profile_.get())->extension_service(); + static_cast<extensions::settings_test_util::MockExtensionService*>(esi)-> + AddExtensionWithId(id, Extension::TYPE_EXTENSION); + + ValueStore* storage = util::GetStorage(id, frontend_.get()); + + // The correctness of Get/Set/Remove/Clear is tested elsewhere so no need to + // be too rigorous. + { + StringValue bar("bar"); + ValueStore::WriteResult result = storage->Set(DEFAULTS, "foo", bar); + ASSERT_FALSE(result->HasError()); + } + + { + ValueStore::ReadResult result = storage->Get(); + ASSERT_FALSE(result->HasError()); + EXPECT_FALSE(result->settings()->empty()); + } + + ResetFrontend(); + storage = util::GetStorage(id, frontend_.get()); + + { + ValueStore::ReadResult result = storage->Get(); + ASSERT_FALSE(result->HasError()); + EXPECT_FALSE(result->settings()->empty()); + } +} + +TEST_F(ExtensionSettingsFrontendTest, SettingsClearedOnUninstall) { + const std::string id = "ext"; + ExtensionServiceInterface* esi = + extensions::ExtensionSystem::Get(profile_.get())->extension_service(); + static_cast<extensions::settings_test_util::MockExtensionService*>(esi)-> + AddExtensionWithId(id, Extension::TYPE_LEGACY_PACKAGED_APP); + + ValueStore* storage = util::GetStorage(id, frontend_.get()); + + { + StringValue bar("bar"); + ValueStore::WriteResult result = storage->Set(DEFAULTS, "foo", bar); + ASSERT_FALSE(result->HasError()); + } + + // This would be triggered by extension uninstall via a DataDeleter. + frontend_->DeleteStorageSoon(id); + MessageLoop::current()->RunUntilIdle(); + + // The storage area may no longer be valid post-uninstall, so re-request. + storage = util::GetStorage(id, frontend_.get()); + { + ValueStore::ReadResult result = storage->Get(); + ASSERT_FALSE(result->HasError()); + EXPECT_TRUE(result->settings()->empty()); + } +} + +TEST_F(ExtensionSettingsFrontendTest, LeveldbDatabaseDeletedFromDiskOnClear) { + const std::string id = "ext"; + ExtensionServiceInterface* esi = + extensions::ExtensionSystem::Get(profile_.get())->extension_service(); + static_cast<extensions::settings_test_util::MockExtensionService*>(esi)-> + AddExtensionWithId(id, Extension::TYPE_EXTENSION); + + ValueStore* storage = util::GetStorage(id, frontend_.get()); + + { + StringValue bar("bar"); + ValueStore::WriteResult result = storage->Set(DEFAULTS, "foo", bar); + ASSERT_FALSE(result->HasError()); + EXPECT_TRUE(file_util::PathExists(temp_dir_.path())); + } + + // Should need to both clear the database and delete the frontend for the + // leveldb database to be deleted from disk. + { + ValueStore::WriteResult result = storage->Clear(); + ASSERT_FALSE(result->HasError()); + EXPECT_TRUE(file_util::PathExists(temp_dir_.path())); + } + + frontend_.reset(); + MessageLoop::current()->RunUntilIdle(); + // TODO(kalman): Figure out why this fails, despite appearing to work. + // Leaving this commented out rather than disabling the whole test so that the + // deletion code paths are at least exercised. + //EXPECT_FALSE(file_util::PathExists(temp_dir_.path())); +} + +#if defined(OS_WIN) +// Failing on vista dbg. http://crbug.com/111100, http://crbug.com/108724 +#define QuotaLimitsEnforcedCorrectlyForSyncAndLocal \ + DISABLED_QuotaLimitsEnforcedCorrectlyForSyncAndLocal +#endif +TEST_F(ExtensionSettingsFrontendTest, + QuotaLimitsEnforcedCorrectlyForSyncAndLocal) { + const std::string id = "ext"; + ExtensionServiceInterface* esi = + extensions::ExtensionSystem::Get(profile_.get())->extension_service(); + static_cast<extensions::settings_test_util::MockExtensionService*>(esi)-> + AddExtensionWithId(id, Extension::TYPE_EXTENSION); + + ValueStore* sync_storage = + util::GetStorage(id, settings::SYNC, frontend_.get()); + ValueStore* local_storage = + util::GetStorage(id, settings::LOCAL, frontend_.get()); + + // Sync storage should run out after ~100K. + scoped_ptr<Value> kilobyte = CreateKilobyte(); + for (int i = 0; i < 100; ++i) { + sync_storage->Set( + ValueStore::DEFAULTS, base::StringPrintf("%d", i), *kilobyte); + } + + EXPECT_TRUE(sync_storage->Set( + ValueStore::DEFAULTS, "WillError", *kilobyte)->HasError()); + + // Local storage shouldn't run out after ~100K. + for (int i = 0; i < 100; ++i) { + local_storage->Set( + ValueStore::DEFAULTS, base::StringPrintf("%d", i), *kilobyte); + } + + EXPECT_FALSE(local_storage->Set( + ValueStore::DEFAULTS, "WontError", *kilobyte)->HasError()); + + // Local storage should run out after ~5MB. + scoped_ptr<Value> megabyte = CreateMegabyte(); + for (int i = 0; i < 5; ++i) { + local_storage->Set( + ValueStore::DEFAULTS, base::StringPrintf("%d", i), *megabyte); + } + + EXPECT_TRUE(local_storage->Set( + ValueStore::DEFAULTS, "WillError", *megabyte)->HasError()); +} + +// In other tests, we assume that the result of GetStorage is a pointer to the +// a Storage owned by a Frontend object, but for the unlimitedStorage case, this +// might not be true. So, write the tests in a "callback" style. +// We should really rewrite all tests to be asynchronous in this way. + +static void UnlimitedSyncStorageTestCallback(ValueStore* sync_storage) { + // Sync storage should still run out after ~100K; the unlimitedStorage + // permission can't apply to sync. + scoped_ptr<Value> kilobyte = CreateKilobyte(); + for (int i = 0; i < 100; ++i) { + sync_storage->Set( + ValueStore::DEFAULTS, base::StringPrintf("%d", i), *kilobyte); + } + + EXPECT_TRUE(sync_storage->Set( + ValueStore::DEFAULTS, "WillError", *kilobyte)->HasError()); +} + +static void UnlimitedLocalStorageTestCallback(ValueStore* local_storage) { + // Local storage should never run out. + scoped_ptr<Value> megabyte = CreateMegabyte(); + for (int i = 0; i < 7; ++i) { + local_storage->Set( + ValueStore::DEFAULTS, base::StringPrintf("%d", i), *megabyte); + } + + EXPECT_FALSE(local_storage->Set( + ValueStore::DEFAULTS, "WontError", *megabyte)->HasError()); +} + +#if defined(OS_WIN) +// Failing on vista dbg. http://crbug.com/111100, http://crbug.com/108724 +#define UnlimitedStorageForLocalButNotSync DISABLED_UnlimitedStorageForLocalButNotSync +#endif +TEST_F(ExtensionSettingsFrontendTest, + UnlimitedStorageForLocalButNotSync) { + const std::string id = "ext"; + std::set<std::string> permissions; + permissions.insert("unlimitedStorage"); + ExtensionServiceInterface* esi = + extensions::ExtensionSystem::Get(profile_.get())->extension_service(); + static_cast<extensions::settings_test_util::MockExtensionService*>(esi)-> + AddExtensionWithIdAndPermissions(id, Extension::TYPE_EXTENSION, + permissions); + + frontend_->RunWithStorage( + id, settings::SYNC, base::Bind(&UnlimitedSyncStorageTestCallback)); + frontend_->RunWithStorage( + id, settings::LOCAL, base::Bind(&UnlimitedLocalStorageTestCallback)); + + MessageLoop::current()->RunUntilIdle(); +} + +} // namespace extensions |