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>(Value::CreateStringValue(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 } 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 TEST_F(ExtensionSettingsFrontendTest, 188 QuotaLimitsEnforcedCorrectlyForSyncAndLocal) { 189 const std::string id = "ext"; 190 ExtensionServiceInterface* esi = 191 extensions::ExtensionSystem::Get(profile_.get())->extension_service(); 192 static_cast<extensions::settings_test_util::MockExtensionService*>(esi)-> 193 AddExtensionWithId(id, Manifest::TYPE_EXTENSION); 194 195 ValueStore* sync_storage = 196 util::GetStorage(id, settings::SYNC, frontend_.get()); 197 ValueStore* local_storage = 198 util::GetStorage(id, settings::LOCAL, frontend_.get()); 199 200 // Sync storage should run out after ~100K. 201 scoped_ptr<Value> kilobyte = CreateKilobyte(); 202 for (int i = 0; i < 100; ++i) { 203 sync_storage->Set( 204 ValueStore::DEFAULTS, base::StringPrintf("%d", i), *kilobyte); 205 } 206 207 EXPECT_TRUE(sync_storage->Set( 208 ValueStore::DEFAULTS, "WillError", *kilobyte)->HasError()); 209 210 // Local storage shouldn't run out after ~100K. 211 for (int i = 0; i < 100; ++i) { 212 local_storage->Set( 213 ValueStore::DEFAULTS, base::StringPrintf("%d", i), *kilobyte); 214 } 215 216 EXPECT_FALSE(local_storage->Set( 217 ValueStore::DEFAULTS, "WontError", *kilobyte)->HasError()); 218 219 // Local storage should run out after ~5MB. 220 scoped_ptr<Value> megabyte = CreateMegabyte(); 221 for (int i = 0; i < 5; ++i) { 222 local_storage->Set( 223 ValueStore::DEFAULTS, base::StringPrintf("%d", i), *megabyte); 224 } 225 226 EXPECT_TRUE(local_storage->Set( 227 ValueStore::DEFAULTS, "WillError", *megabyte)->HasError()); 228 } 229 230 // In other tests, we assume that the result of GetStorage is a pointer to the 231 // a Storage owned by a Frontend object, but for the unlimitedStorage case, this 232 // might not be true. So, write the tests in a "callback" style. 233 // We should really rewrite all tests to be asynchronous in this way. 234 235 static void UnlimitedSyncStorageTestCallback(ValueStore* sync_storage) { 236 // Sync storage should still run out after ~100K; the unlimitedStorage 237 // permission can't apply to sync. 238 scoped_ptr<Value> kilobyte = CreateKilobyte(); 239 for (int i = 0; i < 100; ++i) { 240 sync_storage->Set( 241 ValueStore::DEFAULTS, base::StringPrintf("%d", i), *kilobyte); 242 } 243 244 EXPECT_TRUE(sync_storage->Set( 245 ValueStore::DEFAULTS, "WillError", *kilobyte)->HasError()); 246 } 247 248 static void UnlimitedLocalStorageTestCallback(ValueStore* local_storage) { 249 // Local storage should never run out. 250 scoped_ptr<Value> megabyte = CreateMegabyte(); 251 for (int i = 0; i < 7; ++i) { 252 local_storage->Set( 253 ValueStore::DEFAULTS, base::StringPrintf("%d", i), *megabyte); 254 } 255 256 EXPECT_FALSE(local_storage->Set( 257 ValueStore::DEFAULTS, "WontError", *megabyte)->HasError()); 258 } 259 260 #if defined(OS_WIN) 261 // See: http://crbug.com/227296 262 #define MAYBE_UnlimitedStorageForLocalButNotSync \ 263 DISABLED_UnlimitedStorageForLocalButNotSync 264 #else 265 #define MAYBE_UnlimitedStorageForLocalButNotSync \ 266 UnlimitedStorageForLocalButNotSync 267 #endif 268 269 TEST_F(ExtensionSettingsFrontendTest, 270 MAYBE_UnlimitedStorageForLocalButNotSync) { 271 const std::string id = "ext"; 272 std::set<std::string> permissions; 273 permissions.insert("unlimitedStorage"); 274 ExtensionServiceInterface* esi = 275 extensions::ExtensionSystem::Get(profile_.get())->extension_service(); 276 static_cast<extensions::settings_test_util::MockExtensionService*>(esi)-> 277 AddExtensionWithIdAndPermissions(id, Manifest::TYPE_EXTENSION, 278 permissions); 279 280 frontend_->RunWithStorage( 281 id, settings::SYNC, base::Bind(&UnlimitedSyncStorageTestCallback)); 282 frontend_->RunWithStorage( 283 id, settings::LOCAL, base::Bind(&UnlimitedLocalStorageTestCallback)); 284 285 base::MessageLoop::current()->RunUntilIdle(); 286 } 287 288 } // namespace extensions 289