Home | History | Annotate | Download | only in storage
      1 // Copyright (c) 2012 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 "base/bind.h"
      6 #include "base/file_util.h"
      7 #include "base/files/scoped_temp_dir.h"
      8 #include "base/memory/scoped_ptr.h"
      9 #include "base/message_loop/message_loop.h"
     10 #include "base/strings/stringprintf.h"
     11 #include "chrome/browser/chrome_notification_types.h"
     12 #include "chrome/browser/extensions/api/storage/leveldb_settings_storage_factory.h"
     13 #include "chrome/browser/extensions/api/storage/settings_frontend.h"
     14 #include "chrome/browser/extensions/api/storage/settings_namespace.h"
     15 #include "chrome/browser/extensions/api/storage/settings_test_util.h"
     16 #include "chrome/browser/value_store/value_store.h"
     17 #include "content/public/test/test_browser_thread.h"
     18 #include "testing/gtest/include/gtest/gtest.h"
     19 
     20 using content::BrowserThread;
     21 
     22 namespace extensions {
     23 
     24 namespace settings = settings_namespace;
     25 namespace util = settings_test_util;
     26 
     27 namespace {
     28 
     29 // To save typing ValueStore::DEFAULTS everywhere.
     30 const ValueStore::WriteOptions DEFAULTS = ValueStore::DEFAULTS;
     31 
     32 // Creates a kilobyte of data.
     33 scoped_ptr<Value> CreateKilobyte() {
     34   std::string kilobyte_string;
     35   for (int i = 0; i < 1024; ++i) {
     36     kilobyte_string += "a";
     37   }
     38   return scoped_ptr<Value>(new base::StringValue(kilobyte_string));
     39 }
     40 
     41 // Creates a megabyte of data.
     42 scoped_ptr<Value> CreateMegabyte() {
     43   base::ListValue* megabyte = new base::ListValue();
     44   for (int i = 0; i < 1000; ++i) {
     45     megabyte->Append(CreateKilobyte().release());
     46   }
     47   return scoped_ptr<Value>(megabyte);
     48 }
     49 
     50 }  // namespace
     51 
     52 class ExtensionSettingsFrontendTest : public testing::Test {
     53  public:
     54   ExtensionSettingsFrontendTest()
     55       : storage_factory_(new util::ScopedSettingsStorageFactory()),
     56         ui_thread_(BrowserThread::UI, base::MessageLoop::current()),
     57         file_thread_(BrowserThread::FILE, base::MessageLoop::current()) {}
     58 
     59   virtual void SetUp() OVERRIDE {
     60     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
     61     profile_.reset(new util::MockProfile(temp_dir_.path()));
     62     ResetFrontend();
     63   }
     64 
     65   virtual void TearDown() OVERRIDE {
     66     frontend_.reset();
     67     profile_.reset();
     68     // Execute any pending deletion tasks.
     69     message_loop_.RunUntilIdle();
     70   }
     71 
     72  protected:
     73   void ResetFrontend() {
     74     storage_factory_->Reset(new LeveldbSettingsStorageFactory());
     75     frontend_.reset(
     76         SettingsFrontend::Create(storage_factory_.get(), profile_.get()));
     77   }
     78 
     79   base::ScopedTempDir temp_dir_;
     80   scoped_ptr<util::MockProfile> profile_;
     81   scoped_ptr<SettingsFrontend> frontend_;
     82   scoped_refptr<util::ScopedSettingsStorageFactory> storage_factory_;
     83 
     84  private:
     85   base::MessageLoop message_loop_;
     86   content::TestBrowserThread ui_thread_;
     87   content::TestBrowserThread file_thread_;
     88 };
     89 
     90 // Get a semblance of coverage for both extension and app settings by
     91 // alternating in each test.
     92 // TODO(kalman): explicitly test the two interact correctly.
     93 
     94 TEST_F(ExtensionSettingsFrontendTest, SettingsPreservedAcrossReconstruction) {
     95   const std::string id = "ext";
     96   ExtensionServiceInterface* esi =
     97       extensions::ExtensionSystem::Get(profile_.get())->extension_service();
     98   static_cast<extensions::settings_test_util::MockExtensionService*>(esi)->
     99       AddExtensionWithId(id, Manifest::TYPE_EXTENSION);
    100 
    101   ValueStore* storage = util::GetStorage(id, frontend_.get());
    102 
    103   // The correctness of Get/Set/Remove/Clear is tested elsewhere so no need to
    104   // be too rigorous.
    105   {
    106     StringValue bar("bar");
    107     ValueStore::WriteResult result = storage->Set(DEFAULTS, "foo", bar);
    108     ASSERT_FALSE(result->HasError());
    109   }
    110 
    111   {
    112     ValueStore::ReadResult result = storage->Get();
    113     ASSERT_FALSE(result->HasError());
    114     EXPECT_FALSE(result->settings().empty());
    115   }
    116 
    117   ResetFrontend();
    118   storage = util::GetStorage(id, frontend_.get());
    119 
    120   {
    121     ValueStore::ReadResult result = storage->Get();
    122     ASSERT_FALSE(result->HasError());
    123     EXPECT_FALSE(result->settings().empty());
    124   }
    125 }
    126 
    127 TEST_F(ExtensionSettingsFrontendTest, SettingsClearedOnUninstall) {
    128   const std::string id = "ext";
    129   ExtensionServiceInterface* esi =
    130       extensions::ExtensionSystem::Get(profile_.get())->extension_service();
    131   static_cast<extensions::settings_test_util::MockExtensionService*>(esi)->
    132       AddExtensionWithId(id, Manifest::TYPE_LEGACY_PACKAGED_APP);
    133 
    134   ValueStore* storage = util::GetStorage(id, frontend_.get());
    135 
    136   {
    137     StringValue bar("bar");
    138     ValueStore::WriteResult result = storage->Set(DEFAULTS, "foo", bar);
    139     ASSERT_FALSE(result->HasError());
    140   }
    141 
    142   // This would be triggered by extension uninstall via a DataDeleter.
    143   frontend_->DeleteStorageSoon(id);
    144   base::MessageLoop::current()->RunUntilIdle();
    145 
    146   // The storage area may no longer be valid post-uninstall, so re-request.
    147   storage = util::GetStorage(id, frontend_.get());
    148   {
    149     ValueStore::ReadResult result = storage->Get();
    150     ASSERT_FALSE(result->HasError());
    151     EXPECT_TRUE(result->settings().empty());
    152   }
    153 }
    154 
    155 TEST_F(ExtensionSettingsFrontendTest, LeveldbDatabaseDeletedFromDiskOnClear) {
    156   const std::string id = "ext";
    157   ExtensionServiceInterface* esi =
    158       extensions::ExtensionSystem::Get(profile_.get())->extension_service();
    159   static_cast<extensions::settings_test_util::MockExtensionService*>(esi)->
    160       AddExtensionWithId(id, Manifest::TYPE_EXTENSION);
    161 
    162   ValueStore* storage = util::GetStorage(id, frontend_.get());
    163 
    164   {
    165     StringValue bar("bar");
    166     ValueStore::WriteResult result = storage->Set(DEFAULTS, "foo", bar);
    167     ASSERT_FALSE(result->HasError());
    168     EXPECT_TRUE(base::PathExists(temp_dir_.path()));
    169   }
    170 
    171   // Should need to both clear the database and delete the frontend for the
    172   // leveldb database to be deleted from disk.
    173   {
    174     ValueStore::WriteResult result = storage->Clear();
    175     ASSERT_FALSE(result->HasError());
    176     EXPECT_TRUE(base::PathExists(temp_dir_.path()));
    177   }
    178 
    179   frontend_.reset();
    180   base::MessageLoop::current()->RunUntilIdle();
    181   // TODO(kalman): Figure out why this fails, despite appearing to work.
    182   // Leaving this commented out rather than disabling the whole test so that the
    183   // deletion code paths are at least exercised.
    184   //EXPECT_FALSE(base::PathExists(temp_dir_.path()));
    185 }
    186 
    187 // Disabled (slow), http://crbug.com/322751 .
    188 TEST_F(ExtensionSettingsFrontendTest,
    189        DISABLED_QuotaLimitsEnforcedCorrectlyForSyncAndLocal) {
    190   const std::string id = "ext";
    191   ExtensionServiceInterface* esi =
    192       extensions::ExtensionSystem::Get(profile_.get())->extension_service();
    193   static_cast<extensions::settings_test_util::MockExtensionService*>(esi)->
    194       AddExtensionWithId(id, Manifest::TYPE_EXTENSION);
    195 
    196   ValueStore* sync_storage =
    197       util::GetStorage(id, settings::SYNC, frontend_.get());
    198   ValueStore* local_storage =
    199       util::GetStorage(id, settings::LOCAL, frontend_.get());
    200 
    201   // Sync storage should run out after ~100K.
    202   scoped_ptr<Value> kilobyte = CreateKilobyte();
    203   for (int i = 0; i < 100; ++i) {
    204     sync_storage->Set(
    205         ValueStore::DEFAULTS, base::StringPrintf("%d", i), *kilobyte);
    206   }
    207 
    208   EXPECT_TRUE(sync_storage->Set(
    209       ValueStore::DEFAULTS, "WillError", *kilobyte)->HasError());
    210 
    211   // Local storage shouldn't run out after ~100K.
    212   for (int i = 0; i < 100; ++i) {
    213     local_storage->Set(
    214         ValueStore::DEFAULTS, base::StringPrintf("%d", i), *kilobyte);
    215   }
    216 
    217   EXPECT_FALSE(local_storage->Set(
    218       ValueStore::DEFAULTS, "WontError", *kilobyte)->HasError());
    219 
    220   // Local storage should run out after ~5MB.
    221   scoped_ptr<Value> megabyte = CreateMegabyte();
    222   for (int i = 0; i < 5; ++i) {
    223     local_storage->Set(
    224         ValueStore::DEFAULTS, base::StringPrintf("%d", i), *megabyte);
    225   }
    226 
    227   EXPECT_TRUE(local_storage->Set(
    228       ValueStore::DEFAULTS, "WillError", *megabyte)->HasError());
    229 }
    230 
    231 // In other tests, we assume that the result of GetStorage is a pointer to the
    232 // a Storage owned by a Frontend object, but for the unlimitedStorage case, this
    233 // might not be true. So, write the tests in a "callback" style.
    234 // We should really rewrite all tests to be asynchronous in this way.
    235 
    236 static void UnlimitedSyncStorageTestCallback(ValueStore* sync_storage) {
    237   // Sync storage should still run out after ~100K; the unlimitedStorage
    238   // permission can't apply to sync.
    239   scoped_ptr<Value> kilobyte = CreateKilobyte();
    240   for (int i = 0; i < 100; ++i) {
    241     sync_storage->Set(
    242         ValueStore::DEFAULTS, base::StringPrintf("%d", i), *kilobyte);
    243   }
    244 
    245   EXPECT_TRUE(sync_storage->Set(
    246       ValueStore::DEFAULTS, "WillError", *kilobyte)->HasError());
    247 }
    248 
    249 static void UnlimitedLocalStorageTestCallback(ValueStore* local_storage) {
    250   // Local storage should never run out.
    251   scoped_ptr<Value> megabyte = CreateMegabyte();
    252   for (int i = 0; i < 7; ++i) {
    253     local_storage->Set(
    254         ValueStore::DEFAULTS, base::StringPrintf("%d", i), *megabyte);
    255   }
    256 
    257   EXPECT_FALSE(local_storage->Set(
    258       ValueStore::DEFAULTS, "WontError", *megabyte)->HasError());
    259 }
    260 
    261 #if defined(OS_WIN)
    262 // See: http://crbug.com/227296
    263 #define MAYBE_UnlimitedStorageForLocalButNotSync \
    264     DISABLED_UnlimitedStorageForLocalButNotSync
    265 #else
    266 #define MAYBE_UnlimitedStorageForLocalButNotSync \
    267     UnlimitedStorageForLocalButNotSync
    268 #endif
    269 
    270 TEST_F(ExtensionSettingsFrontendTest,
    271        MAYBE_UnlimitedStorageForLocalButNotSync) {
    272   const std::string id = "ext";
    273   std::set<std::string> permissions;
    274   permissions.insert("unlimitedStorage");
    275   ExtensionServiceInterface* esi =
    276       extensions::ExtensionSystem::Get(profile_.get())->extension_service();
    277   static_cast<extensions::settings_test_util::MockExtensionService*>(esi)->
    278       AddExtensionWithIdAndPermissions(id, Manifest::TYPE_EXTENSION,
    279           permissions);
    280 
    281   frontend_->RunWithStorage(
    282       id, settings::SYNC, base::Bind(&UnlimitedSyncStorageTestCallback));
    283   frontend_->RunWithStorage(
    284       id, settings::LOCAL, base::Bind(&UnlimitedLocalStorageTestCallback));
    285 
    286   base::MessageLoop::current()->RunUntilIdle();
    287 }
    288 
    289 }  // namespace extensions
    290