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